From f1dd52f5791c2889cda0ac1f2eace85c4543edc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 20 Feb 2019 00:38:48 +0100 Subject: [PATCH] Expose yet another bug in the authorization Cannot read property id of null CC @appinteractive --- src/graphql-schema.js | 17 +++++ src/graphql-schema.spec.js | 121 +++++++++++++++++++++---------- src/middleware/userMiddleware.js | 15 ---- 3 files changed, 99 insertions(+), 54 deletions(-) diff --git a/src/graphql-schema.js b/src/graphql-schema.js index 8b5f369e0..521ac97bd 100644 --- a/src/graphql-schema.js +++ b/src/graphql-schema.js @@ -6,6 +6,7 @@ import generateJwt from './jwt/generateToken' import uuid from 'uuid/v4' import { fixUrl } from './middleware/fixImageUrlsMiddleware' import { AuthenticationError } from 'apollo-server' +import { neo4jgraphql } from 'neo4j-graphql-js' export const typeDefs = fs.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, 'schema.graphql')) @@ -161,6 +162,22 @@ export const resolvers = { // TODO: output Report compatible object return data + }, + CreatePost: async (object, params, ctx, resolveInfo) => { + const result = await neo4jgraphql(object, params, ctx, resolveInfo, false) + + const session = ctx.driver.session() + await session.run( + 'MATCH (author:User {id: $userId}), (post:Post {id: $postId}) ' + + 'MERGE (post)<-[:WROTE]-(author) ' + + 'RETURN author', { + userId: ctx.user.id, + postId: result.id + }) + session.close() + + return result } + } } diff --git a/src/graphql-schema.spec.js b/src/graphql-schema.spec.js index 21b3f3d61..e61130fe3 100644 --- a/src/graphql-schema.spec.js +++ b/src/graphql-schema.spec.js @@ -1,7 +1,18 @@ -import { request } from 'graphql-request' import { create, cleanDatabase } from './seed/factories' import jwt from 'jsonwebtoken' -import { testServerHost as host } from './jest/helpers' +import { testServerHost as host, authenticatedHeaders } from './jest/helpers' +import { GraphQLClient, request } from 'graphql-request' + +beforeEach(async () => { + await create('user', { + email: 'test@example.org', + password: '1234' + }) +}) + +afterEach(async () => { + await cleanDatabase() +}) describe('login', () => { const mutation = (params) => { @@ -14,48 +25,80 @@ describe('login', () => { }` } - describe('given an existing user', () => { - beforeEach(async () => { - await create('user', { - email: 'test@example.org', - password: '1234' + 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 + jwt.verify(token, process.env.JWT_SECRET, (err, data) => { + expect(data.email).toEqual('test@example.org') + expect(err).toBeNull() + }) }) }) - afterEach(async () => { - await cleanDatabase() + describe('with a valid email but incorrect password', () => { + it('responds with "Incorrect email address or password."', async () => { + try { + await request(host, mutation({ email: 'test@example.org', password: 'wrong' })) + } catch (error) { + expect(error.response.errors[0].message).toEqual('Incorrect email address or password.') + } + }) }) - describe('asking 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 - jwt.verify(token, process.env.JWT_SECRET, (err, data) => { - expect(data.email).toEqual('test@example.org') - expect(err).toBeNull() - }) - }) - }) - - describe('with a valid email but incorrect password', () => { - it('responds with "Incorrect email address or password."', async () => { - try { - await request(host, mutation({ email: 'test@example.org', password: 'wrong' })) - } catch (error) { - expect(error.response.errors[0].message).toEqual('Incorrect email address or password.') - } - }) - }) - - describe('with a non-existing email', () => { - it('responds with "Incorrect email address or password."', async () => { - try { - await request(host, mutation({ email: 'non-existent@example.org', password: 'wrong' })) - } catch (error) { - expect(error.response.errors[0].message).toEqual('Incorrect email address or password.') - } - }) + describe('with a non-existing email', () => { + it('responds with "Incorrect email address or password."', async () => { + try { + await request(host, mutation({ email: 'non-existent@example.org', password: 'wrong' })) + } catch (error) { + expect(error.response.errors[0].message).toEqual('Incorrect email address or password.') + } + }) + }) + }) +}) + +describe('CreatePost', () => { + describe('unauthenticated', () => { + let client + it('throws authorization error', async () => { + client = new GraphQLClient(host) + await expect(client.request(`mutation { + CreatePost( + title: "I am a post", + content: "Some content" + ) { slug } + }`)).rejects.toThrow('Not Authorised') + }) + + describe('authenticated', () => { + let headers + let response + beforeEach(async () => { + headers = await authenticatedHeaders({ email: 'test@example.org', password: '1234' }) + client = new GraphQLClient(host, { headers }) + response = await client.request(`mutation { + CreatePost( + title: "A title", + content: "Some content" + ) { title, content } + }`, { headers }) + }) + + it('creates a post', () => { + expect(response).toEqual({ CreatePost: { title: 'A title', content: 'Some content' } }) + }) + + it('assigns the authenticated user as author', async () => { + const { User } = await client.request(`{ + User(email:"test@example.org") { + contributions { + title + } + } + }`, { headers }) + expect(User).toEqual([ { contributions: [ { title: 'A title' } ] } ]) }) }) }) diff --git a/src/middleware/userMiddleware.js b/src/middleware/userMiddleware.js index 3af9ce294..55b181bc9 100644 --- a/src/middleware/userMiddleware.js +++ b/src/middleware/userMiddleware.js @@ -10,21 +10,6 @@ export default { UpdateUser: async (resolve, root, args, context, info) => { const result = await resolve(root, args, context, info) await createOrUpdateLocations(args.id, args.locationName, context.driver) - return result - }, - CreatePost: async (resolve, root, args, context, info) => { - const result = await resolve(root, args, context, info) - - const session = context.driver.session() - await session.run( - 'MATCH (author:User {id: $userId}), (post:Post {id: $postId}) ' + - 'MERGE (post)<-[:WROTE]-(author) ' + - 'RETURN author', { - userId: context.user.id, - postId: result.id - }) - session.close() - return result } }