Merge pull request #1808 from Human-Connection/1704_display-user-email-to-moderators

Display user email for administrators
This commit is contained in:
mattwr18 2019-10-07 12:33:33 +02:00 committed by GitHub
commit 23841b95bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 125 additions and 64 deletions

View File

@ -174,7 +174,7 @@ const permissions = shield(
VerifyEmailAddress: isAuthenticated, VerifyEmailAddress: isAuthenticated,
}, },
User: { User: {
email: isMyOwn, email: or(isMyOwn, isAdmin),
}, },
}, },
{ {

View File

@ -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 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 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', () => { describe('authorization', () => {
beforeAll(async () => {
await factory.cleanDatabase()
const { server } = createServer({
context: () => ({
driver,
instance,
user: authenticatedUser,
}),
})
query = createTestClient(server).query
})
describe('given two existing users', () => { describe('given two existing users', () => {
beforeEach(async () => { beforeEach(async () => {
await factory.create('User', { ;[owner, anotherRegularUser, administrator, moderator] = await Promise.all([
email: 'owner@example.org', factory.create('User', {
name: 'Owner', email: 'owner@example.org',
password: 'iamtheowner', name: 'Owner',
}) password: 'iamtheowner',
await factory.create('User', { }),
email: 'someone@example.org', factory.create('User', {
name: 'Someone else', email: 'another.regular.user@example.org',
password: 'else', 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 () => { afterEach(async () => {
@ -24,66 +65,77 @@ describe('authorization', () => {
}) })
describe('access email address', () => { describe('access email address', () => {
let headers = {} describe('unauthenticated', () => {
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', () => {
beforeEach(() => { beforeEach(() => {
loginCredentials = { authenticatedUser = null
email: 'owner@example.org',
password: 'iamtheowner',
}
}) })
it("throws an error and does not expose the owner's email address", async () => {
it("exposes the owner's email address", async () => { await expect(
await expect(action()).resolves.toEqual({ User: [{ email: 'owner@example.org' }] }) query({ query: userQuery, variables: { name: 'Owner' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorised!' }],
data: { User: [null] },
})
}) })
}) })
describe('authenticated as another user', () => { describe('authenticated', () => {
beforeEach(async () => { describe('as the owner', () => {
loginCredentials = { beforeEach(async () => {
email: 'someone@example.org', authenticatedUser = await owner.toJson()
password: 'else', })
}
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 () => { describe('as another regular user', () => {
await expect(action()).rejects.toThrow('Not Authorised!') 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 () => { describe('as a moderator', () => {
let response beforeEach(async () => {
try { authenticatedUser = await moderator.toJson()
await action() })
} catch (error) {
response = error.response.data it("throws an error and does not expose the owner's email address", async () => {
} await expect(
expect(response).toEqual({ User: [null] }) 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,
})
})
}) })
}) })
}) })

View File

@ -280,6 +280,7 @@
"columns": { "columns": {
"number": "Nr.", "number": "Nr.",
"name": "Name", "name": "Name",
"email": "E-mail",
"slug": "Slug", "slug": "Slug",
"role": "Rolle", "role": "Rolle",
"createdAt": "Erstellt am" "createdAt": "Erstellt am"

View File

@ -281,6 +281,7 @@
"columns": { "columns": {
"number": "No.", "number": "No.",
"name": "Name", "name": "Name",
"email": "E-mail",
"slug": "Slug", "slug": "Slug",
"role": "Role", "role": "Role",
"createdAt": "Created at" "createdAt": "Created at"

View File

@ -33,6 +33,11 @@
<b>{{ scope.row.name | truncate(20) }}</b> <b>{{ scope.row.name | truncate(20) }}</b>
</nuxt-link> </nuxt-link>
</template> </template>
<template slot="email" slot-scope="scope">
<a :href="`mailto:${scope.row.email}`">
<b>{{ scope.row.email }}</b>
</a>
</template>
<template slot="slug" slot-scope="scope"> <template slot="slug" slot-scope="scope">
<nuxt-link <nuxt-link
:to="{ :to="{
@ -92,6 +97,7 @@ export default {
return { return {
index: this.$t('admin.users.table.columns.number'), index: this.$t('admin.users.table.columns.number'),
name: this.$t('admin.users.table.columns.name'), name: this.$t('admin.users.table.columns.name'),
email: this.$t('admin.users.table.columns.email'),
slug: this.$t('admin.users.table.columns.slug'), slug: this.$t('admin.users.table.columns.slug'),
createdAt: this.$t('admin.users.table.columns.createdAt'), createdAt: this.$t('admin.users.table.columns.createdAt'),
contributionsCount: { contributionsCount: {
@ -128,6 +134,7 @@ export default {
id id
name name
slug slug
email
role role
createdAt createdAt
contributionsCount contributionsCount