Skip to content

Commit

Permalink
[WIP] Port access control tests to new interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
timleslie committed Feb 25, 2021
1 parent 7c25896 commit 53a8eb8
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 255 deletions.
24 changes: 19 additions & 5 deletions packages-next/keystone/src/lib/createContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,45 @@ import { accessControlContext, skipAccessControlContext } from './createAccessCo

export function makeCreateContext({
graphQLSchema,
internalSchema,
keystone,
}: {
graphQLSchema: GraphQLSchema;
internalSchema: GraphQLSchema;
keystone: BaseKeystone;
}) {
// We precompute these helpers here rather than every time createContext is called
// because they require parsing the entire schema, which is potentially expensive.
const getArgsByList: Record<string, ReturnType<typeof getArgsFactory>> = {};
const publicGetArgsByList: Record<string, ReturnType<typeof getArgsFactory>> = {};
for (const [listKey, list] of Object.entries(keystone.lists)) {
getArgsByList[listKey] = getArgsFactory(list, graphQLSchema);
publicGetArgsByList[listKey] = getArgsFactory(list, graphQLSchema);
}

const internalGetArgsByList: Record<string, ReturnType<typeof getArgsFactory>> = {};
for (const [listKey, list] of Object.entries(keystone.lists)) {
internalGetArgsByList[listKey] = getArgsFactory(list, internalSchema);
}

const createContext = ({
sessionContext,
skipAccessControl = false,
req,
schemaName = 'public',
}: {
sessionContext?: SessionContext<any>;
skipAccessControl?: boolean;
req?: IncomingMessage;
schemaName?: 'public' | 'internal';
} = {}): KeystoneContext => {
const schema = schemaName === 'public' ? graphQLSchema : internalSchema;

const rawGraphQL: KeystoneGraphQLAPI<any>['raw'] = ({ query, context, variables }) => {
if (typeof query === 'string') {
query = parse(query);
}
return Promise.resolve(
execute({
schema: graphQLSchema,
schema,
document: query,
contextValue: context ?? contextToReturn,
variableValues: variables,
Expand All @@ -55,7 +66,7 @@ export function makeCreateContext({
};
const itemAPI: Record<string, ReturnType<typeof itemAPIForList>> = {};
const contextToReturn: KeystoneContext = {
schemaName: 'public',
schemaName,
...(skipAccessControl ? skipAccessControlContext : accessControlContext),
lists: itemAPI,
totalResults: 0,
Expand All @@ -65,10 +76,12 @@ export function makeCreateContext({
knex: keystone.adapter.knex,
mongoose: keystone.adapter.mongoose,
prisma: keystone.adapter.prisma,
graphql: { raw: rawGraphQL, run: runGraphQL, schema: graphQLSchema },
graphql: { raw: rawGraphQL, run: runGraphQL, schema },
maxTotalResults: keystone.queryLimits.maxTotalResults,
sudo: () => createContext({ sessionContext, skipAccessControl: true, req }),
exitSudo: () => createContext({ sessionContext, skipAccessControl: false, req }),
internal: () =>
createContext({ sessionContext, skipAccessControl: true, req, schemaName: 'internal' }),
withSession: session =>
createContext({
sessionContext: { ...sessionContext, session } as SessionContext<any>,
Expand All @@ -82,6 +95,7 @@ export function makeCreateContext({
executeGraphQL: rawGraphQL,
gqlNames: (listKey: string) => keystone.lists[listKey].gqlNames,
};
const getArgsByList = schemaName === 'public' ? publicGetArgsByList : internalGetArgsByList;
for (const [listKey, list] of Object.entries(keystone.lists)) {
itemAPI[listKey] = itemAPIForList(list, contextToReturn, getArgsByList[listKey]);
}
Expand Down
10 changes: 7 additions & 3 deletions packages-next/keystone/src/lib/createGraphQLSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import { getAdminMetaSchema } from '@keystone-next/admin-ui/system';

import { gql } from '../schema';

export function createGraphQLSchema(config: KeystoneConfig, keystone: BaseKeystone) {
export function createGraphQLSchema(
config: KeystoneConfig,
keystone: BaseKeystone,
schemaName: 'public' | 'internal' = 'public'
) {
// Start with the core keystone graphQL schema
let graphQLSchema = makeExecutableSchema({
typeDefs: keystone.getTypeDefs({ schemaName: 'public' }),
resolvers: keystone.getResolvers({ schemaName: 'public' }),
typeDefs: keystone.getTypeDefs({ schemaName }),
resolvers: keystone.getResolvers({ schemaName }),
});

// Filter out the _label_ field from all lists
Expand Down
6 changes: 4 additions & 2 deletions packages-next/keystone/src/lib/createSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { createKeystone } from './createKeystone';
export function createSystem(config: KeystoneConfig, dotKeystonePath: string, script: string) {
const keystone = createKeystone(config, dotKeystonePath, script);

const graphQLSchema = createGraphQLSchema(config, keystone);
const graphQLSchema = createGraphQLSchema(config, keystone, 'public');

const createContext = makeCreateContext({ keystone, graphQLSchema });
const internalSchema = createGraphQLSchema(config, keystone, 'internal');

const createContext = makeCreateContext({ keystone, graphQLSchema, internalSchema });

return { keystone, graphQLSchema, createContext };
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ function getTypeNodeForType(type: GraphQLType): TypeNode {

export function getCoerceAndValidateArgumentsFnForGraphQLField(
schema: GraphQLSchema,
field: GraphQLField<any, any>
field?: GraphQLField<any, any>
) {
if (!field) return;
const variableDefintions: VariableDefinitionNode[] = [];

for (const arg of field.args) {
Expand Down
9 changes: 9 additions & 0 deletions packages-next/keystone/src/lib/itemAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function itemAPIForList(
const listKey = list.key;
return {
findOne({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.findOne) return;
const args = getArgs.findOne(rawArgs) as { where: { id: string } };
if (resolveFields) {
return getItem({ listKey, context, returnFields: resolveFields, itemId: args.where.id });
Expand All @@ -51,6 +52,7 @@ export function itemAPIForList(
}
},
findMany({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.findMany) return;
const args = getArgs.findMany(rawArgs);
if (resolveFields) {
return getItems({ listKey, context, returnFields: resolveFields, ...args });
Expand All @@ -59,10 +61,12 @@ export function itemAPIForList(
}
},
async count(rawArgs) {
if (!getArgs.count) return 0;
const args = getArgs.count(rawArgs);
return (await list.listQueryMeta(args, context)).getCount();
},
createOne({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.createOne) return;
const { data } = getArgs.createOne(rawArgs);
if (resolveFields) {
return createItem({ listKey, context, returnFields: resolveFields, item: data });
Expand All @@ -71,6 +75,7 @@ export function itemAPIForList(
}
},
createMany({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.createMany) return;
const { data } = getArgs.createMany(rawArgs);
if (resolveFields) {
return createItems({ listKey, context, returnFields: resolveFields, items: data });
Expand All @@ -79,6 +84,7 @@ export function itemAPIForList(
}
},
updateOne({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.updateOne) return;
const { id, data } = getArgs.updateOne(rawArgs);
if (resolveFields) {
return updateItem({ listKey, context, returnFields: resolveFields, item: { id, data } });
Expand All @@ -87,6 +93,7 @@ export function itemAPIForList(
}
},
updateMany({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.updateMany) return;
const { data } = getArgs.updateMany(rawArgs);
if (resolveFields) {
return updateItems({ listKey, context, returnFields: resolveFields, items: data });
Expand All @@ -95,6 +102,7 @@ export function itemAPIForList(
}
},
deleteOne({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.deleteOne) return;
const { id } = getArgs.deleteOne(rawArgs);
if (resolveFields) {
return deleteItem({ listKey, context, returnFields: resolveFields, itemId: id });
Expand All @@ -103,6 +111,7 @@ export function itemAPIForList(
}
},
deleteMany({ resolveFields = 'id', ...rawArgs }) {
if (!getArgs.deleteMany) return;
const { ids } = getArgs.deleteMany(rawArgs);
if (resolveFields) {
return deleteItems({ listKey, context, returnFields: resolveFields, items: ids });
Expand Down
3 changes: 2 additions & 1 deletion packages-next/types/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ export type KeystoneContext = {
sudo: () => KeystoneContext;
exitSudo: () => KeystoneContext;
withSession: (session: any) => KeystoneContext;
internal: () => KeystoneContext;
totalResults: number;
maxTotalResults: number;
schemaName: 'public';
schemaName: 'public' | 'internal';
/** @deprecated */
gqlNames: (listKey: string) => Record<string, string>; // TODO: actual keys
/** @deprecated */
Expand Down
16 changes: 2 additions & 14 deletions packages/keystone/lib/ListTypes/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -954,13 +954,7 @@ module.exports = class List {
// We want to include `id` fields
// If read is globally set to false, makes sense to never show it
const readFields = this.getAllFieldsWithAccess({ schemaName, access: 'read' });
if (
schemaAccess.read ||
schemaAccess.create ||
schemaAccess.update ||
schemaAccess.delete ||
schemaAccess.auth
) {
if (schemaAccess.read || schemaAccess.create || schemaAccess.update || schemaAccess.delete) {
types.push(
...flatten(this.fields.map(field => field.getGqlAuxTypes({ schemaName }))),
`
Expand Down Expand Up @@ -1151,13 +1145,7 @@ module.exports = class List {

gqlAuxFieldResolvers({ schemaName }) {
const schemaAccess = this.access[schemaName];
if (
schemaAccess.read ||
schemaAccess.create ||
schemaAccess.update ||
schemaAccess.delete ||
schemaAccess.auth
) {
if (schemaAccess.read || schemaAccess.create || schemaAccess.update || schemaAccess.delete) {
return objMerge(this.fields.map(field => field.gqlAuxFieldResolvers({ schemaName })));
}
return {};
Expand Down
8 changes: 5 additions & 3 deletions packages/test-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,13 @@ function _keystoneRunner(adapterName: AdapterName, tearDownFunction: () => Promi

function _before(adapterName: AdapterName) {
return async function (
setupKeystone: (adapterName: AdapterName) => Promise<{ keystone: Keystone<string>; app: any }>
setupKeystone: (
adapterName: AdapterName
) => Promise<{ keystone: Keystone<string>; app: any; context: any }>
) {
const { keystone, app } = await setupKeystone(adapterName);
const { keystone, context, app } = await setupKeystone(adapterName);
await keystone.connect();
return { keystone, app };
return { keystone, context, app };
};
}

Expand Down
Loading

0 comments on commit 53a8eb8

Please sign in to comment.