diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 59ae06c07..8343443c9 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -174,7 +174,7 @@ const permissions = shield( VerifyEmailAddress: isAuthenticated, }, User: { - email: isMyOwn, + email: or(isMyOwn, isAdmin), }, }, { diff --git a/backend/src/middleware/permissionsMiddleware.spec.js b/backend/src/middleware/permissionsMiddleware.spec.js index 6cf9dc302..da703fb11 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.js +++ b/backend/src/middleware/permissionsMiddleware.spec.js @@ -1,22 +1,63 @@ -import { GraphQLClient } from 'graphql-request' +import { createTestClient } from 'apollo-server-testing' +import createServer from '../server' import Factory from '../seed/factories' -import { host, login } from '../jest/helpers' +import { gql } from '../jest/helpers' +import { getDriver, neode as getNeode } from '../bootstrap/neo4j' const factory = Factory() +const instance = getNeode() +const driver = getDriver() + +let query, authenticatedUser, owner, anotherRegularUser, administrator, variables, moderator + +const userQuery = gql` + query($name: String) { + User(name: $name) { + email + } + } +` describe('authorization', () => { + beforeAll(async () => { + await factory.cleanDatabase() + const { server } = createServer({ + context: () => ({ + driver, + instance, + user: authenticatedUser, + }), + }) + query = createTestClient(server).query + }) + describe('given two existing users', () => { beforeEach(async () => { - await factory.create('User', { - email: 'owner@example.org', - name: 'Owner', - password: 'iamtheowner', - }) - await factory.create('User', { - email: 'someone@example.org', - name: 'Someone else', - password: 'else', - }) + ;[owner, anotherRegularUser, administrator, moderator] = await Promise.all([ + factory.create('User', { + email: 'owner@example.org', + name: 'Owner', + password: 'iamtheowner', + }), + factory.create('User', { + email: 'another.regular.user@example.org', + name: 'Another Regular User', + password: 'else', + }), + factory.create('User', { + email: 'admin@example.org', + name: 'Admin', + password: 'admin', + role: 'admin', + }), + factory.create('User', { + email: 'moderator@example.org', + name: 'Moderator', + password: 'moderator', + role: 'moderator', + }), + ]) + variables = {} }) afterEach(async () => { @@ -24,66 +65,77 @@ describe('authorization', () => { }) describe('access email address', () => { - let headers = {} - let loginCredentials = null - const action = async () => { - if (loginCredentials) { - headers = await login(loginCredentials) - } - const graphQLClient = new GraphQLClient(host, { headers }) - return graphQLClient.request('{User(name: "Owner") { email } }') - } - - describe('not logged in', () => { - it('rejects', async () => { - await expect(action()).rejects.toThrow('Not Authorised!') - }) - - it("does not expose the owner's email address", async () => { - let response = {} - try { - await action() - } catch (error) { - response = error.response.data - } finally { - expect(response).toEqual({ User: [null] }) - } - }) - }) - - describe('as owner', () => { + describe('unauthenticated', () => { beforeEach(() => { - loginCredentials = { - email: 'owner@example.org', - password: 'iamtheowner', - } + authenticatedUser = null }) - - it("exposes the owner's email address", async () => { - await expect(action()).resolves.toEqual({ User: [{ email: 'owner@example.org' }] }) + it("throws an error and does not expose the owner's email address", async () => { + await expect( + query({ query: userQuery, variables: { name: 'Owner' } }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + data: { User: [null] }, + }) }) }) - describe('authenticated as another user', () => { - beforeEach(async () => { - loginCredentials = { - email: 'someone@example.org', - password: 'else', - } + describe('authenticated', () => { + describe('as the owner', () => { + beforeEach(async () => { + authenticatedUser = await owner.toJson() + }) + + it("exposes the owner's email address", async () => { + variables = { name: 'Owner' } + await expect(query({ query: userQuery, variables })).resolves.toMatchObject({ + data: { User: [{ email: 'owner@example.org' }] }, + errors: undefined, + }) + }) }) - it('rejects', async () => { - await expect(action()).rejects.toThrow('Not Authorised!') + describe('as another regular user', () => { + beforeEach(async () => { + authenticatedUser = await anotherRegularUser.toJson() + }) + + it("throws an error and does not expose the owner's email address", async () => { + await expect( + query({ query: userQuery, variables: { name: 'Owner' } }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + data: { User: [null] }, + }) + }) }) - it("does not expose the owner's email address", async () => { - let response - try { - await action() - } catch (error) { - response = error.response.data - } - expect(response).toEqual({ User: [null] }) + describe('as a moderator', () => { + beforeEach(async () => { + authenticatedUser = await moderator.toJson() + }) + + it("throws an error and does not expose the owner's email address", async () => { + await expect( + query({ query: userQuery, variables: { name: 'Owner' } }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + data: { User: [null] }, + }) + }) + }) + + describe('administrator', () => { + beforeEach(async () => { + authenticatedUser = await administrator.toJson() + }) + + it("exposes the owner's email address", async () => { + variables = { name: 'Owner' } + await expect(query({ query: userQuery, variables })).resolves.toMatchObject({ + data: { User: [{ email: 'owner@example.org' }] }, + errors: undefined, + }) + }) }) }) }) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index b6ee3f320..c282533b7 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -280,6 +280,7 @@ "columns": { "number": "Nr.", "name": "Name", + "email": "E-mail", "slug": "Slug", "role": "Rolle", "createdAt": "Erstellt am" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index b243e39a3..f099cdbbc 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -281,6 +281,7 @@ "columns": { "number": "No.", "name": "Name", + "email": "E-mail", "slug": "Slug", "role": "Role", "createdAt": "Created at" diff --git a/webapp/pages/admin/users.vue b/webapp/pages/admin/users.vue index e58b413aa..d5590f474 100644 --- a/webapp/pages/admin/users.vue +++ b/webapp/pages/admin/users.vue @@ -33,6 +33,11 @@ {{ scope.row.name | truncate(20) }} +