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