Skip to content

Commit

Permalink
Rename originalInput to inputData in access control functions (#6680)
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie authored Sep 30, 2021
1 parent 3b510c2 commit 5c0163e
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 55 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-zebras-act.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-next/keystone': major
---

Renamed the `originalInput` argument for access control functions to `inputData`.
42 changes: 21 additions & 21 deletions docs/pages/docs/apis/access-control.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ export default config({
ListKey: list({
access: {
item: {
create: ({ session, context, listKey, operation, originalInput }) => true,
update: ({ session, context, listKey, operation, originalInput, item }) => true,
create: ({ session, context, listKey, operation, inputData }) => true,
update: ({ session, context, listKey, operation, inputData, item }) => true,
delete: ({ session, context, listKey, operation, item }) => true,
}
},
Expand All @@ -163,14 +163,14 @@ Applying access control after fetching items would lead to inconsistent paginati

List-level access control functions are passed a collection of arguments which can be used to determine whether the operation is allowed.

| Argument | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------- |
| `session` | The current session object. See the [Sessions API](./session) for details. |
| `context` | The [`KeystoneContext`](./context) object of the originating GraphQL operation. |
| `listKey` | The key of the list being operated on. |
| `operation` | The operation being performed (`'query'`, `'create'`, `'update'`, `'delete'`). |
| `originalInput` | For `create` and `update` operations, this is the value of `data` passed into the mutation. (Item level only) |
| `item` | The existing item being updated/deleted in `update` and `delete` operations. (Item level only) |
| Argument | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------- |
| `session` | The current session object. See the [Sessions API](./session) for details. |
| `context` | The [`KeystoneContext`](./context) object of the originating GraphQL operation. |
| `listKey` | The key of the list being operated on. |
| `operation` | The operation being performed (`'query'`, `'create'`, `'update'`, `'delete'`). |
| `inputData` | For `create` and `update` operations, this is the value of `data` passed into the mutation. (Item level only) |
| `item` | The existing item being updated/deleted in `update` and `delete` operations. (Item level only) |

## Field Access Control

Expand Down Expand Up @@ -205,8 +205,8 @@ export default config({
fieldName: text({
access: {
read: ({ session, context, listKey, fieldKey, operation, item }) => true,
create: ({ session, context, listKey, fieldKey, operation, originalInput }) => true,
update: ({ session, context, listKey, fieldKey, operation, originalInput, item }) => true,
create: ({ session, context, listKey, fieldKey, operation, inputData }) => true,
update: ({ session, context, listKey, fieldKey, operation, inputData, item }) => true,
},
}),
},
Expand All @@ -221,14 +221,14 @@ export default config({

Field-level access control functions are passed a collection of arguments which can be used to determine whether the operation is allowed.

| Argument | Description |
| --------------- | ------------------------------------------------------------------------------------------- |
| `session` | The current session object. See the [Sessions API](./session) for details. |
| `context` | The [`KeystoneContext`](./context) object of the originating GraphQL operation. |
| `listKey` | The key of the list being operated on. |
| `fieldKey` | The key of the field being operated on. |
| `operation` | The operation being performed (`'read'`, `'create'`, `'update'`). |
| `originalInput` | For `create` and `update` operations, this is the value of `data` passed into the mutation. |
| `item` | The existing item being read/updated in `read` and `update` operations. |
| Argument | Description |
| ----------- | ------------------------------------------------------------------------------------------- |
| `session` | The current session object. See the [Sessions API](./session) for details. |
| `context` | The [`KeystoneContext`](./context) object of the originating GraphQL operation. |
| `listKey` | The key of the list being operated on. |
| `fieldKey` | The key of the field being operated on. |
| `operation` | The operation being performed (`'read'`, `'create'`, `'update'`). |
| `inputData` | For `create` and `update` operations, this is the value of `data` passed into the mutation. |
| `item` | The existing item being read/updated in `read` and `update` operations. |

export default ({ children }) => <Markdown description="Complete reference docs for Keystone’s Access Control API. Configure who can read, create, update, and delete items in your Keystone system.">{children}</Markdown>;
18 changes: 8 additions & 10 deletions packages/keystone/src/lib/core/mutations/access-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export async function getAccessControlledItemForUpdate(
context: KeystoneContext,
uniqueWhere: UniquePrismaFilter,
accessFilters: boolean | InputFilter,
originalInput: Record<string, any>
inputData: Record<string, any>
) {
const operation = 'update' as const;
// Apply the filter access control. Will throw an accessDeniedError if the item isn't found.
Expand All @@ -105,7 +105,7 @@ export async function getAccessControlledItemForUpdate(
listKey: list.listKey,
context,
item,
originalInput,
inputData,
};

// List level 'item' access control
Expand Down Expand Up @@ -142,7 +142,7 @@ export async function getAccessControlledItemForUpdate(
const nonBooleans = [];
const fieldsDenied = [];
const accessErrors = [];
for (const fieldKey of Object.keys(originalInput)) {
for (const fieldKey of Object.keys(inputData)) {
let result;
try {
result =
Expand Down Expand Up @@ -185,7 +185,7 @@ export async function getAccessControlledItemForUpdate(
export async function applyAccessControlForCreate(
list: InitialisedList,
context: KeystoneContext,
originalInput: Record<string, unknown>
inputData: Record<string, unknown>
) {
const operation = 'create' as const;

Expand All @@ -196,7 +196,7 @@ export async function applyAccessControlForCreate(
session: context.session,
listKey: list.listKey,
context,
originalInput,
inputData,
};

// List level 'item' access control
Expand Down Expand Up @@ -224,9 +224,7 @@ export async function applyAccessControlForCreate(

if (!result) {
throw accessDeniedError(
`You cannot perform the '${operation}' operation on the item '${JSON.stringify(
originalInput
)}'.`
`You cannot perform the '${operation}' operation on the item '${JSON.stringify(inputData)}'.`
);
}

Expand All @@ -235,7 +233,7 @@ export async function applyAccessControlForCreate(
const nonBooleans = [];
const fieldsDenied = [];
const accessErrors = [];
for (const fieldKey of Object.keys(originalInput)) {
for (const fieldKey of Object.keys(inputData)) {
let result;
try {
result =
Expand Down Expand Up @@ -267,7 +265,7 @@ export async function applyAccessControlForCreate(
if (fieldsDenied.length) {
throw accessDeniedError(
`You cannot perform the '${operation}' operation on the item '${JSON.stringify(
originalInput
inputData
)}'. You cannot ${operation} the fields ${JSON.stringify(fieldsDenied)}.`
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/keystone/src/types/config/access-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type CreateItemAccessArgs<GeneratedListTypes extends BaseGeneratedListTypes> = B
/**
* The input passed in from the GraphQL API
*/
originalInput: GeneratedListTypes['inputs']['create'];
inputData: GeneratedListTypes['inputs']['create'];
};

export type CreateListItemAccessControl<GeneratedListTypes extends BaseGeneratedListTypes> = (
Expand All @@ -43,7 +43,7 @@ type UpdateItemAccessArgs<GeneratedListTypes extends BaseGeneratedListTypes> = B
/**
* The input passed in from the GraphQL API
*/
originalInput: GeneratedListTypes['inputs']['update'];
inputData: GeneratedListTypes['inputs']['update'];
};

export type UpdateListItemAccessControl<GeneratedListTypes extends BaseGeneratedListTypes> = (
Expand Down
16 changes: 8 additions & 8 deletions tests/api-tests/access-control/mutations-field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ const runner = setupTestRunner({
name: text({
access: {
read: () => true,
create: ({ originalInput }: any) => {
if (Array.isArray(originalInput)) {
return !originalInput.some(item => item.data.name === 'bad');
create: ({ inputData }: any) => {
if (Array.isArray(inputData)) {
return !inputData.some(item => item.data.name === 'bad');
} else {
return (originalInput as any).name !== 'bad';
return (inputData as any).name !== 'bad';
}
},
update: ({ originalInput }: any) => {
if (Array.isArray(originalInput)) {
return !originalInput.some(item => item.data.name === 'bad');
update: ({ inputData }: any) => {
if (Array.isArray(inputData)) {
return !inputData.some(item => item.data.name === 'bad');
} else {
return (originalInput as any).name !== 'bad';
return (inputData as any).name !== 'bad';
}
},
},
Expand Down
8 changes: 4 additions & 4 deletions tests/api-tests/access-control/mutations-list-item.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ const runner = setupTestRunner({
fields: { name: text({ isFilterable: true, isOrderable: true }) },
access: {
item: {
create: ({ originalInput }) => {
return originalInput.name !== 'bad';
create: ({ inputData }) => {
return inputData.name !== 'bad';
},
update: ({ originalInput }) => {
return originalInput.name !== 'bad';
update: ({ inputData }) => {
return inputData.name !== 'bad';
},
delete: async ({ item }) => {
return !item.name.startsWith('no delete');
Expand Down
20 changes: 10 additions & 10 deletions tests/api-tests/hooks/auth-hooks.test.skip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,52 +27,52 @@ const runner = setupTestRunner({
// type: PasswordAuthStrategy,
// list: 'User',
// hooks: {
// resolveAuthInput: ({ context, operation, originalInput }: any) => {
// resolveAuthInput: ({ context, operation, inputData }: any) => {
// expect(context).not.toBe(undefined);
// expect(operation).toEqual('authenticate');

// if (originalInput.email === 'triggerBadResolve') {
// if (inputData.email === 'triggerBadResolve') {
// return undefined;
// }
// if (originalInput.email === 'fixOnResolve') {
// if (inputData.email === 'fixOnResolve') {
// return { email: 'test@example.com', password: 'testing123' };
// }
// return originalInput;
// return inputData;
// },
// validateAuthInput: ({
// resolvedData,
// context,
// originalInput,
// inputData,
// operation,
// addValidationError,
// }: any) => {
// expect(context).not.toBe(undefined);
// expect(operation).toEqual('authenticate');
// expect(originalInput).not.toBe(undefined);
// expect(inputData).not.toBe(undefined);
// if (resolvedData.email === 'invalid') {
// addValidationError('INVALID EMAIL');
// }
// },
// beforeAuth: ({ resolvedData, context, originalInput, operation }: any) => {
// beforeAuth: ({ resolvedData, context, inputData, operation }: any) => {
// expect(context).not.toBe(undefined);
// expect(operation).toEqual('authenticate');
// expect(resolvedData.email).not.toBe(undefined);
// expect(resolvedData.password).not.toBe(undefined);
// expect(originalInput).not.toBe(undefined);
// expect(inputData).not.toBe(undefined);
// },
// afterAuth: ({
// resolvedData,
// context,
// operation,
// originalInput,
// inputData,
// item,
// success,
// message,
// token,
// }: any) => {
// expect(context).not.toBe(undefined);
// expect(operation).toEqual('authenticate');
// expect(originalInput).not.toBe(undefined);
// expect(inputData).not.toBe(undefined);
// if (resolvedData.email === '[email protected]') {
// expect(item.id).not.toBe(undefined);
// expect(success).toEqual(true);
Expand Down

0 comments on commit 5c0163e

Please sign in to comment.