diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 74fc3b6f76..dd061ea894 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -3198,8 +3198,8 @@ describe('ParseGraphQLServer', () => { }); expect(logOut.data.users.logOut).toBeTruthy(); - await expectAsync( - apolloClient.query({ + try { + await apolloClient.query({ query: gql` query GetCurrentUser { users { @@ -3214,8 +3214,111 @@ describe('ParseGraphQLServer', () => { 'X-Parse-Session-Token': sessionToken, }, }, - }) - ).toBeRejected(); + }); + fail('should not retrieve current user due to session token'); + } catch (err) { + const { statusCode, result } = err.networkError; + expect(statusCode).toBe(400); + expect(result).toEqual({ + code: 209, + error: 'Invalid session token', + }); + } + }); + }); + + describe('Session Token', () => { + it('should fail due to invalid session token', async () => { + try { + await apolloClient.query({ + query: gql` + query GetCurrentUser { + users { + me { + username + } + } + } + `, + context: { + headers: { + 'X-Parse-Session-Token': 'foo', + }, + }, + }); + fail('should not retrieve current user due to session token'); + } catch (err) { + const { statusCode, result } = err.networkError; + expect(statusCode).toBe(400); + expect(result).toEqual({ + code: 209, + error: 'Invalid session token', + }); + } + }); + + it('should fail due to empty session token', async () => { + try { + await apolloClient.query({ + query: gql` + query GetCurrentUser { + users { + me { + username + } + } + } + `, + context: { + headers: { + 'X-Parse-Session-Token': '', + }, + }, + }); + fail('should not retrieve current user due to session token'); + } catch (err) { + const { graphQLErrors } = err; + expect(graphQLErrors.length).toBe(1); + expect(graphQLErrors[0].message).toBe('Invalid session token'); + } + }); + + it('should find a user and fail due to empty session token', async () => { + const car = new Parse.Object('Car'); + await car.save(); + + await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear(); + + try { + await apolloClient.query({ + query: gql` + query GetCurrentUser { + users { + me { + username + } + } + objects { + findCar { + results { + objectId + } + } + } + } + `, + context: { + headers: { + 'X-Parse-Session-Token': '', + }, + }, + }); + fail('should not retrieve current user due to session token'); + } catch (err) { + const { graphQLErrors } = err; + expect(graphQLErrors.length).toBe(1); + expect(graphQLErrors[0].message).toBe('Invalid session token'); + } }); }); diff --git a/src/GraphQL/ParseGraphQLSchema.js b/src/GraphQL/ParseGraphQLSchema.js index 7664eef525..e298273bb2 100644 --- a/src/GraphQL/ParseGraphQLSchema.js +++ b/src/GraphQL/ParseGraphQLSchema.js @@ -1,6 +1,5 @@ import Parse from 'parse/node'; import { GraphQLSchema, GraphQLObjectType } from 'graphql'; -import { ApolloError } from 'apollo-server-core'; import requiredParameter from '../requiredParameter'; import * as defaultGraphQLTypes from './loaders/defaultGraphQLTypes'; import * as parseClassTypes from './loaders/parseClassTypes'; @@ -8,6 +7,7 @@ import * as parseClassQueries from './loaders/parseClassQueries'; import * as parseClassMutations from './loaders/parseClassMutations'; import * as defaultGraphQLQueries from './loaders/defaultGraphQLQueries'; import * as defaultGraphQLMutations from './loaders/defaultGraphQLMutations'; +import { toGraphQLError } from './parseGraphQLUtils'; class ParseGraphQLSchema { constructor(databaseController, log) { @@ -100,17 +100,12 @@ class ParseGraphQLSchema { } handleError(error) { - let code, message; if (error instanceof Parse.Error) { this.log.error('Parse error: ', error); - code = error.code; - message = error.message; } else { this.log.error('Uncaught internal server error.', error, error.stack); - code = Parse.Error.INTERNAL_SERVER_ERROR; - message = 'Internal server error.'; } - throw new ApolloError(message, code); + throw toGraphQLError(error); } } diff --git a/src/GraphQL/ParseGraphQLServer.js b/src/GraphQL/ParseGraphQLServer.js index 5cb4c1c747..d9ac8f419a 100644 --- a/src/GraphQL/ParseGraphQLServer.js +++ b/src/GraphQL/ParseGraphQLServer.js @@ -5,7 +5,7 @@ import { graphqlExpress } from 'apollo-server-express/dist/expressApollo'; import { renderPlaygroundPage } from '@apollographql/graphql-playground-html'; import { execute, subscribe } from 'graphql'; import { SubscriptionServer } from 'subscriptions-transport-ws'; -import { handleParseHeaders } from '../middlewares'; +import { handleParseErrors, handleParseHeaders } from '../middlewares'; import requiredParameter from '../requiredParameter'; import defaultLogger from '../logger'; import { ParseGraphQLSchema } from './ParseGraphQLSchema'; @@ -55,6 +55,7 @@ class ParseGraphQLServer { app.use(this.config.graphQLPath, corsMiddleware()); app.use(this.config.graphQLPath, bodyParser.json()); app.use(this.config.graphQLPath, handleParseHeaders); + app.use(this.config.graphQLPath, handleParseErrors); app.use( this.config.graphQLPath, graphqlExpress(async req => await this._getGraphQLOptions(req)) diff --git a/src/GraphQL/parseGraphQLUtils.js b/src/GraphQL/parseGraphQLUtils.js new file mode 100644 index 0000000000..79f7192e53 --- /dev/null +++ b/src/GraphQL/parseGraphQLUtils.js @@ -0,0 +1,14 @@ +import Parse from 'parse/node'; +import { ApolloError } from 'apollo-server-core'; + +export function toGraphQLError(error) { + let code, message; + if (error instanceof Parse.Error) { + code = error.code; + message = error.message; + } else { + code = Parse.Error.INTERNAL_SERVER_ERROR; + message = 'Internal server error'; + } + return new ApolloError(message, code); +}