@casl/mongoose 8.0.2 → 8.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/es6c/index.js +1 -1
- package/dist/es6c/index.js.map +1 -1
- package/dist/es6m/index.mjs +1 -1
- package/dist/es6m/index.mjs.map +1 -1
- package/dist/types/plugins/accessible_fields.d.ts +3 -3
- package/dist/types/plugins/accessible_records.d.ts +2 -2
- package/package.json +16 -15
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/%40casl%2Fmongoose)
|
|
4
4
|
[](https://www.npmjs.com/package/%40casl%2Fmongoose)
|
|
5
|
-
[](https://github.com/stalniy/casl/discussions)
|
|
6
6
|
|
|
7
7
|
This package integrates [CASL] and [MongoDB]. In other words, it allows to fetch records based on CASL rules from MongoDB and answer questions like: "Which records can be read?" or "Which records can be updated?".
|
|
8
8
|
|
|
@@ -63,7 +63,9 @@ posts = await db.collection('posts').find({
|
|
|
63
63
|
// returns { authorId: 1 }
|
|
64
64
|
const permissionRestrictedConditions = accessibleBy(ability, 'update').ofType('Post');
|
|
65
65
|
|
|
66
|
+
// DANGER DO NOT DO THIS (see above use $and)
|
|
66
67
|
const query = {
|
|
68
|
+
// This is bad and potentially wrong code
|
|
67
69
|
...permissionRestrictedConditions,
|
|
68
70
|
authorId: 2
|
|
69
71
|
};
|
|
@@ -90,7 +92,7 @@ Historically, `@casl/mongoose` was intended for super easy integration with [mon
|
|
|
90
92
|
|
|
91
93
|
### Accessible Records plugin
|
|
92
94
|
|
|
93
|
-
This plugin is deprecated, the recommended way is to use [`accessibleBy` helper function](#accessibleBy-helper)
|
|
95
|
+
This plugin is deprecated, the recommended way is to use [`accessibleBy` helper function](#accessibleBy-helper).
|
|
94
96
|
|
|
95
97
|
`accessibleRecordsPlugin` is a plugin which adds `accessibleBy` method to query and static methods of mongoose models. We can add this plugin globally:
|
|
96
98
|
|
package/dist/es6c/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var t=require("@casl/ability/extra");var n=require("@casl/ability");var r=require("mongoose");function e(t){const n=t.conditions;return t.inverted?{$nor:[n]}:n}const o={$expr:{$eq:[0,1]}};class c{constructor(t,n){this.t=t;this.o=n}ofType(n){const r=t.rulesToQuery(this.t,this.o,n,e);return r===null?o:r}}function s(t,n="read"){return new c(t,n)}function i(t,n,r){const e=n.detectSubjectType({constructor:t.model});if(!e)throw new TypeError(`Cannot detect subject type of "${t.model.modelName}" to return accessible records`);const o=s(n,r).ofType(e);return t.and([o])}function u(t,n){return i(this.where(),t,n)}function f(t,n){return i(this,t,n)}function a(t){t.query.accessibleBy=f;t.statics.accessibleBy=u}const l=t=>Object.keys(t.paths);function h(t,r){const e=r.getFields(t);if(!r||!("except"in r))return e;const o=n.wrapArray(r.except);return e.filter(
|
|
1
|
+
"use strict";var t=require("@casl/ability/extra");var n=require("@casl/ability");var r=require("mongoose");function e(t){const n=t.conditions;return t.inverted?{$nor:[n]}:n}const o={$expr:{$eq:[0,1]}};class c{constructor(t,n){this.t=t;this.o=n}ofType(n){const r=t.rulesToQuery(this.t,this.o,n,e);return r===null?o:r}}function s(t,n="read"){return new c(t,n)}function i(t,n,r){const e=n.detectSubjectType({constructor:t.model});if(!e)throw new TypeError(`Cannot detect subject type of "${t.model.modelName}" to return accessible records`);const o=s(n,r).ofType(e);return t.and([o])}function u(t,n){return i(this.where(),t,n)}function f(t,n){return i(this,t,n)}function a(t){t.query.accessibleBy=f;t.statics.accessibleBy=u}const l=t=>Object.keys(t.paths);function h(t,r){const e=r.getFields(t);if(!r||!("except"in r))return e;const o=n.wrapArray(r.except);return e.filter(t=>o.indexOf(t)===-1)}function p(){let t;return(r,e)=>{if(!t){const o=e&&"only"in e?n.wrapArray(e.only):h(r,e);t=()=>o}return t}}function d(n,r){const e=Object.assign({getFields:l},r);const o=p();function c(r,c){return new t.AccessibleFields(r,c||"read",o(n,e)).of(this)}function s(r,c){const s={constructor:this};return new t.AccessibleFields(r,c||"read",o(n,e)).of(s)}n.statics.accessibleFieldsBy=s;n.method("accessibleFieldsBy",c)}const w=t=>{const n=typeof t==="string"?r.models[t]:t;if(!n)throw new Error(`Unknown mongoose model "${t}"`);return"schema"in n?Object.keys(n.schema.paths):[]};function b(n,r="read"){return new t.AccessibleFields(n,r,w)}exports.accessibleBy=s;exports.accessibleFieldsBy=b;exports.accessibleFieldsPlugin=d;exports.accessibleRecordsPlugin=a;exports.getSchemaPaths=l;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/es6c/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","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":"2GAGA,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,EAAYA,aAACJ,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,UAAW,kCAAiCH,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,EAAAA,UAAUJ,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,EAASA,UAACJ,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,EAAgBA,iBAACrC,EAASC,GAAU,OAAQ6B,EAAajB,EAAQO,IAAUkB,GAAG5C,KAC3F,CAEA,SAAS6C,EAA4CvC,EAA0BC,GAG7E,MAAMuC,EAAe,CAAEjD,YAAaG,MACpC,OAAO,IAAI2C,EAAgBA,iBAACrC,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,MAAO,2BAA0BJ,MACvD,MAAO,WAAYC,EAAQ7B,OAAOC,KAAM4B,EAAMhC,OAAeK,OAAS,EAAE,EAGnE,SAASuB,EACdzC,EACAC,EAAuC,QAEvC,OAAO,IAAIoC,EAAAA,iBAAiBrC,EAASC,EAAQ0C,EAC/C"}
|
|
1
|
+
{"version":3,"file":"index.js","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 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 /** @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 as Doc, Model, Schema } from 'mongoose';\n\nexport type AccessibleFieldsOptions =\n {\n getFields(schema: Schema<Doc>): 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<Doc>, 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 = Doc> {\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: Doc, 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":"2GAGA,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,EAAAA,aAAaJ,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,CAkCA,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,CC3DO,MAAMM,EAAuDF,GAAUG,OAAOC,KAAMJ,EAA6BK,OAExH,SAASC,EAASN,EAAqBO,GACrC,MAAMC,EAASD,EAAQE,UAAWT,GAElC,IAAKO,KAAa,WAAYA,GAC5B,OAAOC,EAGT,MAAME,EAAiBC,EAAAA,UAAUJ,EAAQK,QACzC,OAAOJ,EAAOK,OAAOC,GAASJ,EAAeK,QAAQD,QACvD,CA2BA,SAASE,IACP,IAAIC,EACJ,MAAO,CAACjB,EAAqBO,KAC3B,IAAKU,EAAc,CACjB,MAAMC,EAAaX,GAAW,SAAUA,EACpCI,EAAAA,UAAUJ,EAAQY,MAClBb,EAASN,EAAQO,GACrBU,EAAeA,IAAMC,CACvB,CAEA,OAAOD,EAEX,CAEO,SAASG,EACdpB,EACAqB,GAEA,MAAMd,EAAOJ,OAAAmB,OAAA,CAAKb,UAAWP,GAAmBmB,GAChD,MAAMJ,EAAeD,IAErB,SAASO,EAAoCpC,EAA0BC,GACrE,OAAO,IAAIoC,EAAAA,iBAAiBrC,EAASC,GAAU,OAAQ6B,EAAajB,EAAQO,IAAUkB,GAAG5C,KAC3F,CAEA,SAAS6C,EAA4CvC,EAA0BC,GAG7E,MAAMuC,EAAe,CAAEjD,YAAaG,MACpC,OAAO,IAAI2C,EAAAA,iBAAiBrC,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,IAGjE,SAASuB,EACdzC,EACAC,EAAuC,QAEvC,OAAO,IAAIoC,EAAAA,iBAAiBrC,EAASC,EAAQ0C,EAC/C"}
|
package/dist/es6m/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{rulesToQuery as t,AccessibleFields as n}from"@casl/ability/extra";import{wrapArray as r}from"@casl/ability";import o from"mongoose";function e(t){const n=t.conditions;return t.inverted?{$nor:[n]}:n}const c={$expr:{$eq:[0,1]}};class s{constructor(t,n){this.t=t;this.o=n}ofType(n){const r=t(this.t,this.o,n,e);return r===null?c:r}}function i(t,n="read"){return new s(t,n)}function u(t,n,r){const o=n.detectSubjectType({constructor:t.model});if(!o)throw new TypeError(`Cannot detect subject type of "${t.model.modelName}" to return accessible records`);const e=i(n,r).ofType(o);return t.and([e])}function f(t,n){return u(this.where(),t,n)}function a(t,n){return u(this,t,n)}function l(t){t.query.accessibleBy=a;t.statics.accessibleBy=f}const h=t=>Object.keys(t.paths);function m(t,n){const o=n.getFields(t);if(!n||!("except"in n))return o;const e=r(n.except);return o.filter(
|
|
1
|
+
import{rulesToQuery as t,AccessibleFields as n}from"@casl/ability/extra";import{wrapArray as r}from"@casl/ability";import o from"mongoose";function e(t){const n=t.conditions;return t.inverted?{$nor:[n]}:n}const c={$expr:{$eq:[0,1]}};class s{constructor(t,n){this.t=t;this.o=n}ofType(n){const r=t(this.t,this.o,n,e);return r===null?c:r}}function i(t,n="read"){return new s(t,n)}function u(t,n,r){const o=n.detectSubjectType({constructor:t.model});if(!o)throw new TypeError(`Cannot detect subject type of "${t.model.modelName}" to return accessible records`);const e=i(n,r).ofType(o);return t.and([e])}function f(t,n){return u(this.where(),t,n)}function a(t,n){return u(this,t,n)}function l(t){t.query.accessibleBy=a;t.statics.accessibleBy=f}const h=t=>Object.keys(t.paths);function m(t,n){const o=n.getFields(t);if(!n||!("except"in n))return o;const e=r(n.except);return o.filter(t=>e.indexOf(t)===-1)}function p(){let t;return(n,o)=>{if(!t){const e=o&&"only"in o?r(o.only):m(n,o);t=()=>e}return t}}function d(t,r){const o=Object.assign({getFields:h},r);const e=p();function c(r,c){return new n(r,c||"read",e(t,o)).of(this)}function s(r,c){const s={constructor:this};return new n(r,c||"read",e(t,o)).of(s)}t.statics.accessibleFieldsBy=s;t.method("accessibleFieldsBy",c)}const w=t=>{const n=typeof t==="string"?o.models[t]:t;if(!n)throw new Error(`Unknown mongoose model "${t}"`);return"schema"in n?Object.keys(n.schema.paths):[]};function b(t,r="read"){return new n(t,r,w)}export{i as accessibleBy,b as accessibleFieldsBy,d as accessibleFieldsPlugin,l as accessibleRecordsPlugin,h as getSchemaPaths};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/es6m/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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,UAAW,kCAAiCH,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,MAAO,2BAA0BJ,MACvD,MAAO,WAAYC,EAAQ7B,OAAOC,KAAM4B,EAAMhC,OAAeK,OAAS,EAAE,EAGnE,SAASuB,EACdzC,EACAC,EAAuC,QAEvC,OAAO,IAAIoC,EAAiBrC,EAASC,EAAQ0C,EAC/C"}
|
|
1
|
+
{"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 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 /** @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 as Doc, Model, Schema } from 'mongoose';\n\nexport type AccessibleFieldsOptions =\n {\n getFields(schema: Schema<Doc>): 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<Doc>, 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 = Doc> {\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: Doc, 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,CAkCA,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,CC3DO,MAAMM,EAAuDF,GAAUG,OAAOC,KAAMJ,EAA6BK,OAExH,SAASC,EAASN,EAAqBO,GACrC,MAAMC,EAASD,EAAQE,UAAWT,GAElC,IAAKO,KAAa,WAAYA,GAC5B,OAAOC,EAGT,MAAME,EAAiBC,EAAUJ,EAAQK,QACzC,OAAOJ,EAAOK,OAAOC,GAASJ,EAAeK,QAAQD,QACvD,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,EAEX,CAEO,SAASG,EACdpB,EACAqB,GAEA,MAAMd,EAAOJ,OAAAmB,OAAA,CAAKb,UAAWP,GAAmBmB,GAChD,MAAMJ,EAAeD,IAErB,SAASO,EAAoCpC,EAA0BC,GACrE,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,IAGjE,SAASuB,EACdzC,EACAC,EAAuC,QAEvC,OAAO,IAAIoC,EAAiBrC,EAASC,EAAQ0C,EAC/C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnyMongoAbility, Generics, Normalize } from '@casl/ability';
|
|
2
|
-
import type { Document, Model, Schema } from 'mongoose';
|
|
2
|
+
import type { Document as Doc, Model, Schema } from 'mongoose';
|
|
3
3
|
export type AccessibleFieldsOptions = {
|
|
4
|
-
getFields(schema: Schema<
|
|
4
|
+
getFields(schema: Schema<Doc>): string[];
|
|
5
5
|
} & ({
|
|
6
6
|
only: string | string[];
|
|
7
7
|
} | {
|
|
@@ -12,7 +12,7 @@ type GetAccessibleFields<T> = <U extends AnyMongoAbility>(this: Model<T> | T, ab
|
|
|
12
12
|
export interface AccessibleFieldsModel<T, TQueryHelpers = {}, TMethods = {}, TVirtuals = {}> extends Model<T, TQueryHelpers, TMethods & AccessibleFieldDocumentMethods<T>, TVirtuals> {
|
|
13
13
|
accessibleFieldsBy: GetAccessibleFields<T>;
|
|
14
14
|
}
|
|
15
|
-
export interface AccessibleFieldDocumentMethods<T =
|
|
15
|
+
export interface AccessibleFieldDocumentMethods<T = Doc> {
|
|
16
16
|
accessibleFieldsBy: GetAccessibleFields<T>;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AnyMongoAbility, Generics, Normalize } from '@casl/ability';
|
|
2
|
-
import { HydratedDocument, Model, QueryWithHelpers, Schema } from 'mongoose';
|
|
3
|
-
type GetAccessibleRecords<T, TQueryHelpers, TMethods, TVirtuals> = <U extends AnyMongoAbility>(ability: U, action?: Normalize<Generics<U>['abilities']>[0]) => QueryWithHelpers<
|
|
2
|
+
import type { HydratedDocument, Model, QueryWithHelpers, Schema } from 'mongoose';
|
|
3
|
+
type GetAccessibleRecords<T, TQueryHelpers, TMethods, TVirtuals> = <U extends AnyMongoAbility>(ability: U, action?: Normalize<Generics<U>['abilities']>[0]) => QueryWithHelpers<T[], T, AccessibleRecordQueryHelpers<T, TQueryHelpers, TMethods, TVirtuals>>;
|
|
4
4
|
export type AccessibleRecordQueryHelpers<T, TQueryHelpers = {}, TMethods = {}, TVirtuals = {}> = {
|
|
5
5
|
/** @deprecated use accessibleBy helper instead */
|
|
6
6
|
accessibleBy: GetAccessibleRecords<HydratedDocument<T, TMethods, TVirtuals>, TQueryHelpers, TMethods, TVirtuals>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@casl/mongoose",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.4",
|
|
4
4
|
"description": "Allows to query accessible records from MongoDB based on CASL rules",
|
|
5
5
|
"main": "dist/es6c/index.js",
|
|
6
6
|
"es2015": "dist/es6m/index.mjs",
|
|
@@ -21,15 +21,6 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://casl.js.org",
|
|
24
|
-
"scripts": {
|
|
25
|
-
"prebuild": "rm -rf dist/* && npm run build.types",
|
|
26
|
-
"build": "BUILD_TYPES=es6m,es6c dx rollup -e @casl/ability/extra,@casl/ability,mongoose",
|
|
27
|
-
"build.types": "dx tsc",
|
|
28
|
-
"lint": "dx eslint src/ spec/",
|
|
29
|
-
"test": "dx jest",
|
|
30
|
-
"prerelease": "npm run lint && npm test && NODE_ENV=production npm run build",
|
|
31
|
-
"release": "dx semantic-release"
|
|
32
|
-
},
|
|
33
24
|
"keywords": [
|
|
34
25
|
"casl",
|
|
35
26
|
"mongo",
|
|
@@ -41,19 +32,29 @@
|
|
|
41
32
|
"license": "MIT",
|
|
42
33
|
"peerDependencies": {
|
|
43
34
|
"@casl/ability": "^6.7.0",
|
|
44
|
-
"mongoose": "^6.0.13 || ^7.0.0 || ^8.0.0"
|
|
35
|
+
"mongoose": "^6.0.13 || ^7.0.0 || ^8.0.0 || ^9.0.0"
|
|
45
36
|
},
|
|
46
37
|
"devDependencies": {
|
|
47
38
|
"@casl/ability": "^6.0.0",
|
|
48
|
-
"@casl/dx": "
|
|
39
|
+
"@casl/dx": "^1.0.0",
|
|
49
40
|
"@types/jest": "^29.0.0",
|
|
41
|
+
"@types/node": "^24.0.0",
|
|
50
42
|
"chai": "^4.1.0",
|
|
51
43
|
"chai-spies": "^1.0.0",
|
|
52
|
-
"mongoose": "^
|
|
44
|
+
"mongoose": "^9.0.0"
|
|
53
45
|
},
|
|
54
46
|
"files": [
|
|
55
47
|
"dist",
|
|
56
48
|
"*.d.ts",
|
|
57
49
|
"index.js"
|
|
58
|
-
]
|
|
59
|
-
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build.prepare": "rm -rf dist/* && npm run build.types",
|
|
53
|
+
"build": "npm run build.prepare && BUILD_TYPES=es6m,es6c dx rollup -e @casl/ability/extra,@casl/ability,mongoose",
|
|
54
|
+
"build.types": "dx tsc",
|
|
55
|
+
"lint": "dx eslint src/ spec/",
|
|
56
|
+
"test": "dx jest",
|
|
57
|
+
"release.prepare": "npm run lint && npm test && NODE_ENV=production npm run build",
|
|
58
|
+
"release": "npm run release.prepare && pnpm publish"
|
|
59
|
+
}
|
|
60
|
+
}
|