@authhero/multi-tenancy 14.20.0 → 14.20.2

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 ce=Object.defineProperty;var le=(e,t,n)=>t in e?ce(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var k=(e,t,n)=>le(e,typeof t!="symbol"?t+"":t,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ue=require("hono"),_=require("authhero"),T=require("@hono/zod-openapi");function J(e){const{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(r,a){if(a===t)return!0;if(n){const s=r.var.org_name,c=r.var.organization_id,i=s||c;return i?i.toLowerCase()===a.toLowerCase():!1}return!0}}}function X(e,t,n,r){if(t===n)return!0;const a=r||e;return a?a.toLowerCase()===t.toLowerCase():!1}function Y(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(n){console.error(`Failed to resolve data adapters for tenant ${t}:`,n);return}}}}function de(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function N(e){return{async beforeCreate(t,n){return!n.audience&&n.id?{...n,audience:de(n.id)}:n},async afterCreate(t,n){const{accessControl:r,databaseIsolation:a}=e;r&&t.ctx&&await fe(t,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(t,n){const{accessControl:r,databaseIsolation:a}=e;if(r)try{const c=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(i=>i.name===n);c&&await t.adapters.organizations.remove(r.controlPlaneTenantId,c.id)}catch(s){console.warn(`Failed to remove organization for tenant ${n}:`,s)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(s){console.warn(`Failed to deprovision database for tenant ${n}:`,s)}}}}async function fe(e,t,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:s,issuer:c,adminRoleName:i="Tenant Admin",adminRoleDescription:u="Full access to all tenant management operations",addCreatorToOrganization:o=!0}=n,l=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id});let g;if(c&&(g=await ge(e,r,i,u)),o&&e.ctx){const d=e.ctx.var.user;if(d!=null&&d.sub&&!await me(e,r,d.sub))try{await e.adapters.userOrganizations.create(r,{user_id:d.sub,organization_id:l.id}),g&&await e.adapters.userRoles.create(r,d.sub,g,l.id)}catch(m){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,m)}}s&&s.length>0&&console.log(`Would assign roles ${s.join(", ")} to organization ${l.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${l.id}`)}async function me(e,t,n){const r=await e.adapters.userRoles.list(t,n,void 0,"");for(const a of r)if((await e.adapters.rolePermissions.list(t,a.id,{per_page:1e3})).some(i=>i.permission_name==="admin:organizations"))return!0;return!1}async function ge(e,t,n,r){const s=(await e.adapters.roles.list(t,{})).roles.find(o=>o.name===n);if(s)return s.id;const c=await e.adapters.roles.create(t,{name:n,description:r}),i=_.MANAGEMENT_API_AUDIENCE,u=_.MANAGEMENT_API_SCOPES.map(o=>({role_id:c.id,resource_server_identifier:i,permission_name:o.value}));return await e.adapters.rolePermissions.assign(t,c.id,u),c.id}function U(e,t,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:s}=e,c=new Map;async function i(l,g,d){return(await t(l).list(g,{q:`name:${d}`,per_page:1}))[0]??null}async function u(l){const g=await a(),d=t(await s(r));await Promise.all(g.map(async f=>{try{const m=await s(f),w=t(m),y={...d.transform(l),is_system:!0},C=await i(m,f,l.name),P=C?w.getId(C):void 0;if(C&&P){const S=w.preserveOnUpdate?w.preserveOnUpdate(C,y):y;await w.update(f,P,S)}else await w.create(f,y)}catch(m){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${f}":`,m)}}))}async function o(l){const g=await a();await Promise.all(g.map(async d=>{try{const f=await s(d),m=t(f),w=await i(f,d,l),b=w?m.getId(w):void 0;w&&b&&await m.remove(d,b)}catch(f){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,f)}}))}return{afterCreate:async(l,g)=>{l.tenantId===r&&n(g)&&await u(g)},afterUpdate:async(l,g,d)=>{l.tenantId===r&&n(d)&&await u(d)},beforeDelete:async(l,g)=>{if(l.tenantId!==r)return;const f=await t(l.adapters).get(l.tenantId,g);f&&n(f)&&c.set(g,f)},afterDelete:async(l,g)=>{if(l.tenantId!==r)return;const d=c.get(g);d&&(c.delete(g),await o(d.name))}}}function G(e,t,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:s}=e;return{async afterCreate(c,i){if(i.id!==r)try{const u=await a(),o=await s(i.id),l=t(u),g=t(o),d=await _.fetchAll(f=>l.listPaginated(r,f),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(f=>n(f)).map(async f=>{try{const m=l.transform(f);await g.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync entity to new tenant "${i.id}":`,m)}}))}catch(u){console.error(`Failed to sync entities to new tenant "${i.id}":`,u)}}}}const L=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:"resource_servers",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,identifier:t.identifier,scopes:t.scopes,signing_alg:t.signing_alg,token_lifetime:t.token_lifetime,token_lifetime_for_web:t.token_lifetime_for_web})}),W=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:"roles",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,description:t.description})});function K(e){var t;return((t=e.metadata)==null?void 0:t.sync)!==!1}function Z(e){const{sync:t={},filters:n={}}=e,r=t.resourceServers??!0,a=t.roles??!0,s=m=>K(m)?n.resourceServers?n.resourceServers(m):!0:!1,c=m=>K(m)?n.roles?n.roles(m):!0:!1,i=r?U(e,L,s):void 0,u=a?U(e,W,c):void 0,o=r?G(e,L,s):void 0,l=a?G(e,W,c):void 0,g=a?{async afterCreate(m,w){var b;if(w.id!==e.controlPlaneTenantId){await((b=l==null?void 0:l.afterCreate)==null?void 0:b.call(l,m,w));try{const y=await e.getControlPlaneAdapters(),C=await e.getAdapters(w.id),P=await _.fetchAll(p=>y.roles.list(e.controlPlaneTenantId,p),"roles",{cursorField:"id",pageSize:100}),S=new Map;for(const p of P.filter(A=>{var h;return((h=n.roles)==null?void 0:h.call(n,A))??!0})){const A=await d(C,w.id,p.name);A&&S.set(p.name,A.id)}for(const p of P.filter(A=>{var h;return((h=n.roles)==null?void 0:h.call(n,A))??!0})){const A=S.get(p.name);if(A)try{const h=await y.rolePermissions.list(e.controlPlaneTenantId,p.id,{});h.length>0&&await C.rolePermissions.assign(w.id,A,h.map(R=>({role_id:A,resource_server_identifier:R.resource_server_identifier,permission_name:R.permission_name})))}catch(h){console.error(`Failed to sync permissions for role "${p.name}" to tenant "${w.id}":`,h)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${w.id}":`,y)}}}}:void 0;async function d(m,w,b){return(await m.roles.list(w,{q:`name:${b}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:i,roles:u},tenantHooks:{async afterCreate(m,w){const b=[o==null?void 0:o.afterCreate,(g==null?void 0:g.afterCreate)??(l==null?void 0:l.afterCreate)],y=[];for(const C of b)if(C)try{await C(m,w)}catch(P){y.push(P instanceof Error?P:new Error(String(P)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(C=>C.message).join("; "))}}}}var I=class extends Error{constructor(t=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});k(this,"res");k(this,"status");this.res=n==null?void 0:n.res,this.status=t}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function q(e,t){const n=new T.OpenAPIHono;return n.openapi(T.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:_.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:T.z.object({tenants:T.z.array(_.tenantSchema),start:T.z.number().optional(),limit:T.z.number().optional(),length:T.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var m,w,b,y,C,P;const a=r.req.valid("query"),{page:s,per_page:c,include_totals:i,q:u}=a,o=r.var.user,l=(o==null?void 0:o.permissions)||[];if(l.includes("auth:read")||l.includes("admin:organizations")){const S=await r.env.data.tenants.list({page:s,per_page:c,include_totals:i,q:u});return i?r.json({tenants:S.tenants,start:((m=S.totals)==null?void 0:m.start)??0,limit:((w=S.totals)==null?void 0:w.limit)??c,length:S.tenants.length}):r.json({tenants:S.tenants})}const d=((b=e.accessControl)==null?void 0:b.controlPlaneTenantId)??((y=r.env.data.multiTenancyConfig)==null?void 0:y.controlPlaneTenantId);if(d&&(o!=null&&o.sub)){const p=(await _.fetchAll(M=>r.env.data.userOrganizations.listUserOrganizations(d,o.sub,M),"organizations")).map(M=>M.name);if(p.length===0)return i?r.json({tenants:[],start:0,limit:c??50,length:0}):r.json({tenants:[]});const A=p.length,h=s??0,R=c??50,F=h*R,D=p.slice(F,F+R);if(D.length===0)return i?r.json({tenants:[],start:F,limit:R,length:A}):r.json({tenants:[]});const z=D.map(M=>`id:${M}`).join(" OR "),v=u?`(${z}) AND (${u})`:z,$=await r.env.data.tenants.list({q:v,per_page:R,include_totals:!1});return i?r.json({tenants:$.tenants,start:F,limit:R,length:A}):r.json({tenants:$.tenants})}const f=await r.env.data.tenants.list({page:s,per_page:c,include_totals:i,q:u});return i?r.json({tenants:f.tenants,start:((C=f.totals)==null?void 0:C.start)??0,limit:((P=f.totals)==null?void 0:P.limit)??c,length:f.tenants.length}):r.json({tenants:f.tenants})}),n.openapi(T.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:_.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:_.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var u,o;const a=r.var.user;if(!(a!=null&&a.sub))throw new I(401,{message:"Authentication required to create tenants"});let s=r.req.valid("json");const c={adapters:r.env.data,ctx:r};(u=t.tenants)!=null&&u.beforeCreate&&(s=await t.tenants.beforeCreate(c,s));const i=await r.env.data.tenants.create(s);return(o=t.tenants)!=null&&o.afterCreate&&await t.tenants.afterCreate(c,i),r.json(i,201)}),n.openapi(T.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:T.z.object({id:T.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 u,o,l,g;const{id:a}=r.req.valid("param"),s=((u=e.accessControl)==null?void 0:u.controlPlaneTenantId)??((o=r.env.data.multiTenancyConfig)==null?void 0:o.controlPlaneTenantId);if(s){const d=r.var.user;if(!(d!=null&&d.sub))throw new I(401,{message:"Authentication required"});if(a===s)throw new I(403,{message:"Cannot delete the control plane"});if(!(await _.fetchAll(w=>r.env.data.userOrganizations.listUserOrganizations(s,d.sub,w),"organizations")).some(w=>w.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 i={adapters:r.env.data,ctx:r};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(i,a),await r.env.data.tenants.remove(a),(g=t.tenants)!=null&&g.afterDelete&&await t.tenants.afterDelete(i,a),r.body(null,204)}),n.openapi(T.createRoute({tags:["tenants","settings"],method:"get",path:"/settings",request:{headers:T.z.object({"tenant-id":T.z.string().optional()})},security:[{Bearer:["read:tenants","auth:read"]}],responses:{200:{content:{"application/json":{schema:_.tenantSchema}},description:"Current tenant settings"}}}),async r=>{const a=await r.env.data.tenants.get(r.var.tenant_id);if(!a)throw new I(404,{message:"Tenant not found"});return r.json(a)}),n.openapi(T.createRoute({tags:["tenants","settings"],method:"patch",path:"/settings",request:{headers:T.z.object({"tenant-id":T.z.string().optional()}),body:{content:{"application/json":{schema:T.z.object(_.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants","auth:write"]}],responses:{200:{content:{"application/json":{schema:_.tenantSchema}},description:"Updated tenant settings"}}}),async r=>{const a=r.req.valid("json"),{id:s,...c}=a,i=await r.env.data.tenants.get(r.var.tenant_id);if(!i)throw new I(404,{message:"Tenant not found"});const u=_.deepMergePatch(i,c);await r.env.data.tenants.update(r.var.tenant_id,u);const o=await r.env.data.tenants.get(r.var.tenant_id);if(!o)throw new I(500,{message:"Failed to retrieve updated tenant"});return r.json(o)}),n}function we(e){const t=[{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 t){const a=e.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function pe(e,t,n){try{switch(n.type){case"resource_server":{const r=await e.resourceServers.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await e.roles.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await e.connections.get(t,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function he(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function x(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const n=we(e.req.path);if(!n)return t();const r=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!r)return t();if(await pe(e.env.data,r,n))throw new I(403,{message:`This ${he(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function E(e,t){const n=t.find(a=>a.strategy===e.strategy);if(!(n!=null&&n.options))return e;const r=_.connectionSchema.passthrough().parse({...n,...e});return r.options=_.connectionOptionsSchema.passthrough().parse({...n.options||{},...e.options}),r}function j(e,t){const n=[...t||[],...e||[]];return[...new Set(n)]}function ye(e,t){if(!(t!=null&&t.length))return e||[];if(!(e!=null&&e.length))return t;const n=new Map;for(const r of t)n.set(r.value,r);for(const r of e)n.set(r.value,r);return Array.from(n.values())}function Q(e,t){return t?{...e,scopes:ye(e.scopes,t.scopes)}:e}function V(e,t){return t?{...e,callbacks:j(e.callbacks,t.callbacks),web_origins:j(e.web_origins,t.web_origins),allowed_logout_urls:j(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:j(e.allowed_origins,t.allowed_origins)}:e}function ee(e,t){return{...e.resourceServers,get:async(n,r)=>{const a=await e.resourceServers.get(n,r);if(!a||!t||n===t||!a.is_system)return a;const s=await e.resourceServers.get(t,r);return Q(a,s)},list:async(n,r)=>{const a=await e.resourceServers.list(n,r);if(!t||n===t)return a;const s=t,c=a.resource_servers.filter(o=>!!(o.is_system&&o.id)).map(o=>o.id);if(c.length===0)return a;const i=new Map;await Promise.all(c.map(async o=>{const l=await e.resourceServers.get(s,o);l&&i.set(o,l)}));const u=a.resource_servers.map(o=>o.is_system&&o.id?Q(o,i.get(o.id)??null):o);return{...a,resource_servers:u}}}}function te(e,t){return{...e,resourceServers:ee(e,t.controlPlaneTenantId)}}function ne(e,t){const{controlPlaneTenantId:n,controlPlaneClientId:r}=t;return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...e.connections,get:async(a,s)=>{const c=await e.connections.get(a,s);if(!c||!n||a===n)return c;const i=await e.connections.list(n);return E(c,i.connections||[])},list:async(a,s)=>{const c=await e.connections.list(a,s);if(!n||a===n)return c;const i=await e.connections.list(n),u=c.connections.map(o=>E(o,i.connections||[]));return{...c,connections:u}}},clientConnections:{...e.clientConnections,listByClient:async(a,s)=>{let c=await e.clientConnections.listByClient(a,s);if(c.length===0&&(c=(await e.connections.list(a)).connections||[]),!n||a===n)return c;const i=await e.connections.list(n);return c.map(u=>E(u,i.connections||[]))}},clients:{...e.clients,get:async(a,s)=>{const c=await e.clients.get(a,s);if(!c)return null;if(!n||!r||a===n&&s===r)return c;const i=await e.clients.get(n,r);return V(c,i)},getByClientId:async a=>{const s=await e.clients.getByClientId(a);if(!s)return null;if(!n||!r||s.tenant_id===n&&s.client_id===r)return s;const c=await e.clients.get(n,r);return{...V(s,c),tenant_id:s.tenant_id}}},emailProviders:{...e.emailProviders,get:async a=>{const s=await e.emailProviders.get(a);return s||(!n||a===n?null:e.emailProviders.get(n))}},resourceServers:ee(e,n)}}function B(e,t){return ne(e,t)}function re(e){return async(t,n)=>{const r=t.var.user;return(r==null?void 0:r.tenant_id)===e&&r.org_name&&t.set("tenant_id",r.org_name),n()}}function ae(e){return async(t,n)=>{if(!e.accessControl)return n();const{controlPlaneTenantId:r}=e.accessControl,a=t.var.org_name,s=t.var.organization_id,c=a||s;let i=t.var.tenant_id;const u=t.var.user,l=(u!=null&&u.aud?Array.isArray(u.aud)?u.aud:[u.aud]:[]).includes(_.MANAGEMENT_API_AUDIENCE);if(!i&&c&&l&&(t.set("tenant_id",c),i=c),!i)throw new I(400,{message:"Tenant ID not found in request"});if(!X(s,i,r,a))throw new I(403,{message:`Access denied to tenant ${i}`});return n()}}function se(e){return async(t,n)=>{if(!e.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:s}=e.subdomainRouting,c=t.req.header("x-forwarded-host")||t.req.header("host")||"";let i=null;if(c.endsWith(r)){const o=c.slice(0,-(r.length+1));o&&!o.includes(".")&&(i=o)}if(i&&a.includes(i)&&(i=null),!i)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),n();let u=null;if(s)u=await s(i);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const o=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,i);o&&(u=o.id)}catch{}if(!u)throw new I(404,{message:`Tenant not found for subdomain: ${i}`});return t.set("tenant_id",u),n()}}function oe(e){return async(t,n)=>{if(!e.databaseIsolation)return n();const r=t.var.tenant_id;if(!r)throw new I(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(r);t.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 H(e){const t=se(e),n=ae(e),r=oe(e);return async(a,s)=>(await t(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),s())}function ve(e){const{dataAdapter:t,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:s={resourceServers:!0,roles:!0},defaultPermissions:c=["tenant:admin"],requireOrganizationMatch:i=!1,managementApiExtensions:u=[],entityHooks:o,getChildTenantIds:l,getAdapters:g,...d}=e;let f=t,m=t;n&&(f=B(t,{controlPlaneTenantId:r,controlPlaneClientId:a}),m={...te(t,{controlPlaneTenantId:r}),multiTenancyConfig:{controlPlaneTenantId:r,controlPlaneClientId:a}});const w=s!==!1,b=w?{resourceServers:s.resourceServers??!0,roles:s.roles??!0}:{resourceServers:!1,roles:!1},P={controlPlaneTenantId:r,getChildTenantIds:l??(async()=>(await _.fetchAll(v=>f.tenants.list(v),"tenants",{cursorField:"id",pageSize:100})).filter(v=>v.id!==r).map(v=>v.id)),getAdapters:g??(async()=>f),getControlPlaneAdapters:async()=>f,sync:b},{entityHooks:S,tenantHooks:p}=Z(P),A={resourceServers:[S.resourceServers,...(o==null?void 0:o.resourceServers)??[]],roles:[S.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)??[]},h=N({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:c}}),F=q({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:i,defaultPermissions:c}},{tenants:{async beforeCreate(z,v){return h.beforeCreate&&(v=await h.beforeCreate(z,v)),p.beforeCreate&&(v=await p.beforeCreate(z,v)),v},async afterCreate(z,v){var $,M;await(($=h.afterCreate)==null?void 0:$.call(h,z,v)),await((M=p.afterCreate)==null?void 0:M.call(p,z,v))},async beforeDelete(z,v){var $,M;await(($=h.beforeDelete)==null?void 0:$.call(h,z,v)),await((M=p.beforeDelete)==null?void 0:M.call(p,z,v))}}}),{app:D}=_.init({dataAdapter:f,managementDataAdapter:m,...d,entityHooks:A,managementApiExtensions:[...u,{path:"/tenants",router:F}]});return D.use("/api/v2/*",re(r)),w&&D.use("/api/v2/*",x()),{app:D,controlPlaneTenantId:r}}function _e(e){const t=O(e);return{name:"multi-tenancy",middleware:H(e),hooks:t,routes:[{path:"/management",handler:q(e,t)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function O(e){const t=e.accessControl?J(e.accessControl):{},n=e.databaseIsolation?Y(e.databaseIsolation):{},r=N(e);return{...t,...n,tenants:r}}function ie(e){const t=new ue.Hono,n=O(e);return t.route("/tenants",q(e,n)),t}function Te(e){return{hooks:O(e),middleware:H(e),app:ie(e),config:e,wrapAdapters:(t,n)=>{var r;return B(t,{controlPlaneTenantId:(r=e.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=J;exports.createAccessControlMiddleware=ae;exports.createControlPlaneTenantMiddleware=re;exports.createDatabaseHooks=Y;exports.createDatabaseMiddleware=oe;exports.createMultiTenancy=ie;exports.createMultiTenancyHooks=O;exports.createMultiTenancyMiddleware=H;exports.createMultiTenancyPlugin=_e;exports.createProtectSyncedMiddleware=x;exports.createProvisioningHooks=N;exports.createRuntimeFallbackAdapter=ne;exports.createSubdomainMiddleware=se;exports.createSyncHooks=Z;exports.createTenantsOpenAPIRouter=q;exports.initMultiTenant=ve;exports.setupMultiTenancy=Te;exports.validateTenantAccess=X;exports.withRuntimeFallback=B;exports.withSystemResourceServerInheritance=te;
1
+ "use strict";var ue=Object.defineProperty;var de=(e,t,n)=>t in e?ue(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var E=(e,t,n)=>de(e,typeof t!="symbol"?t+"":t,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const fe=require("hono"),_=require("authhero"),C=require("@hono/zod-openapi");function Y(e){const{controlPlaneTenantId:t,requireOrganizationMatch:n=!0}=e;return{async onTenantAccessValidation(r,a){if(a===t)return!0;if(n){const s=r.var.org_name,i=r.var.organization_id,c=s||i;return c?c.toLowerCase()===a.toLowerCase():!1}return!0}}}function Z(e,t,n,r){if(t===n)return!0;const a=r||e;return a?a.toLowerCase()===t.toLowerCase():!1}function x(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(n){console.error(`Failed to resolve data adapters for tenant ${t}:`,n);return}}}}function me(e){return`urn:authhero:tenant:${e.toLowerCase()}`}function B(e){return{async beforeCreate(t,n){return!n.audience&&n.id?{...n,audience:me(n.id)}:n},async afterCreate(t,n){const{accessControl:r,databaseIsolation:a}=e;r&&t.ctx&&await ge(t,n,r),a!=null&&a.onProvision&&await a.onProvision(n.id)},async beforeDelete(t,n){const{accessControl:r,databaseIsolation:a}=e;if(r)try{const i=(await t.adapters.organizations.list(r.controlPlaneTenantId)).organizations.find(c=>c.name===n);i&&await t.adapters.organizations.remove(r.controlPlaneTenantId,i.id)}catch(s){console.warn(`Failed to remove organization for tenant ${n}:`,s)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(n)}catch(s){console.warn(`Failed to deprovision database for tenant ${n}:`,s)}}}}async function ge(e,t,n){const{controlPlaneTenantId:r,defaultPermissions:a,defaultRoles:s,issuer:i,adminRoleName:c="Tenant Admin",adminRoleDescription:u="Full access to all tenant management operations",addCreatorToOrganization:o=!0}=n,l=await e.adapters.organizations.create(r,{name:t.id,display_name:t.friendly_name||t.id});let g;if(i&&(g=await he(e,r,c,u)),o&&e.ctx){const d=e.ctx.var.user;if(d!=null&&d.sub&&!await we(e,r,d.sub))try{await e.adapters.userOrganizations.create(r,{user_id:d.sub,organization_id:l.id}),g&&await e.adapters.userRoles.create(r,d.sub,g,l.id)}catch(f){console.warn(`Failed to add creator ${d.sub} to organization ${l.id}:`,f)}}s&&s.length>0&&console.log(`Would assign roles ${s.join(", ")} to organization ${l.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${l.id}`)}async function we(e,t,n){const r=await e.adapters.userRoles.list(t,n,void 0,"");for(const a of r)if((await e.adapters.rolePermissions.list(t,a.id,{per_page:1e3})).some(c=>c.permission_name==="admin:organizations"))return!0;return!1}async function he(e,t,n,r){const s=(await e.adapters.roles.list(t,{})).roles.find(o=>o.name===n);if(s)return s.id;const i=await e.adapters.roles.create(t,{name:n,description:r}),c=_.MANAGEMENT_API_AUDIENCE,u=_.MANAGEMENT_API_SCOPES.map(o=>({role_id:i.id,resource_server_identifier:c,permission_name:o.value}));return await e.adapters.rolePermissions.assign(t,i.id,u),i.id}function G(e,t,n=()=>!0){const{controlPlaneTenantId:r,getChildTenantIds:a,getAdapters:s}=e,i=new Map;async function c(l,g,d){return(await t(l).list(g,{q:`name:${d}`,per_page:1}))[0]??null}async function u(l){const g=await a(),d=t(await s(r));await Promise.all(g.map(async m=>{try{const f=await s(m),w=t(f),y={...d.transform(l),is_system:!0},T=await c(f,m,l.name),A=T?w.getId(T):void 0;if(T&&A){const z=w.preserveOnUpdate?w.preserveOnUpdate(T,y):y;await w.update(m,A,z)}else await w.create(m,y)}catch(f){console.error(`Failed to sync ${d.listKey} "${l.name}" to tenant "${m}":`,f)}}))}async function o(l){const g=await a();await Promise.all(g.map(async d=>{try{const m=await s(d),f=t(m),w=await c(m,d,l),P=w?f.getId(w):void 0;w&&P&&await f.remove(d,P)}catch(m){console.error(`Failed to delete entity "${l}" from tenant "${d}":`,m)}}))}return{afterCreate:async(l,g)=>{l.tenantId===r&&n(g)&&await u(g)},afterUpdate:async(l,g,d)=>{l.tenantId===r&&n(d)&&await u(d)},beforeDelete:async(l,g)=>{if(l.tenantId!==r)return;const m=await t(l.adapters).get(l.tenantId,g);m&&n(m)&&i.set(g,m)},afterDelete:async(l,g)=>{if(l.tenantId!==r)return;const d=i.get(g);d&&(i.delete(g),await o(d.name))}}}function W(e,t,n=()=>!0){const{controlPlaneTenantId:r,getControlPlaneAdapters:a,getAdapters:s}=e;return{async afterCreate(i,c){if(c.id!==r)try{const u=await a(),o=await s(c.id),l=t(u),g=t(o),d=await _.fetchAll(m=>l.listPaginated(r,m),l.listKey,{cursorField:"id",pageSize:100});await Promise.all(d.filter(m=>n(m)).map(async m=>{try{const f=l.transform(m);await g.create(c.id,{...f,is_system:!0})}catch(f){console.error(`Failed to sync entity to new tenant "${c.id}":`,f)}}))}catch(u){console.error(`Failed to sync entities to new tenant "${c.id}":`,u)}}}}const L=e=>({list:async(t,n)=>(await e.resourceServers.list(t,n)).resource_servers,listPaginated:(t,n)=>e.resourceServers.list(t,n),get:(t,n)=>e.resourceServers.get(t,n),create:(t,n)=>e.resourceServers.create(t,n),update:(t,n,r)=>e.resourceServers.update(t,n,r),remove:(t,n)=>e.resourceServers.remove(t,n),listKey:"resource_servers",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,identifier:t.identifier,scopes:t.scopes,signing_alg:t.signing_alg,token_lifetime:t.token_lifetime,token_lifetime_for_web:t.token_lifetime_for_web})}),K=e=>({list:async(t,n)=>(await e.roles.list(t,n)).roles,listPaginated:(t,n)=>e.roles.list(t,n),get:(t,n)=>e.roles.get(t,n),create:(t,n)=>e.roles.create(t,n),update:(t,n,r)=>e.roles.update(t,n,r),remove:(t,n)=>e.roles.remove(t,n),listKey:"roles",getId:t=>t.id,transform:t=>({id:t.id,name:t.name,description:t.description})});function Q(e){var t;return((t=e.metadata)==null?void 0:t.sync)!==!1}function ee(e){const{sync:t={},filters:n={}}=e,r=t.resourceServers??!0,a=t.roles??!0,s=f=>Q(f)?n.resourceServers?n.resourceServers(f):!0:!1,i=f=>Q(f)?n.roles?n.roles(f):!0:!1,c=r?G(e,L,s):void 0,u=a?G(e,K,i):void 0,o=r?W(e,L,s):void 0,l=a?W(e,K,i):void 0,g=a?{async afterCreate(f,w){var P;if(w.id!==e.controlPlaneTenantId){await((P=l==null?void 0:l.afterCreate)==null?void 0:P.call(l,f,w));try{const y=await e.getControlPlaneAdapters(),T=await e.getAdapters(w.id),A=await _.fetchAll(h=>y.roles.list(e.controlPlaneTenantId,h),"roles",{cursorField:"id",pageSize:100}),z=new Map;for(const h of A.filter(b=>{var p;return((p=n.roles)==null?void 0:p.call(n,b))??!0})){const b=await d(T,w.id,h.name);b&&z.set(h.name,b.id)}for(const h of A.filter(b=>{var p;return((p=n.roles)==null?void 0:p.call(n,b))??!0})){const b=z.get(h.name);if(b)try{const p=await y.rolePermissions.list(e.controlPlaneTenantId,h.id,{});p.length>0&&await T.rolePermissions.assign(w.id,b,p.map(j=>({role_id:b,resource_server_identifier:j.resource_server_identifier,permission_name:j.permission_name})))}catch(p){console.error(`Failed to sync permissions for role "${h.name}" to tenant "${w.id}":`,p)}}}catch(y){console.error(`Failed to sync role permissions to tenant "${w.id}":`,y)}}}}:void 0;async function d(f,w,P){return(await f.roles.list(w,{q:`name:${P}`,per_page:1})).roles[0]??null}return{entityHooks:{resourceServers:c,roles:u},tenantHooks:{async afterCreate(f,w){const P=[o==null?void 0:o.afterCreate,(g==null?void 0:g.afterCreate)??(l==null?void 0:l.afterCreate)],y=[];for(const T of P)if(T)try{await T(f,w)}catch(A){y.push(A instanceof Error?A:new Error(String(A)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,y.map(T=>T.message).join("; "))}}}}var S=class extends Error{constructor(t=500,n){super(n==null?void 0:n.message,{cause:n==null?void 0:n.cause});E(this,"res");E(this,"status");this.res=n==null?void 0:n.res,this.status=t}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function O(e,t){const n=new C.OpenAPIHono;return n.openapi(C.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:_.auth0QuerySchema},security:[{Bearer:["read:tenants","auth:read"]}],responses:{200:{content:{"application/json":{schema:C.z.object({tenants:C.z.array(_.tenantSchema),start:C.z.number().optional(),limit:C.z.number().optional(),length:C.z.number().optional()})}},description:"List of tenants"}}}),async r=>{var w,P,y,T,A,z;const a=r.req.valid("query"),{page:s,per_page:i,include_totals:c,q:u}=a,o=r.var.user,l=(o==null?void 0:o.permissions)||[];if(!!!((o==null?void 0:o.org_id)??r.var.organization_id)&&(l.includes("auth:read")||l.includes("admin:organizations"))){const h=await r.env.data.tenants.list({page:s,per_page:i,include_totals:c,q:u});return c?r.json({tenants:h.tenants,start:((w=h.totals)==null?void 0:w.start)??0,limit:((P=h.totals)==null?void 0:P.limit)??i,length:h.tenants.length}):r.json({tenants:h.tenants})}const m=((y=e.accessControl)==null?void 0:y.controlPlaneTenantId)??((T=r.env.data.multiTenancyConfig)==null?void 0:T.controlPlaneTenantId);if(m&&(o!=null&&o.sub)){const b=(await _.fetchAll(F=>r.env.data.userOrganizations.listUserOrganizations(m,o.sub,F),"organizations")).map(F=>F.name);if(b.length===0)return c?r.json({tenants:[],start:0,limit:i??50,length:0}):r.json({tenants:[]});const p=b.length,j=s??0,k=i??50,M=j*k,I=b.slice(M,M+k);if(I.length===0)return c?r.json({tenants:[],start:M,limit:k,length:p}):r.json({tenants:[]});const v=I.map(F=>`id:${F}`).join(" OR "),$=u?`(${v}) AND (${u})`:v,R=await r.env.data.tenants.list({q:$,per_page:k,include_totals:!1});return c?r.json({tenants:R.tenants,start:M,limit:k,length:p}):r.json({tenants:R.tenants})}const f=await r.env.data.tenants.list({page:s,per_page:i,include_totals:c,q:u});return c?r.json({tenants:f.tenants,start:((A=f.totals)==null?void 0:A.start)??0,limit:((z=f.totals)==null?void 0:z.limit)??i,length:f.tenants.length}):r.json({tenants:f.tenants})}),n.openapi(C.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:_.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:_.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async r=>{var u,o;const a=r.var.user;if(!(a!=null&&a.sub))throw new S(401,{message:"Authentication required to create tenants"});let s=r.req.valid("json");const i={adapters:r.env.data,ctx:r};(u=t.tenants)!=null&&u.beforeCreate&&(s=await t.tenants.beforeCreate(i,s));const c=await r.env.data.tenants.create(s);return(o=t.tenants)!=null&&o.afterCreate&&await t.tenants.afterCreate(i,c),r.json(c,201)}),n.openapi(C.createRoute({tags:["tenants"],method:"delete",path:"/{id}",request:{params:C.z.object({id:C.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 u,o,l,g;const{id:a}=r.req.valid("param"),s=((u=e.accessControl)==null?void 0:u.controlPlaneTenantId)??((o=r.env.data.multiTenancyConfig)==null?void 0:o.controlPlaneTenantId);if(s){const d=r.var.user;if(!(d!=null&&d.sub))throw new S(401,{message:"Authentication required"});if(a===s)throw new S(403,{message:"Cannot delete the control plane"});if(!(await _.fetchAll(w=>r.env.data.userOrganizations.listUserOrganizations(s,d.sub,w),"organizations")).some(w=>w.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 c={adapters:r.env.data,ctx:r};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(c,a),await r.env.data.tenants.remove(a),(g=t.tenants)!=null&&g.afterDelete&&await t.tenants.afterDelete(c,a),r.body(null,204)}),n.openapi(C.createRoute({tags:["tenants","settings"],method:"get",path:"/settings",request:{headers:C.z.object({"tenant-id":C.z.string().optional()})},security:[{Bearer:["read:tenants","auth:read"]}],responses:{200:{content:{"application/json":{schema:_.tenantSchema}},description:"Current tenant settings"}}}),async r=>{const a=await r.env.data.tenants.get(r.var.tenant_id);if(!a)throw new S(404,{message:"Tenant not found"});return r.json(a)}),n.openapi(C.createRoute({tags:["tenants","settings"],method:"patch",path:"/settings",request:{headers:C.z.object({"tenant-id":C.z.string().optional()}),body:{content:{"application/json":{schema:C.z.object(_.tenantInsertSchema.shape).partial()}}}},security:[{Bearer:["update:tenants","auth:write"]}],responses:{200:{content:{"application/json":{schema:_.tenantSchema}},description:"Updated tenant settings"}}}),async r=>{const a=r.req.valid("json"),{id:s,...i}=a,c=await r.env.data.tenants.get(r.var.tenant_id);if(!c)throw new S(404,{message:"Tenant not found"});const u=_.deepMergePatch(c,i);await r.env.data.tenants.update(r.var.tenant_id,u);const o=await r.env.data.tenants.get(r.var.tenant_id);if(!o)throw new S(500,{message:"Failed to retrieve updated tenant"});return r.json(o)}),n}function pe(e){const t=[{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 t){const a=e.match(n);if(a&&a[1])return{type:r,id:a[1]}}return null}async function ye(e,t,n){try{switch(n.type){case"resource_server":{const r=await e.resourceServers.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"role":{const r=await e.roles.get(t,n.id);return(r==null?void 0:r.is_system)===!0}case"connection":{const r=await e.connections.get(t,n.id);return(r==null?void 0:r.is_system)===!0}default:return!1}}catch{return!1}}function ve(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function te(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const n=pe(e.req.path);if(!n)return t();const r=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!r)return t();if(await ye(e.env.data,r,n))throw new S(403,{message:`This ${ve(n.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function N(e,t){const n=t.find(a=>a.strategy===e.strategy);if(!(n!=null&&n.options))return e;const r=_.connectionSchema.passthrough().parse({...n,...e});return r.options=_.connectionOptionsSchema.passthrough().parse({...n.options||{},...e.options}),r}function D(e,t){const n=[...t||[],...e||[]];return[...new Set(n)]}function _e(e,t){if(!(t!=null&&t.length))return e||[];if(!(e!=null&&e.length))return t;const n=new Map;for(const r of t)n.set(r.value,r);for(const r of e)n.set(r.value,r);return Array.from(n.values())}function V(e,t){return t?{...e,scopes:_e(e.scopes,t.scopes)}:e}function J(e,t){return t?{...e,callbacks:D(e.callbacks,t.callbacks),web_origins:D(e.web_origins,t.web_origins),allowed_logout_urls:D(e.allowed_logout_urls,t.allowed_logout_urls),allowed_origins:D(e.allowed_origins,t.allowed_origins)}:e}function ne(e,t){return{...e.resourceServers,get:async(n,r)=>{const a=await e.resourceServers.get(n,r);if(!a||!t||n===t||!a.is_system)return a;const s=await e.resourceServers.get(t,r);return V(a,s)},list:async(n,r)=>{const a=await e.resourceServers.list(n,r);if(!t||n===t)return a;const s=t,i=a.resource_servers.filter(o=>!!(o.is_system&&o.id)).map(o=>o.id);if(i.length===0)return a;const c=new Map;await Promise.all(i.map(async o=>{const l=await e.resourceServers.get(s,o);l&&c.set(o,l)}));const u=a.resource_servers.map(o=>o.is_system&&o.id?V(o,c.get(o.id)??null):o);return{...a,resource_servers:u}}}}function re(e,t){return{...e,resourceServers:ne(e,t.controlPlaneTenantId)}}function ae(e,t){const{controlPlaneTenantId:n,controlPlaneClientId:r}=t;return{...e,multiTenancyConfig:{controlPlaneTenantId:n,controlPlaneClientId:r},connections:{...e.connections,get:async(a,s)=>{const i=await e.connections.get(a,s);if(!i||!n||a===n)return i;const c=await e.connections.list(n);return N(i,c.connections||[])},list:async(a,s)=>{const i=await e.connections.list(a,s);if(!n||a===n)return i;const c=await e.connections.list(n),u=i.connections.map(o=>N(o,c.connections||[]));return{...i,connections:u}}},clientConnections:{...e.clientConnections,listByClient:async(a,s)=>{let i=await e.clientConnections.listByClient(a,s);if(i.length===0&&(i=(await e.connections.list(a)).connections||[]),!n||a===n)return i;const c=await e.connections.list(n);return i.map(u=>N(u,c.connections||[]))}},clients:{...e.clients,get:async(a,s)=>{const i=await e.clients.get(a,s);if(!i)return null;if(!n||!r||a===n&&s===r)return i;const c=await e.clients.get(n,r);return J(i,c)},getByClientId:async a=>{const s=await e.clients.getByClientId(a);if(!s)return null;if(!n||!r||s.tenant_id===n&&s.client_id===r)return s;const i=await e.clients.get(n,r);return{...J(s,i),tenant_id:s.tenant_id}}},emailProviders:{...e.emailProviders,get:async a=>{const s=await e.emailProviders.get(a);return s||(!n||a===n?null:e.emailProviders.get(n))}},resourceServers:ne(e,n),hooks:Ce(e,n)}}function X(e){if(!e||typeof e!="object")return!1;const t=e.metadata;return!t||typeof t!="object"?!1:t.inheritable===!0}function Ce(e,t){return{...e.hooks,list:async(n,r)=>{const a=await e.hooks.list(n,r);if(!t||n===t)return a;const i=((await e.hooks.list(t,r)).hooks||[]).filter(X);if(i.length===0)return a;const c=new Set((a.hooks||[]).map(o=>o.hook_id)),u=i.filter(o=>!c.has(o.hook_id));return{...a,hooks:[...a.hooks||[],...u],length:typeof a.length=="number"?a.length+u.length:a.length}},get:async(n,r)=>{const a=await e.hooks.get(n,r);if(a||!t||n===t)return a;const s=await e.hooks.get(t,r);return s&&X(s)?s:null}}}function H(e,t){return ae(e,t)}function se(e){return async(t,n)=>{const r=t.var.user;return(r==null?void 0:r.tenant_id)===e&&r.org_name&&t.set("tenant_id",r.org_name),n()}}function oe(e){return async(t,n)=>{if(!e.accessControl)return n();const{controlPlaneTenantId:r}=e.accessControl,a=t.var.org_name,s=t.var.organization_id,i=a||s;let c=t.var.tenant_id;const u=t.var.user,l=(u!=null&&u.aud?Array.isArray(u.aud)?u.aud:[u.aud]:[]).includes(_.MANAGEMENT_API_AUDIENCE);if(!c&&i&&l&&(t.set("tenant_id",i),c=i),!c)throw new S(400,{message:"Tenant ID not found in request"});if(!Z(s,c,r,a))throw new S(403,{message:`Access denied to tenant ${c}`});return n()}}function ie(e){return async(t,n)=>{if(!e.subdomainRouting)return n();const{baseDomain:r,reservedSubdomains:a=[],resolveSubdomain:s}=e.subdomainRouting,i=t.req.header("x-forwarded-host")||t.req.header("host")||"";let c=null;if(i.endsWith(r)){const o=i.slice(0,-(r.length+1));o&&!o.includes(".")&&(c=o)}if(c&&a.includes(c)&&(c=null),!c)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),n();let u=null;if(s)u=await s(c);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const o=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,c);o&&(u=o.id)}catch{}if(!u)throw new S(404,{message:`Tenant not found for subdomain: ${c}`});return t.set("tenant_id",u),n()}}function ce(e){return async(t,n)=>{if(!e.databaseIsolation)return n();const r=t.var.tenant_id;if(!r)throw new S(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(r);t.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 U(e){const t=ie(e),n=oe(e),r=ce(e);return async(a,s)=>(await t(a,async()=>{}),await n(a,async()=>{}),await r(a,async()=>{}),s())}function Te(e){const{dataAdapter:t,controlPlane:n,controlPlane:{tenantId:r="control_plane",clientId:a}={},sync:s={resourceServers:!0,roles:!0},defaultPermissions:i=["tenant:admin"],requireOrganizationMatch:c=!1,managementApiExtensions:u=[],entityHooks:o,getChildTenantIds:l,getAdapters:g,...d}=e;let m=t,f=t;n&&(m=H(t,{controlPlaneTenantId:r,controlPlaneClientId:a}),f={...re(t,{controlPlaneTenantId:r}),multiTenancyConfig:{controlPlaneTenantId:r,controlPlaneClientId:a}});const w=s!==!1,P=w?{resourceServers:s.resourceServers??!0,roles:s.roles??!0}:{resourceServers:!1,roles:!1},A={controlPlaneTenantId:r,getChildTenantIds:l??(async()=>(await _.fetchAll(v=>m.tenants.list(v),"tenants",{cursorField:"id",pageSize:100})).filter(v=>v.id!==r).map(v=>v.id)),getAdapters:g??(async()=>m),getControlPlaneAdapters:async()=>m,sync:P},{entityHooks:z,tenantHooks:h}=ee(A),b={resourceServers:[z.resourceServers,...(o==null?void 0:o.resourceServers)??[]],roles:[z.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)??[]},p=B({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:c,defaultPermissions:i}}),k=O({accessControl:{controlPlaneTenantId:r,requireOrganizationMatch:c,defaultPermissions:i}},{tenants:{async beforeCreate(I,v){return p.beforeCreate&&(v=await p.beforeCreate(I,v)),h.beforeCreate&&(v=await h.beforeCreate(I,v)),v},async afterCreate(I,v){var $,R;await(($=p.afterCreate)==null?void 0:$.call(p,I,v)),await((R=h.afterCreate)==null?void 0:R.call(h,I,v))},async beforeDelete(I,v){var $,R;await(($=p.beforeDelete)==null?void 0:$.call(p,I,v)),await((R=h.beforeDelete)==null?void 0:R.call(h,I,v))}}}),{app:M}=_.init({dataAdapter:m,managementDataAdapter:f,...d,entityHooks:b,managementApiExtensions:[...u,{path:"/tenants",router:k}]});return M.use("/api/v2/*",se(r)),w&&M.use("/api/v2/*",te()),{app:M,controlPlaneTenantId:r}}function be(e){const t=q(e);return{name:"multi-tenancy",middleware:U(e),hooks:t,routes:[{path:"/management",handler:O(e,t)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (control plane: ${e.accessControl.controlPlaneTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function q(e){const t=e.accessControl?Y(e.accessControl):{},n=e.databaseIsolation?x(e.databaseIsolation):{},r=B(e);return{...t,...n,tenants:r}}function le(e){const t=new fe.Hono,n=q(e);return t.route("/tenants",O(e,n)),t}function Pe(e){return{hooks:q(e),middleware:U(e),app:le(e),config:e,wrapAdapters:(t,n)=>{var r;return H(t,{controlPlaneTenantId:(r=e.accessControl)==null?void 0:r.controlPlaneTenantId,controlPlaneClientId:n==null?void 0:n.controlPlaneClientId})}}}exports.createAccessControlHooks=Y;exports.createAccessControlMiddleware=oe;exports.createControlPlaneTenantMiddleware=se;exports.createDatabaseHooks=x;exports.createDatabaseMiddleware=ce;exports.createMultiTenancy=le;exports.createMultiTenancyHooks=q;exports.createMultiTenancyMiddleware=U;exports.createMultiTenancyPlugin=be;exports.createProtectSyncedMiddleware=te;exports.createProvisioningHooks=B;exports.createRuntimeFallbackAdapter=ae;exports.createSubdomainMiddleware=ie;exports.createSyncHooks=ee;exports.createTenantsOpenAPIRouter=O;exports.initMultiTenant=Te;exports.setupMultiTenancy=Pe;exports.validateTenantAccess=Z;exports.withRuntimeFallback=H;exports.withSystemResourceServerInheritance=re;