@casl/ability
Version:
CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
3 lines (2 loc) • 9.29 kB
JavaScript
;var t=require("@ucast/mongo2js");const e=Object.hasOwn||((t,e)=>Object.prototype.hasOwnProperty.call(t,e));function i(t){return Array.isArray(t)?t:[t]}const s="__caslSubjectType__";function r(t,i){if(i)if(!e(i,s))Object.defineProperty(i,s,{value:t});else if(t!==i[s])throw new Error(`Trying to cast object to subject type ${t} but previously it was casted to ${i[s]}`);return i}const n=t=>{const e=typeof t;return e==="string"||e==="function"};const o=t=>t.modelName||t.name;function c(t){return typeof t==="string"?t:o(t)}function u(t){if(e(t,s))return t[s];return o(t.constructor)}const h={function:t=>t.constructor,string:u};function l(t,s,r){let n=i(s);let o=0;while(o<n.length){const i=n[o++];if(e(t,i))n=r(n,t[i])}return n}function a(t,e){if(typeof e==="string"&&t.indexOf(e)!==-1)return e;for(let i=0;i<e.length;i++)if(t.indexOf(e[i])!==-1)return e[i];return null}const f=(t,e)=>t.concat(e);function d(t,e){if(e in t)throw new Error(`Cannot use "${e}" as an alias because it's reserved action.`);const i=Object.keys(t);const s=(t,i)=>{const s=a(t,i);if(s)throw new Error(`Detected cycle ${s} -> ${t.join(", ")}`);const r=typeof i==="string"&&i===e||t.indexOf(e)!==-1||Array.isArray(i)&&i.indexOf(e)!==-1;if(r)throw new Error(`Cannot make an alias to "${e}" because this is reserved action`);return t.concat(i)};for(let e=0;e<i.length;e++)l(t,i[e],s)}function p(t,e){if(!e||e.skipValidate!==false)d(t,e&&e.anyAction||"manage");return e=>l(t,e,f)}function y(t,e,i){for(let s=i;s<e.length;s++)t.push(e[s])}function b(t,e){if(!t||!t.length)return e||[];if(!e||!e.length)return t||[];let i=0;let s=0;const r=[];while(i<t.length&&s<e.length)if(t[i].priority<e[s].priority){r.push(t[i]);i++}else{r.push(e[s]);s++}y(r,t,i);y(r,e,s);return r}function w(t,e,i){let s=t.get(e);if(!s){s=i();t.set(e,s)}return s}const g=t=>t;function x(t,e){if(Array.isArray(t.fields)&&!t.fields.length)throw new Error("`rawRule.fields` cannot be an empty array. https://bit.ly/390miLa");if(t.fields&&!e.fieldMatcher)throw new Error('You need to pass "fieldMatcher" option in order to restrict access by fields');if(t.conditions&&!e.conditionsMatcher)throw new Error('You need to pass "conditionsMatcher" option in order to restrict access by conditions')}class ${constructor(t,e,s=0){x(t,e);this.action=e.resolveAction(t.action);this.subject=t.subject;this.inverted=!!t.inverted;this.conditions=t.conditions;this.reason=t.reason;this.origin=t;this.fields=t.fields?i(t.fields):void 0;this.priority=s;this.t=e}i(){if(this.conditions&&!this.o)this.o=this.t.conditionsMatcher(this.conditions);return this.o}get ast(){const t=this.i();return t?t.ast:void 0}matchesConditions(t){if(!this.conditions)return true;if(!t||n(t))return!this.inverted;const e=this.i();return e(t)}matchesField(t){if(!this.fields)return true;if(!t)return!this.inverted;if(!this.u)this.u=this.t.fieldMatcher(this.fields);return this.u(t)}}function A(t,e){const i={value:t,prev:e,next:null};if(e)e.next=i;return i}function M(t){if(t.next)t.next.prev=t.prev;if(t.prev)t.prev.next=t.next;t.next=t.prev=null}const m=t=>({value:t.value,prev:t.prev,next:t.next});const v=()=>({rules:[],merged:false});const E=()=>new Map;class j{constructor(t=[],e={}){this.h=false;this.l=new Map;this.p={conditionsMatcher:e.conditionsMatcher,fieldMatcher:e.fieldMatcher,resolveAction:e.resolveAction||g};this.$=e.anyAction||"manage";this.A=e.anySubjectType||"all";this.M=t;this.m=!!e.detectSubjectType;this.v=e.detectSubjectType||u;this.j(t)}get rules(){return this.M}detectSubjectType(t){if(n(t))return t;if(!t)return this.A;return this.v(t)}update(t){const e={rules:t,ability:this,target:this};this._("update",e);this.M=t;this.j(t);this._("updated",e);return this}j(t){const e=new Map;let s;for(let r=t.length-1;r>=0;r--){const n=t.length-r-1;const o=new $(t[r],this.p,n);const c=i(o.action);const u=i(o.subject||this.A);if(!this.h&&o.fields)this.h=true;for(let t=0;t<u.length;t++){const i=w(e,u[t],E);if(s===void 0)s=typeof u[t];if(typeof u[t]!==s&&s!=="mixed")s="mixed";for(let t=0;t<c.length;t++)w(i,c[t],v).rules.push(o)}}this.l=e;if(s!=="mixed"&&!this.m){const t=h[s]||h.string;this.v=t}}possibleRulesFor(t,e=this.A){if(!n(e))throw new Error('"possibleRulesFor" accepts only subject types (i.e., string or class) as the 2nd parameter');const i=w(this.l,e,E);const s=w(i,t,v);if(s.merged)return s.rules;const r=t!==this.$&&i.has(this.$)?i.get(this.$).rules:void 0;let o=b(s.rules,r);if(e!==this.A)o=b(o,this.possibleRulesFor(t,this.A));s.rules=o;s.merged=true;return o}rulesFor(t,e,i){const s=this.possibleRulesFor(t,e);if(i&&typeof i!=="string")throw new Error("The 3rd, `field` parameter is expected to be a string. See https://stalniy.github.io/casl/en/api/casl-ability#can-of-pure-ability for details");if(!this.h)return s;return s.filter(t=>t.matchesField(i))}actionsFor(t){if(!n(t))throw new Error('"actionsFor" accepts only subject types (i.e., string or class) as a parameter');const e=new Set;const i=this.l.get(t);if(i)Array.from(i.keys()).forEach(t=>e.add(t));const s=t!==this.A?this.l.get(this.A):void 0;if(s)Array.from(s.keys()).forEach(t=>e.add(t));return Array.from(e)}on(t,e){this.F=this.F||new Map;const i=this.F;const s=i.get(t)||null;const r=A(e,s);i.set(t,r);return()=>{const e=i.get(t);if(!r.next&&!r.prev&&e===r)i.delete(t);else if(r===e)i.set(t,r.prev);M(r)}}_(t,e){if(!this.F)return;let i=this.F.get(t)||null;while(i!==null){const t=i.prev?m(i.prev):null;i.value(e);i=t}}}class PureAbility extends j{can(t,e,i){const s=this.relevantRuleFor(t,e,i);return!!s&&!s.inverted}relevantRuleFor(t,e,i){const s=this.detectSubjectType(e);const r=this.rulesFor(t,s,i);for(let t=0,i=r.length;t<i;t++)if(r[t].matchesConditions(e))return r[t];return null}cannot(t,e,i){return!this.can(t,e,i)}}const _={$eq:t.$eq,$ne:t.$ne,$lt:t.$lt,$lte:t.$lte,$gt:t.$gt,$gte:t.$gte,$in:t.$in,$nin:t.$nin,$all:t.$all,$size:t.$size,$regex:t.$regex,$options:t.$options,$elemMatch:t.$elemMatch,$exists:t.$exists};const F={eq:t.eq,ne:t.ne,lt:t.lt,lte:t.lte,gt:t.gt,gte:t.gte,in:t.within,nin:t.nin,all:t.all,size:t.size,regex:t.regex,elemMatch:t.elemMatch,exists:t.exists,and:t.and};const O=(e,i,s)=>t.createFactory(Object.assign({},_,e),Object.assign({},F,i),s);const C=t.createFactory(_,F);const R=/[-/\\^$+?.()|[\]{}]/g;const P=/\.?\*+\.?/g;const S=/\*+/;const T=/\./g;function q(t,e,i){const s=i[0]==="*"||t[0]==="."&&t[t.length-1]==="."?"+":"*";const r=t.indexOf("**")===-1?"[^.]":".";const n=t.replace(T,"\\$&").replace(S,r+s);return e+t.length===i.length?`(?:${n})?`:n}function z(t,e,i){if(t==="."&&(i[e-1]==="*"||i[e+1]==="*"))return t;return`\\${t}`}function B(t){const e=t.map(t=>t.replace(R,z).replace(P,q));const i=e.length>1?`(?:${e.join("|")})`:e[0];return new RegExp(`^${i}$`)}const D=t=>{let e;return i=>{if(typeof e==="undefined")e=t.every(t=>t.indexOf("*")===-1)?null:B(t);return e===null?t.indexOf(i)!==-1:e.test(i)}};class Ability extends PureAbility{constructor(t=[],e={}){super(t,Object.assign({conditionsMatcher:C,fieldMatcher:D},e))}}function createMongoAbility(t=[],e={}){return new PureAbility(t,Object.assign({conditionsMatcher:C,fieldMatcher:D},e))}function isAbilityClass(t){return t.prototype!==void 0&&typeof t.prototype.possibleRulesFor==="function"}class Y{constructor(t){this.O=t}because(t){this.O.reason=t;return this}}class AbilityBuilder{constructor(t){this.rules=[];this.C=t;this.can=(t,e,i,s)=>this.R(t,e,i,s,false);this.cannot=(t,e,i,s)=>this.R(t,e,i,s,true);this.build=t=>isAbilityClass(this.C)?new this.C(this.rules,t):this.C(this.rules,t)}R(t,e,i,s,r){const n={action:t};if(r)n.inverted=r;if(e){n.subject=e;if(Array.isArray(i)||typeof i==="string")n.fields=i;else if(typeof i!=="undefined")n.conditions=i;if(typeof s!=="undefined")n.conditions=s}this.rules.push(n);return new Y(n)}}function defineAbility(t,e){const i=new AbilityBuilder(createMongoAbility);const s=t(i.can,i.cannot);if(s&&typeof s.then==="function")return s.then(()=>i.build(e));return i.build(e)}const k=t=>`Cannot execute "${t.action}" on "${t.subjectType}"`;const L=function t(e){this.message=e};L.prototype=Object.create(Error.prototype);class ForbiddenError extends L{static setDefaultMessage(t){this.P=typeof t==="string"?()=>t:t}static from(t){return new this(t)}constructor(t){super("");this.ability=t;if(typeof Error.captureStackTrace==="function"){this.name="ForbiddenError";Error.captureStackTrace(this,this.constructor)}}setMessage(t){this.message=t;return this}throwUnlessCan(t,e,i){const s=this.unlessCan(t,e,i);if(s)throw s}unlessCan(t,e,i){const s=this.ability.relevantRuleFor(t,e,i);if(s&&!s.inverted)return;this.action=t;this.subject=e;this.subjectType=c(this.ability.detectSubjectType(e));this.field=i;const r=s?s.reason:"";this.message=this.message||r||this.constructor.P(this);return this}}ForbiddenError.P=k;var U=Object.freeze({__proto__:null});exports.Ability=Ability;exports.AbilityBuilder=AbilityBuilder;exports.ForbiddenError=ForbiddenError;exports.PureAbility=PureAbility;exports.buildMongoQueryMatcher=O;exports.createAliasResolver=p;exports.createMongoAbility=createMongoAbility;exports.defineAbility=defineAbility;exports.detectSubjectType=u;exports.fieldPatternMatcher=D;exports.getDefaultErrorMessage=k;exports.hkt=U;exports.mongoQueryMatcher=C;exports.subject=r;exports.wrapArray=i;
//# sourceMappingURL=index.js.map