UNPKG

@casl/mongoose

Version:

Allows to query accessible records from MongoDB based on CASL rules

1 lines 10.6 kB
{"version":3,"file":"index.mjs","sources":["../../src/accessibleBy.ts","../../src/plugins/accessible_records.ts","../../src/plugins/accessible_fields.ts","../../src/accessibleFieldsBy.ts"],"sourcesContent":["import { AnyMongoAbility, Generics, SubjectType, Abilities, AbilityTuple, ExtractSubjectType } from '@casl/ability';\nimport { rulesToQuery } 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\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 query = rulesToQuery(this._ability, this._action, subjectType, convertToMongoQuery);\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 { AnyMongoAbility, Generics, Normalize } from '@casl/ability';\nimport { Document, 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<Document, Document> {\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<\nArray<T>,\nT,\nAccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>\n>;\n\nexport type AccessibleRecordQueryHelpers<T, TQueryHelpers = {}, TMethods = {}, TVirtuals = {}> = {\n /** @deprecated use accessibleBy helper instead */\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 /** @deprecated use accessibleBy helper instead */\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 { AnyMongoAbility, Generics, Normalize, wrapArray } from '@casl/ability';\nimport { AccessibleFields, GetSubjectTypeAllFieldsExtractor } from '@casl/ability/extra';\nimport type { Document, Model, Schema } from 'mongoose';\n\nexport type AccessibleFieldsOptions =\n {\n getFields(schema: Schema<Document>): string[]\n } &\n ({ only: string | string[] } | { except: string | string[] });\n\nexport const getSchemaPaths: AccessibleFieldsOptions['getFields'] = schema => Object.keys((schema as { paths: object }).paths);\n\nfunction fieldsOf(schema: Schema<Document>, options: Partial<AccessibleFieldsOptions>) {\n const fields = options.getFields!(schema);\n\n if (!options || !('except' in options)) {\n return fields;\n }\n\n const excludedFields = wrapArray(options.except);\n return fields.filter(field => excludedFields.indexOf(field) === -1);\n}\n\ntype GetAccessibleFields<T> = <U extends AnyMongoAbility>(\n this: Model<T> | T,\n ability: U,\n action?: Normalize<Generics<U>['abilities']>[0]\n) => string[];\n\nexport interface AccessibleFieldsModel<\n T,\n TQueryHelpers = {},\n TMethods = {},\n TVirtuals = {}\n> extends Model<T, TQueryHelpers, TMethods & AccessibleFieldDocumentMethods<T>, TVirtuals> {\n accessibleFieldsBy: GetAccessibleFields<T>\n}\n\nexport interface AccessibleFieldDocumentMethods<T = Document> {\n accessibleFieldsBy: GetAccessibleFields<T>\n}\n\n/**\n * @deprecated Mongoose recommends against `extends Document`, prefer to use `AccessibleFieldsModel` instead.\n * See here: https://mongoosejs.com/docs/typescript.html#using-extends-document\n */\nexport interface AccessibleFieldsDocument extends Document, AccessibleFieldDocumentMethods {}\n\nfunction getAllSchemaFieldsFactory() {\n let getAllFields: GetSubjectTypeAllFieldsExtractor;\n return (schema: Schema<any>, options: Partial<AccessibleFieldsOptions>) => {\n if (!getAllFields) {\n const ALL_FIELDS = options && 'only' in options\n ? wrapArray(options.only as string[])\n : fieldsOf(schema, options);\n getAllFields = () => ALL_FIELDS;\n }\n\n return getAllFields;\n };\n}\n\nexport function accessibleFieldsPlugin(\n schema: Schema<any>,\n rawOptions?: Partial<AccessibleFieldsOptions>\n): void {\n const options = { getFields: getSchemaPaths, ...rawOptions };\n const getAllFields = getAllSchemaFieldsFactory();\n\n function instanceAccessibleFields(this: Document, ability: AnyMongoAbility, action?: string) {\n return new AccessibleFields(ability, action || 'read', getAllFields(schema, options)).of(this);\n }\n\n function modelAccessibleFields(this: Model<unknown>, ability: AnyMongoAbility, action?: string) {\n // using fake document because at this point we don't know how Ability's detectSubjectType was configured:\n // does it use classes or strings?\n const fakeDocument = { constructor: this };\n return new AccessibleFields(ability, action || 'read', getAllFields(schema, options)).of(fakeDocument);\n }\n\n schema.statics.accessibleFieldsBy = modelAccessibleFields;\n schema.method('accessibleFieldsBy', instanceAccessibleFields);\n}\n","import { AnyMongoAbility, Generics } from \"@casl/ability\";\nimport { AccessibleFields, 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"],"names":["convertToMongoQuery","rule","conditions","inverted","$nor","EMPTY_RESULT_QUERY","$expr","$eq","AccessibleRecords","constructor","_ability","_action","this","ofType","subjectType","query","rulesToQuery","accessibleBy","ability","action","accessibleRecords","baseQuery","detectSubjectType","model","TypeError","modelName","and","modelAccessibleBy","where","queryAccessibleBy","accessibleRecordsPlugin","schema","statics","getSchemaPaths","Object","keys","paths","fieldsOf","options","fields","getFields","excludedFields","wrapArray","except","filter","field","indexOf","getAllSchemaFieldsFactory","getAllFields","ALL_FIELDS","only","accessibleFieldsPlugin","rawOptions","assign","instanceAccessibleFields","AccessibleFields","of","modelAccessibleFields","fakeDocument","accessibleFieldsBy","method","getSubjectTypeAllFieldsExtractor","type","Model","mongoose","models","Error"],"mappings":"2IAGA,SAASA,EAAoBC,GAC3B,MAAMC,EAAaD,EAAKC,WACxB,OAAOD,EAAKE,SAAW,CAAEC,KAAM,CAACF,IAAgBA,CAClD,CAEO,MAAMG,EAAqB,CAAEC,MAAO,CAAEC,IAAK,CAAC,EAAG,KAC/C,MAAMC,EACXC,WAAAA,CACmBC,EACAC,GACjBC,KAFiBF,EAAAA,EAAyBE,KACzBD,EAAAA,CAChB,CAKHE,MAAAA,CAAOC,GACL,MAAMC,EAAQC,EAAaJ,KAAKF,EAAUE,KAAKD,EAASG,EAAad,GACrE,OAAOe,IAAU,KAAOV,EAAqBU,CAC/C,EAUK,SAASE,EACdC,EACAC,EAAuC,QAEvC,OAAO,IAAIX,EAAkBU,EAASC,EACxC,CChCA,SAASC,EACPC,EACAH,EACAC,GAEA,MAAML,EAAcI,EAAQI,kBAAkB,CAC5Cb,YAAaY,EAAUE,QAGzB,IAAKT,EACH,MAAM,IAAIU,UAAU,kCAAkCH,EAAUE,MAAME,2CAGxE,MAAMV,EAAQE,EAAaC,EAASC,GAAQN,OAAOC,GAEnD,OAAOO,EAAUK,IAAI,CAACX,GACxB,CAsCA,SAASY,EAAwCT,EAA0BC,GACzE,OAAOC,EAAkBR,KAAKgB,QAASV,EAASC,EAClD,CAEA,SAASU,EAEPX,EACAC,GAEA,OAAOC,EAAkBR,KAAMM,EAASC,EAC1C,CAEO,SAASW,EAAwBC,GACrCA,EAAOhB,MAAkCE,aAAeY,EACzDE,EAAOC,QAAQf,aAAeU,CAChC,CC/DaM,MAAAA,EAAuDF,GAAUG,OAAOC,KAAMJ,EAA6BK,OAExH,SAASC,EAASN,EAA0BO,GAC1C,MAAMC,EAASD,EAAQE,UAAWT,GAElC,IAAKO,KAAa,WAAYA,GAC5B,OAAOC,EAGT,MAAME,EAAiBC,EAAUJ,EAAQK,QACzC,OAAOJ,EAAOK,QAAOC,GAASJ,EAAeK,QAAQD,MAAY,GACnE,CA2BA,SAASE,IACP,IAAIC,EACJ,MAAO,CAACjB,EAAqBO,KAC3B,IAAKU,EAAc,CACjB,MAAMC,EAAaX,GAAW,SAAUA,EACpCI,EAAUJ,EAAQY,MAClBb,EAASN,EAAQO,GACrBU,EAAeA,IAAMC,CACvB,CAEA,OAAOD,CAAY,CAEvB,CAEO,SAASG,EACdpB,EACAqB,GAEA,MAAMd,EAAOJ,OAAAmB,OAAA,CAAKb,UAAWP,GAAmBmB,GAChD,MAAMJ,EAAeD,IAErB,SAASO,EAAyCpC,EAA0BC,GAC1E,OAAO,IAAIoC,EAAiBrC,EAASC,GAAU,OAAQ6B,EAAajB,EAAQO,IAAUkB,GAAG5C,KAC3F,CAEA,SAAS6C,EAA4CvC,EAA0BC,GAG7E,MAAMuC,EAAe,CAAEjD,YAAaG,MACpC,OAAO,IAAI2C,EAAiBrC,EAASC,GAAU,OAAQ6B,EAAajB,EAAQO,IAAUkB,GAAGE,EAC3F,CAEA3B,EAAOC,QAAQ2B,mBAAqBF,EACpC1B,EAAO6B,OAAO,qBAAsBN,EACtC,CC9EA,MAAMO,EAAsEC,IAC1E,MAAMC,SAAeD,IAAS,SAAWE,EAASC,OAAOH,GAAQA,EACjE,IAAKC,EAAO,MAAM,IAAIG,MAAM,2BAA2BJ,MACvD,MAAO,WAAYC,EAAQ7B,OAAOC,KAAM4B,EAAMhC,OAAeK,OAAS,EAAE,EAGnE,SAASuB,EACdzC,EACAC,EAAuC,QAEvC,OAAO,IAAIoC,EAAiBrC,EAASC,EAAQ0C,EAC/C"}