@authhero/multi-tenancy 14.7.0 → 14.8.0

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.
@@ -1 +1 @@
1
- "use strict";var re=Object.defineProperty;var ae=(t,e,n)=>e in t?re(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var D=(t,e,n)=>ae(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const se=require("hono"),y=require("authhero"),I=require("@hono/zod-openapi");function B(t){const{controlPlaneTenantId:e,requireOrganizationMatch:n=!0}=t;return{async onTenantAccessValidation(r,a){if(a===e)return!0;if(n){const c=r.var.org_name,d=r.var.organization_id,s=c||d;return s?s.toLowerCase()===a.toLowerCase():!1}return!0}}}function K(t,e,n,r){if(e===n)return!0;const a=r||t;return a?a.toLowerCase()===e.toLowerCase():!1}function V(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(n){console.error(`Failed to resolve data adapters for tenant ${e}:`,n);return}}}}function oe(t){return`urn:authhero:tenant:${t.toLowerCase()}`}function W(t){return{async beforeCreate(e,n){return!n.audience&&n.id?{...n,audience:oe(n.id)}:n},async afterCreate(e,n){const{accessControl:r,databaseIsolation:a}=t;r&&e.ctx&&await ie(e,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(e,n){const{accessControl:r,databaseIsolation:a}=t;if(r)try{const d=(await e.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(s=>s.name===n);d&&await e.adapters.organizations.remove(r.controlPlaneTenantId,d.id)}catch(c){console.warn(`Failed to remove organization for tenant ${n}:`,c)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(c){console.warn(`Failed to deprovision database for tenant ${n}:`,c)}}}}async function ie(t,e,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:c,issuer:d,adminRoleName:s="Tenant Admin",adminRoleDescription:f="Full access to all tenant management operations",addCreatorToOrganization:o=!0}=n,i=await t.adapters.organizations.create(r,{name:e.id,display_name:e.friendly_name||e.id});let u;if(d&&(u=await le(t,r,s,f)),o&&t.ctx){const l=t.ctx.var.user;if(l!=null&&l.sub&&!await ce(t,r,l.sub))try{await t.adapters.userOrganizations.create(r,{user_id:l.sub,organization_id:i.id}),u&&await t.adapters.userRoles.create(r,l.sub,u,i.id)}catch(p){console.warn(`Failed to add creator ${l.sub} to organization ${i.id}:`,p)}}c&&c.length>0&&console.log(`Would assign roles ${c.join(", ")} to organization ${i.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${i.id}`)}async function ce(t,e,n){const r=await t.adapters.userRoles.list(e,n,void 0,"");for(const a of r)if((await t.adapters.rolePermissions.list(e,a.id,{per_page:1e3})).some(s=>s.permission_name==="admin:organizations"))return!0;return!1}async function le(t,e,n,r){const c=(await t.adapters.roles.list(e,{})).roles.find(o=>o.name===n);if(c)return c.id;const d=await t.adapters.roles.create(e,{name:n,description:r}),s=y.MANAGEMENT_API_AUDIENCE,f=y.MANAGEMENT_API_SCOPES.map(o=>({role_id:d.id,resource_server_identifier:s,permission_name:o.value}));return await t.adapters.rolePermissions.assign(e,d.id,f),d.id}function k(t,e,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:c}=t,d=new Map;async function s(i,u,l){return(await e(i).list(u,{q:`name:${l}`,per_page:1}))[0]??null}async function f(i){const u=await a(),l=e(await c(r));await Promise.all(u.map(async m=>{try{const p=await c(m),g=e(p),w={...l.transform(i),is_system:!0},h=await s(p,m,i.name),A=h?g.getId(h):void 0;if(h&&A){const P=g.preserveOnUpdate?g.preserveOnUpdate(h,w):w;await g.update(m,A,P)}else await g.create(m,w)}catch(p){console.error(`Failed to sync ${l.listKey} "${i.name}" to tenant "${m}":`,p)}}))}async function o(i){const u=await a();await Promise.all(u.map(async l=>{try{const m=await c(l),p=e(m),g=await s(m,l,i),v=g?p.getId(g):void 0;g&&v&&await p.remove(l,v)}catch(m){console.error(`Failed to delete entity "${i}" from tenant "${l}":`,m)}}))}return{afterCreate:async(i,u)=>{i.tenantId===r&&n(u)&&await f(u)},afterUpdate:async(i,u,l)=>{i.tenantId===r&&n(l)&&await f(l)},beforeDelete:async(i,u)=>{if(i.tenantId!==r)return;const m=await e(i.adapters).get(i.tenantId,u);m&&n(m)&&d.set(u,m)},afterDelete:async(i,u)=>{if(i.tenantId!==r)return;const l=d.get(u);l&&(d.delete(u),await o(l.name))}}}function N(t,e,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:c}=t;return{async afterCreate(d,s){if(s.id!==r)try{const f=await a(),o=await c(s.id),i=e(f),u=e(o),l=await y.fetchAll(m=>i.listPaginated(r,m),i.listKey,{cursorField:"id",pageSize:100});await Promise.all(l.filter(m=>n(m)).map(async m=>{try{const p=i.transform(m);await u.create(s.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync entity to new tenant "${s.id}":`,p)}}))}catch(f){console.error(`Failed to sync entities to new tenant "${s.id}":`,f)}}}}const H=t=>({list:async(e,n)=>(await t.resourceServers.list(e,n)).resource_servers,listPaginated:(e,n)=>t.resourceServers.list(e,n),get:(e,n)=>t.resourceServers.get(e,n),create:(e,n)=>t.resourceServers.create(e,n),update:(e,n,r)=>t.resourceServers.update(e,n,r),remove:(e,n)=>t.resourceServers.remove(e,n),listKey:"resource_servers",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),G=t=>({list:async(e,n)=>(await t.roles.list(e,n)).roles,listPaginated:(e,n)=>t.roles.list(e,n),get:(e,n)=>t.roles.get(e,n),create:(e,n)=>t.roles.create(e,n),update:(e,n,r)=>t.roles.update(e,n,r),remove:(e,n)=>t.roles.remove(e,n),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function L(t){var e;return((e=t.metadata)==null?void 0:e.sync)!==!1}function Q(t){const{sync:e={},filters:n={}}=t,r=e.resourceServers??!0,a=e.roles??!0,c=p=>L(p)?n.resourceServers?n.resourceServers(p):!0:!1,d=p=>L(p)?n.roles?n.roles(p):!0:!1,s=r?k(t,H,c):void 0,f=a?k(t,G,d):void 0,o=r?N(t,H,c):void 0,i=a?N(t,G,d):void 0,u=a?{async afterCreate(p,g){var v;if(g.id!==t.controlPlaneTenantId){await((v=i==null?void 0:i.afterCreate)==null?void 0:v.call(i,p,g));try{const w=await t.getControlPlaneAdapters(),h=await t.getAdapters(g.id),A=await y.fetchAll(_=>w.roles.list(t.controlPlaneTenantId,_),"roles",{cursorField:"id",pageSize:100}),P=new Map;for(const _ of A.filter(T=>{var b;return((b=n.roles)==null?void 0:b.call(n,T))??!0})){const T=await l(h,g.id,_.name);T&&P.set(_.name,T.id)}for(const _ of A.filter(T=>{var b;return((b=n.roles)==null?void 0:b.call(n,T))??!0})){const T=P.get(_.name);if(T)try{const b=await w.rolePermissions.list(t.controlPlaneTenantId,_.id,{});b.length>0&&await h.rolePermissions.assign(g.id,T,b.map(C=>({role_id:T,resource_server_identifier:C.resource_server_identifier,permission_name:C.permission_name})))}catch(b){console.error(`Failed to sync permissions for role "${_.name}" to tenant "${g.id}":`,b)}}}catch(w){console.error(`Failed to sync role permissions to tenant "${g.id}":`,w)}}}}:void 0;async function l(p,g,v){return(await p.roles.list(g,{q:`name:${v}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:s,roles:f},tenantHooks:{async afterCreate(p,g){const v=[o==null?void 0:o.afterCreate,(u==null?void 0:u.afterCreate)??(i==null?void 0:i.afterCreate)],w=[];for(const h of v)if(h)try{await h(p,g)}catch(A){w.push(A instanceof Error?A:new Error(String(A)))}if(w.length===1)throw w[0];if(w.length>1)throw new AggregateError(w,w.map(h=>h.message).join("; "))}}}}var S=class extends Error{constructor(e=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});D(this,"res");D(this,"status");this.res=n==null?void 0:n.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function $(t,e){const n=new I.OpenAPIHono;return n.openapi(I.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:y.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:I.z.object({tenants:I.z.array(y.tenantSchema),start:I.z.number().optional(),limit:I.z.number().optional(),length:I.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var p,g,v,w,h,A;const a=r.req.valid("query"),{page:c,per_page:d,include_totals:s,q:f}=a,o=r.var.user,i=(o==null?void 0:o.permissions)||[];if(i.includes("auth:read")||i.includes("admin:organizations")){const P=await r.env.data.tenants.list({page:c,per_page:d,include_totals:s,q:f});return s?r.json({tenants:P.tenants,start:((p=P.totals)==null?void 0:p.start)??0,limit:((g=P.totals)==null?void 0:g.limit)??d,length:P.tenants.length}):r.json({tenants:P.tenants})}const l=((v=t.accessControl)==null?void 0:v.controlPlaneTenantId)??((w=r.env.data.multiTenancyConfig)==null?void 0:w.controlPlaneTenantId);if(l&&(o!=null&&o.sub)){const _=(await y.fetchAll(O=>r.env.data.userOrganizations.listUserOrganizations(l,o.sub,O),"organizations")).map(O=>O.name);if(_.length===0)return s?r.json({tenants:[],start:0,limit:d??50,length:0}):r.json({tenants:[]});const T=_.length,b=c??0,C=d??50,z=b*C,M=_.slice(z,z+C);if(M.length===0)return s?r.json({tenants:[],start:z,limit:C,length:T}):r.json({tenants:[]});const E=M.map(O=>`id:${O}`).join(" OR "),ne=f?`(${E}) AND (${f})`:E,j=await r.env.data.tenants.list({q:ne,per_page:C,include_totals:!1});return s?r.json({tenants:j.tenants,start:z,limit:C,length:T}):r.json({tenants:j.tenants})}const m=await r.env.data.tenants.list({page:c,per_page:d,include_totals:s,q:f});return s?r.json({tenants:m.tenants,start:((h=m.totals)==null?void 0:h.start)??0,limit:((A=m.totals)==null?void 0:A.limit)??d,length:m.tenants.length}):r.json({tenants:m.tenants})}),n.openapi(I.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:y.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:y.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var f,o;const a=r.var.user;if(!(a!=null&&a.sub))throw new S(401,{message:"Authentication required to create tenants"});let c=r.req.valid("json");const d={adapters:r.env.data,ctx:r};(f=e.tenants)!=null&&f.beforeCreate&&(c=await e.tenants.beforeCreate(d,c));const s=await r.env.data.tenants.create(c);return(o=e.tenants)!=null&&o.afterCreate&&await e.tenants.afterCreate(d,s),r.json(s,201)}),n.openapi(I.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:I.z.object({id:I.z.string()})},security:[{Bearer:["delete:tenants"]}],responses:{204:{description:"Tenant deleted"},403:{description:"Access denied or cannot delete the control plane"},404:{description:"Tenant not found"}}}),async r=>{var f,o,i,u;const{id:a}=r.req.valid("param"),c=((f=t.accessControl)==null?void 0:f.controlPlaneTenantId)??((o=r.env.data.multiTenancyConfig)==null?void 0:o.controlPlaneTenantId);if(c){const l=r.var.user;if(!(l!=null&&l.sub))throw new S(401,{message:"Authentication required"});if(a===c)throw new S(403,{message:"Cannot delete the control plane"});if(!(await y.fetchAll(g=>r.env.data.userOrganizations.listUserOrganizations(c,l.sub,g),"organizations")).some(g=>g.name===a))throw new S(403,{message:"Access denied to this tenant"})}if(!await r.env.data.tenants.get(a))throw new S(404,{message:"Tenant not found"});const s={adapters:r.env.data,ctx:r};return(i=e.tenants)!=null&&i.beforeDelete&&await e.tenants.beforeDelete(s,a),await r.env.data.tenants.remove(a),(u=e.tenants)!=null&&u.afterDelete&&await e.tenants.afterDelete(s,a),r.body(null,204)}),n}function de(t){const e=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:n,type:r}of e){const a=t.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function ue(t,e,n){try{switch(n.type){case"resource_server":{const r=await t.resourceServers.get(e,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await t.roles.get(e,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await t.connections.get(e,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function fe(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function J(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const n=de(t.req.path);if(!n)return e();const r=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!r)return e();if(await ue(t.env.data,r,n))throw new S(403,{message:`This ${fe(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}const me=["client_secret","app_secret","twilio_token"];function U(t){if(!t)return t;const e={...t};for(const n of me)delete e[n];return e}function X(t,e){const{controlPlaneTenantId:n,controlPlaneClientId:r,excludeSensitiveFields:a=!1}=e;return{...t,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...t.connections,get:async(c,d)=>{var l;const s=await t.connections.get(c,d);if(!s||!n||c===n)return s;const o=(l=(await t.connections.list(n)).connections)==null?void 0:l.find(m=>m.strategy===s.strategy);if(!(o!=null&&o.options))return s;const i=y.connectionSchema.parse({...o,...s}),u=a?U(o.options):o.options;return i.options=y.connectionOptionsSchema.parse({...u||{},...s.options}),i},list:async(c,d)=>{const s=await t.connections.list(c,d);if(!n||c===n)return s;const f=await t.connections.list(n),o=s.connections.map(i=>{var p;const u=(p=f.connections)==null?void 0:p.find(g=>g.strategy===i.strategy);if(!(u!=null&&u.options))return i;const l=y.connectionSchema.parse({...u,...i}),m=a?U(u.options):u.options;return l.options=y.connectionOptionsSchema.parse({...m||{},...i.options}),l});return{...s,connections:o}}}}}function R(t,e){return X(t,e)}function Y(t){return async(e,n)=>{const r=e.var.user;return(r==null?void 0:r.tenant_id)===t&&r.org_name&&e.set("tenant_id",r.org_name),n()}}function Z(t){return async(e,n)=>{if(!t.accessControl)return n();const{controlPlaneTenantId:r}=t.accessControl,a=e.var.org_name,c=e.var.organization_id,d=a||c;let s=e.var.tenant_id;const f=e.var.user,i=(f!=null&&f.aud?Array.isArray(f.aud)?f.aud:[f.aud]:[]).includes(y.MANAGEMENT_API_AUDIENCE);if(!s&&d&&i&&(e.set("tenant_id",d),s=d),!s)throw new S(400,{message:"Tenant ID not found in request"});if(!K(c,s,r,a))throw new S(403,{message:`Access denied to tenant ${s}`});return n()}}function x(t){return async(e,n)=>{if(!t.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:c}=t.subdomainRouting,d=e.req.header("host")||"";let s=null;if(d.endsWith(r)){const o=d.slice(0,-(r.length+1));o&&!o.includes(".")&&(s=o)}if(s&&a.includes(s)&&(s=null),!s)return t.accessControl&&e.set("tenant_id",t.accessControl.controlPlaneTenantId),n();let f=null;if(c)f=await c(s);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const o=await e.env.data.organizations.get(t.accessControl.controlPlaneTenantId,s);o&&(f=o.id)}catch{}if(!f)throw new S(404,{message:`Tenant not found for subdomain: ${s}`});return e.set("tenant_id",f),n()}}function ee(t){return async(e,n)=>{if(!t.databaseIsolation)return n();const r=e.var.tenant_id;if(!r)throw new S(400,{message:"Tenant ID not found in request"});try{const a=await t.databaseIsolation.getAdapters(r);e.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${r}:`,a),new S(500,{message:"Failed to resolve tenant database"})}return n()}}function q(t){const e=x(t),n=Z(t),r=ee(t);return async(a,c)=>(await e(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),c())}function pe(t){const{dataAdapter:e,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:c={resourceServers:!0,roles:!0},defaultPermissions:d=["tenant:admin"],requireOrganizationMatch:s=!1,managementApiExtensions:f=[],entityHooks:o,getChildTenantIds:i,getAdapters:u,...l}=t;let m=e,p=e;n&&(m=R(e,{controlPlaneTenantId:r,controlPlaneClientId:a}),p=R(e,{controlPlaneTenantId:r,controlPlaneClientId:a,excludeSensitiveFields:!0}));const g=c!==!1,v=g?{resourceServers:c.resourceServers??!0,roles:c.roles??!0}:{resourceServers:!1,roles:!1},A={controlPlaneTenantId:r,getChildTenantIds:i??(async()=>(await y.fetchAll(M=>m.tenants.list(M),"tenants",{cursorField:"id",pageSize:100})).filter(M=>M.id!==r).map(M=>M.id)),getAdapters:u??(async()=>m),getControlPlaneAdapters:async()=>m,sync:v},{entityHooks:P,tenantHooks:_}=Q(A),T={resourceServers:[P.resourceServers,...(o==null?void 0:o.resourceServers)??[]],roles:[P.roles,...(o==null?void 0:o.roles)??[]],connections:(o==null?void 0:o.connections)??[],tenants:(o==null?void 0:o.tenants)??[],rolePermissions:(o==null?void 0:o.rolePermissions)??[]},b=$({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:s,defaultPermissions:d}},{tenants:_}),{app:C}=y.init({dataAdapter:m,managementDataAdapter:p,...l,entityHooks:T,managementApiExtensions:[...f,{path:"/tenants",router:b}]});return C.use("/api/v2/*",Y(r)),g&&C.use("/api/v2/*",J()),{app:C,controlPlaneTenantId:r}}function ge(t){const e=F(t);return{name:"multi-tenancy",middleware:q(t),hooks:e,routes:[{path:"/management",handler:$(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (control plane: ${t.accessControl.controlPlaneTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function F(t){const e=t.accessControl?B(t.accessControl):{},n=t.databaseIsolation?V(t.databaseIsolation):{},r=W(t);return{...e,...n,tenants:r}}function te(t){const e=new se.Hono,n=F(t);return e.route("/tenants",$(t,n)),e}function we(t){return{hooks:F(t),middleware:q(t),app:te(t),config:t,wrapAdapters:(e,n)=>{var r;return R(e,{controlPlaneTenantId:(r=t.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=B;exports.createAccessControlMiddleware=Z;exports.createControlPlaneTenantMiddleware=Y;exports.createDatabaseHooks=V;exports.createDatabaseMiddleware=ee;exports.createMultiTenancy=te;exports.createMultiTenancyHooks=F;exports.createMultiTenancyMiddleware=q;exports.createMultiTenancyPlugin=ge;exports.createProtectSyncedMiddleware=J;exports.createProvisioningHooks=W;exports.createRuntimeFallbackAdapter=X;exports.createSubdomainMiddleware=x;exports.createSyncHooks=Q;exports.createTenantsOpenAPIRouter=$;exports.initMultiTenant=pe;exports.setupMultiTenancy=we;exports.validateTenantAccess=K;exports.withRuntimeFallback=R;
1
+ "use strict";var re=Object.defineProperty;var ae=(t,e,n)=>e in t?re(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var D=(t,e,n)=>ae(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const se=require("hono"),T=require("authhero"),S=require("@hono/zod-openapi");function B(t){const{controlPlaneTenantId:e,requireOrganizationMatch:n=!0}=t;return{async onTenantAccessValidation(r,a){if(a===e)return!0;if(n){const o=r.var.org_name,c=r.var.organization_id,s=o||c;return s?s.toLowerCase()===a.toLowerCase():!1}return!0}}}function K(t,e,n,r){if(e===n)return!0;const a=r||t;return a?a.toLowerCase()===e.toLowerCase():!1}function W(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(n){console.error(`Failed to resolve data adapters for tenant ${e}:`,n);return}}}}function oe(t){return`urn:authhero:tenant:${t.toLowerCase()}`}function V(t){return{async beforeCreate(e,n){return!n.audience&&n.id?{...n,audience:oe(n.id)}:n},async afterCreate(e,n){const{accessControl:r,databaseIsolation:a}=t;r&&e.ctx&&await ie(e,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(e,n){const{accessControl:r,databaseIsolation:a}=t;if(r)try{const c=(await e.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(s=>s.name===n);c&&await e.adapters.organizations.remove(r.controlPlaneTenantId,c.id)}catch(o){console.warn(`Failed to remove organization for tenant ${n}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(o){console.warn(`Failed to deprovision database for tenant ${n}:`,o)}}}}async function ie(t,e,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:o,issuer:c,adminRoleName:s="Tenant Admin",adminRoleDescription:d="Full access to all tenant management operations",addCreatorToOrganization:i=!0}=n,l=await t.adapters.organizations.create(r,{name:e.id,display_name:e.friendly_name||e.id});let g;if(c&&(g=await le(t,r,s,d)),i&&t.ctx){const u=t.ctx.var.user;if(u!=null&&u.sub&&!await ce(t,r,u.sub))try{await t.adapters.userOrganizations.create(r,{user_id:u.sub,organization_id:l.id}),g&&await t.adapters.userRoles.create(r,u.sub,g,l.id)}catch(m){console.warn(`Failed to add creator ${u.sub} to organization ${l.id}:`,m)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${l.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${l.id}`)}async function ce(t,e,n){const r=await t.adapters.userRoles.list(e,n,void 0,"");for(const a of r)if((await t.adapters.rolePermissions.list(e,a.id,{per_page:1e3})).some(s=>s.permission_name==="admin:organizations"))return!0;return!1}async function le(t,e,n,r){const o=(await t.adapters.roles.list(e,{})).roles.find(i=>i.name===n);if(o)return o.id;const c=await t.adapters.roles.create(e,{name:n,description:r}),s=T.MANAGEMENT_API_AUDIENCE,d=T.MANAGEMENT_API_SCOPES.map(i=>({role_id:c.id,resource_server_identifier:s,permission_name:i.value}));return await t.adapters.rolePermissions.assign(e,c.id,d),c.id}function N(t,e,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:o}=t,c=new Map;async function s(l,g,u){return(await e(l).list(g,{q:`name:${u}`,per_page:1}))[0]??null}async function d(l){const g=await a(),u=e(await o(r));await Promise.all(g.map(async f=>{try{const m=await o(f),p=e(m),w={...u.transform(l),is_system:!0},y=await s(m,f,l.name),P=y?p.getId(y):void 0;if(y&&P){const A=p.preserveOnUpdate?p.preserveOnUpdate(y,w):w;await p.update(f,P,A)}else await p.create(f,w)}catch(m){console.error(`Failed to sync ${u.listKey} "${l.name}" to tenant "${f}":`,m)}}))}async function i(l){const g=await a();await Promise.all(g.map(async u=>{try{const f=await o(u),m=e(f),p=await s(f,u,l),h=p?m.getId(p):void 0;p&&h&&await m.remove(u,h)}catch(f){console.error(`Failed to delete entity "${l}" from tenant "${u}":`,f)}}))}return{afterCreate:async(l,g)=>{l.tenantId===r&&n(g)&&await d(g)},afterUpdate:async(l,g,u)=>{l.tenantId===r&&n(u)&&await d(u)},beforeDelete:async(l,g)=>{if(l.tenantId!==r)return;const f=await e(l.adapters).get(l.tenantId,g);f&&n(f)&&c.set(g,f)},afterDelete:async(l,g)=>{if(l.tenantId!==r)return;const u=c.get(g);u&&(c.delete(g),await i(u.name))}}}function H(t,e,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:o}=t;return{async afterCreate(c,s){if(s.id!==r)try{const d=await a(),i=await o(s.id),l=e(d),g=e(i),u=await T.fetchAll(f=>l.listPaginated(r,f),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(u.filter(f=>n(f)).map(async f=>{try{const m=l.transform(f);await g.create(s.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync entity to new tenant "${s.id}":`,m)}}))}catch(d){console.error(`Failed to sync entities to new tenant "${s.id}":`,d)}}}}const G=t=>({list:async(e,n)=>(await t.resourceServers.list(e,n)).resource_servers,listPaginated:(e,n)=>t.resourceServers.list(e,n),get:(e,n)=>t.resourceServers.get(e,n),create:(e,n)=>t.resourceServers.create(e,n),update:(e,n,r)=>t.resourceServers.update(e,n,r),remove:(e,n)=>t.resourceServers.remove(e,n),listKey:"resource_servers",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,identifier:e.identifier,scopes:e.scopes,signing_alg:e.signing_alg,token_lifetime:e.token_lifetime,token_lifetime_for_web:e.token_lifetime_for_web})}),L=t=>({list:async(e,n)=>(await t.roles.list(e,n)).roles,listPaginated:(e,n)=>t.roles.list(e,n),get:(e,n)=>t.roles.get(e,n),create:(e,n)=>t.roles.create(e,n),update:(e,n,r)=>t.roles.update(e,n,r),remove:(e,n)=>t.roles.remove(e,n),listKey:"roles",getId:e=>e.id,transform:e=>({id:e.id,name:e.name,description:e.description})});function U(t){var e;return((e=t.metadata)==null?void 0:e.sync)!==!1}function Q(t){const{sync:e={},filters:n={}}=t,r=e.resourceServers??!0,a=e.roles??!0,o=m=>U(m)?n.resourceServers?n.resourceServers(m):!0:!1,c=m=>U(m)?n.roles?n.roles(m):!0:!1,s=r?N(t,G,o):void 0,d=a?N(t,L,c):void 0,i=r?H(t,G,o):void 0,l=a?H(t,L,c):void 0,g=a?{async afterCreate(m,p){var h;if(p.id!==t.controlPlaneTenantId){await((h=l==null?void 0:l.afterCreate)==null?void 0:h.call(l,m,p));try{const w=await t.getControlPlaneAdapters(),y=await t.getAdapters(p.id),P=await T.fetchAll(C=>w.roles.list(t.controlPlaneTenantId,C),"roles",{cursorField:"id",pageSize:100}),A=new Map;for(const C of P.filter(v=>{var _;return((_=n.roles)==null?void 0:_.call(n,v))??!0})){const v=await u(y,p.id,C.name);v&&A.set(C.name,v.id)}for(const C of P.filter(v=>{var _;return((_=n.roles)==null?void 0:_.call(n,v))??!0})){const v=A.get(C.name);if(v)try{const _=await w.rolePermissions.list(t.controlPlaneTenantId,C.id,{});_.length>0&&await y.rolePermissions.assign(p.id,v,_.map(b=>({role_id:v,resource_server_identifier:b.resource_server_identifier,permission_name:b.permission_name})))}catch(_){console.error(`Failed to sync permissions for role "${C.name}" to tenant "${p.id}":`,_)}}}catch(w){console.error(`Failed to sync role permissions to tenant "${p.id}":`,w)}}}}:void 0;async function u(m,p,h){return(await m.roles.list(p,{q:`name:${h}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:s,roles:d},tenantHooks:{async afterCreate(m,p){const h=[i==null?void 0:i.afterCreate,(g==null?void 0:g.afterCreate)??(l==null?void 0:l.afterCreate)],w=[];for(const y of h)if(y)try{await y(m,p)}catch(P){w.push(P instanceof Error?P:new Error(String(P)))}if(w.length===1)throw w[0];if(w.length>1)throw new AggregateError(w,w.map(y=>y.message).join("; "))}}}}var I=class extends Error{constructor(e=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});D(this,"res");D(this,"status");this.res=n==null?void 0:n.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function $(t,e){const n=new S.OpenAPIHono;return n.openapi(S.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:T.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:S.z.object({tenants:S.z.array(T.tenantSchema),start:S.z.number().optional(),limit:S.z.number().optional(),length:S.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var m,p,h,w,y,P;const a=r.req.valid("query"),{page:o,per_page:c,include_totals:s,q:d}=a,i=r.var.user,l=(i==null?void 0:i.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const A=await r.env.data.tenants.list({page:o,per_page:c,include_totals:s,q:d});return s?r.json({tenants:A.tenants,start:((m=A.totals)==null?void 0:m.start)??0,limit:((p=A.totals)==null?void 0:p.limit)??c,length:A.tenants.length}):r.json({tenants:A.tenants})}const u=((h=t.accessControl)==null?void 0:h.controlPlaneTenantId)??((w=r.env.data.multiTenancyConfig)==null?void 0:w.controlPlaneTenantId);if(u&&(i!=null&&i.sub)){const C=(await T.fetchAll(R=>r.env.data.userOrganizations.listUserOrganizations(u,i.sub,R),"organizations")).map(R=>R.name);if(C.length===0)return s?r.json({tenants:[],start:0,limit:c??50,length:0}):r.json({tenants:[]});const v=C.length,_=o??0,b=c??50,z=_*b,M=C.slice(z,z+b);if(M.length===0)return s?r.json({tenants:[],start:z,limit:b,length:v}):r.json({tenants:[]});const k=M.map(R=>`id:${R}`).join(" OR "),ne=d?`(${k}) AND (${d})`:k,j=await r.env.data.tenants.list({q:ne,per_page:b,include_totals:!1});return s?r.json({tenants:j.tenants,start:z,limit:b,length:v}):r.json({tenants:j.tenants})}const f=await r.env.data.tenants.list({page:o,per_page:c,include_totals:s,q:d});return s?r.json({tenants:f.tenants,start:((y=f.totals)==null?void 0:y.start)??0,limit:((P=f.totals)==null?void 0:P.limit)??c,length:f.tenants.length}):r.json({tenants:f.tenants})}),n.openapi(S.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:T.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:T.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var d,i;const a=r.var.user;if(!(a!=null&&a.sub))throw new I(401,{message:"Authentication required to create tenants"});let o=r.req.valid("json");const c={adapters:r.env.data,ctx:r};(d=e.tenants)!=null&&d.beforeCreate&&(o=await e.tenants.beforeCreate(c,o));const s=await r.env.data.tenants.create(o);return(i=e.tenants)!=null&&i.afterCreate&&await e.tenants.afterCreate(c,s),r.json(s,201)}),n.openapi(S.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:S.z.object({id:S.z.string()})},security:[{Bearer:["delete:tenants"]}],responses:{204:{description:"Tenant deleted"},403:{description:"Access denied or cannot delete the control plane"},404:{description:"Tenant not found"}}}),async r=>{var d,i,l,g;const{id:a}=r.req.valid("param"),o=((d=t.accessControl)==null?void 0:d.controlPlaneTenantId)??((i=r.env.data.multiTenancyConfig)==null?void 0:i.controlPlaneTenantId);if(o){const u=r.var.user;if(!(u!=null&&u.sub))throw new I(401,{message:"Authentication required"});if(a===o)throw new I(403,{message:"Cannot delete the control plane"});if(!(await T.fetchAll(p=>r.env.data.userOrganizations.listUserOrganizations(o,u.sub,p),"organizations")).some(p=>p.name===a))throw new I(403,{message:"Access denied to this tenant"})}if(!await r.env.data.tenants.get(a))throw new I(404,{message:"Tenant not found"});const s={adapters:r.env.data,ctx:r};return(l=e.tenants)!=null&&l.beforeDelete&&await e.tenants.beforeDelete(s,a),await r.env.data.tenants.remove(a),(g=e.tenants)!=null&&g.afterDelete&&await e.tenants.afterDelete(s,a),r.body(null,204)}),n}function de(t){const e=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:n,type:r}of e){const a=t.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function ue(t,e,n){try{switch(n.type){case"resource_server":{const r=await t.resourceServers.get(e,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await t.roles.get(e,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await t.connections.get(e,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function fe(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function J(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const n=de(t.req.path);if(!n)return e();const r=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!r)return e();if(await ue(t.env.data,r,n))throw new I(403,{message:`This ${fe(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return e()}}const me=["client_secret","app_secret","twilio_token"];function ge(t){if(!t)return t;const e={...t};for(const n of me)delete e[n];return e}function q(t,e,n){const r=e.find(c=>c.strategy===t.strategy);if(!(r!=null&&r.options))return t;const a=T.connectionSchema.passthrough().parse({...r,...t}),o=n?ge(r.options):r.options;return a.options=T.connectionOptionsSchema.passthrough().parse({...o||{},...t.options}),a}function X(t,e){const{controlPlaneTenantId:n,controlPlaneClientId:r,excludeSensitiveFields:a=!1}=e;return{...t,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...t.connections,get:async(o,c)=>{const s=await t.connections.get(o,c);if(!s||!n||o===n)return s;const d=await t.connections.list(n);return q(s,d.connections||[],a)},list:async(o,c)=>{const s=await t.connections.list(o,c);if(!n||o===n)return s;const d=await t.connections.list(n),i=s.connections.map(l=>q(l,d.connections||[],a));return{...s,connections:i}}},clientConnections:{...t.clientConnections,listByClient:async(o,c)=>{let s=await t.clientConnections.listByClient(o,c);if(s.length===0&&(s=(await t.connections.list(o)).connections||[]),!n||o===n)return s;const d=await t.connections.list(n);return s.map(i=>q(i,d.connections||[],a))}},emailProviders:{...t.emailProviders,get:async o=>{const c=await t.emailProviders.get(o);return c||(!n||o===n?null:t.emailProviders.get(n))}}}}function F(t,e){return X(t,e)}function Y(t){return async(e,n)=>{const r=e.var.user;return(r==null?void 0:r.tenant_id)===t&&r.org_name&&e.set("tenant_id",r.org_name),n()}}function Z(t){return async(e,n)=>{if(!t.accessControl)return n();const{controlPlaneTenantId:r}=t.accessControl,a=e.var.org_name,o=e.var.organization_id,c=a||o;let s=e.var.tenant_id;const d=e.var.user,l=(d!=null&&d.aud?Array.isArray(d.aud)?d.aud:[d.aud]:[]).includes(T.MANAGEMENT_API_AUDIENCE);if(!s&&c&&l&&(e.set("tenant_id",c),s=c),!s)throw new I(400,{message:"Tenant ID not found in request"});if(!K(o,s,r,a))throw new I(403,{message:`Access denied to tenant ${s}`});return n()}}function x(t){return async(e,n)=>{if(!t.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:o}=t.subdomainRouting,c=e.req.header("host")||"";let s=null;if(c.endsWith(r)){const i=c.slice(0,-(r.length+1));i&&!i.includes(".")&&(s=i)}if(s&&a.includes(s)&&(s=null),!s)return t.accessControl&&e.set("tenant_id",t.accessControl.controlPlaneTenantId),n();let d=null;if(o)d=await o(s);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const i=await e.env.data.organizations.get(t.accessControl.controlPlaneTenantId,s);i&&(d=i.id)}catch{}if(!d)throw new I(404,{message:`Tenant not found for subdomain: ${s}`});return e.set("tenant_id",d),n()}}function ee(t){return async(e,n)=>{if(!t.databaseIsolation)return n();const r=e.var.tenant_id;if(!r)throw new I(400,{message:"Tenant ID not found in request"});try{const a=await t.databaseIsolation.getAdapters(r);e.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${r}:`,a),new I(500,{message:"Failed to resolve tenant database"})}return n()}}function E(t){const e=x(t),n=Z(t),r=ee(t);return async(a,o)=>(await e(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),o())}function pe(t){const{dataAdapter:e,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:o={resourceServers:!0,roles:!0},defaultPermissions:c=["tenant:admin"],requireOrganizationMatch:s=!1,managementApiExtensions:d=[],entityHooks:i,getChildTenantIds:l,getAdapters:g,...u}=t;let f=e,m=e;n&&(f=F(e,{controlPlaneTenantId:r,controlPlaneClientId:a}),m=F(e,{controlPlaneTenantId:r,controlPlaneClientId:a,excludeSensitiveFields:!0}));const p=o!==!1,h=p?{resourceServers:o.resourceServers??!0,roles:o.roles??!0}:{resourceServers:!1,roles:!1},P={controlPlaneTenantId:r,getChildTenantIds:l??(async()=>(await T.fetchAll(M=>f.tenants.list(M),"tenants",{cursorField:"id",pageSize:100})).filter(M=>M.id!==r).map(M=>M.id)),getAdapters:g??(async()=>f),getControlPlaneAdapters:async()=>f,sync:h},{entityHooks:A,tenantHooks:C}=Q(P),v={resourceServers:[A.resourceServers,...(i==null?void 0:i.resourceServers)??[]],roles:[A.roles,...(i==null?void 0:i.roles)??[]],connections:(i==null?void 0:i.connections)??[],tenants:(i==null?void 0:i.tenants)??[],rolePermissions:(i==null?void 0:i.rolePermissions)??[]},_=$({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:s,defaultPermissions:c}},{tenants:C}),{app:b}=T.init({dataAdapter:f,managementDataAdapter:m,...u,entityHooks:v,managementApiExtensions:[...d,{path:"/tenants",router:_}]});return b.use("/api/v2/*",Y(r)),p&&b.use("/api/v2/*",J()),{app:b,controlPlaneTenantId:r}}function we(t){const e=O(t);return{name:"multi-tenancy",middleware:E(t),hooks:e,routes:[{path:"/management",handler:$(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (control plane: ${t.accessControl.controlPlaneTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function O(t){const e=t.accessControl?B(t.accessControl):{},n=t.databaseIsolation?W(t.databaseIsolation):{},r=V(t);return{...e,...n,tenants:r}}function te(t){const e=new se.Hono,n=O(t);return e.route("/tenants",$(t,n)),e}function ye(t){return{hooks:O(t),middleware:E(t),app:te(t),config:t,wrapAdapters:(e,n)=>{var r;return F(e,{controlPlaneTenantId:(r=t.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=B;exports.createAccessControlMiddleware=Z;exports.createControlPlaneTenantMiddleware=Y;exports.createDatabaseHooks=W;exports.createDatabaseMiddleware=ee;exports.createMultiTenancy=te;exports.createMultiTenancyHooks=O;exports.createMultiTenancyMiddleware=E;exports.createMultiTenancyPlugin=we;exports.createProtectSyncedMiddleware=J;exports.createProvisioningHooks=V;exports.createRuntimeFallbackAdapter=X;exports.createSubdomainMiddleware=x;exports.createSyncHooks=Q;exports.createTenantsOpenAPIRouter=$;exports.initMultiTenant=pe;exports.setupMultiTenancy=ye;exports.validateTenantAccess=K;exports.withRuntimeFallback=F;
@@ -1,8 +1,8 @@
1
- var X = Object.defineProperty;
2
- var Y = (t, e, n) => e in t ? X(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
- var O = (t, e, n) => Y(t, typeof e != "symbol" ? e + "" : e, n);
4
- import { Hono as Z } from "hono";
5
- import { MANAGEMENT_API_SCOPES as x, MANAGEMENT_API_AUDIENCE as W, fetchAll as F, auth0QuerySchema as ee, tenantSchema as N, tenantInsertSchema as te, connectionSchema as k, connectionOptionsSchema as H, init as ne } from "authhero";
1
+ var Q = Object.defineProperty;
2
+ var J = (t, e, n) => e in t ? Q(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n;
3
+ var O = (t, e, n) => J(t, typeof e != "symbol" ? e + "" : e, n);
4
+ import { Hono as X } from "hono";
5
+ import { MANAGEMENT_API_SCOPES as Y, MANAGEMENT_API_AUDIENCE as K, fetchAll as F, auth0QuerySchema as Z, tenantSchema as k, tenantInsertSchema as x, connectionSchema as ee, connectionOptionsSchema as te, init as ne } from "authhero";
6
6
  import { OpenAPIHono as re, createRoute as R, z as S } from "@hono/zod-openapi";
7
7
  function ae(t) {
8
8
  const { controlPlaneTenantId: e, requireOrganizationMatch: n = !0 } = t;
@@ -11,7 +11,7 @@ function ae(t) {
11
11
  if (a === e)
12
12
  return !0;
13
13
  if (n) {
14
- const c = r.var.org_name, d = r.var.organization_id, s = c || d;
14
+ const o = r.var.org_name, c = r.var.organization_id, s = o || c;
15
15
  return s ? s.toLowerCase() === a.toLowerCase() : !1;
16
16
  }
17
17
  return !0;
@@ -58,26 +58,26 @@ function ce(t) {
58
58
  const { accessControl: r, databaseIsolation: a } = t;
59
59
  if (r)
60
60
  try {
61
- const d = (await e.adapters.organizations.list(
61
+ const c = (await e.adapters.organizations.list(
62
62
  r.controlPlaneTenantId
63
63
  )).organizations.find((s) => s.name === n);
64
- d && await e.adapters.organizations.remove(
64
+ c && await e.adapters.organizations.remove(
65
65
  r.controlPlaneTenantId,
66
- d.id
66
+ c.id
67
67
  );
68
- } catch (c) {
68
+ } catch (o) {
69
69
  console.warn(
70
70
  `Failed to remove organization for tenant ${n}:`,
71
- c
71
+ o
72
72
  );
73
73
  }
74
74
  if (a != null && a.onDeprovision)
75
75
  try {
76
76
  await a.onDeprovision(n);
77
- } catch (c) {
77
+ } catch (o) {
78
78
  console.warn(
79
79
  `Failed to deprovision database for tenant ${n}:`,
80
- c
80
+ o
81
81
  );
82
82
  }
83
83
  }
@@ -87,53 +87,53 @@ async function le(t, e, n) {
87
87
  const {
88
88
  controlPlaneTenantId: r,
89
89
  defaultPermissions: a,
90
- defaultRoles: c,
91
- issuer: d,
90
+ defaultRoles: o,
91
+ issuer: c,
92
92
  adminRoleName: s = "Tenant Admin",
93
- adminRoleDescription: f = "Full access to all tenant management operations",
94
- addCreatorToOrganization: o = !0
95
- } = n, i = await t.adapters.organizations.create(
93
+ adminRoleDescription: d = "Full access to all tenant management operations",
94
+ addCreatorToOrganization: i = !0
95
+ } = n, l = await t.adapters.organizations.create(
96
96
  r,
97
97
  {
98
98
  name: e.id,
99
99
  display_name: e.friendly_name || e.id
100
100
  }
101
101
  );
102
- let u;
103
- if (d && (u = await ue(
102
+ let g;
103
+ if (c && (g = await ue(
104
104
  t,
105
105
  r,
106
106
  s,
107
- f
108
- )), o && t.ctx) {
109
- const l = t.ctx.var.user;
110
- if (l != null && l.sub && !await de(
107
+ d
108
+ )), i && t.ctx) {
109
+ const u = t.ctx.var.user;
110
+ if (u != null && u.sub && !await de(
111
111
  t,
112
112
  r,
113
- l.sub
113
+ u.sub
114
114
  ))
115
115
  try {
116
116
  await t.adapters.userOrganizations.create(r, {
117
- user_id: l.sub,
118
- organization_id: i.id
119
- }), u && await t.adapters.userRoles.create(
117
+ user_id: u.sub,
118
+ organization_id: l.id
119
+ }), g && await t.adapters.userRoles.create(
120
120
  r,
121
- l.sub,
122
- u,
123
- i.id
121
+ u.sub,
122
+ g,
123
+ l.id
124
124
  // organizationId
125
125
  );
126
- } catch (p) {
126
+ } catch (m) {
127
127
  console.warn(
128
- `Failed to add creator ${l.sub} to organization ${i.id}:`,
129
- p
128
+ `Failed to add creator ${u.sub} to organization ${l.id}:`,
129
+ m
130
130
  );
131
131
  }
132
132
  }
133
- c && c.length > 0 && console.log(
134
- `Would assign roles ${c.join(", ")} to organization ${i.id}`
133
+ o && o.length > 0 && console.log(
134
+ `Would assign roles ${o.join(", ")} to organization ${l.id}`
135
135
  ), a && a.length > 0 && console.log(
136
- `Would grant permissions ${a.join(", ")} to organization ${i.id}`
136
+ `Would grant permissions ${a.join(", ")} to organization ${l.id}`
137
137
  );
138
138
  }
139
139
  async function de(t, e, n) {
@@ -156,126 +156,126 @@ async function de(t, e, n) {
156
156
  return !1;
157
157
  }
158
158
  async function ue(t, e, n, r) {
159
- const c = (await t.adapters.roles.list(e, {})).roles.find((o) => o.name === n);
160
- if (c)
161
- return c.id;
162
- const d = await t.adapters.roles.create(e, {
159
+ const o = (await t.adapters.roles.list(e, {})).roles.find((i) => i.name === n);
160
+ if (o)
161
+ return o.id;
162
+ const c = await t.adapters.roles.create(e, {
163
163
  name: n,
164
164
  description: r
165
- }), s = W, f = x.map((o) => ({
166
- role_id: d.id,
165
+ }), s = K, d = Y.map((i) => ({
166
+ role_id: c.id,
167
167
  resource_server_identifier: s,
168
- permission_name: o.value
168
+ permission_name: i.value
169
169
  }));
170
170
  return await t.adapters.rolePermissions.assign(
171
171
  e,
172
- d.id,
173
- f
174
- ), d.id;
172
+ c.id,
173
+ d
174
+ ), c.id;
175
175
  }
176
- function G(t, e, n = () => !0) {
177
- const { controlPlaneTenantId: r, getChildTenantIds: a, getAdapters: c } = t, d = /* @__PURE__ */ new Map();
178
- async function s(i, u, l) {
179
- return (await e(i).list(u, {
180
- q: `name:${l}`,
176
+ function H(t, e, n = () => !0) {
177
+ const { controlPlaneTenantId: r, getChildTenantIds: a, getAdapters: o } = t, c = /* @__PURE__ */ new Map();
178
+ async function s(l, g, u) {
179
+ return (await e(l).list(g, {
180
+ q: `name:${u}`,
181
181
  per_page: 1
182
182
  }))[0] ?? null;
183
183
  }
184
- async function f(i) {
185
- const u = await a(), l = e(await c(r));
184
+ async function d(l) {
185
+ const g = await a(), u = e(await o(r));
186
186
  await Promise.all(
187
- u.map(async (m) => {
187
+ g.map(async (f) => {
188
188
  try {
189
- const p = await c(m), g = e(p), w = {
190
- ...l.transform(i),
189
+ const m = await o(f), p = e(m), w = {
190
+ ...u.transform(l),
191
191
  is_system: !0
192
- }, y = await s(p, m, i.name), T = y ? g.getId(y) : void 0;
192
+ }, y = await s(m, f, l.name), T = y ? p.getId(y) : void 0;
193
193
  if (y && T) {
194
- const A = g.preserveOnUpdate ? g.preserveOnUpdate(y, w) : w;
195
- await g.update(m, T, A);
194
+ const C = p.preserveOnUpdate ? p.preserveOnUpdate(y, w) : w;
195
+ await p.update(f, T, C);
196
196
  } else
197
- await g.create(m, w);
198
- } catch (p) {
197
+ await p.create(f, w);
198
+ } catch (m) {
199
199
  console.error(
200
- `Failed to sync ${l.listKey} "${i.name}" to tenant "${m}":`,
201
- p
200
+ `Failed to sync ${u.listKey} "${l.name}" to tenant "${f}":`,
201
+ m
202
202
  );
203
203
  }
204
204
  })
205
205
  );
206
206
  }
207
- async function o(i) {
208
- const u = await a();
207
+ async function i(l) {
208
+ const g = await a();
209
209
  await Promise.all(
210
- u.map(async (l) => {
210
+ g.map(async (u) => {
211
211
  try {
212
- const m = await c(l), p = e(m), g = await s(m, l, i), h = g ? p.getId(g) : void 0;
213
- g && h && await p.remove(l, h);
214
- } catch (m) {
212
+ const f = await o(u), m = e(f), p = await s(f, u, l), h = p ? m.getId(p) : void 0;
213
+ p && h && await m.remove(u, h);
214
+ } catch (f) {
215
215
  console.error(
216
- `Failed to delete entity "${i}" from tenant "${l}":`,
217
- m
216
+ `Failed to delete entity "${l}" from tenant "${u}":`,
217
+ f
218
218
  );
219
219
  }
220
220
  })
221
221
  );
222
222
  }
223
223
  return {
224
- afterCreate: async (i, u) => {
225
- i.tenantId === r && n(u) && await f(u);
224
+ afterCreate: async (l, g) => {
225
+ l.tenantId === r && n(g) && await d(g);
226
226
  },
227
- afterUpdate: async (i, u, l) => {
228
- i.tenantId === r && n(l) && await f(l);
227
+ afterUpdate: async (l, g, u) => {
228
+ l.tenantId === r && n(u) && await d(u);
229
229
  },
230
- beforeDelete: async (i, u) => {
231
- if (i.tenantId !== r) return;
232
- const m = await e(i.adapters).get(i.tenantId, u);
233
- m && n(m) && d.set(u, m);
230
+ beforeDelete: async (l, g) => {
231
+ if (l.tenantId !== r) return;
232
+ const f = await e(l.adapters).get(l.tenantId, g);
233
+ f && n(f) && c.set(g, f);
234
234
  },
235
- afterDelete: async (i, u) => {
236
- if (i.tenantId !== r) return;
237
- const l = d.get(u);
238
- l && (d.delete(u), await o(l.name));
235
+ afterDelete: async (l, g) => {
236
+ if (l.tenantId !== r) return;
237
+ const u = c.get(g);
238
+ u && (c.delete(g), await i(u.name));
239
239
  }
240
240
  };
241
241
  }
242
- function L(t, e, n = () => !0) {
243
- const { controlPlaneTenantId: r, getControlPlaneAdapters: a, getAdapters: c } = t;
242
+ function G(t, e, n = () => !0) {
243
+ const { controlPlaneTenantId: r, getControlPlaneAdapters: a, getAdapters: o } = t;
244
244
  return {
245
- async afterCreate(d, s) {
245
+ async afterCreate(c, s) {
246
246
  if (s.id !== r)
247
247
  try {
248
- const f = await a(), o = await c(s.id), i = e(f), u = e(o), l = await F(
249
- (m) => i.listPaginated(r, m),
250
- i.listKey,
248
+ const d = await a(), i = await o(s.id), l = e(d), g = e(i), u = await F(
249
+ (f) => l.listPaginated(r, f),
250
+ l.listKey,
251
251
  { cursorField: "id", pageSize: 100 }
252
252
  );
253
253
  await Promise.all(
254
- l.filter((m) => n(m)).map(async (m) => {
254
+ u.filter((f) => n(f)).map(async (f) => {
255
255
  try {
256
- const p = i.transform(m);
257
- await u.create(s.id, {
258
- ...p,
256
+ const m = l.transform(f);
257
+ await g.create(s.id, {
258
+ ...m,
259
259
  is_system: !0
260
260
  });
261
- } catch (p) {
261
+ } catch (m) {
262
262
  console.error(
263
263
  `Failed to sync entity to new tenant "${s.id}":`,
264
- p
264
+ m
265
265
  );
266
266
  }
267
267
  })
268
268
  );
269
- } catch (f) {
269
+ } catch (d) {
270
270
  console.error(
271
271
  `Failed to sync entities to new tenant "${s.id}":`,
272
- f
272
+ d
273
273
  );
274
274
  }
275
275
  }
276
276
  };
277
277
  }
278
- const U = (t) => ({
278
+ const L = (t) => ({
279
279
  list: async (e, n) => (await t.resourceServers.list(e, n)).resource_servers,
280
280
  listPaginated: (e, n) => t.resourceServers.list(e, n),
281
281
  get: (e, n) => t.resourceServers.get(e, n),
@@ -308,95 +308,95 @@ const U = (t) => ({
308
308
  description: e.description
309
309
  })
310
310
  });
311
- function K(t) {
311
+ function U(t) {
312
312
  var e;
313
313
  return ((e = t.metadata) == null ? void 0 : e.sync) !== !1;
314
314
  }
315
315
  function fe(t) {
316
- const { sync: e = {}, filters: n = {} } = t, r = e.resourceServers ?? !0, a = e.roles ?? !0, c = (p) => K(p) ? n.resourceServers ? n.resourceServers(p) : !0 : !1, d = (p) => K(p) ? n.roles ? n.roles(p) : !0 : !1, s = r ? G(
316
+ const { sync: e = {}, filters: n = {} } = t, r = e.resourceServers ?? !0, a = e.roles ?? !0, o = (m) => U(m) ? n.resourceServers ? n.resourceServers(m) : !0 : !1, c = (m) => U(m) ? n.roles ? n.roles(m) : !0 : !1, s = r ? H(
317
317
  t,
318
- U,
319
- c
320
- ) : void 0, f = a ? G(
318
+ L,
319
+ o
320
+ ) : void 0, d = a ? H(
321
321
  t,
322
322
  B,
323
- d
324
- ) : void 0, o = r ? L(
325
- t,
326
- U,
327
323
  c
328
- ) : void 0, i = a ? L(
324
+ ) : void 0, i = r ? G(
325
+ t,
326
+ L,
327
+ o
328
+ ) : void 0, l = a ? G(
329
329
  t,
330
330
  B,
331
- d
332
- ) : void 0, u = a ? {
333
- async afterCreate(p, g) {
331
+ c
332
+ ) : void 0, g = a ? {
333
+ async afterCreate(m, p) {
334
334
  var h;
335
- if (g.id !== t.controlPlaneTenantId) {
336
- await ((h = i == null ? void 0 : i.afterCreate) == null ? void 0 : h.call(i, p, g));
335
+ if (p.id !== t.controlPlaneTenantId) {
336
+ await ((h = l == null ? void 0 : l.afterCreate) == null ? void 0 : h.call(l, m, p));
337
337
  try {
338
- const w = await t.getControlPlaneAdapters(), y = await t.getAdapters(g.id), T = await F(
339
- (_) => w.roles.list(
338
+ const w = await t.getControlPlaneAdapters(), y = await t.getAdapters(p.id), T = await F(
339
+ (P) => w.roles.list(
340
340
  t.controlPlaneTenantId,
341
- _
341
+ P
342
342
  ),
343
343
  "roles",
344
344
  { cursorField: "id", pageSize: 100 }
345
- ), A = /* @__PURE__ */ new Map();
346
- for (const _ of T.filter(
345
+ ), C = /* @__PURE__ */ new Map();
346
+ for (const P of T.filter(
347
347
  (v) => {
348
- var P;
349
- return ((P = n.roles) == null ? void 0 : P.call(n, v)) ?? !0;
348
+ var _;
349
+ return ((_ = n.roles) == null ? void 0 : _.call(n, v)) ?? !0;
350
350
  }
351
351
  )) {
352
- const v = await l(
352
+ const v = await u(
353
353
  y,
354
- g.id,
355
- _.name
354
+ p.id,
355
+ P.name
356
356
  );
357
- v && A.set(_.name, v.id);
357
+ v && C.set(P.name, v.id);
358
358
  }
359
- for (const _ of T.filter(
359
+ for (const P of T.filter(
360
360
  (v) => {
361
- var P;
362
- return ((P = n.roles) == null ? void 0 : P.call(n, v)) ?? !0;
361
+ var _;
362
+ return ((_ = n.roles) == null ? void 0 : _.call(n, v)) ?? !0;
363
363
  }
364
364
  )) {
365
- const v = A.get(_.name);
365
+ const v = C.get(P.name);
366
366
  if (v)
367
367
  try {
368
- const P = await w.rolePermissions.list(
368
+ const _ = await w.rolePermissions.list(
369
369
  t.controlPlaneTenantId,
370
- _.id,
370
+ P.id,
371
371
  {}
372
372
  );
373
- P.length > 0 && await y.rolePermissions.assign(
374
- g.id,
373
+ _.length > 0 && await y.rolePermissions.assign(
374
+ p.id,
375
375
  v,
376
- P.map((C) => ({
376
+ _.map((A) => ({
377
377
  role_id: v,
378
- resource_server_identifier: C.resource_server_identifier,
379
- permission_name: C.permission_name
378
+ resource_server_identifier: A.resource_server_identifier,
379
+ permission_name: A.permission_name
380
380
  }))
381
381
  );
382
- } catch (P) {
382
+ } catch (_) {
383
383
  console.error(
384
- `Failed to sync permissions for role "${_.name}" to tenant "${g.id}":`,
385
- P
384
+ `Failed to sync permissions for role "${P.name}" to tenant "${p.id}":`,
385
+ _
386
386
  );
387
387
  }
388
388
  }
389
389
  } catch (w) {
390
390
  console.error(
391
- `Failed to sync role permissions to tenant "${g.id}":`,
391
+ `Failed to sync role permissions to tenant "${p.id}":`,
392
392
  w
393
393
  );
394
394
  }
395
395
  }
396
396
  }
397
397
  } : void 0;
398
- async function l(p, g, h) {
399
- return (await p.roles.list(g, {
398
+ async function u(m, p, h) {
399
+ return (await m.roles.list(p, {
400
400
  q: `name:${h}`,
401
401
  per_page: 1
402
402
  })).roles[0] ?? null;
@@ -404,18 +404,18 @@ function fe(t) {
404
404
  return {
405
405
  entityHooks: {
406
406
  resourceServers: s,
407
- roles: f
407
+ roles: d
408
408
  },
409
409
  tenantHooks: {
410
- async afterCreate(p, g) {
410
+ async afterCreate(m, p) {
411
411
  const h = [
412
- o == null ? void 0 : o.afterCreate,
413
- (u == null ? void 0 : u.afterCreate) ?? (i == null ? void 0 : i.afterCreate)
412
+ i == null ? void 0 : i.afterCreate,
413
+ (g == null ? void 0 : g.afterCreate) ?? (l == null ? void 0 : l.afterCreate)
414
414
  ], w = [];
415
415
  for (const y of h)
416
416
  if (y)
417
417
  try {
418
- await y(p, g);
418
+ await y(m, p);
419
419
  } catch (T) {
420
420
  w.push(T instanceof Error ? T : new Error(String(T)));
421
421
  }
@@ -455,7 +455,7 @@ var b = class extends Error {
455
455
  });
456
456
  }
457
457
  };
458
- function M(t, e) {
458
+ function q(t, e) {
459
459
  const n = new re();
460
460
  return n.openapi(
461
461
  R({
@@ -463,7 +463,7 @@ function M(t, e) {
463
463
  method: "get",
464
464
  path: "/",
465
465
  request: {
466
- query: ee
466
+ query: Z
467
467
  },
468
468
  security: [
469
469
  {
@@ -475,7 +475,7 @@ function M(t, e) {
475
475
  content: {
476
476
  "application/json": {
477
477
  schema: S.object({
478
- tenants: S.array(N),
478
+ tenants: S.array(k),
479
479
  start: S.number().optional(),
480
480
  limit: S.number().optional(),
481
481
  length: S.number().optional()
@@ -487,72 +487,72 @@ function M(t, e) {
487
487
  }
488
488
  }),
489
489
  async (r) => {
490
- var p, g, h, w, y, T;
491
- const a = r.req.valid("query"), { page: c, per_page: d, include_totals: s, q: f } = a, o = r.var.user, i = (o == null ? void 0 : o.permissions) || [];
492
- if (i.includes("auth:read") || i.includes("admin:organizations")) {
493
- const A = await r.env.data.tenants.list({
494
- page: c,
495
- per_page: d,
490
+ var m, p, h, w, y, T;
491
+ const a = r.req.valid("query"), { page: o, per_page: c, include_totals: s, q: d } = a, i = r.var.user, l = (i == null ? void 0 : i.permissions) || [];
492
+ if (l.includes("auth:read") || l.includes("admin:organizations")) {
493
+ const C = await r.env.data.tenants.list({
494
+ page: o,
495
+ per_page: c,
496
496
  include_totals: s,
497
- q: f
497
+ q: d
498
498
  });
499
499
  return s ? r.json({
500
- tenants: A.tenants,
501
- start: ((p = A.totals) == null ? void 0 : p.start) ?? 0,
502
- limit: ((g = A.totals) == null ? void 0 : g.limit) ?? d,
503
- length: A.tenants.length
504
- }) : r.json({ tenants: A.tenants });
500
+ tenants: C.tenants,
501
+ start: ((m = C.totals) == null ? void 0 : m.start) ?? 0,
502
+ limit: ((p = C.totals) == null ? void 0 : p.limit) ?? c,
503
+ length: C.tenants.length
504
+ }) : r.json({ tenants: C.tenants });
505
505
  }
506
- const l = ((h = t.accessControl) == null ? void 0 : h.controlPlaneTenantId) ?? ((w = r.env.data.multiTenancyConfig) == null ? void 0 : w.controlPlaneTenantId);
507
- if (l && (o != null && o.sub)) {
508
- const _ = (await F(
506
+ const u = ((h = t.accessControl) == null ? void 0 : h.controlPlaneTenantId) ?? ((w = r.env.data.multiTenancyConfig) == null ? void 0 : w.controlPlaneTenantId);
507
+ if (u && (i != null && i.sub)) {
508
+ const P = (await F(
509
509
  (z) => r.env.data.userOrganizations.listUserOrganizations(
510
- l,
511
- o.sub,
510
+ u,
511
+ i.sub,
512
512
  z
513
513
  ),
514
514
  "organizations"
515
515
  )).map((z) => z.name);
516
- if (_.length === 0)
516
+ if (P.length === 0)
517
517
  return s ? r.json({
518
518
  tenants: [],
519
519
  start: 0,
520
- limit: d ?? 50,
520
+ limit: c ?? 50,
521
521
  length: 0
522
522
  }) : r.json({ tenants: [] });
523
- const v = _.length, P = c ?? 0, C = d ?? 50, $ = P * C, I = _.slice($, $ + C);
523
+ const v = P.length, _ = o ?? 0, A = c ?? 50, $ = _ * A, I = P.slice($, $ + A);
524
524
  if (I.length === 0)
525
525
  return s ? r.json({
526
526
  tenants: [],
527
527
  start: $,
528
- limit: C,
528
+ limit: A,
529
529
  length: v
530
530
  }) : r.json({ tenants: [] });
531
- const j = I.map((z) => `id:${z}`).join(" OR "), J = f ? `(${j}) AND (${f})` : j, E = await r.env.data.tenants.list({
532
- q: J,
533
- per_page: C,
531
+ const E = I.map((z) => `id:${z}`).join(" OR "), V = d ? `(${E}) AND (${d})` : E, N = await r.env.data.tenants.list({
532
+ q: V,
533
+ per_page: A,
534
534
  include_totals: !1
535
535
  // We calculate totals from accessibleTenantIds
536
536
  });
537
537
  return s ? r.json({
538
- tenants: E.tenants,
538
+ tenants: N.tenants,
539
539
  start: $,
540
- limit: C,
540
+ limit: A,
541
541
  length: v
542
- }) : r.json({ tenants: E.tenants });
542
+ }) : r.json({ tenants: N.tenants });
543
543
  }
544
- const m = await r.env.data.tenants.list({
545
- page: c,
546
- per_page: d,
544
+ const f = await r.env.data.tenants.list({
545
+ page: o,
546
+ per_page: c,
547
547
  include_totals: s,
548
- q: f
548
+ q: d
549
549
  });
550
550
  return s ? r.json({
551
- tenants: m.tenants,
552
- start: ((y = m.totals) == null ? void 0 : y.start) ?? 0,
553
- limit: ((T = m.totals) == null ? void 0 : T.limit) ?? d,
554
- length: m.tenants.length
555
- }) : r.json({ tenants: m.tenants });
551
+ tenants: f.tenants,
552
+ start: ((y = f.totals) == null ? void 0 : y.start) ?? 0,
553
+ limit: ((T = f.totals) == null ? void 0 : T.limit) ?? c,
554
+ length: f.tenants.length
555
+ }) : r.json({ tenants: f.tenants });
556
556
  }
557
557
  ), n.openapi(
558
558
  R({
@@ -563,7 +563,7 @@ function M(t, e) {
563
563
  body: {
564
564
  content: {
565
565
  "application/json": {
566
- schema: te
566
+ schema: x
567
567
  }
568
568
  }
569
569
  }
@@ -577,7 +577,7 @@ function M(t, e) {
577
577
  201: {
578
578
  content: {
579
579
  "application/json": {
580
- schema: N
580
+ schema: k
581
581
  }
582
582
  },
583
583
  description: "Tenant created"
@@ -591,20 +591,20 @@ function M(t, e) {
591
591
  }
592
592
  }),
593
593
  async (r) => {
594
- var f, o;
594
+ var d, i;
595
595
  const a = r.var.user;
596
596
  if (!(a != null && a.sub))
597
597
  throw new b(401, {
598
598
  message: "Authentication required to create tenants"
599
599
  });
600
- let c = r.req.valid("json");
601
- const d = {
600
+ let o = r.req.valid("json");
601
+ const c = {
602
602
  adapters: r.env.data,
603
603
  ctx: r
604
604
  };
605
- (f = e.tenants) != null && f.beforeCreate && (c = await e.tenants.beforeCreate(d, c));
606
- const s = await r.env.data.tenants.create(c);
607
- return (o = e.tenants) != null && o.afterCreate && await e.tenants.afterCreate(d, s), r.json(s, 201);
605
+ (d = e.tenants) != null && d.beforeCreate && (o = await e.tenants.beforeCreate(c, o));
606
+ const s = await r.env.data.tenants.create(o);
607
+ return (i = e.tenants) != null && i.afterCreate && await e.tenants.afterCreate(c, s), r.json(s, 201);
608
608
  }
609
609
  ), n.openapi(
610
610
  R({
@@ -634,26 +634,26 @@ function M(t, e) {
634
634
  }
635
635
  }),
636
636
  async (r) => {
637
- var f, o, i, u;
638
- const { id: a } = r.req.valid("param"), c = ((f = t.accessControl) == null ? void 0 : f.controlPlaneTenantId) ?? ((o = r.env.data.multiTenancyConfig) == null ? void 0 : o.controlPlaneTenantId);
639
- if (c) {
640
- const l = r.var.user;
641
- if (!(l != null && l.sub))
637
+ var d, i, l, g;
638
+ const { id: a } = r.req.valid("param"), o = ((d = t.accessControl) == null ? void 0 : d.controlPlaneTenantId) ?? ((i = r.env.data.multiTenancyConfig) == null ? void 0 : i.controlPlaneTenantId);
639
+ if (o) {
640
+ const u = r.var.user;
641
+ if (!(u != null && u.sub))
642
642
  throw new b(401, {
643
643
  message: "Authentication required"
644
644
  });
645
- if (a === c)
645
+ if (a === o)
646
646
  throw new b(403, {
647
647
  message: "Cannot delete the control plane"
648
648
  });
649
649
  if (!(await F(
650
- (g) => r.env.data.userOrganizations.listUserOrganizations(
651
- c,
652
- l.sub,
653
- g
650
+ (p) => r.env.data.userOrganizations.listUserOrganizations(
651
+ o,
652
+ u.sub,
653
+ p
654
654
  ),
655
655
  "organizations"
656
- )).some((g) => g.name === a))
656
+ )).some((p) => p.name === a))
657
657
  throw new b(403, {
658
658
  message: "Access denied to this tenant"
659
659
  });
@@ -666,7 +666,7 @@ function M(t, e) {
666
666
  adapters: r.env.data,
667
667
  ctx: r
668
668
  };
669
- return (i = e.tenants) != null && i.beforeDelete && await e.tenants.beforeDelete(s, a), await r.env.data.tenants.remove(a), (u = e.tenants) != null && u.afterDelete && await e.tenants.afterDelete(s, a), r.body(null, 204);
669
+ return (l = e.tenants) != null && l.beforeDelete && await e.tenants.beforeDelete(s, a), await r.env.data.tenants.remove(a), (g = e.tenants) != null && g.afterDelete && await e.tenants.afterDelete(s, a), r.body(null, 204);
670
670
  }
671
671
  ), n;
672
672
  }
@@ -686,7 +686,7 @@ function me(t) {
686
686
  }
687
687
  return null;
688
688
  }
689
- async function pe(t, e, n) {
689
+ async function ge(t, e, n) {
690
690
  try {
691
691
  switch (n.type) {
692
692
  case "resource_server": {
@@ -708,7 +708,7 @@ async function pe(t, e, n) {
708
708
  return !1;
709
709
  }
710
710
  }
711
- function ge(t) {
711
+ function pe(t) {
712
712
  return {
713
713
  resource_server: "resource server",
714
714
  role: "role",
@@ -725,9 +725,9 @@ function we() {
725
725
  const r = t.var.tenant_id || t.req.header("x-tenant-id") || t.req.header("tenant-id");
726
726
  if (!r)
727
727
  return e();
728
- if (await pe(t.env.data, r, n))
728
+ if (await ge(t.env.data, r, n))
729
729
  throw new b(403, {
730
- message: `This ${ge(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`
730
+ message: `This ${pe(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`
731
731
  });
732
732
  return e();
733
733
  };
@@ -737,14 +737,29 @@ const ye = [
737
737
  "app_secret",
738
738
  "twilio_token"
739
739
  ];
740
- function V(t) {
740
+ function he(t) {
741
741
  if (!t) return t;
742
742
  const e = { ...t };
743
743
  for (const n of ye)
744
744
  delete e[n];
745
745
  return e;
746
746
  }
747
- function he(t, e) {
747
+ function D(t, e, n) {
748
+ const r = e.find(
749
+ (c) => c.strategy === t.strategy
750
+ );
751
+ if (!(r != null && r.options))
752
+ return t;
753
+ const a = ee.passthrough().parse({
754
+ ...r,
755
+ ...t
756
+ }), o = n ? he(r.options) : r.options;
757
+ return a.options = te.passthrough().parse({
758
+ ...o || {},
759
+ ...t.options
760
+ }), a;
761
+ }
762
+ function ve(t, e) {
748
763
  const {
749
764
  controlPlaneTenantId: n,
750
765
  controlPlaneClientId: r,
@@ -759,85 +774,90 @@ function he(t, e) {
759
774
  },
760
775
  connections: {
761
776
  ...t.connections,
762
- get: async (c, d) => {
763
- var l;
777
+ get: async (o, c) => {
764
778
  const s = await t.connections.get(
765
- c,
766
- d
779
+ o,
780
+ c
767
781
  );
768
- if (!s || !n || c === n)
782
+ if (!s || !n || o === n)
769
783
  return s;
770
- const o = (l = (await t.connections.list(n)).connections) == null ? void 0 : l.find(
771
- (m) => m.strategy === s.strategy
784
+ const d = await t.connections.list(n);
785
+ return D(
786
+ s,
787
+ d.connections || [],
788
+ a
772
789
  );
773
- if (!(o != null && o.options))
774
- return s;
775
- const i = k.parse({
776
- ...o,
777
- ...s
778
- }), u = a ? V(o.options) : o.options;
779
- return i.options = H.parse({
780
- ...u || {},
781
- ...s.options
782
- }), i;
783
790
  },
784
- list: async (c, d) => {
785
- const s = await t.connections.list(c, d);
786
- if (!n || c === n)
791
+ list: async (o, c) => {
792
+ const s = await t.connections.list(o, c);
793
+ if (!n || o === n)
787
794
  return s;
788
- const f = await t.connections.list(n), o = s.connections.map((i) => {
789
- var p;
790
- const u = (p = f.connections) == null ? void 0 : p.find(
791
- (g) => g.strategy === i.strategy
792
- );
793
- if (!(u != null && u.options))
794
- return i;
795
- const l = k.parse({
796
- ...u,
797
- ...i
798
- }), m = a ? V(u.options) : u.options;
799
- return l.options = H.parse({
800
- ...m || {},
801
- ...i.options
802
- }), l;
803
- });
795
+ const d = await t.connections.list(n), i = s.connections.map(
796
+ (l) => D(
797
+ l,
798
+ d.connections || [],
799
+ a
800
+ )
801
+ );
804
802
  return {
805
803
  ...s,
806
- connections: o
804
+ connections: i
807
805
  };
808
806
  }
807
+ },
808
+ clientConnections: {
809
+ ...t.clientConnections,
810
+ listByClient: async (o, c) => {
811
+ let s = await t.clientConnections.listByClient(
812
+ o,
813
+ c
814
+ );
815
+ if (s.length === 0 && (s = (await t.connections.list(o)).connections || []), !n || o === n)
816
+ return s;
817
+ const d = await t.connections.list(n);
818
+ return s.map(
819
+ (i) => D(
820
+ i,
821
+ d.connections || [],
822
+ a
823
+ )
824
+ );
825
+ }
826
+ },
827
+ emailProviders: {
828
+ ...t.emailProviders,
829
+ get: async (o) => {
830
+ const c = await t.emailProviders.get(o);
831
+ return c || (!n || o === n ? null : t.emailProviders.get(n));
832
+ }
809
833
  }
810
834
  // Note: Additional adapters can be extended here for runtime fallback:
811
835
  // - promptSettings: Fall back to control plane prompts
812
836
  // - branding: Fall back to control plane branding/themes
813
- // - emailProviders: Fall back to control plane SMTP settings
814
- //
815
- // For now, we pass through other adapters unchanged.
816
- // They remain part of ...baseAdapters and can be properly wrapped by caching.
817
837
  };
818
838
  }
819
- function D(t, e) {
820
- return he(t, e);
839
+ function M(t, e) {
840
+ return ve(t, e);
821
841
  }
822
- function ve(t) {
842
+ function Te(t) {
823
843
  return async (e, n) => {
824
844
  const r = e.var.user;
825
845
  return (r == null ? void 0 : r.tenant_id) === t && r.org_name && e.set("tenant_id", r.org_name), n();
826
846
  };
827
847
  }
828
- function Te(t) {
848
+ function Ce(t) {
829
849
  return async (e, n) => {
830
850
  if (!t.accessControl)
831
851
  return n();
832
- const { controlPlaneTenantId: r } = t.accessControl, a = e.var.org_name, c = e.var.organization_id, d = a || c;
852
+ const { controlPlaneTenantId: r } = t.accessControl, a = e.var.org_name, o = e.var.organization_id, c = a || o;
833
853
  let s = e.var.tenant_id;
834
- const f = e.var.user, i = (f != null && f.aud ? Array.isArray(f.aud) ? f.aud : [f.aud] : []).includes(W);
835
- if (!s && d && i && (e.set("tenant_id", d), s = d), !s)
854
+ const d = e.var.user, l = (d != null && d.aud ? Array.isArray(d.aud) ? d.aud : [d.aud] : []).includes(K);
855
+ if (!s && c && l && (e.set("tenant_id", c), s = c), !s)
836
856
  throw new b(400, {
837
857
  message: "Tenant ID not found in request"
838
858
  });
839
859
  if (!se(
840
- c,
860
+ o,
841
861
  s,
842
862
  r,
843
863
  a
@@ -848,39 +868,39 @@ function Te(t) {
848
868
  return n();
849
869
  };
850
870
  }
851
- function Ae(t) {
871
+ function Pe(t) {
852
872
  return async (e, n) => {
853
873
  if (!t.subdomainRouting)
854
874
  return n();
855
875
  const {
856
876
  baseDomain: r,
857
877
  reservedSubdomains: a = [],
858
- resolveSubdomain: c
859
- } = t.subdomainRouting, d = e.req.header("host") || "";
878
+ resolveSubdomain: o
879
+ } = t.subdomainRouting, c = e.req.header("host") || "";
860
880
  let s = null;
861
- if (d.endsWith(r)) {
862
- const o = d.slice(0, -(r.length + 1));
863
- o && !o.includes(".") && (s = o);
881
+ if (c.endsWith(r)) {
882
+ const i = c.slice(0, -(r.length + 1));
883
+ i && !i.includes(".") && (s = i);
864
884
  }
865
885
  if (s && a.includes(s) && (s = null), !s)
866
886
  return t.accessControl && e.set("tenant_id", t.accessControl.controlPlaneTenantId), n();
867
- let f = null;
868
- if (c)
869
- f = await c(s);
887
+ let d = null;
888
+ if (o)
889
+ d = await o(s);
870
890
  else if (t.subdomainRouting.useOrganizations !== !1 && t.accessControl)
871
891
  try {
872
- const o = await e.env.data.organizations.get(
892
+ const i = await e.env.data.organizations.get(
873
893
  t.accessControl.controlPlaneTenantId,
874
894
  s
875
895
  );
876
- o && (f = o.id);
896
+ i && (d = i.id);
877
897
  } catch {
878
898
  }
879
- if (!f)
899
+ if (!d)
880
900
  throw new b(404, {
881
901
  message: `Tenant not found for subdomain: ${s}`
882
902
  });
883
- return e.set("tenant_id", f), n();
903
+ return e.set("tenant_id", d), n();
884
904
  };
885
905
  }
886
906
  function _e(t) {
@@ -906,14 +926,14 @@ function _e(t) {
906
926
  return n();
907
927
  };
908
928
  }
909
- function Q(t) {
910
- const e = Ae(t), n = Te(t), r = _e(t);
911
- return async (a, c) => (await e(a, async () => {
929
+ function W(t) {
930
+ const e = Pe(t), n = Ce(t), r = _e(t);
931
+ return async (a, o) => (await e(a, async () => {
912
932
  }), await n(a, async () => {
913
933
  }), await r(a, async () => {
914
- }), c());
934
+ }), o());
915
935
  }
916
- function $e(t) {
936
+ function ze(t) {
917
937
  const {
918
938
  dataAdapter: e,
919
939
  controlPlane: n,
@@ -921,80 +941,80 @@ function $e(t) {
921
941
  tenantId: r = "control_plane",
922
942
  clientId: a
923
943
  } = {},
924
- sync: c = { resourceServers: !0, roles: !0 },
925
- defaultPermissions: d = ["tenant:admin"],
944
+ sync: o = { resourceServers: !0, roles: !0 },
945
+ defaultPermissions: c = ["tenant:admin"],
926
946
  requireOrganizationMatch: s = !1,
927
- managementApiExtensions: f = [],
928
- entityHooks: o,
929
- getChildTenantIds: i,
930
- getAdapters: u,
931
- ...l
947
+ managementApiExtensions: d = [],
948
+ entityHooks: i,
949
+ getChildTenantIds: l,
950
+ getAdapters: g,
951
+ ...u
932
952
  } = t;
933
- let m = e, p = e;
934
- n && (m = D(e, {
953
+ let f = e, m = e;
954
+ n && (f = M(e, {
935
955
  controlPlaneTenantId: r,
936
956
  controlPlaneClientId: a
937
- }), p = D(e, {
957
+ }), m = M(e, {
938
958
  controlPlaneTenantId: r,
939
959
  controlPlaneClientId: a,
940
960
  excludeSensitiveFields: !0
941
961
  }));
942
- const g = c !== !1, h = g ? {
943
- resourceServers: c.resourceServers ?? !0,
944
- roles: c.roles ?? !0
962
+ const p = o !== !1, h = p ? {
963
+ resourceServers: o.resourceServers ?? !0,
964
+ roles: o.roles ?? !0
945
965
  } : { resourceServers: !1, roles: !1 }, T = {
946
966
  controlPlaneTenantId: r,
947
- getChildTenantIds: i ?? (async () => (await F(
948
- (I) => m.tenants.list(I),
967
+ getChildTenantIds: l ?? (async () => (await F(
968
+ (I) => f.tenants.list(I),
949
969
  "tenants",
950
970
  { cursorField: "id", pageSize: 100 }
951
971
  )).filter((I) => I.id !== r).map((I) => I.id)),
952
- getAdapters: u ?? (async () => m),
953
- getControlPlaneAdapters: async () => m,
972
+ getAdapters: g ?? (async () => f),
973
+ getControlPlaneAdapters: async () => f,
954
974
  sync: h
955
- }, { entityHooks: A, tenantHooks: _ } = fe(T), v = {
975
+ }, { entityHooks: C, tenantHooks: P } = fe(T), v = {
956
976
  resourceServers: [
957
- A.resourceServers,
958
- ...(o == null ? void 0 : o.resourceServers) ?? []
977
+ C.resourceServers,
978
+ ...(i == null ? void 0 : i.resourceServers) ?? []
959
979
  ],
960
- roles: [A.roles, ...(o == null ? void 0 : o.roles) ?? []],
961
- connections: (o == null ? void 0 : o.connections) ?? [],
962
- tenants: (o == null ? void 0 : o.tenants) ?? [],
963
- rolePermissions: (o == null ? void 0 : o.rolePermissions) ?? []
964
- }, P = M(
980
+ roles: [C.roles, ...(i == null ? void 0 : i.roles) ?? []],
981
+ connections: (i == null ? void 0 : i.connections) ?? [],
982
+ tenants: (i == null ? void 0 : i.tenants) ?? [],
983
+ rolePermissions: (i == null ? void 0 : i.rolePermissions) ?? []
984
+ }, _ = q(
965
985
  {
966
986
  accessControl: {
967
987
  controlPlaneTenantId: r,
968
988
  requireOrganizationMatch: s,
969
- defaultPermissions: d
989
+ defaultPermissions: c
970
990
  }
971
991
  },
972
- { tenants: _ }
973
- ), { app: C } = ne({
974
- dataAdapter: m,
975
- managementDataAdapter: p,
976
- ...l,
992
+ { tenants: P }
993
+ ), { app: A } = ne({
994
+ dataAdapter: f,
995
+ managementDataAdapter: m,
996
+ ...u,
977
997
  entityHooks: v,
978
998
  managementApiExtensions: [
979
- ...f,
980
- { path: "/tenants", router: P }
999
+ ...d,
1000
+ { path: "/tenants", router: _ }
981
1001
  ]
982
1002
  });
983
- return C.use("/api/v2/*", ve(r)), g && C.use("/api/v2/*", we()), { app: C, controlPlaneTenantId: r };
1003
+ return A.use("/api/v2/*", Te(r)), p && A.use("/api/v2/*", we()), { app: A, controlPlaneTenantId: r };
984
1004
  }
985
- function ze(t) {
986
- const e = q(t);
1005
+ function Fe(t) {
1006
+ const e = j(t);
987
1007
  return {
988
1008
  name: "multi-tenancy",
989
1009
  // Apply multi-tenancy middleware for subdomain routing, database resolution, etc.
990
- middleware: Q(t),
1010
+ middleware: W(t),
991
1011
  // Provide lifecycle hooks
992
1012
  hooks: e,
993
1013
  // Mount tenant management routes
994
1014
  routes: [
995
1015
  {
996
1016
  path: "/management",
997
- handler: M(t, e)
1017
+ handler: q(t, e)
998
1018
  }
999
1019
  ],
1000
1020
  // Called when plugin is registered
@@ -1007,7 +1027,7 @@ function ze(t) {
1007
1027
  }
1008
1028
  };
1009
1029
  }
1010
- function q(t) {
1030
+ function j(t) {
1011
1031
  const e = t.accessControl ? ae(t.accessControl) : {}, n = t.databaseIsolation ? oe(t.databaseIsolation) : {}, r = ce(t);
1012
1032
  return {
1013
1033
  ...e,
@@ -1015,15 +1035,15 @@ function q(t) {
1015
1035
  tenants: r
1016
1036
  };
1017
1037
  }
1018
- function Pe(t) {
1019
- const e = new Z(), n = q(t);
1020
- return e.route("/tenants", M(t, n)), e;
1038
+ function Ae(t) {
1039
+ const e = new X(), n = j(t);
1040
+ return e.route("/tenants", q(t, n)), e;
1021
1041
  }
1022
- function Fe(t) {
1042
+ function Oe(t) {
1023
1043
  return {
1024
- hooks: q(t),
1025
- middleware: Q(t),
1026
- app: Pe(t),
1044
+ hooks: j(t),
1045
+ middleware: W(t),
1046
+ app: Ae(t),
1027
1047
  config: t,
1028
1048
  /**
1029
1049
  * Wraps data adapters with runtime fallback from the control plane.
@@ -1035,7 +1055,7 @@ function Fe(t) {
1035
1055
  */
1036
1056
  wrapAdapters: (e, n) => {
1037
1057
  var r;
1038
- return D(e, {
1058
+ return M(e, {
1039
1059
  controlPlaneTenantId: (r = t.accessControl) == null ? void 0 : r.controlPlaneTenantId,
1040
1060
  controlPlaneClientId: n == null ? void 0 : n.controlPlaneClientId
1041
1061
  });
@@ -1044,22 +1064,22 @@ function Fe(t) {
1044
1064
  }
1045
1065
  export {
1046
1066
  ae as createAccessControlHooks,
1047
- Te as createAccessControlMiddleware,
1048
- ve as createControlPlaneTenantMiddleware,
1067
+ Ce as createAccessControlMiddleware,
1068
+ Te as createControlPlaneTenantMiddleware,
1049
1069
  oe as createDatabaseHooks,
1050
1070
  _e as createDatabaseMiddleware,
1051
- Pe as createMultiTenancy,
1052
- q as createMultiTenancyHooks,
1053
- Q as createMultiTenancyMiddleware,
1054
- ze as createMultiTenancyPlugin,
1071
+ Ae as createMultiTenancy,
1072
+ j as createMultiTenancyHooks,
1073
+ W as createMultiTenancyMiddleware,
1074
+ Fe as createMultiTenancyPlugin,
1055
1075
  we as createProtectSyncedMiddleware,
1056
1076
  ce as createProvisioningHooks,
1057
- he as createRuntimeFallbackAdapter,
1058
- Ae as createSubdomainMiddleware,
1077
+ ve as createRuntimeFallbackAdapter,
1078
+ Pe as createSubdomainMiddleware,
1059
1079
  fe as createSyncHooks,
1060
- M as createTenantsOpenAPIRouter,
1061
- $e as initMultiTenant,
1062
- Fe as setupMultiTenancy,
1080
+ q as createTenantsOpenAPIRouter,
1081
+ ze as initMultiTenant,
1082
+ Oe as setupMultiTenancy,
1063
1083
  se as validateTenantAccess,
1064
- D as withRuntimeFallback
1084
+ M as withRuntimeFallback
1065
1085
  };
@@ -1 +1 @@
1
- {"version":3,"file":"settings-inheritance.d.ts","sourceRoot":"","sources":["../../../src/middleware/settings-inheritance.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,UAAU,CAAC;AA+BlB;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,qBAAqB,GAC5B,YAAY,CA0Hd;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,qBAAqB,GAC5B,YAAY,CAEd"}
1
+ {"version":3,"file":"settings-inheritance.d.ts","sourceRoot":"","sources":["../../../src/middleware/settings-inheritance.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAIb,MAAM,UAAU,CAAC;AAoElB;;;;GAIG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,qBAAqB,GAC5B,YAAY,CAwId;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,qBAAqB,GAC5B,YAAY,CAEd"}
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "git",
12
12
  "url": "https://github.com/markusahlstrand/authhero"
13
13
  },
14
- "version": "14.7.0",
14
+ "version": "14.8.0",
15
15
  "description": "Multi-tenancy support for AuthHero with organization-based access control and per-tenant database isolation",
16
16
  "files": [
17
17
  "dist"
@@ -36,9 +36,9 @@
36
36
  "typescript": "^5.6.0",
37
37
  "vite": "^6.0.0",
38
38
  "vitest": "^2.1.0",
39
- "@authhero/kysely-adapter": "10.88.0",
40
- "authhero": "4.23.0",
41
- "@authhero/adapter-interfaces": "0.127.0"
39
+ "@authhero/kysely-adapter": "10.91.0",
40
+ "authhero": "4.27.0",
41
+ "@authhero/adapter-interfaces": "0.129.0"
42
42
  },
43
43
  "dependencies": {
44
44
  "zod": "^3.24.0"