Skip to content

Commit

Permalink
refactor: make data validation a separate enhancement kind (#1226)
Browse files Browse the repository at this point in the history
  • Loading branch information
ymc9 authored Apr 9, 2024
1 parent 101e642 commit 0a2aaf7
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 59 deletions.
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

0 comments on commit 0a2aaf7

Please sign in to comment.