UNPKG

@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.28 kB
import{createFactory as t,$exists as i,$elemMatch as e,$options as s,$regex as n,$size as r,$all as o,$nin as c,$in as u,$gte as h,$gt as l,$lte as a,$lt as f,$ne as d,$eq as y,and as p,exists as b,elemMatch as w,regex as g,size as $,all as A,nin as m,within as M,gte as j,gt as E,lte as v,lt as x,ne as _,eq as F}from"@ucast/mongo2js";const O=Object.hasOwn||((t,i)=>Object.prototype.hasOwnProperty.call(t,i));function C(t){return Array.isArray(t)?t:[t]}const R="__caslSubjectType__";function P(t,i){if(i)if(!O(i,R))Object.defineProperty(i,R,{value:t});else if(t!==i[R])throw new Error(`Trying to cast object to subject type ${t} but previously it was casted to ${i[R]}`);return i}const S=t=>{const i=typeof t;return i==="string"||i==="function"};const T=t=>t.modelName||t.name;function z(t){return typeof t==="string"?t:T(t)}function B(t){if(O(t,R))return t[R];return T(t.constructor)}const q={function:t=>t.constructor,string:B};function D(t,i,e){let s=C(i);let n=0;while(n<s.length){const i=s[n++];if(O(t,i))s=e(s,t[i])}return s}function Y(t,i){if(typeof i==="string"&&t.indexOf(i)!==-1)return i;for(let e=0;e<i.length;e++)if(t.indexOf(i[e])!==-1)return i[e];return null}const k=(t,i)=>t.concat(i);function L(t,i){if(i in t)throw new Error(`Cannot use "${i}" as an alias because it's reserved action.`);const e=Object.keys(t);const s=(t,e)=>{const s=Y(t,e);if(s)throw new Error(`Detected cycle ${s} -> ${t.join(", ")}`);const n=typeof e==="string"&&e===i||t.indexOf(i)!==-1||Array.isArray(e)&&e.indexOf(i)!==-1;if(n)throw new Error(`Cannot make an alias to "${i}" because this is reserved action`);return t.concat(e)};for(let i=0;i<e.length;i++)D(t,e[i],s)}function U(t,i){if(!i||i.skipValidate!==false)L(t,i&&i.anyAction||"manage");return i=>D(t,i,k)}function G(t,i,e){for(let s=e;s<i.length;s++)t.push(i[s])}function H(t,i){if(!t||!t.length)return i||[];if(!i||!i.length)return t||[];let e=0;let s=0;const n=[];while(e<t.length&&s<i.length)if(t[e].priority<i[s].priority){n.push(t[e]);e++}else{n.push(i[s]);s++}G(n,t,e);G(n,i,s);return n}function I(t,i,e){let s=t.get(i);if(!s){s=e();t.set(i,s)}return s}const J=t=>t;function K(t,i){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&&!i.fieldMatcher)throw new Error('You need to pass "fieldMatcher" option in order to restrict access by fields');if(t.conditions&&!i.conditionsMatcher)throw new Error('You need to pass "conditionsMatcher" option in order to restrict access by conditions')}class N{constructor(t,i,e=0){K(t,i);this.action=i.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?C(t.fields):void 0;this.priority=e;this.t=i}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||S(t))return!this.inverted;const i=this.i();return i(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 Q(t,i){const e={value:t,prev:i,next:null};if(i)i.next=e;return e}function V(t){if(t.next)t.next.prev=t.prev;if(t.prev)t.prev.next=t.next;t.next=t.prev=null}const W=t=>({value:t.value,prev:t.prev,next:t.next});const X=()=>({rules:[],merged:false});const Z=()=>new Map;class tt{constructor(t=[],i={}){this.h=false;this.l=new Map;this.p={conditionsMatcher:i.conditionsMatcher,fieldMatcher:i.fieldMatcher,resolveAction:i.resolveAction||J};this.$=i.anyAction||"manage";this.A=i.anySubjectType||"all";this.m=t;this.M=!!i.detectSubjectType;this.j=i.detectSubjectType||B;this.v(t)}get rules(){return this.m}detectSubjectType(t){if(S(t))return t;if(!t)return this.A;return this.j(t)}update(t){const i={rules:t,ability:this,target:this};this._("update",i);this.m=t;this.v(t);this._("updated",i);return this}v(t){const i=new Map;let e;for(let s=t.length-1;s>=0;s--){const n=t.length-s-1;const r=new N(t[s],this.p,n);const o=C(r.action);const c=C(r.subject||this.A);if(!this.h&&r.fields)this.h=true;for(let t=0;t<c.length;t++){const s=I(i,c[t],Z);if(e===void 0)e=typeof c[t];if(typeof c[t]!==e&&e!=="mixed")e="mixed";for(let t=0;t<o.length;t++)I(s,o[t],X).rules.push(r)}}this.l=i;if(e!=="mixed"&&!this.M){const t=q[e]||q.string;this.j=t}}possibleRulesFor(t,i=this.A){if(!S(i))throw new Error('"possibleRulesFor" accepts only subject types (i.e., string or class) as the 2nd parameter');const e=I(this.l,i,Z);const s=I(e,t,X);if(s.merged)return s.rules;const n=t!==this.$&&e.has(this.$)?e.get(this.$).rules:void 0;let r=H(s.rules,n);if(i!==this.A)r=H(r,this.possibleRulesFor(t,this.A));s.rules=r;s.merged=true;return r}rulesFor(t,i,e){const s=this.possibleRulesFor(t,i);if(e&&typeof e!=="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(e))}actionsFor(t){if(!S(t))throw new Error('"actionsFor" accepts only subject types (i.e., string or class) as a parameter');const i=new Set;const e=this.l.get(t);if(e)Array.from(e.keys()).forEach(t=>i.add(t));const s=t!==this.A?this.l.get(this.A):void 0;if(s)Array.from(s.keys()).forEach(t=>i.add(t));return Array.from(i)}on(t,i){this.F=this.F||new Map;const e=this.F;const s=e.get(t)||null;const n=Q(i,s);e.set(t,n);return()=>{const i=e.get(t);if(!n.next&&!n.prev&&i===n)e.delete(t);else if(n===i)e.set(t,n.prev);V(n)}}_(t,i){if(!this.F)return;let e=this.F.get(t)||null;while(e!==null){const t=e.prev?W(e.prev):null;e.value(i);e=t}}}class PureAbility extends tt{can(t,i,e){const s=this.relevantRuleFor(t,i,e);return!!s&&!s.inverted}relevantRuleFor(t,i,e){const s=this.detectSubjectType(i);const n=this.rulesFor(t,s,e);for(let t=0,e=n.length;t<e;t++)if(n[t].matchesConditions(i))return n[t];return null}cannot(t,i,e){return!this.can(t,i,e)}}const it={$eq:y,$ne:d,$lt:f,$lte:a,$gt:l,$gte:h,$in:u,$nin:c,$all:o,$size:r,$regex:n,$options:s,$elemMatch:e,$exists:i};const et={eq:F,ne:_,lt:x,lte:v,gt:E,gte:j,in:M,nin:m,all:A,size:$,regex:g,elemMatch:w,exists:b,and:p};const st=(i,e,s)=>t(Object.assign({},it,i),Object.assign({},et,e),s);const nt=t(it,et);const rt=/[-/\\^$+?.()|[\]{}]/g;const ot=/\.?\*+\.?/g;const ct=/\*+/;const ut=/\./g;function ht(t,i,e){const s=e[0]==="*"||t[0]==="."&&t[t.length-1]==="."?"+":"*";const n=t.indexOf("**")===-1?"[^.]":".";const r=t.replace(ut,"\\$&").replace(ct,n+s);return i+t.length===e.length?`(?:${r})?`:r}function lt(t,i,e){if(t==="."&&(e[i-1]==="*"||e[i+1]==="*"))return t;return`\\${t}`}function at(t){const i=t.map(t=>t.replace(rt,lt).replace(ot,ht));const e=i.length>1?`(?:${i.join("|")})`:i[0];return new RegExp(`^${e}$`)}const ft=t=>{let i;return e=>{if(typeof i==="undefined")i=t.every(t=>t.indexOf("*")===-1)?null:at(t);return i===null?t.indexOf(e)!==-1:i.test(e)}};class Ability extends PureAbility{constructor(t=[],i={}){super(t,Object.assign({conditionsMatcher:nt,fieldMatcher:ft},i))}}function createMongoAbility(t=[],i={}){return new PureAbility(t,Object.assign({conditionsMatcher:nt,fieldMatcher:ft},i))}function isAbilityClass(t){return t.prototype!==void 0&&typeof t.prototype.possibleRulesFor==="function"}class dt{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,i,e,s)=>this.R(t,i,e,s,false);this.cannot=(t,i,e,s)=>this.R(t,i,e,s,true);this.build=t=>isAbilityClass(this.C)?new this.C(this.rules,t):this.C(this.rules,t)}R(t,i,e,s,n){const r={action:t};if(n)r.inverted=n;if(i){r.subject=i;if(Array.isArray(e)||typeof e==="string")r.fields=e;else if(typeof e!=="undefined")r.conditions=e;if(typeof s!=="undefined")r.conditions=s}this.rules.push(r);return new dt(r)}}function defineAbility(t,i){const e=new AbilityBuilder(createMongoAbility);const s=t(e.can,e.cannot);if(s&&typeof s.then==="function")return s.then(()=>e.build(i));return e.build(i)}const yt=t=>`Cannot execute "${t.action}" on "${t.subjectType}"`;const pt=function t(i){this.message=i};pt.prototype=Object.create(Error.prototype);class ForbiddenError extends pt{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,i,e){const s=this.unlessCan(t,i,e);if(s)throw s}unlessCan(t,i,e){const s=this.ability.relevantRuleFor(t,i,e);if(s&&!s.inverted)return;this.action=t;this.subject=i;this.subjectType=z(this.ability.detectSubjectType(i));this.field=e;const n=s?s.reason:"";this.message=this.message||n||this.constructor.P(this);return this}}ForbiddenError.P=yt;var bt=Object.freeze({__proto__:null});export{Ability,AbilityBuilder,ForbiddenError,PureAbility,st as buildMongoQueryMatcher,U as createAliasResolver,createMongoAbility,defineAbility,B as detectSubjectType,ft as fieldPatternMatcher,yt as getDefaultErrorMessage,bt as hkt,nt as mongoQueryMatcher,P as subject,C as wrapArray}; //# sourceMappingURL=index.mjs.map