Fix legacy permissions for M2O fields in GraphQL (#16430)

* Remove relation for legacy permission without allowed field

* Remove deprecated formatError and improve error handling

* Add unit test

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
ian
2022-11-18 00:04:56 +08:00
committed by GitHub
parent ed53673b0f
commit 0ac08da7ed
4 changed files with 64 additions and 3 deletions

View File

@@ -6,7 +6,6 @@ import {
execute,
ExecutionResult,
FieldNode,
formatError,
FormattedExecutionResult,
FragmentDefinitionNode,
GraphQLBoolean,
@@ -41,6 +40,7 @@ import {
toInputObjectType,
ObjectTypeComposerFieldConfigDefinition,
} from 'graphql-compose';
import processError from './utils/process-error';
import { Knex } from 'knex';
import { flatten, get, mapKeys, merge, omit, pick, set, transform, uniq } from 'lodash';
import ms from 'ms';
@@ -159,7 +159,7 @@ export class GraphQLService {
const formattedResult: FormattedExecutionResult = {
...result,
errors: result.errors?.map(formatError),
errors: result.errors?.map((error) => processError(this.accountability, error)),
};
return formattedResult;

View File

@@ -0,0 +1,34 @@
import { GraphQLError } from 'graphql';
import { describe, expect, test } from 'vitest';
import processError from './process-error';
describe('GraphQL processError util', () => {
const sampleError = new GraphQLError('An error message', { path: ['test_collection'] });
const redactedError = {
message: 'An unexpected error occurred.',
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
};
test('returns redacted error when unauthenticated', () => {
expect(processError(null, sampleError)).toEqual(expect.objectContaining(redactedError));
});
test('returns redacted error when authenticated but not an admin', () => {
expect(processError({ role: 'd674e22b-f405-48ba-9958-9a7bd16a1aa9' }, sampleError)).toEqual(
expect.objectContaining(redactedError)
);
});
test('returns original error when authenticated and is an admin', () => {
expect(processError({ role: 'd674e22b-f405-48ba-9958-9a7bd16a1aa9', admin: true }, sampleError)).toEqual(
expect.objectContaining({
...sampleError,
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
})
);
});
});

View File

@@ -0,0 +1,25 @@
import logger from '../../../logger';
import { GraphQLError, GraphQLFormattedError } from 'graphql';
import { Accountability } from '@directus/shared/types';
const processError = (accountability: Accountability | null, error: Readonly<GraphQLError>): GraphQLFormattedError => {
logger.error(error);
if (accountability?.admin === true) {
return {
...error,
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
};
} else {
return {
message: 'An unexpected error occurred.',
extensions: {
code: 'INTERNAL_SERVER_ERROR',
},
};
}
};
export default processError;

View File

@@ -85,7 +85,9 @@ export function reduceSchema(
if (
relation.related_collection &&
Object.keys(allowedFieldsInCollection).includes(relation.related_collection) === false
(Object.keys(allowedFieldsInCollection).includes(relation.related_collection) === false ||
// Ignore legacy permissions with an empty fields array
allowedFieldsInCollection[relation.related_collection].length === 0)
) {
collectionsAllowed = false;
}