mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
You cannot login if your account is deleted
This commit is contained in:
parent
491a626031
commit
2b83745f6b
@ -13,7 +13,7 @@ export default async (driver, authorizationHeader) => {
|
||||
}
|
||||
const session = driver.session()
|
||||
const query = `
|
||||
MATCH (user:User {id: {id} })
|
||||
MATCH (user:User {id: $id, deleted: false, disabled: false })
|
||||
RETURN user {.id, .slug, .name, .avatar, .email, .role, .disabled, .actorId}
|
||||
LIMIT 1
|
||||
`
|
||||
@ -23,7 +23,6 @@ export default async (driver, authorizationHeader) => {
|
||||
return record.get('user')
|
||||
})
|
||||
if (!currentUser) return null
|
||||
if (currentUser.disabled) return null
|
||||
return {
|
||||
token,
|
||||
...currentUser,
|
||||
|
||||
104
backend/src/jwt/decode.spec.js
Normal file
104
backend/src/jwt/decode.spec.js
Normal file
@ -0,0 +1,104 @@
|
||||
import Factory from '../seed/factories/index'
|
||||
import { getDriver } from '../bootstrap/neo4j'
|
||||
import decode from './decode'
|
||||
|
||||
const factory = Factory()
|
||||
const driver = getDriver()
|
||||
|
||||
// here is the decoded JWT token:
|
||||
// {
|
||||
// role: 'user',
|
||||
// locationName: null,
|
||||
// name: 'Jenny Rostock',
|
||||
// about: null,
|
||||
// avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sasha_shestakov/128.jpg',
|
||||
// id: 'u3',
|
||||
// email: 'user@example.org',
|
||||
// slug: 'jenny-rostock',
|
||||
// iat: 1550846680,
|
||||
// exp: 1637246680,
|
||||
// aud: 'http://localhost:3000',
|
||||
// iss: 'http://localhost:4000',
|
||||
// sub: 'u3'
|
||||
// }
|
||||
export const validAuthorizationHeader =
|
||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc'
|
||||
|
||||
afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('decode', () => {
|
||||
let authorizationHeader
|
||||
const returnsNull = async () => {
|
||||
await expect(decode(driver, authorizationHeader)).resolves.toBeNull()
|
||||
}
|
||||
|
||||
describe('given `null` as JWT Bearer token', () => {
|
||||
beforeEach(() => {
|
||||
authorizationHeader = null
|
||||
})
|
||||
it('returns null', returnsNull)
|
||||
})
|
||||
|
||||
describe('given no JWT Bearer token', () => {
|
||||
beforeEach(() => {
|
||||
authorizationHeader = undefined
|
||||
})
|
||||
it('returns null', returnsNull)
|
||||
})
|
||||
|
||||
describe('given malformed JWT Bearer token', () => {
|
||||
beforeEach(() => {
|
||||
authorizationHeader = 'blah'
|
||||
})
|
||||
it('returns null', returnsNull)
|
||||
})
|
||||
|
||||
describe('given valid JWT Bearer token', () => {
|
||||
beforeEach(() => {
|
||||
authorizationHeader = validAuthorizationHeader
|
||||
})
|
||||
it('returns null', returnsNull)
|
||||
|
||||
describe('and corresponding user in the database', () => {
|
||||
let user
|
||||
beforeEach(async () => {
|
||||
user = await factory.create('User', {
|
||||
role: 'user',
|
||||
name: 'Jenny Rostock',
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sasha_shestakov/128.jpg',
|
||||
id: 'u3',
|
||||
email: 'user@example.org',
|
||||
slug: 'jenny-rostock',
|
||||
})
|
||||
})
|
||||
|
||||
it('returns user object except email', async () => {
|
||||
await expect(decode(driver, authorizationHeader)).resolves.toMatchObject({
|
||||
role: 'user',
|
||||
name: 'Jenny Rostock',
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sasha_shestakov/128.jpg',
|
||||
id: 'u3',
|
||||
email: null,
|
||||
slug: 'jenny-rostock',
|
||||
})
|
||||
})
|
||||
|
||||
describe('but user is deleted', () => {
|
||||
beforeEach(async () => {
|
||||
await user.update({ updatedAt: new Date().toISOString(), deleted: true })
|
||||
})
|
||||
|
||||
it('returns null', returnsNull)
|
||||
})
|
||||
describe('but user is disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await user.update({ updatedAt: new Date().toISOString(), disabled: true })
|
||||
})
|
||||
|
||||
it('returns null', returnsNull)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -24,11 +24,11 @@ export default {
|
||||
// }
|
||||
const session = driver.session()
|
||||
const result = await session.run(
|
||||
'MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $userEmail})' +
|
||||
'RETURN user {.id, .slug, .name, .avatar, .encryptedPassword, .role, .disabled, email:e.email} as user LIMIT 1',
|
||||
{
|
||||
userEmail: email,
|
||||
},
|
||||
`
|
||||
MATCH (user:User {deleted: false})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $userEmail})
|
||||
RETURN user {.id, .slug, .name, .avatar, .encryptedPassword, .role, .disabled, email:e.email} as user LIMIT 1
|
||||
`,
|
||||
{ userEmail: email },
|
||||
)
|
||||
session.close()
|
||||
const [currentUser] = await result.records.map(record => {
|
||||
|
||||
@ -1,50 +1,54 @@
|
||||
import { GraphQLClient, request } from 'graphql-request'
|
||||
import jwt from 'jsonwebtoken'
|
||||
import CONFIG from './../../config'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login } from '../../jest/helpers'
|
||||
import { gql } from '../../jest/helpers'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer, { context } from '../../server'
|
||||
|
||||
const factory = Factory()
|
||||
let query
|
||||
let mutate
|
||||
let variables
|
||||
let req
|
||||
let user
|
||||
|
||||
// here is the decoded JWT token:
|
||||
// {
|
||||
// role: 'user',
|
||||
// locationName: null,
|
||||
// name: 'Jenny Rostock',
|
||||
// about: null,
|
||||
// avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sasha_shestakov/128.jpg',
|
||||
// id: 'u3',
|
||||
// email: 'user@example.org',
|
||||
// slug: 'jenny-rostock',
|
||||
// iat: 1550846680,
|
||||
// exp: 1637246680,
|
||||
// aud: 'http://localhost:3000',
|
||||
// iss: 'http://localhost:4000',
|
||||
// sub: 'u3'
|
||||
// }
|
||||
const jennyRostocksHeaders = {
|
||||
authorization:
|
||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc',
|
||||
}
|
||||
// This is a bearer token of a user with id `u3`:
|
||||
const userBearerToken =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsIm5hbWUiOiJKZW5ueSBSb3N0b2NrIiwiZGlzYWJsZWQiOmZhbHNlLCJhdmF0YXIiOiJodHRwczovL3MzLmFtYXpvbmF3cy5jb20vdWlmYWNlcy9mYWNlcy90d2l0dGVyL2tleXVyaTg1LzEyOC5qcGciLCJpZCI6InUzIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUub3JnIiwic2x1ZyI6Implbm55LXJvc3RvY2siLCJpYXQiOjE1Njc0NjgyMDIsImV4cCI6MTU2NzU1NDYwMiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0MDAwIiwic3ViIjoidTMifQ.RkmrdJDL1kIqGnMWUBl_sJJ4grzfpTEGdT6doMsbLW8'
|
||||
|
||||
// This is a bearer token of a user with id `u2`:
|
||||
const moderatorBearerToken =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoibW9kZXJhdG9yIiwibmFtZSI6IkJvYiBkZXIgQmF1bWVpc3RlciIsImRpc2FibGVkIjpmYWxzZSwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9hbmRyZXdvZmZpY2VyLzEyOC5qcGciLCJpZCI6InUyIiwiZW1haWwiOiJtb2RlcmF0b3JAZXhhbXBsZS5vcmciLCJzbHVnIjoiYm9iLWRlci1iYXVtZWlzdGVyIiwiaWF0IjoxNTY3NDY4MDUwLCJleHAiOjE1Njc1NTQ0NTAsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6MzAwMCIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6NDAwMCIsInN1YiI6InUyIn0.LdVFPKqIcoY0a7_kFZSTgnc8NzmZD7CrR3vkWLSqedM'
|
||||
|
||||
const disable = async id => {
|
||||
const moderatorParams = { email: 'moderator@example.org', role: 'moderator', password: '1234' }
|
||||
const asModerator = Factory()
|
||||
await asModerator.create('User', moderatorParams)
|
||||
await asModerator.authenticateAs(moderatorParams)
|
||||
await asModerator.mutate('mutation($id: ID!) { disable(id: $id) }', { id })
|
||||
await factory.create('User', { id: 'u2', role: 'moderator' })
|
||||
req = { headers: { authorization: `Bearer ${moderatorBearerToken}` } }
|
||||
await mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
disable(id: $id)
|
||||
}
|
||||
`,
|
||||
variables: { id },
|
||||
})
|
||||
req = { headers: {} }
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||
name: 'Matilde Hermiston',
|
||||
slug: 'matilde-hermiston',
|
||||
role: 'user',
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
beforeEach(() => {
|
||||
user = null
|
||||
req = { headers: {} }
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
const { server } = createServer({
|
||||
context: () => {
|
||||
// One of the rare occasions where we test
|
||||
// the actual `context` implementation here
|
||||
return context({ req })
|
||||
},
|
||||
})
|
||||
query = createTestClient(server).query
|
||||
mutate = createTestClient(server).mutate
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
@ -52,261 +56,266 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
describe('isLoggedIn', () => {
|
||||
const query = '{ isLoggedIn }'
|
||||
const isLoggedInQuery = gql`
|
||||
{
|
||||
isLoggedIn
|
||||
}
|
||||
`
|
||||
const respondsWith = async expected => {
|
||||
await expect(query({ query: isLoggedInQuery })).resolves.toMatchObject(expected)
|
||||
}
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('returns false', async () => {
|
||||
await expect(request(host, query)).resolves.toEqual({
|
||||
isLoggedIn: false,
|
||||
})
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('with malformed JWT Bearer token', () => {
|
||||
const headers = { authorization: 'blah' }
|
||||
const client = new GraphQLClient(host, { headers })
|
||||
|
||||
it('returns false', async () => {
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: false,
|
||||
})
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
user = await factory.create('User', { id: 'u3' })
|
||||
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
|
||||
})
|
||||
})
|
||||
|
||||
describe('with valid JWT Bearer token', () => {
|
||||
const client = new GraphQLClient(host, { headers: jennyRostocksHeaders })
|
||||
it('returns true', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: true } })
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: false,
|
||||
describe('but user is disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await disable('u3')
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('and a corresponding user in the database', () => {
|
||||
describe('user is enabled', () => {
|
||||
it('returns true', async () => {
|
||||
// see the decoded token above
|
||||
await factory.create('User', { id: 'u3' })
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: true,
|
||||
})
|
||||
})
|
||||
describe('but user is deleted', () => {
|
||||
beforeEach(async () => {
|
||||
await user.update({ updatedAt: new Date().toISOString(), deleted: true })
|
||||
})
|
||||
|
||||
describe('user is disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { id: 'u3' })
|
||||
await disable('u3')
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: false,
|
||||
})
|
||||
})
|
||||
it('returns false', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('currentUser', () => {
|
||||
const query = `{
|
||||
currentUser {
|
||||
id
|
||||
slug
|
||||
name
|
||||
avatar
|
||||
email
|
||||
role
|
||||
const currentUserQuery = gql`
|
||||
{
|
||||
currentUser {
|
||||
id
|
||||
slug
|
||||
name
|
||||
avatar
|
||||
email
|
||||
role
|
||||
}
|
||||
}
|
||||
}`
|
||||
`
|
||||
|
||||
const respondsWith = async expected => {
|
||||
await expect(query({ query: currentUserQuery, variables })).resolves.toMatchObject(expected)
|
||||
}
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('returns null', async () => {
|
||||
const expected = { currentUser: null }
|
||||
await expect(request(host, query)).resolves.toEqual(expected)
|
||||
await respondsWith({ data: { currentUser: null } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('with valid JWT Bearer Token', () => {
|
||||
let client
|
||||
let headers
|
||||
|
||||
describe('but no corresponding user in the database', () => {
|
||||
beforeEach(async () => {
|
||||
client = new GraphQLClient(host, { headers: jennyRostocksHeaders })
|
||||
})
|
||||
|
||||
it('returns null', async () => {
|
||||
const expected = { currentUser: null }
|
||||
await expect(client.request(query)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
describe('and corresponding user in the database', () => {
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
await factory.create('User', {
|
||||
id: 'u3',
|
||||
// the `id` is the only thing that has to match the decoded JWT bearer token
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
email: 'test@example.org',
|
||||
name: 'Matilde Hermiston',
|
||||
slug: 'matilde-hermiston',
|
||||
role: 'user',
|
||||
})
|
||||
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
|
||||
})
|
||||
|
||||
it('returns the whole user object', async () => {
|
||||
const expected = {
|
||||
currentUser: {
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
email: 'test@example.org',
|
||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||
name: 'Matilde Hermiston',
|
||||
slug: 'matilde-hermiston',
|
||||
role: 'user',
|
||||
data: {
|
||||
currentUser: {
|
||||
id: 'u3',
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
email: 'test@example.org',
|
||||
name: 'Matilde Hermiston',
|
||||
slug: 'matilde-hermiston',
|
||||
role: 'user',
|
||||
},
|
||||
},
|
||||
}
|
||||
await expect(client.request(query)).resolves.toEqual(expected)
|
||||
await respondsWith(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('login', () => {
|
||||
const mutation = params => {
|
||||
const { email, password } = params
|
||||
return `
|
||||
mutation {
|
||||
login(email:"${email}", password:"${password}")
|
||||
}`
|
||||
const loginMutation = gql`
|
||||
mutation($email: String!, $password: String!) {
|
||||
login(email: $email, password: $password)
|
||||
}
|
||||
`
|
||||
|
||||
const respondsWith = async expected => {
|
||||
await expect(mutate({ mutation: loginMutation, variables })).resolves.toMatchObject(expected)
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
variables = { email: 'test@example.org', password: '1234' }
|
||||
user = await factory.create('User', {
|
||||
...variables,
|
||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||
})
|
||||
})
|
||||
|
||||
describe('ask for a `token`', () => {
|
||||
describe('with valid email/password combination', () => {
|
||||
it('responds with a JWT token', async () => {
|
||||
const data = await request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
}),
|
||||
)
|
||||
const token = data.login
|
||||
describe('with a valid email/password combination', () => {
|
||||
it('responds with a JWT bearer token', async done => {
|
||||
const {
|
||||
data: { login: token },
|
||||
} = await mutate({ mutation: loginMutation, variables })
|
||||
jwt.verify(token, CONFIG.JWT_SECRET, (err, data) => {
|
||||
expect(data.email).toEqual('test@example.org')
|
||||
expect(err).toBeNull()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
describe('but user account is deleted', () => {
|
||||
beforeEach(async () => {
|
||||
await user.update({ updatedAt: new Date().toISOString(), deleted: true })
|
||||
})
|
||||
|
||||
it('responds with "Incorrect email address or password."', async () => {
|
||||
await respondsWith({
|
||||
data: null,
|
||||
errors: [{ message: 'Incorrect email address or password.' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('but user account is disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await disable('acb2d923-f3af-479e-9f00-61b12e864666')
|
||||
})
|
||||
|
||||
it('responds with "Your account has been disabled."', async () => {
|
||||
await respondsWith({
|
||||
data: null,
|
||||
errors: [{ message: 'Your account has been disabled.' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid email/password but user is disabled', () => {
|
||||
it('responds with "Your account has been disabled."', async () => {
|
||||
await disable('acb2d923-f3af-479e-9f00-61b12e864666')
|
||||
await expect(
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Your account has been disabled.')
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a valid email but incorrect password', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, email: 'test@example.org', password: 'wrong' }
|
||||
})
|
||||
|
||||
it('responds with "Incorrect email address or password."', async () => {
|
||||
await expect(
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'test@example.org',
|
||||
password: 'wrong',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Incorrect email address or password.')
|
||||
await respondsWith({
|
||||
errors: [{ message: 'Incorrect email address or password.' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a non-existing email', () => {
|
||||
beforeEach(() => {
|
||||
variables = {
|
||||
...variables,
|
||||
email: 'non-existent@example.org',
|
||||
password: '1234',
|
||||
}
|
||||
})
|
||||
|
||||
it('responds with "Incorrect email address or password."', async () => {
|
||||
await expect(
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'non-existent@example.org',
|
||||
password: 'wrong',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Incorrect email address or password.')
|
||||
await respondsWith({
|
||||
errors: [{ message: 'Incorrect email address or password.' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('change password', () => {
|
||||
let headers
|
||||
let client
|
||||
const changePasswordMutation = gql`
|
||||
mutation($oldPassword: String!, $newPassword: String!) {
|
||||
changePassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
||||
}
|
||||
`
|
||||
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
const mutation = params => {
|
||||
const { oldPassword, newPassword } = params
|
||||
return `
|
||||
mutation {
|
||||
changePassword(oldPassword:"${oldPassword}", newPassword:"${newPassword}")
|
||||
}`
|
||||
const respondsWith = async expected => {
|
||||
await expect(mutate({ mutation: changePasswordMutation, variables })).resolves.toMatchObject(
|
||||
expected,
|
||||
)
|
||||
}
|
||||
|
||||
describe('should be authenticated before changing password', () => {
|
||||
beforeEach(async () => {
|
||||
variables = { ...variables, oldPassword: 'what', newPassword: 'ever' }
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws "Not Authorised!"', async () => {
|
||||
await expect(
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
oldPassword: '1234',
|
||||
newPassword: '1234',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Not Authorised!')
|
||||
await respondsWith({ errors: [{ message: 'Not Authorised!' }] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('old and new password should not match', () => {
|
||||
it('responds with "Old password and new password should be different"', async () => {
|
||||
await expect(
|
||||
client.request(
|
||||
mutation({
|
||||
oldPassword: '1234',
|
||||
newPassword: '1234',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Old password and new password should be different')
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { id: 'u3' })
|
||||
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
|
||||
})
|
||||
})
|
||||
describe('old password === new password', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, oldPassword: '1234', newPassword: '1234' }
|
||||
})
|
||||
|
||||
describe('incorrect old password', () => {
|
||||
it('responds with "Old password isn\'t valid"', async () => {
|
||||
await expect(
|
||||
client.request(
|
||||
mutation({
|
||||
oldPassword: 'notOldPassword',
|
||||
newPassword: '12345',
|
||||
}),
|
||||
),
|
||||
).rejects.toThrow('Old password is not correct')
|
||||
it('responds with "Old password and new password should be different"', async () => {
|
||||
await respondsWith({
|
||||
errors: [{ message: 'Old password and new password should be different' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('correct password', () => {
|
||||
it('changes the password if given correct credentials "', async () => {
|
||||
const response = await client.request(
|
||||
mutation({
|
||||
describe('incorrect old password', () => {
|
||||
beforeEach(() => {
|
||||
variables = {
|
||||
...variables,
|
||||
oldPassword: 'notOldPassword',
|
||||
newPassword: '12345',
|
||||
}
|
||||
})
|
||||
|
||||
it('responds with "Old password isn\'t valid"', async () => {
|
||||
await respondsWith({ errors: [{ message: 'Old password is not correct' }] })
|
||||
})
|
||||
})
|
||||
|
||||
describe('correct password', () => {
|
||||
beforeEach(() => {
|
||||
variables = {
|
||||
...variables,
|
||||
oldPassword: '1234',
|
||||
newPassword: '12345',
|
||||
}),
|
||||
)
|
||||
await expect(response).toEqual(
|
||||
expect.objectContaining({
|
||||
changePassword: expect.any(String),
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('changes the password if given correct credentials "', async () => {
|
||||
await respondsWith({ data: { changePassword: expect.any(String) } })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -18,20 +18,22 @@ Object.entries(requiredConfigs).map(entry => {
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
export const context = async ({ req }) => {
|
||||
const user = await decode(driver, req.headers.authorization)
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user,
|
||||
req,
|
||||
cypherParams: {
|
||||
currentUserId: user ? user.id : null,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const createServer = options => {
|
||||
const defaults = {
|
||||
context: async ({ req }) => {
|
||||
const user = await decode(driver, req.headers.authorization)
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user,
|
||||
req,
|
||||
cypherParams: {
|
||||
currentUserId: user ? user.id : null,
|
||||
},
|
||||
}
|
||||
},
|
||||
context,
|
||||
schema: middleware(schema),
|
||||
debug: !!CONFIG.DEBUG,
|
||||
tracing: !!CONFIG.DEBUG,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user