Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie committed Aug 31, 2021
1 parent 58d276a commit 4b8cd2b
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 75 deletions.
2 changes: 1 addition & 1 deletion packages/keystone/src/lib/core/access-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ export async function getAccessFilters(

// Check the mutation access as well
const access = list.access.filter[operation];
// @ts-ignore
const mutationAccess: boolean | InputFilter =
// @ts-ignore
typeof access === 'function' ? await access(args) : access;
if (mutationAccess === false) {
return false;
Expand Down
142 changes: 71 additions & 71 deletions packages/keystone/src/lib/core/mutations/access-control.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ItemRootValue, KeystoneContext } from '../../../types';
import { KeystoneContext } from '../../../types';
import { validateFieldAccessControl } from '../access-control';
import { accessDeniedError } from '../graphql-errors';
import { mapUniqueWhereToWhere } from '../queries/resolvers';
Expand Down Expand Up @@ -30,20 +30,41 @@ async function getFilteredItem(
return item;
}

export async function getAccessControlledItemForMutation(
export async function getAccessControlledItemForDelete(
list: InitialisedList,
context: KeystoneContext,
operation: 'delete' | 'update' | 'create',
accessFilters?: boolean | InputFilter,
uniqueWhere?: UniquePrismaFilter,
originalInput?: Record<string, any>
uniqueWhere: UniquePrismaFilter,
accessFilters: boolean | InputFilter
) {
let item;
if (operation === 'update' || operation === 'delete') {
// Apply the filter access control. Will throw an accessDeniedError if the item isn't found.
item = await getFilteredItem(list, context, uniqueWhere!, accessFilters);
const operation = 'delete' as const;
// Apply the filter access control. Will throw an accessDeniedError if the item isn't found.
const item = await getFilteredItem(list, context, uniqueWhere!, accessFilters);

// Apply item level access control
const access = list.access.item[operation];
const args = { operation, session: context.session, listKey: list.listKey, context, item };

// List level 'item' access control
if (!(await access(args))) {
throw accessDeniedError();
}

// No field level access control for delete

return item;
}

export async function getAccessControlledItemForUpdate(
list: InitialisedList,
context: KeystoneContext,
uniqueWhere: UniquePrismaFilter,
accessFilters: boolean | InputFilter,
originalInput: Record<string, any>
) {
const operation = 'update' as const;
// Apply the filter access control. Will throw an accessDeniedError if the item isn't found.
const item = await getFilteredItem(list, context, uniqueWhere!, accessFilters);

// Apply item level access control
const access = list.access.item[operation];
const args = {
Expand All @@ -54,83 +75,62 @@ export async function getAccessControlledItemForMutation(
item,
originalInput,
};
if (operation === 'update' || operation === 'delete') {
args.item = item;
}
if (operation === 'create' || operation === 'update') {
args.originalInput = originalInput;
}

// List level 'item' access control
if (!(await access(args))) {
throw accessDeniedError();
}

if (operation === 'update' || operation === 'create') {
// Field level 'item' access control
const results = await Promise.all(
Object.keys(originalInput!).map(fieldKey =>
validateFieldAccessControl({
access: list.fields[fieldKey].access[operation],
args: { ...args, fieldKey },
})
)
);

if (results.some(canAccess => !canAccess)) {
throw accessDeniedError();
}
}
// Field level 'item' access control
const results = await Promise.all(
Object.keys(originalInput!).map(fieldKey =>
validateFieldAccessControl({
access: list.fields[fieldKey].access[operation],
args: { ...args, fieldKey },
})
)
);

if (operation === 'update' || operation === 'delete') {
return item;
if (results.some(canAccess => !canAccess)) {
throw accessDeniedError();
}
}

export async function getAccessControlledItemForDelete(
list: InitialisedList,
context: KeystoneContext,
uniqueWhere: UniquePrismaFilter,
accessFilters: boolean | InputFilter
): Promise<ItemRootValue> {
return getAccessControlledItemForMutation(
list,
context,
'delete',
accessFilters,
uniqueWhere,
undefined
) as Promise<ItemRootValue>;
}

export async function getAccessControlledItemForUpdate(
list: InitialisedList,
context: KeystoneContext,
uniqueWhere: UniquePrismaFilter,
accessFilters: boolean | InputFilter,
originalInput: Record<string, any>
): Promise<ItemRootValue> {
return getAccessControlledItemForMutation(
list,
context,
'update',
accessFilters,
uniqueWhere,
originalInput
) as Promise<ItemRootValue>;
return item;
}

export async function applyAccessControlForCreate(
list: InitialisedList,
context: KeystoneContext,
originalInput: Record<string, unknown>
) {
return getAccessControlledItemForMutation(
list,
const operation = 'create' as const;

// Apply item level access control
const access = list.access.item[operation];
const args = {
operation,
session: context.session,
listKey: list.listKey,
context,
'create',
undefined,
undefined,
originalInput
originalInput,
};

// List level 'item' access control
if (!(await access(args))) {
throw accessDeniedError();
}

// Field level 'item' access control
const results = await Promise.all(
Object.keys(originalInput!).map(fieldKey =>
validateFieldAccessControl({
access: list.fields[fieldKey].access[operation],
args: { ...args, fieldKey },
})
)
);

if (results.some(canAccess => !canAccess)) {
throw accessDeniedError();
}
}
4 changes: 2 additions & 2 deletions packages/keystone/src/lib/core/mutations/create-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
IdType,
runWithPrisma,
} from '../utils';
import { resolveUniqueWhereInput, UniqueInputFilter } from '../where-inputs';
import { InputFilter, resolveUniqueWhereInput, UniqueInputFilter } from '../where-inputs';
import { accessDeniedError, extensionError } from '../graphql-errors';
import { checkOperationAccess, getAccessFilters } from '../access-control';
import {
Expand Down Expand Up @@ -135,7 +135,7 @@ async function updateSingle(
updateInput: { where: UniqueInputFilter; data: Record<string, any> },
list: InitialisedList,
context: KeystoneContext,
accessFilters: boolean | Record<string, any>,
accessFilters: boolean | InputFilter,
operationAccess: boolean,
writeLimit: Limit
) {
Expand Down
3 changes: 2 additions & 1 deletion packages/keystone/src/lib/core/queries/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
resolveUniqueWhereInput,
resolveWhereInput,
UniqueInputFilter,
InputFilter,
} from '../where-inputs';
import { limitsExceededError } from '../graphql-errors';
import { InitialisedList } from '../types-for-lists';
Expand Down Expand Up @@ -42,7 +43,7 @@ export async function accessControlledFilter(
list: InitialisedList,
context: KeystoneContext,
resolvedWhere: PrismaFilter,
accessFilters: true | Record<string, any>
accessFilters: boolean | InputFilter
) {
// Merge the filter access control
if (typeof accessFilters === 'object') {
Expand Down

0 comments on commit 4b8cd2b

Please sign in to comment.