diff --git a/src/graphql-schema.js b/src/graphql-schema.js index 6d10183c9..c2d96ce16 100644 --- a/src/graphql-schema.js +++ b/src/graphql-schema.js @@ -4,6 +4,7 @@ import userManagement from './resolvers/user_management.js' import statistics from './resolvers/statistics.js' import reports from './resolvers/reports.js' import posts from './resolvers/posts.js' +import moderation from './resolvers/moderation.js' export const typeDefs = fs.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql')) @@ -17,6 +18,7 @@ export const resolvers = { Mutation: { ...userManagement.Mutation, ...reports.Mutation, + ...moderation.Mutation, ...posts.Mutation } } diff --git a/src/middleware/permissionsMiddleware.js b/src/middleware/permissionsMiddleware.js index 44ed2ed34..dff11b888 100644 --- a/src/middleware/permissionsMiddleware.js +++ b/src/middleware/permissionsMiddleware.js @@ -63,8 +63,8 @@ const permissions = shield({ UpdateBadge: isAdmin, DeleteBadge: isAdmin, - AddPostDisabledBy: and(isModerator, fromUserMatchesCurrentUser), - RemovePostDisabledBy: and(isModerator, fromUserMatchesCurrentUser) + enable: isModerator, + disable: isModerator, // addFruitToBasket: isAuthenticated // CreateUser: allow, }, diff --git a/src/resolvers/moderation.js b/src/resolvers/moderation.js new file mode 100644 index 000000000..15fd291e9 --- /dev/null +++ b/src/resolvers/moderation.js @@ -0,0 +1,10 @@ +export default { + Mutation: { + disable: async (object, params, {user, driver}) => { + return true + }, + enable: async (object, params, {user, driver}) => { + return true + } + } +} diff --git a/src/resolvers/moderation.spec.js b/src/resolvers/moderation.spec.js new file mode 100644 index 000000000..b6249a8b7 --- /dev/null +++ b/src/resolvers/moderation.spec.js @@ -0,0 +1,170 @@ +import Factory from '../seed/factories' +import { GraphQLClient } from 'graphql-request' +import { host, login } from '../jest/helpers' + +const factory = Factory() +let client + +afterEach(async () => { + await factory.cleanDatabase() +}) + +describe('disable', () => { + const setup = async (params = {}) => { + await factory.create('User', { email: 'author@example.org', password: '1234' }) + await factory.authenticateAs({ email: 'author@example.org', password: '1234' }) + await factory.create('Post', { + id: 'p9' // that's the ID we will look for + }) + + // create the user we use in the scenario below + let headers = {} + const { email, password } = params + if (email && password) { + await factory.create('User', params) + headers = await login({ email, password }) + } + client = new GraphQLClient(host, { headers }) + } + + const mutation = ` + mutation { + disable(resource: { + id: "p9" + type: contribution + }) + } + ` + + it('throws authorization error', async () => { + await setup() + await expect(client.request(mutation)).rejects.toThrow('Not Authorised') + }) + + describe('authenticated', () => { + it('throws authorization error', async () => { + await setup({ + email: 'someUser@example.org', + password: '1234' + }) + await expect(client.request(mutation)).rejects.toThrow('Not Authorised') + }) + + describe('as moderator', () => { + beforeEach(async () => { + await setup({ + id: 'u7', + email: 'moderator@example.org', + password: '1234', + role: 'moderator' + }) + }) + + it('returns true', async () => { + const expected = { disable: true } + await expect(client.request(mutation)).resolves.toEqual(expected) + }) + + it('sets current user', async () => { + const before = { Post: [{ id: 'p9', disabledBy: null }] } + const expected = { Post: [{ id: 'p9', disabledBy: { id: 'u7' } }] } + + await expect(client.request( + '{ Post { id, disabledBy { id } } }' + )).resolves.toEqual(before) + await client.request(mutation) + await expect(client.request( + '{ Post(disabled: true) { id, disabledBy { id } } }' + )).resolves.toEqual(expected) + }) + + it('updates .disabled on post', async () => { + const before = { Post: [ { id: 'p9', disabled: false } ] } + const expected = { Post: [ { id: 'p9', disabled: true } ] } + + await expect(client.request( + '{ Post { id disabled } }' + )).resolves.toEqual(before) + await client.request(mutation) // this updates .disabled + await expect(client.request( + '{ Post(disabled: true) { id disabled } }' + )).resolves.toEqual(expected) + }) + }) + }) +}) + +describe('enable', () => { + const setup = async (params = {}) => { + await factory.create('User', { email: 'anotherModerator@example.org', password: '1234', id: 'u123', role: 'moderator' }) + await factory.authenticateAs({ email: 'anotherModerator@example.org', password: '1234' }) + await factory.create('Post', { + id: 'p9' // that's the ID we will look for + }) + await factory.relate('Post', 'DisabledBy', { + from: 'u123', + to: 'p9' + }) // that's we want to delete + + let headers = {} + const { email, password } = params + if (email && password) { + await factory.create('User', params) + headers = await login({ email, password }) + } + client = new GraphQLClient(host, { headers }) + } + + + const mutation = ` + mutation { + enable(resource: { + id: "p9" + type: contribution + }) + } + ` + + it('throws authorization error', async () => { + await setup() + await expect(client.request(mutation)).rejects.toThrow('Not Authorised') + }) + + describe('authenticated', () => { + it('throws authorization error', async () => { + await setup({ + email: 'someUser@example.org', + password: '1234' + }) + await expect(client.request(mutation)).rejects.toThrow('Not Authorised') + }) + + describe('as moderator', () => { + beforeEach(async () => { + await setup({ + role: 'moderator', + email: 'someUser@example.org', + password: '1234' + }) + }) + + it('returns true', async () => { + const expected = { enable: true } + await expect(client.request(mutation)).resolves.toEqual(expected) + }) + + it('updates .disabled on post', async () => { + const before = { Post: [ { id: 'p9', disabled: true } ] } + const expected = { Post: [ { id: 'p9', disabled: false } ] } + + await expect(client.request( + '{ Post(disabled: true) { id disabled } }' + )).resolves.toEqual(before) + await client.request(mutation) // this updates .disabled + await expect(client.request( + '{ Post { id disabled } }' + )).resolves.toEqual(expected) + }) + }) + }) +}) diff --git a/src/resolvers/posts.spec.js b/src/resolvers/posts.spec.js index 89fb1c8e4..5603683eb 100644 --- a/src/resolvers/posts.spec.js +++ b/src/resolvers/posts.spec.js @@ -200,196 +200,3 @@ describe('DeletePost', () => { }) }) }) - -describe('AddPostDisabledBy', () => { - const setup = async (params = {}) => { - await factory.create('User', { email: 'author@example.org', password: '1234' }) - await factory.authenticateAs({ email: 'author@example.org', password: '1234' }) - await factory.create('Post', { - id: 'p9' // that's the ID we will look for - }) - - let headers = {} - const { email, password } = params - if (email && password) { - await factory.create('User', params) - headers = await login({ email, password }) - } - client = new GraphQLClient(host, { headers }) - } - - const mutation = ` - mutation { - AddPostDisabledBy(from: { id: "u7" }, to: { id: "p9" }) { - from { - id - } - to { - id - } - } - } - ` - - it('throws authorization error', async () => { - await setup() - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('authenticated', () => { - it('throws authorization error', async () => { - await setup({ - email: 'someUser@example.org', - password: '1234' - }) - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('as moderator', () => { - it('throws authorization error', async () => { - await setup({ - email: 'attributedUserMismatch@example.org', - password: '1234', - role: 'moderator' - }) - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('current user matches provided user', () => { - beforeEach(async () => { - await setup({ - id: 'u7', - email: 'moderator@example.org', - password: '1234', - role: 'moderator' - }) - }) - - it('returns created relation', async () => { - const expected = { - AddPostDisabledBy: { - from: { id: 'u7' }, - to: { id: 'p9' } - } - } - await expect(client.request(mutation)).resolves.toEqual(expected) - }) - - it('sets current user', async () => { - await client.request(mutation) - const query = '{ Post(disabled: true) { id, disabledBy { id } } }' - const expected = { Post: [{ id: 'p9', disabledBy: { id: 'u7' } }] } - await expect(client.request(query)).resolves.toEqual(expected) - }) - - it('updates .disabled on post', async () => { - const before = { Post: [ { id: 'p9', disabled: false } ] } - const expected = { Post: [ { id: 'p9', disabled: true } ] } - - await expect(client.request( - '{ Post { id disabled } }' - )).resolves.toEqual(before) - await client.request(mutation) // this updates .disabled - await expect(client.request( - '{ Post(disabled: true) { id disabled } }' - )).resolves.toEqual(expected) - }) - }) - }) - }) -}) - -describe('RemovePostDisabledBy', () => { - const setup = async (params = {}) => { - await factory.create('User', { email: 'anotherModerator@example.org', password: '1234', id: 'u123', role: 'moderator' }) - await factory.authenticateAs({ email: 'anotherModerator@example.org', password: '1234' }) - await factory.create('Post', { - id: 'p9' // that's the ID we will look for - }) - await factory.relate('Post', 'DisabledBy', { - from: 'u123', - to: 'p9' - }) // that's we want to delete - - let headers = {} - const { email, password } = params - if (email && password) { - await factory.create('User', params) - headers = await login({ email, password }) - } - client = new GraphQLClient(host, { headers }) - } - - const mutation = ` - mutation { - RemovePostDisabledBy(from: { id: "u7" }, to: { id: "p9" }) { - from { - id - } - to { - id - } - } - } - ` - - it('throws authorization error', async () => { - await setup() - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('authenticated', () => { - it('throws authorization error', async () => { - await setup({ - email: 'someUser@example.org', - password: '1234' - }) - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('as moderator', () => { - it('throws authorization error', async () => { - await setup({ - role: 'moderator', - email: 'someUser@example.org', - password: '1234' - }) - await expect(client.request(mutation)).rejects.toThrow('Not Authorised') - }) - - describe('current user matches provided user', () => { - beforeEach(async () => { - await setup({ - id: 'u7', - role: 'moderator', - email: 'someUser@example.org', - password: '1234' - }) - }) - - it('returns deleted relation', async () => { - const expected = { - RemovePostDisabledBy: { - from: { id: 'u7' }, - to: { id: 'p9' } - } - } - await expect(client.request(mutation)).resolves.toEqual(expected) - }) - - it('updates .disabled on post', async () => { - const before = { Post: [ { id: 'p9', disabled: true } ] } - const expected = { Post: [ { id: 'p9', disabled: false } ] } - - await expect(client.request( - '{ Post(disabled: true) { id disabled } }' - )).resolves.toEqual(before) - await client.request(mutation) // this updates .disabled - await expect(client.request( - '{ Post { id disabled } }' - )).resolves.toEqual(expected) - }) - }) - }) - }) -}) diff --git a/src/schema.graphql b/src/schema.graphql index 4c2a58505..ddafd880e 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -7,6 +7,8 @@ type Mutation { login(email: String!, password: String!): String! signup(email: String!, password: String!): Boolean! report(resource: Resource!, description: String): Report + disable(resource: Resource!): Boolean! + enable(resource: Resource!): Boolean! } type Statistics { @@ -133,7 +135,7 @@ type Post { visibility: VisibilityEnum deleted: Boolean disabled: Boolean - disabledBy: User! @relation(name: "DISABLED", direction: "IN") + disabledBy: User @relation(name: "DISABLED", direction: "IN") createdAt: String updatedAt: String