@casl/ability
Version:
CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
1 lines • 11 kB
Source Map (JSON)
{"version":3,"file":"extra.mjs","names":[],"sources":["../../src/extra/packRules.ts","../../src/extra/permittedFieldsOf.ts","../../src/extra/rulesToFields.ts","../../src/extra/rulesToCondition.ts"],"sourcesContent":["\nimport { RawRule } from '../RawRule';\nimport { SubjectType } from '../types';\nimport { wrapArray } from '../utils';\n\nconst joinIfArray = (value: string | string[]) => Array.isArray(value) ? value.join(',') : value;\n\nexport type PackRule<T extends RawRule<any, any>> =\n [string, string] |\n [string, string, T['conditions']] |\n [string, string, T['conditions'] | 0, 1] |\n [string, string, T['conditions'] | 0, 1 | 0, string] |\n [string, string, T['conditions'] | 0, 1 | 0, string | 0, string];\n\nexport type PackSubjectType<T extends SubjectType> = (type: T) => string;\n\nexport function packRules<T extends RawRule<any, any>>(\n rules: readonly T[],\n packSubject?: PackSubjectType<T['subject']>\n): PackRule<T>[] {\n return rules.map((rule) => {\n const packedRule: PackRule<T> = [\n joinIfArray((rule as any).action || (rule as any).actions),\n typeof packSubject === 'function'\n ? wrapArray(rule.subject).map(packSubject).join(',')\n : joinIfArray(rule.subject),\n rule.conditions || 0,\n rule.inverted ? 1 : 0,\n rule.fields ? joinIfArray(rule.fields) : 0,\n rule.reason || ''\n ];\n\n while (packedRule.length > 0 && !packedRule[packedRule.length - 1]) packedRule.pop();\n\n return packedRule;\n });\n}\n\nexport type UnpackSubjectType<T extends SubjectType> = (type: string) => T;\n\nexport function unpackRules<T extends RawRule<any, any>>(\n rules: PackRule<T>[],\n unpackSubject?: UnpackSubjectType<T['subject']>\n): T[] {\n return rules.map(([action, subject, conditions, inverted, fields, reason]) => {\n const subjects = subject.split(',');\n const rule = {\n inverted: !!inverted,\n action: action.split(','),\n subject: typeof unpackSubject === 'function'\n ? subjects.map(unpackSubject)\n : subjects\n } as T;\n\n if (conditions) rule.conditions = conditions;\n if (fields) rule.fields = fields.split(',');\n if (reason) rule.reason = reason;\n\n return rule;\n });\n}\n","import { AnyAbility } from '../Ability';\nimport { Rule } from '../Rule';\nimport { RuleOf } from '../RuleIndex';\nimport { Subject, SubjectType } from '../types';\n\nexport type GetRuleFields<R extends Rule<any, any>> = (rule: R) => string[];\n\nexport interface PermittedFieldsOptions<T extends AnyAbility> {\n fieldsFrom: GetRuleFields<RuleOf<T>>\n}\n\nexport function permittedFieldsOf<T extends AnyAbility>(\n ability: T,\n action: Parameters<T['can']>[0],\n subject: Parameters<T['can']>[1],\n options: PermittedFieldsOptions<T>\n): string[] {\n const subjectType = ability.detectSubjectType(subject);\n const rules = ability.possibleRulesFor(action, subjectType);\n const uniqueFields = new Set<string>();\n const deleteItem = uniqueFields.delete.bind(uniqueFields);\n const addItem = uniqueFields.add.bind(uniqueFields);\n let i = rules.length;\n\n while (i--) {\n const rule = rules[i];\n if (rule.matchesConditions(subject)) {\n const toggle = rule.inverted ? deleteItem : addItem;\n options.fieldsFrom(rule).forEach(toggle);\n }\n }\n\n return Array.from(uniqueFields);\n}\n\nexport type GetSubjectTypeAllFieldsExtractor = (subjectType: SubjectType) => string[];\n\n/**\n * Helper class to make custom `accessibleFieldsBy` helper function\n */\nexport class AccessibleFields<T extends Subject> {\n constructor(\n private readonly _ability: AnyAbility,\n private readonly _action: string,\n private readonly _getAllFields: GetSubjectTypeAllFieldsExtractor\n ) {}\n\n /**\n * Returns accessible fields for Model type\n */\n ofType(subjectType: Extract<T, SubjectType>): string[] {\n return permittedFieldsOf(this._ability, this._action, subjectType, {\n fieldsFrom: this._getRuleFields(subjectType)\n });\n }\n\n /**\n * Returns accessible fields for particular document\n */\n of(subject: Exclude<T, SubjectType>): string[] {\n return permittedFieldsOf(this._ability, this._action, subject, {\n fieldsFrom: this._getRuleFields(this._ability.detectSubjectType(subject))\n });\n }\n\n private _getRuleFields(type: SubjectType): GetRuleFields<RuleOf<AnyAbility>> {\n return (rule) => (rule.fields || this._getAllFields(type));\n }\n}\n","import { Ability } from '../Ability';\nimport { AnyObject, ExtractSubjectType } from '../types';\nimport { setByPath } from '../utils';\n\n/**\n * Extracts rules condition values into an object of default values\n */\nexport function rulesToFields<T extends Ability<any, AnyObject>>(\n ability: T,\n action: Parameters<T['rulesFor']>[0],\n subjectType: ExtractSubjectType<Parameters<T['rulesFor']>[1]>,\n): AnyObject {\n return ability.rulesFor(action, subjectType)\n .reduce((values, rule) => {\n if (rule.inverted || !rule.conditions) {\n return values;\n }\n\n return Object.keys(rule.conditions).reduce((fields, fieldName) => {\n const value = rule.conditions![fieldName];\n\n if (!value || (value as any).constructor !== Object) {\n setByPath(fields, fieldName, value);\n }\n\n return fields;\n }, values);\n }, {} as AnyObject);\n}\n","import { CompoundCondition, Condition, buildAnd, buildOr } from '@ucast/mongo2js';\nimport type { AnyAbility } from '../Ability';\nimport type { RuleOf } from '../RuleIndex';\nimport type { ExtractSubjectType } from '../types';\n\nexport type RuleToQueryConverter<T extends AnyAbility, R = object> = (rule: RuleOf<T>) => R;\n\nfunction ruleToAST(rule: RuleOf<AnyAbility>): Condition {\n if (!rule.ast) {\n throw new Error(`Ability rule \"${JSON.stringify(rule)}\" does not have \"ast\" property. So, cannot be used to generate AST`);\n }\n\n return rule.inverted ? new CompoundCondition('not', [rule.ast]) : rule.ast;\n}\n\nexport function rulesToAST<T extends AnyAbility>(\n ability: T,\n action: Parameters<T['rulesFor']>[0],\n subjectType: ExtractSubjectType<Parameters<T['rulesFor']>[1]>,\n): Condition | null {\n return rulesToCondition<T, Condition, Condition>(\n ability.rulesFor(action, subjectType),\n ruleToAST,\n {\n and: buildAnd,\n or: buildOr,\n empty: () => buildAnd([])\n }\n );\n}\n\n/**\n * Converts CASL's sequential, switch-case priority enforcement into flat boolean logic.\n *\n * CASL evaluates rules from bottom to top (highest priority). When a record is evaluated:\n * - If it matches a `cannot` rule, it returns `false`.\n * - If it matches a `can` rule, it returns `true`.\n * - Thus, a `can` rule is only reached if it was not intercepted by any higher-priority `cannot` rule.\n *\n * This function flattens this logic for database queries by isolating each `can` rule (\"OR\" branches)\n * and strictly bounding it by all the preceding `cannot` conditions (\"AND NOT\" bounds).\n * Because standard `$or` logic inherently absorbs the overlap of previously matched `can` paths,\n * we don't mathematically need to subtract higher-priority `can` rules.\n *\n * @param rules - The sorted array of CASL rules (highest priority first).\n * @param convert - The transformer mapping a CASL rule to the target query/AST format.\n * @param hooks - The logical combination hooks for the target format.\n */\nexport function rulesToCondition<T extends AnyAbility, R, Result>(\n rules: readonly RuleOf<T>[],\n convert: (rule: RuleOf<T>) => R,\n hooks: {\n and: (conditions: R[]) => Result,\n or: (conditions: R[]) => Result,\n empty: () => Result,\n }\n): Result | null {\n const higherCannots: R[] = [];\n const orConditions: R[] = [];\n let hasUnconditionalCan = false;\n\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n\n if (rule.inverted) {\n if (!rule.conditions) {\n break; // stop evaluation on unconditional cannot\n }\n higherCannots.push(convert(rule));\n } else {\n if (!rule.conditions) {\n hasUnconditionalCan = true;\n break; // stop evaluation on unconditional can\n }\n\n let cond = convert(rule);\n if (higherCannots.length > 0) {\n cond = hooks.and([cond, ...higherCannots]) as unknown as R;\n }\n orConditions.push(cond);\n }\n }\n\n if (hasUnconditionalCan) {\n if (higherCannots.length === 0) {\n return hooks.empty();\n }\n if (orConditions.length === 0) {\n return hooks.and(higherCannots);\n }\n orConditions.push(hooks.and(higherCannots) as unknown as R);\n }\n\n if (orConditions.length === 0) return null;\n return hooks.or(orConditions);\n}\n"],"mappings":";;;;AAKA,MAAM,IAAe,KAA6B,MAAM,QAAQ,KAAS,EAAM,KAAK,OAAO;;AAW3F,SAAgB,EACd,GACA;IAEA,OAAO,EAAM,IAAK;QAChB,MAAM,IAA0B,EAC9B,EAAa,EAAa,UAAW,EAAa,UAC3B,qBAAhB,IACH,EAAU,EAAK,SAAS,IAAI,GAAa,KAAK,OAC9C,EAAY,EAAK,UACrB,EAAK,cAAc,GACnB,EAAK,WAAW,IAAI,GACpB,EAAK,SAAS,EAAY,EAAK,UAAU,GACzC,EAAK,UAAU;QAGjB,MAAO,EAAW,SAAS,MAAM,EAAW,EAAW,SAAS,MAAI,EAAW;QAE/E,OAAO;;;;AAMX,SAAgB,EACd,GACA;IAEA,OAAO,EAAM,IAAA,EAAM,GAAQ,GAAS,GAAY,GAAU,GAAQ;QAChE,MAAM,IAAW,EAAQ,MAAM,MACzB,IAAO;YACX,YAAY;YACZ,QAAQ,EAAO,MAAM;YACrB,SAAkC,qBAAlB,IACZ,EAAS,IAAI,KACb;;QAON,OAJI,MAAY,EAAK,aAAa,IAC9B,MAAQ,EAAK,SAAS,EAAO,MAAM,OACnC,MAAQ,EAAK,SAAS;QAEnB;;;;AC/CX,SAAgB,EACd,GACA,GACA,GACA;IAEA,MAAM,IAAc,EAAQ,kBAAkB,IACxC,IAAQ,EAAQ,iBAAiB,GAAQ,IACzC,IAAe,IAAI,KACnB,IAAa,EAAa,OAAO,KAAK,IACtC,IAAU,EAAa,IAAI,KAAK;IACtC,IAAI,IAAI,EAAM;IAEd,MAAO,OAAK;QACV,MAAM,IAAO,EAAM;QACnB,IAAI,EAAK,kBAAkB,IAAU;YACnC,MAAM,IAAS,EAAK,WAAW,IAAa;YAC5C,EAAQ,WAAW,GAAM,QAAQ;;;IAIrC,OAAO,MAAM,KAAK;;;AAQpB,IAAa,IAAb;IACE,WAAA,CACE,GACA,GACA;QAFiB,KAAA,IAAA,GACA,KAAA,IAAA,GACA,KAAA,IAAA;;IAMnB,MAAA,CAAO;QACL,OAAO,EAAkB,KAAK,GAAU,KAAK,GAAS,GAAa;YACjE,YAAY,KAAK,EAAe;;;IAOpC,EAAA,CAAG;QACD,OAAO,EAAkB,KAAK,GAAU,KAAK,GAAS,GAAS;YAC7D,YAAY,KAAK,EAAe,KAAK,EAAS,kBAAkB;;;IAIpE,CAAA,CAAuB;QACrB,OAAQ,KAAU,EAAK,UAAU,KAAK,EAAc;;;;AC3DxD,SAAgB,EACd,GACA,GACA;IAEA,OAAO,EAAQ,SAAS,GAAQ,GAC7B,OAAA,CAAQ,GAAQ,MACX,EAAK,aAAa,EAAK,aAClB,IAGF,OAAO,KAAK,EAAK,YAAY,OAAA,CAAQ,GAAQ;QAClD,MAAM,IAAQ,EAAK,WAAY;QAM/B,OAJK,KAAU,EAAc,gBAAgB,UAC3C,EAAU,GAAQ,GAAW,IAGxB;OACN,IACF,CAAA;;;ACpBP,SAAS,EAAU;IACjB,KAAK,EAAK,KACR,MAAM,IAAI,MAAM,iBAAiB,KAAK,UAAU;IAGlD,OAAO,EAAK,WAAW,IAAI,EAAkB,OAAO,EAAC,EAAK,SAAQ,EAAK;;;AAGzE,SAAgB,EACd,GACA,GACA;IAEA,OAAO,EACL,EAAQ,SAAS,GAAQ,IACzB,GACA;QACE,KAAK;QACL,IAAI;QACJ,OAAA,MAAa,EAAS;;;;AAsB5B,SAAgB,EACd,GACA,GACA;IAMA,MAAM,IAAqB,IACrB,IAAoB;IAC1B,IAAI,KAAsB;IAE1B,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK;QACrC,MAAM,IAAO,EAAM;QAEnB,IAAI,EAAK,UAAU;YACjB,KAAK,EAAK,YACR;YAEF,EAAc,KAAK,EAAQ;eACtB;YACL,KAAK,EAAK,YAAY;gBACpB,KAAsB;gBACtB;;YAGF,IAAI,IAAO,EAAQ;YACf,EAAc,SAAS,MACzB,IAAO,EAAM,IAAI,EAAC,MAAS,OAE7B,EAAa,KAAK;;;IAItB,IAAI,GAAqB;QACvB,IAA6B,MAAzB,EAAc,QAChB,OAAO,EAAM;QAEf,IAA4B,MAAxB,EAAa,QACf,OAAO,EAAM,IAAI;QAEnB,EAAa,KAAK,EAAM,IAAI;;IAG9B,OAA4B,MAAxB,EAAa,SAAqB,OAC/B,EAAM,GAAG"}