UNPKG

@casl/mongoose

Version:

Allows to query accessible records from MongoDB based on CASL rules

1 lines 5.92 kB
{"version":3,"file":"index.cjs","names":["AccessibleFields"],"sources":["../../src/accessibleBy.ts","../../src/plugins/accessible_records.ts","../../src/accessibleFieldsBy.ts"],"sourcesContent":["import type { AnyMongoAbility, Generics, SubjectType, Abilities, AbilityTuple, ExtractSubjectType } from '@casl/ability';\nimport { rulesToCondition } from '@casl/ability/extra';\n\nfunction convertToMongoQuery(rule: AnyMongoAbility['rules'][number]) {\n const conditions = rule.conditions!;\n return rule.inverted ? { $nor: [conditions] } : conditions;\n}\n\nconst MONGO_QUERY_AGGREGATION = {\n and: (conditions: unknown[]) => ({ $and: conditions }),\n or: (conditions: unknown[]) => ({ $or: conditions }),\n empty: () => ({})\n};\n\nexport const EMPTY_RESULT_QUERY = { $expr: { $eq: [0, 1] } };\nexport class AccessibleRecords<T extends SubjectType> {\n constructor(\n private readonly _ability: AnyMongoAbility,\n private readonly _action: string\n ) {}\n\n /**\n * In case action is not allowed, it returns `{ $expr: { $eq: [0, 1] } }`\n */\n ofType(subjectType: T): Record<string, unknown> {\n const rules = this._ability.rulesFor(this._action, subjectType);\n const query = rulesToCondition(rules, convertToMongoQuery, MONGO_QUERY_AGGREGATION);\n return query === null ? EMPTY_RESULT_QUERY : query as Record<string, unknown>;\n }\n}\n\ntype SubjectTypes<T extends Abilities> = T extends AbilityTuple\n ? ExtractSubjectType<T[1]>\n : never;\n\n/**\n * Returns accessible records Mongo query per record type (i.e., entity type) based on provided Ability and action.\n */\nexport function accessibleBy<T extends AnyMongoAbility>(\n ability: T,\n action: Parameters<T['rulesFor']>[0] = 'read'\n): AccessibleRecords<SubjectTypes<Generics<T>['abilities']>> {\n return new AccessibleRecords(ability, action);\n}\n","import type { AnyMongoAbility, Generics, Normalize } from '@casl/ability';\nimport type { Document as Doc, HydratedDocument, Model, Query, QueryWithHelpers, Schema } from 'mongoose';\nimport { accessibleBy } from '../accessibleBy';\n\nfunction accessibleRecords<T extends AnyMongoAbility>(\n baseQuery: Query<any, any>,\n ability: T,\n action?: Normalize<Generics<T>['abilities']>[0]\n): QueryWithHelpers<Doc, Doc> {\n const subjectType = ability.detectSubjectType({\n constructor: baseQuery.model\n });\n\n if (!subjectType) {\n throw new TypeError(`Cannot detect subject type of \"${baseQuery.model.modelName}\" to return accessible records`);\n }\n\n const query = accessibleBy(ability, action).ofType(subjectType);\n\n return baseQuery.and([query]);\n}\n\ntype GetAccessibleRecords<T, TQueryHelpers, TMethods, TVirtuals> = <U extends AnyMongoAbility>(\n ability: U,\n action?: Normalize<Generics<U>['abilities']>[0]\n) => QueryWithHelpers<T[], T, AccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>>;\n\nexport type AccessibleRecordQueryHelpers<T, TQueryHelpers = {}, TMethods = {}, TVirtuals = {}> = {\n accessibleBy: GetAccessibleRecords<\n HydratedDocument<T, TMethods, TVirtuals>,\n TQueryHelpers,\n TMethods,\n TVirtuals\n >\n};\nexport interface AccessibleRecordModel<\n T,\n TQueryHelpers = {},\n TMethods = {},\n TVirtuals = {}\n> extends Model<T,\n TQueryHelpers & AccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>,\n TMethods,\n TVirtuals> {\n accessibleBy: GetAccessibleRecords<\n HydratedDocument<T, TMethods, TVirtuals>,\n TQueryHelpers,\n TMethods,\n TVirtuals\n >\n}\n\nfunction modelAccessibleBy(this: Model<unknown>, ability: AnyMongoAbility, action?: string) {\n return accessibleRecords(this.where(), ability, action);\n}\n\nfunction queryAccessibleBy(\n this: Query<unknown, unknown>,\n ability: AnyMongoAbility,\n action?: string\n) {\n return accessibleRecords(this, ability, action);\n}\n\nexport function accessibleRecordsPlugin(schema: Schema<any>): void {\n (schema.query as Record<string, unknown>).accessibleBy = queryAccessibleBy;\n schema.statics.accessibleBy = modelAccessibleBy;\n}\n","import type { AnyMongoAbility, Generics } from \"@casl/ability\";\nimport { AccessibleFields, type GetSubjectTypeAllFieldsExtractor } from \"@casl/ability/extra\";\nimport mongoose from 'mongoose';\n\nconst getSubjectTypeAllFieldsExtractor: GetSubjectTypeAllFieldsExtractor = (type) => {\n const Model = typeof type === 'string' ? mongoose.models[type] : type;\n if (!Model) throw new Error(`Unknown mongoose model \"${type}\"`);\n return 'schema' in Model ? Object.keys((Model.schema as any).paths) : [];\n};\n\nexport function accessibleFieldsBy<T extends AnyMongoAbility>(\n ability: T,\n action: Parameters<T['rulesFor']>[0] = 'read'\n): AccessibleFields<Extract<Generics<T>['abilities'], unknown[]>[1]> {\n return new AccessibleFields(ability, action, getSubjectTypeAllFieldsExtractor);\n}\n"],"mappings":";;;;;;;;;;AAGA,SAAS,EAAoB;IAC3B,MAAM,IAAa,EAAK;IACxB,OAAO,EAAK,WAAW;QAAE,MAAM,EAAC;QAAgB;;;;;;;;;;;;;;;AAGlD,MAAM,IAA0B;IAC9B,KAAM,MAAA;QAA6B,MAAM;;IACzC,IAAK,MAAA;QAA6B,KAAK;;IACvC,OAAA,OAAA,CAAgB;GAGL,IAAqB;IAAE,OAAO;QAAE,KAAK,EAAC,GAAG;;;;AACtD,IAAa,IAAb;IACE,WAAA,CACE,GACA;QADiB,KAAA,IAAA,GACA,KAAA,IAAA;;IAMnB,MAAA,CAAO;QACL,MAAM,IAAQ,KAAK,EAAS,SAAS,KAAK,GAAS,IAC7C,KAAA,GAAA,EAAA,kBAAyB,GAAO,GAAqB;QAC3D,OAAiB,SAAV,IAAiB,IAAqB;;;;AAWjD,SAAgB,EACd,GACA,IAAuC;IAEvC,OAAO,IAAI,EAAkB,GAAS;;;ACtCxC,SAAS,EACP,GACA,GACA;IAEA,MAAM,IAAc,EAAQ,kBAAkB;QAC5C,aAAa,EAAU;;IAGzB,KAAK,GACH,MAAM,IAAI,UAAU,kCAAkC,EAAU,MAAM;IAGxE,MAAM,IAAQ,EAAa,GAAS,GAAQ,OAAO;IAEnD,OAAO,EAAU,IAAI,EAAC;;;AAiCxB,SAAS,EAAwC,GAA0B;IACzE,OAAO,EAAkB,KAAK,SAAS,GAAS;;;AAGlD,SAAS,EAEP,GACA;IAEA,OAAO,EAAkB,MAAM,GAAS;;;ACzD1C,MAAM,IAAsE;IAC1E,MAAM,IAAwB,mBAAT,IAAoB,EAAA,QAAS,OAAO,KAAQ;IACjE,KAAK,GAAO,MAAM,IAAI,MAAM,2BAA2B;IACvD,OAAO,YAAY,IAAQ,OAAO,KAAM,EAAM,OAAe,SAAS;;;uDAGxE,SACE,GACA,IAAuC;IAEvC,OAAO,IAAIA,EAAAA,iBAAiB,GAAS,GAAQ;qCDkD/C,SAAwC;IACrC,EAAO,MAAkC,eAAe,GACzD,EAAO,QAAQ,eAAe"}