@autofleet/zehut 4.6.3-beta-5cca7a57.5 → 4.6.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/lib/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,`__esModule`,{value:!0});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n&&t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`@autofleet/outbreak`);l=c(l);let u=require(`jsonwebtoken`);u=c(u);let d=require(`node-cache`);d=c(d);let f=require(`object-hash`);f=c(f);let p=require(`moment`);p=c(p);let m=require(`@autofleet/network`);m=c(m);const{DEPRECATED_JWT_SECRET:ee,JWT_NEW_SECRET:te,DEPRECATED_REFRESH_JWT_SECRET:ne,REFRESH_JWT_SECRET:re,DEPRECATION_UNIX_TIMESTAMP:ie}=process.env,h=(e,t,n)=>{let r=(0,p.default)(parseInt(ie||``,10)*1e3);try{let i;if(e){let{iat:t}=u.default.decode(e);i=(0,p.default)(t*1e3)}else i=(0,p.default)();return i.isBefore(r)?t:n}catch{return n}},ae=e=>h(e,ne,re),g=e=>h(e,ee,te),_=e=>e.replace(`Bearer `,``),v=(e,t)=>{let n=_(e);return u.default.verify(n,t||g(n))},oe=`00000000-0000-0000-0000-000000000000`,se=`ffffffff-ffff-ffff-ffff-ffffffffffff`,y=`[0-9a-f]`,ce=RegExp(`^(?:${y}{8}-${y}{4}-[1-8]${y}{3}-[89ab]${y}{3}-${y}{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`,`i`);function le(e){return typeof e==`string`&&ce.test(e)}const ue=10,de=process.env.API_GATEWAY_URL||`https://api.autofleet.io`,b=new m.default({serviceName:`IDENTITY_MS`,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),fe=new m.default({baseURL:de,serviceUrl:de,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),x=`x-af-elevated-permissions`,S=`x-af-context-ids`,C=Number.parseInt(process.env.MAX_CONTEXT_IDS??`0`,10)||20,w=new d.default({stdTTL:10}),T=(e,t)=>{let n={...e,fleets:{...e?.fleets},businessModels:{...e?.businessModels},demandSources:{...e?.demandSources}};for(let e of t)Object.entries(e??{}).forEach(([e,t])=>{n[e]??={},Object.entries(t).forEach(([t,r])=>{n[e][t]=(n[e][t]||[]).concat(r)})});return n};typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1}),typeof Symbol.asyncDispose!=`symbol`&&Object.defineProperty(Symbol,`asyncDispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.asyncDispose`),writable:!1});var E=class{constructor(e,t,n,r){this.id=e,this.accountType=t,this.contextIds=r,this.privateElevatedPermissionsHash=new Map,this.appPermission={},this.emptyUser=!!e,n&&this.privateElevatedPermissionsHash.set(Symbol(`initial`),n)}async getUserPermissions(){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let e=(0,f.default)({id:this.id,contextIds:this.contextIds}),t=w.get(e);return t||({data:t}=await b.get(`/api/v1/users/${this.id}/authorization-payload`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.accountType=t.accountType,this.privatePermissions=t,this.privatePermissions}async useCustomPermissionLoader(e){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let t=this.id,n=w.get(t);if(n)return this.privatePermissions=n,n;let r=await e(this.id);return w.set(t,r),this.privatePermissions=r,this.privatePermissions}get businessModels(){return this.getUserProperty(`businessModels`)}get fleets(){return this.getUserProperty(`fleets`)}get demandSources(){return this.getUserProperty(`demandSources`)}getUserProperty(e){if(!this.privatePermissions)throw Error(`Cannot get ${e} without calling (async) getUserPermissions before`);return Object.keys(this.privatePermissions[e]||{})}get elevatedPermissions(){return T(void 0,this.privateElevatedPermissionsHash.values())}get permissions(){if(!this.privatePermissions)throw Error(`Cannot get permissions without calling (async) getUserPermissions before`);return T(this.privatePermissions,this.privateElevatedPermissionsHash.values())}elevatePermissions(e){let t=Symbol();Object.values(e).forEach(e=>{Object.keys(e).forEach(e=>{if(!le(e))throw Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${e}`)})});let n=(0,l.getCurrentContext)();if(!n)throw Error(`Cannot find current user cross services trace`);let r=JSON.parse(n.context?.get(x)||`{}`),i=Object.assign(r,e);this.privateElevatedPermissionsHash.set(t,i),n.context.set(x,JSON.stringify(this.elevatedPermissions));let a=()=>{this.privateElevatedPermissionsHash.delete(t),n.context.set(x,JSON.stringify(this.elevatedPermissions))};return a[Symbol.dispose]=a,a}async getUserPermissionsLegacy(){if(!this.id)return;if(this.privatePermissionsLegacy)return this.privatePermissionsLegacy;let e=(0,f.default)({id:this.id,contextIds:this.contextIds,legacy:!0}),t=w.get(e);return t||({data:t}=await b.get(`/api/v1/users/${this.id}/authorization-payload-legacy`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.privatePermissionsLegacy=t,this.privatePermissionsLegacy}get permissionsLegacy(){if(!this.privatePermissionsLegacy)throw Error(`Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before`);return this.privatePermissionsLegacy}async getUserAppPermissions(e,t){if(!this.id||!e||!t)return;let n=this.appPermission[e];if(n)return n;let r=`${this.id}:${e}`,i=w.get(r);if(i)return this.appPermission[e]=i,i;let{data:a}=await fe.post(`/api/v1/apps/${e}/get-user-payload`,{userId:this.id},{headers:{"x-autofleet-apps-secret":t}});return w.set(r,a),this.appPermission[e]=a,this.appPermission[e]}extendRequiredContexts(e){if(!Array.isArray(e))return;let t=Array.from(new Set([...this.contextIds??[],...e]));t.length>C||(this.contextIds=t,(0,l.getCurrentContext)()?.context?.set(S,this.contextIds.join(`,`)))}};const pe=async(e,t)=>{let{data:n}=await fe.post(`/api/v1/auth`,{bearer:e,appId:t});return n};var D=class extends Error{constructor(...e){super(...e),this.name=`AppDoesNotExist`,this.message=`app does not exist`}};const me=`identity-ms`,he=`accessToken`,O=`userObject`,k=`x-af-user-id`,ge=`X-IAF-ORIGIN-SERVICE`,A=`x-af-user-permissions`,_e=`x-iaf-origin-service`,ve=`x-autofleet-apps-secret`,j=async(e,t)=>{let n=t[`X-IAF-ORIGIN-SERVICE`]||t[`x-iaf-origin-service`]||``;if(!Array.isArray(n)&&n.toLowerCase()===`identity-ms`)return;let{eagerLoadUserPermissions:r,eagerLoadUserPermissionsLegacy:i,customPermissionLoader:a}=e,o=t[k];if(!o||Array.isArray(o))return;let s=t[x],c=s?.length&&s.length>0?JSON.parse(s):{},u=(t?.[S])?.split(`,`),d=new E(o,`user`,c,u);return r&&(a?await d.useCustomPermissionLoader(a):await d.getUserPermissions()),i&&await d.getUserPermissionsLegacy(),(0,l.getCurrentContext)().nonHeaderContext?.set(O,d),d},M=(e={})=>async(t,n,r)=>{try{let n=await j(e,t.headers);n&&(t.user=n,t.headers[A]=n),r()}catch{n.status(401).json({error:`cannot authenticate user`})}},N=(e={})=>async(t,n,r)=>{let{eagerLoadUserPermissions:i,eagerLoadUserPermissionsLegacy:a,returnErrorIfNoToken:o,appSecret:s}=e,c;if(t.headers.authorization){try{c=await v(t.headers.authorization,s)}catch(e){e instanceof u.default.TokenExpiredError?n.status(401).json({errors:[`Access token expired`]}):e instanceof u.default.JsonWebTokenError?n.status(400).json({errors:[e.message]}):n.status(500).json({errors:[`Server error while parsing token`]});return}let e=c?.user?.id;e&&(t.headers[k]=e);let r=(t.headers?.[S])?.split(`,`),o=new E(e,c?.user?.accountType,void 0,r);(i||a)&&await Promise.all([i&&o.getUserPermissions(),a&&o.getUserPermissionsLegacy()]),t.user=o,(0,l.getCurrentContext)().nonHeaderContext?.set(O,o),t.headers[A]=o}else if(o){n.status(401).json({errors:[`No token provided`]});return}r()},P=e=>async(t,n,r)=>{let{appId:i,clientSecret:a}=e,o;if(!t.headers.authorization){n.status(401).json({errors:[`No token provided`]});return}try{if(o=await pe(t.headers.authorization,i),!o)throw new D}catch(e){if(e instanceof u.default.TokenExpiredError){n.status(401).json({errors:[`Access token expired`]});return}if([u.default.JsonWebTokenError,D].some(t=>e instanceof t)){n.status(400).json({errors:[e.message]});return}n.status(500).json({errors:[`Server error while parsing token`]});return}let s=o?.userId;s&&(t.headers[k]=s);let c=new E(s);i&&(t.headers[`x-autofleet-apps-secret`]=a,await c.getUserAppPermissions(i,a)),t.user=c;let d=(0,l.getCurrentContext)().nonHeaderContext;d?.set(O,c),d?.set(`accessToken`,_(t.headers.authorization)),t.headers[A]=c,r()},F=async(e,t,n)=>{await e.user.getUserPermissions(),n()},I=(e,t)=>e.headers.authorization?v(e.headers.authorization,t):null,L=async(e,t,n)=>{let r=Array.isArray(n)&&n.length<=C,i=new E(t,void 0,void 0,r?n:void 0);await i.getUserPermissions(),e??=(0,l.newTrace)(l.traceTypes.RABBIT),e.nonHeaderContext.set(O,i),r&&e.context.set(S,n?.join(`,`)||``)};var R=E;const z=(e,t,n)=>{e.decorateRequest(`user`,void 0),e.addHook(`onRequest`,async(e,n)=>{try{let n=await j(t,e.headers);n&&(e.user=n)}catch{n.status(401).send({error:`cannot authenticate user`})}}),n()};Object.defineProperty(z,Symbol.for(`skip-override`),{value:!0});const B=()=>(0,l.getCurrentContext)().nonHeaderContext?.get(O),V=()=>B()?.id,H=(e,t)=>!V()||Object.hasOwn(B().permissions?.[t]??{},e),U=e=>H(e,`fleets`),W=e=>H(e,`businessModels`),G=e=>H(e,`demandSources`);var K=class extends Error{constructor(e=null,t=`UnauthorizedAccessError`){super(t),this.user=e,this.name=`UnauthorizedAccessError`}};const q={NONE:`NONE`,BASIC:`BASIC`,JWT:`JWT`},J={[q.NONE]:()=>void 0,[q.BASIC]:e=>{let{username:t,password:n}=e;return`Basic ${Buffer.from(`${t}:${n}`).toString(`base64`)}`},[q.JWT]:e=>{let{secret:t}=e;if(t)return`Bearer ${u.default.sign({},t,{expiresIn:10})}`}},Y=e=>{let t=e?.method;if(!(!t||!J[t]))return J[t](e)};var ye=class e{constructor(e,t){this.ttl=10,t&&(this.ttl=t),this.cache=e??new d.default({stdTTL:this.ttl})}static getCacheKeys(e,t,n){let r=`${e}-${t}`;return{baseCacheKey:r,cacheKeys:n.map(e=>`${r}-${e}`)}}getUserPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`perm`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setUserPermissions(e,t){try{let n=t=>`perm-${e}-${t}`,r=Object.entries(t).map(([e,t])=>({key:n(e),val:t,ttl:this.ttl}));return this.cache.mset(r),{success:!0}}catch{return{success:!1}}}getCachedDeniedPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`seen`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setCachedDeniedPermissions(e,t,n){try{let r=t=>`seen-${e}-${t}`,i=t.map(e=>({key:r(e),val:n,ttl:this.ttl}));return this.cache.mset(i),{success:!0}}catch{return{success:!1}}}};const X=new ye,Z={USER_NOT_FOUND:`USER_NOT_FOUND`,INSUFFICIENT_PERMISSIONS:`INSUFFICIENT_PERMISSIONS`,VALIDATION_ERROR:`VALIDATION_ERROR`,API_ERROR:`API_ERROR`,NO_REQUIRED_PERMISSIONS:`NO_REQUIRED_PERMISSIONS`,UNAUTHORIZED:`UNAUTHORIZED`,BAD_REQUEST:`BAD_REQUEST`,INTERNAL_ERROR:`INTERNAL_ERROR`},Q={USER_NOT_FOUND:`User not found`,INSUFFICIENT_PERMISSIONS:`User does not have sufficient permissions`,VALIDATION_ERROR:`Validation error occurred`,API_ERROR:`API error occurred`,NO_REQUIRED_PERMISSIONS:`No required permissions provided for evaluation`,UNAUTHORIZED:`User is not authorized to perform this action`,BAD_REQUEST:`Bad request, please check the input parameters`,INTERNAL_ERROR:`Internal server error occurred while checking permissions`},be=(e,t)=>{let n=new Set(e);return t.some(e=>typeof e==`string`?n.has(e):e.every(e=>n.has(e)))},xe=(e,t)=>{let n=Object.values(e);return t?n.every(e=>e.hasRequiredPermissions):n.some(e=>e.hasRequiredPermissions)},Se=e=>{let t=[];return e?.length||t.push(`contextIds cannot be empty`),t},Ce=(e,t,n,r)=>({isAuthorized:!1,error:e,resolvedPermissions:{},requiredPermissions:t,contextIds:n,...r&&{message:r}}),we=(e,t,n,r)=>{let i={},a=new Map;if(r?.debug(`Start caching permissions results`,{userId:e,resolvedPermissions:t,requiredPermissions:n}),Object.entries(t).forEach(([e,t])=>{if(t?.permissions){let r=new Set(t.permissions),o=n.flat().filter(e=>r.has(e)),s=n.flat().filter(e=>!r.has(e));if(o.length&&(i[e]=o),s.length){let t=s.sort().join(`,`);a.has(t)||a.set(t,[]),a.get(t).push(e)}}}),Object.keys(i).length>0){let t=X.getUserPermissions(e,Object.keys(i));Object.keys(t??{}).forEach(e=>{i[e]&&(i[e]=Array.from(new Set([...t[e]||[],...i[e]])))}),r?.debug(`Caching granted permissions`,{userId:e,permissionsToCache:i,pastCache:t});let{success:n}=X.setUserPermissions(e,i);n||r?.error(`Failed to cache granted permissions`,{userId:e,permissionsToCache:i})}Array.from(a.entries()).map(([t,n])=>{let r=t.split(`,`);return X.setCachedDeniedPermissions(e,n,r)}),r?.debug(`Caching denied permissions`,{userId:e,deniedPermissionsGroups:Array.from(a.entries()).map(([e,t])=>({deniedPermissions:e.split(`,`),contextIds:t}))})},Te=(e,t)=>{if(!e.length)return{isResolvableFromCache:!1,hasRequiredPermissions:!1};let n=new Set(e);return{isResolvableFromCache:t.every(e=>typeof e==`string`?n.has(e):e.some(e=>n.has(e))),hasRequiredPermissions:!1}},Ee=(e,t,n)=>{let r=[...e],i=t.filter(e=>!n[e]);return r.push(...i),r},De=(e,t,n)=>{let r={};return e.forEach(e=>{let i=t[e],a=Te(i,n);a.isResolvableFromCache&&(r[e]={permissions:[],hasRequiredPermissions:a.hasRequiredPermissions})}),r},Oe=(e,t)=>{let n={};return Object.keys(e||{}).length&&Object.entries(e).forEach(([e,r])=>{let i=be(r,t);i&&(n[e]={permissions:r,hasRequiredPermissions:i})}),n},ke=e=>Object.fromEntries(e.map(e=>[e,{permissions:[],hasRequiredPermissions:!0}])),Ae=async({userId:e,contextIds:t,requiredPermissions:n,options:r})=>{if(!n.length)return ke(t);let i=await b.post(`/api/v1/permissions/users/${e}`,{userId:e,contextIds:t},{timeout:r.timeout});if(!Object.keys(i?.data?.permissionsByContexts||{}).length)throw Error(`Failed to resolve permissions`);return Object.fromEntries(Object.entries(i.data.permissionsByContexts).map(([e,t])=>[e,{permissions:t,hasRequiredPermissions:be(t,n)}]))},je=(e,t,n,r)=>{let i=Se(e);return i.length?Ce(Z.VALIDATION_ERROR,t,e,i.join(`, `)):n?t?.length?null:(r?.info(`No requiredPermissions provided`,{userId:n,contextIds:e}),{isAuthorized:!1,userId:n,resolvedPermissions:{},requiredPermissions:t,contextIds:e,error:Z.NO_REQUIRED_PERMISSIONS}):(r?.warn(`User not found in context, cannot check permissions`,{contextIds:e,requiredPermissions:t}),Ce(Z.USER_NOT_FOUND,t,e))},Me=async(e,t,n,r,i)=>{let a={...i},o=await Ae({userId:e,contextIds:t,requiredPermissions:n,options:r});return a={...i,...o},we(e,o,n),a},Ne=async(e,t,n,r)=>{let{logger:i}=r,a=Oe(X.getUserPermissions(e,t),n),o={...a},s=t.filter(e=>!a[e]),c=X.getCachedDeniedPermissions(e,s),l=s.filter(e=>!c[e]),u=s.filter(e=>c[e]),d=De(u,c,n);o={...o,...d};let f=Ee(l,u,d);return f.length?Me(e,f,n,r,o):(i?.debug(`Final resolved permissions`,{userId:e,requiredPermissions:n,resolvedPermissions:o}),o)},Pe=async({requiredPermissions:e,contextIds:t,logger:n,userId:r,options:{requireAll:i=!0,timeout:a=1e4}})=>{let o=r||B()?.id||null,s=je(t,e,o,n);if(s)return s;let c=await Ne(o,t,e,{timeout:a,logger:n}),l=xe(c,i);return n?.info(`Resolved permissions`,{userId:o,requiredPermissions:e,resolvedPermissions:c,isAuthorized:l,requireAll:i,contextIds:t}),{isAuthorized:l,userId:o,resolvedPermissions:c,requiredPermissions:e,contextIds:t,...l?{}:{error:Z.INSUFFICIENT_PERMISSIONS}}},Fe=e=>{let t=Object.keys(e.permissions?.contexts??{});return Array.from(new Set([...t])).filter(Boolean)},Ie=(e,t,n,r,i,a,o,s,c)=>{if(r){s?.error(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),i.status(n).json({error:e,message:t,...c?.requiredPermissions&&{requiredPermissions:c.requiredPermissions}});return}s?.warn(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),o()},Le=(e,t={enforce:!1,requireAll:!0})=>async(n,r,i)=>{try{let{logger:a,enforce:o,requireAll:s}=t,c=(t,s,c)=>Ie(t,s,c,o,r,n,i,a,{requiredPermissions:e}),l=n.user,u=Fe(l);if(!u?.length){c(Z.BAD_REQUEST,Q.BAD_REQUEST,400);return}let d=await Pe({requiredPermissions:e,contextIds:u,logger:a,userId:l.id,options:{requireAll:s??!0,timeout:1e4,...a&&{logger:a}}});if(d.isAuthorized){a?.info(`User has required permissions`,{userId:l.id,requiredPermissions:e,contextIds:u,url:n.url,method:n.method}),i();return}if(!o){a?.warn(`User does not have required permissions, skipping enforcement`,{userId:l.id,requiredPermissions:e,contextIds:u,url:n.url,method:n.method}),i();return}r.status(403).json({error:Z.INSUFFICIENT_PERMISSIONS,message:Q.INSUFFICIENT_PERMISSIONS,required:e,contexts:u,userId:d.userId})}catch(a){if(t.logger?.error(`Error in requirePermissions middleware`,{error:a,requiredPermissions:e,url:n.url,method:n.method}),!t.enforce){t.logger?.error(`Error during permission check, skipping enforcement`,{error:a,url:n.url,method:n.method}),i();return}r.status(500).json({error:Z.INTERNAL_ERROR,message:Q.INTERNAL_ERROR})}};var Re=o({default:()=>Ve,middlewares:()=>Be,sdk:()=>ze});const ze={evaluatePermissions:Pe},Be={requirePermissions:Le};var Ve={sdk:ze,middlewares:Be};const He=l.getCurrentContext,Ue=({outbreakOptions:e={},logger:t}={})=>{l.default({headersPrefix:`x-af`,contextMiddlewareGetter:t?.addContextMiddleware,...e})},We=l.traceTypes,$=l.newTrace,Ge={traceTypes:We,newTrace:$,User:R,middleware:M,middlewareWithDecode:N,eagerLoadPermissionsMiddleware:F,getCurrentPayload:He,getDecodedBearer:I,checkFleetPermission:U,checkBusinessModelPermission:W,checkDemandSourcePermission:G,isUserExist:V,getUser:B,UnauthorizedAccessError:K,appMiddleware:P,createOrSetRabbitTrace:L,outbreak:l,AUTHORIZATION_METHODS:q,getAuthorizationHeader:Y,CONTEXTS_IDS_HEADER:S,authFromUserIdHeaderPlugin:z,permissions:Re};var Ke=Ge;exports.AUTHORIZATION_METHODS=q,exports.CONTEXTS_IDS_HEADER=S,exports.UnauthorizedAccessError=K,exports.User=R,exports.appMiddleware=P,exports.authFromUserIdHeaderPlugin=z,exports.checkBusinessModelPermission=W,exports.checkDemandSourcePermission=G,exports.checkFleetPermission=U,exports.createOrSetRabbitTrace=L,exports.default=Ke,exports.eagerLoadPermissionsMiddleware=F,exports.enableTracing=Ue,exports.getAuthorizationHeader=Y,exports.getCurrentPayload=He,exports.getDecodedBearer=I,exports.getRefreshTokenSecret=ae,exports.getTokenSecret=g,exports.getUser=B,exports.isUserExist=V,exports.middleware=M,exports.middlewareWithDecode=N,exports.newTrace=$,Object.defineProperty(exports,`outbreak`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`permissions`,{enumerable:!0,get:function(){return Re}}),exports.traceTypes=We;
1
+ Object.defineProperty(exports,`__esModule`,{value:!0});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n&&t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`@autofleet/outbreak`);l=c(l);let u=require(`jsonwebtoken`);u=c(u);let d=require(`node-cache`);d=c(d);let f=require(`object-hash`);f=c(f);let p=require(`moment`);p=c(p);let m=require(`@autofleet/network`);m=c(m);const{DEPRECATED_JWT_SECRET:ee,JWT_NEW_SECRET:te,DEPRECATED_REFRESH_JWT_SECRET:ne,REFRESH_JWT_SECRET:re,DEPRECATION_UNIX_TIMESTAMP:ie}=process.env,h=(e,t,n)=>{let r=(0,p.default)(parseInt(ie||``,10)*1e3);try{let i;if(e){let{iat:t}=u.default.decode(e);i=(0,p.default)(t*1e3)}else i=(0,p.default)();return i.isBefore(r)?t:n}catch{return n}},ae=e=>h(e,ne,re),g=e=>h(e,ee,te),_=e=>e.replace(`Bearer `,``),v=(e,t)=>{let n=_(e);return u.default.verify(n,t||g(n))},oe=`00000000-0000-0000-0000-000000000000`,se=`ffffffff-ffff-ffff-ffff-ffffffffffff`,y=`[0-9a-f]`,ce=RegExp(`^(?:${y}{8}-${y}{4}-[1-8]${y}{3}-[89ab]${y}{3}-${y}{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`,`i`);function le(e){return typeof e==`string`&&ce.test(e)}const ue=10,de=process.env.API_GATEWAY_URL||`https://api.autofleet.io`,b=new m.default({serviceName:`IDENTITY_MS`,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),fe=new m.default({baseURL:de,serviceUrl:de,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),x=`x-af-elevated-permissions`,S=`x-af-context-ids`,C=Number.parseInt(process.env.MAX_CONTEXT_IDS??`0`,10)||20,w=new d.default({stdTTL:10}),T=(e,t)=>{let n={...e,fleets:{...e?.fleets},businessModels:{...e?.businessModels},demandSources:{...e?.demandSources}};for(let e of t)Object.entries(e??{}).forEach(([e,t])=>{n[e]??={},Object.entries(t).forEach(([t,r])=>{n[e][t]=(n[e][t]||[]).concat(r)})});return n};typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1}),typeof Symbol.asyncDispose!=`symbol`&&Object.defineProperty(Symbol,`asyncDispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.asyncDispose`),writable:!1});var E=class{constructor(e,t,n,r){this.id=e,this.accountType=t,this.contextIds=r,this.privateElevatedPermissionsHash=new Map,this.appPermission={},this.emptyUser=!!e,n&&this.privateElevatedPermissionsHash.set(Symbol(`initial`),n)}async getUserPermissions(){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let e=(0,f.default)({id:this.id,contextIds:this.contextIds}),t=w.get(e);return t||({data:t}=await b.get(`/api/v1/users/${this.id}/authorization-payload`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.accountType=t.accountType,this.privatePermissions=t,this.privatePermissions}async useCustomPermissionLoader(e){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let t=this.id,n=w.get(t);if(n)return this.privatePermissions=n,n;let r=await e(this.id);return w.set(t,r),this.privatePermissions=r,this.privatePermissions}get businessModels(){return this.getUserProperty(`businessModels`)}get fleets(){return this.getUserProperty(`fleets`)}get demandSources(){return this.getUserProperty(`demandSources`)}getUserProperty(e){if(!this.privatePermissions)throw Error(`Cannot get ${e} without calling (async) getUserPermissions before`);return Object.keys(this.privatePermissions[e]||{})}get elevatedPermissions(){return T(void 0,this.privateElevatedPermissionsHash.values())}get permissions(){if(!this.privatePermissions)throw Error(`Cannot get permissions without calling (async) getUserPermissions before`);return T(this.privatePermissions,this.privateElevatedPermissionsHash.values())}elevatePermissions(e){let t=Symbol();Object.values(e).forEach(e=>{Object.keys(e).forEach(e=>{if(!le(e))throw Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${e}`)})});let n=(0,l.getCurrentContext)();if(!n)throw Error(`Cannot find current user cross services trace`);let r=JSON.parse(n.context?.get(x)||`{}`),i=Object.assign(r,e);this.privateElevatedPermissionsHash.set(t,i),n.context.set(x,JSON.stringify(this.elevatedPermissions));let a=()=>{this.privateElevatedPermissionsHash.delete(t),n.context.set(x,JSON.stringify(this.elevatedPermissions))};return a[Symbol.dispose]=a,a}async getUserPermissionsLegacy(){if(!this.id)return;if(this.privatePermissionsLegacy)return this.privatePermissionsLegacy;let e=(0,f.default)({id:this.id,contextIds:this.contextIds,legacy:!0}),t=w.get(e);return t||({data:t}=await b.get(`/api/v1/users/${this.id}/authorization-payload-legacy`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.privatePermissionsLegacy=t,this.privatePermissionsLegacy}get permissionsLegacy(){if(!this.privatePermissionsLegacy)throw Error(`Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before`);return this.privatePermissionsLegacy}async getUserAppPermissions(e,t){if(!this.id||!e||!t)return;let n=this.appPermission[e];if(n)return n;let r=`${this.id}:${e}`,i=w.get(r);if(i)return this.appPermission[e]=i,i;let{data:a}=await fe.post(`/api/v1/apps/${e}/get-user-payload`,{userId:this.id},{headers:{"x-autofleet-apps-secret":t}});return w.set(r,a),this.appPermission[e]=a,this.appPermission[e]}extendRequiredContexts(e){if(!Array.isArray(e))return;let t=Array.from(new Set([...this.contextIds??[],...e]));t.length>C||(this.contextIds=t,(0,l.getCurrentContext)()?.context?.set(S,this.contextIds.join(`,`)))}};const pe=async(e,t)=>{let{data:n}=await fe.post(`/api/v1/auth`,{bearer:e,appId:t});return n};var D=class extends Error{constructor(...e){super(...e),this.name=`AppDoesNotExist`,this.message=`app does not exist`}};const me=`identity-ms`,he=`accessToken`,O=`userObject`,k=`x-af-user-id`,ge=`X-IAF-ORIGIN-SERVICE`,A=`x-af-user-permissions`,_e=`x-iaf-origin-service`,ve=`x-autofleet-apps-secret`,j=async(e,t)=>{let n=t[`X-IAF-ORIGIN-SERVICE`]||t[`x-iaf-origin-service`]||``;if(!Array.isArray(n)&&n.toLowerCase()===`identity-ms`)return;let{eagerLoadUserPermissions:r,eagerLoadUserPermissionsLegacy:i,customPermissionLoader:a}=e,o=t[k];if(!o||Array.isArray(o))return;let s=t[x],c=s?.length&&s.length>0?JSON.parse(s):{},u=(t?.[S])?.split(`,`),d=new E(o,`user`,c,u);return r&&(a?await d.useCustomPermissionLoader(a):await d.getUserPermissions()),i&&await d.getUserPermissionsLegacy(),(0,l.getCurrentContext)().nonHeaderContext?.set(O,d),d},M=(e={})=>async(t,n,r)=>{try{let n=await j(e,t.headers);n&&(t.user=n,t.headers[A]=n),r()}catch{n.status(401).json({error:`cannot authenticate user`})}},N=(e={})=>async(t,n,r)=>{let{eagerLoadUserPermissions:i,eagerLoadUserPermissionsLegacy:a,returnErrorIfNoToken:o,appSecret:s}=e,c;if(t.headers.authorization){try{c=await v(t.headers.authorization,s)}catch(e){e instanceof u.default.TokenExpiredError?n.status(401).json({errors:[`Access token expired`]}):e instanceof u.default.JsonWebTokenError?n.status(400).json({errors:[e.message]}):n.status(500).json({errors:[`Server error while parsing token`]});return}let e=c?.user?.id;e&&(t.headers[k]=e);let r=(t.headers?.[S])?.split(`,`),o=new E(e,c?.user?.accountType,void 0,r);(i||a)&&await Promise.all([i&&o.getUserPermissions(),a&&o.getUserPermissionsLegacy()]),t.user=o,(0,l.getCurrentContext)().nonHeaderContext?.set(O,o),t.headers[A]=o}else if(o){n.status(401).json({errors:[`No token provided`]});return}r()},P=e=>async(t,n,r)=>{let{appId:i,clientSecret:a}=e,o;if(!t.headers.authorization){n.status(401).json({errors:[`No token provided`]});return}try{if(o=await pe(t.headers.authorization,i),!o)throw new D}catch(e){if(e instanceof u.default.TokenExpiredError){n.status(401).json({errors:[`Access token expired`]});return}if([u.default.JsonWebTokenError,D].some(t=>e instanceof t)){n.status(400).json({errors:[e.message]});return}n.status(500).json({errors:[`Server error while parsing token`]});return}let s=o?.userId;s&&(t.headers[k]=s);let c=new E(s);i&&(t.headers[`x-autofleet-apps-secret`]=a,await c.getUserAppPermissions(i,a)),t.user=c;let d=(0,l.getCurrentContext)().nonHeaderContext;d?.set(O,c),d?.set(`accessToken`,_(t.headers.authorization)),t.headers[A]=c,r()},F=async(e,t,n)=>{await e.user.getUserPermissions(),n()},I=(e,t)=>e.headers.authorization?v(e.headers.authorization,t):null,L=async(e,t,n)=>{let r=Array.isArray(n)&&n.length<=C,i=new E(t,void 0,void 0,r?n:void 0);await i.getUserPermissions(),e??=(0,l.newTrace)(l.traceTypes.RABBIT),e.nonHeaderContext.set(O,i),r&&e.context.set(S,n?.join(`,`)||``)};var R=E;const z=(e,t,n)=>{e.decorateRequest(`user`,void 0),e.addHook(`onRequest`,async(e,n)=>{try{let n=await j(t,e.headers);n&&(e.user=n)}catch{n.status(401).send({error:`cannot authenticate user`})}}),n()};Object.defineProperty(z,Symbol.for(`skip-override`),{value:!0});const B=()=>(0,l.getCurrentContext)().nonHeaderContext?.get(O),V=()=>B()?.id,H=(e,t)=>!V()||Object.hasOwn(B().permissions?.[t]??{},e),U=e=>H(e,`fleets`),W=e=>H(e,`businessModels`),G=e=>H(e,`demandSources`);var K=class extends Error{constructor(e=null,t=`UnauthorizedAccessError`){super(t),this.user=e,this.name=`UnauthorizedAccessError`}};const q={NONE:`NONE`,BASIC:`BASIC`,JWT:`JWT`},J={[q.NONE]:()=>void 0,[q.BASIC]:e=>{let{username:t,password:n}=e;return`Basic ${Buffer.from(`${t}:${n}`).toString(`base64`)}`},[q.JWT]:e=>{let{secret:t}=e;if(t)return`Bearer ${u.default.sign({},t,{expiresIn:10})}`}},ye=e=>{let t=e?.method;if(!(!t||!J[t]))return J[t](e)};var be=class e{constructor(e,t){this.ttl=10,t&&(this.ttl=t),this.cache=e??new d.default({stdTTL:this.ttl})}static getCacheKeys(e,t,n){let r=`${e}-${t}`;return{baseCacheKey:r,cacheKeys:n.map(e=>`${r}-${e}`)}}getUserPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`perm`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setUserPermissions(e,t){try{let n=t=>`perm-${e}-${t}`,r=Object.entries(t).map(([e,t])=>({key:n(e),val:t,ttl:this.ttl}));return this.cache.mset(r),{success:!0}}catch{return{success:!1}}}getCachedDeniedPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`seen`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setCachedDeniedPermissions(e,t,n){try{let r=t=>`seen-${e}-${t}`,i=t.map(e=>({key:r(e),val:n,ttl:this.ttl}));return this.cache.mset(i),{success:!0}}catch{return{success:!1}}}};const Y=new be,X={AND:`and`,OR:`or`},Z={USER_NOT_FOUND:`USER_NOT_FOUND`,INSUFFICIENT_PERMISSIONS:`INSUFFICIENT_PERMISSIONS`,VALIDATION_ERROR:`VALIDATION_ERROR`,API_ERROR:`API_ERROR`,NO_REQUIRED_PERMISSIONS:`NO_REQUIRED_PERMISSIONS`,UNAUTHORIZED:`UNAUTHORIZED`,BAD_REQUEST:`BAD_REQUEST`,INTERNAL_ERROR:`INTERNAL_ERROR`},Q={USER_NOT_FOUND:`User not found`,INSUFFICIENT_PERMISSIONS:`User does not have sufficient permissions`,VALIDATION_ERROR:`Validation error occurred`,API_ERROR:`API error occurred`,NO_REQUIRED_PERMISSIONS:`No required permissions provided for evaluation`,UNAUTHORIZED:`User is not authorized to perform this action`,BAD_REQUEST:`Bad request, please check the input parameters`,INTERNAL_ERROR:`Internal server error occurred while checking permissions`},xe=(e,t,n)=>{let r=new Set(e),i=t.filter(e=>r.has(e));return n===X.AND?i.length===t.length:i.length>0},Se=(e,t)=>{let n=Object.values(e);return t?n.every(e=>e.hasRequiredPermissions):n.some(e=>e.hasRequiredPermissions)},Ce=e=>{let t=[];return e?.length||t.push(`contextIds cannot be empty`),t},we=(e,t,n,r)=>({isAuthorized:!1,error:e,resolvedPermissions:{},requiredPermissions:t,contextIds:n,...r&&{message:r}}),Te=(e,t,n,r)=>{let i={},a=new Map;if(r?.debug(`Start caching permissions results`,{userId:e,resolvedPermissions:t,requiredPermissions:n}),Object.entries(t).forEach(([e,t])=>{if(t?.permissions){let r=new Set(t.permissions),o=n.filter(e=>r.has(e)),s=n.filter(e=>!r.has(e));if(o.length&&(i[e]=o),s.length){let t=s.sort().join(`,`);a.has(t)||a.set(t,[]),a.get(t).push(e)}}}),Object.keys(i).length>0){let t=Y.getUserPermissions(e,Object.keys(i));Object.keys(t??{}).forEach(e=>{i[e]&&(i[e]=Array.from(new Set([...t[e]||[],...i[e]])))}),r?.debug(`Caching granted permissions`,{userId:e,permissionsToCache:i,pastCache:t});let{success:n}=Y.setUserPermissions(e,i);n||r?.error(`Failed to cache granted permissions`,{userId:e,permissionsToCache:i})}Array.from(a.entries()).map(([t,n])=>{let r=t.split(`,`);return Y.setCachedDeniedPermissions(e,n,r)}),r?.debug(`Caching denied permissions`,{userId:e,deniedPermissionsGroups:Array.from(a.entries()).map(([e,t])=>({deniedPermissions:e.split(`,`),contextIds:t}))})},Ee=(e,t,n)=>{if(!e.length)return{isResolvableFromCache:!1,hasRequiredPermissions:!1};let r=new Set(e),i=t.filter(e=>r.has(e));return n===X.AND?{isResolvableFromCache:i.length>0,hasRequiredPermissions:!1}:{isResolvableFromCache:i.length===t.length,hasRequiredPermissions:!1}},De=(e,t,n)=>{let r=[...e],i=t.filter(e=>!n[e]);return r.push(...i),r},Oe=(e,t,n,r)=>{let i={};return e.forEach(e=>{let a=t[e],o=Ee(a,n,r);o.isResolvableFromCache&&(i[e]={permissions:[],hasRequiredPermissions:o.hasRequiredPermissions})}),i},ke=(e,t,n)=>{let r={};return Object.keys(e||{}).length&&Object.entries(e).forEach(([e,i])=>{let a=xe(i,t,n);a&&(r[e]={permissions:i,hasRequiredPermissions:a})}),r},Ae=async(e,t,n,r)=>{let i=(await b.post(`/api/v1/permissions/resolve`,{userId:e,contextIds:t,requiredPermissions:n},{timeout:r.timeout}))?.data||{};if(!Object.keys(i||{}).length)throw Error(`Failed to resolve permissions`);if(r.permissionsEvaluationOperator===X.AND)return i;let a={};return Object.entries(i).forEach(([e,t])=>{let i=t?.permissions||[];a[e]={permissions:i,hasRequiredPermissions:xe(i,n,r.permissionsEvaluationOperator)}}),a},je=(e,t,n,r)=>{let i=Ce(e);return i.length?we(Z.VALIDATION_ERROR,t,e,i.join(`, `)):n?t?.length?null:(r?.info(`No requiredPermissions provided`,{userId:n,contextIds:e}),{isAuthorized:!1,userId:n,resolvedPermissions:{},requiredPermissions:t,contextIds:e,error:Z.NO_REQUIRED_PERMISSIONS}):(r?.warn(`User not found in context, cannot check permissions`,{contextIds:e,requiredPermissions:t}),we(Z.USER_NOT_FOUND,t,e))},Me=async(e,t,n,r,i)=>{let a={...i},o=await Ae(e,t,n,r);return a={...i,...o},Te(e,o,n),a},Ne=async(e,t,n,r)=>{let{logger:i}=r,a=ke(Y.getUserPermissions(e,t),n,r.permissionsEvaluationOperator),o={...a},s=t.filter(e=>!a[e]),c=Y.getCachedDeniedPermissions(e,s),l=s.filter(e=>!c[e]),u=s.filter(e=>c[e]),d=Oe(u,c,n,r.permissionsEvaluationOperator);o={...o,...d};let f=De(l,u,d);return f.length?Me(e,f,n,r,o):(i?.debug(`Final resolved permissions`,{userId:e,requiredPermissions:n,resolvedPermissions:o}),o)},Pe=async({requiredPermissions:e,contextIds:t,logger:n,userId:r,options:{requireAll:i=!0,permissionsEvaluationOperator:a=X.AND,timeout:o=1e4}})=>{let s=r||B()?.id||null,c=je(t,e,s,n);if(c)return c;let l=await Ne(s,t,e,{permissionsEvaluationOperator:a,timeout:o,logger:n}),u=Se(l,i);return n?.info(`Resolved permissions`,{userId:s,requiredPermissions:e,resolvedPermissions:l,isAuthorized:u,requireAll:i,permissionsEvaluationOperator:a,contextIds:t}),{isAuthorized:u,userId:s,resolvedPermissions:l,requiredPermissions:e,contextIds:t,...u?{}:{error:Z.INSUFFICIENT_PERMISSIONS}}},Fe=e=>{let t=Object.keys(e.permissions?.contexts??{});return Array.from(new Set([...t])).filter(Boolean)},Ie=(e,t,n,r,i,a,o,s,c)=>{if(r){s?.error(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),i.status(n).json({error:e,message:t,...c?.requiredPermissions&&{requiredPermissions:c.requiredPermissions}});return}s?.warn(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),o()},Le=(e,t={enforce:!1,requireAll:!0,permissionsEvaluationOperator:X.AND})=>async(n,r,i)=>{try{let{logger:a,enforce:o,requireAll:s,permissionsEvaluationOperator:c}=t,l=(t,s,c)=>Ie(t,s,c,o,r,n,i,a,{requiredPermissions:e}),u=n.user,d=Fe(u);if(!d?.length){l(Z.BAD_REQUEST,Q.BAD_REQUEST,400);return}let f=await Pe({requiredPermissions:e,contextIds:d,logger:a,userId:u.id,options:{requireAll:s??!0,permissionsEvaluationOperator:c??X.AND,timeout:1e4,...a&&{logger:a}}});if(f.isAuthorized){a?.info(`User has required permissions`,{userId:u.id,requiredPermissions:e,contextIds:d,url:n.url,method:n.method}),i();return}if(!o){a?.warn(`User does not have required permissions, skipping enforcement`,{userId:u.id,requiredPermissions:e,contextIds:d,url:n.url,method:n.method}),i();return}r.status(403).json({error:Z.INSUFFICIENT_PERMISSIONS,message:Q.INSUFFICIENT_PERMISSIONS,required:e,contexts:d,userId:f.userId})}catch(a){if(t.logger?.error(`Error in requirePermissions middleware`,{error:a,requiredPermissions:e,url:n.url,method:n.method}),!t.enforce){t.logger?.error(`Error during permission check, skipping enforcement`,{error:a,url:n.url,method:n.method}),i();return}r.status(500).json({error:Z.INTERNAL_ERROR,message:Q.INTERNAL_ERROR})}};var Re=o({default:()=>Ve,middlewares:()=>Be,sdk:()=>ze});const ze={evaluatePermissions:Pe},Be={requirePermissions:Le};var Ve={sdk:ze,middlewares:Be};const He=l.getCurrentContext,Ue=({outbreakOptions:e={},logger:t}={})=>{l.default({headersPrefix:`x-af`,contextMiddlewareGetter:t?.addContextMiddleware,...e})},We=l.traceTypes,$=l.newTrace,Ge={traceTypes:We,newTrace:$,User:R,middleware:M,middlewareWithDecode:N,eagerLoadPermissionsMiddleware:F,getCurrentPayload:He,getDecodedBearer:I,checkFleetPermission:U,checkBusinessModelPermission:W,checkDemandSourcePermission:G,isUserExist:V,getUser:B,UnauthorizedAccessError:K,appMiddleware:P,createOrSetRabbitTrace:L,outbreak:l,AUTHORIZATION_METHODS:q,getAuthorizationHeader:ye,CONTEXTS_IDS_HEADER:S,authFromUserIdHeaderPlugin:z,permissions:Re};var Ke=Ge;exports.AUTHORIZATION_METHODS=q,exports.CONTEXTS_IDS_HEADER=S,exports.UnauthorizedAccessError=K,exports.User=R,exports.appMiddleware=P,exports.authFromUserIdHeaderPlugin=z,exports.checkBusinessModelPermission=W,exports.checkDemandSourcePermission=G,exports.checkFleetPermission=U,exports.createOrSetRabbitTrace=L,exports.default=Ke,exports.eagerLoadPermissionsMiddleware=F,exports.enableTracing=Ue,exports.getAuthorizationHeader=ye,exports.getCurrentPayload=He,exports.getDecodedBearer=I,exports.getRefreshTokenSecret=ae,exports.getTokenSecret=g,exports.getUser=B,exports.isUserExist=V,exports.middleware=M,exports.middlewareWithDecode=N,exports.newTrace=$,Object.defineProperty(exports,`outbreak`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`permissions`,{enumerable:!0,get:function(){return Re}}),exports.traceTypes=We;
2
2
  //# sourceMappingURL=index.cjs.map
package/lib/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["unixTime: moment.Moment","jwt","jwt","IdentityNetwork: Network","Network","AutofleetApiNetwork: Network","MAX_CONTEXT_IDS: number","NodeCache","permissions: UserPayload","id?: string","accountType?: AccountType | undefined","contextIds?: string[]","jwt","eagerLoadPermissionsMiddleware: Asyncify<Handler>","traceTypes","authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions>","user: ApiUser | null","jwt","NodeCache","result: EntityPermissions","permissionCache: PermissionCache","errors: string[]","permissionsToCache: PermissionsByContextId","result: PermissionsEvaluationByContextId","processedPermissions: PermissionsEvaluationByContextId","response: AxiosResponse<{ permissionsByContexts: PermissionsByContextId; }>","newResolvedPermissions: PermissionsEvaluationByContextId","contextIds: string[]","sdk: SdkExports","middlewares: MiddlewareExports","getCurrentPayload: typeof outbreak.getCurrentContext","outbreak","traceTypes: typeof outbreak.traceTypes","newTrace: typeof outbreak.newTrace","zehutDefault: Default"],"sources":["../src/secret-getter.ts","../src/utils.ts","../src/services.ts","../src/user/ApiUser.ts","../src/app-auth.ts","../src/exceptions/appDoesNotExist.ts","../src/user/const.ts","../src/user/common.ts","../src/user/index.ts","../src/user/fastify.ts","../src/check-permission.ts","../src/errors.ts","../src/authorization.ts","../src/permissions/SDK/permissionCache.ts","../src/permissions/SDK/consts.ts","../src/permissions/SDK/evaluatePermissions.ts","../src/permissions/middleware/requirePermissions.ts","../src/permissions/index.ts","../src/index.ts"],"sourcesContent":["import jwt from 'jsonwebtoken';\nimport moment from 'moment';\n\nconst {\n DEPRECATED_JWT_SECRET, JWT_NEW_SECRET,\n DEPRECATED_REFRESH_JWT_SECRET, REFRESH_JWT_SECRET,\n DEPRECATION_UNIX_TIMESTAMP,\n} = process.env;\n\nconst getRelevantSecret = (token: string | undefined, deprecatedSecret: string, newSecret: string): string => {\n const deprecationTime = moment(parseInt(DEPRECATION_UNIX_TIMESTAMP || '', 10) * 1000);\n try {\n let unixTime: moment.Moment;\n if (token) {\n const { iat } = jwt.decode(token) as jwt.JwtPayload;\n unixTime = moment(iat! * 1000);\n } else {\n unixTime = moment();\n }\n return unixTime.isBefore(deprecationTime) ? deprecatedSecret : newSecret;\n } catch {\n return newSecret;\n }\n};\n\nexport const getRefreshTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_REFRESH_JWT_SECRET!, REFRESH_JWT_SECRET!);\nexport const getTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_JWT_SECRET!, JWT_NEW_SECRET!);\n","import type { UUID } from 'node:crypto';\nimport jwt from 'jsonwebtoken';\nimport { getTokenSecret } from './secret-getter';\n\ntype Context = Partial<Record<ContextProp | 'id', string>> & { subSystem?: SubSystemType; permissions?: string[]; entityId: string; };\n\nconst CONTEXT_PROPS = ['fleetId', 'businessModelId', 'demandSourceId'] as const;\ntype ContextProp = typeof CONTEXT_PROPS[number];\nconst CONTEXT_MAP_PROPS = {\n fleet: 'fleets',\n business: 'businessModels',\n demand: 'demandSources',\n} as const;\ntype SubSystemType = keyof typeof CONTEXT_MAP_PROPS;\ntype ContextSubSystemProp = typeof CONTEXT_MAP_PROPS[SubSystemType];\n\nexport const getAuthFromBearer = (bearer: string): string => bearer.replace('Bearer ', '');\n\nexport const decodeBearer = (bearer: string, appSecret?: string): any => {\n const token = getAuthFromBearer(bearer);\n const decoded = jwt.verify(token, appSecret || getTokenSecret(token));\n return decoded;\n};\n\nexport const parsePermissions = (contextId: string, decodedToken: { contexts: Context[]; }): { key: string; value: string; } | undefined => {\n if (!decodedToken) return undefined;\n const { contexts } = decodedToken;\n const activeContext = contexts.find(context => context.id === contextId);\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const permissionsValue = `${activeContext!.permissions?.map(cp => `${cp},`)}`;\n\n return {\n key: activeContext!.entityId,\n value: permissionsValue,\n };\n};\n\ntype EntitiesFromContext = Partial<Record<ContextSubSystemProp, Record<string, string>>>;\nexport const getEntitiesFromContext = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): EntitiesFromContext => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n\n const attributes: EntitiesFromContext = {};\n contexts.forEach((context) => {\n const prop = CONTEXT_MAP_PROPS[context.subSystem || 'business'];\n\n const permissions = parsePermissions(context.id!, decodedToken);\n if (!permissions) return;\n attributes[prop] ??= {};\n attributes[prop][permissions.key] = permissions.value;\n });\n\n return attributes;\n};\n\ntype ContextAttributes = Partial<Record<ContextProp, string[]>>;\nexport const getContextAttributes = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): ContextAttributes => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n const attributes: ContextAttributes = {};\n contexts.forEach((context) => {\n CONTEXT_PROPS.forEach((prop) => {\n if (context[prop]) {\n attributes[prop] ??= [];\n attributes[prop].push(context[prop]);\n }\n });\n });\n return attributes;\n};\n\nconst EMPTY_UUID = '00000000-0000-0000-0000-000000000000';\nconst FULL_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff';\nconst VALID_CHARS_REGEX = '[0-9a-f]';\nconst UUID_VERSION_REGEX = '[1-8]';\nconst UUID_REGEX = new RegExp(\n `^(?:${VALID_CHARS_REGEX}{8}-${VALID_CHARS_REGEX}{4}-${UUID_VERSION_REGEX}${VALID_CHARS_REGEX}{3}-[89ab]${VALID_CHARS_REGEX}{3}-${VALID_CHARS_REGEX}{12}|${EMPTY_UUID}|${FULL_UUID})$`,\n 'i',\n);\nexport function validateUUID(uuid: unknown): uuid is UUID {\n return typeof uuid === 'string' && UUID_REGEX.test(uuid);\n}\n","import Network from '@autofleet/network';\n\nconst CACHE_LIFETIME_IN_SEC = 10;\nconst apiGwUrl = process.env.API_GATEWAY_URL || 'https://api.autofleet.io';\n\nexport const IdentityNetwork: Network = new Network({\n serviceName: 'IDENTITY_MS',\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n\nexport const AutofleetApiNetwork: Network = new Network({\n baseURL: apiGwUrl,\n serviceUrl: apiGwUrl,\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n","import NodeCache from 'node-cache';\nimport objectHash from 'object-hash';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport { validateUUID } from '../utils';\nimport { AutofleetApiNetwork, IdentityNetwork } from '../services';\n\nexport type AccountType = 'client' | 'user' | 'service' | 'driver';\ntype EntityPermissions = Record<string, string[]>;\n\nexport const ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';\nexport const CONTEXTS_IDS_HEADER = 'x-af-context-ids';\nexport const MAX_CONTEXT_IDS: number = Number.parseInt(process.env.MAX_CONTEXT_IDS ?? `0`, 10) || 20;\n\nexport interface UserExternalData {\n element?: {\n internalUser: boolean;\n };\n [key: string]: any;\n}\n\nexport interface UserPayload {\n businessModels: EntityPermissions;\n fleets: EntityPermissions;\n demandSources: EntityPermissions;\n businessAccounts?: EntityPermissions;\n accountType?: AccountType;\n contexts?: EntityPermissions;\n createdAt?: string;\n externalData?: UserExternalData | null;\n}\n\nexport interface PartialUserPayload {\n businessModels?: EntityPermissions;\n fleets?: EntityPermissions;\n demandSources?: EntityPermissions;\n vehicles?: EntityPermissions;\n drivers?: EntityPermissions;\n businessAccounts?: EntityPermissions;\n}\n\nconst userCache = new NodeCache({ stdTTL: 10 });\n\nconst mergePermissions = (target: UserPayload | undefined, sources: Iterable<PartialUserPayload | undefined>): UserPayload => {\n const permissions: UserPayload = {\n ...target,\n fleets: { ...target?.fleets },\n businessModels: { ...target?.businessModels },\n demandSources: { ...target?.demandSources },\n // Clone other nested objects as needed\n };\n\n for (const source of sources) {\n (Object.entries(source ?? {}) as [Exclude<keyof UserPayload, 'accountType' | 'createdAt'>, EntityPermissions][]).forEach(([entityType, entityValue]) => {\n permissions[entityType] ??= {};\n Object.entries(entityValue).forEach(([entityId, perms]) => {\n permissions[entityType]![entityId] = (permissions[entityType]![entityId] || []).concat(perms);\n });\n });\n }\n\n return permissions;\n};\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\nif (typeof Symbol.asyncDispose !== 'symbol') {\n // Polyfill for asyncDispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L174-L183\n Object.defineProperty(Symbol, 'asyncDispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.asyncDispose'),\n writable: false,\n });\n}\n\nexport default class ApiUser {\n private privatePermissions: UserPayload | undefined;\n\n private readonly privateElevatedPermissionsHash = new Map<symbol, PartialUserPayload | undefined>();\n\n private privatePermissionsLegacy: any;\n\n private readonly appPermission: Record<string, any> = {};\n\n public readonly emptyUser: boolean;\n\n constructor(public id?: string, public accountType?: AccountType | undefined, elevatedPermissions?: PartialUserPayload, public contextIds?: string[]) {\n this.emptyUser = !!id;\n if (elevatedPermissions) {\n this.privateElevatedPermissionsHash.set(Symbol('initial'), elevatedPermissions);\n }\n }\n\n public async getUserPermissions(): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n });\n\n let data = userCache.get<UserPayload>(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get<UserPayload>(`/api/v1/users/${this.id}/authorization-payload`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.accountType = data.accountType;\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public async useCustomPermissionLoader(customPermissionLoader: (userId: string) => UserPayload | PromiseLike<UserPayload>): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n\n const cacheKey = this.id;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.privatePermissions = cachedResult;\n return cachedResult;\n }\n\n const data = await customPermissionLoader(this.id);\n userCache.set(cacheKey, data);\n\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public get businessModels(): string[] {\n return this.getUserProperty('businessModels');\n }\n\n public get fleets(): string[] {\n return this.getUserProperty('fleets');\n }\n\n public get demandSources(): string[] {\n return this.getUserProperty('demandSources');\n }\n\n private getUserProperty(key: keyof UserPayload): string[] {\n if (!this.privatePermissions) {\n throw new Error(`Cannot get ${key} without calling (async) getUserPermissions before`);\n }\n return Object.keys(this.privatePermissions[key] || {});\n }\n\n public get elevatedPermissions(): UserPayload {\n return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());\n }\n\n public get permissions(): UserPayload | undefined {\n if (!this.privatePermissions) {\n throw new Error('Cannot get permissions without calling (async) getUserPermissions before');\n }\n\n return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());\n }\n\n public elevatePermissions(addedPermissions: PartialUserPayload): (() => void) & { [Symbol.dispose]: () => void; } {\n // @itayankri is concerned about memory consumption, so create a symbol with no description, to avoid assigning memory for the description string\n const elevationId = Symbol();\n\n // Validate that the added permissions are valid UUIDs\n Object.values(addedPermissions).forEach((entityIds) => {\n Object.keys(entityIds).forEach((entityId) => {\n if (!validateUUID(entityId)) {\n throw new Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${entityId}`);\n }\n });\n });\n\n const currentUserTrace = getCurrentContext();\n if (!currentUserTrace) {\n throw new Error('Cannot find current user cross services trace');\n }\n\n const currentElevation = JSON.parse(currentUserTrace.context?.get(ELEVATED_PERMISSIONS_HEADER) as string | undefined || '{}');\n const newElevation = Object.assign(currentElevation, addedPermissions);\n this.privateElevatedPermissionsHash.set(elevationId, newElevation);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n const cleanup = () => {\n this.privateElevatedPermissionsHash.delete(elevationId);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n };\n cleanup[Symbol.dispose] = cleanup;\n return cleanup;\n }\n\n public async getUserPermissionsLegacy(): Promise<unknown> {\n if (!this.id) {\n return undefined;\n }\n if (this.privatePermissionsLegacy) {\n return this.privatePermissionsLegacy;\n }\n\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n legacy: true,\n });\n let data = userCache.get(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get(`/api/v1/users/${this.id}/authorization-payload-legacy`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.privatePermissionsLegacy = data;\n return this.privatePermissionsLegacy;\n }\n\n public get permissionsLegacy(): any {\n if (!this.privatePermissionsLegacy) {\n throw new Error('Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before');\n }\n return this.privatePermissionsLegacy;\n }\n\n public async getUserAppPermissions(appId: string, clientSecret: string): Promise<UserPayload | undefined> {\n if (!this.id || !appId || !clientSecret) {\n return undefined;\n }\n const currentAppPermission = this.appPermission[appId];\n\n if (currentAppPermission) {\n return currentAppPermission;\n }\n\n const cacheKey = `${this.id}:${appId}`;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.appPermission[appId] = cachedResult;\n return cachedResult;\n }\n\n const { data } = await AutofleetApiNetwork.post<UserPayload>(`/api/v1/apps/${appId}/get-user-payload`, {\n userId: this.id,\n }, {\n headers: {\n 'x-autofleet-apps-secret': clientSecret,\n },\n });\n\n userCache.set(cacheKey, data);\n this.appPermission[appId] = data;\n return this.appPermission[appId];\n }\n\n public extendRequiredContexts(contextIdsToAdd?: string[]): void {\n if (!Array.isArray(contextIdsToAdd)) {\n return;\n }\n\n const newContextIds = Array.from(new Set([...(this.contextIds ?? []), ...contextIdsToAdd]));\n\n if (newContextIds.length > MAX_CONTEXT_IDS) {\n return;\n }\n\n this.contextIds = newContextIds;\n const currentUserTrace = getCurrentContext();\n currentUserTrace?.context?.set(CONTEXTS_IDS_HEADER, this.contextIds.join(','));\n }\n}\n","import { AutofleetApiNetwork } from './services';\n\nexport const decodeAppBearer = async (bearer: string, appId: string): Promise<any> => {\n const { data: decoded } = await AutofleetApiNetwork.post('/api/v1/auth', { bearer, appId });\n return decoded;\n};\n\nexport const getClientSecret = async (appId: string): Promise<any> => {\n const { data: secret } = await AutofleetApiNetwork.get(`/api/v1/auth/client-secret/${appId}`);\n return secret;\n};\n","export default class AppDoesNotExist extends Error {\n name = 'AppDoesNotExist';\n\n message = 'app does not exist';\n}\n","export const IDENTITY_MS = 'identity-ms';\nexport const ACCESS_TOKEN = 'accessToken';\nexport const USER_OBJECT = 'userObject';\nexport const USER_TRACING_HEADER = 'x-af-user-id';\nexport const ORIGIN_HEADER = 'X-IAF-ORIGIN-SERVICE';\nexport const USER_PERMISSIONS_HEADER = 'x-af-user-permissions';\nexport const LOWER_CASE_ORIGIN_HEADER = ORIGIN_HEADER.toLowerCase() as Lowercase<typeof ORIGIN_HEADER>;\nexport const AUTOFLEET_APPS_SECRET_HEADER = 'x-autofleet-apps-secret';\n","import type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport ApiUser, { CONTEXTS_IDS_HEADER, ELEVATED_PERMISSIONS_HEADER, type UserPayload } from './ApiUser';\nimport {\n IDENTITY_MS,\n USER_OBJECT,\n USER_TRACING_HEADER,\n ORIGIN_HEADER,\n LOWER_CASE_ORIGIN_HEADER,\n} from './const';\n\nexport type CustomPermissionLoader = (userId: string) => Promise<UserPayload>;\nexport interface AuthFromUserIdHeaderOptions {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n customPermissionLoader?: CustomPermissionLoader;\n}\n\nexport const authFromUserIdHeader = async (options: AuthFromUserIdHeaderOptions, headers: IncomingHttpHeaders): Promise<ApiUser | undefined> => {\n const originHeader = headers[ORIGIN_HEADER] || headers[LOWER_CASE_ORIGIN_HEADER] || '';\n if (!Array.isArray(originHeader) && originHeader.toLowerCase() === IDENTITY_MS) {\n return undefined;\n }\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n customPermissionLoader,\n } = options;\n const userId = headers[USER_TRACING_HEADER] as string;\n if (!userId || Array.isArray(userId)) {\n return undefined;\n }\n\n const elevatedPermHeaderValue = headers[ELEVATED_PERMISSIONS_HEADER];\n const elevatedPermissionsFromHeader = elevatedPermHeaderValue?.length && elevatedPermHeaderValue.length > 0 ? JSON.parse(elevatedPermHeaderValue as string) : {};\n const contextIds = (headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n\n const userObject = new ApiUser(userId, 'user', elevatedPermissionsFromHeader, contextIds);\n if (eagerLoadUserPermissions) {\n if (customPermissionLoader) {\n await userObject.useCustomPermissionLoader(customPermissionLoader);\n } else {\n await userObject.getUserPermissions();\n }\n }\n\n if (eagerLoadUserPermissionsLegacy) {\n await userObject.getUserPermissionsLegacy();\n }\n\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n return userObject;\n};\n","import type { Handler, Request } from 'express';\nimport { getCurrentContext, newTrace, traceTypes } from '@autofleet/outbreak';\nimport jwt from 'jsonwebtoken';\nimport ApiUser, { CONTEXTS_IDS_HEADER, MAX_CONTEXT_IDS } from './ApiUser';\nimport { decodeAppBearer } from '../app-auth';\nimport AppDoesNotExist from '../exceptions/appDoesNotExist';\nimport { decodeBearer, getAuthFromBearer } from '../utils';\nimport {\n ACCESS_TOKEN,\n USER_OBJECT,\n USER_TRACING_HEADER,\n USER_PERMISSIONS_HEADER,\n AUTOFLEET_APPS_SECRET_HEADER,\n} from './const';\nimport { authFromUserIdHeader, type AuthFromUserIdHeaderOptions } from './common';\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n user: ApiUser;\n }\n}\n\ntype Asyncify<T extends (...a: any[]) => any> = (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;\n\nexport const middleware = (options: AuthFromUserIdHeaderOptions = {}): Asyncify<Handler> => async (req, res, next): Promise<any> => {\n try {\n const userObject = await authFromUserIdHeader(options, req.headers);\n if (userObject) {\n req.user = userObject;\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n }\n\n next();\n } catch {\n res.status(401).json({ error: 'cannot authenticate user' });\n }\n};\n\nexport const middlewareWithDecode = (options: {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n returnErrorIfNoToken?: boolean;\n appSecret?: string;\n} = {}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n returnErrorIfNoToken,\n appSecret,\n } = options;\n let decoded;\n if (req.headers.authorization) {\n try {\n decoded = await decodeBearer(req.headers.authorization, appSecret);\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n } else if (e instanceof jwt.JsonWebTokenError) {\n res.status(400).json({ errors: [e.message] });\n } else {\n res.status(500).json({ errors: ['Server error while parsing token'] });\n }\n return;\n }\n const userId = decoded?.user?.id;\n\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const contextIds = (req.headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n const userObject = new ApiUser(userId, decoded?.user?.accountType, undefined, contextIds);\n\n if (eagerLoadUserPermissions || eagerLoadUserPermissionsLegacy) {\n await Promise.all([\n eagerLoadUserPermissions && userObject.getUserPermissions(),\n eagerLoadUserPermissionsLegacy && userObject.getUserPermissionsLegacy(),\n ]);\n }\n\n req.user = userObject;\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n } else if (returnErrorIfNoToken) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n next();\n};\n\nexport const appMiddleware = (options: {\n appId: string;\n clientSecret: string;\n}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n appId,\n clientSecret,\n } = options;\n let decoded;\n\n if (!req.headers.authorization) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n\n try {\n decoded = await decodeAppBearer(req.headers.authorization, appId);\n if (!decoded) {\n throw new AppDoesNotExist();\n }\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n return;\n }\n if ([jwt.JsonWebTokenError, AppDoesNotExist].some(Err => e instanceof Err)) {\n res.status(400).json({ errors: [(e as Error).message] });\n return;\n }\n res.status(500).json({ errors: ['Server error while parsing token'] });\n return;\n }\n const userId = decoded?.userId;\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const userObject = new ApiUser(userId);\n\n if (appId) {\n req.headers[AUTOFLEET_APPS_SECRET_HEADER] = clientSecret;\n // Won't work until we find a better solution for identity ms\n await userObject.getUserAppPermissions(appId, clientSecret);\n }\n\n req.user = userObject;\n const currentTraceContext = getCurrentContext().nonHeaderContext;\n currentTraceContext?.set(USER_OBJECT, userObject);\n currentTraceContext?.set(ACCESS_TOKEN, getAuthFromBearer(req.headers.authorization));\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n\n next();\n};\n\nexport const eagerLoadPermissionsMiddleware: Asyncify<Handler> = async (req, res, next) => {\n await req.user.getUserPermissions();\n next();\n};\n\nexport const getDecodedBearer = (req: Request, appSecret?: string): any => {\n if (!req.headers.authorization) {\n return null;\n }\n return decodeBearer(req.headers.authorization, appSecret);\n};\n\nexport const createOrSetRabbitTrace = async (trace: ReturnType<typeof newTrace> | undefined, userId: string | undefined, contextIds?: string[]): Promise<void> => {\n const addContextIds = Array.isArray(contextIds) && contextIds.length <= MAX_CONTEXT_IDS;\n const userObject = new ApiUser(\n userId,\n undefined,\n undefined,\n addContextIds ? contextIds : undefined,\n );\n\n await userObject.getUserPermissions();\n trace ??= newTrace(traceTypes.RABBIT);\n trace.nonHeaderContext.set(USER_OBJECT, userObject);\n\n if (addContextIds) {\n trace.context.set(CONTEXTS_IDS_HEADER, contextIds?.join(',') || '');\n }\n};\n\nexport default ApiUser;\n","import type { FastifyPluginCallback } from 'fastify';\nimport type ApiUser from './ApiUser';\nimport { type AuthFromUserIdHeaderOptions, authFromUserIdHeader } from './common';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n user?: ApiUser;\n }\n}\n\nexport const authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions> = (fastify, options, done) => {\n fastify.decorateRequest('user', undefined);\n fastify.addHook('onRequest', async (request, reply) => {\n try {\n const user = await authFromUserIdHeader(options, request.headers);\n if (user) {\n request.user = user;\n }\n } catch {\n reply.status(401).send({ error: 'cannot authenticate user' });\n }\n });\n\n done();\n};\nObject.defineProperty(authFromUserIdHeaderPlugin, Symbol.for('skip-override'), { value: true }); // Prevent Fastify from overriding the plugin\n","import { getCurrentContext } from '@autofleet/outbreak';\nimport { USER_OBJECT } from './user/const';\nimport type ApiUser from './user/ApiUser';\nimport type { UserPayload } from './user/ApiUser';\n\nexport const getUser = (): ApiUser | undefined => getCurrentContext().nonHeaderContext?.get(USER_OBJECT) as ApiUser | undefined;\n\nexport const isUserExist = (): string | undefined => getUser()?.id;\n\nconst checkUserPermissions = (\n entityId: string,\n entityType: Exclude<keyof UserPayload, 'accountType' | 'createdAt'>,\n) => !isUserExist() || Object.hasOwn(getUser()!.permissions?.[entityType] ?? {}, entityId);\n\nexport const checkFleetPermission = (fleetId: string): boolean => checkUserPermissions(fleetId, 'fleets');\nexport const checkBusinessModelPermission = (businessModelId: string): boolean => checkUserPermissions(businessModelId, 'businessModels');\nexport const checkDemandSourcePermission = (demandSourceId: string): boolean => checkUserPermissions(demandSourceId, 'demandSources');\n","import type ApiUser from './user';\n\nexport class UnauthorizedAccessError extends Error {\n constructor(public user: ApiUser | null = null, message = 'UnauthorizedAccessError') {\n super(message);\n this.name = 'UnauthorizedAccessError';\n }\n}\n","import jwt from 'jsonwebtoken';\n\nexport const AUTHORIZATION_METHODS = {\n NONE: 'NONE',\n BASIC: 'BASIC',\n JWT: 'JWT',\n};\n\nconst AUTHORIZATION_ACTIONS = {\n [AUTHORIZATION_METHODS.NONE]: () => undefined,\n [AUTHORIZATION_METHODS.BASIC]: (authorizationSettings: any) => {\n const { username, password } = authorizationSettings;\n const encodedCredentials = Buffer.from(`${username}:${password}`).toString('base64');\n return `Basic ${encodedCredentials}`;\n },\n [AUTHORIZATION_METHODS.JWT]: (authorizationSettings: any) => {\n const { secret } = authorizationSettings;\n if (secret) {\n return `Bearer ${jwt.sign({}, secret, { expiresIn: 10 })}`;\n }\n return undefined;\n },\n};\n\nexport const getAuthorizationHeader = (authorizationSettings: { method: string; } | undefined): string | undefined => {\n const authorizationMethod = authorizationSettings?.method;\n\n if (!authorizationMethod || !AUTHORIZATION_ACTIONS[authorizationMethod]) {\n return undefined;\n }\n\n return AUTHORIZATION_ACTIONS[authorizationMethod](authorizationSettings);\n};\n","import type { Permission } from '@autofleet/common-types/lib/identity';\nimport NodeCache from 'node-cache';\n\ntype EntityPermissions = Record<string, Permission[]>;\n\nexport class PermissionCache {\n private cache: NodeCache;\n\n private ttl = 10;\n\n /**\n * Creates a new PermissionCache instance\n * @param cache - Optional NodeCache instance. If not provided, creates a new one with default TTL of 10 seconds\n */\n constructor(cache?: NodeCache, ttl?: number) {\n if (ttl) {\n this.ttl = ttl;\n }\n this.cache = cache ?? new NodeCache({ stdTTL: this.ttl });\n }\n\n private static getCacheKeys(cacheName: string, userId: string, contextIds: string[]) {\n const baseCacheKey = `${cacheName}-${userId}`;\n const cacheKeys = contextIds.map(contextId => `${baseCacheKey}-${contextId}`);\n\n return { baseCacheKey, cacheKeys };\n }\n\n /**\n * Retrieves user permissions for multiple contexts from cache\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to fetch permissions for\n * @returns An object mapping context IDs to permission arrays\n */\n public getUserPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('perm', userId, contextIds);\n const cacheResult = this.cache.mget<Permission[]>(cacheKeys);\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Stores user permissions for multiple contexts in cache\n * @param userId - The user identifier\n * @param PermissionsByContextId - Object mapping context IDs to permission arrays\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setUserPermissions(userId: string, PermissionsByContextId: EntityPermissions): { success: boolean; } {\n try {\n const cacheKey = (contextId: string) => `perm-${userId}-${contextId}`;\n\n const cacheEntries = Object.entries(PermissionsByContextId).map(([contextId, permissions]) => ({\n key: cacheKey(contextId),\n val: permissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n /**\n * Retrieves denied permissions status for multiple contexts from cache.\n * \"Denied\" permissions indicate that a user has already been evaluated for a specific set of\n * required permissions in a given context, avoiding redundant permission checks.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to check\n * @returns An object mapping context IDs to arrays of denied permissions\n */\n public getCachedDeniedPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('seen', userId, contextIds);\n const cacheResult = this.cache.mget<Permission[]>(cacheKeys);\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Marks permissions as seen for multiple contexts in cache.\n * \"Seen\" permissions are used to track that a user has already been evaluated for a specific\n * set of required permissions in given contexts, preventing duplicate permission evaluations.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to mark as seen\n * @param deniedPermissions - Array of permissions that were evaluated and denied\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setCachedDeniedPermissions(userId: string, contextIds: string[], deniedPermissions: string[]): { success: boolean; } {\n try {\n const seenKey = (contextId: string) => `seen-${userId}-${contextId}`;\n\n const cacheEntries = contextIds.map(contextId => ({\n key: seenKey(contextId),\n val: deniedPermissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n}\n\nexport const permissionCache: PermissionCache = new PermissionCache();\n","export const PERMISSION_ERROR_TYPES = {\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n API_ERROR: 'API_ERROR',\n NO_REQUIRED_PERMISSIONS: 'NO_REQUIRED_PERMISSIONS',\n UNAUTHORIZED: 'UNAUTHORIZED',\n BAD_REQUEST: 'BAD_REQUEST',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n};\n\nexport const PERMISSION_ERROR_MESSAGES = {\n USER_NOT_FOUND: 'User not found',\n INSUFFICIENT_PERMISSIONS: 'User does not have sufficient permissions',\n VALIDATION_ERROR: 'Validation error occurred',\n API_ERROR: 'API error occurred',\n NO_REQUIRED_PERMISSIONS: 'No required permissions provided for evaluation',\n UNAUTHORIZED: 'User is not authorized to perform this action',\n BAD_REQUEST: 'Bad request, please check the input parameters',\n INTERNAL_ERROR: 'Internal server error occurred while checking permissions',\n};\n","import { getUser } from '../../check-permission';\nimport { IdentityNetwork } from '../../services';\nimport type {\n EvaluatePermissionsParams,\n PermissionsEvaluationByContextId,\n PermissionCheckResult,\n Logger,\n PermissionsByContextId,\n ResolvePermissionsParams,\n} from './types';\nimport type { PermissionErrorType } from './errors';\nimport { permissionCache } from './permissionCache';\nimport { PERMISSION_ERROR_TYPES } from './consts';\nimport type { Permission, RequiredPermission } from '@autofleet/common-types/lib/identity';\nimport type { AxiosResponse } from 'axios';\n\n/**\n * Evaluates whether user permissions meet requirements\n * @param permissions - User's actual permissions\n * @param requiredPermissions - Required permissions to check against\n * @returns true if permissions meet requirements\n */\nconst getHasRequiredPermissions = (\n permissions: Permission[],\n requiredPermissions: RequiredPermission[],\n): boolean => {\n const permissionsSet = new Set(permissions);\n\n return requiredPermissions.some((requiredPermission) => {\n if (typeof requiredPermission === 'string') return permissionsSet.has(requiredPermission);\n\n return requiredPermission.every(nestedRequiredPermission => permissionsSet.has(nestedRequiredPermission));\n });\n};\n\n/**\n * Checks if permission requirements are met across all contexts\n * @param permissionsEvaluationByContextId - {@link PermissionsEvaluationByContextId} Permission evaluation results by context\n * @param requireAll - Whether all contexts must satisfy requirements (true) or just one (false)\n * @returns true if requirements are met\n */\nexport const checkAuthorizeRequirements = (permissionsEvaluationByContextId: PermissionsEvaluationByContextId, requireAll: boolean): boolean => {\n const permissionsEvaluationValues = Object.values(permissionsEvaluationByContextId);\n\n if (requireAll) {\n return permissionsEvaluationValues.every(evaluation => evaluation.hasRequiredPermissions);\n }\n return permissionsEvaluationValues.some(evaluation => evaluation.hasRequiredPermissions);\n};\n\nconst validateInput = (contextIds: string[]): string[] => {\n const errors: string[] = [];\n\n if (!contextIds?.length) {\n errors.push('contextIds cannot be empty');\n }\n\n return errors;\n};\n\nconst createErrorResult = (\n error: PermissionErrorType,\n requiredPermissions: RequiredPermission[],\n contextIds: string[],\n message?: string,\n): PermissionCheckResult => ({\n isAuthorized: false,\n error,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n ...(message && { message }),\n});\n\n/**\n * Caches permission results based on whether they meet requirements\n * - Caches granted permissions directly\n * - Groups denied permissions by identical sets to optimize cache storage\n * @param userId - User ID\n * @param resolvedPermissions - Permissions evaluation results to cache\n * @param requiredPermissions - Required permissions used for evaluation\n */\nconst cachePermissionsResults = (\n userId: string,\n resolvedPermissions: PermissionsEvaluationByContextId,\n requiredPermissions: RequiredPermission[],\n logger?: Logger,\n): void => {\n const permissionsToCache: PermissionsByContextId = {};\n const deniedPermissionsMap = new Map<string, string[]>();\n\n logger?.debug('Start caching permissions results', {\n userId,\n resolvedPermissions,\n requiredPermissions,\n });\n\n Object.entries(resolvedPermissions).forEach(([contextId, evaluation]) => {\n if (evaluation?.permissions) {\n const userPermissionsSet = new Set(evaluation.permissions);\n const effectivePermissions = requiredPermissions.flat().filter(permission => userPermissionsSet.has(permission));\n const deniedPermissions = requiredPermissions.flat().filter(permission => !userPermissionsSet.has(permission));\n\n if (effectivePermissions.length) {\n // Store only effective permissions (intersection of user and required permissions)\n permissionsToCache[contextId] = effectivePermissions;\n }\n\n if (deniedPermissions.length) {\n // Calculate which required permissions were denied by filtering out the ones the user has\n const deniedPermissionsKey = deniedPermissions.sort().join(',');\n\n if (!deniedPermissionsMap.has(deniedPermissionsKey)) {\n deniedPermissionsMap.set(deniedPermissionsKey, []);\n }\n deniedPermissionsMap.get(deniedPermissionsKey)!.push(contextId);\n }\n }\n });\n\n if (Object.keys(permissionsToCache).length > 0) {\n const pastCache = permissionCache.getUserPermissions(userId, Object.keys(permissionsToCache));\n\n Object.keys(pastCache ?? {}).forEach((contextId) => {\n if (permissionsToCache[contextId]) {\n // Merge with any previously cached permissions to avoid overwriting\n const mergedPermissions = Array.from(new Set([...(pastCache[contextId] || []), ...permissionsToCache[contextId]]));\n permissionsToCache[contextId] = mergedPermissions;\n }\n });\n\n logger?.debug('Caching granted permissions', { userId, permissionsToCache, pastCache });\n const { success } = permissionCache.setUserPermissions(userId, permissionsToCache);\n if (!success) {\n logger?.error('Failed to cache granted permissions', { userId, permissionsToCache });\n }\n }\n\n // Cache denied permissions grouped by the same denied permissions to utilize mset efficiently\n Array.from(deniedPermissionsMap.entries()).map(([deniedPermissionsKey, contextIds]) => {\n const deniedPermissions = deniedPermissionsKey.split(',');\n return permissionCache.setCachedDeniedPermissions(userId, contextIds, deniedPermissions);\n });\n\n logger?.debug('Caching denied permissions', {\n userId,\n deniedPermissionsGroups: Array.from(deniedPermissionsMap.entries()).map(([key, contexts]) => ({\n deniedPermissions: key.split(','),\n contextIds: contexts,\n })),\n });\n};\n\n/**\n * Determines if a context can be resolved from denied permissions cache\n * @param deniedPermissions - Previously denied permissions for the context\n * @param requiredPermissions - Currently required permissions\n * @returns Object indicating if context is resolvable from cache and the result\n */\nconst isResolvableFromDeniedCache = (\n deniedPermissions: string[],\n requiredPermissions: RequiredPermission[],\n): { isResolvableFromCache: boolean; hasRequiredPermissions: boolean; } => {\n if (!deniedPermissions.length) {\n return { isResolvableFromCache: false, hasRequiredPermissions: false };\n }\n\n const deniedSet = new Set(deniedPermissions);\n\n // For OR: only if ALL required permissions were denied, we can resolve as false\n const areAllRequiredDenied = requiredPermissions.every((requiredPermission) => {\n if (typeof requiredPermission === 'string') return deniedSet.has(requiredPermission);\n\n // For AND: if any required permission was denied, we can resolve as false\n return requiredPermission.some(nestedRequiredPermission => deniedSet.has(nestedRequiredPermission));\n });\n\n return { isResolvableFromCache: areAllRequiredDenied, hasRequiredPermissions: false };\n};\n\n/**\n * Gets contexts that need API calls (unseen + partially resolvable seen contexts)\n * @param unseenContextIds - Context IDs that have no cached data\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedContextEntries - Context IDs that were fully resolved from cache\n * @returns Array of context IDs that need API calls\n */\nconst getContextsNeedingAPI = (\n unseenContextIds: string[],\n deniedContextIds: string[],\n deniedContextEntries: PermissionsEvaluationByContextId,\n): string[] => {\n const contextIdsNeedingAPI = [...unseenContextIds];\n\n // Add denied contexts that couldn't be resolved from cache\n const unresolvedDeniedContextIds = deniedContextIds.filter(contextId => !deniedContextEntries[contextId]);\n contextIdsNeedingAPI.push(...unresolvedDeniedContextIds);\n\n return contextIdsNeedingAPI;\n};\n\n/**\n * Processes denied contexts that can be resolved from denied permissions cache\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedPermissions - Denied permissions cache data\n * @param requiredPermissions - Currently required permissions\n * @returns Object with cache-resolvable contexts and their evaluations\n */\nconst processCachedDeniedContexts = (\n deniedContextIds: string[],\n seenPermissions: PermissionsByContextId,\n requiredPermissions: RequiredPermission[],\n): PermissionsEvaluationByContextId => {\n const result: PermissionsEvaluationByContextId = {};\n\n deniedContextIds.forEach((contextId) => {\n const deniedPermissions = seenPermissions[contextId];\n const cacheResult = isResolvableFromDeniedCache(deniedPermissions, requiredPermissions);\n\n if (cacheResult.isResolvableFromCache) {\n result[contextId] = {\n permissions: [],\n hasRequiredPermissions: cacheResult.hasRequiredPermissions,\n };\n }\n });\n\n return result;\n};\n\n/**\n * Processes cached permissions into evaluation format\n * @param cachedPermissions - Raw permissions from cache\n * @param requiredPermissions - Required permissions to evaluate against\n * @returns Processed permissions evaluation\n */\nconst processCachedPermissions = (\n cachedPermissions: PermissionsByContextId,\n requiredPermissions: RequiredPermission[],\n): PermissionsEvaluationByContextId => {\n const processedPermissions: PermissionsEvaluationByContextId = {};\n\n if (Object.keys(cachedPermissions || {}).length) {\n Object.entries(cachedPermissions).forEach(([contextId, permissions]) => {\n const hasRequiredPermissions = getHasRequiredPermissions(permissions, requiredPermissions);\n\n // Only include contexts that can be fully resolved from cache\n // Since we only cache effective permissions we won't have all of the user's permissions to fully resolve\n if (hasRequiredPermissions) {\n processedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n }\n });\n }\n\n return processedPermissions;\n};\n\n/**\n * Creates empty permissions result for cases with no required permissions\n * @param contextIds - Context IDs to create results for\n * @returns {@link PermissionsEvaluationByContextId} with empty permissions and granted access\n */\nconst createEmptyPermissionsResult = (contextIds: string[]): PermissionsEvaluationByContextId => Object.fromEntries(\n contextIds.map(contextId => [\n contextId,\n { permissions: [], hasRequiredPermissions: true },\n ]),\n);\n\n/**\n * Calls the Identity API and resolves permissions for unseen contexts\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve\n * @param requiredPermissions - Required permissions\n * @param options - API call options\n * @returns Resolved permissions from API\n */\nexport const resolvePermissions = async ({\n userId,\n contextIds,\n requiredPermissions,\n options,\n}: ResolvePermissionsParams): Promise<PermissionsEvaluationByContextId> => {\n if (!requiredPermissions.length) {\n return createEmptyPermissionsResult(contextIds);\n }\n\n const response: AxiosResponse<{ permissionsByContexts: PermissionsByContextId; }> = await IdentityNetwork.post(`/api/v1/permissions/users/${userId}`,\n { userId, contextIds },\n { timeout: options.timeout },\n );\n\n if (!Object.keys(response?.data?.permissionsByContexts || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n return Object.fromEntries(\n Object.entries(response.data.permissionsByContexts).map(([contextId, permissions]) => [\n contextId,\n {\n permissions,\n hasRequiredPermissions: getHasRequiredPermissions(permissions, requiredPermissions),\n },\n ]),\n );\n};\n\n/**\n * Validates user context and required permissions for evaluation\n * @param contextIds - Context IDs to validate\n * @param requiredPermissions - Required permissions to validate\n * @param userId - User ID to validate\n * @param logger - Logger instance for warnings\n * @returns Error result if validation fails, null if validation passes\n */\nconst validateEvaluationInput = (\n contextIds: string[],\n requiredPermissions: RequiredPermission[],\n userId: string | null,\n logger?: Logger,\n): PermissionCheckResult | null => {\n const validationErrors = validateInput(contextIds);\n if (validationErrors.length) {\n return createErrorResult(PERMISSION_ERROR_TYPES.VALIDATION_ERROR, requiredPermissions, contextIds, validationErrors.join(', '));\n }\n\n if (!userId) {\n logger?.warn('User not found in context, cannot check permissions', {\n contextIds,\n requiredPermissions,\n });\n return createErrorResult(PERMISSION_ERROR_TYPES.USER_NOT_FOUND, requiredPermissions, contextIds);\n }\n\n if (!requiredPermissions?.length) {\n logger?.info('No requiredPermissions provided', {\n userId,\n contextIds,\n });\n return {\n isAuthorized: false,\n userId,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n error: PERMISSION_ERROR_TYPES.NO_REQUIRED_PERMISSIONS,\n };\n }\n\n return null;\n};\n\n/**\n * Resolves permissions from Identity API and combines with existing resolved permissions\n * @param userId - User ID\n * @param contextIdsNeedingAPI - Context IDs that need API resolution\n * @param requiredPermissions - Required permissions to check\n * @param options - API call options {@link ResolvePermissionsParams.options}\n * @param resolvedPermissions - Already resolved permissions from cache and denied cache\n * @returns Combined permissions including those resolved from API\n */\nconst resolvePermissionsFromAPI = async (\n userId: string,\n contextIdsNeedingAPI: string[],\n requiredPermissions: RequiredPermission[],\n options: { timeout: number; },\n resolvedPermissions: PermissionsEvaluationByContextId,\n): Promise<PermissionsEvaluationByContextId> => {\n let newResolvedPermissions: PermissionsEvaluationByContextId = { ...resolvedPermissions };\n // Call Identity API for unresolved contexts\n const resolvedPermissionsFromIdentityMS = await resolvePermissions({\n userId,\n contextIds: contextIdsNeedingAPI,\n requiredPermissions,\n options,\n });\n\n // Combine all resolved permissions\n newResolvedPermissions = {\n ...resolvedPermissions,\n ...resolvedPermissionsFromIdentityMS,\n };\n\n cachePermissionsResults(userId, resolvedPermissionsFromIdentityMS, requiredPermissions);\n\n return newResolvedPermissions;\n};\n\n/**\n * Resolves permissions from multiple sources (cache, seen contexts, API)\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve permissions for\n * @param requiredPermissions - Required permissions to check\n * @param options - Evaluation options\n * @returns Combined permissions from all sources: cache, denied cache, and API\n */\nconst resolveAllPermissions = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: RequiredPermission[],\n options: {\n timeout: number;\n logger?: Logger;\n },\n): Promise<PermissionsEvaluationByContextId> => {\n const { logger } = options;\n // Cached allowed permissions\n const cachedAllowedPermissions = permissionCache.getUserPermissions(userId, contextIds);\n const resolvedPermissionsFromCache = processCachedPermissions(\n cachedAllowedPermissions,\n requiredPermissions,\n );\n let resolvedPermissions = { ...resolvedPermissionsFromCache };\n\n // Context ids without allowed permissions in cache\n const contextIdsWithoutAllowedCache = contextIds.filter(contextId => !resolvedPermissionsFromCache[contextId]);\n\n // Handle denied cache permissions\n const cachedDeniedPermissions = permissionCache.getCachedDeniedPermissions(userId, contextIdsWithoutAllowedCache);\n\n // Context ids that have never been seen before (not in allowed or denied cache)\n const unseenContextIds = contextIdsWithoutAllowedCache.filter(contextId => !cachedDeniedPermissions[contextId]);\n\n // Process denied permissions cache\n const deniedContextIds = contextIdsWithoutAllowedCache.filter(contextId => cachedDeniedPermissions[contextId]);\n const deniedContextEntries = processCachedDeniedContexts(\n deniedContextIds,\n cachedDeniedPermissions,\n requiredPermissions,\n );\n\n // Combine resolved permissions with those resolved from denied cache\n resolvedPermissions = { ...resolvedPermissions, ...deniedContextEntries };\n\n // Resolve the remaining contexts using the Identity API\n const contextIdsNeedingAPI = getContextsNeedingAPI(unseenContextIds, deniedContextIds, deniedContextEntries);\n if (contextIdsNeedingAPI.length) {\n // Call Identity API for unresolved contexts\n return resolvePermissionsFromAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n resolvedPermissions,\n );\n }\n\n logger?.debug('Final resolved permissions', {\n userId,\n requiredPermissions,\n resolvedPermissions,\n });\n\n return resolvedPermissions;\n};\n\n/**\n * Main SDK function to evaluate user permissions\n * Checks both cached and API-resolved permissions to determine access\n * @param params - {@link EvaluatePermissionsParams}\n * @returns {@link PermissionCheckResult} Detailed permission check result with access status and context\n */\nexport const evaluatePermissions = async ({\n requiredPermissions,\n contextIds,\n logger,\n userId,\n options: {\n requireAll = true,\n timeout = 10000,\n },\n}: EvaluatePermissionsParams): Promise<PermissionCheckResult> => {\n const resolvedUserId = userId || getUser()?.id || null;\n\n const validationError = validateEvaluationInput(contextIds, requiredPermissions, resolvedUserId, logger);\n if (validationError) {\n return validationError;\n }\n\n const resolvedPermissions = await resolveAllPermissions(\n resolvedUserId!,\n contextIds,\n requiredPermissions,\n { timeout, logger },\n );\n\n const isAuthorized = checkAuthorizeRequirements(resolvedPermissions, requireAll);\n\n logger?.info('Resolved permissions', {\n userId: resolvedUserId,\n requiredPermissions,\n resolvedPermissions,\n isAuthorized,\n requireAll,\n contextIds,\n });\n\n return {\n isAuthorized,\n userId: resolvedUserId!,\n resolvedPermissions,\n requiredPermissions,\n contextIds,\n ...(isAuthorized ? {} : { error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS }),\n };\n};\n\nexport default evaluatePermissions;\n","import type { Request, Response, NextFunction } from 'express';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { evaluatePermissions } from '../SDK/evaluatePermissions';\nimport type { EvaluatePermissionsOpts } from '../SDK/types';\nimport type ApiUser from '../../user/ApiUser';\nimport { PERMISSION_ERROR_MESSAGES, PERMISSION_ERROR_TYPES } from '../SDK/consts';\nimport type { RequiredPermission } from '@autofleet/common-types/lib/identity';\n\nexport interface RequirePermissionsOptions extends EvaluatePermissionsOpts {\n logger?: LoggerInstanceManager;\n}\nconst getContextIds = (user: ApiUser): string[] => {\n const contexts = Object.keys(user.permissions?.contexts ?? {});\n return Array.from(new Set([\n ...contexts,\n ])).filter(Boolean);\n};\n\nconst handleError = (\n errorLabel: string,\n message: string,\n code: number,\n enforce: boolean | undefined,\n res: Response,\n req: Request,\n next: NextFunction,\n logger?: LoggerInstanceManager,\n additionalInfo?: { requiredPermissions?: RequiredPermission[]; },\n) => {\n if (enforce) {\n logger?.error(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n\n res.status(code).json({\n error: errorLabel,\n message,\n ...(additionalInfo?.requiredPermissions && { requiredPermissions: additionalInfo.requiredPermissions }),\n });\n return;\n }\n\n logger?.warn(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n next();\n};\n\n/**\n * Express middleware that requires specific permissions for route access\n *\n * @param requiredPermissions - Array of permissions required to access the route\n * @param options - Configuration options for permission checking\n * @returns Express middleware function\n */\nexport const requirePermissions = (\n requiredPermissions: RequiredPermission[],\n options: RequirePermissionsOptions = {\n enforce: false,\n requireAll: true,\n },\n) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n const {\n logger,\n enforce,\n requireAll,\n } = options;\n\n const handleErrorWrapper = (errorLabel: string, message: string, code: number) => handleError(\n errorLabel,\n message,\n code,\n enforce,\n res,\n req,\n next,\n logger,\n { requiredPermissions },\n );\n const user = req.user;\n\n const contextIds: string[] = getContextIds(user);\n\n if (!contextIds?.length) {\n handleErrorWrapper(\n PERMISSION_ERROR_TYPES.BAD_REQUEST,\n PERMISSION_ERROR_MESSAGES.BAD_REQUEST,\n 400,\n );\n return;\n }\n\n // Evaluate permissions using SDK\n const result = await evaluatePermissions({\n requiredPermissions,\n contextIds,\n logger,\n userId: user.id,\n options: {\n requireAll: requireAll ?? true,\n timeout: 10000,\n ...(logger && { logger }),\n },\n });\n\n if (result.isAuthorized) {\n logger?.info('User has required permissions', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n if (!enforce) {\n logger?.warn('User does not have required permissions, skipping enforcement', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(403).json({\n error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS,\n message: PERMISSION_ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS,\n required: requiredPermissions,\n contexts: contextIds,\n userId: result.userId,\n });\n } catch (error) {\n const requestLogger = options.logger;\n requestLogger?.error('Error in requirePermissions middleware', {\n error,\n requiredPermissions,\n url: req.url,\n method: req.method,\n });\n\n if (!options.enforce) {\n options.logger?.error('Error during permission check, skipping enforcement', {\n error,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(500).json({\n error: PERMISSION_ERROR_TYPES.INTERNAL_ERROR,\n message: PERMISSION_ERROR_MESSAGES.INTERNAL_ERROR,\n });\n }\n};\n\nexport default requirePermissions;\n","import { evaluatePermissions } from './SDK/evaluatePermissions';\nimport { requirePermissions } from './middleware/requirePermissions';\n\ninterface SdkExports {\n evaluatePermissions: typeof evaluatePermissions;\n}\n\ninterface MiddlewareExports {\n requirePermissions: typeof requirePermissions;\n}\n\ninterface PermissionsModule {\n sdk: SdkExports;\n middlewares: MiddlewareExports;\n}\n\nexport const sdk: SdkExports = {\n evaluatePermissions,\n};\n\nexport const middlewares: MiddlewareExports = {\n requirePermissions,\n};\n\nexport default {\n sdk,\n middlewares,\n} as PermissionsModule;\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport * as outbreak from '@autofleet/outbreak';\nimport User, {\n middleware,\n eagerLoadPermissionsMiddleware,\n middlewareWithDecode,\n getDecodedBearer,\n appMiddleware,\n createOrSetRabbitTrace,\n} from './user';\nimport { authFromUserIdHeaderPlugin } from './user/fastify';\nimport { type UserPayload, CONTEXTS_IDS_HEADER } from './user/ApiUser';\nimport {\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n} from './check-permission';\nimport { UnauthorizedAccessError } from './errors';\nimport { getRefreshTokenSecret, getTokenSecret } from './secret-getter';\nimport { AUTHORIZATION_METHODS, getAuthorizationHeader } from './authorization';\nimport * as permissions from './permissions';\n\nconst getCurrentPayload: typeof outbreak.getCurrentContext = outbreak.getCurrentContext;\n\ntype OutbreakOptions = Parameters<typeof outbreak.default>[0];\ntype LoggerWithContextMiddleware = Partial<Pick<LoggerInstanceManager, 'addContextMiddleware'>>;\nconst enableTracing = ({ outbreakOptions = {}, logger }: { outbreakOptions?: OutbreakOptions; logger?: LoggerWithContextMiddleware; } = {}): void => {\n outbreak.default({\n headersPrefix: 'x-af',\n contextMiddlewareGetter: logger?.addContextMiddleware,\n ...outbreakOptions,\n });\n};\n\nconst traceTypes: typeof outbreak.traceTypes = outbreak.traceTypes;\nconst newTrace: typeof outbreak.newTrace = outbreak.newTrace;\n\nexport {\n traceTypes,\n newTrace,\n enableTracing,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n getRefreshTokenSecret,\n getTokenSecret,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n type UserPayload,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\ninterface Default {\n traceTypes: typeof outbreak.traceTypes;\n newTrace: typeof outbreak.newTrace;\n User: typeof User;\n middleware: typeof middleware;\n middlewareWithDecode: typeof middlewareWithDecode;\n eagerLoadPermissionsMiddleware: typeof eagerLoadPermissionsMiddleware;\n getCurrentPayload: typeof outbreak.getCurrentContext;\n getDecodedBearer: typeof getDecodedBearer;\n checkFleetPermission: typeof checkFleetPermission;\n checkBusinessModelPermission: typeof checkBusinessModelPermission;\n checkDemandSourcePermission: typeof checkDemandSourcePermission;\n isUserExist: typeof isUserExist;\n getUser: typeof getUser;\n UnauthorizedAccessError: typeof UnauthorizedAccessError;\n appMiddleware: typeof appMiddleware;\n createOrSetRabbitTrace: typeof createOrSetRabbitTrace;\n outbreak: typeof outbreak;\n AUTHORIZATION_METHODS: typeof AUTHORIZATION_METHODS;\n getAuthorizationHeader: typeof getAuthorizationHeader;\n CONTEXTS_IDS_HEADER: typeof CONTEXTS_IDS_HEADER;\n authFromUserIdHeaderPlugin: typeof authFromUserIdHeaderPlugin;\n permissions: typeof permissions;\n}\n\nconst zehutDefault: Default = {\n traceTypes,\n newTrace,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\nexport default zehutDefault;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["unixTime: moment.Moment","jwt","jwt","IdentityNetwork: Network","Network","AutofleetApiNetwork: Network","MAX_CONTEXT_IDS: number","NodeCache","permissions: UserPayload","id?: string","accountType?: AccountType | undefined","contextIds?: string[]","jwt","eagerLoadPermissionsMiddleware: Asyncify<Handler>","traceTypes","authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions>","user: ApiUser | null","jwt","NodeCache","result: EntityPermissions","result: Record<string, string[]>","permissionCache: PermissionCache","errors: string[]","permissionsToCache: PermissionsByContextId","result: PermissionsEvaluationByContextId","processedPermissions: PermissionsEvaluationByContextId","data: PermissionsEvaluationByContextId","reEvaluatedPermissions: PermissionsEvaluationByContextId","newResolvedPermissions: PermissionsEvaluationByContextId","contextIds: string[]","sdk: SdkExports","middlewares: MiddlewareExports","getCurrentPayload: typeof outbreak.getCurrentContext","outbreak","traceTypes: typeof outbreak.traceTypes","newTrace: typeof outbreak.newTrace","zehutDefault: Default"],"sources":["../src/secret-getter.ts","../src/utils.ts","../src/services.ts","../src/user/ApiUser.ts","../src/app-auth.ts","../src/exceptions/appDoesNotExist.ts","../src/user/const.ts","../src/user/common.ts","../src/user/index.ts","../src/user/fastify.ts","../src/check-permission.ts","../src/errors.ts","../src/authorization.ts","../src/permissions/SDK/permissionCache.ts","../src/permissions/SDK/consts.ts","../src/permissions/SDK/evaluatePermissions.ts","../src/permissions/middleware/requirePermissions.ts","../src/permissions/index.ts","../src/index.ts"],"sourcesContent":["import jwt from 'jsonwebtoken';\nimport moment from 'moment';\n\nconst {\n DEPRECATED_JWT_SECRET, JWT_NEW_SECRET,\n DEPRECATED_REFRESH_JWT_SECRET, REFRESH_JWT_SECRET,\n DEPRECATION_UNIX_TIMESTAMP,\n} = process.env;\n\nconst getRelevantSecret = (token: string | undefined, deprecatedSecret: string, newSecret: string): string => {\n const deprecationTime = moment(parseInt(DEPRECATION_UNIX_TIMESTAMP || '', 10) * 1000);\n try {\n let unixTime: moment.Moment;\n if (token) {\n const { iat } = jwt.decode(token) as jwt.JwtPayload;\n unixTime = moment(iat! * 1000);\n } else {\n unixTime = moment();\n }\n return unixTime.isBefore(deprecationTime) ? deprecatedSecret : newSecret;\n } catch {\n return newSecret;\n }\n};\n\nexport const getRefreshTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_REFRESH_JWT_SECRET!, REFRESH_JWT_SECRET!);\nexport const getTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_JWT_SECRET!, JWT_NEW_SECRET!);\n","import type { UUID } from 'node:crypto';\nimport jwt from 'jsonwebtoken';\nimport { getTokenSecret } from './secret-getter';\n\ntype Context = Partial<Record<ContextProp | 'id', string>> & { subSystem?: SubSystemType; permissions?: string[]; entityId: string; };\n\nconst CONTEXT_PROPS = ['fleetId', 'businessModelId', 'demandSourceId'] as const;\ntype ContextProp = typeof CONTEXT_PROPS[number];\nconst CONTEXT_MAP_PROPS = {\n fleet: 'fleets',\n business: 'businessModels',\n demand: 'demandSources',\n} as const;\ntype SubSystemType = keyof typeof CONTEXT_MAP_PROPS;\ntype ContextSubSystemProp = typeof CONTEXT_MAP_PROPS[SubSystemType];\n\nexport const getAuthFromBearer = (bearer: string): string => bearer.replace('Bearer ', '');\n\nexport const decodeBearer = (bearer: string, appSecret?: string): any => {\n const token = getAuthFromBearer(bearer);\n const decoded = jwt.verify(token, appSecret || getTokenSecret(token));\n return decoded;\n};\n\nexport const parsePermissions = (contextId: string, decodedToken: { contexts: Context[]; }): { key: string; value: string; } | undefined => {\n if (!decodedToken) return undefined;\n const { contexts } = decodedToken;\n const activeContext = contexts.find(context => context.id === contextId);\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const permissionsValue = `${activeContext!.permissions?.map(cp => `${cp},`)}`;\n\n return {\n key: activeContext!.entityId,\n value: permissionsValue,\n };\n};\n\ntype EntitiesFromContext = Partial<Record<ContextSubSystemProp, Record<string, string>>>;\nexport const getEntitiesFromContext = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): EntitiesFromContext => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n\n const attributes: EntitiesFromContext = {};\n contexts.forEach((context) => {\n const prop = CONTEXT_MAP_PROPS[context.subSystem || 'business'];\n\n const permissions = parsePermissions(context.id!, decodedToken);\n if (!permissions) return;\n attributes[prop] ??= {};\n attributes[prop][permissions.key] = permissions.value;\n });\n\n return attributes;\n};\n\ntype ContextAttributes = Partial<Record<ContextProp, string[]>>;\nexport const getContextAttributes = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): ContextAttributes => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n const attributes: ContextAttributes = {};\n contexts.forEach((context) => {\n CONTEXT_PROPS.forEach((prop) => {\n if (context[prop]) {\n attributes[prop] ??= [];\n attributes[prop].push(context[prop]);\n }\n });\n });\n return attributes;\n};\n\nconst EMPTY_UUID = '00000000-0000-0000-0000-000000000000';\nconst FULL_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff';\nconst VALID_CHARS_REGEX = '[0-9a-f]';\nconst UUID_VERSION_REGEX = '[1-8]';\nconst UUID_REGEX = new RegExp(\n `^(?:${VALID_CHARS_REGEX}{8}-${VALID_CHARS_REGEX}{4}-${UUID_VERSION_REGEX}${VALID_CHARS_REGEX}{3}-[89ab]${VALID_CHARS_REGEX}{3}-${VALID_CHARS_REGEX}{12}|${EMPTY_UUID}|${FULL_UUID})$`,\n 'i',\n);\nexport function validateUUID(uuid: unknown): uuid is UUID {\n return typeof uuid === 'string' && UUID_REGEX.test(uuid);\n}\n","import Network from '@autofleet/network';\n\nconst CACHE_LIFETIME_IN_SEC = 10;\nconst apiGwUrl = process.env.API_GATEWAY_URL || 'https://api.autofleet.io';\n\nexport const IdentityNetwork: Network = new Network({\n serviceName: 'IDENTITY_MS',\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n\nexport const AutofleetApiNetwork: Network = new Network({\n baseURL: apiGwUrl,\n serviceUrl: apiGwUrl,\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n","import NodeCache from 'node-cache';\nimport objectHash from 'object-hash';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport { validateUUID } from '../utils';\nimport { AutofleetApiNetwork, IdentityNetwork } from '../services';\n\nexport type AccountType = 'client' | 'user' | 'service' | 'driver';\ntype EntityPermissions = Record<string, string[]>;\n\nexport const ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';\nexport const CONTEXTS_IDS_HEADER = 'x-af-context-ids';\nexport const MAX_CONTEXT_IDS: number = Number.parseInt(process.env.MAX_CONTEXT_IDS ?? `0`, 10) || 20;\n\nexport interface UserExternalData {\n element?: {\n internalUser: boolean;\n };\n [key: string]: any;\n}\n\nexport interface UserPayload {\n businessModels: EntityPermissions;\n fleets: EntityPermissions;\n demandSources: EntityPermissions;\n businessAccounts?: EntityPermissions;\n accountType?: AccountType;\n contexts?: EntityPermissions;\n createdAt?: string;\n externalData?: UserExternalData | null;\n}\n\nexport interface PartialUserPayload {\n businessModels?: EntityPermissions;\n fleets?: EntityPermissions;\n demandSources?: EntityPermissions;\n vehicles?: EntityPermissions;\n drivers?: EntityPermissions;\n businessAccounts?: EntityPermissions;\n}\n\nconst userCache = new NodeCache({ stdTTL: 10 });\n\nconst mergePermissions = (target: UserPayload | undefined, sources: Iterable<PartialUserPayload | undefined>): UserPayload => {\n const permissions: UserPayload = {\n ...target,\n fleets: { ...target?.fleets },\n businessModels: { ...target?.businessModels },\n demandSources: { ...target?.demandSources },\n // Clone other nested objects as needed\n };\n\n for (const source of sources) {\n (Object.entries(source ?? {}) as [Exclude<keyof UserPayload, 'accountType' | 'createdAt'>, EntityPermissions][]).forEach(([entityType, entityValue]) => {\n permissions[entityType] ??= {};\n Object.entries(entityValue).forEach(([entityId, perms]) => {\n permissions[entityType]![entityId] = (permissions[entityType]![entityId] || []).concat(perms);\n });\n });\n }\n\n return permissions;\n};\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\nif (typeof Symbol.asyncDispose !== 'symbol') {\n // Polyfill for asyncDispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L174-L183\n Object.defineProperty(Symbol, 'asyncDispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.asyncDispose'),\n writable: false,\n });\n}\n\nexport default class ApiUser {\n private privatePermissions: UserPayload | undefined;\n\n private readonly privateElevatedPermissionsHash = new Map<symbol, PartialUserPayload | undefined>();\n\n private privatePermissionsLegacy: any;\n\n private readonly appPermission: Record<string, any> = {};\n\n public readonly emptyUser: boolean;\n\n constructor(public id?: string, public accountType?: AccountType | undefined, elevatedPermissions?: PartialUserPayload, public contextIds?: string[]) {\n this.emptyUser = !!id;\n if (elevatedPermissions) {\n this.privateElevatedPermissionsHash.set(Symbol('initial'), elevatedPermissions);\n }\n }\n\n public async getUserPermissions(): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n });\n\n let data = userCache.get<UserPayload>(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get<UserPayload>(`/api/v1/users/${this.id}/authorization-payload`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.accountType = data.accountType;\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public async useCustomPermissionLoader(customPermissionLoader: (userId: string) => UserPayload | PromiseLike<UserPayload>): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n\n const cacheKey = this.id;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.privatePermissions = cachedResult;\n return cachedResult;\n }\n\n const data = await customPermissionLoader(this.id);\n userCache.set(cacheKey, data);\n\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public get businessModels(): string[] {\n return this.getUserProperty('businessModels');\n }\n\n public get fleets(): string[] {\n return this.getUserProperty('fleets');\n }\n\n public get demandSources(): string[] {\n return this.getUserProperty('demandSources');\n }\n\n private getUserProperty(key: keyof UserPayload): string[] {\n if (!this.privatePermissions) {\n throw new Error(`Cannot get ${key} without calling (async) getUserPermissions before`);\n }\n return Object.keys(this.privatePermissions[key] || {});\n }\n\n public get elevatedPermissions(): UserPayload {\n return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());\n }\n\n public get permissions(): UserPayload | undefined {\n if (!this.privatePermissions) {\n throw new Error('Cannot get permissions without calling (async) getUserPermissions before');\n }\n\n return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());\n }\n\n public elevatePermissions(addedPermissions: PartialUserPayload): (() => void) & { [Symbol.dispose]: () => void; } {\n // @itayankri is concerned about memory consumption, so create a symbol with no description, to avoid assigning memory for the description string\n const elevationId = Symbol();\n\n // Validate that the added permissions are valid UUIDs\n Object.values(addedPermissions).forEach((entityIds) => {\n Object.keys(entityIds).forEach((entityId) => {\n if (!validateUUID(entityId)) {\n throw new Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${entityId}`);\n }\n });\n });\n\n const currentUserTrace = getCurrentContext();\n if (!currentUserTrace) {\n throw new Error('Cannot find current user cross services trace');\n }\n\n const currentElevation = JSON.parse(currentUserTrace.context?.get(ELEVATED_PERMISSIONS_HEADER) as string | undefined || '{}');\n const newElevation = Object.assign(currentElevation, addedPermissions);\n this.privateElevatedPermissionsHash.set(elevationId, newElevation);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n const cleanup = () => {\n this.privateElevatedPermissionsHash.delete(elevationId);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n };\n cleanup[Symbol.dispose] = cleanup;\n return cleanup;\n }\n\n public async getUserPermissionsLegacy(): Promise<unknown> {\n if (!this.id) {\n return undefined;\n }\n if (this.privatePermissionsLegacy) {\n return this.privatePermissionsLegacy;\n }\n\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n legacy: true,\n });\n let data = userCache.get(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get(`/api/v1/users/${this.id}/authorization-payload-legacy`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.privatePermissionsLegacy = data;\n return this.privatePermissionsLegacy;\n }\n\n public get permissionsLegacy(): any {\n if (!this.privatePermissionsLegacy) {\n throw new Error('Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before');\n }\n return this.privatePermissionsLegacy;\n }\n\n public async getUserAppPermissions(appId: string, clientSecret: string): Promise<UserPayload | undefined> {\n if (!this.id || !appId || !clientSecret) {\n return undefined;\n }\n const currentAppPermission = this.appPermission[appId];\n\n if (currentAppPermission) {\n return currentAppPermission;\n }\n\n const cacheKey = `${this.id}:${appId}`;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.appPermission[appId] = cachedResult;\n return cachedResult;\n }\n\n const { data } = await AutofleetApiNetwork.post<UserPayload>(`/api/v1/apps/${appId}/get-user-payload`, {\n userId: this.id,\n }, {\n headers: {\n 'x-autofleet-apps-secret': clientSecret,\n },\n });\n\n userCache.set(cacheKey, data);\n this.appPermission[appId] = data;\n return this.appPermission[appId];\n }\n\n public extendRequiredContexts(contextIdsToAdd?: string[]): void {\n if (!Array.isArray(contextIdsToAdd)) {\n return;\n }\n\n const newContextIds = Array.from(new Set([...(this.contextIds ?? []), ...contextIdsToAdd]));\n\n if (newContextIds.length > MAX_CONTEXT_IDS) {\n return;\n }\n\n this.contextIds = newContextIds;\n const currentUserTrace = getCurrentContext();\n currentUserTrace?.context?.set(CONTEXTS_IDS_HEADER, this.contextIds.join(','));\n }\n}\n","import { AutofleetApiNetwork } from './services';\n\nexport const decodeAppBearer = async (bearer: string, appId: string): Promise<any> => {\n const { data: decoded } = await AutofleetApiNetwork.post('/api/v1/auth', { bearer, appId });\n return decoded;\n};\n\nexport const getClientSecret = async (appId: string): Promise<any> => {\n const { data: secret } = await AutofleetApiNetwork.get(`/api/v1/auth/client-secret/${appId}`);\n return secret;\n};\n","export default class AppDoesNotExist extends Error {\n name = 'AppDoesNotExist';\n\n message = 'app does not exist';\n}\n","export const IDENTITY_MS = 'identity-ms';\nexport const ACCESS_TOKEN = 'accessToken';\nexport const USER_OBJECT = 'userObject';\nexport const USER_TRACING_HEADER = 'x-af-user-id';\nexport const ORIGIN_HEADER = 'X-IAF-ORIGIN-SERVICE';\nexport const USER_PERMISSIONS_HEADER = 'x-af-user-permissions';\nexport const LOWER_CASE_ORIGIN_HEADER = ORIGIN_HEADER.toLowerCase() as Lowercase<typeof ORIGIN_HEADER>;\nexport const AUTOFLEET_APPS_SECRET_HEADER = 'x-autofleet-apps-secret';\n","import type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport ApiUser, { CONTEXTS_IDS_HEADER, ELEVATED_PERMISSIONS_HEADER, type UserPayload } from './ApiUser';\nimport {\n IDENTITY_MS,\n USER_OBJECT,\n USER_TRACING_HEADER,\n ORIGIN_HEADER,\n LOWER_CASE_ORIGIN_HEADER,\n} from './const';\n\nexport type CustomPermissionLoader = (userId: string) => Promise<UserPayload>;\nexport interface AuthFromUserIdHeaderOptions {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n customPermissionLoader?: CustomPermissionLoader;\n}\n\nexport const authFromUserIdHeader = async (options: AuthFromUserIdHeaderOptions, headers: IncomingHttpHeaders): Promise<ApiUser | undefined> => {\n const originHeader = headers[ORIGIN_HEADER] || headers[LOWER_CASE_ORIGIN_HEADER] || '';\n if (!Array.isArray(originHeader) && originHeader.toLowerCase() === IDENTITY_MS) {\n return undefined;\n }\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n customPermissionLoader,\n } = options;\n const userId = headers[USER_TRACING_HEADER] as string;\n if (!userId || Array.isArray(userId)) {\n return undefined;\n }\n\n const elevatedPermHeaderValue = headers[ELEVATED_PERMISSIONS_HEADER];\n const elevatedPermissionsFromHeader = elevatedPermHeaderValue?.length && elevatedPermHeaderValue.length > 0 ? JSON.parse(elevatedPermHeaderValue as string) : {};\n const contextIds = (headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n\n const userObject = new ApiUser(userId, 'user', elevatedPermissionsFromHeader, contextIds);\n if (eagerLoadUserPermissions) {\n if (customPermissionLoader) {\n await userObject.useCustomPermissionLoader(customPermissionLoader);\n } else {\n await userObject.getUserPermissions();\n }\n }\n\n if (eagerLoadUserPermissionsLegacy) {\n await userObject.getUserPermissionsLegacy();\n }\n\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n return userObject;\n};\n","import type { Handler, Request } from 'express';\nimport { getCurrentContext, newTrace, traceTypes } from '@autofleet/outbreak';\nimport jwt from 'jsonwebtoken';\nimport ApiUser, { CONTEXTS_IDS_HEADER, MAX_CONTEXT_IDS } from './ApiUser';\nimport { decodeAppBearer } from '../app-auth';\nimport AppDoesNotExist from '../exceptions/appDoesNotExist';\nimport { decodeBearer, getAuthFromBearer } from '../utils';\nimport {\n ACCESS_TOKEN,\n USER_OBJECT,\n USER_TRACING_HEADER,\n USER_PERMISSIONS_HEADER,\n AUTOFLEET_APPS_SECRET_HEADER,\n} from './const';\nimport { authFromUserIdHeader, type AuthFromUserIdHeaderOptions } from './common';\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n user: ApiUser;\n }\n}\n\ntype Asyncify<T extends (...a: any[]) => any> = (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;\n\nexport const middleware = (options: AuthFromUserIdHeaderOptions = {}): Asyncify<Handler> => async (req, res, next): Promise<any> => {\n try {\n const userObject = await authFromUserIdHeader(options, req.headers);\n if (userObject) {\n req.user = userObject;\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n }\n\n next();\n } catch {\n res.status(401).json({ error: 'cannot authenticate user' });\n }\n};\n\nexport const middlewareWithDecode = (options: {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n returnErrorIfNoToken?: boolean;\n appSecret?: string;\n} = {}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n returnErrorIfNoToken,\n appSecret,\n } = options;\n let decoded;\n if (req.headers.authorization) {\n try {\n decoded = await decodeBearer(req.headers.authorization, appSecret);\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n } else if (e instanceof jwt.JsonWebTokenError) {\n res.status(400).json({ errors: [e.message] });\n } else {\n res.status(500).json({ errors: ['Server error while parsing token'] });\n }\n return;\n }\n const userId = decoded?.user?.id;\n\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const contextIds = (req.headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n const userObject = new ApiUser(userId, decoded?.user?.accountType, undefined, contextIds);\n\n if (eagerLoadUserPermissions || eagerLoadUserPermissionsLegacy) {\n await Promise.all([\n eagerLoadUserPermissions && userObject.getUserPermissions(),\n eagerLoadUserPermissionsLegacy && userObject.getUserPermissionsLegacy(),\n ]);\n }\n\n req.user = userObject;\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n } else if (returnErrorIfNoToken) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n next();\n};\n\nexport const appMiddleware = (options: {\n appId: string;\n clientSecret: string;\n}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n appId,\n clientSecret,\n } = options;\n let decoded;\n\n if (!req.headers.authorization) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n\n try {\n decoded = await decodeAppBearer(req.headers.authorization, appId);\n if (!decoded) {\n throw new AppDoesNotExist();\n }\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n return;\n }\n if ([jwt.JsonWebTokenError, AppDoesNotExist].some(Err => e instanceof Err)) {\n res.status(400).json({ errors: [(e as Error).message] });\n return;\n }\n res.status(500).json({ errors: ['Server error while parsing token'] });\n return;\n }\n const userId = decoded?.userId;\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const userObject = new ApiUser(userId);\n\n if (appId) {\n req.headers[AUTOFLEET_APPS_SECRET_HEADER] = clientSecret;\n // Won't work until we find a better solution for identity ms\n await userObject.getUserAppPermissions(appId, clientSecret);\n }\n\n req.user = userObject;\n const currentTraceContext = getCurrentContext().nonHeaderContext;\n currentTraceContext?.set(USER_OBJECT, userObject);\n currentTraceContext?.set(ACCESS_TOKEN, getAuthFromBearer(req.headers.authorization));\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n\n next();\n};\n\nexport const eagerLoadPermissionsMiddleware: Asyncify<Handler> = async (req, res, next) => {\n await req.user.getUserPermissions();\n next();\n};\n\nexport const getDecodedBearer = (req: Request, appSecret?: string): any => {\n if (!req.headers.authorization) {\n return null;\n }\n return decodeBearer(req.headers.authorization, appSecret);\n};\n\nexport const createOrSetRabbitTrace = async (trace: ReturnType<typeof newTrace> | undefined, userId: string | undefined, contextIds?: string[]): Promise<void> => {\n const addContextIds = Array.isArray(contextIds) && contextIds.length <= MAX_CONTEXT_IDS;\n const userObject = new ApiUser(\n userId,\n undefined,\n undefined,\n addContextIds ? contextIds : undefined,\n );\n\n await userObject.getUserPermissions();\n trace ??= newTrace(traceTypes.RABBIT);\n trace.nonHeaderContext.set(USER_OBJECT, userObject);\n\n if (addContextIds) {\n trace.context.set(CONTEXTS_IDS_HEADER, contextIds?.join(',') || '');\n }\n};\n\nexport default ApiUser;\n","import type { FastifyPluginCallback } from 'fastify';\nimport type ApiUser from './ApiUser';\nimport { type AuthFromUserIdHeaderOptions, authFromUserIdHeader } from './common';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n user?: ApiUser;\n }\n}\n\nexport const authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions> = (fastify, options, done) => {\n fastify.decorateRequest('user', undefined);\n fastify.addHook('onRequest', async (request, reply) => {\n try {\n const user = await authFromUserIdHeader(options, request.headers);\n if (user) {\n request.user = user;\n }\n } catch {\n reply.status(401).send({ error: 'cannot authenticate user' });\n }\n });\n\n done();\n};\nObject.defineProperty(authFromUserIdHeaderPlugin, Symbol.for('skip-override'), { value: true }); // Prevent Fastify from overriding the plugin\n","import { getCurrentContext } from '@autofleet/outbreak';\nimport { USER_OBJECT } from './user/const';\nimport type ApiUser from './user/ApiUser';\nimport type { UserPayload } from './user/ApiUser';\n\nexport const getUser = (): ApiUser | undefined => getCurrentContext().nonHeaderContext?.get(USER_OBJECT) as ApiUser | undefined;\n\nexport const isUserExist = (): string | undefined => getUser()?.id;\n\nconst checkUserPermissions = (\n entityId: string,\n entityType: Exclude<keyof UserPayload, 'accountType' | 'createdAt'>,\n) => !isUserExist() || Object.hasOwn(getUser()!.permissions?.[entityType] ?? {}, entityId);\n\nexport const checkFleetPermission = (fleetId: string): boolean => checkUserPermissions(fleetId, 'fleets');\nexport const checkBusinessModelPermission = (businessModelId: string): boolean => checkUserPermissions(businessModelId, 'businessModels');\nexport const checkDemandSourcePermission = (demandSourceId: string): boolean => checkUserPermissions(demandSourceId, 'demandSources');\n","import type ApiUser from './user';\n\nexport class UnauthorizedAccessError extends Error {\n constructor(public user: ApiUser | null = null, message = 'UnauthorizedAccessError') {\n super(message);\n this.name = 'UnauthorizedAccessError';\n }\n}\n","import jwt from 'jsonwebtoken';\n\nexport const AUTHORIZATION_METHODS = {\n NONE: 'NONE',\n BASIC: 'BASIC',\n JWT: 'JWT',\n};\n\nconst AUTHORIZATION_ACTIONS = {\n [AUTHORIZATION_METHODS.NONE]: () => undefined,\n [AUTHORIZATION_METHODS.BASIC]: (authorizationSettings: any) => {\n const { username, password } = authorizationSettings;\n const encodedCredentials = Buffer.from(`${username}:${password}`).toString('base64');\n return `Basic ${encodedCredentials}`;\n },\n [AUTHORIZATION_METHODS.JWT]: (authorizationSettings: any) => {\n const { secret } = authorizationSettings;\n if (secret) {\n return `Bearer ${jwt.sign({}, secret, { expiresIn: 10 })}`;\n }\n return undefined;\n },\n};\n\nexport const getAuthorizationHeader = (authorizationSettings: { method: string; } | undefined): string | undefined => {\n const authorizationMethod = authorizationSettings?.method;\n\n if (!authorizationMethod || !AUTHORIZATION_ACTIONS[authorizationMethod]) {\n return undefined;\n }\n\n return AUTHORIZATION_ACTIONS[authorizationMethod](authorizationSettings);\n};\n","import NodeCache from 'node-cache';\n\ntype EntityPermissions = Record<string, string[]>;\n\nexport class PermissionCache {\n private cache: NodeCache;\n\n private ttl = 10;\n\n /**\n * Creates a new PermissionCache instance\n * @param cache - Optional NodeCache instance. If not provided, creates a new one with default TTL of 10 seconds\n */\n constructor(cache?: NodeCache, ttl?: number) {\n if (ttl) {\n this.ttl = ttl;\n }\n this.cache = cache ?? new NodeCache({ stdTTL: this.ttl });\n }\n\n private static getCacheKeys(cacheName: string, userId: string, contextIds: string[]) {\n const baseCacheKey = `${cacheName}-${userId}`;\n const cacheKeys = contextIds.map(contextId => `${baseCacheKey}-${contextId}`);\n\n return { baseCacheKey, cacheKeys };\n }\n\n /**\n * Retrieves user permissions for multiple contexts from cache\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to fetch permissions for\n * @returns An object mapping context IDs to permission arrays\n */\n public getUserPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('perm', userId, contextIds);\n const cacheResult = this.cache.mget(cacheKeys) as Record<string, string[]>;\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Stores user permissions for multiple contexts in cache\n * @param userId - The user identifier\n * @param PermissionsByContextId - Object mapping context IDs to permission arrays\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setUserPermissions(userId: string, PermissionsByContextId: EntityPermissions): { success: boolean; } {\n try {\n const cacheKey = (contextId: string) => `perm-${userId}-${contextId}`;\n\n const cacheEntries = Object.entries(PermissionsByContextId).map(([contextId, permissions]) => ({\n key: cacheKey(contextId),\n val: permissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n /**\n * Retrieves denied permissions status for multiple contexts from cache.\n * \"Denied\" permissions indicate that a user has already been evaluated for a specific set of\n * required permissions in a given context, avoiding redundant permission checks.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to check\n * @returns An object mapping context IDs to arrays of denied permissions\n */\n public getCachedDeniedPermissions(userId: string, contextIds: string[]): Record<string, string[]> {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('seen', userId, contextIds);\n const cacheResult = this.cache.mget(cacheKeys) as Record<string, string[]>;\n\n const result: Record<string, string[]> = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Marks permissions as seen for multiple contexts in cache.\n * \"Seen\" permissions are used to track that a user has already been evaluated for a specific\n * set of required permissions in given contexts, preventing duplicate permission evaluations.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to mark as seen\n * @param deniedPermissions - Array of permissions that were evaluated and denied\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setCachedDeniedPermissions(userId: string, contextIds: string[], deniedPermissions: string[]): { success: boolean; } {\n try {\n const seenKey = (contextId: string) => `seen-${userId}-${contextId}`;\n\n const cacheEntries = contextIds.map(contextId => ({\n key: seenKey(contextId),\n val: deniedPermissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n}\n\nexport const permissionCache: PermissionCache = new PermissionCache();\n","export const PERMISSIONS_EVALUATION_OPERATORS = {\n AND: 'and',\n OR: 'or',\n};\n\nexport const PERMISSION_ERROR_TYPES = {\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n API_ERROR: 'API_ERROR',\n NO_REQUIRED_PERMISSIONS: 'NO_REQUIRED_PERMISSIONS',\n UNAUTHORIZED: 'UNAUTHORIZED',\n BAD_REQUEST: 'BAD_REQUEST',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n};\n\nexport const PERMISSION_ERROR_MESSAGES = {\n USER_NOT_FOUND: 'User not found',\n INSUFFICIENT_PERMISSIONS: 'User does not have sufficient permissions',\n VALIDATION_ERROR: 'Validation error occurred',\n API_ERROR: 'API error occurred',\n NO_REQUIRED_PERMISSIONS: 'No required permissions provided for evaluation',\n UNAUTHORIZED: 'User is not authorized to perform this action',\n BAD_REQUEST: 'Bad request, please check the input parameters',\n INTERNAL_ERROR: 'Internal server error occurred while checking permissions',\n};\n","import { getUser } from '../../check-permission';\nimport { IdentityNetwork } from '../../services';\nimport type {\n PermissionsEvaluationOperator,\n EvaluatePermissionsParams,\n PermissionsEvaluationByContextId,\n PermissionCheckResult,\n ResolvePermissionsParams,\n Logger,\n PermissionsByContextId,\n} from './types';\nimport type { PermissionErrorType } from './errors';\nimport { permissionCache } from './permissionCache';\nimport { PERMISSIONS_EVALUATION_OPERATORS, PERMISSION_ERROR_TYPES } from './consts';\n\n/**\n * Evaluates whether user permissions meet requirements based on AND/OR operator\n * @param permissions - User's actual permissions\n * @param requiredPermissions - Required permissions to check against\n * @param operator - {@link PermissionsEvaluationOperator} AND (all required) or OR (at least one required)\n * @returns true if permissions meet requirements\n */\nconst evaluatePermissionsByOperator = (\n permissions: string[],\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): boolean => {\n const permissionsSet = new Set(permissions);\n const matchingPermissions = requiredPermissions.filter(permission => permissionsSet.has(permission));\n\n return operator === PERMISSIONS_EVALUATION_OPERATORS.AND\n ? matchingPermissions.length === requiredPermissions.length // All required permissions must be present\n : matchingPermissions.length > 0; // At least one required permission must be present\n};\n\n/**\n * Creates empty permissions result for cases with no required permissions\n * @param contextIds - Context IDs to create results for\n * @returns {@link PermissionsEvaluationByContextId} with empty permissions and granted access\n */\nconst createEmptyPermissionsResult = (contextIds: string[]): PermissionsEvaluationByContextId => Object.fromEntries(\n contextIds.map(contextId => [\n contextId,\n { permissions: [], hasRequiredPermissions: true },\n ]),\n);\n\n/**\n * Checks if permission requirements are met across all contexts\n * @param permissionsEvaluationByContextId - {@link PermissionsEvaluationByContextId} Permission evaluation results by context\n * @param requireAll - Whether all contexts must satisfy requirements (true) or just one (false)\n * @returns true if requirements are met\n */\nexport const checkAuthorizeRequirements = (permissionsEvaluationByContextId: PermissionsEvaluationByContextId, requireAll: boolean): boolean => {\n const permissionsEvaluationValues = Object.values(permissionsEvaluationByContextId);\n\n if (requireAll) {\n return permissionsEvaluationValues.every(evaluation => evaluation.hasRequiredPermissions);\n }\n return permissionsEvaluationValues.some(evaluation => evaluation.hasRequiredPermissions);\n};\n\nexport const formatResultsWithOrOperator = (\n contextsPermissionsEvaluations: PermissionsEvaluationByContextId,\n requiredPermissions: string[],\n): PermissionsEvaluationByContextId => {\n const permissionsEvaluationByContextId: PermissionsEvaluationByContextId = {};\n Object.entries(contextsPermissionsEvaluations).forEach(([contextId, evaluation]) => {\n const permissions = evaluation?.permissions || [];\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n PERMISSIONS_EVALUATION_OPERATORS.OR,\n );\n\n permissionsEvaluationByContextId[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n });\n\n return permissionsEvaluationByContextId;\n};\n\n/**\n * Resolves permissions from Identity Service API\n * @param params - {@link ResolvePermissionsParams}\n * @returns {@link PermissionsEvaluationByContextId} Permission evaluation results by context ID\n */\nexport const resolvePermissions = async ({\n user,\n contextIds,\n requiredPermissions,\n options,\n}: ResolvePermissionsParams): Promise<PermissionsEvaluationByContextId> => {\n if (!requiredPermissions?.length) {\n return createEmptyPermissionsResult(contextIds);\n }\n\n const response = await IdentityNetwork.post('/api/v1/permissions/resolve', {\n userId: user.id,\n contextIds,\n requiredPermissions,\n }, {\n timeout: options.timeout,\n });\n\n const data: PermissionsEvaluationByContextId = response?.data || {};\n\n if (!Object.keys(data || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n if (options.permissionsEvaluationOperator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n return data;\n }\n\n // Re-evaluate with OR operator\n return formatResultsWithOrOperator(data, requiredPermissions);\n};\n\nconst validateInput = (contextIds: string[]): string[] => {\n const errors: string[] = [];\n\n if (!contextIds?.length) {\n errors.push('contextIds cannot be empty');\n }\n\n return errors;\n};\n\nconst createErrorResult = (\n error: PermissionErrorType,\n requiredPermissions: string[],\n contextIds: string[],\n message?: string,\n): PermissionCheckResult => ({\n isAuthorized: false,\n error,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n ...(message && { message }),\n});\n\n/**\n * Caches permission results based on whether they meet requirements\n * - Caches granted permissions directly\n * - Groups denied permissions by identical sets to optimize cache storage\n * @param userId - User ID\n * @param resolvedPermissions - Permissions evaluation results to cache\n * @param requiredPermissions - Required permissions used for evaluation\n */\nconst cachePermissionsResults = (\n userId: string,\n resolvedPermissions: PermissionsEvaluationByContextId,\n requiredPermissions: string[],\n logger?: Logger,\n): void => {\n const permissionsToCache: PermissionsByContextId = {};\n const deniedPermissionsMap = new Map<string, string[]>();\n\n logger?.debug('Start caching permissions results', {\n userId,\n resolvedPermissions,\n requiredPermissions,\n });\n\n Object.entries(resolvedPermissions).forEach(([contextId, evaluation]) => {\n if (evaluation?.permissions) {\n const userPermissionsSet = new Set(evaluation.permissions);\n const effectivePermissions = requiredPermissions.filter(permission => userPermissionsSet.has(permission));\n const deniedPermissions = requiredPermissions.filter(permission => !userPermissionsSet.has(permission));\n\n if (effectivePermissions.length) {\n // Store only effective permissions (intersection of user and required permissions)\n permissionsToCache[contextId] = effectivePermissions;\n }\n\n if (deniedPermissions.length) {\n // Calculate which required permissions were denied by filtering out the ones the user has\n const deniedPermissionsKey = deniedPermissions.sort().join(',');\n\n if (!deniedPermissionsMap.has(deniedPermissionsKey)) {\n deniedPermissionsMap.set(deniedPermissionsKey, []);\n }\n deniedPermissionsMap.get(deniedPermissionsKey)!.push(contextId);\n }\n }\n });\n\n if (Object.keys(permissionsToCache).length > 0) {\n const pastCache = permissionCache.getUserPermissions(userId, Object.keys(permissionsToCache));\n\n Object.keys(pastCache ?? {}).forEach((contextId) => {\n if (permissionsToCache[contextId]) {\n // Merge with any previously cached permissions to avoid overwriting\n const mergedPermissions = Array.from(new Set([...(pastCache[contextId] || []), ...permissionsToCache[contextId]]));\n permissionsToCache[contextId] = mergedPermissions;\n }\n });\n\n logger?.debug('Caching granted permissions', { userId, permissionsToCache, pastCache });\n const { success } = permissionCache.setUserPermissions(userId, permissionsToCache);\n if (!success) {\n logger?.error('Failed to cache granted permissions', { userId, permissionsToCache });\n }\n }\n\n // Cache denied permissions grouped by the same denied permissions to utilize mset efficiently\n Array.from(deniedPermissionsMap.entries()).map(([deniedPermissionsKey, contextIds]) => {\n const deniedPermissions = deniedPermissionsKey.split(',');\n return permissionCache.setCachedDeniedPermissions(userId, contextIds, deniedPermissions);\n });\n\n logger?.debug('Caching denied permissions', {\n userId,\n deniedPermissionsGroups: Array.from(deniedPermissionsMap.entries()).map(([key, contexts]) => ({\n deniedPermissions: key.split(','),\n contextIds: contexts,\n })),\n });\n};\n\n/**\n * Determines if a context can be resolved from denied permissions cache\n * @param deniedPermissions - Previously denied permissions for the context\n * @param requiredPermissions - Currently required permissions\n * @param operator - Evaluation operator (AND/OR)\n * @returns Object indicating if context is resolvable from cache and the result\n */\nconst isResolvableFromDeniedCache = (\n deniedPermissions: string[],\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): { isResolvableFromCache: boolean; hasRequiredPermissions: boolean; } => {\n if (!deniedPermissions.length) {\n return { isResolvableFromCache: false, hasRequiredPermissions: false };\n }\n\n const deniedSet = new Set(deniedPermissions);\n const deniedRequiredPermissions = requiredPermissions.filter(permission => deniedSet.has(permission));\n\n if (operator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n // For AND: if any required permission was denied, we can resolve as false\n return {\n isResolvableFromCache: deniedRequiredPermissions.length > 0,\n hasRequiredPermissions: false,\n };\n }\n\n // For OR: only if ALL required permissions were denied, we can resolve as false\n return {\n isResolvableFromCache: deniedRequiredPermissions.length === requiredPermissions.length,\n hasRequiredPermissions: false,\n };\n};\n\n/**\n * Gets contexts that need API calls (unseen + partially resolvable seen contexts)\n * @param unseenContextIds - Context IDs that have no cached data\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedContextEntries - Context IDs that were fully resolved from cache\n * @returns Array of context IDs that need API calls\n */\nconst getContextsNeedingAPI = (\n unseenContextIds: string[],\n deniedContextIds: string[],\n deniedContextEntries: PermissionsEvaluationByContextId,\n): string[] => {\n const contextIdsNeedingAPI = [...unseenContextIds];\n\n // Add denied contexts that couldn't be resolved from cache\n const unresolvedDeniedContextIds = deniedContextIds.filter(contextId => !deniedContextEntries[contextId]);\n contextIdsNeedingAPI.push(...unresolvedDeniedContextIds);\n\n return contextIdsNeedingAPI;\n};\n\n/**\n * Processes denied contexts that can be resolved from denied permissions cache\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedPermissions - Denied permissions cache data\n * @param requiredPermissions - Currently required permissions\n * @param operator - Evaluation operator (AND/OR)\n * @returns Object with cache-resolvable contexts and their evaluations\n */\nconst processCachedDeniedContexts = (\n deniedContextIds: string[],\n seenPermissions: Record<string, string[]>,\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): PermissionsEvaluationByContextId => {\n const result: PermissionsEvaluationByContextId = {};\n\n deniedContextIds.forEach((contextId) => {\n const deniedPermissions = seenPermissions[contextId];\n const cacheResult = isResolvableFromDeniedCache(deniedPermissions, requiredPermissions, operator);\n\n if (cacheResult.isResolvableFromCache) {\n result[contextId] = {\n permissions: [],\n hasRequiredPermissions: cacheResult.hasRequiredPermissions,\n };\n }\n });\n\n return result;\n};\n\n/**\n * Processes cached permissions into evaluation format\n * @param cachedPermissions - Raw permissions from cache\n * @param requiredPermissions - Required permissions to evaluate against\n * @param operator - Evaluation operator (AND/OR)\n * @returns Processed permissions evaluation\n */\nconst processCachedPermissions = (\n cachedPermissions: Record<string, string[]>,\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): PermissionsEvaluationByContextId => {\n const processedPermissions: PermissionsEvaluationByContextId = {};\n\n if (Object.keys(cachedPermissions || {}).length) {\n Object.entries(cachedPermissions).forEach(([contextId, permissions]) => {\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n operator,\n );\n\n // Only include contexts that can be fully resolved from cache\n // Since we only cache effective permissions we won't have all of the user's permissions to fully resolve\n if (hasRequiredPermissions) {\n processedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n }\n });\n }\n\n return processedPermissions;\n};\n\n/**\n * Calls the Identity API to resolve permissions for unseen contexts\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve\n * @param requiredPermissions - Required permissions\n * @param options - API call options\n * @returns Resolved permissions from API\n */\nconst callIdentityAPI = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: string[],\n options: { permissionsEvaluationOperator: PermissionsEvaluationOperator; timeout: number; },\n): Promise<PermissionsEvaluationByContextId> => {\n const response = await IdentityNetwork.post('/api/v1/permissions/resolve', {\n userId,\n contextIds,\n requiredPermissions,\n }, {\n timeout: options.timeout,\n });\n\n const data: PermissionsEvaluationByContextId = response?.data || {};\n\n if (!Object.keys(data || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n // If using AND operator, return data as-is. For OR, re-evaluate.\n if (options.permissionsEvaluationOperator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n return data;\n }\n\n // Re-evaluate with OR operator\n const reEvaluatedPermissions: PermissionsEvaluationByContextId = {};\n Object.entries(data).forEach(([contextId, evaluation]) => {\n const permissions = evaluation?.permissions || [];\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n\n reEvaluatedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n });\n\n return reEvaluatedPermissions;\n};\n\n/**\n * Validates user context and required permissions for evaluation\n * @param contextIds - Context IDs to validate\n * @param requiredPermissions - Required permissions to validate\n * @param userId - User ID to validate\n * @param logger - Logger instance for warnings\n * @returns Error result if validation fails, null if validation passes\n */\nconst validateEvaluationInput = (\n contextIds: string[],\n requiredPermissions: string[],\n userId: string | null,\n logger?: Logger,\n): PermissionCheckResult | null => {\n const validationErrors = validateInput(contextIds);\n if (validationErrors.length) {\n return createErrorResult(PERMISSION_ERROR_TYPES.VALIDATION_ERROR, requiredPermissions, contextIds, validationErrors.join(', '));\n }\n\n if (!userId) {\n logger?.warn('User not found in context, cannot check permissions', {\n contextIds,\n requiredPermissions,\n });\n return createErrorResult(PERMISSION_ERROR_TYPES.USER_NOT_FOUND, requiredPermissions, contextIds);\n }\n\n if (!requiredPermissions?.length) {\n logger?.info('No requiredPermissions provided', {\n userId,\n contextIds,\n });\n return {\n isAuthorized: false,\n userId,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n error: PERMISSION_ERROR_TYPES.NO_REQUIRED_PERMISSIONS,\n };\n }\n\n return null;\n};\n\n/**\n * Resolves permissions from Identity API and combines with existing resolved permissions\n * @param userId - User ID\n * @param contextIdsNeedingAPI - Context IDs that need API resolution\n * @param requiredPermissions - Required permissions to check\n * @param options - API call options {@link ResolvePermissionsParams}\n * @param resolvedPermissions - Already resolved permissions from cache and denied cache\n * @returns Combined permissions including those resolved from API\n */\nconst resolvePermissionsFromAPI = async (\n userId: string,\n contextIdsNeedingAPI: string[],\n requiredPermissions: string[],\n options: { permissionsEvaluationOperator: PermissionsEvaluationOperator; timeout: number; },\n resolvedPermissions: PermissionsEvaluationByContextId,\n): Promise<PermissionsEvaluationByContextId> => {\n let newResolvedPermissions: PermissionsEvaluationByContextId = { ...resolvedPermissions };\n // Call Identity API for unresolved contexts\n const resolvedPermissionsFromIdentityMS = await callIdentityAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n );\n\n // Combine all resolved permissions\n newResolvedPermissions = {\n ...resolvedPermissions,\n ...resolvedPermissionsFromIdentityMS,\n };\n\n cachePermissionsResults(userId, resolvedPermissionsFromIdentityMS, requiredPermissions);\n\n return newResolvedPermissions;\n};\n\n/**\n * Resolves permissions from multiple sources (cache, seen contexts, API)\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve permissions for\n * @param requiredPermissions - Required permissions to check\n * @param options - Evaluation options\n * @returns Combined permissions from all sources: cache, denied cache, and API\n */\nconst resolveAllPermissions = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: string[],\n options: {\n permissionsEvaluationOperator: PermissionsEvaluationOperator;\n timeout: number;\n logger?: Logger;\n },\n): Promise<PermissionsEvaluationByContextId> => {\n const { logger } = options;\n // Cached allowed permissions\n const cachedAllowedPermissions = permissionCache.getUserPermissions(userId, contextIds);\n const resolvedPermissionsFromCache = processCachedPermissions(\n cachedAllowedPermissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n let resolvedPermissions = { ...resolvedPermissionsFromCache };\n\n // Context ids without allowed permissions in cache\n const contextIdsWithoutAllowedCache = contextIds.filter(contextId => !resolvedPermissionsFromCache[contextId]);\n\n // Handle denied cache permissions\n const cachedDeniedPermissions = permissionCache.getCachedDeniedPermissions(userId, contextIdsWithoutAllowedCache);\n\n // Context ids that have never been seen before (not in allowed or denied cache)\n const unseenContextIds = contextIdsWithoutAllowedCache.filter(contextId => !cachedDeniedPermissions[contextId]);\n\n // Process denied permissions cache\n const deniedContextIds = contextIdsWithoutAllowedCache.filter(contextId => cachedDeniedPermissions[contextId]);\n const deniedContextEntries = processCachedDeniedContexts(\n deniedContextIds,\n cachedDeniedPermissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n\n // Combine resolved permissions with those resolved from denied cache\n resolvedPermissions = { ...resolvedPermissions, ...deniedContextEntries };\n\n // Resolve the remaining contexts using the Identity API\n const contextIdsNeedingAPI = getContextsNeedingAPI(unseenContextIds, deniedContextIds, deniedContextEntries);\n if (contextIdsNeedingAPI.length) {\n // Call Identity API for unresolved contexts\n return resolvePermissionsFromAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n resolvedPermissions,\n );\n }\n\n logger?.debug('Final resolved permissions', {\n userId,\n requiredPermissions,\n resolvedPermissions,\n });\n\n return resolvedPermissions;\n};\n\n/**\n * Main SDK function to evaluate user permissions\n * Checks both cached and API-resolved permissions to determine access\n * @param params - {@link EvaluatePermissionsParams}\n * @returns {@link PermissionCheckResult} Detailed permission check result with access status and context\n */\nexport const evaluatePermissions = async ({\n requiredPermissions,\n contextIds,\n logger,\n userId,\n options: {\n requireAll = true,\n permissionsEvaluationOperator = PERMISSIONS_EVALUATION_OPERATORS.AND,\n timeout = 10000,\n },\n}: EvaluatePermissionsParams): Promise<PermissionCheckResult> => {\n const resolvedUserId = userId || getUser()?.id || null;\n\n const validationError = validateEvaluationInput(contextIds, requiredPermissions, resolvedUserId, logger);\n if (validationError) {\n return validationError;\n }\n\n const resolvedPermissions = await resolveAllPermissions(\n resolvedUserId!,\n contextIds,\n requiredPermissions,\n { permissionsEvaluationOperator, timeout, logger },\n );\n\n const isAuthorized = checkAuthorizeRequirements(resolvedPermissions, requireAll);\n\n logger?.info('Resolved permissions', {\n userId: resolvedUserId,\n requiredPermissions,\n resolvedPermissions,\n isAuthorized,\n requireAll,\n permissionsEvaluationOperator,\n contextIds,\n });\n\n return {\n isAuthorized,\n userId: resolvedUserId!,\n resolvedPermissions,\n requiredPermissions,\n contextIds,\n ...(isAuthorized ? {} : { error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS }),\n };\n};\n\nexport default evaluatePermissions;\n","import type { Request, Response, NextFunction } from 'express';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { evaluatePermissions } from '../SDK/evaluatePermissions';\nimport type { EvaluatePermissionsOpts } from '../SDK/types';\nimport ApiUser from '../../user/ApiUser';\nimport { PERMISSION_ERROR_MESSAGES, PERMISSION_ERROR_TYPES, PERMISSIONS_EVALUATION_OPERATORS } from '../SDK/consts';\n\nexport interface RequirePermissionsOptions extends EvaluatePermissionsOpts {\n logger?: LoggerInstanceManager;\n}\nconst getContextIds = (user: ApiUser): string[] => {\n const contexts = Object.keys(user.permissions?.contexts ?? {});\n return Array.from(new Set([\n ...contexts,\n ])).filter(Boolean);\n};\n\nconst handleError = (\n errorLabel: string,\n message: string,\n code: number,\n enforce: boolean | undefined,\n res: Response,\n req: Request,\n next: NextFunction,\n logger?: LoggerInstanceManager,\n additionalInfo?: { requiredPermissions?: string[]; },\n) => {\n if (enforce) {\n logger?.error(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n\n res.status(code).json({\n error: errorLabel,\n message,\n ...(additionalInfo?.requiredPermissions && { requiredPermissions: additionalInfo.requiredPermissions }),\n });\n return;\n }\n\n logger?.warn(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n next();\n};\n\n/**\n * Express middleware that requires specific permissions for route access\n *\n * @param requiredPermissions - Array of permissions required to access the route\n * @param options - Configuration options for permission checking\n * @returns Express middleware function\n */\nexport const requirePermissions = (\n requiredPermissions: string[],\n options: RequirePermissionsOptions = {\n enforce: false,\n requireAll: true,\n permissionsEvaluationOperator: PERMISSIONS_EVALUATION_OPERATORS.AND,\n },\n) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n const {\n logger,\n enforce,\n requireAll,\n permissionsEvaluationOperator,\n } = options;\n\n const handleErrorWrapper = (errorLabel: string, message: string, code: number) => handleError(\n errorLabel,\n message,\n code,\n enforce,\n res,\n req,\n next,\n logger,\n { requiredPermissions },\n );\n const user = req.user;\n\n const contextIds: string[] = getContextIds(user);\n\n if (!contextIds?.length) {\n handleErrorWrapper(\n PERMISSION_ERROR_TYPES.BAD_REQUEST,\n PERMISSION_ERROR_MESSAGES.BAD_REQUEST,\n 400,\n );\n return;\n }\n\n // Evaluate permissions using SDK\n const result = await evaluatePermissions({\n requiredPermissions,\n contextIds,\n logger,\n userId: user.id,\n options: {\n requireAll: requireAll ?? true,\n permissionsEvaluationOperator: permissionsEvaluationOperator ?? PERMISSIONS_EVALUATION_OPERATORS.AND,\n timeout: 10000,\n ...(logger && { logger }),\n },\n });\n\n if (result.isAuthorized) {\n logger?.info('User has required permissions', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n if (!enforce) {\n logger?.warn('User does not have required permissions, skipping enforcement', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(403).json({\n error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS,\n message: PERMISSION_ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS,\n required: requiredPermissions,\n contexts: contextIds,\n userId: result.userId,\n });\n } catch (error) {\n const requestLogger = options.logger;\n requestLogger?.error('Error in requirePermissions middleware', {\n error,\n requiredPermissions,\n url: req.url,\n method: req.method,\n });\n\n if (!options.enforce) {\n options.logger?.error('Error during permission check, skipping enforcement', {\n error,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(500).json({\n error: PERMISSION_ERROR_TYPES.INTERNAL_ERROR,\n message: PERMISSION_ERROR_MESSAGES.INTERNAL_ERROR,\n });\n }\n};\n\nexport default requirePermissions;\n","import { evaluatePermissions } from './SDK/evaluatePermissions';\nimport { requirePermissions } from './middleware/requirePermissions';\n\ninterface SdkExports {\n evaluatePermissions: typeof evaluatePermissions;\n}\n\ninterface MiddlewareExports {\n requirePermissions: typeof requirePermissions;\n}\n\ninterface PermissionsModule {\n sdk: SdkExports;\n middlewares: MiddlewareExports;\n}\n\nexport const sdk: SdkExports = {\n evaluatePermissions,\n};\n\nexport const middlewares: MiddlewareExports = {\n requirePermissions,\n};\n\nexport default {\n sdk,\n middlewares,\n} as PermissionsModule;\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport * as outbreak from '@autofleet/outbreak';\nimport User, {\n middleware,\n eagerLoadPermissionsMiddleware,\n middlewareWithDecode,\n getDecodedBearer,\n appMiddleware,\n createOrSetRabbitTrace,\n} from './user';\nimport { authFromUserIdHeaderPlugin } from './user/fastify';\nimport { type UserPayload, CONTEXTS_IDS_HEADER } from './user/ApiUser';\nimport {\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n} from './check-permission';\nimport { UnauthorizedAccessError } from './errors';\nimport { getRefreshTokenSecret, getTokenSecret } from './secret-getter';\nimport { AUTHORIZATION_METHODS, getAuthorizationHeader } from './authorization';\nimport * as permissions from './permissions';\n\nconst getCurrentPayload: typeof outbreak.getCurrentContext = outbreak.getCurrentContext;\n\ntype OutbreakOptions = Parameters<typeof outbreak.default>[0];\ntype LoggerWithContextMiddleware = Partial<Pick<LoggerInstanceManager, 'addContextMiddleware'>>;\nconst enableTracing = ({ outbreakOptions = {}, logger }: { outbreakOptions?: OutbreakOptions; logger?: LoggerWithContextMiddleware; } = {}): void => {\n outbreak.default({\n headersPrefix: 'x-af',\n contextMiddlewareGetter: logger?.addContextMiddleware,\n ...outbreakOptions,\n });\n};\n\nconst traceTypes: typeof outbreak.traceTypes = outbreak.traceTypes;\nconst newTrace: typeof outbreak.newTrace = outbreak.newTrace;\n\nexport {\n traceTypes,\n newTrace,\n enableTracing,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n getRefreshTokenSecret,\n getTokenSecret,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n type UserPayload,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\ninterface Default {\n traceTypes: typeof outbreak.traceTypes;\n newTrace: typeof outbreak.newTrace;\n User: typeof User;\n middleware: typeof middleware;\n middlewareWithDecode: typeof middlewareWithDecode;\n eagerLoadPermissionsMiddleware: typeof eagerLoadPermissionsMiddleware;\n getCurrentPayload: typeof outbreak.getCurrentContext;\n getDecodedBearer: typeof getDecodedBearer;\n checkFleetPermission: typeof checkFleetPermission;\n checkBusinessModelPermission: typeof checkBusinessModelPermission;\n checkDemandSourcePermission: typeof checkDemandSourcePermission;\n isUserExist: typeof isUserExist;\n getUser: typeof getUser;\n UnauthorizedAccessError: typeof UnauthorizedAccessError;\n appMiddleware: typeof appMiddleware;\n createOrSetRabbitTrace: typeof createOrSetRabbitTrace;\n outbreak: typeof outbreak;\n AUTHORIZATION_METHODS: typeof AUTHORIZATION_METHODS;\n getAuthorizationHeader: typeof getAuthorizationHeader;\n CONTEXTS_IDS_HEADER: typeof CONTEXTS_IDS_HEADER;\n authFromUserIdHeaderPlugin: typeof authFromUserIdHeaderPlugin;\n permissions: typeof permissions;\n}\n\nconst zehutDefault: Default = {\n traceTypes,\n newTrace,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\nexport default zehutDefault;\n"],"mappings":"+2BAGA,KAAM,CACJ,yBAAuB,kBACvB,iCAA+B,sBAC/B,+BACE,QAAQ,IAEN,GAAqB,EAA2B,EAA0B,IAA8B,CAC5G,IAAM,GAAA,EAAA,EAAA,SAAyB,SAAS,IAA8B,GAAI,GAAG,CAAG,IAAK,CACrF,GAAI,CACF,IAAIA,EACJ,GAAI,EAAO,CACT,GAAM,CAAE,OAAQC,EAAAA,QAAI,OAAO,EAAM,CACjC,GAAA,EAAA,EAAA,SAAkB,EAAO,IAAK,MAE9B,GAAA,EAAA,EAAA,UAAmB,CAErB,OAAO,EAAS,SAAS,EAAgB,CAAG,EAAmB,OACzD,CACN,OAAO,IAIE,GAAyB,GAA2B,EAAkB,EAAO,GAAgC,GAAoB,CACjI,EAAkB,GAA2B,EAAkB,EAAO,GAAwB,GAAgB,CCV9G,EAAqB,GAA2B,EAAO,QAAQ,UAAW,GAAG,CAE7E,GAAgB,EAAgB,IAA4B,CACvE,IAAM,EAAQ,EAAkB,EAAO,CAEvC,OADgBC,EAAAA,QAAI,OAAO,EAAO,GAAa,EAAe,EAAM,CAAC,EA0DjE,GAAa,uCACb,GAAY,uCACZ,EAAoB,WAEpB,GAAiB,OACrB,OAAO,EAAkB,MAAM,EAAkB,WAA2B,EAAkB,YAAY,EAAkB,MAAM,EAAkB,kFACpJ,IACD,CACD,SAAgB,GAAa,EAA6B,CACxD,OAAO,OAAO,GAAS,UAAY,GAAW,KAAK,EAAK,CCrF1D,MAAM,GAAwB,GACxB,GAAW,QAAQ,IAAI,iBAAmB,2BAEnCC,EAA2B,IAAIC,EAAAA,QAAQ,CAClD,YAAa,cACb,QAAS,EACT,mBAAsB,GACtB,MAAO,QAAQ,IAAI,WAAa,OAE5B,IAAA,GAFqC,CACvC,OAAQ,GAAwB,IACjC,CACF,CAAC,CAEWC,GAA+B,IAAID,EAAAA,QAAQ,CACtD,QAAS,GACT,WAAY,GACZ,QAAS,EACT,mBAAsB,GACtB,MAAO,QAAQ,IAAI,WAAa,OAE5B,IAAA,GAFqC,CACvC,OAAQ,GAAwB,IACjC,CACF,CAAC,CCbW,EAA8B,4BAC9B,EAAsB,mBACtBE,EAA0B,OAAO,SAAS,QAAQ,IAAI,iBAAmB,IAAK,GAAG,EAAI,GA6B5F,EAAY,IAAIC,EAAAA,QAAU,CAAE,OAAQ,GAAI,CAAC,CAEzC,GAAoB,EAAiC,IAAmE,CAC5H,IAAMC,EAA2B,CAC/B,GAAG,EACH,OAAQ,CAAE,GAAG,GAAQ,OAAQ,CAC7B,eAAgB,CAAE,GAAG,GAAQ,eAAgB,CAC7C,cAAe,CAAE,GAAG,GAAQ,cAAe,CAE5C,CAED,IAAK,IAAM,KAAU,EAClB,OAAO,QAAQ,GAAU,EAAE,CAAC,CAAoF,SAAS,CAAC,EAAY,KAAiB,CACtJ,EAAY,KAAgB,EAAE,CAC9B,OAAO,QAAQ,EAAY,CAAC,SAAS,CAAC,EAAU,KAAW,CACzD,EAAY,GAAa,IAAa,EAAY,GAAa,IAAa,EAAE,EAAE,OAAO,EAAM,EAC7F,EACF,CAGJ,OAAO,GAGL,OAAO,OAAO,SAAY,UAE5B,OAAO,eAAe,OAAQ,UAAW,CAEvC,UAAW,KACX,aAAc,GACd,WAAY,GACZ,MAAO,OAAO,IAAI,iBAAiB,CACnC,SAAU,GACX,CAAC,CAEA,OAAO,OAAO,cAAiB,UAEjC,OAAO,eAAe,OAAQ,eAAgB,CAE5C,UAAW,KACX,aAAc,GACd,WAAY,GACZ,MAAO,OAAO,IAAI,sBAAsB,CACxC,SAAU,GACX,CAAC,CAGJ,IAAqB,EAArB,KAA6B,CAW3B,YAAY,EAAoB,EAA8C,EAA0C,EAA8B,CAAnI,KAAA,GAAA,EAAoB,KAAA,YAAA,EAAwF,KAAA,WAAA,sCAR7E,IAAI,uBAIA,EAAE,CAKtD,KAAK,UAAY,CAAC,CAAC,EACf,GACF,KAAK,+BAA+B,IAAI,OAAO,UAAU,CAAE,EAAoB,CAInF,MAAa,oBAA2C,CACtD,GAAI,CAAC,KAAK,GACR,OAEF,GAAI,KAAK,mBACP,OAAO,KAAK,mBAEd,IAAM,GAAA,EAAA,EAAA,SAAsB,CAC1B,GAAI,KAAK,GACT,WAAY,KAAK,WAClB,CAAC,CAEE,EAAO,EAAU,IAAiB,EAAS,CAS/C,OAPK,IACF,SAAW,MAAM,EAAgB,IAAiB,iBAAiB,KAAK,GAAG,wBAAyB,CAAE,OAAQ,CAAE,WAAY,KAAK,WAAY,CAAE,CAAC,CACjJ,EAAU,IAAI,EAAU,EAAK,EAG/B,KAAK,YAAc,EAAK,YACxB,KAAK,mBAAqB,EACnB,KAAK,mBAGd,MAAa,0BAA0B,EAA0G,CAC/I,GAAI,CAAC,KAAK,GACR,OAEF,GAAI,KAAK,mBACP,OAAO,KAAK,mBAGd,IAAM,EAAW,KAAK,GAEhB,EAAe,EAAU,IAAiB,EAAS,CACzD,GAAI,EAEF,MADA,MAAK,mBAAqB,EACnB,EAGT,IAAM,EAAO,MAAM,EAAuB,KAAK,GAAG,CAIlD,OAHA,EAAU,IAAI,EAAU,EAAK,CAE7B,KAAK,mBAAqB,EACnB,KAAK,mBAGd,IAAW,gBAA2B,CACpC,OAAO,KAAK,gBAAgB,iBAAiB,CAG/C,IAAW,QAAmB,CAC5B,OAAO,KAAK,gBAAgB,SAAS,CAGvC,IAAW,eAA0B,CACnC,OAAO,KAAK,gBAAgB,gBAAgB,CAG9C,gBAAwB,EAAkC,CACxD,GAAI,CAAC,KAAK,mBACR,MAAU,MAAM,cAAc,EAAI,oDAAoD,CAExF,OAAO,OAAO,KAAK,KAAK,mBAAmB,IAAQ,EAAE,CAAC,CAGxD,IAAW,qBAAmC,CAC5C,OAAO,EAAiB,IAAA,GAAW,KAAK,+BAA+B,QAAQ,CAAC,CAGlF,IAAW,aAAuC,CAChD,GAAI,CAAC,KAAK,mBACR,MAAU,MAAM,2EAA2E,CAG7F,OAAO,EAAiB,KAAK,mBAAoB,KAAK,+BAA+B,QAAQ,CAAC,CAGhG,mBAA0B,EAAwF,CAEhH,IAAM,EAAc,QAAQ,CAG5B,OAAO,OAAO,EAAiB,CAAC,QAAS,GAAc,CACrD,OAAO,KAAK,EAAU,CAAC,QAAS,GAAa,CAC3C,GAAI,CAAC,GAAa,EAAS,CACzB,MAAU,MAAM,kEAAkE,IAAW,EAE/F,EACF,CAEF,IAAM,GAAA,EAAA,EAAA,oBAAsC,CAC5C,GAAI,CAAC,EACH,MAAU,MAAM,gDAAgD,CAGlE,IAAM,EAAmB,KAAK,MAAM,EAAiB,SAAS,IAAI,EAA4B,EAA0B,KAAK,CACvH,EAAe,OAAO,OAAO,EAAkB,EAAiB,CACtE,KAAK,+BAA+B,IAAI,EAAa,EAAa,CAClE,EAAiB,QAAQ,IAAI,EAA6B,KAAK,UAAU,KAAK,oBAAoB,CAAC,CACnG,IAAM,MAAgB,CACpB,KAAK,+BAA+B,OAAO,EAAY,CACvD,EAAiB,QAAQ,IAAI,EAA6B,KAAK,UAAU,KAAK,oBAAoB,CAAC,EAGrG,MADA,GAAQ,OAAO,SAAW,EACnB,EAGT,MAAa,0BAA6C,CACxD,GAAI,CAAC,KAAK,GACR,OAEF,GAAI,KAAK,yBACP,OAAO,KAAK,yBAGd,IAAM,GAAA,EAAA,EAAA,SAAsB,CAC1B,GAAI,KAAK,GACT,WAAY,KAAK,WACjB,OAAQ,GACT,CAAC,CACE,EAAO,EAAU,IAAI,EAAS,CAQlC,OANK,IACF,SAAW,MAAM,EAAgB,IAAI,iBAAiB,KAAK,GAAG,+BAAgC,CAAE,OAAQ,CAAE,WAAY,KAAK,WAAY,CAAE,CAAC,CAC3I,EAAU,IAAI,EAAU,EAAK,EAG/B,KAAK,yBAA2B,EACzB,KAAK,yBAGd,IAAW,mBAAyB,CAClC,GAAI,CAAC,KAAK,yBACR,MAAU,MAAM,uFAAuF,CAEzG,OAAO,KAAK,yBAGd,MAAa,sBAAsB,EAAe,EAAwD,CACxG,GAAI,CAAC,KAAK,IAAM,CAAC,GAAS,CAAC,EACzB,OAEF,IAAM,EAAuB,KAAK,cAAc,GAEhD,GAAI,EACF,OAAO,EAGT,IAAM,EAAW,GAAG,KAAK,GAAG,GAAG,IAEzB,EAAe,EAAU,IAAiB,EAAS,CACzD,GAAI,EAEF,MADA,MAAK,cAAc,GAAS,EACrB,EAGT,GAAM,CAAE,QAAS,MAAM,GAAoB,KAAkB,gBAAgB,EAAM,mBAAoB,CACrG,OAAQ,KAAK,GACd,CAAE,CACD,QAAS,CACP,0BAA2B,EAC5B,CACF,CAAC,CAIF,OAFA,EAAU,IAAI,EAAU,EAAK,CAC7B,KAAK,cAAc,GAAS,EACrB,KAAK,cAAc,GAG5B,uBAA8B,EAAkC,CAC9D,GAAI,CAAC,MAAM,QAAQ,EAAgB,CACjC,OAGF,IAAM,EAAgB,MAAM,KAAK,IAAI,IAAI,CAAC,GAAI,KAAK,YAAc,EAAE,CAAG,GAAG,EAAgB,CAAC,CAAC,CAEvF,EAAc,OAAS,IAI3B,KAAK,WAAa,GAElB,EAAA,EAAA,oBAD4C,EAC1B,SAAS,IAAI,EAAqB,KAAK,WAAW,KAAK,IAAI,CAAC,IC7RlF,MAAa,GAAkB,MAAO,EAAgB,IAAgC,CACpF,GAAM,CAAE,KAAM,GAAY,MAAM,GAAoB,KAAK,eAAgB,CAAE,SAAQ,QAAO,CAAC,CAC3F,OAAO,GCJT,IAAqB,EAArB,cAA6C,KAAM,yCAC1C,+BAEG,uBCHZ,MAAa,GAAc,cACd,GAAe,cACf,EAAc,aACd,EAAsB,eACtB,GAAgB,uBAChB,EAA0B,wBAC1B,GAA2B,uBAC3B,GAA+B,0BCW/B,EAAuB,MAAO,EAAsC,IAA+D,CAC9I,IAAM,EAAe,EAAQ,yBAAkB,EAAQ,yBAA6B,GACpF,GAAI,CAAC,MAAM,QAAQ,EAAa,EAAI,EAAa,aAAa,GAAK,cACjE,OAEF,GAAM,CACJ,2BACA,iCACA,0BACE,EACE,EAAS,EAAQ,GACvB,GAAI,CAAC,GAAU,MAAM,QAAQ,EAAO,CAClC,OAGF,IAAM,EAA0B,EAAQ,GAClC,EAAgC,GAAyB,QAAU,EAAwB,OAAS,EAAI,KAAK,MAAM,EAAkC,CAAG,EAAE,CAC1J,GAAc,IAAU,KAAiC,MAAM,IAAI,CAEnE,EAAa,IAAI,EAAQ,EAAQ,OAAQ,EAA+B,EAAW,CAczF,OAbI,IACE,EACF,MAAM,EAAW,0BAA0B,EAAuB,CAElE,MAAM,EAAW,oBAAoB,EAIrC,GACF,MAAM,EAAW,0BAA0B,EAG7C,EAAA,EAAA,oBAAmB,CAAC,kBAAkB,IAAI,EAAa,EAAW,CAC3D,GC3BI,GAAc,EAAuC,EAAE,GAAwB,MAAO,EAAK,EAAK,IAAuB,CAClI,GAAI,CACF,IAAM,EAAa,MAAM,EAAqB,EAAS,EAAI,QAAQ,CAC/D,IACF,EAAI,KAAO,EAGX,EAAI,QAAQ,GAA2B,GAGzC,GAAM,MACA,CACN,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,MAAO,2BAA4B,CAAC,GAIlD,GAAwB,EAKjC,EAAE,GAAwB,MAAO,EAAK,EAAK,IAAwB,CACrE,GAAM,CACJ,2BACA,iCACA,uBACA,aACE,EACA,EACJ,GAAI,EAAI,QAAQ,cAAe,CAC7B,GAAI,CACF,EAAU,MAAM,EAAa,EAAI,QAAQ,cAAe,EAAU,OAC3D,EAAG,CACN,aAAaI,EAAAA,QAAI,kBACnB,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,uBAAuB,CAAE,CAAC,CACjD,aAAaA,EAAAA,QAAI,kBAC1B,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,EAAE,QAAQ,CAAE,CAAC,CAE7C,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,mCAAmC,CAAE,CAAC,CAExE,OAEF,IAAM,EAAS,GAAS,MAAM,GAE1B,IACF,EAAI,QAAQ,GAAuB,GAGrC,IAAM,GAAc,EAAI,UAAU,KAAiC,MAAM,IAAI,CACvE,EAAa,IAAI,EAAQ,EAAQ,GAAS,MAAM,YAAa,IAAA,GAAW,EAAW,EAErF,GAA4B,IAC9B,MAAM,QAAQ,IAAI,CAChB,GAA4B,EAAW,oBAAoB,CAC3D,GAAkC,EAAW,0BAA0B,CACxE,CAAC,CAGJ,EAAI,KAAO,GACX,EAAA,EAAA,oBAAmB,CAAC,kBAAkB,IAAI,EAAa,EAAW,CAIlE,EAAI,QAAQ,GAA2B,UAC9B,EAAsB,CAC/B,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,oBAAoB,CAAE,CAAC,CACvD,OAEF,GAAM,EAGK,EAAiB,GAGL,MAAO,EAAK,EAAK,IAAwB,CAChE,GAAM,CACJ,QACA,gBACE,EACA,EAEJ,GAAI,CAAC,EAAI,QAAQ,cAAe,CAC9B,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,oBAAoB,CAAE,CAAC,CACvD,OAGF,GAAI,CAEF,GADA,EAAU,MAAM,GAAgB,EAAI,QAAQ,cAAe,EAAM,CAC7D,CAAC,EACH,MAAM,IAAI,QAEL,EAAG,CACV,GAAI,aAAaA,EAAAA,QAAI,kBAAmB,CACtC,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,uBAAuB,CAAE,CAAC,CAC1D,OAEF,GAAI,CAACA,EAAAA,QAAI,kBAAmB,EAAgB,CAAC,KAAK,GAAO,aAAa,EAAI,CAAE,CAC1E,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAE,EAAY,QAAQ,CAAE,CAAC,CACxD,OAEF,EAAI,OAAO,IAAI,CAAC,KAAK,CAAE,OAAQ,CAAC,mCAAmC,CAAE,CAAC,CACtE,OAEF,IAAM,EAAS,GAAS,OACpB,IACF,EAAI,QAAQ,GAAuB,GAGrC,IAAM,EAAa,IAAI,EAAQ,EAAO,CAElC,IACF,EAAI,QAAQ,2BAAgC,EAE5C,MAAM,EAAW,sBAAsB,EAAO,EAAa,EAG7D,EAAI,KAAO,EACX,IAAM,GAAA,EAAA,EAAA,oBAAyC,CAAC,iBAChD,GAAqB,IAAI,EAAa,EAAW,CACjD,GAAqB,IAAI,cAAc,EAAkB,EAAI,QAAQ,cAAc,CAAC,CAIpF,EAAI,QAAQ,GAA2B,EAEvC,GAAM,EAGKC,EAAoD,MAAO,EAAK,EAAK,IAAS,CACzF,MAAM,EAAI,KAAK,oBAAoB,CACnC,GAAM,EAGK,GAAoB,EAAc,IACxC,EAAI,QAAQ,cAGV,EAAa,EAAI,QAAQ,cAAe,EAAU,CAFhD,KAKE,EAAyB,MAAO,EAAgD,EAA4B,IAAyC,CAChK,IAAM,EAAgB,MAAM,QAAQ,EAAW,EAAI,EAAW,QAAU,EAClE,EAAa,IAAI,EACrB,EACA,IAAA,GACA,IAAA,GACA,EAAgB,EAAa,IAAA,GAC9B,CAED,MAAM,EAAW,oBAAoB,CACrC,KAAA,EAAA,EAAA,UAAmBC,EAAAA,WAAW,OAAO,CACrC,EAAM,iBAAiB,IAAI,EAAa,EAAW,CAE/C,GACF,EAAM,QAAQ,IAAI,EAAqB,GAAY,KAAK,IAAI,EAAI,GAAG,EAIvE,IAAA,EAAe,EC5Kf,MAAaC,GAAkF,EAAS,EAAS,IAAS,CACxH,EAAQ,gBAAgB,OAAQ,IAAA,GAAU,CAC1C,EAAQ,QAAQ,YAAa,MAAO,EAAS,IAAU,CACrD,GAAI,CACF,IAAM,EAAO,MAAM,EAAqB,EAAS,EAAQ,QAAQ,CAC7D,IACF,EAAQ,KAAO,QAEX,CACN,EAAM,OAAO,IAAI,CAAC,KAAK,CAAE,MAAO,2BAA4B,CAAC,GAE/D,CAEF,GAAM,EAER,OAAO,eAAe,EAA4B,OAAO,IAAI,gBAAgB,CAAE,CAAE,MAAO,GAAM,CAAC,CCpB/F,MAAa,OAAA,EAAA,EAAA,oBAAwD,CAAC,kBAAkB,IAAI,EAAY,CAE3F,MAAwC,GAAS,EAAE,GAE1D,GACJ,EACA,IACG,CAAC,GAAa,EAAI,OAAO,OAAO,GAAS,CAAE,cAAc,IAAe,EAAE,CAAE,EAAS,CAE7E,EAAwB,GAA6B,EAAqB,EAAS,SAAS,CAC5F,EAAgC,GAAqC,EAAqB,EAAiB,iBAAiB,CAC5H,EAA+B,GAAoC,EAAqB,EAAgB,gBAAgB,CCdrI,IAAa,EAAb,cAA6C,KAAM,CACjD,YAAY,EAA8B,KAAM,EAAU,0BAA2B,CACnF,MAAM,EAAQ,CADG,KAAA,KAAA,EAEjB,KAAK,KAAO,4BCHhB,MAAa,EAAwB,CACnC,KAAM,OACN,MAAO,QACP,IAAK,MACN,CAEK,EAAwB,EAC3B,EAAsB,UAAa,IAAA,IACnC,EAAsB,OAAS,GAA+B,CAC7D,GAAM,CAAE,WAAU,YAAa,EAE/B,MAAO,SADoB,OAAO,KAAK,GAAG,EAAS,GAAG,IAAW,CAAC,SAAS,SAAS,KAGrF,EAAsB,KAAO,GAA+B,CAC3D,GAAM,CAAE,UAAW,EACnB,GAAI,EACF,MAAO,UAAUE,EAAAA,QAAI,KAAK,EAAE,CAAE,EAAQ,CAAE,UAAW,GAAI,CAAC,IAI7D,CAEY,GAA0B,GAA+E,CACpH,IAAM,EAAsB,GAAuB,OAE/C,MAAC,GAAuB,CAAC,EAAsB,IAInD,OAAO,EAAsB,GAAqB,EAAsB,EC3B1E,IAAa,GAAb,MAAa,CAAgB,CAS3B,YAAY,EAAmB,EAAc,UAN/B,GAOR,IACF,KAAK,IAAM,GAEb,KAAK,MAAQ,GAAS,IAAIC,EAAAA,QAAU,CAAE,OAAQ,KAAK,IAAK,CAAC,CAG3D,OAAe,aAAa,EAAmB,EAAgB,EAAsB,CACnF,IAAM,EAAe,GAAG,EAAU,GAAG,IAGrC,MAAO,CAAE,eAAc,UAFL,EAAW,IAAI,GAAa,GAAG,EAAa,GAAG,IAAY,CAE3C,CASpC,mBAA0B,EAAgB,EAAyC,CACjF,GAAI,CACF,GAAM,CAAE,eAAc,aAAc,EAAgB,aAAa,OAAQ,EAAQ,EAAW,CACtF,EAAc,KAAK,MAAM,KAAK,EAAU,CAExCC,EAA4B,EAAE,CAQpC,OAPA,OAAO,QAAQ,EAAY,CACxB,QAAQ,EAAG,KAAiB,EAAY,CACxC,SAAS,CAAC,EAAK,KAAiB,CAC/B,IAAM,EAAY,EAAI,QAAQ,GAAG,EAAa,GAAI,GAAG,CACrD,EAAO,GAAa,GACpB,CAEG,OACD,CACN,MAAO,EAAE,EAWb,mBAA0B,EAAgB,EAAkE,CAC1G,GAAI,CACF,IAAM,EAAY,GAAsB,QAAQ,EAAO,GAAG,IAEpD,EAAe,OAAO,QAAQ,EAAuB,CAAC,KAAK,CAAC,EAAW,MAAkB,CAC7F,IAAK,EAAS,EAAU,CACxB,IAAK,EACL,IAAK,KAAK,IACX,EAAE,CAIH,OAFA,KAAK,MAAM,KAAK,EAAa,CAEtB,CAAE,QAAS,GAAM,MAClB,CACN,MAAO,CAAE,QAAS,GAAO,EAY7B,2BAAkC,EAAgB,EAAgD,CAChG,GAAI,CACF,GAAM,CAAE,eAAc,aAAc,EAAgB,aAAa,OAAQ,EAAQ,EAAW,CACtF,EAAc,KAAK,MAAM,KAAK,EAAU,CAExCC,EAAmC,EAAE,CAQ3C,OAPA,OAAO,QAAQ,EAAY,CACxB,QAAQ,EAAG,KAAiB,EAAY,CACxC,SAAS,CAAC,EAAK,KAAiB,CAC/B,IAAM,EAAY,EAAI,QAAQ,GAAG,EAAa,GAAI,GAAG,CACrD,EAAO,GAAa,GACpB,CAEG,OACD,CACN,MAAO,EAAE,EAcb,2BAAkC,EAAgB,EAAsB,EAAoD,CAC1H,GAAI,CACF,IAAM,EAAW,GAAsB,QAAQ,EAAO,GAAG,IAEnD,EAAe,EAAW,IAAI,IAAc,CAChD,IAAK,EAAQ,EAAU,CACvB,IAAK,EACL,IAAK,KAAK,IACX,EAAE,CAIH,OAFA,KAAK,MAAM,KAAK,EAAa,CAEtB,CAAE,QAAS,GAAM,MAClB,CACN,MAAO,CAAE,QAAS,GAAO,IAK/B,MAAaC,EAAmC,IAAI,GCrIvC,EAAmC,CAC9C,IAAK,MACL,GAAI,KACL,CAEY,EAAyB,CACpC,eAAgB,iBAChB,yBAA0B,2BAC1B,iBAAkB,mBAClB,UAAW,YACX,wBAAyB,0BACzB,aAAc,eACd,YAAa,cACb,eAAgB,iBACjB,CAEY,EAA4B,CACvC,eAAgB,iBAChB,yBAA0B,4CAC1B,iBAAkB,4BAClB,UAAW,qBACX,wBAAyB,kDACzB,aAAc,gDACd,YAAa,iDACb,eAAgB,4DACjB,CCHK,IACJ,EACA,EACA,IACY,CACZ,IAAM,EAAiB,IAAI,IAAI,EAAY,CACrC,EAAsB,EAAoB,OAAO,GAAc,EAAe,IAAI,EAAW,CAAC,CAEpG,OAAO,IAAa,EAAiC,IACjD,EAAoB,SAAW,EAAoB,OACnD,EAAoB,OAAS,GAqBtB,IAA8B,EAAoE,IAAiC,CAC9I,IAAM,EAA8B,OAAO,OAAO,EAAiC,CAKnF,OAHI,EACK,EAA4B,MAAM,GAAc,EAAW,uBAAuB,CAEpF,EAA4B,KAAK,GAAc,EAAW,uBAAuB,EA8DpF,GAAiB,GAAmC,CACxD,IAAMC,EAAmB,EAAE,CAM3B,OAJK,GAAY,QACf,EAAO,KAAK,6BAA6B,CAGpC,GAGH,IACJ,EACA,EACA,EACA,KAC2B,CAC3B,aAAc,GACd,QACA,oBAAqB,EAAE,CACvB,sBACA,aACA,GAAI,GAAW,CAAE,UAAS,CAC3B,EAUK,IACJ,EACA,EACA,EACA,IACS,CACT,IAAMC,EAA6C,EAAE,CAC/C,EAAuB,IAAI,IA+BjC,GA7BA,GAAQ,MAAM,oCAAqC,CACjD,SACA,sBACA,sBACD,CAAC,CAEF,OAAO,QAAQ,EAAoB,CAAC,SAAS,CAAC,EAAW,KAAgB,CACvE,GAAI,GAAY,YAAa,CAC3B,IAAM,EAAqB,IAAI,IAAI,EAAW,YAAY,CACpD,EAAuB,EAAoB,OAAO,GAAc,EAAmB,IAAI,EAAW,CAAC,CACnG,EAAoB,EAAoB,OAAO,GAAc,CAAC,EAAmB,IAAI,EAAW,CAAC,CAOvG,GALI,EAAqB,SAEvB,EAAmB,GAAa,GAG9B,EAAkB,OAAQ,CAE5B,IAAM,EAAuB,EAAkB,MAAM,CAAC,KAAK,IAAI,CAE1D,EAAqB,IAAI,EAAqB,EACjD,EAAqB,IAAI,EAAsB,EAAE,CAAC,CAEpD,EAAqB,IAAI,EAAqB,CAAE,KAAK,EAAU,IAGnE,CAEE,OAAO,KAAK,EAAmB,CAAC,OAAS,EAAG,CAC9C,IAAM,EAAY,EAAgB,mBAAmB,EAAQ,OAAO,KAAK,EAAmB,CAAC,CAE7F,OAAO,KAAK,GAAa,EAAE,CAAC,CAAC,QAAS,GAAc,CAC9C,EAAmB,KAGrB,EAAmB,GADO,MAAM,KAAK,IAAI,IAAI,CAAC,GAAI,EAAU,IAAc,EAAE,CAAG,GAAG,EAAmB,GAAW,CAAC,CAAC,GAGpH,CAEF,GAAQ,MAAM,8BAA+B,CAAE,SAAQ,qBAAoB,YAAW,CAAC,CACvF,GAAM,CAAE,WAAY,EAAgB,mBAAmB,EAAQ,EAAmB,CAC7E,GACH,GAAQ,MAAM,sCAAuC,CAAE,SAAQ,qBAAoB,CAAC,CAKxF,MAAM,KAAK,EAAqB,SAAS,CAAC,CAAC,KAAK,CAAC,EAAsB,KAAgB,CACrF,IAAM,EAAoB,EAAqB,MAAM,IAAI,CACzD,OAAO,EAAgB,2BAA2B,EAAQ,EAAY,EAAkB,EACxF,CAEF,GAAQ,MAAM,6BAA8B,CAC1C,SACA,wBAAyB,MAAM,KAAK,EAAqB,SAAS,CAAC,CAAC,KAAK,CAAC,EAAK,MAAe,CAC5F,kBAAmB,EAAI,MAAM,IAAI,CACjC,WAAY,EACb,EAAE,CACJ,CAAC,EAUE,IACJ,EACA,EACA,IACyE,CACzE,GAAI,CAAC,EAAkB,OACrB,MAAO,CAAE,sBAAuB,GAAO,uBAAwB,GAAO,CAGxE,IAAM,EAAY,IAAI,IAAI,EAAkB,CACtC,EAA4B,EAAoB,OAAO,GAAc,EAAU,IAAI,EAAW,CAAC,CAWrG,OATI,IAAa,EAAiC,IAEzC,CACL,sBAAuB,EAA0B,OAAS,EAC1D,uBAAwB,GACzB,CAII,CACL,sBAAuB,EAA0B,SAAW,EAAoB,OAChF,uBAAwB,GACzB,EAUG,IACJ,EACA,EACA,IACa,CACb,IAAM,EAAuB,CAAC,GAAG,EAAiB,CAG5C,EAA6B,EAAiB,OAAO,GAAa,CAAC,EAAqB,GAAW,CAGzG,OAFA,EAAqB,KAAK,GAAG,EAA2B,CAEjD,GAWH,IACJ,EACA,EACA,EACA,IACqC,CACrC,IAAMC,EAA2C,EAAE,CAcnD,OAZA,EAAiB,QAAS,GAAc,CACtC,IAAM,EAAoB,EAAgB,GACpC,EAAc,GAA4B,EAAmB,EAAqB,EAAS,CAE7F,EAAY,wBACd,EAAO,GAAa,CAClB,YAAa,EAAE,CACf,uBAAwB,EAAY,uBACrC,GAEH,CAEK,GAUH,IACJ,EACA,EACA,IACqC,CACrC,IAAMC,EAAyD,EAAE,CAqBjE,OAnBI,OAAO,KAAK,GAAqB,EAAE,CAAC,CAAC,QACvC,OAAO,QAAQ,EAAkB,CAAC,SAAS,CAAC,EAAW,KAAiB,CACtE,IAAM,EAAyB,GAC7B,EACA,EACA,EACD,CAIG,IACF,EAAqB,GAAa,CAChC,cACA,yBACD,GAEH,CAGG,GAWH,GAAkB,MACtB,EACA,EACA,EACA,IAC8C,CAS9C,IAAMC,GARW,MAAM,EAAgB,KAAK,8BAA+B,CACzE,SACA,aACA,sBACD,CAAE,CACD,QAAS,EAAQ,QAClB,CAAC,GAEuD,MAAQ,EAAE,CAEnE,GAAI,CAAC,OAAO,KAAK,GAAQ,EAAE,CAAC,CAAC,OAC3B,MAAU,MAAM,gCAAgC,CAIlD,GAAI,EAAQ,gCAAkC,EAAiC,IAC7E,OAAO,EAIT,IAAMC,EAA2D,EAAE,CAenE,OAdA,OAAO,QAAQ,EAAK,CAAC,SAAS,CAAC,EAAW,KAAgB,CACxD,IAAM,EAAc,GAAY,aAAe,EAAE,CAOjD,EAAuB,GAAa,CAClC,cACA,uBAR6B,GAC7B,EACA,EACA,EAAQ,8BACT,CAKA,EACD,CAEK,GAWH,IACJ,EACA,EACA,EACA,IACiC,CACjC,IAAM,EAAmB,GAAc,EAAW,CA4BlD,OA3BI,EAAiB,OACZ,GAAkB,EAAuB,iBAAkB,EAAqB,EAAY,EAAiB,KAAK,KAAK,CAAC,CAG5H,EAQA,GAAqB,OAenB,MAdL,GAAQ,KAAK,kCAAmC,CAC9C,SACA,aACD,CAAC,CACK,CACL,aAAc,GACd,SACA,oBAAqB,EAAE,CACvB,sBACA,aACA,MAAO,EAAuB,wBAC/B,GAnBD,GAAQ,KAAK,sDAAuD,CAClE,aACA,sBACD,CAAC,CACK,GAAkB,EAAuB,eAAgB,EAAqB,EAAW,GA8B9F,GAA4B,MAChC,EACA,EACA,EACA,EACA,IAC8C,CAC9C,IAAIC,EAA2D,CAAE,GAAG,EAAqB,CAEnF,EAAoC,MAAM,GAC9C,EACA,EACA,EACA,EACD,CAUD,MAPA,GAAyB,CACvB,GAAG,EACH,GAAG,EACJ,CAED,GAAwB,EAAQ,EAAmC,EAAoB,CAEhF,GAWH,GAAwB,MAC5B,EACA,EACA,EACA,IAK8C,CAC9C,GAAM,CAAE,UAAW,EAGb,EAA+B,GADJ,EAAgB,mBAAmB,EAAQ,EAAW,CAGrF,EACA,EAAQ,8BACT,CACG,EAAsB,CAAE,GAAG,EAA8B,CAGvD,EAAgC,EAAW,OAAO,GAAa,CAAC,EAA6B,GAAW,CAGxG,EAA0B,EAAgB,2BAA2B,EAAQ,EAA8B,CAG3G,EAAmB,EAA8B,OAAO,GAAa,CAAC,EAAwB,GAAW,CAGzG,EAAmB,EAA8B,OAAO,GAAa,EAAwB,GAAW,CACxG,EAAuB,GAC3B,EACA,EACA,EACA,EAAQ,8BACT,CAGD,EAAsB,CAAE,GAAG,EAAqB,GAAG,EAAsB,CAGzE,IAAM,EAAuB,GAAsB,EAAkB,EAAkB,EAAqB,CAkB5G,OAjBI,EAAqB,OAEhB,GACL,EACA,EACA,EACA,EACA,EACD,EAGH,GAAQ,MAAM,6BAA8B,CAC1C,SACA,sBACA,sBACD,CAAC,CAEK,IASI,GAAsB,MAAO,CACxC,sBACA,aACA,SACA,SACA,QAAS,CACP,aAAa,GACb,gCAAgC,EAAiC,IACjE,UAAU,QAEmD,CAC/D,IAAM,EAAiB,GAAU,GAAS,EAAE,IAAM,KAE5C,EAAkB,GAAwB,EAAY,EAAqB,EAAgB,EAAO,CACxG,GAAI,EACF,OAAO,EAGT,IAAM,EAAsB,MAAM,GAChC,EACA,EACA,EACA,CAAE,gCAA+B,UAAS,SAAQ,CACnD,CAEK,EAAe,GAA2B,EAAqB,EAAW,CAYhF,OAVA,GAAQ,KAAK,uBAAwB,CACnC,OAAQ,EACR,sBACA,sBACA,eACA,aACA,gCACA,aACD,CAAC,CAEK,CACL,eACA,OAAQ,EACR,sBACA,sBACA,aACA,GAAI,EAAe,EAAE,CAAG,CAAE,MAAO,EAAuB,yBAA0B,CACnF,EC9kBG,GAAiB,GAA4B,CACjD,IAAM,EAAW,OAAO,KAAK,EAAK,aAAa,UAAY,EAAE,CAAC,CAC9D,OAAO,MAAM,KAAK,IAAI,IAAI,CACxB,GAAG,EACJ,CAAC,CAAC,CAAC,OAAO,QAAQ,EAGf,IACJ,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,IACG,CACH,GAAI,EAAS,CACX,GAAQ,MAAM,EAAS,CACrB,IAAK,EAAI,IACT,OAAQ,EAAI,OACZ,oBAAqB,GAAgB,qBAAuB,EAAE,CAC9D,GAAG,EACJ,CAAC,CAEF,EAAI,OAAO,EAAK,CAAC,KAAK,CACpB,MAAO,EACP,UACA,GAAI,GAAgB,qBAAuB,CAAE,oBAAqB,EAAe,oBAAqB,CACvG,CAAC,CACF,OAGF,GAAQ,KAAK,EAAS,CACpB,IAAK,EAAI,IACT,OAAQ,EAAI,OACZ,oBAAqB,GAAgB,qBAAuB,EAAE,CAC9D,GAAG,EACJ,CAAC,CACF,GAAM,EAUK,IACX,EACA,EAAqC,CACnC,QAAS,GACT,WAAY,GACZ,8BAA+B,EAAiC,IACjE,GACE,MAAO,EAAc,EAAe,IAAsC,CAC7E,GAAI,CACF,GAAM,CACJ,SACA,UACA,aACA,iCACE,EAEE,GAAsB,EAAoB,EAAiB,IAAiB,GAChF,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CAAE,sBAAqB,CACxB,CACK,EAAO,EAAI,KAEXC,EAAuB,GAAc,EAAK,CAEhD,GAAI,CAAC,GAAY,OAAQ,CACvB,EACE,EAAuB,YACvB,EAA0B,YAC1B,IACD,CACD,OAIF,IAAM,EAAS,MAAM,GAAoB,CACvC,sBACA,aACA,SACA,OAAQ,EAAK,GACb,QAAS,CACP,WAAY,GAAc,GAC1B,8BAA+B,GAAiC,EAAiC,IACjG,QAAS,IACT,GAAI,GAAU,CAAE,SAAQ,CACzB,CACF,CAAC,CAEF,GAAI,EAAO,aAAc,CACvB,GAAQ,KAAK,gCAAiC,CAC5C,OAAQ,EAAK,GACb,sBACA,aACA,IAAK,EAAI,IACT,OAAQ,EAAI,OACb,CAAC,CACF,GAAM,CACN,OAGF,GAAI,CAAC,EAAS,CACZ,GAAQ,KAAK,gEAAiE,CAC5E,OAAQ,EAAK,GACb,sBACA,aACA,IAAK,EAAI,IACT,OAAQ,EAAI,OACb,CAAC,CACF,GAAM,CACN,OAGF,EAAI,OAAO,IAAI,CAAC,KAAK,CACnB,MAAO,EAAuB,yBAC9B,QAAS,EAA0B,yBACnC,SAAU,EACV,SAAU,EACV,OAAQ,EAAO,OAChB,CAAC,OACK,EAAO,CASd,GARsB,EAAQ,QACf,MAAM,yCAA0C,CAC7D,QACA,sBACA,IAAK,EAAI,IACT,OAAQ,EAAI,OACb,CAAC,CAEE,CAAC,EAAQ,QAAS,CACpB,EAAQ,QAAQ,MAAM,sDAAuD,CAC3E,QACA,IAAK,EAAI,IACT,OAAQ,EAAI,OACb,CAAC,CACF,GAAM,CACN,OAGF,EAAI,OAAO,IAAI,CAAC,KAAK,CACnB,MAAO,EAAuB,eAC9B,QAAS,EAA0B,eACpC,CAAC,4DCvJN,MAAaC,GAAkB,CAC7B,uBACD,CAEYC,GAAiC,CAC5C,sBACD,CAED,IAAA,GAAe,CACb,OACA,eACD,CCHD,MAAMC,GAAuDC,EAAS,kBAIhE,IAAiB,CAAE,kBAAkB,EAAE,CAAE,UAAyF,EAAE,GAAW,CACnJ,EAAS,QAAQ,CACf,cAAe,OACf,wBAAyB,GAAQ,qBACjC,GAAG,EACJ,CAAC,EAGEC,GAAyCD,EAAS,WAClDE,EAAqCF,EAAS,SAwD9CG,GAAwB,CAC5B,cACA,WACA,KAAA,EACA,aACA,uBACA,iCACA,qBACA,mBACA,uBACA,+BACA,8BACA,cACA,UACA,0BACA,gBACA,yBACA,SAAA,EACA,wBACA,0BACA,sBACA,6BACA,YAAA,GACD,CAED,IAAA,GAAe"}
package/lib/index.d.cts CHANGED
@@ -3,7 +3,6 @@ import * as outbreak from "@autofleet/outbreak";
3
3
  import { newTrace as newTrace$1 } from "@autofleet/outbreak";
4
4
  import { Handler, NextFunction, Request, Response } from "express";
5
5
  import { FastifyPluginCallback } from "fastify";
6
- import { Permission, RequiredPermission } from "@autofleet/common-types/lib/identity";
7
6
 
8
7
  //#region rolldown:runtime
9
8
  //#endregion
@@ -128,6 +127,10 @@ declare const getAuthorizationHeader: (authorizationSettings: {
128
127
  } | undefined) => string | undefined;
129
128
  //#endregion
130
129
  //#region src/permissions/SDK/consts.d.ts
130
+ declare const PERMISSIONS_EVALUATION_OPERATORS: {
131
+ AND: string;
132
+ OR: string;
133
+ };
131
134
  declare const PERMISSION_ERROR_TYPES: {
132
135
  USER_NOT_FOUND: string;
133
136
  INSUFFICIENT_PERMISSIONS: string;
@@ -150,15 +153,23 @@ interface Logger {
150
153
  error: (message: string, meta?: unknown) => void;
151
154
  }
152
155
  /**
156
+ * The operator to use when evaluating permissions
157
+ * - AND: All required permissions must be present
158
+ * - OR: At least one required permission must be present
159
+ */
160
+ type PermissionsEvaluationOperator = typeof PERMISSIONS_EVALUATION_OPERATORS[keyof typeof PERMISSIONS_EVALUATION_OPERATORS];
161
+ /**
153
162
  * Options for evaluating permissions
154
163
  * - requireAll: If true, all contexts must have the required permissions for the user to be considered authorized (default: true)
155
164
  * - timeout: Optional - timeout in milliseconds for the permissions evaluation (default: 10 seconds)
156
165
  * - enforce: Optional - If true, the permissions evaluation will throw an error if the user does not have the required permissions (default: false)
166
+ * - permissionsEvaluationOperator: Optional - If set, the permissions evaluation will use this operator to evaluate the permissions (default: AND)
157
167
  */
158
168
  interface EvaluatePermissionsOpts {
159
169
  requireAll?: boolean;
160
170
  timeout?: number;
161
171
  enforce?: boolean;
172
+ permissionsEvaluationOperator?: PermissionsEvaluationOperator;
162
173
  }
163
174
  /**
164
175
  * Parameters for evaluating permissions for a user across multiple contexts
@@ -169,7 +180,7 @@ interface EvaluatePermissionsOpts {
169
180
  * - userId: The user id, if not provided, we will get the user from the current context
170
181
  */
171
182
  interface EvaluatePermissionsParams {
172
- requiredPermissions: RequiredPermission[];
183
+ requiredPermissions: string[];
173
184
  contextIds: string[];
174
185
  logger?: Logger;
175
186
  options: EvaluatePermissionsOpts;
@@ -181,7 +192,7 @@ interface EvaluatePermissionsParams {
181
192
  * - hasRequiredPermissions: Whether the user has the required permissions in the context
182
193
  */
183
194
  interface PermissionsEvaluation {
184
- permissions: Permission[];
195
+ permissions: string[];
185
196
  hasRequiredPermissions: boolean;
186
197
  }
187
198
  /**
@@ -202,7 +213,7 @@ interface PermissionCheckResult {
202
213
  isAuthorized: boolean;
203
214
  userId?: string;
204
215
  resolvedPermissions: PermissionsEvaluationByContextId;
205
- requiredPermissions: RequiredPermission[];
216
+ requiredPermissions: string[];
206
217
  contextIds: string[];
207
218
  error?: PermissionErrorType;
208
219
  message?: string;
@@ -222,6 +233,7 @@ declare const evaluatePermissions: ({
222
233
  userId,
223
234
  options: {
224
235
  requireAll,
236
+ permissionsEvaluationOperator,
225
237
  timeout
226
238
  }
227
239
  }: EvaluatePermissionsParams) => Promise<PermissionCheckResult>;
@@ -237,7 +249,7 @@ interface RequirePermissionsOptions extends EvaluatePermissionsOpts {
237
249
  * @param options - Configuration options for permission checking
238
250
  * @returns Express middleware function
239
251
  */
240
- declare const requirePermissions: (requiredPermissions: RequiredPermission[], options?: RequirePermissionsOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
252
+ declare const requirePermissions: (requiredPermissions: string[], options?: RequirePermissionsOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
241
253
  declare namespace index_d_exports {
242
254
  export { _default as default, middlewares, sdk };
243
255
  }
package/lib/index.d.ts CHANGED
@@ -3,7 +3,6 @@ import { newTrace as newTrace$1 } from "@autofleet/outbreak";
3
3
  import { LoggerInstanceManager } from "@autofleet/logger";
4
4
  import { Handler, NextFunction, Request, Response } from "express";
5
5
  import { FastifyPluginCallback } from "fastify";
6
- import { Permission, RequiredPermission } from "@autofleet/common-types/lib/identity";
7
6
 
8
7
  //#region src/user/ApiUser.d.ts
9
8
  type AccountType = "client" | "user" | "service" | "driver";
@@ -126,6 +125,10 @@ declare const getAuthorizationHeader: (authorizationSettings: {
126
125
  } | undefined) => string | undefined;
127
126
  //#endregion
128
127
  //#region src/permissions/SDK/consts.d.ts
128
+ declare const PERMISSIONS_EVALUATION_OPERATORS: {
129
+ AND: string;
130
+ OR: string;
131
+ };
129
132
  declare const PERMISSION_ERROR_TYPES: {
130
133
  USER_NOT_FOUND: string;
131
134
  INSUFFICIENT_PERMISSIONS: string;
@@ -148,15 +151,23 @@ interface Logger {
148
151
  error: (message: string, meta?: unknown) => void;
149
152
  }
150
153
  /**
154
+ * The operator to use when evaluating permissions
155
+ * - AND: All required permissions must be present
156
+ * - OR: At least one required permission must be present
157
+ */
158
+ type PermissionsEvaluationOperator = typeof PERMISSIONS_EVALUATION_OPERATORS[keyof typeof PERMISSIONS_EVALUATION_OPERATORS];
159
+ /**
151
160
  * Options for evaluating permissions
152
161
  * - requireAll: If true, all contexts must have the required permissions for the user to be considered authorized (default: true)
153
162
  * - timeout: Optional - timeout in milliseconds for the permissions evaluation (default: 10 seconds)
154
163
  * - enforce: Optional - If true, the permissions evaluation will throw an error if the user does not have the required permissions (default: false)
164
+ * - permissionsEvaluationOperator: Optional - If set, the permissions evaluation will use this operator to evaluate the permissions (default: AND)
155
165
  */
156
166
  interface EvaluatePermissionsOpts {
157
167
  requireAll?: boolean;
158
168
  timeout?: number;
159
169
  enforce?: boolean;
170
+ permissionsEvaluationOperator?: PermissionsEvaluationOperator;
160
171
  }
161
172
  /**
162
173
  * Parameters for evaluating permissions for a user across multiple contexts
@@ -167,7 +178,7 @@ interface EvaluatePermissionsOpts {
167
178
  * - userId: The user id, if not provided, we will get the user from the current context
168
179
  */
169
180
  interface EvaluatePermissionsParams {
170
- requiredPermissions: RequiredPermission[];
181
+ requiredPermissions: string[];
171
182
  contextIds: string[];
172
183
  logger?: Logger;
173
184
  options: EvaluatePermissionsOpts;
@@ -179,7 +190,7 @@ interface EvaluatePermissionsParams {
179
190
  * - hasRequiredPermissions: Whether the user has the required permissions in the context
180
191
  */
181
192
  interface PermissionsEvaluation {
182
- permissions: Permission[];
193
+ permissions: string[];
183
194
  hasRequiredPermissions: boolean;
184
195
  }
185
196
  /**
@@ -200,7 +211,7 @@ interface PermissionCheckResult {
200
211
  isAuthorized: boolean;
201
212
  userId?: string;
202
213
  resolvedPermissions: PermissionsEvaluationByContextId;
203
- requiredPermissions: RequiredPermission[];
214
+ requiredPermissions: string[];
204
215
  contextIds: string[];
205
216
  error?: PermissionErrorType;
206
217
  message?: string;
@@ -220,6 +231,7 @@ declare const evaluatePermissions: ({
220
231
  userId,
221
232
  options: {
222
233
  requireAll,
234
+ permissionsEvaluationOperator,
223
235
  timeout
224
236
  }
225
237
  }: EvaluatePermissionsParams) => Promise<PermissionCheckResult>;
@@ -235,7 +247,7 @@ interface RequirePermissionsOptions extends EvaluatePermissionsOpts {
235
247
  * @param options - Configuration options for permission checking
236
248
  * @returns Express middleware function
237
249
  */
238
- declare const requirePermissions: (requiredPermissions: RequiredPermission[], options?: RequirePermissionsOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
250
+ declare const requirePermissions: (requiredPermissions: string[], options?: RequirePermissionsOptions) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
239
251
  declare namespace index_d_exports {
240
252
  export { _default as default, middlewares, sdk };
241
253
  }
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./chunk-ClKQ-cuU.js";import*as t from"@autofleet/outbreak";import{getCurrentContext as n,newTrace as r,traceTypes as i}from"@autofleet/outbreak";import a from"jsonwebtoken";import o from"node-cache";import s from"object-hash";import c from"moment";import l from"@autofleet/network";const{DEPRECATED_JWT_SECRET:u,JWT_NEW_SECRET:d,DEPRECATED_REFRESH_JWT_SECRET:f,REFRESH_JWT_SECRET:ee,DEPRECATION_UNIX_TIMESTAMP:te}=process.env,p=(e,t,n)=>{let r=c(parseInt(te||``,10)*1e3);try{let i;if(e){let{iat:t}=a.decode(e);i=c(t*1e3)}else i=c();return i.isBefore(r)?t:n}catch{return n}},ne=e=>p(e,f,ee),m=e=>p(e,u,d),h=e=>e.replace(`Bearer `,``),g=(e,t)=>{let n=h(e);return a.verify(n,t||m(n))},_=`[0-9a-f]`,re=RegExp(`^(?:${_}{8}-${_}{4}-[1-8]${_}{3}-[89ab]${_}{3}-${_}{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`,`i`);function ie(e){return typeof e==`string`&&re.test(e)}const v=process.env.API_GATEWAY_URL||`https://api.autofleet.io`,y=new l({serviceName:`IDENTITY_MS`,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),b=new l({baseURL:v,serviceUrl:v,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),x=`x-af-elevated-permissions`,S=`x-af-context-ids`,C=Number.parseInt(process.env.MAX_CONTEXT_IDS??`0`,10)||20,w=new o({stdTTL:10}),T=(e,t)=>{let n={...e,fleets:{...e?.fleets},businessModels:{...e?.businessModels},demandSources:{...e?.demandSources}};for(let e of t)Object.entries(e??{}).forEach(([e,t])=>{n[e]??={},Object.entries(t).forEach(([t,r])=>{n[e][t]=(n[e][t]||[]).concat(r)})});return n};typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1}),typeof Symbol.asyncDispose!=`symbol`&&Object.defineProperty(Symbol,`asyncDispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.asyncDispose`),writable:!1});var E=class{constructor(e,t,n,r){this.id=e,this.accountType=t,this.contextIds=r,this.privateElevatedPermissionsHash=new Map,this.appPermission={},this.emptyUser=!!e,n&&this.privateElevatedPermissionsHash.set(Symbol(`initial`),n)}async getUserPermissions(){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let e=s({id:this.id,contextIds:this.contextIds}),t=w.get(e);return t||({data:t}=await y.get(`/api/v1/users/${this.id}/authorization-payload`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.accountType=t.accountType,this.privatePermissions=t,this.privatePermissions}async useCustomPermissionLoader(e){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let t=this.id,n=w.get(t);if(n)return this.privatePermissions=n,n;let r=await e(this.id);return w.set(t,r),this.privatePermissions=r,this.privatePermissions}get businessModels(){return this.getUserProperty(`businessModels`)}get fleets(){return this.getUserProperty(`fleets`)}get demandSources(){return this.getUserProperty(`demandSources`)}getUserProperty(e){if(!this.privatePermissions)throw Error(`Cannot get ${e} without calling (async) getUserPermissions before`);return Object.keys(this.privatePermissions[e]||{})}get elevatedPermissions(){return T(void 0,this.privateElevatedPermissionsHash.values())}get permissions(){if(!this.privatePermissions)throw Error(`Cannot get permissions without calling (async) getUserPermissions before`);return T(this.privatePermissions,this.privateElevatedPermissionsHash.values())}elevatePermissions(e){let t=Symbol();Object.values(e).forEach(e=>{Object.keys(e).forEach(e=>{if(!ie(e))throw Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${e}`)})});let r=n();if(!r)throw Error(`Cannot find current user cross services trace`);let i=JSON.parse(r.context?.get(x)||`{}`),a=Object.assign(i,e);this.privateElevatedPermissionsHash.set(t,a),r.context.set(x,JSON.stringify(this.elevatedPermissions));let o=()=>{this.privateElevatedPermissionsHash.delete(t),r.context.set(x,JSON.stringify(this.elevatedPermissions))};return o[Symbol.dispose]=o,o}async getUserPermissionsLegacy(){if(!this.id)return;if(this.privatePermissionsLegacy)return this.privatePermissionsLegacy;let e=s({id:this.id,contextIds:this.contextIds,legacy:!0}),t=w.get(e);return t||({data:t}=await y.get(`/api/v1/users/${this.id}/authorization-payload-legacy`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.privatePermissionsLegacy=t,this.privatePermissionsLegacy}get permissionsLegacy(){if(!this.privatePermissionsLegacy)throw Error(`Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before`);return this.privatePermissionsLegacy}async getUserAppPermissions(e,t){if(!this.id||!e||!t)return;let n=this.appPermission[e];if(n)return n;let r=`${this.id}:${e}`,i=w.get(r);if(i)return this.appPermission[e]=i,i;let{data:a}=await b.post(`/api/v1/apps/${e}/get-user-payload`,{userId:this.id},{headers:{"x-autofleet-apps-secret":t}});return w.set(r,a),this.appPermission[e]=a,this.appPermission[e]}extendRequiredContexts(e){if(!Array.isArray(e))return;let t=Array.from(new Set([...this.contextIds??[],...e]));t.length>C||(this.contextIds=t,n()?.context?.set(S,this.contextIds.join(`,`)))}};const ae=async(e,t)=>{let{data:n}=await b.post(`/api/v1/auth`,{bearer:e,appId:t});return n};var D=class extends Error{constructor(...e){super(...e),this.name=`AppDoesNotExist`,this.message=`app does not exist`}};const O=`userObject`,k=`x-af-user-id`,A=`x-af-user-permissions`,j=async(e,t)=>{let r=t[`X-IAF-ORIGIN-SERVICE`]||t[`x-iaf-origin-service`]||``;if(!Array.isArray(r)&&r.toLowerCase()===`identity-ms`)return;let{eagerLoadUserPermissions:i,eagerLoadUserPermissionsLegacy:a,customPermissionLoader:o}=e,s=t[k];if(!s||Array.isArray(s))return;let c=t[x],l=c?.length&&c.length>0?JSON.parse(c):{},u=(t?.[S])?.split(`,`),d=new E(s,`user`,l,u);return i&&(o?await d.useCustomPermissionLoader(o):await d.getUserPermissions()),a&&await d.getUserPermissionsLegacy(),n().nonHeaderContext?.set(O,d),d},M=(e={})=>async(t,n,r)=>{try{let n=await j(e,t.headers);n&&(t.user=n,t.headers[A]=n),r()}catch{n.status(401).json({error:`cannot authenticate user`})}},N=(e={})=>async(t,r,i)=>{let{eagerLoadUserPermissions:o,eagerLoadUserPermissionsLegacy:s,returnErrorIfNoToken:c,appSecret:l}=e,u;if(t.headers.authorization){try{u=await g(t.headers.authorization,l)}catch(e){e instanceof a.TokenExpiredError?r.status(401).json({errors:[`Access token expired`]}):e instanceof a.JsonWebTokenError?r.status(400).json({errors:[e.message]}):r.status(500).json({errors:[`Server error while parsing token`]});return}let e=u?.user?.id;e&&(t.headers[k]=e);let i=(t.headers?.[S])?.split(`,`),c=new E(e,u?.user?.accountType,void 0,i);(o||s)&&await Promise.all([o&&c.getUserPermissions(),s&&c.getUserPermissionsLegacy()]),t.user=c,n().nonHeaderContext?.set(O,c),t.headers[A]=c}else if(c){r.status(401).json({errors:[`No token provided`]});return}i()},P=e=>async(t,r,i)=>{let{appId:o,clientSecret:s}=e,c;if(!t.headers.authorization){r.status(401).json({errors:[`No token provided`]});return}try{if(c=await ae(t.headers.authorization,o),!c)throw new D}catch(e){if(e instanceof a.TokenExpiredError){r.status(401).json({errors:[`Access token expired`]});return}if([a.JsonWebTokenError,D].some(t=>e instanceof t)){r.status(400).json({errors:[e.message]});return}r.status(500).json({errors:[`Server error while parsing token`]});return}let l=c?.userId;l&&(t.headers[k]=l);let u=new E(l);o&&(t.headers[`x-autofleet-apps-secret`]=s,await u.getUserAppPermissions(o,s)),t.user=u;let d=n().nonHeaderContext;d?.set(O,u),d?.set(`accessToken`,h(t.headers.authorization)),t.headers[A]=u,i()},F=async(e,t,n)=>{await e.user.getUserPermissions(),n()},I=(e,t)=>e.headers.authorization?g(e.headers.authorization,t):null,L=async(e,t,n)=>{let a=Array.isArray(n)&&n.length<=C,o=new E(t,void 0,void 0,a?n:void 0);await o.getUserPermissions(),e??=r(i.RABBIT),e.nonHeaderContext.set(O,o),a&&e.context.set(S,n?.join(`,`)||``)};var R=E;const z=(e,t,n)=>{e.decorateRequest(`user`,void 0),e.addHook(`onRequest`,async(e,n)=>{try{let n=await j(t,e.headers);n&&(e.user=n)}catch{n.status(401).send({error:`cannot authenticate user`})}}),n()};Object.defineProperty(z,Symbol.for(`skip-override`),{value:!0});const B=()=>n().nonHeaderContext?.get(O),V=()=>B()?.id,H=(e,t)=>!V()||Object.hasOwn(B().permissions?.[t]??{},e),U=e=>H(e,`fleets`),W=e=>H(e,`businessModels`),G=e=>H(e,`demandSources`);var K=class extends Error{constructor(e=null,t=`UnauthorizedAccessError`){super(t),this.user=e,this.name=`UnauthorizedAccessError`}};const q={NONE:`NONE`,BASIC:`BASIC`,JWT:`JWT`},J={[q.NONE]:()=>void 0,[q.BASIC]:e=>{let{username:t,password:n}=e;return`Basic ${Buffer.from(`${t}:${n}`).toString(`base64`)}`},[q.JWT]:e=>{let{secret:t}=e;if(t)return`Bearer ${a.sign({},t,{expiresIn:10})}`}},oe=e=>{let t=e?.method;if(!(!t||!J[t]))return J[t](e)},Y=new class e{constructor(e,t){this.ttl=10,t&&(this.ttl=t),this.cache=e??new o({stdTTL:this.ttl})}static getCacheKeys(e,t,n){let r=`${e}-${t}`;return{baseCacheKey:r,cacheKeys:n.map(e=>`${r}-${e}`)}}getUserPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`perm`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setUserPermissions(e,t){try{let n=t=>`perm-${e}-${t}`,r=Object.entries(t).map(([e,t])=>({key:n(e),val:t,ttl:this.ttl}));return this.cache.mset(r),{success:!0}}catch{return{success:!1}}}getCachedDeniedPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`seen`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setCachedDeniedPermissions(e,t,n){try{let r=t=>`seen-${e}-${t}`,i=t.map(e=>({key:r(e),val:n,ttl:this.ttl}));return this.cache.mset(i),{success:!0}}catch{return{success:!1}}}},X={USER_NOT_FOUND:`USER_NOT_FOUND`,INSUFFICIENT_PERMISSIONS:`INSUFFICIENT_PERMISSIONS`,VALIDATION_ERROR:`VALIDATION_ERROR`,API_ERROR:`API_ERROR`,NO_REQUIRED_PERMISSIONS:`NO_REQUIRED_PERMISSIONS`,UNAUTHORIZED:`UNAUTHORIZED`,BAD_REQUEST:`BAD_REQUEST`,INTERNAL_ERROR:`INTERNAL_ERROR`},Z={USER_NOT_FOUND:`User not found`,INSUFFICIENT_PERMISSIONS:`User does not have sufficient permissions`,VALIDATION_ERROR:`Validation error occurred`,API_ERROR:`API error occurred`,NO_REQUIRED_PERMISSIONS:`No required permissions provided for evaluation`,UNAUTHORIZED:`User is not authorized to perform this action`,BAD_REQUEST:`Bad request, please check the input parameters`,INTERNAL_ERROR:`Internal server error occurred while checking permissions`},Q=(e,t)=>{let n=new Set(e);return t.some(e=>typeof e==`string`?n.has(e):e.every(e=>n.has(e)))},se=(e,t)=>{let n=Object.values(e);return t?n.every(e=>e.hasRequiredPermissions):n.some(e=>e.hasRequiredPermissions)},ce=e=>{let t=[];return e?.length||t.push(`contextIds cannot be empty`),t},le=(e,t,n,r)=>({isAuthorized:!1,error:e,resolvedPermissions:{},requiredPermissions:t,contextIds:n,...r&&{message:r}}),ue=(e,t,n,r)=>{let i={},a=new Map;if(r?.debug(`Start caching permissions results`,{userId:e,resolvedPermissions:t,requiredPermissions:n}),Object.entries(t).forEach(([e,t])=>{if(t?.permissions){let r=new Set(t.permissions),o=n.flat().filter(e=>r.has(e)),s=n.flat().filter(e=>!r.has(e));if(o.length&&(i[e]=o),s.length){let t=s.sort().join(`,`);a.has(t)||a.set(t,[]),a.get(t).push(e)}}}),Object.keys(i).length>0){let t=Y.getUserPermissions(e,Object.keys(i));Object.keys(t??{}).forEach(e=>{i[e]&&(i[e]=Array.from(new Set([...t[e]||[],...i[e]])))}),r?.debug(`Caching granted permissions`,{userId:e,permissionsToCache:i,pastCache:t});let{success:n}=Y.setUserPermissions(e,i);n||r?.error(`Failed to cache granted permissions`,{userId:e,permissionsToCache:i})}Array.from(a.entries()).map(([t,n])=>{let r=t.split(`,`);return Y.setCachedDeniedPermissions(e,n,r)}),r?.debug(`Caching denied permissions`,{userId:e,deniedPermissionsGroups:Array.from(a.entries()).map(([e,t])=>({deniedPermissions:e.split(`,`),contextIds:t}))})},de=(e,t)=>{if(!e.length)return{isResolvableFromCache:!1,hasRequiredPermissions:!1};let n=new Set(e);return{isResolvableFromCache:t.every(e=>typeof e==`string`?n.has(e):e.some(e=>n.has(e))),hasRequiredPermissions:!1}},fe=(e,t,n)=>{let r=[...e],i=t.filter(e=>!n[e]);return r.push(...i),r},pe=(e,t,n)=>{let r={};return e.forEach(e=>{let i=t[e],a=de(i,n);a.isResolvableFromCache&&(r[e]={permissions:[],hasRequiredPermissions:a.hasRequiredPermissions})}),r},me=(e,t)=>{let n={};return Object.keys(e||{}).length&&Object.entries(e).forEach(([e,r])=>{let i=Q(r,t);i&&(n[e]={permissions:r,hasRequiredPermissions:i})}),n},he=e=>Object.fromEntries(e.map(e=>[e,{permissions:[],hasRequiredPermissions:!0}])),ge=async({userId:e,contextIds:t,requiredPermissions:n,options:r})=>{if(!n.length)return he(t);let i=await y.post(`/api/v1/permissions/users/${e}`,{userId:e,contextIds:t},{timeout:r.timeout});if(!Object.keys(i?.data?.permissionsByContexts||{}).length)throw Error(`Failed to resolve permissions`);return Object.fromEntries(Object.entries(i.data.permissionsByContexts).map(([e,t])=>[e,{permissions:t,hasRequiredPermissions:Q(t,n)}]))},_e=(e,t,n,r)=>{let i=ce(e);return i.length?le(X.VALIDATION_ERROR,t,e,i.join(`, `)):n?t?.length?null:(r?.info(`No requiredPermissions provided`,{userId:n,contextIds:e}),{isAuthorized:!1,userId:n,resolvedPermissions:{},requiredPermissions:t,contextIds:e,error:X.NO_REQUIRED_PERMISSIONS}):(r?.warn(`User not found in context, cannot check permissions`,{contextIds:e,requiredPermissions:t}),le(X.USER_NOT_FOUND,t,e))},ve=async(e,t,n,r,i)=>{let a={...i},o=await ge({userId:e,contextIds:t,requiredPermissions:n,options:r});return a={...i,...o},ue(e,o,n),a},ye=async(e,t,n,r)=>{let{logger:i}=r,a=me(Y.getUserPermissions(e,t),n),o={...a},s=t.filter(e=>!a[e]),c=Y.getCachedDeniedPermissions(e,s),l=s.filter(e=>!c[e]),u=s.filter(e=>c[e]),d=pe(u,c,n);o={...o,...d};let f=fe(l,u,d);return f.length?ve(e,f,n,r,o):(i?.debug(`Final resolved permissions`,{userId:e,requiredPermissions:n,resolvedPermissions:o}),o)},be=async({requiredPermissions:e,contextIds:t,logger:n,userId:r,options:{requireAll:i=!0,timeout:a=1e4}})=>{let o=r||B()?.id||null,s=_e(t,e,o,n);if(s)return s;let c=await ye(o,t,e,{timeout:a,logger:n}),l=se(c,i);return n?.info(`Resolved permissions`,{userId:o,requiredPermissions:e,resolvedPermissions:c,isAuthorized:l,requireAll:i,contextIds:t}),{isAuthorized:l,userId:o,resolvedPermissions:c,requiredPermissions:e,contextIds:t,...l?{}:{error:X.INSUFFICIENT_PERMISSIONS}}},xe=e=>{let t=Object.keys(e.permissions?.contexts??{});return Array.from(new Set([...t])).filter(Boolean)},Se=(e,t,n,r,i,a,o,s,c)=>{if(r){s?.error(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),i.status(n).json({error:e,message:t,...c?.requiredPermissions&&{requiredPermissions:c.requiredPermissions}});return}s?.warn(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),o()},Ce=(e,t={enforce:!1,requireAll:!0})=>async(n,r,i)=>{try{let{logger:a,enforce:o,requireAll:s}=t,c=(t,s,c)=>Se(t,s,c,o,r,n,i,a,{requiredPermissions:e}),l=n.user,u=xe(l);if(!u?.length){c(X.BAD_REQUEST,Z.BAD_REQUEST,400);return}let d=await be({requiredPermissions:e,contextIds:u,logger:a,userId:l.id,options:{requireAll:s??!0,timeout:1e4,...a&&{logger:a}}});if(d.isAuthorized){a?.info(`User has required permissions`,{userId:l.id,requiredPermissions:e,contextIds:u,url:n.url,method:n.method}),i();return}if(!o){a?.warn(`User does not have required permissions, skipping enforcement`,{userId:l.id,requiredPermissions:e,contextIds:u,url:n.url,method:n.method}),i();return}r.status(403).json({error:X.INSUFFICIENT_PERMISSIONS,message:Z.INSUFFICIENT_PERMISSIONS,required:e,contexts:u,userId:d.userId})}catch(a){if(t.logger?.error(`Error in requirePermissions middleware`,{error:a,requiredPermissions:e,url:n.url,method:n.method}),!t.enforce){t.logger?.error(`Error during permission check, skipping enforcement`,{error:a,url:n.url,method:n.method}),i();return}r.status(500).json({error:X.INTERNAL_ERROR,message:Z.INTERNAL_ERROR})}};var we=e({default:()=>De,middlewares:()=>Ee,sdk:()=>Te});const Te={evaluatePermissions:be},Ee={requirePermissions:Ce};var De={sdk:Te,middlewares:Ee};const Oe=t.getCurrentContext,ke=({outbreakOptions:e={},logger:n}={})=>{t.default({headersPrefix:`x-af`,contextMiddlewareGetter:n?.addContextMiddleware,...e})},Ae=t.traceTypes,$=t.newTrace;var je={traceTypes:Ae,newTrace:$,User:R,middleware:M,middlewareWithDecode:N,eagerLoadPermissionsMiddleware:F,getCurrentPayload:Oe,getDecodedBearer:I,checkFleetPermission:U,checkBusinessModelPermission:W,checkDemandSourcePermission:G,isUserExist:V,getUser:B,UnauthorizedAccessError:K,appMiddleware:P,createOrSetRabbitTrace:L,outbreak:t,AUTHORIZATION_METHODS:q,getAuthorizationHeader:oe,CONTEXTS_IDS_HEADER:S,authFromUserIdHeaderPlugin:z,permissions:we};export{q as AUTHORIZATION_METHODS,S as CONTEXTS_IDS_HEADER,K as UnauthorizedAccessError,R as User,P as appMiddleware,z as authFromUserIdHeaderPlugin,W as checkBusinessModelPermission,G as checkDemandSourcePermission,U as checkFleetPermission,L as createOrSetRabbitTrace,je as default,F as eagerLoadPermissionsMiddleware,ke as enableTracing,oe as getAuthorizationHeader,Oe as getCurrentPayload,I as getDecodedBearer,ne as getRefreshTokenSecret,m as getTokenSecret,B as getUser,V as isUserExist,M as middleware,N as middlewareWithDecode,$ as newTrace,t as outbreak,we as permissions,Ae as traceTypes};
1
+ import{t as e}from"./chunk-ClKQ-cuU.js";import*as t from"@autofleet/outbreak";import{getCurrentContext as n,newTrace as r,traceTypes as i}from"@autofleet/outbreak";import a from"jsonwebtoken";import o from"node-cache";import s from"object-hash";import c from"moment";import l from"@autofleet/network";const{DEPRECATED_JWT_SECRET:u,JWT_NEW_SECRET:d,DEPRECATED_REFRESH_JWT_SECRET:f,REFRESH_JWT_SECRET:ee,DEPRECATION_UNIX_TIMESTAMP:te}=process.env,p=(e,t,n)=>{let r=c(parseInt(te||``,10)*1e3);try{let i;if(e){let{iat:t}=a.decode(e);i=c(t*1e3)}else i=c();return i.isBefore(r)?t:n}catch{return n}},ne=e=>p(e,f,ee),m=e=>p(e,u,d),h=e=>e.replace(`Bearer `,``),g=(e,t)=>{let n=h(e);return a.verify(n,t||m(n))},_=`[0-9a-f]`,re=RegExp(`^(?:${_}{8}-${_}{4}-[1-8]${_}{3}-[89ab]${_}{3}-${_}{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$`,`i`);function ie(e){return typeof e==`string`&&re.test(e)}const v=process.env.API_GATEWAY_URL||`https://api.autofleet.io`,y=new l({serviceName:`IDENTITY_MS`,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),b=new l({baseURL:v,serviceUrl:v,retries:3,retryCondition:()=>!0,cache:process.env.NODE_ENV===`test`?void 0:{maxAge:10*1e3}}),x=`x-af-elevated-permissions`,S=`x-af-context-ids`,C=Number.parseInt(process.env.MAX_CONTEXT_IDS??`0`,10)||20,w=new o({stdTTL:10}),T=(e,t)=>{let n={...e,fleets:{...e?.fleets},businessModels:{...e?.businessModels},demandSources:{...e?.demandSources}};for(let e of t)Object.entries(e??{}).forEach(([e,t])=>{n[e]??={},Object.entries(t).forEach(([t,r])=>{n[e][t]=(n[e][t]||[]).concat(r)})});return n};typeof Symbol.dispose!=`symbol`&&Object.defineProperty(Symbol,`dispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.dispose`),writable:!1}),typeof Symbol.asyncDispose!=`symbol`&&Object.defineProperty(Symbol,`asyncDispose`,{__proto__:null,configurable:!1,enumerable:!1,value:Symbol.for(`nodejs.asyncDispose`),writable:!1});var E=class{constructor(e,t,n,r){this.id=e,this.accountType=t,this.contextIds=r,this.privateElevatedPermissionsHash=new Map,this.appPermission={},this.emptyUser=!!e,n&&this.privateElevatedPermissionsHash.set(Symbol(`initial`),n)}async getUserPermissions(){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let e=s({id:this.id,contextIds:this.contextIds}),t=w.get(e);return t||({data:t}=await y.get(`/api/v1/users/${this.id}/authorization-payload`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.accountType=t.accountType,this.privatePermissions=t,this.privatePermissions}async useCustomPermissionLoader(e){if(!this.id)return;if(this.privatePermissions)return this.privatePermissions;let t=this.id,n=w.get(t);if(n)return this.privatePermissions=n,n;let r=await e(this.id);return w.set(t,r),this.privatePermissions=r,this.privatePermissions}get businessModels(){return this.getUserProperty(`businessModels`)}get fleets(){return this.getUserProperty(`fleets`)}get demandSources(){return this.getUserProperty(`demandSources`)}getUserProperty(e){if(!this.privatePermissions)throw Error(`Cannot get ${e} without calling (async) getUserPermissions before`);return Object.keys(this.privatePermissions[e]||{})}get elevatedPermissions(){return T(void 0,this.privateElevatedPermissionsHash.values())}get permissions(){if(!this.privatePermissions)throw Error(`Cannot get permissions without calling (async) getUserPermissions before`);return T(this.privatePermissions,this.privateElevatedPermissionsHash.values())}elevatePermissions(e){let t=Symbol();Object.values(e).forEach(e=>{Object.keys(e).forEach(e=>{if(!ie(e))throw Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${e}`)})});let r=n();if(!r)throw Error(`Cannot find current user cross services trace`);let i=JSON.parse(r.context?.get(x)||`{}`),a=Object.assign(i,e);this.privateElevatedPermissionsHash.set(t,a),r.context.set(x,JSON.stringify(this.elevatedPermissions));let o=()=>{this.privateElevatedPermissionsHash.delete(t),r.context.set(x,JSON.stringify(this.elevatedPermissions))};return o[Symbol.dispose]=o,o}async getUserPermissionsLegacy(){if(!this.id)return;if(this.privatePermissionsLegacy)return this.privatePermissionsLegacy;let e=s({id:this.id,contextIds:this.contextIds,legacy:!0}),t=w.get(e);return t||({data:t}=await y.get(`/api/v1/users/${this.id}/authorization-payload-legacy`,{params:{contextIds:this.contextIds}}),w.set(e,t)),this.privatePermissionsLegacy=t,this.privatePermissionsLegacy}get permissionsLegacy(){if(!this.privatePermissionsLegacy)throw Error(`Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before`);return this.privatePermissionsLegacy}async getUserAppPermissions(e,t){if(!this.id||!e||!t)return;let n=this.appPermission[e];if(n)return n;let r=`${this.id}:${e}`,i=w.get(r);if(i)return this.appPermission[e]=i,i;let{data:a}=await b.post(`/api/v1/apps/${e}/get-user-payload`,{userId:this.id},{headers:{"x-autofleet-apps-secret":t}});return w.set(r,a),this.appPermission[e]=a,this.appPermission[e]}extendRequiredContexts(e){if(!Array.isArray(e))return;let t=Array.from(new Set([...this.contextIds??[],...e]));t.length>C||(this.contextIds=t,n()?.context?.set(S,this.contextIds.join(`,`)))}};const ae=async(e,t)=>{let{data:n}=await b.post(`/api/v1/auth`,{bearer:e,appId:t});return n};var D=class extends Error{constructor(...e){super(...e),this.name=`AppDoesNotExist`,this.message=`app does not exist`}};const O=`userObject`,k=`x-af-user-id`,A=`x-af-user-permissions`,j=async(e,t)=>{let r=t[`X-IAF-ORIGIN-SERVICE`]||t[`x-iaf-origin-service`]||``;if(!Array.isArray(r)&&r.toLowerCase()===`identity-ms`)return;let{eagerLoadUserPermissions:i,eagerLoadUserPermissionsLegacy:a,customPermissionLoader:o}=e,s=t[k];if(!s||Array.isArray(s))return;let c=t[x],l=c?.length&&c.length>0?JSON.parse(c):{},u=(t?.[S])?.split(`,`),d=new E(s,`user`,l,u);return i&&(o?await d.useCustomPermissionLoader(o):await d.getUserPermissions()),a&&await d.getUserPermissionsLegacy(),n().nonHeaderContext?.set(O,d),d},M=(e={})=>async(t,n,r)=>{try{let n=await j(e,t.headers);n&&(t.user=n,t.headers[A]=n),r()}catch{n.status(401).json({error:`cannot authenticate user`})}},N=(e={})=>async(t,r,i)=>{let{eagerLoadUserPermissions:o,eagerLoadUserPermissionsLegacy:s,returnErrorIfNoToken:c,appSecret:l}=e,u;if(t.headers.authorization){try{u=await g(t.headers.authorization,l)}catch(e){e instanceof a.TokenExpiredError?r.status(401).json({errors:[`Access token expired`]}):e instanceof a.JsonWebTokenError?r.status(400).json({errors:[e.message]}):r.status(500).json({errors:[`Server error while parsing token`]});return}let e=u?.user?.id;e&&(t.headers[k]=e);let i=(t.headers?.[S])?.split(`,`),c=new E(e,u?.user?.accountType,void 0,i);(o||s)&&await Promise.all([o&&c.getUserPermissions(),s&&c.getUserPermissionsLegacy()]),t.user=c,n().nonHeaderContext?.set(O,c),t.headers[A]=c}else if(c){r.status(401).json({errors:[`No token provided`]});return}i()},P=e=>async(t,r,i)=>{let{appId:o,clientSecret:s}=e,c;if(!t.headers.authorization){r.status(401).json({errors:[`No token provided`]});return}try{if(c=await ae(t.headers.authorization,o),!c)throw new D}catch(e){if(e instanceof a.TokenExpiredError){r.status(401).json({errors:[`Access token expired`]});return}if([a.JsonWebTokenError,D].some(t=>e instanceof t)){r.status(400).json({errors:[e.message]});return}r.status(500).json({errors:[`Server error while parsing token`]});return}let l=c?.userId;l&&(t.headers[k]=l);let u=new E(l);o&&(t.headers[`x-autofleet-apps-secret`]=s,await u.getUserAppPermissions(o,s)),t.user=u;let d=n().nonHeaderContext;d?.set(O,u),d?.set(`accessToken`,h(t.headers.authorization)),t.headers[A]=u,i()},F=async(e,t,n)=>{await e.user.getUserPermissions(),n()},I=(e,t)=>e.headers.authorization?g(e.headers.authorization,t):null,L=async(e,t,n)=>{let a=Array.isArray(n)&&n.length<=C,o=new E(t,void 0,void 0,a?n:void 0);await o.getUserPermissions(),e??=r(i.RABBIT),e.nonHeaderContext.set(O,o),a&&e.context.set(S,n?.join(`,`)||``)};var R=E;const z=(e,t,n)=>{e.decorateRequest(`user`,void 0),e.addHook(`onRequest`,async(e,n)=>{try{let n=await j(t,e.headers);n&&(e.user=n)}catch{n.status(401).send({error:`cannot authenticate user`})}}),n()};Object.defineProperty(z,Symbol.for(`skip-override`),{value:!0});const B=()=>n().nonHeaderContext?.get(O),V=()=>B()?.id,H=(e,t)=>!V()||Object.hasOwn(B().permissions?.[t]??{},e),U=e=>H(e,`fleets`),W=e=>H(e,`businessModels`),G=e=>H(e,`demandSources`);var K=class extends Error{constructor(e=null,t=`UnauthorizedAccessError`){super(t),this.user=e,this.name=`UnauthorizedAccessError`}};const q={NONE:`NONE`,BASIC:`BASIC`,JWT:`JWT`},J={[q.NONE]:()=>void 0,[q.BASIC]:e=>{let{username:t,password:n}=e;return`Basic ${Buffer.from(`${t}:${n}`).toString(`base64`)}`},[q.JWT]:e=>{let{secret:t}=e;if(t)return`Bearer ${a.sign({},t,{expiresIn:10})}`}},Y=e=>{let t=e?.method;if(!(!t||!J[t]))return J[t](e)},X=new class e{constructor(e,t){this.ttl=10,t&&(this.ttl=t),this.cache=e??new o({stdTTL:this.ttl})}static getCacheKeys(e,t,n){let r=`${e}-${t}`;return{baseCacheKey:r,cacheKeys:n.map(e=>`${r}-${e}`)}}getUserPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`perm`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setUserPermissions(e,t){try{let n=t=>`perm-${e}-${t}`,r=Object.entries(t).map(([e,t])=>({key:n(e),val:t,ttl:this.ttl}));return this.cache.mset(r),{success:!0}}catch{return{success:!1}}}getCachedDeniedPermissions(t,n){try{let{baseCacheKey:r,cacheKeys:i}=e.getCacheKeys(`seen`,t,n),a=this.cache.mget(i),o={};return Object.entries(a).filter(([,e])=>e).forEach(([e,t])=>{let n=e.replace(`${r}-`,``);o[n]=t}),o}catch{return{}}}setCachedDeniedPermissions(e,t,n){try{let r=t=>`seen-${e}-${t}`,i=t.map(e=>({key:r(e),val:n,ttl:this.ttl}));return this.cache.mset(i),{success:!0}}catch{return{success:!1}}}},Z={AND:`and`,OR:`or`},Q={USER_NOT_FOUND:`USER_NOT_FOUND`,INSUFFICIENT_PERMISSIONS:`INSUFFICIENT_PERMISSIONS`,VALIDATION_ERROR:`VALIDATION_ERROR`,API_ERROR:`API_ERROR`,NO_REQUIRED_PERMISSIONS:`NO_REQUIRED_PERMISSIONS`,UNAUTHORIZED:`UNAUTHORIZED`,BAD_REQUEST:`BAD_REQUEST`,INTERNAL_ERROR:`INTERNAL_ERROR`},$={USER_NOT_FOUND:`User not found`,INSUFFICIENT_PERMISSIONS:`User does not have sufficient permissions`,VALIDATION_ERROR:`Validation error occurred`,API_ERROR:`API error occurred`,NO_REQUIRED_PERMISSIONS:`No required permissions provided for evaluation`,UNAUTHORIZED:`User is not authorized to perform this action`,BAD_REQUEST:`Bad request, please check the input parameters`,INTERNAL_ERROR:`Internal server error occurred while checking permissions`},oe=(e,t,n)=>{let r=new Set(e),i=t.filter(e=>r.has(e));return n===Z.AND?i.length===t.length:i.length>0},se=(e,t)=>{let n=Object.values(e);return t?n.every(e=>e.hasRequiredPermissions):n.some(e=>e.hasRequiredPermissions)},ce=e=>{let t=[];return e?.length||t.push(`contextIds cannot be empty`),t},le=(e,t,n,r)=>({isAuthorized:!1,error:e,resolvedPermissions:{},requiredPermissions:t,contextIds:n,...r&&{message:r}}),ue=(e,t,n,r)=>{let i={},a=new Map;if(r?.debug(`Start caching permissions results`,{userId:e,resolvedPermissions:t,requiredPermissions:n}),Object.entries(t).forEach(([e,t])=>{if(t?.permissions){let r=new Set(t.permissions),o=n.filter(e=>r.has(e)),s=n.filter(e=>!r.has(e));if(o.length&&(i[e]=o),s.length){let t=s.sort().join(`,`);a.has(t)||a.set(t,[]),a.get(t).push(e)}}}),Object.keys(i).length>0){let t=X.getUserPermissions(e,Object.keys(i));Object.keys(t??{}).forEach(e=>{i[e]&&(i[e]=Array.from(new Set([...t[e]||[],...i[e]])))}),r?.debug(`Caching granted permissions`,{userId:e,permissionsToCache:i,pastCache:t});let{success:n}=X.setUserPermissions(e,i);n||r?.error(`Failed to cache granted permissions`,{userId:e,permissionsToCache:i})}Array.from(a.entries()).map(([t,n])=>{let r=t.split(`,`);return X.setCachedDeniedPermissions(e,n,r)}),r?.debug(`Caching denied permissions`,{userId:e,deniedPermissionsGroups:Array.from(a.entries()).map(([e,t])=>({deniedPermissions:e.split(`,`),contextIds:t}))})},de=(e,t,n)=>{if(!e.length)return{isResolvableFromCache:!1,hasRequiredPermissions:!1};let r=new Set(e),i=t.filter(e=>r.has(e));return n===Z.AND?{isResolvableFromCache:i.length>0,hasRequiredPermissions:!1}:{isResolvableFromCache:i.length===t.length,hasRequiredPermissions:!1}},fe=(e,t,n)=>{let r=[...e],i=t.filter(e=>!n[e]);return r.push(...i),r},pe=(e,t,n,r)=>{let i={};return e.forEach(e=>{let a=t[e],o=de(a,n,r);o.isResolvableFromCache&&(i[e]={permissions:[],hasRequiredPermissions:o.hasRequiredPermissions})}),i},me=(e,t,n)=>{let r={};return Object.keys(e||{}).length&&Object.entries(e).forEach(([e,i])=>{let a=oe(i,t,n);a&&(r[e]={permissions:i,hasRequiredPermissions:a})}),r},he=async(e,t,n,r)=>{let i=(await y.post(`/api/v1/permissions/resolve`,{userId:e,contextIds:t,requiredPermissions:n},{timeout:r.timeout}))?.data||{};if(!Object.keys(i||{}).length)throw Error(`Failed to resolve permissions`);if(r.permissionsEvaluationOperator===Z.AND)return i;let a={};return Object.entries(i).forEach(([e,t])=>{let i=t?.permissions||[];a[e]={permissions:i,hasRequiredPermissions:oe(i,n,r.permissionsEvaluationOperator)}}),a},ge=(e,t,n,r)=>{let i=ce(e);return i.length?le(Q.VALIDATION_ERROR,t,e,i.join(`, `)):n?t?.length?null:(r?.info(`No requiredPermissions provided`,{userId:n,contextIds:e}),{isAuthorized:!1,userId:n,resolvedPermissions:{},requiredPermissions:t,contextIds:e,error:Q.NO_REQUIRED_PERMISSIONS}):(r?.warn(`User not found in context, cannot check permissions`,{contextIds:e,requiredPermissions:t}),le(Q.USER_NOT_FOUND,t,e))},_e=async(e,t,n,r,i)=>{let a={...i},o=await he(e,t,n,r);return a={...i,...o},ue(e,o,n),a},ve=async(e,t,n,r)=>{let{logger:i}=r,a=me(X.getUserPermissions(e,t),n,r.permissionsEvaluationOperator),o={...a},s=t.filter(e=>!a[e]),c=X.getCachedDeniedPermissions(e,s),l=s.filter(e=>!c[e]),u=s.filter(e=>c[e]),d=pe(u,c,n,r.permissionsEvaluationOperator);o={...o,...d};let f=fe(l,u,d);return f.length?_e(e,f,n,r,o):(i?.debug(`Final resolved permissions`,{userId:e,requiredPermissions:n,resolvedPermissions:o}),o)},ye=async({requiredPermissions:e,contextIds:t,logger:n,userId:r,options:{requireAll:i=!0,permissionsEvaluationOperator:a=Z.AND,timeout:o=1e4}})=>{let s=r||B()?.id||null,c=ge(t,e,s,n);if(c)return c;let l=await ve(s,t,e,{permissionsEvaluationOperator:a,timeout:o,logger:n}),u=se(l,i);return n?.info(`Resolved permissions`,{userId:s,requiredPermissions:e,resolvedPermissions:l,isAuthorized:u,requireAll:i,permissionsEvaluationOperator:a,contextIds:t}),{isAuthorized:u,userId:s,resolvedPermissions:l,requiredPermissions:e,contextIds:t,...u?{}:{error:Q.INSUFFICIENT_PERMISSIONS}}},be=e=>{let t=Object.keys(e.permissions?.contexts??{});return Array.from(new Set([...t])).filter(Boolean)},xe=(e,t,n,r,i,a,o,s,c)=>{if(r){s?.error(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),i.status(n).json({error:e,message:t,...c?.requiredPermissions&&{requiredPermissions:c.requiredPermissions}});return}s?.warn(t,{url:a.url,method:a.method,requiredPermissions:c?.requiredPermissions??[],...c}),o()},Se=(e,t={enforce:!1,requireAll:!0,permissionsEvaluationOperator:Z.AND})=>async(n,r,i)=>{try{let{logger:a,enforce:o,requireAll:s,permissionsEvaluationOperator:c}=t,l=(t,s,c)=>xe(t,s,c,o,r,n,i,a,{requiredPermissions:e}),u=n.user,d=be(u);if(!d?.length){l(Q.BAD_REQUEST,$.BAD_REQUEST,400);return}let f=await ye({requiredPermissions:e,contextIds:d,logger:a,userId:u.id,options:{requireAll:s??!0,permissionsEvaluationOperator:c??Z.AND,timeout:1e4,...a&&{logger:a}}});if(f.isAuthorized){a?.info(`User has required permissions`,{userId:u.id,requiredPermissions:e,contextIds:d,url:n.url,method:n.method}),i();return}if(!o){a?.warn(`User does not have required permissions, skipping enforcement`,{userId:u.id,requiredPermissions:e,contextIds:d,url:n.url,method:n.method}),i();return}r.status(403).json({error:Q.INSUFFICIENT_PERMISSIONS,message:$.INSUFFICIENT_PERMISSIONS,required:e,contexts:d,userId:f.userId})}catch(a){if(t.logger?.error(`Error in requirePermissions middleware`,{error:a,requiredPermissions:e,url:n.url,method:n.method}),!t.enforce){t.logger?.error(`Error during permission check, skipping enforcement`,{error:a,url:n.url,method:n.method}),i();return}r.status(500).json({error:Q.INTERNAL_ERROR,message:$.INTERNAL_ERROR})}};var Ce=e({default:()=>Ee,middlewares:()=>Te,sdk:()=>we});const we={evaluatePermissions:ye},Te={requirePermissions:Se};var Ee={sdk:we,middlewares:Te};const De=t.getCurrentContext,Oe=({outbreakOptions:e={},logger:n}={})=>{t.default({headersPrefix:`x-af`,contextMiddlewareGetter:n?.addContextMiddleware,...e})},ke=t.traceTypes,Ae=t.newTrace;var je={traceTypes:ke,newTrace:Ae,User:R,middleware:M,middlewareWithDecode:N,eagerLoadPermissionsMiddleware:F,getCurrentPayload:De,getDecodedBearer:I,checkFleetPermission:U,checkBusinessModelPermission:W,checkDemandSourcePermission:G,isUserExist:V,getUser:B,UnauthorizedAccessError:K,appMiddleware:P,createOrSetRabbitTrace:L,outbreak:t,AUTHORIZATION_METHODS:q,getAuthorizationHeader:Y,CONTEXTS_IDS_HEADER:S,authFromUserIdHeaderPlugin:z,permissions:Ce};export{q as AUTHORIZATION_METHODS,S as CONTEXTS_IDS_HEADER,K as UnauthorizedAccessError,R as User,P as appMiddleware,z as authFromUserIdHeaderPlugin,W as checkBusinessModelPermission,G as checkDemandSourcePermission,U as checkFleetPermission,L as createOrSetRabbitTrace,je as default,F as eagerLoadPermissionsMiddleware,Oe as enableTracing,Y as getAuthorizationHeader,De as getCurrentPayload,I as getDecodedBearer,ne as getRefreshTokenSecret,m as getTokenSecret,B as getUser,V as isUserExist,M as middleware,N as middlewareWithDecode,Ae as newTrace,t as outbreak,Ce as permissions,ke as traceTypes};
2
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["unixTime: moment.Moment","IdentityNetwork: Network","AutofleetApiNetwork: Network","MAX_CONTEXT_IDS: number","permissions: UserPayload","id?: string","accountType?: AccountType | undefined","contextIds?: string[]","eagerLoadPermissionsMiddleware: Asyncify<Handler>","newTrace","traceTypes","authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions>","user: ApiUser | null","result: EntityPermissions","permissionCache: PermissionCache","errors: string[]","permissionsToCache: PermissionsByContextId","result: PermissionsEvaluationByContextId","processedPermissions: PermissionsEvaluationByContextId","response: AxiosResponse<{ permissionsByContexts: PermissionsByContextId; }>","newResolvedPermissions: PermissionsEvaluationByContextId","contextIds: string[]","sdk: SdkExports","middlewares: MiddlewareExports","getCurrentPayload: typeof outbreak.getCurrentContext","traceTypes: typeof outbreak.traceTypes","newTrace: typeof outbreak.newTrace","zehutDefault: Default"],"sources":["../src/secret-getter.ts","../src/utils.ts","../src/services.ts","../src/user/ApiUser.ts","../src/app-auth.ts","../src/exceptions/appDoesNotExist.ts","../src/user/const.ts","../src/user/common.ts","../src/user/index.ts","../src/user/fastify.ts","../src/check-permission.ts","../src/errors.ts","../src/authorization.ts","../src/permissions/SDK/permissionCache.ts","../src/permissions/SDK/consts.ts","../src/permissions/SDK/evaluatePermissions.ts","../src/permissions/middleware/requirePermissions.ts","../src/permissions/index.ts","../src/index.ts"],"sourcesContent":["import jwt from 'jsonwebtoken';\nimport moment from 'moment';\n\nconst {\n DEPRECATED_JWT_SECRET, JWT_NEW_SECRET,\n DEPRECATED_REFRESH_JWT_SECRET, REFRESH_JWT_SECRET,\n DEPRECATION_UNIX_TIMESTAMP,\n} = process.env;\n\nconst getRelevantSecret = (token: string | undefined, deprecatedSecret: string, newSecret: string): string => {\n const deprecationTime = moment(parseInt(DEPRECATION_UNIX_TIMESTAMP || '', 10) * 1000);\n try {\n let unixTime: moment.Moment;\n if (token) {\n const { iat } = jwt.decode(token) as jwt.JwtPayload;\n unixTime = moment(iat! * 1000);\n } else {\n unixTime = moment();\n }\n return unixTime.isBefore(deprecationTime) ? deprecatedSecret : newSecret;\n } catch {\n return newSecret;\n }\n};\n\nexport const getRefreshTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_REFRESH_JWT_SECRET!, REFRESH_JWT_SECRET!);\nexport const getTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_JWT_SECRET!, JWT_NEW_SECRET!);\n","import type { UUID } from 'node:crypto';\nimport jwt from 'jsonwebtoken';\nimport { getTokenSecret } from './secret-getter';\n\ntype Context = Partial<Record<ContextProp | 'id', string>> & { subSystem?: SubSystemType; permissions?: string[]; entityId: string; };\n\nconst CONTEXT_PROPS = ['fleetId', 'businessModelId', 'demandSourceId'] as const;\ntype ContextProp = typeof CONTEXT_PROPS[number];\nconst CONTEXT_MAP_PROPS = {\n fleet: 'fleets',\n business: 'businessModels',\n demand: 'demandSources',\n} as const;\ntype SubSystemType = keyof typeof CONTEXT_MAP_PROPS;\ntype ContextSubSystemProp = typeof CONTEXT_MAP_PROPS[SubSystemType];\n\nexport const getAuthFromBearer = (bearer: string): string => bearer.replace('Bearer ', '');\n\nexport const decodeBearer = (bearer: string, appSecret?: string): any => {\n const token = getAuthFromBearer(bearer);\n const decoded = jwt.verify(token, appSecret || getTokenSecret(token));\n return decoded;\n};\n\nexport const parsePermissions = (contextId: string, decodedToken: { contexts: Context[]; }): { key: string; value: string; } | undefined => {\n if (!decodedToken) return undefined;\n const { contexts } = decodedToken;\n const activeContext = contexts.find(context => context.id === contextId);\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const permissionsValue = `${activeContext!.permissions?.map(cp => `${cp},`)}`;\n\n return {\n key: activeContext!.entityId,\n value: permissionsValue,\n };\n};\n\ntype EntitiesFromContext = Partial<Record<ContextSubSystemProp, Record<string, string>>>;\nexport const getEntitiesFromContext = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): EntitiesFromContext => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n\n const attributes: EntitiesFromContext = {};\n contexts.forEach((context) => {\n const prop = CONTEXT_MAP_PROPS[context.subSystem || 'business'];\n\n const permissions = parsePermissions(context.id!, decodedToken);\n if (!permissions) return;\n attributes[prop] ??= {};\n attributes[prop][permissions.key] = permissions.value;\n });\n\n return attributes;\n};\n\ntype ContextAttributes = Partial<Record<ContextProp, string[]>>;\nexport const getContextAttributes = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): ContextAttributes => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n const attributes: ContextAttributes = {};\n contexts.forEach((context) => {\n CONTEXT_PROPS.forEach((prop) => {\n if (context[prop]) {\n attributes[prop] ??= [];\n attributes[prop].push(context[prop]);\n }\n });\n });\n return attributes;\n};\n\nconst EMPTY_UUID = '00000000-0000-0000-0000-000000000000';\nconst FULL_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff';\nconst VALID_CHARS_REGEX = '[0-9a-f]';\nconst UUID_VERSION_REGEX = '[1-8]';\nconst UUID_REGEX = new RegExp(\n `^(?:${VALID_CHARS_REGEX}{8}-${VALID_CHARS_REGEX}{4}-${UUID_VERSION_REGEX}${VALID_CHARS_REGEX}{3}-[89ab]${VALID_CHARS_REGEX}{3}-${VALID_CHARS_REGEX}{12}|${EMPTY_UUID}|${FULL_UUID})$`,\n 'i',\n);\nexport function validateUUID(uuid: unknown): uuid is UUID {\n return typeof uuid === 'string' && UUID_REGEX.test(uuid);\n}\n","import Network from '@autofleet/network';\n\nconst CACHE_LIFETIME_IN_SEC = 10;\nconst apiGwUrl = process.env.API_GATEWAY_URL || 'https://api.autofleet.io';\n\nexport const IdentityNetwork: Network = new Network({\n serviceName: 'IDENTITY_MS',\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n\nexport const AutofleetApiNetwork: Network = new Network({\n baseURL: apiGwUrl,\n serviceUrl: apiGwUrl,\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n","import NodeCache from 'node-cache';\nimport objectHash from 'object-hash';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport { validateUUID } from '../utils';\nimport { AutofleetApiNetwork, IdentityNetwork } from '../services';\n\nexport type AccountType = 'client' | 'user' | 'service' | 'driver';\ntype EntityPermissions = Record<string, string[]>;\n\nexport const ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';\nexport const CONTEXTS_IDS_HEADER = 'x-af-context-ids';\nexport const MAX_CONTEXT_IDS: number = Number.parseInt(process.env.MAX_CONTEXT_IDS ?? `0`, 10) || 20;\n\nexport interface UserExternalData {\n element?: {\n internalUser: boolean;\n };\n [key: string]: any;\n}\n\nexport interface UserPayload {\n businessModels: EntityPermissions;\n fleets: EntityPermissions;\n demandSources: EntityPermissions;\n businessAccounts?: EntityPermissions;\n accountType?: AccountType;\n contexts?: EntityPermissions;\n createdAt?: string;\n externalData?: UserExternalData | null;\n}\n\nexport interface PartialUserPayload {\n businessModels?: EntityPermissions;\n fleets?: EntityPermissions;\n demandSources?: EntityPermissions;\n vehicles?: EntityPermissions;\n drivers?: EntityPermissions;\n businessAccounts?: EntityPermissions;\n}\n\nconst userCache = new NodeCache({ stdTTL: 10 });\n\nconst mergePermissions = (target: UserPayload | undefined, sources: Iterable<PartialUserPayload | undefined>): UserPayload => {\n const permissions: UserPayload = {\n ...target,\n fleets: { ...target?.fleets },\n businessModels: { ...target?.businessModels },\n demandSources: { ...target?.demandSources },\n // Clone other nested objects as needed\n };\n\n for (const source of sources) {\n (Object.entries(source ?? {}) as [Exclude<keyof UserPayload, 'accountType' | 'createdAt'>, EntityPermissions][]).forEach(([entityType, entityValue]) => {\n permissions[entityType] ??= {};\n Object.entries(entityValue).forEach(([entityId, perms]) => {\n permissions[entityType]![entityId] = (permissions[entityType]![entityId] || []).concat(perms);\n });\n });\n }\n\n return permissions;\n};\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\nif (typeof Symbol.asyncDispose !== 'symbol') {\n // Polyfill for asyncDispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L174-L183\n Object.defineProperty(Symbol, 'asyncDispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.asyncDispose'),\n writable: false,\n });\n}\n\nexport default class ApiUser {\n private privatePermissions: UserPayload | undefined;\n\n private readonly privateElevatedPermissionsHash = new Map<symbol, PartialUserPayload | undefined>();\n\n private privatePermissionsLegacy: any;\n\n private readonly appPermission: Record<string, any> = {};\n\n public readonly emptyUser: boolean;\n\n constructor(public id?: string, public accountType?: AccountType | undefined, elevatedPermissions?: PartialUserPayload, public contextIds?: string[]) {\n this.emptyUser = !!id;\n if (elevatedPermissions) {\n this.privateElevatedPermissionsHash.set(Symbol('initial'), elevatedPermissions);\n }\n }\n\n public async getUserPermissions(): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n });\n\n let data = userCache.get<UserPayload>(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get<UserPayload>(`/api/v1/users/${this.id}/authorization-payload`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.accountType = data.accountType;\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public async useCustomPermissionLoader(customPermissionLoader: (userId: string) => UserPayload | PromiseLike<UserPayload>): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n\n const cacheKey = this.id;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.privatePermissions = cachedResult;\n return cachedResult;\n }\n\n const data = await customPermissionLoader(this.id);\n userCache.set(cacheKey, data);\n\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public get businessModels(): string[] {\n return this.getUserProperty('businessModels');\n }\n\n public get fleets(): string[] {\n return this.getUserProperty('fleets');\n }\n\n public get demandSources(): string[] {\n return this.getUserProperty('demandSources');\n }\n\n private getUserProperty(key: keyof UserPayload): string[] {\n if (!this.privatePermissions) {\n throw new Error(`Cannot get ${key} without calling (async) getUserPermissions before`);\n }\n return Object.keys(this.privatePermissions[key] || {});\n }\n\n public get elevatedPermissions(): UserPayload {\n return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());\n }\n\n public get permissions(): UserPayload | undefined {\n if (!this.privatePermissions) {\n throw new Error('Cannot get permissions without calling (async) getUserPermissions before');\n }\n\n return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());\n }\n\n public elevatePermissions(addedPermissions: PartialUserPayload): (() => void) & { [Symbol.dispose]: () => void; } {\n // @itayankri is concerned about memory consumption, so create a symbol with no description, to avoid assigning memory for the description string\n const elevationId = Symbol();\n\n // Validate that the added permissions are valid UUIDs\n Object.values(addedPermissions).forEach((entityIds) => {\n Object.keys(entityIds).forEach((entityId) => {\n if (!validateUUID(entityId)) {\n throw new Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${entityId}`);\n }\n });\n });\n\n const currentUserTrace = getCurrentContext();\n if (!currentUserTrace) {\n throw new Error('Cannot find current user cross services trace');\n }\n\n const currentElevation = JSON.parse(currentUserTrace.context?.get(ELEVATED_PERMISSIONS_HEADER) as string | undefined || '{}');\n const newElevation = Object.assign(currentElevation, addedPermissions);\n this.privateElevatedPermissionsHash.set(elevationId, newElevation);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n const cleanup = () => {\n this.privateElevatedPermissionsHash.delete(elevationId);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n };\n cleanup[Symbol.dispose] = cleanup;\n return cleanup;\n }\n\n public async getUserPermissionsLegacy(): Promise<unknown> {\n if (!this.id) {\n return undefined;\n }\n if (this.privatePermissionsLegacy) {\n return this.privatePermissionsLegacy;\n }\n\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n legacy: true,\n });\n let data = userCache.get(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get(`/api/v1/users/${this.id}/authorization-payload-legacy`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.privatePermissionsLegacy = data;\n return this.privatePermissionsLegacy;\n }\n\n public get permissionsLegacy(): any {\n if (!this.privatePermissionsLegacy) {\n throw new Error('Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before');\n }\n return this.privatePermissionsLegacy;\n }\n\n public async getUserAppPermissions(appId: string, clientSecret: string): Promise<UserPayload | undefined> {\n if (!this.id || !appId || !clientSecret) {\n return undefined;\n }\n const currentAppPermission = this.appPermission[appId];\n\n if (currentAppPermission) {\n return currentAppPermission;\n }\n\n const cacheKey = `${this.id}:${appId}`;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.appPermission[appId] = cachedResult;\n return cachedResult;\n }\n\n const { data } = await AutofleetApiNetwork.post<UserPayload>(`/api/v1/apps/${appId}/get-user-payload`, {\n userId: this.id,\n }, {\n headers: {\n 'x-autofleet-apps-secret': clientSecret,\n },\n });\n\n userCache.set(cacheKey, data);\n this.appPermission[appId] = data;\n return this.appPermission[appId];\n }\n\n public extendRequiredContexts(contextIdsToAdd?: string[]): void {\n if (!Array.isArray(contextIdsToAdd)) {\n return;\n }\n\n const newContextIds = Array.from(new Set([...(this.contextIds ?? []), ...contextIdsToAdd]));\n\n if (newContextIds.length > MAX_CONTEXT_IDS) {\n return;\n }\n\n this.contextIds = newContextIds;\n const currentUserTrace = getCurrentContext();\n currentUserTrace?.context?.set(CONTEXTS_IDS_HEADER, this.contextIds.join(','));\n }\n}\n","import { AutofleetApiNetwork } from './services';\n\nexport const decodeAppBearer = async (bearer: string, appId: string): Promise<any> => {\n const { data: decoded } = await AutofleetApiNetwork.post('/api/v1/auth', { bearer, appId });\n return decoded;\n};\n\nexport const getClientSecret = async (appId: string): Promise<any> => {\n const { data: secret } = await AutofleetApiNetwork.get(`/api/v1/auth/client-secret/${appId}`);\n return secret;\n};\n","export default class AppDoesNotExist extends Error {\n name = 'AppDoesNotExist';\n\n message = 'app does not exist';\n}\n","export const IDENTITY_MS = 'identity-ms';\nexport const ACCESS_TOKEN = 'accessToken';\nexport const USER_OBJECT = 'userObject';\nexport const USER_TRACING_HEADER = 'x-af-user-id';\nexport const ORIGIN_HEADER = 'X-IAF-ORIGIN-SERVICE';\nexport const USER_PERMISSIONS_HEADER = 'x-af-user-permissions';\nexport const LOWER_CASE_ORIGIN_HEADER = ORIGIN_HEADER.toLowerCase() as Lowercase<typeof ORIGIN_HEADER>;\nexport const AUTOFLEET_APPS_SECRET_HEADER = 'x-autofleet-apps-secret';\n","import type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport ApiUser, { CONTEXTS_IDS_HEADER, ELEVATED_PERMISSIONS_HEADER, type UserPayload } from './ApiUser';\nimport {\n IDENTITY_MS,\n USER_OBJECT,\n USER_TRACING_HEADER,\n ORIGIN_HEADER,\n LOWER_CASE_ORIGIN_HEADER,\n} from './const';\n\nexport type CustomPermissionLoader = (userId: string) => Promise<UserPayload>;\nexport interface AuthFromUserIdHeaderOptions {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n customPermissionLoader?: CustomPermissionLoader;\n}\n\nexport const authFromUserIdHeader = async (options: AuthFromUserIdHeaderOptions, headers: IncomingHttpHeaders): Promise<ApiUser | undefined> => {\n const originHeader = headers[ORIGIN_HEADER] || headers[LOWER_CASE_ORIGIN_HEADER] || '';\n if (!Array.isArray(originHeader) && originHeader.toLowerCase() === IDENTITY_MS) {\n return undefined;\n }\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n customPermissionLoader,\n } = options;\n const userId = headers[USER_TRACING_HEADER] as string;\n if (!userId || Array.isArray(userId)) {\n return undefined;\n }\n\n const elevatedPermHeaderValue = headers[ELEVATED_PERMISSIONS_HEADER];\n const elevatedPermissionsFromHeader = elevatedPermHeaderValue?.length && elevatedPermHeaderValue.length > 0 ? JSON.parse(elevatedPermHeaderValue as string) : {};\n const contextIds = (headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n\n const userObject = new ApiUser(userId, 'user', elevatedPermissionsFromHeader, contextIds);\n if (eagerLoadUserPermissions) {\n if (customPermissionLoader) {\n await userObject.useCustomPermissionLoader(customPermissionLoader);\n } else {\n await userObject.getUserPermissions();\n }\n }\n\n if (eagerLoadUserPermissionsLegacy) {\n await userObject.getUserPermissionsLegacy();\n }\n\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n return userObject;\n};\n","import type { Handler, Request } from 'express';\nimport { getCurrentContext, newTrace, traceTypes } from '@autofleet/outbreak';\nimport jwt from 'jsonwebtoken';\nimport ApiUser, { CONTEXTS_IDS_HEADER, MAX_CONTEXT_IDS } from './ApiUser';\nimport { decodeAppBearer } from '../app-auth';\nimport AppDoesNotExist from '../exceptions/appDoesNotExist';\nimport { decodeBearer, getAuthFromBearer } from '../utils';\nimport {\n ACCESS_TOKEN,\n USER_OBJECT,\n USER_TRACING_HEADER,\n USER_PERMISSIONS_HEADER,\n AUTOFLEET_APPS_SECRET_HEADER,\n} from './const';\nimport { authFromUserIdHeader, type AuthFromUserIdHeaderOptions } from './common';\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n user: ApiUser;\n }\n}\n\ntype Asyncify<T extends (...a: any[]) => any> = (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;\n\nexport const middleware = (options: AuthFromUserIdHeaderOptions = {}): Asyncify<Handler> => async (req, res, next): Promise<any> => {\n try {\n const userObject = await authFromUserIdHeader(options, req.headers);\n if (userObject) {\n req.user = userObject;\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n }\n\n next();\n } catch {\n res.status(401).json({ error: 'cannot authenticate user' });\n }\n};\n\nexport const middlewareWithDecode = (options: {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n returnErrorIfNoToken?: boolean;\n appSecret?: string;\n} = {}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n returnErrorIfNoToken,\n appSecret,\n } = options;\n let decoded;\n if (req.headers.authorization) {\n try {\n decoded = await decodeBearer(req.headers.authorization, appSecret);\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n } else if (e instanceof jwt.JsonWebTokenError) {\n res.status(400).json({ errors: [e.message] });\n } else {\n res.status(500).json({ errors: ['Server error while parsing token'] });\n }\n return;\n }\n const userId = decoded?.user?.id;\n\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const contextIds = (req.headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n const userObject = new ApiUser(userId, decoded?.user?.accountType, undefined, contextIds);\n\n if (eagerLoadUserPermissions || eagerLoadUserPermissionsLegacy) {\n await Promise.all([\n eagerLoadUserPermissions && userObject.getUserPermissions(),\n eagerLoadUserPermissionsLegacy && userObject.getUserPermissionsLegacy(),\n ]);\n }\n\n req.user = userObject;\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n } else if (returnErrorIfNoToken) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n next();\n};\n\nexport const appMiddleware = (options: {\n appId: string;\n clientSecret: string;\n}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n appId,\n clientSecret,\n } = options;\n let decoded;\n\n if (!req.headers.authorization) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n\n try {\n decoded = await decodeAppBearer(req.headers.authorization, appId);\n if (!decoded) {\n throw new AppDoesNotExist();\n }\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n return;\n }\n if ([jwt.JsonWebTokenError, AppDoesNotExist].some(Err => e instanceof Err)) {\n res.status(400).json({ errors: [(e as Error).message] });\n return;\n }\n res.status(500).json({ errors: ['Server error while parsing token'] });\n return;\n }\n const userId = decoded?.userId;\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const userObject = new ApiUser(userId);\n\n if (appId) {\n req.headers[AUTOFLEET_APPS_SECRET_HEADER] = clientSecret;\n // Won't work until we find a better solution for identity ms\n await userObject.getUserAppPermissions(appId, clientSecret);\n }\n\n req.user = userObject;\n const currentTraceContext = getCurrentContext().nonHeaderContext;\n currentTraceContext?.set(USER_OBJECT, userObject);\n currentTraceContext?.set(ACCESS_TOKEN, getAuthFromBearer(req.headers.authorization));\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n\n next();\n};\n\nexport const eagerLoadPermissionsMiddleware: Asyncify<Handler> = async (req, res, next) => {\n await req.user.getUserPermissions();\n next();\n};\n\nexport const getDecodedBearer = (req: Request, appSecret?: string): any => {\n if (!req.headers.authorization) {\n return null;\n }\n return decodeBearer(req.headers.authorization, appSecret);\n};\n\nexport const createOrSetRabbitTrace = async (trace: ReturnType<typeof newTrace> | undefined, userId: string | undefined, contextIds?: string[]): Promise<void> => {\n const addContextIds = Array.isArray(contextIds) && contextIds.length <= MAX_CONTEXT_IDS;\n const userObject = new ApiUser(\n userId,\n undefined,\n undefined,\n addContextIds ? contextIds : undefined,\n );\n\n await userObject.getUserPermissions();\n trace ??= newTrace(traceTypes.RABBIT);\n trace.nonHeaderContext.set(USER_OBJECT, userObject);\n\n if (addContextIds) {\n trace.context.set(CONTEXTS_IDS_HEADER, contextIds?.join(',') || '');\n }\n};\n\nexport default ApiUser;\n","import type { FastifyPluginCallback } from 'fastify';\nimport type ApiUser from './ApiUser';\nimport { type AuthFromUserIdHeaderOptions, authFromUserIdHeader } from './common';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n user?: ApiUser;\n }\n}\n\nexport const authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions> = (fastify, options, done) => {\n fastify.decorateRequest('user', undefined);\n fastify.addHook('onRequest', async (request, reply) => {\n try {\n const user = await authFromUserIdHeader(options, request.headers);\n if (user) {\n request.user = user;\n }\n } catch {\n reply.status(401).send({ error: 'cannot authenticate user' });\n }\n });\n\n done();\n};\nObject.defineProperty(authFromUserIdHeaderPlugin, Symbol.for('skip-override'), { value: true }); // Prevent Fastify from overriding the plugin\n","import { getCurrentContext } from '@autofleet/outbreak';\nimport { USER_OBJECT } from './user/const';\nimport type ApiUser from './user/ApiUser';\nimport type { UserPayload } from './user/ApiUser';\n\nexport const getUser = (): ApiUser | undefined => getCurrentContext().nonHeaderContext?.get(USER_OBJECT) as ApiUser | undefined;\n\nexport const isUserExist = (): string | undefined => getUser()?.id;\n\nconst checkUserPermissions = (\n entityId: string,\n entityType: Exclude<keyof UserPayload, 'accountType' | 'createdAt'>,\n) => !isUserExist() || Object.hasOwn(getUser()!.permissions?.[entityType] ?? {}, entityId);\n\nexport const checkFleetPermission = (fleetId: string): boolean => checkUserPermissions(fleetId, 'fleets');\nexport const checkBusinessModelPermission = (businessModelId: string): boolean => checkUserPermissions(businessModelId, 'businessModels');\nexport const checkDemandSourcePermission = (demandSourceId: string): boolean => checkUserPermissions(demandSourceId, 'demandSources');\n","import type ApiUser from './user';\n\nexport class UnauthorizedAccessError extends Error {\n constructor(public user: ApiUser | null = null, message = 'UnauthorizedAccessError') {\n super(message);\n this.name = 'UnauthorizedAccessError';\n }\n}\n","import jwt from 'jsonwebtoken';\n\nexport const AUTHORIZATION_METHODS = {\n NONE: 'NONE',\n BASIC: 'BASIC',\n JWT: 'JWT',\n};\n\nconst AUTHORIZATION_ACTIONS = {\n [AUTHORIZATION_METHODS.NONE]: () => undefined,\n [AUTHORIZATION_METHODS.BASIC]: (authorizationSettings: any) => {\n const { username, password } = authorizationSettings;\n const encodedCredentials = Buffer.from(`${username}:${password}`).toString('base64');\n return `Basic ${encodedCredentials}`;\n },\n [AUTHORIZATION_METHODS.JWT]: (authorizationSettings: any) => {\n const { secret } = authorizationSettings;\n if (secret) {\n return `Bearer ${jwt.sign({}, secret, { expiresIn: 10 })}`;\n }\n return undefined;\n },\n};\n\nexport const getAuthorizationHeader = (authorizationSettings: { method: string; } | undefined): string | undefined => {\n const authorizationMethod = authorizationSettings?.method;\n\n if (!authorizationMethod || !AUTHORIZATION_ACTIONS[authorizationMethod]) {\n return undefined;\n }\n\n return AUTHORIZATION_ACTIONS[authorizationMethod](authorizationSettings);\n};\n","import type { Permission } from '@autofleet/common-types/lib/identity';\nimport NodeCache from 'node-cache';\n\ntype EntityPermissions = Record<string, Permission[]>;\n\nexport class PermissionCache {\n private cache: NodeCache;\n\n private ttl = 10;\n\n /**\n * Creates a new PermissionCache instance\n * @param cache - Optional NodeCache instance. If not provided, creates a new one with default TTL of 10 seconds\n */\n constructor(cache?: NodeCache, ttl?: number) {\n if (ttl) {\n this.ttl = ttl;\n }\n this.cache = cache ?? new NodeCache({ stdTTL: this.ttl });\n }\n\n private static getCacheKeys(cacheName: string, userId: string, contextIds: string[]) {\n const baseCacheKey = `${cacheName}-${userId}`;\n const cacheKeys = contextIds.map(contextId => `${baseCacheKey}-${contextId}`);\n\n return { baseCacheKey, cacheKeys };\n }\n\n /**\n * Retrieves user permissions for multiple contexts from cache\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to fetch permissions for\n * @returns An object mapping context IDs to permission arrays\n */\n public getUserPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('perm', userId, contextIds);\n const cacheResult = this.cache.mget<Permission[]>(cacheKeys);\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Stores user permissions for multiple contexts in cache\n * @param userId - The user identifier\n * @param PermissionsByContextId - Object mapping context IDs to permission arrays\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setUserPermissions(userId: string, PermissionsByContextId: EntityPermissions): { success: boolean; } {\n try {\n const cacheKey = (contextId: string) => `perm-${userId}-${contextId}`;\n\n const cacheEntries = Object.entries(PermissionsByContextId).map(([contextId, permissions]) => ({\n key: cacheKey(contextId),\n val: permissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n /**\n * Retrieves denied permissions status for multiple contexts from cache.\n * \"Denied\" permissions indicate that a user has already been evaluated for a specific set of\n * required permissions in a given context, avoiding redundant permission checks.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to check\n * @returns An object mapping context IDs to arrays of denied permissions\n */\n public getCachedDeniedPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('seen', userId, contextIds);\n const cacheResult = this.cache.mget<Permission[]>(cacheKeys);\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Marks permissions as seen for multiple contexts in cache.\n * \"Seen\" permissions are used to track that a user has already been evaluated for a specific\n * set of required permissions in given contexts, preventing duplicate permission evaluations.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to mark as seen\n * @param deniedPermissions - Array of permissions that were evaluated and denied\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setCachedDeniedPermissions(userId: string, contextIds: string[], deniedPermissions: string[]): { success: boolean; } {\n try {\n const seenKey = (contextId: string) => `seen-${userId}-${contextId}`;\n\n const cacheEntries = contextIds.map(contextId => ({\n key: seenKey(contextId),\n val: deniedPermissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n}\n\nexport const permissionCache: PermissionCache = new PermissionCache();\n","export const PERMISSION_ERROR_TYPES = {\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n API_ERROR: 'API_ERROR',\n NO_REQUIRED_PERMISSIONS: 'NO_REQUIRED_PERMISSIONS',\n UNAUTHORIZED: 'UNAUTHORIZED',\n BAD_REQUEST: 'BAD_REQUEST',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n};\n\nexport const PERMISSION_ERROR_MESSAGES = {\n USER_NOT_FOUND: 'User not found',\n INSUFFICIENT_PERMISSIONS: 'User does not have sufficient permissions',\n VALIDATION_ERROR: 'Validation error occurred',\n API_ERROR: 'API error occurred',\n NO_REQUIRED_PERMISSIONS: 'No required permissions provided for evaluation',\n UNAUTHORIZED: 'User is not authorized to perform this action',\n BAD_REQUEST: 'Bad request, please check the input parameters',\n INTERNAL_ERROR: 'Internal server error occurred while checking permissions',\n};\n","import { getUser } from '../../check-permission';\nimport { IdentityNetwork } from '../../services';\nimport type {\n EvaluatePermissionsParams,\n PermissionsEvaluationByContextId,\n PermissionCheckResult,\n Logger,\n PermissionsByContextId,\n ResolvePermissionsParams,\n} from './types';\nimport type { PermissionErrorType } from './errors';\nimport { permissionCache } from './permissionCache';\nimport { PERMISSION_ERROR_TYPES } from './consts';\nimport type { Permission, RequiredPermission } from '@autofleet/common-types/lib/identity';\nimport type { AxiosResponse } from 'axios';\n\n/**\n * Evaluates whether user permissions meet requirements\n * @param permissions - User's actual permissions\n * @param requiredPermissions - Required permissions to check against\n * @returns true if permissions meet requirements\n */\nconst getHasRequiredPermissions = (\n permissions: Permission[],\n requiredPermissions: RequiredPermission[],\n): boolean => {\n const permissionsSet = new Set(permissions);\n\n return requiredPermissions.some((requiredPermission) => {\n if (typeof requiredPermission === 'string') return permissionsSet.has(requiredPermission);\n\n return requiredPermission.every(nestedRequiredPermission => permissionsSet.has(nestedRequiredPermission));\n });\n};\n\n/**\n * Checks if permission requirements are met across all contexts\n * @param permissionsEvaluationByContextId - {@link PermissionsEvaluationByContextId} Permission evaluation results by context\n * @param requireAll - Whether all contexts must satisfy requirements (true) or just one (false)\n * @returns true if requirements are met\n */\nexport const checkAuthorizeRequirements = (permissionsEvaluationByContextId: PermissionsEvaluationByContextId, requireAll: boolean): boolean => {\n const permissionsEvaluationValues = Object.values(permissionsEvaluationByContextId);\n\n if (requireAll) {\n return permissionsEvaluationValues.every(evaluation => evaluation.hasRequiredPermissions);\n }\n return permissionsEvaluationValues.some(evaluation => evaluation.hasRequiredPermissions);\n};\n\nconst validateInput = (contextIds: string[]): string[] => {\n const errors: string[] = [];\n\n if (!contextIds?.length) {\n errors.push('contextIds cannot be empty');\n }\n\n return errors;\n};\n\nconst createErrorResult = (\n error: PermissionErrorType,\n requiredPermissions: RequiredPermission[],\n contextIds: string[],\n message?: string,\n): PermissionCheckResult => ({\n isAuthorized: false,\n error,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n ...(message && { message }),\n});\n\n/**\n * Caches permission results based on whether they meet requirements\n * - Caches granted permissions directly\n * - Groups denied permissions by identical sets to optimize cache storage\n * @param userId - User ID\n * @param resolvedPermissions - Permissions evaluation results to cache\n * @param requiredPermissions - Required permissions used for evaluation\n */\nconst cachePermissionsResults = (\n userId: string,\n resolvedPermissions: PermissionsEvaluationByContextId,\n requiredPermissions: RequiredPermission[],\n logger?: Logger,\n): void => {\n const permissionsToCache: PermissionsByContextId = {};\n const deniedPermissionsMap = new Map<string, string[]>();\n\n logger?.debug('Start caching permissions results', {\n userId,\n resolvedPermissions,\n requiredPermissions,\n });\n\n Object.entries(resolvedPermissions).forEach(([contextId, evaluation]) => {\n if (evaluation?.permissions) {\n const userPermissionsSet = new Set(evaluation.permissions);\n const effectivePermissions = requiredPermissions.flat().filter(permission => userPermissionsSet.has(permission));\n const deniedPermissions = requiredPermissions.flat().filter(permission => !userPermissionsSet.has(permission));\n\n if (effectivePermissions.length) {\n // Store only effective permissions (intersection of user and required permissions)\n permissionsToCache[contextId] = effectivePermissions;\n }\n\n if (deniedPermissions.length) {\n // Calculate which required permissions were denied by filtering out the ones the user has\n const deniedPermissionsKey = deniedPermissions.sort().join(',');\n\n if (!deniedPermissionsMap.has(deniedPermissionsKey)) {\n deniedPermissionsMap.set(deniedPermissionsKey, []);\n }\n deniedPermissionsMap.get(deniedPermissionsKey)!.push(contextId);\n }\n }\n });\n\n if (Object.keys(permissionsToCache).length > 0) {\n const pastCache = permissionCache.getUserPermissions(userId, Object.keys(permissionsToCache));\n\n Object.keys(pastCache ?? {}).forEach((contextId) => {\n if (permissionsToCache[contextId]) {\n // Merge with any previously cached permissions to avoid overwriting\n const mergedPermissions = Array.from(new Set([...(pastCache[contextId] || []), ...permissionsToCache[contextId]]));\n permissionsToCache[contextId] = mergedPermissions;\n }\n });\n\n logger?.debug('Caching granted permissions', { userId, permissionsToCache, pastCache });\n const { success } = permissionCache.setUserPermissions(userId, permissionsToCache);\n if (!success) {\n logger?.error('Failed to cache granted permissions', { userId, permissionsToCache });\n }\n }\n\n // Cache denied permissions grouped by the same denied permissions to utilize mset efficiently\n Array.from(deniedPermissionsMap.entries()).map(([deniedPermissionsKey, contextIds]) => {\n const deniedPermissions = deniedPermissionsKey.split(',');\n return permissionCache.setCachedDeniedPermissions(userId, contextIds, deniedPermissions);\n });\n\n logger?.debug('Caching denied permissions', {\n userId,\n deniedPermissionsGroups: Array.from(deniedPermissionsMap.entries()).map(([key, contexts]) => ({\n deniedPermissions: key.split(','),\n contextIds: contexts,\n })),\n });\n};\n\n/**\n * Determines if a context can be resolved from denied permissions cache\n * @param deniedPermissions - Previously denied permissions for the context\n * @param requiredPermissions - Currently required permissions\n * @returns Object indicating if context is resolvable from cache and the result\n */\nconst isResolvableFromDeniedCache = (\n deniedPermissions: string[],\n requiredPermissions: RequiredPermission[],\n): { isResolvableFromCache: boolean; hasRequiredPermissions: boolean; } => {\n if (!deniedPermissions.length) {\n return { isResolvableFromCache: false, hasRequiredPermissions: false };\n }\n\n const deniedSet = new Set(deniedPermissions);\n\n // For OR: only if ALL required permissions were denied, we can resolve as false\n const areAllRequiredDenied = requiredPermissions.every((requiredPermission) => {\n if (typeof requiredPermission === 'string') return deniedSet.has(requiredPermission);\n\n // For AND: if any required permission was denied, we can resolve as false\n return requiredPermission.some(nestedRequiredPermission => deniedSet.has(nestedRequiredPermission));\n });\n\n return { isResolvableFromCache: areAllRequiredDenied, hasRequiredPermissions: false };\n};\n\n/**\n * Gets contexts that need API calls (unseen + partially resolvable seen contexts)\n * @param unseenContextIds - Context IDs that have no cached data\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedContextEntries - Context IDs that were fully resolved from cache\n * @returns Array of context IDs that need API calls\n */\nconst getContextsNeedingAPI = (\n unseenContextIds: string[],\n deniedContextIds: string[],\n deniedContextEntries: PermissionsEvaluationByContextId,\n): string[] => {\n const contextIdsNeedingAPI = [...unseenContextIds];\n\n // Add denied contexts that couldn't be resolved from cache\n const unresolvedDeniedContextIds = deniedContextIds.filter(contextId => !deniedContextEntries[contextId]);\n contextIdsNeedingAPI.push(...unresolvedDeniedContextIds);\n\n return contextIdsNeedingAPI;\n};\n\n/**\n * Processes denied contexts that can be resolved from denied permissions cache\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedPermissions - Denied permissions cache data\n * @param requiredPermissions - Currently required permissions\n * @returns Object with cache-resolvable contexts and their evaluations\n */\nconst processCachedDeniedContexts = (\n deniedContextIds: string[],\n seenPermissions: PermissionsByContextId,\n requiredPermissions: RequiredPermission[],\n): PermissionsEvaluationByContextId => {\n const result: PermissionsEvaluationByContextId = {};\n\n deniedContextIds.forEach((contextId) => {\n const deniedPermissions = seenPermissions[contextId];\n const cacheResult = isResolvableFromDeniedCache(deniedPermissions, requiredPermissions);\n\n if (cacheResult.isResolvableFromCache) {\n result[contextId] = {\n permissions: [],\n hasRequiredPermissions: cacheResult.hasRequiredPermissions,\n };\n }\n });\n\n return result;\n};\n\n/**\n * Processes cached permissions into evaluation format\n * @param cachedPermissions - Raw permissions from cache\n * @param requiredPermissions - Required permissions to evaluate against\n * @returns Processed permissions evaluation\n */\nconst processCachedPermissions = (\n cachedPermissions: PermissionsByContextId,\n requiredPermissions: RequiredPermission[],\n): PermissionsEvaluationByContextId => {\n const processedPermissions: PermissionsEvaluationByContextId = {};\n\n if (Object.keys(cachedPermissions || {}).length) {\n Object.entries(cachedPermissions).forEach(([contextId, permissions]) => {\n const hasRequiredPermissions = getHasRequiredPermissions(permissions, requiredPermissions);\n\n // Only include contexts that can be fully resolved from cache\n // Since we only cache effective permissions we won't have all of the user's permissions to fully resolve\n if (hasRequiredPermissions) {\n processedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n }\n });\n }\n\n return processedPermissions;\n};\n\n/**\n * Creates empty permissions result for cases with no required permissions\n * @param contextIds - Context IDs to create results for\n * @returns {@link PermissionsEvaluationByContextId} with empty permissions and granted access\n */\nconst createEmptyPermissionsResult = (contextIds: string[]): PermissionsEvaluationByContextId => Object.fromEntries(\n contextIds.map(contextId => [\n contextId,\n { permissions: [], hasRequiredPermissions: true },\n ]),\n);\n\n/**\n * Calls the Identity API and resolves permissions for unseen contexts\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve\n * @param requiredPermissions - Required permissions\n * @param options - API call options\n * @returns Resolved permissions from API\n */\nexport const resolvePermissions = async ({\n userId,\n contextIds,\n requiredPermissions,\n options,\n}: ResolvePermissionsParams): Promise<PermissionsEvaluationByContextId> => {\n if (!requiredPermissions.length) {\n return createEmptyPermissionsResult(contextIds);\n }\n\n const response: AxiosResponse<{ permissionsByContexts: PermissionsByContextId; }> = await IdentityNetwork.post(`/api/v1/permissions/users/${userId}`,\n { userId, contextIds },\n { timeout: options.timeout },\n );\n\n if (!Object.keys(response?.data?.permissionsByContexts || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n return Object.fromEntries(\n Object.entries(response.data.permissionsByContexts).map(([contextId, permissions]) => [\n contextId,\n {\n permissions,\n hasRequiredPermissions: getHasRequiredPermissions(permissions, requiredPermissions),\n },\n ]),\n );\n};\n\n/**\n * Validates user context and required permissions for evaluation\n * @param contextIds - Context IDs to validate\n * @param requiredPermissions - Required permissions to validate\n * @param userId - User ID to validate\n * @param logger - Logger instance for warnings\n * @returns Error result if validation fails, null if validation passes\n */\nconst validateEvaluationInput = (\n contextIds: string[],\n requiredPermissions: RequiredPermission[],\n userId: string | null,\n logger?: Logger,\n): PermissionCheckResult | null => {\n const validationErrors = validateInput(contextIds);\n if (validationErrors.length) {\n return createErrorResult(PERMISSION_ERROR_TYPES.VALIDATION_ERROR, requiredPermissions, contextIds, validationErrors.join(', '));\n }\n\n if (!userId) {\n logger?.warn('User not found in context, cannot check permissions', {\n contextIds,\n requiredPermissions,\n });\n return createErrorResult(PERMISSION_ERROR_TYPES.USER_NOT_FOUND, requiredPermissions, contextIds);\n }\n\n if (!requiredPermissions?.length) {\n logger?.info('No requiredPermissions provided', {\n userId,\n contextIds,\n });\n return {\n isAuthorized: false,\n userId,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n error: PERMISSION_ERROR_TYPES.NO_REQUIRED_PERMISSIONS,\n };\n }\n\n return null;\n};\n\n/**\n * Resolves permissions from Identity API and combines with existing resolved permissions\n * @param userId - User ID\n * @param contextIdsNeedingAPI - Context IDs that need API resolution\n * @param requiredPermissions - Required permissions to check\n * @param options - API call options {@link ResolvePermissionsParams.options}\n * @param resolvedPermissions - Already resolved permissions from cache and denied cache\n * @returns Combined permissions including those resolved from API\n */\nconst resolvePermissionsFromAPI = async (\n userId: string,\n contextIdsNeedingAPI: string[],\n requiredPermissions: RequiredPermission[],\n options: { timeout: number; },\n resolvedPermissions: PermissionsEvaluationByContextId,\n): Promise<PermissionsEvaluationByContextId> => {\n let newResolvedPermissions: PermissionsEvaluationByContextId = { ...resolvedPermissions };\n // Call Identity API for unresolved contexts\n const resolvedPermissionsFromIdentityMS = await resolvePermissions({\n userId,\n contextIds: contextIdsNeedingAPI,\n requiredPermissions,\n options,\n });\n\n // Combine all resolved permissions\n newResolvedPermissions = {\n ...resolvedPermissions,\n ...resolvedPermissionsFromIdentityMS,\n };\n\n cachePermissionsResults(userId, resolvedPermissionsFromIdentityMS, requiredPermissions);\n\n return newResolvedPermissions;\n};\n\n/**\n * Resolves permissions from multiple sources (cache, seen contexts, API)\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve permissions for\n * @param requiredPermissions - Required permissions to check\n * @param options - Evaluation options\n * @returns Combined permissions from all sources: cache, denied cache, and API\n */\nconst resolveAllPermissions = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: RequiredPermission[],\n options: {\n timeout: number;\n logger?: Logger;\n },\n): Promise<PermissionsEvaluationByContextId> => {\n const { logger } = options;\n // Cached allowed permissions\n const cachedAllowedPermissions = permissionCache.getUserPermissions(userId, contextIds);\n const resolvedPermissionsFromCache = processCachedPermissions(\n cachedAllowedPermissions,\n requiredPermissions,\n );\n let resolvedPermissions = { ...resolvedPermissionsFromCache };\n\n // Context ids without allowed permissions in cache\n const contextIdsWithoutAllowedCache = contextIds.filter(contextId => !resolvedPermissionsFromCache[contextId]);\n\n // Handle denied cache permissions\n const cachedDeniedPermissions = permissionCache.getCachedDeniedPermissions(userId, contextIdsWithoutAllowedCache);\n\n // Context ids that have never been seen before (not in allowed or denied cache)\n const unseenContextIds = contextIdsWithoutAllowedCache.filter(contextId => !cachedDeniedPermissions[contextId]);\n\n // Process denied permissions cache\n const deniedContextIds = contextIdsWithoutAllowedCache.filter(contextId => cachedDeniedPermissions[contextId]);\n const deniedContextEntries = processCachedDeniedContexts(\n deniedContextIds,\n cachedDeniedPermissions,\n requiredPermissions,\n );\n\n // Combine resolved permissions with those resolved from denied cache\n resolvedPermissions = { ...resolvedPermissions, ...deniedContextEntries };\n\n // Resolve the remaining contexts using the Identity API\n const contextIdsNeedingAPI = getContextsNeedingAPI(unseenContextIds, deniedContextIds, deniedContextEntries);\n if (contextIdsNeedingAPI.length) {\n // Call Identity API for unresolved contexts\n return resolvePermissionsFromAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n resolvedPermissions,\n );\n }\n\n logger?.debug('Final resolved permissions', {\n userId,\n requiredPermissions,\n resolvedPermissions,\n });\n\n return resolvedPermissions;\n};\n\n/**\n * Main SDK function to evaluate user permissions\n * Checks both cached and API-resolved permissions to determine access\n * @param params - {@link EvaluatePermissionsParams}\n * @returns {@link PermissionCheckResult} Detailed permission check result with access status and context\n */\nexport const evaluatePermissions = async ({\n requiredPermissions,\n contextIds,\n logger,\n userId,\n options: {\n requireAll = true,\n timeout = 10000,\n },\n}: EvaluatePermissionsParams): Promise<PermissionCheckResult> => {\n const resolvedUserId = userId || getUser()?.id || null;\n\n const validationError = validateEvaluationInput(contextIds, requiredPermissions, resolvedUserId, logger);\n if (validationError) {\n return validationError;\n }\n\n const resolvedPermissions = await resolveAllPermissions(\n resolvedUserId!,\n contextIds,\n requiredPermissions,\n { timeout, logger },\n );\n\n const isAuthorized = checkAuthorizeRequirements(resolvedPermissions, requireAll);\n\n logger?.info('Resolved permissions', {\n userId: resolvedUserId,\n requiredPermissions,\n resolvedPermissions,\n isAuthorized,\n requireAll,\n contextIds,\n });\n\n return {\n isAuthorized,\n userId: resolvedUserId!,\n resolvedPermissions,\n requiredPermissions,\n contextIds,\n ...(isAuthorized ? {} : { error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS }),\n };\n};\n\nexport default evaluatePermissions;\n","import type { Request, Response, NextFunction } from 'express';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { evaluatePermissions } from '../SDK/evaluatePermissions';\nimport type { EvaluatePermissionsOpts } from '../SDK/types';\nimport type ApiUser from '../../user/ApiUser';\nimport { PERMISSION_ERROR_MESSAGES, PERMISSION_ERROR_TYPES } from '../SDK/consts';\nimport type { RequiredPermission } from '@autofleet/common-types/lib/identity';\n\nexport interface RequirePermissionsOptions extends EvaluatePermissionsOpts {\n logger?: LoggerInstanceManager;\n}\nconst getContextIds = (user: ApiUser): string[] => {\n const contexts = Object.keys(user.permissions?.contexts ?? {});\n return Array.from(new Set([\n ...contexts,\n ])).filter(Boolean);\n};\n\nconst handleError = (\n errorLabel: string,\n message: string,\n code: number,\n enforce: boolean | undefined,\n res: Response,\n req: Request,\n next: NextFunction,\n logger?: LoggerInstanceManager,\n additionalInfo?: { requiredPermissions?: RequiredPermission[]; },\n) => {\n if (enforce) {\n logger?.error(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n\n res.status(code).json({\n error: errorLabel,\n message,\n ...(additionalInfo?.requiredPermissions && { requiredPermissions: additionalInfo.requiredPermissions }),\n });\n return;\n }\n\n logger?.warn(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n next();\n};\n\n/**\n * Express middleware that requires specific permissions for route access\n *\n * @param requiredPermissions - Array of permissions required to access the route\n * @param options - Configuration options for permission checking\n * @returns Express middleware function\n */\nexport const requirePermissions = (\n requiredPermissions: RequiredPermission[],\n options: RequirePermissionsOptions = {\n enforce: false,\n requireAll: true,\n },\n) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n const {\n logger,\n enforce,\n requireAll,\n } = options;\n\n const handleErrorWrapper = (errorLabel: string, message: string, code: number) => handleError(\n errorLabel,\n message,\n code,\n enforce,\n res,\n req,\n next,\n logger,\n { requiredPermissions },\n );\n const user = req.user;\n\n const contextIds: string[] = getContextIds(user);\n\n if (!contextIds?.length) {\n handleErrorWrapper(\n PERMISSION_ERROR_TYPES.BAD_REQUEST,\n PERMISSION_ERROR_MESSAGES.BAD_REQUEST,\n 400,\n );\n return;\n }\n\n // Evaluate permissions using SDK\n const result = await evaluatePermissions({\n requiredPermissions,\n contextIds,\n logger,\n userId: user.id,\n options: {\n requireAll: requireAll ?? true,\n timeout: 10000,\n ...(logger && { logger }),\n },\n });\n\n if (result.isAuthorized) {\n logger?.info('User has required permissions', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n if (!enforce) {\n logger?.warn('User does not have required permissions, skipping enforcement', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(403).json({\n error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS,\n message: PERMISSION_ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS,\n required: requiredPermissions,\n contexts: contextIds,\n userId: result.userId,\n });\n } catch (error) {\n const requestLogger = options.logger;\n requestLogger?.error('Error in requirePermissions middleware', {\n error,\n requiredPermissions,\n url: req.url,\n method: req.method,\n });\n\n if (!options.enforce) {\n options.logger?.error('Error during permission check, skipping enforcement', {\n error,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(500).json({\n error: PERMISSION_ERROR_TYPES.INTERNAL_ERROR,\n message: PERMISSION_ERROR_MESSAGES.INTERNAL_ERROR,\n });\n }\n};\n\nexport default requirePermissions;\n","import { evaluatePermissions } from './SDK/evaluatePermissions';\nimport { requirePermissions } from './middleware/requirePermissions';\n\ninterface SdkExports {\n evaluatePermissions: typeof evaluatePermissions;\n}\n\ninterface MiddlewareExports {\n requirePermissions: typeof requirePermissions;\n}\n\ninterface PermissionsModule {\n sdk: SdkExports;\n middlewares: MiddlewareExports;\n}\n\nexport const sdk: SdkExports = {\n evaluatePermissions,\n};\n\nexport const middlewares: MiddlewareExports = {\n requirePermissions,\n};\n\nexport default {\n sdk,\n middlewares,\n} as PermissionsModule;\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport * as outbreak from '@autofleet/outbreak';\nimport User, {\n middleware,\n eagerLoadPermissionsMiddleware,\n middlewareWithDecode,\n getDecodedBearer,\n appMiddleware,\n createOrSetRabbitTrace,\n} from './user';\nimport { authFromUserIdHeaderPlugin } from './user/fastify';\nimport { type UserPayload, CONTEXTS_IDS_HEADER } from './user/ApiUser';\nimport {\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n} from './check-permission';\nimport { UnauthorizedAccessError } from './errors';\nimport { getRefreshTokenSecret, getTokenSecret } from './secret-getter';\nimport { AUTHORIZATION_METHODS, getAuthorizationHeader } from './authorization';\nimport * as permissions from './permissions';\n\nconst getCurrentPayload: typeof outbreak.getCurrentContext = outbreak.getCurrentContext;\n\ntype OutbreakOptions = Parameters<typeof outbreak.default>[0];\ntype LoggerWithContextMiddleware = Partial<Pick<LoggerInstanceManager, 'addContextMiddleware'>>;\nconst enableTracing = ({ outbreakOptions = {}, logger }: { outbreakOptions?: OutbreakOptions; logger?: LoggerWithContextMiddleware; } = {}): void => {\n outbreak.default({\n headersPrefix: 'x-af',\n contextMiddlewareGetter: logger?.addContextMiddleware,\n ...outbreakOptions,\n });\n};\n\nconst traceTypes: typeof outbreak.traceTypes = outbreak.traceTypes;\nconst newTrace: typeof outbreak.newTrace = outbreak.newTrace;\n\nexport {\n traceTypes,\n newTrace,\n enableTracing,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n getRefreshTokenSecret,\n getTokenSecret,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n type UserPayload,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\ninterface Default {\n traceTypes: typeof outbreak.traceTypes;\n newTrace: typeof outbreak.newTrace;\n User: typeof User;\n middleware: typeof middleware;\n middlewareWithDecode: typeof middlewareWithDecode;\n eagerLoadPermissionsMiddleware: typeof eagerLoadPermissionsMiddleware;\n getCurrentPayload: typeof outbreak.getCurrentContext;\n getDecodedBearer: typeof getDecodedBearer;\n checkFleetPermission: typeof checkFleetPermission;\n checkBusinessModelPermission: typeof checkBusinessModelPermission;\n checkDemandSourcePermission: typeof checkDemandSourcePermission;\n isUserExist: typeof isUserExist;\n getUser: typeof getUser;\n UnauthorizedAccessError: typeof UnauthorizedAccessError;\n appMiddleware: typeof appMiddleware;\n createOrSetRabbitTrace: typeof createOrSetRabbitTrace;\n outbreak: typeof outbreak;\n AUTHORIZATION_METHODS: typeof AUTHORIZATION_METHODS;\n getAuthorizationHeader: typeof getAuthorizationHeader;\n CONTEXTS_IDS_HEADER: typeof CONTEXTS_IDS_HEADER;\n authFromUserIdHeaderPlugin: typeof authFromUserIdHeaderPlugin;\n permissions: typeof permissions;\n}\n\nconst zehutDefault: Default = {\n traceTypes,\n newTrace,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\nexport default zehutDefault;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["unixTime: moment.Moment","IdentityNetwork: Network","AutofleetApiNetwork: Network","MAX_CONTEXT_IDS: number","permissions: UserPayload","id?: string","accountType?: AccountType | undefined","contextIds?: string[]","eagerLoadPermissionsMiddleware: Asyncify<Handler>","newTrace","traceTypes","authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions>","user: ApiUser | null","result: EntityPermissions","result: Record<string, string[]>","permissionCache: PermissionCache","errors: string[]","permissionsToCache: PermissionsByContextId","result: PermissionsEvaluationByContextId","processedPermissions: PermissionsEvaluationByContextId","data: PermissionsEvaluationByContextId","reEvaluatedPermissions: PermissionsEvaluationByContextId","newResolvedPermissions: PermissionsEvaluationByContextId","contextIds: string[]","sdk: SdkExports","middlewares: MiddlewareExports","getCurrentPayload: typeof outbreak.getCurrentContext","traceTypes: typeof outbreak.traceTypes","newTrace: typeof outbreak.newTrace","zehutDefault: Default"],"sources":["../src/secret-getter.ts","../src/utils.ts","../src/services.ts","../src/user/ApiUser.ts","../src/app-auth.ts","../src/exceptions/appDoesNotExist.ts","../src/user/const.ts","../src/user/common.ts","../src/user/index.ts","../src/user/fastify.ts","../src/check-permission.ts","../src/errors.ts","../src/authorization.ts","../src/permissions/SDK/permissionCache.ts","../src/permissions/SDK/consts.ts","../src/permissions/SDK/evaluatePermissions.ts","../src/permissions/middleware/requirePermissions.ts","../src/permissions/index.ts","../src/index.ts"],"sourcesContent":["import jwt from 'jsonwebtoken';\nimport moment from 'moment';\n\nconst {\n DEPRECATED_JWT_SECRET, JWT_NEW_SECRET,\n DEPRECATED_REFRESH_JWT_SECRET, REFRESH_JWT_SECRET,\n DEPRECATION_UNIX_TIMESTAMP,\n} = process.env;\n\nconst getRelevantSecret = (token: string | undefined, deprecatedSecret: string, newSecret: string): string => {\n const deprecationTime = moment(parseInt(DEPRECATION_UNIX_TIMESTAMP || '', 10) * 1000);\n try {\n let unixTime: moment.Moment;\n if (token) {\n const { iat } = jwt.decode(token) as jwt.JwtPayload;\n unixTime = moment(iat! * 1000);\n } else {\n unixTime = moment();\n }\n return unixTime.isBefore(deprecationTime) ? deprecatedSecret : newSecret;\n } catch {\n return newSecret;\n }\n};\n\nexport const getRefreshTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_REFRESH_JWT_SECRET!, REFRESH_JWT_SECRET!);\nexport const getTokenSecret = (token?: string): string => getRelevantSecret(token, DEPRECATED_JWT_SECRET!, JWT_NEW_SECRET!);\n","import type { UUID } from 'node:crypto';\nimport jwt from 'jsonwebtoken';\nimport { getTokenSecret } from './secret-getter';\n\ntype Context = Partial<Record<ContextProp | 'id', string>> & { subSystem?: SubSystemType; permissions?: string[]; entityId: string; };\n\nconst CONTEXT_PROPS = ['fleetId', 'businessModelId', 'demandSourceId'] as const;\ntype ContextProp = typeof CONTEXT_PROPS[number];\nconst CONTEXT_MAP_PROPS = {\n fleet: 'fleets',\n business: 'businessModels',\n demand: 'demandSources',\n} as const;\ntype SubSystemType = keyof typeof CONTEXT_MAP_PROPS;\ntype ContextSubSystemProp = typeof CONTEXT_MAP_PROPS[SubSystemType];\n\nexport const getAuthFromBearer = (bearer: string): string => bearer.replace('Bearer ', '');\n\nexport const decodeBearer = (bearer: string, appSecret?: string): any => {\n const token = getAuthFromBearer(bearer);\n const decoded = jwt.verify(token, appSecret || getTokenSecret(token));\n return decoded;\n};\n\nexport const parsePermissions = (contextId: string, decodedToken: { contexts: Context[]; }): { key: string; value: string; } | undefined => {\n if (!decodedToken) return undefined;\n const { contexts } = decodedToken;\n const activeContext = contexts.find(context => context.id === contextId);\n\n // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n const permissionsValue = `${activeContext!.permissions?.map(cp => `${cp},`)}`;\n\n return {\n key: activeContext!.entityId,\n value: permissionsValue,\n };\n};\n\ntype EntitiesFromContext = Partial<Record<ContextSubSystemProp, Record<string, string>>>;\nexport const getEntitiesFromContext = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): EntitiesFromContext => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n\n const attributes: EntitiesFromContext = {};\n contexts.forEach((context) => {\n const prop = CONTEXT_MAP_PROPS[context.subSystem || 'business'];\n\n const permissions = parsePermissions(context.id!, decodedToken);\n if (!permissions) return;\n attributes[prop] ??= {};\n attributes[prop][permissions.key] = permissions.value;\n });\n\n return attributes;\n};\n\ntype ContextAttributes = Partial<Record<ContextProp, string[]>>;\nexport const getContextAttributes = (contextId: string | undefined, decodedToken: { contexts: Context[]; }): ContextAttributes => {\n if (!decodedToken) return {};\n let { contexts } = decodedToken;\n if (contextId) {\n contexts = contexts.filter(context => context.id === contextId);\n }\n const attributes: ContextAttributes = {};\n contexts.forEach((context) => {\n CONTEXT_PROPS.forEach((prop) => {\n if (context[prop]) {\n attributes[prop] ??= [];\n attributes[prop].push(context[prop]);\n }\n });\n });\n return attributes;\n};\n\nconst EMPTY_UUID = '00000000-0000-0000-0000-000000000000';\nconst FULL_UUID = 'ffffffff-ffff-ffff-ffff-ffffffffffff';\nconst VALID_CHARS_REGEX = '[0-9a-f]';\nconst UUID_VERSION_REGEX = '[1-8]';\nconst UUID_REGEX = new RegExp(\n `^(?:${VALID_CHARS_REGEX}{8}-${VALID_CHARS_REGEX}{4}-${UUID_VERSION_REGEX}${VALID_CHARS_REGEX}{3}-[89ab]${VALID_CHARS_REGEX}{3}-${VALID_CHARS_REGEX}{12}|${EMPTY_UUID}|${FULL_UUID})$`,\n 'i',\n);\nexport function validateUUID(uuid: unknown): uuid is UUID {\n return typeof uuid === 'string' && UUID_REGEX.test(uuid);\n}\n","import Network from '@autofleet/network';\n\nconst CACHE_LIFETIME_IN_SEC = 10;\nconst apiGwUrl = process.env.API_GATEWAY_URL || 'https://api.autofleet.io';\n\nexport const IdentityNetwork: Network = new Network({\n serviceName: 'IDENTITY_MS',\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n\nexport const AutofleetApiNetwork: Network = new Network({\n baseURL: apiGwUrl,\n serviceUrl: apiGwUrl,\n retries: 3,\n retryCondition: () => true,\n cache: process.env.NODE_ENV !== 'test' ? {\n maxAge: CACHE_LIFETIME_IN_SEC * 1000,\n } : undefined,\n});\n","import NodeCache from 'node-cache';\nimport objectHash from 'object-hash';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport { validateUUID } from '../utils';\nimport { AutofleetApiNetwork, IdentityNetwork } from '../services';\n\nexport type AccountType = 'client' | 'user' | 'service' | 'driver';\ntype EntityPermissions = Record<string, string[]>;\n\nexport const ELEVATED_PERMISSIONS_HEADER = 'x-af-elevated-permissions';\nexport const CONTEXTS_IDS_HEADER = 'x-af-context-ids';\nexport const MAX_CONTEXT_IDS: number = Number.parseInt(process.env.MAX_CONTEXT_IDS ?? `0`, 10) || 20;\n\nexport interface UserExternalData {\n element?: {\n internalUser: boolean;\n };\n [key: string]: any;\n}\n\nexport interface UserPayload {\n businessModels: EntityPermissions;\n fleets: EntityPermissions;\n demandSources: EntityPermissions;\n businessAccounts?: EntityPermissions;\n accountType?: AccountType;\n contexts?: EntityPermissions;\n createdAt?: string;\n externalData?: UserExternalData | null;\n}\n\nexport interface PartialUserPayload {\n businessModels?: EntityPermissions;\n fleets?: EntityPermissions;\n demandSources?: EntityPermissions;\n vehicles?: EntityPermissions;\n drivers?: EntityPermissions;\n businessAccounts?: EntityPermissions;\n}\n\nconst userCache = new NodeCache({ stdTTL: 10 });\n\nconst mergePermissions = (target: UserPayload | undefined, sources: Iterable<PartialUserPayload | undefined>): UserPayload => {\n const permissions: UserPayload = {\n ...target,\n fleets: { ...target?.fleets },\n businessModels: { ...target?.businessModels },\n demandSources: { ...target?.demandSources },\n // Clone other nested objects as needed\n };\n\n for (const source of sources) {\n (Object.entries(source ?? {}) as [Exclude<keyof UserPayload, 'accountType' | 'createdAt'>, EntityPermissions][]).forEach(([entityType, entityValue]) => {\n permissions[entityType] ??= {};\n Object.entries(entityValue).forEach(([entityId, perms]) => {\n permissions[entityType]![entityId] = (permissions[entityType]![entityId] || []).concat(perms);\n });\n });\n }\n\n return permissions;\n};\n\nif (typeof Symbol.dispose !== 'symbol') {\n // Polyfill for dispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L163-L171\n Object.defineProperty(Symbol, 'dispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.dispose'),\n writable: false,\n });\n}\nif (typeof Symbol.asyncDispose !== 'symbol') {\n // Polyfill for asyncDispose if it does not exist, based on https://github.com/nodejs/node/blob/9a9409ff1f45c968173118de4cd37dea784f8ec9/lib/internal/process/pre_execution.js#L174-L183\n Object.defineProperty(Symbol, 'asyncDispose', {\n // @ts-expect-error: TypeScript does not recognize __proto__ as a valid property\n __proto__: null,\n configurable: false,\n enumerable: false,\n value: Symbol.for('nodejs.asyncDispose'),\n writable: false,\n });\n}\n\nexport default class ApiUser {\n private privatePermissions: UserPayload | undefined;\n\n private readonly privateElevatedPermissionsHash = new Map<symbol, PartialUserPayload | undefined>();\n\n private privatePermissionsLegacy: any;\n\n private readonly appPermission: Record<string, any> = {};\n\n public readonly emptyUser: boolean;\n\n constructor(public id?: string, public accountType?: AccountType | undefined, elevatedPermissions?: PartialUserPayload, public contextIds?: string[]) {\n this.emptyUser = !!id;\n if (elevatedPermissions) {\n this.privateElevatedPermissionsHash.set(Symbol('initial'), elevatedPermissions);\n }\n }\n\n public async getUserPermissions(): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n });\n\n let data = userCache.get<UserPayload>(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get<UserPayload>(`/api/v1/users/${this.id}/authorization-payload`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.accountType = data.accountType;\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public async useCustomPermissionLoader(customPermissionLoader: (userId: string) => UserPayload | PromiseLike<UserPayload>): Promise<UserPayload> {\n if (!this.id) {\n return undefined!;\n }\n if (this.privatePermissions) {\n return this.privatePermissions;\n }\n\n const cacheKey = this.id;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.privatePermissions = cachedResult;\n return cachedResult;\n }\n\n const data = await customPermissionLoader(this.id);\n userCache.set(cacheKey, data);\n\n this.privatePermissions = data;\n return this.privatePermissions;\n }\n\n public get businessModels(): string[] {\n return this.getUserProperty('businessModels');\n }\n\n public get fleets(): string[] {\n return this.getUserProperty('fleets');\n }\n\n public get demandSources(): string[] {\n return this.getUserProperty('demandSources');\n }\n\n private getUserProperty(key: keyof UserPayload): string[] {\n if (!this.privatePermissions) {\n throw new Error(`Cannot get ${key} without calling (async) getUserPermissions before`);\n }\n return Object.keys(this.privatePermissions[key] || {});\n }\n\n public get elevatedPermissions(): UserPayload {\n return mergePermissions(undefined, this.privateElevatedPermissionsHash.values());\n }\n\n public get permissions(): UserPayload | undefined {\n if (!this.privatePermissions) {\n throw new Error('Cannot get permissions without calling (async) getUserPermissions before');\n }\n\n return mergePermissions(this.privatePermissions, this.privateElevatedPermissionsHash.values());\n }\n\n public elevatePermissions(addedPermissions: PartialUserPayload): (() => void) & { [Symbol.dispose]: () => void; } {\n // @itayankri is concerned about memory consumption, so create a symbol with no description, to avoid assigning memory for the description string\n const elevationId = Symbol();\n\n // Validate that the added permissions are valid UUIDs\n Object.values(addedPermissions).forEach((entityIds) => {\n Object.keys(entityIds).forEach((entityId) => {\n if (!validateUUID(entityId)) {\n throw new Error(`Entity id on elevatePermissions is not a valid UUID, provided: ${entityId}`);\n }\n });\n });\n\n const currentUserTrace = getCurrentContext();\n if (!currentUserTrace) {\n throw new Error('Cannot find current user cross services trace');\n }\n\n const currentElevation = JSON.parse(currentUserTrace.context?.get(ELEVATED_PERMISSIONS_HEADER) as string | undefined || '{}');\n const newElevation = Object.assign(currentElevation, addedPermissions);\n this.privateElevatedPermissionsHash.set(elevationId, newElevation);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n const cleanup = () => {\n this.privateElevatedPermissionsHash.delete(elevationId);\n currentUserTrace.context.set(ELEVATED_PERMISSIONS_HEADER, JSON.stringify(this.elevatedPermissions));\n };\n cleanup[Symbol.dispose] = cleanup;\n return cleanup;\n }\n\n public async getUserPermissionsLegacy(): Promise<unknown> {\n if (!this.id) {\n return undefined;\n }\n if (this.privatePermissionsLegacy) {\n return this.privatePermissionsLegacy;\n }\n\n const cacheKey = objectHash({\n id: this.id,\n contextIds: this.contextIds,\n legacy: true,\n });\n let data = userCache.get(cacheKey);\n\n if (!data) {\n ({ data } = await IdentityNetwork.get(`/api/v1/users/${this.id}/authorization-payload-legacy`, { params: { contextIds: this.contextIds } }));\n userCache.set(cacheKey, data);\n }\n\n this.privatePermissionsLegacy = data;\n return this.privatePermissionsLegacy;\n }\n\n public get permissionsLegacy(): any {\n if (!this.privatePermissionsLegacy) {\n throw new Error('Cannot get permissionsLegacy without calling (async) getUserPermissionsLegacy before');\n }\n return this.privatePermissionsLegacy;\n }\n\n public async getUserAppPermissions(appId: string, clientSecret: string): Promise<UserPayload | undefined> {\n if (!this.id || !appId || !clientSecret) {\n return undefined;\n }\n const currentAppPermission = this.appPermission[appId];\n\n if (currentAppPermission) {\n return currentAppPermission;\n }\n\n const cacheKey = `${this.id}:${appId}`;\n\n const cachedResult = userCache.get<UserPayload>(cacheKey);\n if (cachedResult) {\n this.appPermission[appId] = cachedResult;\n return cachedResult;\n }\n\n const { data } = await AutofleetApiNetwork.post<UserPayload>(`/api/v1/apps/${appId}/get-user-payload`, {\n userId: this.id,\n }, {\n headers: {\n 'x-autofleet-apps-secret': clientSecret,\n },\n });\n\n userCache.set(cacheKey, data);\n this.appPermission[appId] = data;\n return this.appPermission[appId];\n }\n\n public extendRequiredContexts(contextIdsToAdd?: string[]): void {\n if (!Array.isArray(contextIdsToAdd)) {\n return;\n }\n\n const newContextIds = Array.from(new Set([...(this.contextIds ?? []), ...contextIdsToAdd]));\n\n if (newContextIds.length > MAX_CONTEXT_IDS) {\n return;\n }\n\n this.contextIds = newContextIds;\n const currentUserTrace = getCurrentContext();\n currentUserTrace?.context?.set(CONTEXTS_IDS_HEADER, this.contextIds.join(','));\n }\n}\n","import { AutofleetApiNetwork } from './services';\n\nexport const decodeAppBearer = async (bearer: string, appId: string): Promise<any> => {\n const { data: decoded } = await AutofleetApiNetwork.post('/api/v1/auth', { bearer, appId });\n return decoded;\n};\n\nexport const getClientSecret = async (appId: string): Promise<any> => {\n const { data: secret } = await AutofleetApiNetwork.get(`/api/v1/auth/client-secret/${appId}`);\n return secret;\n};\n","export default class AppDoesNotExist extends Error {\n name = 'AppDoesNotExist';\n\n message = 'app does not exist';\n}\n","export const IDENTITY_MS = 'identity-ms';\nexport const ACCESS_TOKEN = 'accessToken';\nexport const USER_OBJECT = 'userObject';\nexport const USER_TRACING_HEADER = 'x-af-user-id';\nexport const ORIGIN_HEADER = 'X-IAF-ORIGIN-SERVICE';\nexport const USER_PERMISSIONS_HEADER = 'x-af-user-permissions';\nexport const LOWER_CASE_ORIGIN_HEADER = ORIGIN_HEADER.toLowerCase() as Lowercase<typeof ORIGIN_HEADER>;\nexport const AUTOFLEET_APPS_SECRET_HEADER = 'x-autofleet-apps-secret';\n","import type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentContext } from '@autofleet/outbreak';\nimport ApiUser, { CONTEXTS_IDS_HEADER, ELEVATED_PERMISSIONS_HEADER, type UserPayload } from './ApiUser';\nimport {\n IDENTITY_MS,\n USER_OBJECT,\n USER_TRACING_HEADER,\n ORIGIN_HEADER,\n LOWER_CASE_ORIGIN_HEADER,\n} from './const';\n\nexport type CustomPermissionLoader = (userId: string) => Promise<UserPayload>;\nexport interface AuthFromUserIdHeaderOptions {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n customPermissionLoader?: CustomPermissionLoader;\n}\n\nexport const authFromUserIdHeader = async (options: AuthFromUserIdHeaderOptions, headers: IncomingHttpHeaders): Promise<ApiUser | undefined> => {\n const originHeader = headers[ORIGIN_HEADER] || headers[LOWER_CASE_ORIGIN_HEADER] || '';\n if (!Array.isArray(originHeader) && originHeader.toLowerCase() === IDENTITY_MS) {\n return undefined;\n }\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n customPermissionLoader,\n } = options;\n const userId = headers[USER_TRACING_HEADER] as string;\n if (!userId || Array.isArray(userId)) {\n return undefined;\n }\n\n const elevatedPermHeaderValue = headers[ELEVATED_PERMISSIONS_HEADER];\n const elevatedPermissionsFromHeader = elevatedPermHeaderValue?.length && elevatedPermHeaderValue.length > 0 ? JSON.parse(elevatedPermHeaderValue as string) : {};\n const contextIds = (headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n\n const userObject = new ApiUser(userId, 'user', elevatedPermissionsFromHeader, contextIds);\n if (eagerLoadUserPermissions) {\n if (customPermissionLoader) {\n await userObject.useCustomPermissionLoader(customPermissionLoader);\n } else {\n await userObject.getUserPermissions();\n }\n }\n\n if (eagerLoadUserPermissionsLegacy) {\n await userObject.getUserPermissionsLegacy();\n }\n\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n return userObject;\n};\n","import type { Handler, Request } from 'express';\nimport { getCurrentContext, newTrace, traceTypes } from '@autofleet/outbreak';\nimport jwt from 'jsonwebtoken';\nimport ApiUser, { CONTEXTS_IDS_HEADER, MAX_CONTEXT_IDS } from './ApiUser';\nimport { decodeAppBearer } from '../app-auth';\nimport AppDoesNotExist from '../exceptions/appDoesNotExist';\nimport { decodeBearer, getAuthFromBearer } from '../utils';\nimport {\n ACCESS_TOKEN,\n USER_OBJECT,\n USER_TRACING_HEADER,\n USER_PERMISSIONS_HEADER,\n AUTOFLEET_APPS_SECRET_HEADER,\n} from './const';\nimport { authFromUserIdHeader, type AuthFromUserIdHeaderOptions } from './common';\n\ndeclare module 'express-serve-static-core' {\n interface Request {\n user: ApiUser;\n }\n}\n\ntype Asyncify<T extends (...a: any[]) => any> = (...a: Parameters<T>) => Promise<Awaited<ReturnType<T>>>;\n\nexport const middleware = (options: AuthFromUserIdHeaderOptions = {}): Asyncify<Handler> => async (req, res, next): Promise<any> => {\n try {\n const userObject = await authFromUserIdHeader(options, req.headers);\n if (userObject) {\n req.user = userObject;\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n }\n\n next();\n } catch {\n res.status(401).json({ error: 'cannot authenticate user' });\n }\n};\n\nexport const middlewareWithDecode = (options: {\n eagerLoadUserPermissions?: boolean;\n eagerLoadUserPermissionsLegacy?: boolean;\n returnErrorIfNoToken?: boolean;\n appSecret?: string;\n} = {}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n eagerLoadUserPermissions,\n eagerLoadUserPermissionsLegacy,\n returnErrorIfNoToken,\n appSecret,\n } = options;\n let decoded;\n if (req.headers.authorization) {\n try {\n decoded = await decodeBearer(req.headers.authorization, appSecret);\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n } else if (e instanceof jwt.JsonWebTokenError) {\n res.status(400).json({ errors: [e.message] });\n } else {\n res.status(500).json({ errors: ['Server error while parsing token'] });\n }\n return;\n }\n const userId = decoded?.user?.id;\n\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const contextIds = (req.headers?.[CONTEXTS_IDS_HEADER] as string)?.split(',');\n const userObject = new ApiUser(userId, decoded?.user?.accountType, undefined, contextIds);\n\n if (eagerLoadUserPermissions || eagerLoadUserPermissionsLegacy) {\n await Promise.all([\n eagerLoadUserPermissions && userObject.getUserPermissions(),\n eagerLoadUserPermissionsLegacy && userObject.getUserPermissionsLegacy(),\n ]);\n }\n\n req.user = userObject;\n getCurrentContext().nonHeaderContext?.set(USER_OBJECT, userObject);\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n } else if (returnErrorIfNoToken) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n next();\n};\n\nexport const appMiddleware = (options: {\n appId: string;\n clientSecret: string;\n}): Asyncify<Handler> => async (req, res, next): Promise<void> => {\n const {\n appId,\n clientSecret,\n } = options;\n let decoded;\n\n if (!req.headers.authorization) {\n res.status(401).json({ errors: ['No token provided'] });\n return;\n }\n\n try {\n decoded = await decodeAppBearer(req.headers.authorization, appId);\n if (!decoded) {\n throw new AppDoesNotExist();\n }\n } catch (e) {\n if (e instanceof jwt.TokenExpiredError) {\n res.status(401).json({ errors: ['Access token expired'] });\n return;\n }\n if ([jwt.JsonWebTokenError, AppDoesNotExist].some(Err => e instanceof Err)) {\n res.status(400).json({ errors: [(e as Error).message] });\n return;\n }\n res.status(500).json({ errors: ['Server error while parsing token'] });\n return;\n }\n const userId = decoded?.userId;\n if (userId) {\n req.headers[USER_TRACING_HEADER] = userId;\n }\n\n const userObject = new ApiUser(userId);\n\n if (appId) {\n req.headers[AUTOFLEET_APPS_SECRET_HEADER] = clientSecret;\n // Won't work until we find a better solution for identity ms\n await userObject.getUserAppPermissions(appId, clientSecret);\n }\n\n req.user = userObject;\n const currentTraceContext = getCurrentContext().nonHeaderContext;\n currentTraceContext?.set(USER_OBJECT, userObject);\n currentTraceContext?.set(ACCESS_TOKEN, getAuthFromBearer(req.headers.authorization));\n\n // Added in order to support outbreak.\n // @ts-expect-error we are setting an object onto the request headers.\n req.headers[USER_PERMISSIONS_HEADER] = userObject;\n\n next();\n};\n\nexport const eagerLoadPermissionsMiddleware: Asyncify<Handler> = async (req, res, next) => {\n await req.user.getUserPermissions();\n next();\n};\n\nexport const getDecodedBearer = (req: Request, appSecret?: string): any => {\n if (!req.headers.authorization) {\n return null;\n }\n return decodeBearer(req.headers.authorization, appSecret);\n};\n\nexport const createOrSetRabbitTrace = async (trace: ReturnType<typeof newTrace> | undefined, userId: string | undefined, contextIds?: string[]): Promise<void> => {\n const addContextIds = Array.isArray(contextIds) && contextIds.length <= MAX_CONTEXT_IDS;\n const userObject = new ApiUser(\n userId,\n undefined,\n undefined,\n addContextIds ? contextIds : undefined,\n );\n\n await userObject.getUserPermissions();\n trace ??= newTrace(traceTypes.RABBIT);\n trace.nonHeaderContext.set(USER_OBJECT, userObject);\n\n if (addContextIds) {\n trace.context.set(CONTEXTS_IDS_HEADER, contextIds?.join(',') || '');\n }\n};\n\nexport default ApiUser;\n","import type { FastifyPluginCallback } from 'fastify';\nimport type ApiUser from './ApiUser';\nimport { type AuthFromUserIdHeaderOptions, authFromUserIdHeader } from './common';\n\ndeclare module 'fastify' {\n interface FastifyRequest {\n user?: ApiUser;\n }\n}\n\nexport const authFromUserIdHeaderPlugin: FastifyPluginCallback<AuthFromUserIdHeaderOptions> = (fastify, options, done) => {\n fastify.decorateRequest('user', undefined);\n fastify.addHook('onRequest', async (request, reply) => {\n try {\n const user = await authFromUserIdHeader(options, request.headers);\n if (user) {\n request.user = user;\n }\n } catch {\n reply.status(401).send({ error: 'cannot authenticate user' });\n }\n });\n\n done();\n};\nObject.defineProperty(authFromUserIdHeaderPlugin, Symbol.for('skip-override'), { value: true }); // Prevent Fastify from overriding the plugin\n","import { getCurrentContext } from '@autofleet/outbreak';\nimport { USER_OBJECT } from './user/const';\nimport type ApiUser from './user/ApiUser';\nimport type { UserPayload } from './user/ApiUser';\n\nexport const getUser = (): ApiUser | undefined => getCurrentContext().nonHeaderContext?.get(USER_OBJECT) as ApiUser | undefined;\n\nexport const isUserExist = (): string | undefined => getUser()?.id;\n\nconst checkUserPermissions = (\n entityId: string,\n entityType: Exclude<keyof UserPayload, 'accountType' | 'createdAt'>,\n) => !isUserExist() || Object.hasOwn(getUser()!.permissions?.[entityType] ?? {}, entityId);\n\nexport const checkFleetPermission = (fleetId: string): boolean => checkUserPermissions(fleetId, 'fleets');\nexport const checkBusinessModelPermission = (businessModelId: string): boolean => checkUserPermissions(businessModelId, 'businessModels');\nexport const checkDemandSourcePermission = (demandSourceId: string): boolean => checkUserPermissions(demandSourceId, 'demandSources');\n","import type ApiUser from './user';\n\nexport class UnauthorizedAccessError extends Error {\n constructor(public user: ApiUser | null = null, message = 'UnauthorizedAccessError') {\n super(message);\n this.name = 'UnauthorizedAccessError';\n }\n}\n","import jwt from 'jsonwebtoken';\n\nexport const AUTHORIZATION_METHODS = {\n NONE: 'NONE',\n BASIC: 'BASIC',\n JWT: 'JWT',\n};\n\nconst AUTHORIZATION_ACTIONS = {\n [AUTHORIZATION_METHODS.NONE]: () => undefined,\n [AUTHORIZATION_METHODS.BASIC]: (authorizationSettings: any) => {\n const { username, password } = authorizationSettings;\n const encodedCredentials = Buffer.from(`${username}:${password}`).toString('base64');\n return `Basic ${encodedCredentials}`;\n },\n [AUTHORIZATION_METHODS.JWT]: (authorizationSettings: any) => {\n const { secret } = authorizationSettings;\n if (secret) {\n return `Bearer ${jwt.sign({}, secret, { expiresIn: 10 })}`;\n }\n return undefined;\n },\n};\n\nexport const getAuthorizationHeader = (authorizationSettings: { method: string; } | undefined): string | undefined => {\n const authorizationMethod = authorizationSettings?.method;\n\n if (!authorizationMethod || !AUTHORIZATION_ACTIONS[authorizationMethod]) {\n return undefined;\n }\n\n return AUTHORIZATION_ACTIONS[authorizationMethod](authorizationSettings);\n};\n","import NodeCache from 'node-cache';\n\ntype EntityPermissions = Record<string, string[]>;\n\nexport class PermissionCache {\n private cache: NodeCache;\n\n private ttl = 10;\n\n /**\n * Creates a new PermissionCache instance\n * @param cache - Optional NodeCache instance. If not provided, creates a new one with default TTL of 10 seconds\n */\n constructor(cache?: NodeCache, ttl?: number) {\n if (ttl) {\n this.ttl = ttl;\n }\n this.cache = cache ?? new NodeCache({ stdTTL: this.ttl });\n }\n\n private static getCacheKeys(cacheName: string, userId: string, contextIds: string[]) {\n const baseCacheKey = `${cacheName}-${userId}`;\n const cacheKeys = contextIds.map(contextId => `${baseCacheKey}-${contextId}`);\n\n return { baseCacheKey, cacheKeys };\n }\n\n /**\n * Retrieves user permissions for multiple contexts from cache\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to fetch permissions for\n * @returns An object mapping context IDs to permission arrays\n */\n public getUserPermissions(userId: string, contextIds: string[]): EntityPermissions {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('perm', userId, contextIds);\n const cacheResult = this.cache.mget(cacheKeys) as Record<string, string[]>;\n\n const result: EntityPermissions = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Stores user permissions for multiple contexts in cache\n * @param userId - The user identifier\n * @param PermissionsByContextId - Object mapping context IDs to permission arrays\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setUserPermissions(userId: string, PermissionsByContextId: EntityPermissions): { success: boolean; } {\n try {\n const cacheKey = (contextId: string) => `perm-${userId}-${contextId}`;\n\n const cacheEntries = Object.entries(PermissionsByContextId).map(([contextId, permissions]) => ({\n key: cacheKey(contextId),\n val: permissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n\n /**\n * Retrieves denied permissions status for multiple contexts from cache.\n * \"Denied\" permissions indicate that a user has already been evaluated for a specific set of\n * required permissions in a given context, avoiding redundant permission checks.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to check\n * @returns An object mapping context IDs to arrays of denied permissions\n */\n public getCachedDeniedPermissions(userId: string, contextIds: string[]): Record<string, string[]> {\n try {\n const { baseCacheKey, cacheKeys } = PermissionCache.getCacheKeys('seen', userId, contextIds);\n const cacheResult = this.cache.mget(cacheKeys) as Record<string, string[]>;\n\n const result: Record<string, string[]> = {};\n Object.entries(cacheResult)\n .filter(([, permissions]) => permissions)\n .forEach(([key, permissions]) => {\n const contextId = key.replace(`${baseCacheKey}-`, '');\n result[contextId] = permissions;\n });\n\n return result;\n } catch {\n return {};\n }\n }\n\n /**\n * Marks permissions as seen for multiple contexts in cache.\n * \"Seen\" permissions are used to track that a user has already been evaluated for a specific\n * set of required permissions in given contexts, preventing duplicate permission evaluations.\n * @param userId - The user identifier\n * @param contextIds - Array of context identifiers to mark as seen\n * @param deniedPermissions - Array of permissions that were evaluated and denied\n * @param ttl - Optional time to live in seconds. Defaults to 10 seconds\n * @returns An object indicating success/failure\n */\n public setCachedDeniedPermissions(userId: string, contextIds: string[], deniedPermissions: string[]): { success: boolean; } {\n try {\n const seenKey = (contextId: string) => `seen-${userId}-${contextId}`;\n\n const cacheEntries = contextIds.map(contextId => ({\n key: seenKey(contextId),\n val: deniedPermissions,\n ttl: this.ttl,\n }));\n\n this.cache.mset(cacheEntries);\n\n return { success: true };\n } catch {\n return { success: false };\n }\n }\n}\n\nexport const permissionCache: PermissionCache = new PermissionCache();\n","export const PERMISSIONS_EVALUATION_OPERATORS = {\n AND: 'and',\n OR: 'or',\n};\n\nexport const PERMISSION_ERROR_TYPES = {\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n API_ERROR: 'API_ERROR',\n NO_REQUIRED_PERMISSIONS: 'NO_REQUIRED_PERMISSIONS',\n UNAUTHORIZED: 'UNAUTHORIZED',\n BAD_REQUEST: 'BAD_REQUEST',\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n};\n\nexport const PERMISSION_ERROR_MESSAGES = {\n USER_NOT_FOUND: 'User not found',\n INSUFFICIENT_PERMISSIONS: 'User does not have sufficient permissions',\n VALIDATION_ERROR: 'Validation error occurred',\n API_ERROR: 'API error occurred',\n NO_REQUIRED_PERMISSIONS: 'No required permissions provided for evaluation',\n UNAUTHORIZED: 'User is not authorized to perform this action',\n BAD_REQUEST: 'Bad request, please check the input parameters',\n INTERNAL_ERROR: 'Internal server error occurred while checking permissions',\n};\n","import { getUser } from '../../check-permission';\nimport { IdentityNetwork } from '../../services';\nimport type {\n PermissionsEvaluationOperator,\n EvaluatePermissionsParams,\n PermissionsEvaluationByContextId,\n PermissionCheckResult,\n ResolvePermissionsParams,\n Logger,\n PermissionsByContextId,\n} from './types';\nimport type { PermissionErrorType } from './errors';\nimport { permissionCache } from './permissionCache';\nimport { PERMISSIONS_EVALUATION_OPERATORS, PERMISSION_ERROR_TYPES } from './consts';\n\n/**\n * Evaluates whether user permissions meet requirements based on AND/OR operator\n * @param permissions - User's actual permissions\n * @param requiredPermissions - Required permissions to check against\n * @param operator - {@link PermissionsEvaluationOperator} AND (all required) or OR (at least one required)\n * @returns true if permissions meet requirements\n */\nconst evaluatePermissionsByOperator = (\n permissions: string[],\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): boolean => {\n const permissionsSet = new Set(permissions);\n const matchingPermissions = requiredPermissions.filter(permission => permissionsSet.has(permission));\n\n return operator === PERMISSIONS_EVALUATION_OPERATORS.AND\n ? matchingPermissions.length === requiredPermissions.length // All required permissions must be present\n : matchingPermissions.length > 0; // At least one required permission must be present\n};\n\n/**\n * Creates empty permissions result for cases with no required permissions\n * @param contextIds - Context IDs to create results for\n * @returns {@link PermissionsEvaluationByContextId} with empty permissions and granted access\n */\nconst createEmptyPermissionsResult = (contextIds: string[]): PermissionsEvaluationByContextId => Object.fromEntries(\n contextIds.map(contextId => [\n contextId,\n { permissions: [], hasRequiredPermissions: true },\n ]),\n);\n\n/**\n * Checks if permission requirements are met across all contexts\n * @param permissionsEvaluationByContextId - {@link PermissionsEvaluationByContextId} Permission evaluation results by context\n * @param requireAll - Whether all contexts must satisfy requirements (true) or just one (false)\n * @returns true if requirements are met\n */\nexport const checkAuthorizeRequirements = (permissionsEvaluationByContextId: PermissionsEvaluationByContextId, requireAll: boolean): boolean => {\n const permissionsEvaluationValues = Object.values(permissionsEvaluationByContextId);\n\n if (requireAll) {\n return permissionsEvaluationValues.every(evaluation => evaluation.hasRequiredPermissions);\n }\n return permissionsEvaluationValues.some(evaluation => evaluation.hasRequiredPermissions);\n};\n\nexport const formatResultsWithOrOperator = (\n contextsPermissionsEvaluations: PermissionsEvaluationByContextId,\n requiredPermissions: string[],\n): PermissionsEvaluationByContextId => {\n const permissionsEvaluationByContextId: PermissionsEvaluationByContextId = {};\n Object.entries(contextsPermissionsEvaluations).forEach(([contextId, evaluation]) => {\n const permissions = evaluation?.permissions || [];\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n PERMISSIONS_EVALUATION_OPERATORS.OR,\n );\n\n permissionsEvaluationByContextId[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n });\n\n return permissionsEvaluationByContextId;\n};\n\n/**\n * Resolves permissions from Identity Service API\n * @param params - {@link ResolvePermissionsParams}\n * @returns {@link PermissionsEvaluationByContextId} Permission evaluation results by context ID\n */\nexport const resolvePermissions = async ({\n user,\n contextIds,\n requiredPermissions,\n options,\n}: ResolvePermissionsParams): Promise<PermissionsEvaluationByContextId> => {\n if (!requiredPermissions?.length) {\n return createEmptyPermissionsResult(contextIds);\n }\n\n const response = await IdentityNetwork.post('/api/v1/permissions/resolve', {\n userId: user.id,\n contextIds,\n requiredPermissions,\n }, {\n timeout: options.timeout,\n });\n\n const data: PermissionsEvaluationByContextId = response?.data || {};\n\n if (!Object.keys(data || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n if (options.permissionsEvaluationOperator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n return data;\n }\n\n // Re-evaluate with OR operator\n return formatResultsWithOrOperator(data, requiredPermissions);\n};\n\nconst validateInput = (contextIds: string[]): string[] => {\n const errors: string[] = [];\n\n if (!contextIds?.length) {\n errors.push('contextIds cannot be empty');\n }\n\n return errors;\n};\n\nconst createErrorResult = (\n error: PermissionErrorType,\n requiredPermissions: string[],\n contextIds: string[],\n message?: string,\n): PermissionCheckResult => ({\n isAuthorized: false,\n error,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n ...(message && { message }),\n});\n\n/**\n * Caches permission results based on whether they meet requirements\n * - Caches granted permissions directly\n * - Groups denied permissions by identical sets to optimize cache storage\n * @param userId - User ID\n * @param resolvedPermissions - Permissions evaluation results to cache\n * @param requiredPermissions - Required permissions used for evaluation\n */\nconst cachePermissionsResults = (\n userId: string,\n resolvedPermissions: PermissionsEvaluationByContextId,\n requiredPermissions: string[],\n logger?: Logger,\n): void => {\n const permissionsToCache: PermissionsByContextId = {};\n const deniedPermissionsMap = new Map<string, string[]>();\n\n logger?.debug('Start caching permissions results', {\n userId,\n resolvedPermissions,\n requiredPermissions,\n });\n\n Object.entries(resolvedPermissions).forEach(([contextId, evaluation]) => {\n if (evaluation?.permissions) {\n const userPermissionsSet = new Set(evaluation.permissions);\n const effectivePermissions = requiredPermissions.filter(permission => userPermissionsSet.has(permission));\n const deniedPermissions = requiredPermissions.filter(permission => !userPermissionsSet.has(permission));\n\n if (effectivePermissions.length) {\n // Store only effective permissions (intersection of user and required permissions)\n permissionsToCache[contextId] = effectivePermissions;\n }\n\n if (deniedPermissions.length) {\n // Calculate which required permissions were denied by filtering out the ones the user has\n const deniedPermissionsKey = deniedPermissions.sort().join(',');\n\n if (!deniedPermissionsMap.has(deniedPermissionsKey)) {\n deniedPermissionsMap.set(deniedPermissionsKey, []);\n }\n deniedPermissionsMap.get(deniedPermissionsKey)!.push(contextId);\n }\n }\n });\n\n if (Object.keys(permissionsToCache).length > 0) {\n const pastCache = permissionCache.getUserPermissions(userId, Object.keys(permissionsToCache));\n\n Object.keys(pastCache ?? {}).forEach((contextId) => {\n if (permissionsToCache[contextId]) {\n // Merge with any previously cached permissions to avoid overwriting\n const mergedPermissions = Array.from(new Set([...(pastCache[contextId] || []), ...permissionsToCache[contextId]]));\n permissionsToCache[contextId] = mergedPermissions;\n }\n });\n\n logger?.debug('Caching granted permissions', { userId, permissionsToCache, pastCache });\n const { success } = permissionCache.setUserPermissions(userId, permissionsToCache);\n if (!success) {\n logger?.error('Failed to cache granted permissions', { userId, permissionsToCache });\n }\n }\n\n // Cache denied permissions grouped by the same denied permissions to utilize mset efficiently\n Array.from(deniedPermissionsMap.entries()).map(([deniedPermissionsKey, contextIds]) => {\n const deniedPermissions = deniedPermissionsKey.split(',');\n return permissionCache.setCachedDeniedPermissions(userId, contextIds, deniedPermissions);\n });\n\n logger?.debug('Caching denied permissions', {\n userId,\n deniedPermissionsGroups: Array.from(deniedPermissionsMap.entries()).map(([key, contexts]) => ({\n deniedPermissions: key.split(','),\n contextIds: contexts,\n })),\n });\n};\n\n/**\n * Determines if a context can be resolved from denied permissions cache\n * @param deniedPermissions - Previously denied permissions for the context\n * @param requiredPermissions - Currently required permissions\n * @param operator - Evaluation operator (AND/OR)\n * @returns Object indicating if context is resolvable from cache and the result\n */\nconst isResolvableFromDeniedCache = (\n deniedPermissions: string[],\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): { isResolvableFromCache: boolean; hasRequiredPermissions: boolean; } => {\n if (!deniedPermissions.length) {\n return { isResolvableFromCache: false, hasRequiredPermissions: false };\n }\n\n const deniedSet = new Set(deniedPermissions);\n const deniedRequiredPermissions = requiredPermissions.filter(permission => deniedSet.has(permission));\n\n if (operator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n // For AND: if any required permission was denied, we can resolve as false\n return {\n isResolvableFromCache: deniedRequiredPermissions.length > 0,\n hasRequiredPermissions: false,\n };\n }\n\n // For OR: only if ALL required permissions were denied, we can resolve as false\n return {\n isResolvableFromCache: deniedRequiredPermissions.length === requiredPermissions.length,\n hasRequiredPermissions: false,\n };\n};\n\n/**\n * Gets contexts that need API calls (unseen + partially resolvable seen contexts)\n * @param unseenContextIds - Context IDs that have no cached data\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedContextEntries - Context IDs that were fully resolved from cache\n * @returns Array of context IDs that need API calls\n */\nconst getContextsNeedingAPI = (\n unseenContextIds: string[],\n deniedContextIds: string[],\n deniedContextEntries: PermissionsEvaluationByContextId,\n): string[] => {\n const contextIdsNeedingAPI = [...unseenContextIds];\n\n // Add denied contexts that couldn't be resolved from cache\n const unresolvedDeniedContextIds = deniedContextIds.filter(contextId => !deniedContextEntries[contextId]);\n contextIdsNeedingAPI.push(...unresolvedDeniedContextIds);\n\n return contextIdsNeedingAPI;\n};\n\n/**\n * Processes denied contexts that can be resolved from denied permissions cache\n * @param deniedContextIds - Context IDs that have cached denied permissions\n * @param deniedPermissions - Denied permissions cache data\n * @param requiredPermissions - Currently required permissions\n * @param operator - Evaluation operator (AND/OR)\n * @returns Object with cache-resolvable contexts and their evaluations\n */\nconst processCachedDeniedContexts = (\n deniedContextIds: string[],\n seenPermissions: Record<string, string[]>,\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): PermissionsEvaluationByContextId => {\n const result: PermissionsEvaluationByContextId = {};\n\n deniedContextIds.forEach((contextId) => {\n const deniedPermissions = seenPermissions[contextId];\n const cacheResult = isResolvableFromDeniedCache(deniedPermissions, requiredPermissions, operator);\n\n if (cacheResult.isResolvableFromCache) {\n result[contextId] = {\n permissions: [],\n hasRequiredPermissions: cacheResult.hasRequiredPermissions,\n };\n }\n });\n\n return result;\n};\n\n/**\n * Processes cached permissions into evaluation format\n * @param cachedPermissions - Raw permissions from cache\n * @param requiredPermissions - Required permissions to evaluate against\n * @param operator - Evaluation operator (AND/OR)\n * @returns Processed permissions evaluation\n */\nconst processCachedPermissions = (\n cachedPermissions: Record<string, string[]>,\n requiredPermissions: string[],\n operator: PermissionsEvaluationOperator,\n): PermissionsEvaluationByContextId => {\n const processedPermissions: PermissionsEvaluationByContextId = {};\n\n if (Object.keys(cachedPermissions || {}).length) {\n Object.entries(cachedPermissions).forEach(([contextId, permissions]) => {\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n operator,\n );\n\n // Only include contexts that can be fully resolved from cache\n // Since we only cache effective permissions we won't have all of the user's permissions to fully resolve\n if (hasRequiredPermissions) {\n processedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n }\n });\n }\n\n return processedPermissions;\n};\n\n/**\n * Calls the Identity API to resolve permissions for unseen contexts\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve\n * @param requiredPermissions - Required permissions\n * @param options - API call options\n * @returns Resolved permissions from API\n */\nconst callIdentityAPI = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: string[],\n options: { permissionsEvaluationOperator: PermissionsEvaluationOperator; timeout: number; },\n): Promise<PermissionsEvaluationByContextId> => {\n const response = await IdentityNetwork.post('/api/v1/permissions/resolve', {\n userId,\n contextIds,\n requiredPermissions,\n }, {\n timeout: options.timeout,\n });\n\n const data: PermissionsEvaluationByContextId = response?.data || {};\n\n if (!Object.keys(data || {}).length) {\n throw new Error('Failed to resolve permissions');\n }\n\n // If using AND operator, return data as-is. For OR, re-evaluate.\n if (options.permissionsEvaluationOperator === PERMISSIONS_EVALUATION_OPERATORS.AND) {\n return data;\n }\n\n // Re-evaluate with OR operator\n const reEvaluatedPermissions: PermissionsEvaluationByContextId = {};\n Object.entries(data).forEach(([contextId, evaluation]) => {\n const permissions = evaluation?.permissions || [];\n const hasRequiredPermissions = evaluatePermissionsByOperator(\n permissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n\n reEvaluatedPermissions[contextId] = {\n permissions,\n hasRequiredPermissions,\n };\n });\n\n return reEvaluatedPermissions;\n};\n\n/**\n * Validates user context and required permissions for evaluation\n * @param contextIds - Context IDs to validate\n * @param requiredPermissions - Required permissions to validate\n * @param userId - User ID to validate\n * @param logger - Logger instance for warnings\n * @returns Error result if validation fails, null if validation passes\n */\nconst validateEvaluationInput = (\n contextIds: string[],\n requiredPermissions: string[],\n userId: string | null,\n logger?: Logger,\n): PermissionCheckResult | null => {\n const validationErrors = validateInput(contextIds);\n if (validationErrors.length) {\n return createErrorResult(PERMISSION_ERROR_TYPES.VALIDATION_ERROR, requiredPermissions, contextIds, validationErrors.join(', '));\n }\n\n if (!userId) {\n logger?.warn('User not found in context, cannot check permissions', {\n contextIds,\n requiredPermissions,\n });\n return createErrorResult(PERMISSION_ERROR_TYPES.USER_NOT_FOUND, requiredPermissions, contextIds);\n }\n\n if (!requiredPermissions?.length) {\n logger?.info('No requiredPermissions provided', {\n userId,\n contextIds,\n });\n return {\n isAuthorized: false,\n userId,\n resolvedPermissions: {},\n requiredPermissions,\n contextIds,\n error: PERMISSION_ERROR_TYPES.NO_REQUIRED_PERMISSIONS,\n };\n }\n\n return null;\n};\n\n/**\n * Resolves permissions from Identity API and combines with existing resolved permissions\n * @param userId - User ID\n * @param contextIdsNeedingAPI - Context IDs that need API resolution\n * @param requiredPermissions - Required permissions to check\n * @param options - API call options {@link ResolvePermissionsParams}\n * @param resolvedPermissions - Already resolved permissions from cache and denied cache\n * @returns Combined permissions including those resolved from API\n */\nconst resolvePermissionsFromAPI = async (\n userId: string,\n contextIdsNeedingAPI: string[],\n requiredPermissions: string[],\n options: { permissionsEvaluationOperator: PermissionsEvaluationOperator; timeout: number; },\n resolvedPermissions: PermissionsEvaluationByContextId,\n): Promise<PermissionsEvaluationByContextId> => {\n let newResolvedPermissions: PermissionsEvaluationByContextId = { ...resolvedPermissions };\n // Call Identity API for unresolved contexts\n const resolvedPermissionsFromIdentityMS = await callIdentityAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n );\n\n // Combine all resolved permissions\n newResolvedPermissions = {\n ...resolvedPermissions,\n ...resolvedPermissionsFromIdentityMS,\n };\n\n cachePermissionsResults(userId, resolvedPermissionsFromIdentityMS, requiredPermissions);\n\n return newResolvedPermissions;\n};\n\n/**\n * Resolves permissions from multiple sources (cache, seen contexts, API)\n * @param userId - User ID\n * @param contextIds - Context IDs to resolve permissions for\n * @param requiredPermissions - Required permissions to check\n * @param options - Evaluation options\n * @returns Combined permissions from all sources: cache, denied cache, and API\n */\nconst resolveAllPermissions = async (\n userId: string,\n contextIds: string[],\n requiredPermissions: string[],\n options: {\n permissionsEvaluationOperator: PermissionsEvaluationOperator;\n timeout: number;\n logger?: Logger;\n },\n): Promise<PermissionsEvaluationByContextId> => {\n const { logger } = options;\n // Cached allowed permissions\n const cachedAllowedPermissions = permissionCache.getUserPermissions(userId, contextIds);\n const resolvedPermissionsFromCache = processCachedPermissions(\n cachedAllowedPermissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n let resolvedPermissions = { ...resolvedPermissionsFromCache };\n\n // Context ids without allowed permissions in cache\n const contextIdsWithoutAllowedCache = contextIds.filter(contextId => !resolvedPermissionsFromCache[contextId]);\n\n // Handle denied cache permissions\n const cachedDeniedPermissions = permissionCache.getCachedDeniedPermissions(userId, contextIdsWithoutAllowedCache);\n\n // Context ids that have never been seen before (not in allowed or denied cache)\n const unseenContextIds = contextIdsWithoutAllowedCache.filter(contextId => !cachedDeniedPermissions[contextId]);\n\n // Process denied permissions cache\n const deniedContextIds = contextIdsWithoutAllowedCache.filter(contextId => cachedDeniedPermissions[contextId]);\n const deniedContextEntries = processCachedDeniedContexts(\n deniedContextIds,\n cachedDeniedPermissions,\n requiredPermissions,\n options.permissionsEvaluationOperator,\n );\n\n // Combine resolved permissions with those resolved from denied cache\n resolvedPermissions = { ...resolvedPermissions, ...deniedContextEntries };\n\n // Resolve the remaining contexts using the Identity API\n const contextIdsNeedingAPI = getContextsNeedingAPI(unseenContextIds, deniedContextIds, deniedContextEntries);\n if (contextIdsNeedingAPI.length) {\n // Call Identity API for unresolved contexts\n return resolvePermissionsFromAPI(\n userId,\n contextIdsNeedingAPI,\n requiredPermissions,\n options,\n resolvedPermissions,\n );\n }\n\n logger?.debug('Final resolved permissions', {\n userId,\n requiredPermissions,\n resolvedPermissions,\n });\n\n return resolvedPermissions;\n};\n\n/**\n * Main SDK function to evaluate user permissions\n * Checks both cached and API-resolved permissions to determine access\n * @param params - {@link EvaluatePermissionsParams}\n * @returns {@link PermissionCheckResult} Detailed permission check result with access status and context\n */\nexport const evaluatePermissions = async ({\n requiredPermissions,\n contextIds,\n logger,\n userId,\n options: {\n requireAll = true,\n permissionsEvaluationOperator = PERMISSIONS_EVALUATION_OPERATORS.AND,\n timeout = 10000,\n },\n}: EvaluatePermissionsParams): Promise<PermissionCheckResult> => {\n const resolvedUserId = userId || getUser()?.id || null;\n\n const validationError = validateEvaluationInput(contextIds, requiredPermissions, resolvedUserId, logger);\n if (validationError) {\n return validationError;\n }\n\n const resolvedPermissions = await resolveAllPermissions(\n resolvedUserId!,\n contextIds,\n requiredPermissions,\n { permissionsEvaluationOperator, timeout, logger },\n );\n\n const isAuthorized = checkAuthorizeRequirements(resolvedPermissions, requireAll);\n\n logger?.info('Resolved permissions', {\n userId: resolvedUserId,\n requiredPermissions,\n resolvedPermissions,\n isAuthorized,\n requireAll,\n permissionsEvaluationOperator,\n contextIds,\n });\n\n return {\n isAuthorized,\n userId: resolvedUserId!,\n resolvedPermissions,\n requiredPermissions,\n contextIds,\n ...(isAuthorized ? {} : { error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS }),\n };\n};\n\nexport default evaluatePermissions;\n","import type { Request, Response, NextFunction } from 'express';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { evaluatePermissions } from '../SDK/evaluatePermissions';\nimport type { EvaluatePermissionsOpts } from '../SDK/types';\nimport ApiUser from '../../user/ApiUser';\nimport { PERMISSION_ERROR_MESSAGES, PERMISSION_ERROR_TYPES, PERMISSIONS_EVALUATION_OPERATORS } from '../SDK/consts';\n\nexport interface RequirePermissionsOptions extends EvaluatePermissionsOpts {\n logger?: LoggerInstanceManager;\n}\nconst getContextIds = (user: ApiUser): string[] => {\n const contexts = Object.keys(user.permissions?.contexts ?? {});\n return Array.from(new Set([\n ...contexts,\n ])).filter(Boolean);\n};\n\nconst handleError = (\n errorLabel: string,\n message: string,\n code: number,\n enforce: boolean | undefined,\n res: Response,\n req: Request,\n next: NextFunction,\n logger?: LoggerInstanceManager,\n additionalInfo?: { requiredPermissions?: string[]; },\n) => {\n if (enforce) {\n logger?.error(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n\n res.status(code).json({\n error: errorLabel,\n message,\n ...(additionalInfo?.requiredPermissions && { requiredPermissions: additionalInfo.requiredPermissions }),\n });\n return;\n }\n\n logger?.warn(message, {\n url: req.url,\n method: req.method,\n requiredPermissions: additionalInfo?.requiredPermissions ?? [],\n ...additionalInfo,\n });\n next();\n};\n\n/**\n * Express middleware that requires specific permissions for route access\n *\n * @param requiredPermissions - Array of permissions required to access the route\n * @param options - Configuration options for permission checking\n * @returns Express middleware function\n */\nexport const requirePermissions = (\n requiredPermissions: string[],\n options: RequirePermissionsOptions = {\n enforce: false,\n requireAll: true,\n permissionsEvaluationOperator: PERMISSIONS_EVALUATION_OPERATORS.AND,\n },\n) => async (req: Request, res: Response, next: NextFunction): Promise<void> => {\n try {\n const {\n logger,\n enforce,\n requireAll,\n permissionsEvaluationOperator,\n } = options;\n\n const handleErrorWrapper = (errorLabel: string, message: string, code: number) => handleError(\n errorLabel,\n message,\n code,\n enforce,\n res,\n req,\n next,\n logger,\n { requiredPermissions },\n );\n const user = req.user;\n\n const contextIds: string[] = getContextIds(user);\n\n if (!contextIds?.length) {\n handleErrorWrapper(\n PERMISSION_ERROR_TYPES.BAD_REQUEST,\n PERMISSION_ERROR_MESSAGES.BAD_REQUEST,\n 400,\n );\n return;\n }\n\n // Evaluate permissions using SDK\n const result = await evaluatePermissions({\n requiredPermissions,\n contextIds,\n logger,\n userId: user.id,\n options: {\n requireAll: requireAll ?? true,\n permissionsEvaluationOperator: permissionsEvaluationOperator ?? PERMISSIONS_EVALUATION_OPERATORS.AND,\n timeout: 10000,\n ...(logger && { logger }),\n },\n });\n\n if (result.isAuthorized) {\n logger?.info('User has required permissions', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n if (!enforce) {\n logger?.warn('User does not have required permissions, skipping enforcement', {\n userId: user.id,\n requiredPermissions,\n contextIds,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(403).json({\n error: PERMISSION_ERROR_TYPES.INSUFFICIENT_PERMISSIONS,\n message: PERMISSION_ERROR_MESSAGES.INSUFFICIENT_PERMISSIONS,\n required: requiredPermissions,\n contexts: contextIds,\n userId: result.userId,\n });\n } catch (error) {\n const requestLogger = options.logger;\n requestLogger?.error('Error in requirePermissions middleware', {\n error,\n requiredPermissions,\n url: req.url,\n method: req.method,\n });\n\n if (!options.enforce) {\n options.logger?.error('Error during permission check, skipping enforcement', {\n error,\n url: req.url,\n method: req.method,\n });\n next();\n return;\n }\n\n res.status(500).json({\n error: PERMISSION_ERROR_TYPES.INTERNAL_ERROR,\n message: PERMISSION_ERROR_MESSAGES.INTERNAL_ERROR,\n });\n }\n};\n\nexport default requirePermissions;\n","import { evaluatePermissions } from './SDK/evaluatePermissions';\nimport { requirePermissions } from './middleware/requirePermissions';\n\ninterface SdkExports {\n evaluatePermissions: typeof evaluatePermissions;\n}\n\ninterface MiddlewareExports {\n requirePermissions: typeof requirePermissions;\n}\n\ninterface PermissionsModule {\n sdk: SdkExports;\n middlewares: MiddlewareExports;\n}\n\nexport const sdk: SdkExports = {\n evaluatePermissions,\n};\n\nexport const middlewares: MiddlewareExports = {\n requirePermissions,\n};\n\nexport default {\n sdk,\n middlewares,\n} as PermissionsModule;\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport * as outbreak from '@autofleet/outbreak';\nimport User, {\n middleware,\n eagerLoadPermissionsMiddleware,\n middlewareWithDecode,\n getDecodedBearer,\n appMiddleware,\n createOrSetRabbitTrace,\n} from './user';\nimport { authFromUserIdHeaderPlugin } from './user/fastify';\nimport { type UserPayload, CONTEXTS_IDS_HEADER } from './user/ApiUser';\nimport {\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n} from './check-permission';\nimport { UnauthorizedAccessError } from './errors';\nimport { getRefreshTokenSecret, getTokenSecret } from './secret-getter';\nimport { AUTHORIZATION_METHODS, getAuthorizationHeader } from './authorization';\nimport * as permissions from './permissions';\n\nconst getCurrentPayload: typeof outbreak.getCurrentContext = outbreak.getCurrentContext;\n\ntype OutbreakOptions = Parameters<typeof outbreak.default>[0];\ntype LoggerWithContextMiddleware = Partial<Pick<LoggerInstanceManager, 'addContextMiddleware'>>;\nconst enableTracing = ({ outbreakOptions = {}, logger }: { outbreakOptions?: OutbreakOptions; logger?: LoggerWithContextMiddleware; } = {}): void => {\n outbreak.default({\n headersPrefix: 'x-af',\n contextMiddlewareGetter: logger?.addContextMiddleware,\n ...outbreakOptions,\n });\n};\n\nconst traceTypes: typeof outbreak.traceTypes = outbreak.traceTypes;\nconst newTrace: typeof outbreak.newTrace = outbreak.newTrace;\n\nexport {\n traceTypes,\n newTrace,\n enableTracing,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n getRefreshTokenSecret,\n getTokenSecret,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n type UserPayload,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\ninterface Default {\n traceTypes: typeof outbreak.traceTypes;\n newTrace: typeof outbreak.newTrace;\n User: typeof User;\n middleware: typeof middleware;\n middlewareWithDecode: typeof middlewareWithDecode;\n eagerLoadPermissionsMiddleware: typeof eagerLoadPermissionsMiddleware;\n getCurrentPayload: typeof outbreak.getCurrentContext;\n getDecodedBearer: typeof getDecodedBearer;\n checkFleetPermission: typeof checkFleetPermission;\n checkBusinessModelPermission: typeof checkBusinessModelPermission;\n checkDemandSourcePermission: typeof checkDemandSourcePermission;\n isUserExist: typeof isUserExist;\n getUser: typeof getUser;\n UnauthorizedAccessError: typeof UnauthorizedAccessError;\n appMiddleware: typeof appMiddleware;\n createOrSetRabbitTrace: typeof createOrSetRabbitTrace;\n outbreak: typeof outbreak;\n AUTHORIZATION_METHODS: typeof AUTHORIZATION_METHODS;\n getAuthorizationHeader: typeof getAuthorizationHeader;\n CONTEXTS_IDS_HEADER: typeof CONTEXTS_IDS_HEADER;\n authFromUserIdHeaderPlugin: typeof authFromUserIdHeaderPlugin;\n permissions: typeof permissions;\n}\n\nconst zehutDefault: Default = {\n traceTypes,\n newTrace,\n User,\n middleware,\n middlewareWithDecode,\n eagerLoadPermissionsMiddleware,\n getCurrentPayload,\n getDecodedBearer,\n checkFleetPermission,\n checkBusinessModelPermission,\n checkDemandSourcePermission,\n isUserExist,\n getUser,\n UnauthorizedAccessError,\n appMiddleware,\n createOrSetRabbitTrace,\n outbreak,\n AUTHORIZATION_METHODS,\n getAuthorizationHeader,\n CONTEXTS_IDS_HEADER,\n authFromUserIdHeaderPlugin,\n permissions,\n};\n\nexport default zehutDefault;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/zehut",
3
- "version": "4.6.3-beta-5cca7a57.5",
3
+ "version": "4.6.4",
4
4
  "description": "manage user's identity",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -31,12 +31,11 @@
31
31
  },
32
32
  "homepage": "https://github.com/Autofleet/autorepo/tree/master/packages/zehut#readme",
33
33
  "dependencies": {
34
- "@autofleet/common-types": "4.101.0",
35
34
  "jsonwebtoken": "^8.5.1",
36
35
  "moment": "^2.30.1",
37
36
  "node-cache": "^5.1.2",
38
37
  "object-hash": "^3.0.0",
39
- "@autofleet/network": "^1.9.5",
38
+ "@autofleet/network": "^1.9.7",
40
39
  "@autofleet/outbreak": "^2.5.11"
41
40
  },
42
41
  "devDependencies": {
@@ -50,7 +49,7 @@
50
49
  "fastify": "^5.6.2",
51
50
  "nock": "^14.0.0",
52
51
  "supertest": "^7.0.0",
53
- "@autofleet/logger": "^4.2.39"
52
+ "@autofleet/logger": "^4.2.41"
54
53
  },
55
54
  "files": [
56
55
  "lib/**/*"