Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make data validation a separate enhancement kind #1226

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/runtime/src/enhancements/create-enhancement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import type { PolicyDef, ZodSchemas } from './types';
/**
* Kinds of enhancements to `PrismaClient`
*/
export type EnhancementKind = 'password' | 'omit' | 'policy' | 'delegate';
export type EnhancementKind = 'password' | 'omit' | 'policy' | 'validation' | 'delegate';

/**
* All enhancement kinds
*/
const ALL_ENHANCEMENTS = ['password', 'omit', 'policy', 'delegate'];
const ALL_ENHANCEMENTS: EnhancementKind[] = ['password', 'omit', 'policy', 'validation', 'delegate'];

/**
* Transaction isolation levels: https://www.prisma.io/docs/orm/prisma-client/queries/transactions#transaction-isolation-level
Expand Down Expand Up @@ -148,10 +148,10 @@ export function createEnhancement<DbClient extends object>(
}
}

// policy proxy
if (kinds.includes('policy')) {
// 'policy' and 'validation' enhancements are both enabled by `withPolicy`
if (kinds.includes('policy') || kinds.includes('validation')) {
result = withPolicy(result, options, context);
if (hasDefaultAuth) {
if (kinds.includes('policy') && hasDefaultAuth) {
// @default(auth()) proxy
result = withDefaultAuth(result, options, context);
}
Expand Down
35 changes: 27 additions & 8 deletions packages/runtime/src/enhancements/policy/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,33 @@ export class PolicyUtil extends QueryUtils {

//# Auth guard

private readonly FULLY_OPEN_AUTH_GUARD = {
create: true,
read: true,
update: true,
delete: true,
postUpdate: true,
create_input: true,
update_input: true,
};

private getModelAuthGuard(model: string): PolicyDef['guard']['string'] {
if (this.options.kinds && !this.options.kinds.includes('policy')) {
// policy enhancement not enabled, return an fully open guard
return this.FULLY_OPEN_AUTH_GUARD;
} else {
return this.policy.guard[lowerCaseFirst(model)];
}
}

/**
* Gets pregenerated authorization guard object for a given model and operation.
*
* @returns true if operation is unconditionally allowed, false if unconditionally denied,
* otherwise returns a guard object
*/
getAuthGuard(db: CrudContract, model: string, operation: PolicyOperationKind, preValue?: any) {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
Expand Down Expand Up @@ -318,7 +337,7 @@ export class PolicyUtil extends QueryUtils {
* Checks if the given model has a policy guard for the given operation.
*/
hasAuthGuard(model: string, operation: PolicyOperationKind) {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
return false;
}
Expand Down Expand Up @@ -347,7 +366,7 @@ export class PolicyUtil extends QueryUtils {
* @returns boolean if static analysis is enough to determine the result, undefined if not
*/
checkInputGuard(model: string, args: any, operation: 'create'): boolean | undefined {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
return undefined;
}
Expand Down Expand Up @@ -1020,23 +1039,23 @@ export class PolicyUtil extends QueryUtils {
* Gets field selection for fetching pre-update entity values for the given model.
*/
getPreValueSelect(model: string): object | undefined {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
return guard[PRE_UPDATE_VALUE_SELECTOR];
}

private getReadFieldSelect(model: string): object | undefined {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
return guard[FIELD_LEVEL_READ_CHECKER_SELECTOR];
}

private checkReadField(model: string, field: string, entity: any) {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
Expand All @@ -1053,7 +1072,7 @@ export class PolicyUtil extends QueryUtils {
}

private hasFieldLevelPolicy(model: string) {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
Expand Down Expand Up @@ -1228,7 +1247,7 @@ export class PolicyUtil extends QueryUtils {
}

private requireGuard(model: string) {
const guard = this.policy.guard[lowerCaseFirst(model)];
const guard = this.getModelAuthGuard(model);
if (!guard) {
throw this.unknownError(`unable to load policy guard for ${model}`);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime/src/enhancements/query-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { InternalEnhancementOptions } from './create-enhancement';
import { prismaClientUnknownRequestError, prismaClientValidationError } from './utils';

export class QueryUtils {
constructor(private readonly prisma: DbClientContract, private readonly options: InternalEnhancementOptions) {}
constructor(private readonly prisma: DbClientContract, protected readonly options: InternalEnhancementOptions) {}

getIdFields(model: string) {
return getIdFields(this.options.modelMeta, model, true);
Expand Down
Loading
Loading