UNPKG

@casl/prisma

Version:

Allows to query accessible records using Prisma client based on CASL rules

1 lines 21.8 kB
{"version":3,"file":"runtime-CsD-HGw1.mjs","names":["not","within","lt","has","hasSome"],"sources":["../../src/errors/ParsingQueryError.ts","../../src/prisma/PrismaQueryParser.ts","../../src/prisma/interpretPrismaQuery.ts","../../src/prisma/prismaQuery.ts","../../src/accessibleBy.ts","../../src/createPrismaAbility.ts","../../src/createCaslExtension.ts"],"sourcesContent":["export class ParsingQueryError extends Error {\n static invalidArgument(operatorName: string, value: unknown, expectValueType: string) {\n const valueType = `${typeof value}(${JSON.stringify(value, null, 2)})`;\n return new this(\n `\"${operatorName}\" expects to receive ${expectValueType} but instead got \"${valueType}\"`\n );\n }\n}\n","import {\n buildAnd,\n Comparable,\n CompoundCondition,\n CompoundInstruction,\n Condition,\n FieldCondition,\n FieldInstruction,\n FieldParsingContext,\n NULL_CONDITION,\n ObjectQueryFieldParsingContext,\n ObjectQueryParser\n} from '@ucast/core';\nimport { ParsingQueryError } from '../errors/ParsingQueryError';\n\nconst isPlainObject = (value: any) => {\n return value && (value.constructor === Object || !value.constructor);\n};\n\nconst equals: FieldInstruction = {\n type: 'field',\n validate(instruction, value) {\n if (Array.isArray(value) || isPlainObject(value)) {\n throw new ParsingQueryError(`\"${instruction.name}\" does not supports comparison of arrays and objects`);\n }\n }\n};\n\nconst not: FieldInstruction<unknown, ObjectQueryFieldParsingContext> = {\n type: 'field',\n parse(instruction, value, { hasOperators, field, parse }) {\n if (isPlainObject(value) && !hasOperators(value) || Array.isArray(value)) {\n throw new ParsingQueryError(`\"${instruction.name}\" does not supports comparison of arrays and objects`);\n }\n\n if (!isPlainObject(value)) {\n return new FieldCondition('notEquals', field, value);\n }\n\n return new CompoundCondition('NOT', [parse(value, { field })]);\n }\n};\n\nconst within: FieldInstruction<unknown[]> = {\n type: 'field',\n validate(instruction, value) {\n if (!Array.isArray(value)) {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'an array');\n }\n }\n};\n\nconst lt: FieldInstruction<Comparable> = {\n type: 'field',\n validate(instruction, value) {\n const type = typeof value;\n const isComparable = type === 'string'\n || type === 'number' && Number.isFinite(value)\n || value instanceof Date;\n\n if (!isComparable) {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'comparable value');\n }\n }\n};\n\nconst POSSIBLE_MODES = new Set(['insensitive', 'default']);\nconst mode: FieldInstruction<string> = {\n type: 'field',\n validate(instruction, value) {\n if (!POSSIBLE_MODES.has(value)) {\n throw ParsingQueryError.invalidArgument(\n instruction.name,\n value,\n `one of ${Array.from(POSSIBLE_MODES).join(', ')}`\n );\n }\n },\n parse: () => NULL_CONDITION\n};\n\ninterface StringFieldContext extends FieldParsingContext {\n query: {\n mode?: 'insensitive'\n }\n}\n\nconst compareString: FieldInstruction<string, StringFieldContext> = {\n type: 'field',\n validate(instruction, value) {\n if (typeof value !== 'string') {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'string');\n }\n },\n parse(instruction, value, { query, field }) {\n const name = query.mode === 'insensitive' ? `i${instruction.name}` : instruction.name;\n return new FieldCondition(name, field, value);\n }\n};\n\nconst compound: CompoundInstruction = {\n type: 'compound',\n validate(instruction, value) {\n if (!value || typeof value !== 'object') {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'an array or object');\n }\n },\n parse(instruction, arrayOrObject, { parse }) {\n const value = Array.isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject];\n const conditions = value.map(v => parse(v));\n return new CompoundCondition(instruction.name, conditions);\n }\n};\n\nconst booleanField: FieldInstruction<boolean> = {\n type: 'field',\n validate(instruction, value) {\n if (typeof value !== 'boolean') {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'a boolean');\n }\n }\n};\n\nconst has: FieldInstruction<unknown> = {\n type: 'field'\n};\n\nconst hasSome: FieldInstruction<unknown[]> = {\n type: 'field',\n validate(instruction, value) {\n if (!Array.isArray(value)) {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'an array');\n }\n }\n};\n\nconst relation: FieldInstruction<Record<string, unknown>, ObjectQueryFieldParsingContext> = {\n type: 'field',\n parse(instruction, value, { field, parse }) {\n if (!isPlainObject(value)) {\n throw ParsingQueryError.invalidArgument(instruction.name, value, 'a query for nested relation');\n }\n\n return new FieldCondition(instruction.name, field, parse(value));\n }\n};\n\nconst inverted = (name: string, baseInstruction: FieldInstruction): FieldInstruction => {\n const parse = baseInstruction.parse;\n\n if (!parse) {\n return {\n ...baseInstruction,\n parse(_, value, ctx) {\n return new CompoundCondition('NOT', [new FieldCondition(name, ctx.field, value)]);\n }\n };\n }\n\n return {\n ...baseInstruction,\n parse(instruction, value, ctx) {\n const condition = parse(instruction, value, ctx);\n if (condition.operator !== instruction.name) {\n throw new Error(`Cannot invert \"${name}\" operator parser because it returns a complex Condition`);\n }\n (condition as Mutable<Condition>).operator = name;\n return new CompoundCondition('NOT', [condition]);\n }\n };\n};\n\nconst instructions = {\n equals,\n not,\n in: within,\n notIn: inverted('in', within),\n lt,\n lte: lt,\n gt: lt,\n gte: lt,\n mode,\n startsWith: compareString,\n endsWith: compareString,\n contains: compareString,\n isEmpty: booleanField,\n has,\n hasSome,\n hasEvery: hasSome,\n NOT: compound,\n AND: compound,\n OR: compound,\n every: relation,\n some: relation,\n none: inverted('some', relation),\n is: relation,\n isNot: inverted('is', relation),\n isSet: booleanField\n};\n\nexport interface ParseOptions {\n field: string\n}\n\ntype Query = Record<string, any>;\nexport class PrismaQueryParser extends ObjectQueryParser<Query> {\n constructor() {\n super(instructions, {\n defaultOperatorName: 'equals',\n });\n }\n\n parse(query: Query, options?: ParseOptions): Condition {\n if (options && options.field) {\n return buildAnd(this.parseFieldOperators(options.field, query));\n }\n\n return super.parse(query);\n }\n}\n\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] };\n","import { CompoundCondition, Condition, FieldCondition, type Interpreter } from '@ucast/core';\nimport {\n JsInterpreter,\n createJsInterpreter,\n eq,\n ne,\n and,\n or,\n within,\n lt,\n lte,\n gt,\n gte,\n compare\n} from '@ucast/js';\n\ntype StringInterpreter = JsInterpreter<FieldCondition<string>, Record<string, string>>;\nconst startsWith: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).startsWith(condition.value);\n};\nconst istartsWith: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).toLowerCase().startsWith(condition.value.toLowerCase());\n};\n\nconst endsWith: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).endsWith(condition.value);\n};\nconst iendsWith: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).toLowerCase().endsWith(condition.value.toLowerCase());\n};\n\nconst contains: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).includes(condition.value);\n};\nconst icontains: StringInterpreter = (condition, object, { get }) => {\n return get(object, condition.field).toLowerCase().includes(condition.value.toLowerCase());\n};\n\ntype ArrayInterpreter<\n TConditionValue,\n TValue extends Record<string, unknown[]> = Record<string, unknown[]>\n> = JsInterpreter<FieldCondition<TConditionValue>, TValue>;\nconst isEmpty: ArrayInterpreter<boolean> = (condition, object, { get }) => {\n const value = get(object, condition.field);\n const empty = Array.isArray(value) && value.length === 0;\n return empty === condition.value;\n};\nconst has: ArrayInterpreter<unknown> = (condition, object, { get }) => {\n const value = get(object, condition.field);\n return Array.isArray(value) && value.includes(condition.value);\n};\nconst hasSome: ArrayInterpreter<unknown[]> = (condition, object, { get }) => {\n const value = get(object, condition.field);\n return Array.isArray(value) && condition.value.some(v => value.includes(v));\n};\nconst hasEvery: ArrayInterpreter<unknown[]> = (condition, object, { get }) => {\n const value = get(object, condition.field);\n return Array.isArray(value) && condition.value.every(v => value.includes(v));\n};\n\nconst every: JsInterpreter<FieldCondition<Condition>> = (condition, object, { get, interpret }) => {\n const items = get(object, condition.field) as Record<string, unknown>[];\n return Array.isArray(items)\n && items.every(item => interpret(condition.value, item));\n};\n\nconst some: JsInterpreter<FieldCondition<Condition>> = (condition, object, { get, interpret }) => {\n const items = get(object, condition.field) as Record<string, unknown>[];\n return Array.isArray(items) && items.some(item => interpret(condition.value, item));\n};\n\nconst is: JsInterpreter<FieldCondition<Condition>> = (condition, object, { get, interpret }) => {\n const item = get(object, condition.field) as Record<string, unknown>;\n return item && typeof item === 'object' && interpret(condition.value, item);\n};\n\nconst not: JsInterpreter<CompoundCondition> = (condition, object, { interpret }) => {\n return condition.value.every(subCondition => !interpret(subCondition, object));\n};\n\nconst isSet: JsInterpreter<FieldCondition<Condition>> = (condition, object, { get }) => {\n const item = get(object, condition.field);\n return item !== undefined;\n}\n\nfunction toComparable(value: unknown) {\n return value && typeof value === 'object' ? value.valueOf() : value;\n}\n\nconst compareValues: typeof compare = (a, b) => compare(toComparable(a), toComparable(b));\n\nexport const interpretPrismaQuery = createJsInterpreter({\n equals: eq,\n notEquals: ne,\n in: within,\n lt,\n lte,\n gt,\n gte,\n startsWith,\n istartsWith,\n endsWith,\n iendsWith,\n contains,\n icontains,\n isEmpty,\n has,\n hasSome,\n hasEvery,\n and,\n or,\n AND: and,\n OR: or,\n NOT: not,\n every,\n some,\n is,\n isSet,\n}, {\n get: (object, field) => object[field],\n compare: compareValues,\n}) as Interpreter<Condition, boolean>;\n","import { AnyInterpreter, createTranslatorFactory } from '@ucast/core';\nimport { ForcedSubject } from '@casl/ability';\nimport { PrismaQueryParser } from './PrismaQueryParser';\nimport { interpretPrismaQuery } from './interpretPrismaQuery';\n\nconst parser = new PrismaQueryParser();\nexport const prismaQuery = createTranslatorFactory(\n parser.parse,\n interpretPrismaQuery as AnyInterpreter\n);\n\nexport type Model<T, TName extends string> = T & ForcedSubject<TName>;\nexport type Subjects<T extends Partial<Record<string, Record<string, unknown>>>> =\n | keyof T\n | { [K in keyof T]: Model<T[K], K & string> }[keyof T];\n\n/**\n * Extracts Prisma model name from given object and possible list of all subjects\n */\nexport type ExtractModelName<\n TObject,\n TModelName extends PropertyKey\n> = TObject extends { kind: TModelName }\n ? TObject['kind']\n : TObject extends ForcedSubject<TModelName>\n ? TObject['__caslSubjectType__']\n : TObject extends { __typename: TModelName }\n ? TObject['__typename']\n : TModelName;\n","import { Ability, Generics, Normalize, ExtractSubjectType } from '@casl/ability';\nimport { rulesToCondition } from '@casl/ability/extra';\nimport { BasePrismaQuery, InferPrismaTypes } from './types';\n\nfunction convertToPrismaQuery(rule: Ability<any, BasePrismaQuery>['rules'][number]) {\n return rule.inverted ? { NOT: rule.conditions } : rule.conditions;\n}\n\nconst PRISMA_QUERY_AGGREGATION = {\n and: (conditions: unknown[]) => ({ AND: conditions }),\n or: (conditions: unknown[]) => ({ OR: conditions }),\n empty: () => ({})\n};\n\ntype ModelName<TAbility extends Ability<any, BasePrismaQuery>> = Extract<\n keyof InferPrismaTypes<Generics<TAbility>['conditions']>['WhereInput'],\n string\n>;\ntype SubjectType<TAbility extends Ability<any, BasePrismaQuery>> = Extract<\n ExtractSubjectType<Normalize<Generics<TAbility>['abilities']>[1]>,\n ModelName<TAbility>\n>;\n\nexport class AccessibleRecords<TAbility extends Ability<any, BasePrismaQuery>> {\n constructor(\n private readonly _ability: TAbility,\n private readonly _action: string\n ) {}\n\n ofType<TSubjectType extends SubjectType<TAbility>>(\n subjectType: TSubjectType\n ): InferPrismaTypes<Generics<TAbility>['conditions']>['WhereInput'][TSubjectType] {\n const rules = this._ability.rulesFor(this._action, subjectType);\n const query = rulesToCondition(rules, convertToPrismaQuery, PRISMA_QUERY_AGGREGATION);\n const finalQuery = query === null ? { OR: [] } : query;\n\n return finalQuery as InferPrismaTypes<Generics<TAbility>['conditions']>['WhereInput'][TSubjectType];\n }\n}\n\nexport function accessibleBy<TAbility extends Ability<any, BasePrismaQuery>>(\n ability: TAbility,\n action: TAbility[\"rules\"][number][\"action\"] & string = \"read\"\n): AccessibleRecords<TAbility> {\n return new AccessibleRecords(ability, action);\n}\n","import {\n type AbilityOptions,\n type AbilityOptionsOf,\n type AbilityTuple,\n fieldPatternMatcher,\n Ability,\n type RawRuleFrom,\n type RawRuleOf\n} from '@casl/ability';\nimport { prismaQuery } from './prisma/prismaQuery';\nimport type { BasePrismaQuery } from './types';\n\nexport function createPrismaAbility<\n T extends Ability<any, BasePrismaQuery>\n>(rules?: RawRuleOf<T>[], options?: AbilityOptionsOf<T>): T;\nexport function createPrismaAbility<\n A extends AbilityTuple = [string, string],\n C extends BasePrismaQuery = any\n>(\n rules?: RawRuleFrom<A, C>[],\n options?: AbilityOptions<A, C>\n): Ability<A, C>;\nexport function createPrismaAbility(rules: any[] = [], options = {}): Ability<any, any> {\n return new Ability(rules, {\n ...options,\n conditionsMatcher: prismaQuery,\n fieldMatcher: fieldPatternMatcher,\n });\n}\n","import { Prisma } from '@prisma/client/extension';\n\n/**\n * This is needed because Prisma doesn't reliably mock empty OR condition:\n * https://github.com/prisma/prisma/issues/17367\n */\nexport function createCaslExtension() {\n return Prisma.defineExtension({\n name: 'casl',\n query: {\n $allModels: {\n $allOperations({ args, query }) {\n const where = (args as { where?: unknown })?.where;\n let newArgs = args;\n if (hasEmptyCondition(where)) {\n newArgs = { ...args };\n if (Array.isArray(where)) {\n (newArgs as any).where = { OR: [], AND: where };\n } else if (typeof where === 'object') {\n (newArgs as any).where = { ...where, OR: [], AND: [where] };\n } else {\n (newArgs as any).where = { OR: [], AND: [where] };\n }\n }\n\n return query(newArgs);\n },\n },\n },\n });\n}\n\nfunction hasEmptyCondition(rawWhere: unknown): boolean {\n if (!rawWhere || typeof rawWhere !== 'object') return false;\n\n const where = rawWhere as Record<string, unknown>;\n\n if (Array.isArray(where)) {\n for (let i = 0, len = where.length; i < len; i++) {\n if (hasEmptyCondition(where[i])) return true;\n }\n return false;\n }\n\n if ((where.OR as unknown[])?.length === 0) {\n return true;\n }\n\n for (const key in where) {\n if (!Object.hasOwn(where, key)) continue;\n const value = where[key];\n if (value && typeof value === 'object' && hasEmptyCondition(value)) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":"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"}