@authhero/multi-tenancy 13.11.0 → 13.12.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.
- package/dist/multi-tenancy.cjs +1 -1
- package/dist/multi-tenancy.mjs +130 -107
- package/package.json +2 -2
package/dist/multi-tenancy.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var Z=Object.defineProperty;var H=(e,t,s)=>t in e?Z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var j=(e,t,s)=>H(e,typeof t!="symbol"?t+"":t,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),b=require("authhero"),C=require("@hono/zod-openapi"),I=require("@authhero/adapter-interfaces");var v=class extends Error{constructor(t=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});j(this,"res");j(this,"status");this.res=s==null?void 0:s.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 U(e){const{controlPlaneTenantId:t,requireOrganizationMatch:s=!0}=e;return{async onTenantAccessValidation(a,i){if(i===t)return!0;if(s){const n=a.var.org_name,u=a.var.organization_id,l=n||u;return l?l===i:!1}return!0}}}function E(e,t,s,a){if(t===s)return!0;const i=a||e;return i?i===t:!1}function K(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(s){console.error(`Failed to resolve data adapters for tenant ${t}:`,s);return}}}}function B(e){return{async beforeCreate(t,s){return!s.audience&&s.id?{...s,audience:b.getTenantAudience(s.id)}:s},async afterCreate(t,s){const{accessControl:a,databaseIsolation:i,settingsInheritance:n}=e;a&&t.ctx&&await x(t,s,a),i!=null&&i.onProvision&&await i.onProvision(s.id),(n==null?void 0:n.inheritFromControlPlane)!==!1&&t.ctx&&await te(t,s,e)},async beforeDelete(t,s){const{accessControl:a,databaseIsolation:i}=e;if(a)try{const u=(await t.adapters.organizations.list(a.controlPlaneTenantId)).organizations.find(l=>l.name===s);u&&await t.adapters.organizations.remove(a.controlPlaneTenantId,u.id)}catch(n){console.warn(`Failed to remove organization for tenant ${s}:`,n)}if(i!=null&&i.onDeprovision)try{await i.onDeprovision(s)}catch(n){console.warn(`Failed to deprovision database for tenant ${s}:`,n)}}}}async function x(e,t,s){const{controlPlaneTenantId:a,defaultPermissions:i,defaultRoles:n,issuer:u,adminRoleName:l="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s,f=await e.adapters.organizations.create(a,{name:t.id,display_name:t.friendly_name||t.id});let d;if(u&&(d=await ee(e,a,l,c)),r&&e.ctx){const o=e.ctx.var.user;if(o!=null&&o.sub)try{await e.adapters.userOrganizations.create(a,{user_id:o.sub,organization_id:f.id}),d&&await e.adapters.userRoles.create(a,o.sub,d,f.id)}catch(p){console.warn(`Failed to add creator ${o.sub} to organization ${f.id}:`,p)}}n&&n.length>0&&console.log(`Would assign roles ${n.join(", ")} to organization ${f.id}`),i&&i.length>0&&console.log(`Would grant permissions ${i.join(", ")} to organization ${f.id}`)}async function ee(e,t,s,a){const n=(await e.adapters.roles.list(t,{})).roles.find(r=>r.name===s);if(n)return n.id;const u=await e.adapters.roles.create(t,{name:s,description:a}),l=b.MANAGEMENT_API_AUDIENCE,c=b.MANAGEMENT_API_SCOPES.map(r=>({role_id:u.id,resource_server_identifier:l,permission_name:r.value}));return await e.adapters.rolePermissions.assign(t,u.id,c),u.id}async function te(e,t,s){const{accessControl:a,settingsInheritance:i}=s;if(!a)return;const n=await e.adapters.tenants.get(a.controlPlaneTenantId);if(!n)return;let u={...n};const l=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of l)delete u[c];if(i!=null&&i.inheritedKeys){const c={};for(const r of i.inheritedKeys)r in n&&!l.includes(r)&&(c[r]=n[r]);u=c}if(i!=null&&i.excludedKeys)for(const c of i.excludedKeys)delete u[c];i!=null&&i.transformSettings&&(u=i.transformSettings(u,t.id)),Object.keys(u).length>0&&await e.adapters.tenants.update(t.id,u)}function W(e){const{controlPlaneTenantId:t,getChildTenantIds:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;async function u(r,f,d){return(await r.resourceServers.list(f,{q:`identifier:${d}`,per_page:1})).resource_servers[0]??null}async function l(r,f){const d=await s();await Promise.all(d.map(async o=>{try{const p=await a(o),h={...n?n(r,o):{name:r.name,identifier:r.identifier,scopes:r.scopes,signing_alg:r.signing_alg,signing_secret:r.signing_secret,token_lifetime:r.token_lifetime,token_lifetime_for_web:r.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:r.skip_consent_for_verifiable_first_party_clients,allow_offline_access:r.allow_offline_access,verificationKey:r.verificationKey,options:r.options},is_system:!0};if(f==="create"){const A=await u(p,o,r.identifier);A&&A.id?await p.resourceServers.update(o,A.id,h):await p.resourceServers.create(o,h)}else{const A=await u(p,o,r.identifier);A&&A.id?await p.resourceServers.update(o,A.id,h):await p.resourceServers.create(o,h)}}catch(p){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${o}":`,p)}}))}async function c(r){const f=await s();await Promise.all(f.map(async d=>{try{const o=await a(d),p=await u(o,d,r);p&&p.id&&await o.resourceServers.remove(d,p.id)}catch(o){console.error(`Failed to delete resource server "${r}" from tenant "${d}":`,o)}}))}return{afterCreate:async(r,f)=>{r.tenantId===t&&i(f)&&await l(f,"create")},afterUpdate:async(r,f,d)=>{r.tenantId===t&&i(d)&&await l(d,"update")},afterDelete:async(r,f)=>{r.tenantId===t&&await c(f)}}}function G(e){const{controlPlaneTenantId:t,getControlPlaneAdapters:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;return{async afterCreate(u,l){if(l.id!==t)try{const c=await s(),r=await a(l.id),f=await b.fetchAll(d=>c.resourceServers.list(t,d),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(f.filter(d=>i(d)).map(async d=>{const o=d;try{const p=n?n(o,l.id):{name:o.name,identifier:o.identifier,scopes:o.scopes,signing_alg:o.signing_alg,signing_secret:o.signing_secret,token_lifetime:o.token_lifetime,token_lifetime_for_web:o.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:o.skip_consent_for_verifiable_first_party_clients,allow_offline_access:o.allow_offline_access,verificationKey:o.verificationKey,options:o.options};await r.resourceServers.create(l.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync resource server "${o.identifier}" to new tenant "${l.id}":`,p)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${l.id}":`,c)}}}}function L(e){const{controlPlaneTenantId:t,getChildTenantIds:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;async function u(c,r,f){return(await c.roles.list(r,{q:`name:${f}`,per_page:1})).roles[0]??null}async function l(c,r){const f=await s();await Promise.all(f.map(async d=>{try{const o=await a(d),g={...n?n(c,d):{name:c.name,description:c.description},is_system:!0};if(r==="create"){const h=await u(o,d,c.name);h&&h.id?await o.roles.update(d,h.id,g):await o.roles.create(d,g)}else{const h=await u(o,d,c.name);h&&h.id?await o.roles.update(d,h.id,g):await o.roles.create(d,g)}}catch(o){console.error(`Failed to sync role "${c.name}" to tenant "${d}":`,o)}}))}return{afterCreate:async(c,r)=>{c.tenantId===t&&i(r)&&await l(r,"create")},afterUpdate:async(c,r,f)=>{c.tenantId===t&&i(f)&&await l(f,"update")},afterDelete:async(c,r)=>{c.tenantId===t&&console.warn(`Role ${r} was deleted from control plane. Child tenant roles with matching names should be deleted manually or implement role name tracking.`)}}}function Q(e){const{controlPlaneTenantId:t,getControlPlaneAdapters:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n,syncPermissions:u=!0}=e;return{async afterCreate(l,c){if(c.id!==t)try{const r=await s(),f=await a(c.id),d=await b.fetchAll(p=>r.roles.list(t,p),"roles",{cursorField:"id",pageSize:100}),o=new Map;if(await Promise.all(d.filter(p=>i(p)).map(async p=>{const g=p;try{const h=n?n(g,c.id):{name:g.name,description:g.description},A=await f.roles.create(c.id,{...h,is_system:!0});o.set(g.id,A.id)}catch(h){console.error(`Failed to sync role "${g.name}" to new tenant "${c.id}":`,h)}})),u)for(const[p,g]of o)try{const h=await r.rolePermissions.list(t,p,{});h.length>0&&await f.rolePermissions.assign(c.id,g,h.map(A=>({role_id:g,resource_server_identifier:A.resource_server_identifier,permission_name:A.permission_name})))}catch(h){console.error(`Failed to sync permissions for role to new tenant "${c.id}":`,h)}}catch(r){console.error(`Failed to sync roles to new tenant "${c.id}":`,r)}}}}function D(e,t){const s=new C.OpenAPIHono;return s.openapi(C.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:I.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:C.z.object({tenants:C.z.array(I.tenantSchema),start:C.z.number().optional(),limit:C.z.number().optional(),length:C.z.number().optional()})}},description:"List of tenants"}}}),async a=>{var p,g,h,A;const i=a.req.valid("query"),{page:n,per_page:u,include_totals:l,q:c}=i,r=a.var.user,f=((r==null?void 0:r.scope)||"").split(" "),d=f.includes("auth:read");if(console.log("User scopes:",f,"hasAuthRead:",d),d){const S=await a.env.data.tenants.list({page:n,per_page:u,include_totals:l,q:c});return l?a.json({tenants:S.tenants,start:((p=S.totals)==null?void 0:p.start)??0,limit:((g=S.totals)==null?void 0:g.limit)??u,length:S.tenants.length}):a.json({tenants:S.tenants})}if(e.accessControl&&(r!=null&&r.sub)){const S=e.accessControl.controlPlaneTenantId,M=(await b.fetchAll(R=>a.env.data.userOrganizations.listUserOrganizations(S,r.sub,R),"organizations")).map(R=>R.name);if(M.length===0)return l?a.json({tenants:[],start:0,limit:u??50,length:0}):a.json({tenants:[]});const $=M.length,F=n??0,P=u??50,w=F*P,m=M.slice(w,w+P);if(m.length===0)return l?a.json({tenants:[],start:w,limit:P,length:$}):a.json({tenants:[]});const y=m.map(R=>`id:${R}`).join(" OR "),_=c?`(${y}) AND (${c})`:y,T=await a.env.data.tenants.list({q:_,per_page:P,include_totals:!1});return l?a.json({tenants:T.tenants,start:w,limit:P,length:$}):a.json({tenants:T.tenants})}const o=await a.env.data.tenants.list({page:n,per_page:u,include_totals:l,q:c});return l?a.json({tenants:o.tenants,start:((h=o.totals)==null?void 0:h.start)??0,limit:((A=o.totals)==null?void 0:A.limit)??u,length:o.tenants.length}):a.json({tenants:o.tenants})}),s.openapi(C.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:I.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:I.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async a=>{var c,r;const i=a.var.user;if(!(i!=null&&i.sub))throw new v(401,{message:"Authentication required to create tenants"});let n=a.req.valid("json");const u={adapters:a.env.data,ctx:a};(c=t.tenants)!=null&&c.beforeCreate&&(n=await t.tenants.beforeCreate(u,n));const l=await a.env.data.tenants.create(n);return(r=t.tenants)!=null&&r.afterCreate&&await t.tenants.afterCreate(u,l),a.json(l,201)}),s.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 a=>{var l,c;const{id:i}=a.req.valid("param");if(e.accessControl){const r=a.var.user,f=e.accessControl.controlPlaneTenantId;if(!(r!=null&&r.sub))throw new v(401,{message:"Authentication required"});if(i===f)throw new v(403,{message:"Cannot delete the control plane"});if(!(await b.fetchAll(p=>a.env.data.userOrganizations.listUserOrganizations(f,r.sub,p),"organizations")).some(p=>p.name===i))throw new v(403,{message:"Access denied to this tenant"})}if(!await a.env.data.tenants.get(i))throw new v(404,{message:"Tenant not found"});const u={adapters:a.env.data,ctx:a};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(u,i),await a.env.data.tenants.remove(i),(c=t.tenants)!=null&&c.afterDelete&&await t.tenants.afterDelete(u,i),a.body(null,204)}),s}function ae(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:s,type:a}of t){const i=e.match(s);if(i&&i[1])return{type:a,id:i[1]}}return null}async function ne(e,t,s){try{switch(s.type){case"resource_server":{const a=await e.resourceServers.get(t,s.id);return(a==null?void 0:a.is_system)===!0}case"role":{const a=await e.roles.get(t,s.id);return(a==null?void 0:a.is_system)===!0}case"connection":{const a=await e.connections.get(t,s.id);return(a==null?void 0:a.is_system)===!0}default:return!1}}catch{return!1}}function re(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function V(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const s=ae(e.req.path);if(!s)return t();const a=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!a)return t();if(await ne(e.env.data,a,s))throw new v(403,{message:`This ${re(s.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function k(e){return async(t,s)=>{if(!e.accessControl)return s();const a=t.var.tenant_id,i=t.var.organization_id;if(!a)throw new v(400,{message:"Tenant ID not found in request"});if(!E(i,a,e.accessControl.controlPlaneTenantId))throw new v(403,{message:`Access denied to tenant ${a}`});return s()}}function J(e){return async(t,s)=>{if(!e.subdomainRouting)return s();const{baseDomain:a,reservedSubdomains:i=[],resolveSubdomain:n}=e.subdomainRouting,u=t.req.header("host")||"";let l=null;if(u.endsWith(a)){const r=u.slice(0,-(a.length+1));r&&!r.includes(".")&&(l=r)}if(l&&i.includes(l)&&(l=null),!l)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),s();let c=null;if(n)c=await n(l);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const r=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,l);r&&(c=r.id)}catch{}if(!c)throw new v(404,{message:`Tenant not found for subdomain: ${l}`});return t.set("tenant_id",c),s()}}function X(e){return async(t,s)=>{if(!e.databaseIsolation)return s();const a=t.var.tenant_id;if(!a)throw new v(400,{message:"Tenant ID not found in request"});try{const i=await e.databaseIsolation.getAdapters(a);t.env.data=i}catch(i){throw console.error(`Failed to resolve database for tenant ${a}:`,i),new v(500,{message:"Failed to resolve tenant database"})}return s()}}function q(e){const t=J(e),s=k(e),a=X(e);return async(i,n)=>(await t(i,async()=>{}),await s(i,async()=>{}),await a(i,async()=>{}),n())}function se(e){const t=z(e);return{name:"multi-tenancy",middleware:q(e),hooks:t,routes:[{path:"/management",handler:D(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 z(e){const t=e.accessControl?U(e.accessControl):{},s=e.databaseIsolation?K(e.databaseIsolation):{},a=B(e);return{...t,...s,tenants:a}}function Y(e){const t=new N.Hono,s=z(e);return t.route("/tenants",D(e,s)),t}function ie(e){return{hooks:z(e),middleware:q(e),app:Y(e),config:e}}function oe(e){const{controlPlaneTenantId:t="control_plane",syncResourceServers:s=!0,syncRoles:a=!0,multiTenancy:i,entityHooks:n,...u}=e,l={...i,accessControl:{controlPlaneTenantId:t,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...i==null?void 0:i.accessControl}},c=z(l);let r,f;s&&(r=W({controlPlaneTenantId:t,getChildTenantIds:async()=>(await b.fetchAll(m=>e.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==t).map(m=>m.id),getAdapters:async w=>e.dataAdapter}),f=G({controlPlaneTenantId:t,getControlPlaneAdapters:async()=>e.dataAdapter,getAdapters:async w=>e.dataAdapter}));let d,o;a&&(d=L({controlPlaneTenantId:t,getChildTenantIds:async()=>(await b.fetchAll(m=>e.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==t).map(m=>m.id),getAdapters:async w=>e.dataAdapter}),o=Q({controlPlaneTenantId:t,getControlPlaneAdapters:async()=>e.dataAdapter,getAdapters:async w=>e.dataAdapter,syncPermissions:!0}));const p=async(w,m,...y)=>{const _=[];if(w)try{await w(...y)}catch(T){_.push(T instanceof Error?T:new Error(String(T)))}if(m)try{await m(...y)}catch(T){_.push(T instanceof Error?T:new Error(String(T)))}if(_.length===1)throw _[0];if(_.length>1)throw new AggregateError(_,`Multiple hook errors: ${_.map(T=>T.message).join("; ")}`)},g=async(w,...m)=>{const y=[];for(const _ of w)if(_)try{await _(...m)}catch(T){y.push(T instanceof Error?T:new Error(String(T)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,`Multiple hook errors: ${y.map(_=>_.message).join("; ")}`)},h={...n,resourceServers:r?{...n==null?void 0:n.resourceServers,afterCreate:async(w,m)=>{var y;await p((y=n==null?void 0:n.resourceServers)==null?void 0:y.afterCreate,r==null?void 0:r.afterCreate,w,m)},afterUpdate:async(w,m,y)=>{var _;await p((_=n==null?void 0:n.resourceServers)==null?void 0:_.afterUpdate,r==null?void 0:r.afterUpdate,w,m,y)},afterDelete:async(w,m)=>{var y;await p((y=n==null?void 0:n.resourceServers)==null?void 0:y.afterDelete,r==null?void 0:r.afterDelete,w,m)}}:n==null?void 0:n.resourceServers,roles:d?{...n==null?void 0:n.roles,afterCreate:async(w,m)=>{var y;await p((y=n==null?void 0:n.roles)==null?void 0:y.afterCreate,d==null?void 0:d.afterCreate,w,m)},afterUpdate:async(w,m,y)=>{var _;await p((_=n==null?void 0:n.roles)==null?void 0:_.afterUpdate,d==null?void 0:d.afterUpdate,w,m,y)},afterDelete:async(w,m)=>{var y;await p((y=n==null?void 0:n.roles)==null?void 0:y.afterDelete,d==null?void 0:d.afterDelete,w,m)}}:n==null?void 0:n.roles,tenants:f||o?{...n==null?void 0:n.tenants,afterCreate:async(w,m)=>{var y;await g([(y=n==null?void 0:n.tenants)==null?void 0:y.afterCreate,f==null?void 0:f.afterCreate,o==null?void 0:o.afterCreate],w,m)}}:n==null?void 0:n.tenants},A={...c,tenants:f||o?{...c.tenants,afterCreate:async(w,m)=>{var y;(y=c.tenants)!=null&&y.afterCreate&&await c.tenants.afterCreate(w,m),await g([f==null?void 0:f.afterCreate,o==null?void 0:o.afterCreate],w,m)}}:c.tenants},S=D(l,A),O=b.init({...u,entityHooks:h,managementApiExtensions:[...u.managementApiExtensions||[],{path:"/tenants",router:S}]}),{app:M,managementApp:$,...F}=O,P=new N.Hono;return P.onError((w,m)=>w instanceof v?w.getResponse():(console.error(w),m.json({message:"Internal Server Error"},500))),P.use("/api/v2/*",V()),P.route("/",M),{app:P,managementApp:$,...F,multiTenancyConfig:l,multiTenancyHooks:c}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>b.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"fetchAll",{enumerable:!0,get:()=>b.fetchAll});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>b.seed});exports.createAccessControlHooks=U;exports.createAccessControlMiddleware=k;exports.createDatabaseHooks=K;exports.createDatabaseMiddleware=X;exports.createMultiTenancy=Y;exports.createMultiTenancyHooks=z;exports.createMultiTenancyMiddleware=q;exports.createMultiTenancyPlugin=se;exports.createProtectSyncedMiddleware=V;exports.createProvisioningHooks=B;exports.createResourceServerSyncHooks=W;exports.createRoleSyncHooks=L;exports.createSubdomainMiddleware=J;exports.createTenantResourceServerSyncHooks=G;exports.createTenantRoleSyncHooks=Q;exports.createTenantsOpenAPIRouter=D;exports.init=oe;exports.setupMultiTenancy=ie;exports.validateTenantAccess=E;
|
|
1
|
+
"use strict";var Z=Object.defineProperty;var H=(e,t,s)=>t in e?Z(e,t,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[t]=s;var F=(e,t,s)=>H(e,typeof t!="symbol"?t+"":t,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),T=require("authhero"),C=require("@hono/zod-openapi"),I=require("@authhero/adapter-interfaces");var v=class extends Error{constructor(t=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});F(this,"res");F(this,"status");this.res=s==null?void 0:s.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 U(e){const{controlPlaneTenantId:t,requireOrganizationMatch:s=!0}=e;return{async onTenantAccessValidation(a,i){if(i===t)return!0;if(s){const n=a.var.org_name,u=a.var.organization_id,l=n||u;return l?l===i:!1}return!0}}}function E(e,t,s,a){if(t===s)return!0;const i=a||e;return i?i===t:!1}function K(e){return{async resolveDataAdapters(t){try{return await e.getAdapters(t)}catch(s){console.error(`Failed to resolve data adapters for tenant ${t}:`,s);return}}}}function G(e){return{async beforeCreate(t,s){return!s.audience&&s.id?{...s,audience:T.getTenantAudience(s.id)}:s},async afterCreate(t,s){const{accessControl:a,databaseIsolation:i,settingsInheritance:n}=e;a&&t.ctx&&await x(t,s,a),i!=null&&i.onProvision&&await i.onProvision(s.id),(n==null?void 0:n.inheritFromControlPlane)!==!1&&t.ctx&&await ae(t,s,e)},async beforeDelete(t,s){const{accessControl:a,databaseIsolation:i}=e;if(a)try{const u=(await t.adapters.organizations.list(a.controlPlaneTenantId)).organizations.find(l=>l.name===s);u&&await t.adapters.organizations.remove(a.controlPlaneTenantId,u.id)}catch(n){console.warn(`Failed to remove organization for tenant ${s}:`,n)}if(i!=null&&i.onDeprovision)try{await i.onDeprovision(s)}catch(n){console.warn(`Failed to deprovision database for tenant ${s}:`,n)}}}}async function x(e,t,s){const{controlPlaneTenantId:a,defaultPermissions:i,defaultRoles:n,issuer:u,adminRoleName:l="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s,f=await e.adapters.organizations.create(a,{name:t.id,display_name:t.friendly_name||t.id});let d;if(u&&(d=await te(e,a,l,c)),r&&e.ctx){const o=e.ctx.var.user;if(o!=null&&o.sub&&!await ee(e,a,o.sub))try{await e.adapters.userOrganizations.create(a,{user_id:o.sub,organization_id:f.id}),d&&await e.adapters.userRoles.create(a,o.sub,d,f.id)}catch(g){console.warn(`Failed to add creator ${o.sub} to organization ${f.id}:`,g)}}n&&n.length>0&&console.log(`Would assign roles ${n.join(", ")} to organization ${f.id}`),i&&i.length>0&&console.log(`Would grant permissions ${i.join(", ")} to organization ${f.id}`)}async function ee(e,t,s){const a=await e.adapters.userRoles.list(t,s,void 0,"");for(const i of a)if((await e.adapters.rolePermissions.list(t,i.id,{per_page:1e3})).some(l=>l.permission_name==="admin:organizations"))return!0;return!1}async function te(e,t,s,a){const n=(await e.adapters.roles.list(t,{})).roles.find(r=>r.name===s);if(n)return n.id;const u=await e.adapters.roles.create(t,{name:s,description:a}),l=T.MANAGEMENT_API_AUDIENCE,c=T.MANAGEMENT_API_SCOPES.map(r=>({role_id:u.id,resource_server_identifier:l,permission_name:r.value}));return await e.adapters.rolePermissions.assign(t,u.id,c),u.id}async function ae(e,t,s){const{accessControl:a,settingsInheritance:i}=s;if(!a)return;const n=await e.adapters.tenants.get(a.controlPlaneTenantId);if(!n)return;let u={...n};const l=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of l)delete u[c];if(i!=null&&i.inheritedKeys){const c={};for(const r of i.inheritedKeys)r in n&&!l.includes(r)&&(c[r]=n[r]);u=c}if(i!=null&&i.excludedKeys)for(const c of i.excludedKeys)delete u[c];i!=null&&i.transformSettings&&(u=i.transformSettings(u,t.id)),Object.keys(u).length>0&&await e.adapters.tenants.update(t.id,u)}function B(e){const{controlPlaneTenantId:t,getChildTenantIds:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;async function u(r,f,d){return(await r.resourceServers.list(f,{q:`identifier:${d}`,per_page:1})).resource_servers[0]??null}async function l(r,f){const d=await s();await Promise.all(d.map(async o=>{try{const p=await a(o),h={...n?n(r,o):{name:r.name,identifier:r.identifier,scopes:r.scopes,signing_alg:r.signing_alg,signing_secret:r.signing_secret,token_lifetime:r.token_lifetime,token_lifetime_for_web:r.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:r.skip_consent_for_verifiable_first_party_clients,allow_offline_access:r.allow_offline_access,verificationKey:r.verificationKey,options:r.options},is_system:!0};if(f==="create"){const A=await u(p,o,r.identifier);A&&A.id?await p.resourceServers.update(o,A.id,h):await p.resourceServers.create(o,h)}else{const A=await u(p,o,r.identifier);A&&A.id?await p.resourceServers.update(o,A.id,h):await p.resourceServers.create(o,h)}}catch(p){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${o}":`,p)}}))}async function c(r){const f=await s();await Promise.all(f.map(async d=>{try{const o=await a(d),p=await u(o,d,r);p&&p.id&&await o.resourceServers.remove(d,p.id)}catch(o){console.error(`Failed to delete resource server "${r}" from tenant "${d}":`,o)}}))}return{afterCreate:async(r,f)=>{r.tenantId===t&&i(f)&&await l(f,"create")},afterUpdate:async(r,f,d)=>{r.tenantId===t&&i(d)&&await l(d,"update")},afterDelete:async(r,f)=>{r.tenantId===t&&await c(f)}}}function W(e){const{controlPlaneTenantId:t,getControlPlaneAdapters:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;return{async afterCreate(u,l){if(l.id!==t)try{const c=await s(),r=await a(l.id),f=await T.fetchAll(d=>c.resourceServers.list(t,d),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(f.filter(d=>i(d)).map(async d=>{const o=d;try{const p=n?n(o,l.id):{name:o.name,identifier:o.identifier,scopes:o.scopes,signing_alg:o.signing_alg,signing_secret:o.signing_secret,token_lifetime:o.token_lifetime,token_lifetime_for_web:o.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:o.skip_consent_for_verifiable_first_party_clients,allow_offline_access:o.allow_offline_access,verificationKey:o.verificationKey,options:o.options};await r.resourceServers.create(l.id,{...p,is_system:!0})}catch(p){console.error(`Failed to sync resource server "${o.identifier}" to new tenant "${l.id}":`,p)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${l.id}":`,c)}}}}function L(e){const{controlPlaneTenantId:t,getChildTenantIds:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n}=e;async function u(c,r,f){return(await c.roles.list(r,{q:`name:${f}`,per_page:1})).roles[0]??null}async function l(c,r){const f=await s();await Promise.all(f.map(async d=>{try{const o=await a(d),g={...n?n(c,d):{name:c.name,description:c.description},is_system:!0};if(r==="create"){const h=await u(o,d,c.name);h&&h.id?await o.roles.update(d,h.id,g):await o.roles.create(d,g)}else{const h=await u(o,d,c.name);h&&h.id?await o.roles.update(d,h.id,g):await o.roles.create(d,g)}}catch(o){console.error(`Failed to sync role "${c.name}" to tenant "${d}":`,o)}}))}return{afterCreate:async(c,r)=>{c.tenantId===t&&i(r)&&await l(r,"create")},afterUpdate:async(c,r,f)=>{c.tenantId===t&&i(f)&&await l(f,"update")},afterDelete:async(c,r)=>{c.tenantId===t&&console.warn(`Role ${r} was deleted from control plane. Child tenant roles with matching names should be deleted manually or implement role name tracking.`)}}}function Q(e){const{controlPlaneTenantId:t,getControlPlaneAdapters:s,getAdapters:a,shouldSync:i=()=>!0,transformForSync:n,syncPermissions:u=!0}=e;return{async afterCreate(l,c){if(c.id!==t)try{const r=await s(),f=await a(c.id),d=await T.fetchAll(p=>r.roles.list(t,p),"roles",{cursorField:"id",pageSize:100}),o=new Map;if(await Promise.all(d.filter(p=>i(p)).map(async p=>{const g=p;try{const h=n?n(g,c.id):{name:g.name,description:g.description},A=await f.roles.create(c.id,{...h,is_system:!0});o.set(g.id,A.id)}catch(h){console.error(`Failed to sync role "${g.name}" to new tenant "${c.id}":`,h)}})),u)for(const[p,g]of o)try{const h=await r.rolePermissions.list(t,p,{});h.length>0&&await f.rolePermissions.assign(c.id,g,h.map(A=>({role_id:g,resource_server_identifier:A.resource_server_identifier,permission_name:A.permission_name})))}catch(h){console.error(`Failed to sync permissions for role to new tenant "${c.id}":`,h)}}catch(r){console.error(`Failed to sync roles to new tenant "${c.id}":`,r)}}}}function O(e,t){const s=new C.OpenAPIHono;return s.openapi(C.createRoute({tags:["tenants"],method:"get",path:"/",request:{query:I.auth0QuerySchema},security:[{Bearer:[]}],responses:{200:{content:{"application/json":{schema:C.z.object({tenants:C.z.array(I.tenantSchema),start:C.z.number().optional(),limit:C.z.number().optional(),length:C.z.number().optional()})}},description:"List of tenants"}}}),async a=>{var p,g,h,A;const i=a.req.valid("query"),{page:n,per_page:u,include_totals:l,q:c}=i,r=a.var.user,f=((r==null?void 0:r.scope)||"").split(" "),d=f.includes("auth:read");if(console.log("User scopes:",f,"hasAuthRead:",d),d){const S=await a.env.data.tenants.list({page:n,per_page:u,include_totals:l,q:c});return l?a.json({tenants:S.tenants,start:((p=S.totals)==null?void 0:p.start)??0,limit:((g=S.totals)==null?void 0:g.limit)??u,length:S.tenants.length}):a.json({tenants:S.tenants})}if(e.accessControl&&(r!=null&&r.sub)){const S=e.accessControl.controlPlaneTenantId,R=(await T.fetchAll(M=>a.env.data.userOrganizations.listUserOrganizations(S,r.sub,M),"organizations")).map(M=>M.name);if(R.length===0)return l?a.json({tenants:[],start:0,limit:u??50,length:0}):a.json({tenants:[]});const $=R.length,D=n??0,P=u??50,w=D*P,m=R.slice(w,w+P);if(m.length===0)return l?a.json({tenants:[],start:w,limit:P,length:$}):a.json({tenants:[]});const y=m.map(M=>`id:${M}`).join(" OR "),_=c?`(${y}) AND (${c})`:y,b=await a.env.data.tenants.list({q:_,per_page:P,include_totals:!1});return l?a.json({tenants:b.tenants,start:w,limit:P,length:$}):a.json({tenants:b.tenants})}const o=await a.env.data.tenants.list({page:n,per_page:u,include_totals:l,q:c});return l?a.json({tenants:o.tenants,start:((h=o.totals)==null?void 0:h.start)??0,limit:((A=o.totals)==null?void 0:A.limit)??u,length:o.tenants.length}):a.json({tenants:o.tenants})}),s.openapi(C.createRoute({tags:["tenants"],method:"post",path:"/",request:{body:{content:{"application/json":{schema:I.tenantInsertSchema}}}},security:[{Bearer:[]}],responses:{201:{content:{"application/json":{schema:I.tenantSchema}},description:"Tenant created"},400:{description:"Validation error"},409:{description:"Tenant with this ID already exists"}}}),async a=>{var c,r;const i=a.var.user;if(!(i!=null&&i.sub))throw new v(401,{message:"Authentication required to create tenants"});let n=a.req.valid("json");const u={adapters:a.env.data,ctx:a};(c=t.tenants)!=null&&c.beforeCreate&&(n=await t.tenants.beforeCreate(u,n));const l=await a.env.data.tenants.create(n);return(r=t.tenants)!=null&&r.afterCreate&&await t.tenants.afterCreate(u,l),a.json(l,201)}),s.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 a=>{var l,c;const{id:i}=a.req.valid("param");if(e.accessControl){const r=a.var.user,f=e.accessControl.controlPlaneTenantId;if(!(r!=null&&r.sub))throw new v(401,{message:"Authentication required"});if(i===f)throw new v(403,{message:"Cannot delete the control plane"});if(!(await T.fetchAll(p=>a.env.data.userOrganizations.listUserOrganizations(f,r.sub,p),"organizations")).some(p=>p.name===i))throw new v(403,{message:"Access denied to this tenant"})}if(!await a.env.data.tenants.get(i))throw new v(404,{message:"Tenant not found"});const u={adapters:a.env.data,ctx:a};return(l=t.tenants)!=null&&l.beforeDelete&&await t.tenants.beforeDelete(u,i),await a.env.data.tenants.remove(i),(c=t.tenants)!=null&&c.afterDelete&&await t.tenants.afterDelete(u,i),a.body(null,204)}),s}function ne(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:s,type:a}of t){const i=e.match(s);if(i&&i[1])return{type:a,id:i[1]}}return null}async function re(e,t,s){try{switch(s.type){case"resource_server":{const a=await e.resourceServers.get(t,s.id);return(a==null?void 0:a.is_system)===!0}case"role":{const a=await e.roles.get(t,s.id);return(a==null?void 0:a.is_system)===!0}case"connection":{const a=await e.connections.get(t,s.id);return(a==null?void 0:a.is_system)===!0}default:return!1}}catch{return!1}}function se(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function V(){return async(e,t)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return t();const s=ne(e.req.path);if(!s)return t();const a=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!a)return t();if(await re(e.env.data,a,s))throw new v(403,{message:`This ${se(s.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`});return t()}}function k(e){return async(t,s)=>{if(!e.accessControl)return s();const a=t.var.tenant_id,i=t.var.organization_id;if(!a)throw new v(400,{message:"Tenant ID not found in request"});if(!E(i,a,e.accessControl.controlPlaneTenantId))throw new v(403,{message:`Access denied to tenant ${a}`});return s()}}function J(e){return async(t,s)=>{if(!e.subdomainRouting)return s();const{baseDomain:a,reservedSubdomains:i=[],resolveSubdomain:n}=e.subdomainRouting,u=t.req.header("host")||"";let l=null;if(u.endsWith(a)){const r=u.slice(0,-(a.length+1));r&&!r.includes(".")&&(l=r)}if(l&&i.includes(l)&&(l=null),!l)return e.accessControl&&t.set("tenant_id",e.accessControl.controlPlaneTenantId),s();let c=null;if(n)c=await n(l);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const r=await t.env.data.organizations.get(e.accessControl.controlPlaneTenantId,l);r&&(c=r.id)}catch{}if(!c)throw new v(404,{message:`Tenant not found for subdomain: ${l}`});return t.set("tenant_id",c),s()}}function X(e){return async(t,s)=>{if(!e.databaseIsolation)return s();const a=t.var.tenant_id;if(!a)throw new v(400,{message:"Tenant ID not found in request"});try{const i=await e.databaseIsolation.getAdapters(a);t.env.data=i}catch(i){throw console.error(`Failed to resolve database for tenant ${a}:`,i),new v(500,{message:"Failed to resolve tenant database"})}return s()}}function j(e){const t=J(e),s=k(e),a=X(e);return async(i,n)=>(await t(i,async()=>{}),await s(i,async()=>{}),await a(i,async()=>{}),n())}function ie(e){const t=z(e);return{name:"multi-tenancy",middleware:j(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 z(e){const t=e.accessControl?U(e.accessControl):{},s=e.databaseIsolation?K(e.databaseIsolation):{},a=G(e);return{...t,...s,tenants:a}}function Y(e){const t=new N.Hono,s=z(e);return t.route("/tenants",O(e,s)),t}function oe(e){return{hooks:z(e),middleware:j(e),app:Y(e),config:e}}function ce(e){const{controlPlaneTenantId:t="control_plane",syncResourceServers:s=!0,syncRoles:a=!0,multiTenancy:i,entityHooks:n,...u}=e,l={...i,accessControl:{controlPlaneTenantId:t,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...i==null?void 0:i.accessControl}},c=z(l);let r,f;s&&(r=B({controlPlaneTenantId:t,getChildTenantIds:async()=>(await T.fetchAll(m=>e.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==t).map(m=>m.id),getAdapters:async w=>e.dataAdapter}),f=W({controlPlaneTenantId:t,getControlPlaneAdapters:async()=>e.dataAdapter,getAdapters:async w=>e.dataAdapter}));let d,o;a&&(d=L({controlPlaneTenantId:t,getChildTenantIds:async()=>(await T.fetchAll(m=>e.dataAdapter.tenants.list(m),"tenants",{cursorField:"id",pageSize:100})).filter(m=>m.id!==t).map(m=>m.id),getAdapters:async w=>e.dataAdapter}),o=Q({controlPlaneTenantId:t,getControlPlaneAdapters:async()=>e.dataAdapter,getAdapters:async w=>e.dataAdapter,syncPermissions:!0}));const p=async(w,m,...y)=>{const _=[];if(w)try{await w(...y)}catch(b){_.push(b instanceof Error?b:new Error(String(b)))}if(m)try{await m(...y)}catch(b){_.push(b instanceof Error?b:new Error(String(b)))}if(_.length===1)throw _[0];if(_.length>1)throw new AggregateError(_,`Multiple hook errors: ${_.map(b=>b.message).join("; ")}`)},g=async(w,...m)=>{const y=[];for(const _ of w)if(_)try{await _(...m)}catch(b){y.push(b instanceof Error?b:new Error(String(b)))}if(y.length===1)throw y[0];if(y.length>1)throw new AggregateError(y,`Multiple hook errors: ${y.map(_=>_.message).join("; ")}`)},h={...n,resourceServers:r?{...n==null?void 0:n.resourceServers,afterCreate:async(w,m)=>{var y;await p((y=n==null?void 0:n.resourceServers)==null?void 0:y.afterCreate,r==null?void 0:r.afterCreate,w,m)},afterUpdate:async(w,m,y)=>{var _;await p((_=n==null?void 0:n.resourceServers)==null?void 0:_.afterUpdate,r==null?void 0:r.afterUpdate,w,m,y)},afterDelete:async(w,m)=>{var y;await p((y=n==null?void 0:n.resourceServers)==null?void 0:y.afterDelete,r==null?void 0:r.afterDelete,w,m)}}:n==null?void 0:n.resourceServers,roles:d?{...n==null?void 0:n.roles,afterCreate:async(w,m)=>{var y;await p((y=n==null?void 0:n.roles)==null?void 0:y.afterCreate,d==null?void 0:d.afterCreate,w,m)},afterUpdate:async(w,m,y)=>{var _;await p((_=n==null?void 0:n.roles)==null?void 0:_.afterUpdate,d==null?void 0:d.afterUpdate,w,m,y)},afterDelete:async(w,m)=>{var y;await p((y=n==null?void 0:n.roles)==null?void 0:y.afterDelete,d==null?void 0:d.afterDelete,w,m)}}:n==null?void 0:n.roles,tenants:f||o?{...n==null?void 0:n.tenants,afterCreate:async(w,m)=>{var y;await g([(y=n==null?void 0:n.tenants)==null?void 0:y.afterCreate,f==null?void 0:f.afterCreate,o==null?void 0:o.afterCreate],w,m)}}:n==null?void 0:n.tenants},A={...c,tenants:f||o?{...c.tenants,afterCreate:async(w,m)=>{var y;(y=c.tenants)!=null&&y.afterCreate&&await c.tenants.afterCreate(w,m),await g([f==null?void 0:f.afterCreate,o==null?void 0:o.afterCreate],w,m)}}:c.tenants},S=O(l,A),q=T.init({...u,entityHooks:h,managementApiExtensions:[...u.managementApiExtensions||[],{path:"/tenants",router:S}]}),{app:R,managementApp:$,...D}=q,P=new N.Hono;return P.onError((w,m)=>w instanceof v?w.getResponse():(console.error(w),m.json({message:"Internal Server Error"},500))),P.use("/api/v2/*",V()),P.route("/",R),{app:P,managementApp:$,...D,multiTenancyConfig:l,multiTenancyHooks:c}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>T.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"fetchAll",{enumerable:!0,get:()=>T.fetchAll});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>T.seed});exports.createAccessControlHooks=U;exports.createAccessControlMiddleware=k;exports.createDatabaseHooks=K;exports.createDatabaseMiddleware=X;exports.createMultiTenancy=Y;exports.createMultiTenancyHooks=z;exports.createMultiTenancyMiddleware=j;exports.createMultiTenancyPlugin=ie;exports.createProtectSyncedMiddleware=V;exports.createProvisioningHooks=G;exports.createResourceServerSyncHooks=B;exports.createRoleSyncHooks=L;exports.createSubdomainMiddleware=J;exports.createTenantResourceServerSyncHooks=W;exports.createTenantRoleSyncHooks=Q;exports.createTenantsOpenAPIRouter=O;exports.init=ce;exports.setupMultiTenancy=oe;exports.validateTenantAccess=E;
|
package/dist/multi-tenancy.mjs
CHANGED
|
@@ -2,11 +2,11 @@ var K = Object.defineProperty;
|
|
|
2
2
|
var E = (e, t, s) => t in e ? K(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s;
|
|
3
3
|
var D = (e, t, s) => E(e, typeof t != "symbol" ? t + "" : t, s);
|
|
4
4
|
import { Hono as U } from "hono";
|
|
5
|
-
import { getTenantAudience as
|
|
6
|
-
import { MANAGEMENT_API_SCOPES as
|
|
5
|
+
import { getTenantAudience as G, MANAGEMENT_API_SCOPES as B, MANAGEMENT_API_AUDIENCE as W, fetchAll as S, init as L } from "authhero";
|
|
6
|
+
import { MANAGEMENT_API_SCOPES as Ce, fetchAll as Pe, seed as Se } from "authhero";
|
|
7
7
|
import { OpenAPIHono as Q, createRoute as M, z as P } from "@hono/zod-openapi";
|
|
8
|
-
import { auth0QuerySchema as V, tenantSchema as
|
|
9
|
-
var
|
|
8
|
+
import { auth0QuerySchema as V, tenantSchema as q, tenantInsertSchema as J } from "@authhero/adapter-interfaces";
|
|
9
|
+
var T = class extends Error {
|
|
10
10
|
/**
|
|
11
11
|
* Creates an instance of `HTTPException`.
|
|
12
12
|
* @param status - HTTP status code for the exception. Defaults to 500.
|
|
@@ -72,12 +72,12 @@ function k(e) {
|
|
|
72
72
|
async beforeCreate(t, s) {
|
|
73
73
|
return !s.audience && s.id ? {
|
|
74
74
|
...s,
|
|
75
|
-
audience:
|
|
75
|
+
audience: G(s.id)
|
|
76
76
|
} : s;
|
|
77
77
|
},
|
|
78
78
|
async afterCreate(t, s) {
|
|
79
79
|
const { accessControl: a, databaseIsolation: i, settingsInheritance: n } = e;
|
|
80
|
-
a && t.ctx && await H(t, s, a), i != null && i.onProvision && await i.onProvision(s.id), (n == null ? void 0 : n.inheritFromControlPlane) !== !1 && t.ctx && await
|
|
80
|
+
a && t.ctx && await H(t, s, a), i != null && i.onProvision && await i.onProvision(s.id), (n == null ? void 0 : n.inheritFromControlPlane) !== !1 && t.ctx && await te(t, s, e);
|
|
81
81
|
},
|
|
82
82
|
async beforeDelete(t, s) {
|
|
83
83
|
const { accessControl: a, databaseIsolation: i } = e;
|
|
@@ -125,14 +125,18 @@ async function H(e, t, s) {
|
|
|
125
125
|
}
|
|
126
126
|
);
|
|
127
127
|
let d;
|
|
128
|
-
if (u && (d = await
|
|
128
|
+
if (u && (d = await ee(
|
|
129
129
|
e,
|
|
130
130
|
a,
|
|
131
131
|
l,
|
|
132
132
|
c
|
|
133
133
|
)), r && e.ctx) {
|
|
134
134
|
const o = e.ctx.var.user;
|
|
135
|
-
if (o != null && o.sub
|
|
135
|
+
if (o != null && o.sub && !await x(
|
|
136
|
+
e,
|
|
137
|
+
a,
|
|
138
|
+
o.sub
|
|
139
|
+
))
|
|
136
140
|
try {
|
|
137
141
|
await e.adapters.userOrganizations.create(a, {
|
|
138
142
|
user_id: o.sub,
|
|
@@ -144,10 +148,10 @@ async function H(e, t, s) {
|
|
|
144
148
|
f.id
|
|
145
149
|
// organizationId
|
|
146
150
|
);
|
|
147
|
-
} catch (
|
|
151
|
+
} catch (g) {
|
|
148
152
|
console.warn(
|
|
149
153
|
`Failed to add creator ${o.sub} to organization ${f.id}:`,
|
|
150
|
-
|
|
154
|
+
g
|
|
151
155
|
);
|
|
152
156
|
}
|
|
153
157
|
}
|
|
@@ -157,14 +161,33 @@ async function H(e, t, s) {
|
|
|
157
161
|
`Would grant permissions ${i.join(", ")} to organization ${f.id}`
|
|
158
162
|
);
|
|
159
163
|
}
|
|
160
|
-
async function x(e, t, s
|
|
164
|
+
async function x(e, t, s) {
|
|
165
|
+
const a = await e.adapters.userRoles.list(
|
|
166
|
+
t,
|
|
167
|
+
s,
|
|
168
|
+
void 0,
|
|
169
|
+
""
|
|
170
|
+
// Empty string for global roles
|
|
171
|
+
);
|
|
172
|
+
for (const i of a)
|
|
173
|
+
if ((await e.adapters.rolePermissions.list(
|
|
174
|
+
t,
|
|
175
|
+
i.id,
|
|
176
|
+
{ per_page: 1e3 }
|
|
177
|
+
)).some(
|
|
178
|
+
(l) => l.permission_name === "admin:organizations"
|
|
179
|
+
))
|
|
180
|
+
return !0;
|
|
181
|
+
return !1;
|
|
182
|
+
}
|
|
183
|
+
async function ee(e, t, s, a) {
|
|
161
184
|
const n = (await e.adapters.roles.list(t, {})).roles.find((r) => r.name === s);
|
|
162
185
|
if (n)
|
|
163
186
|
return n.id;
|
|
164
187
|
const u = await e.adapters.roles.create(t, {
|
|
165
188
|
name: s,
|
|
166
189
|
description: a
|
|
167
|
-
}), l =
|
|
190
|
+
}), l = W, c = B.map((r) => ({
|
|
168
191
|
role_id: u.id,
|
|
169
192
|
resource_server_identifier: l,
|
|
170
193
|
permission_name: r.value
|
|
@@ -175,7 +198,7 @@ async function x(e, t, s, a) {
|
|
|
175
198
|
c
|
|
176
199
|
), u.id;
|
|
177
200
|
}
|
|
178
|
-
async function
|
|
201
|
+
async function te(e, t, s) {
|
|
179
202
|
const { accessControl: a, settingsInheritance: i } = s;
|
|
180
203
|
if (!a)
|
|
181
204
|
return;
|
|
@@ -211,7 +234,7 @@ async function ee(e, t, s) {
|
|
|
211
234
|
t.id
|
|
212
235
|
)), Object.keys(u).length > 0 && await e.adapters.tenants.update(t.id, u);
|
|
213
236
|
}
|
|
214
|
-
function
|
|
237
|
+
function ae(e) {
|
|
215
238
|
const {
|
|
216
239
|
controlPlaneTenantId: t,
|
|
217
240
|
getChildTenantIds: s,
|
|
@@ -244,25 +267,25 @@ function te(e) {
|
|
|
244
267
|
options: r.options
|
|
245
268
|
}, is_system: !0 };
|
|
246
269
|
if (f === "create") {
|
|
247
|
-
const
|
|
270
|
+
const A = await u(
|
|
248
271
|
p,
|
|
249
272
|
o,
|
|
250
273
|
r.identifier
|
|
251
274
|
);
|
|
252
|
-
|
|
275
|
+
A && A.id ? await p.resourceServers.update(
|
|
253
276
|
o,
|
|
254
|
-
|
|
277
|
+
A.id,
|
|
255
278
|
h
|
|
256
279
|
) : await p.resourceServers.create(o, h);
|
|
257
280
|
} else {
|
|
258
|
-
const
|
|
281
|
+
const A = await u(
|
|
259
282
|
p,
|
|
260
283
|
o,
|
|
261
284
|
r.identifier
|
|
262
285
|
);
|
|
263
|
-
|
|
286
|
+
A && A.id ? await p.resourceServers.update(
|
|
264
287
|
o,
|
|
265
|
-
|
|
288
|
+
A.id,
|
|
266
289
|
h
|
|
267
290
|
) : await p.resourceServers.create(o, h);
|
|
268
291
|
}
|
|
@@ -307,7 +330,7 @@ function te(e) {
|
|
|
307
330
|
}
|
|
308
331
|
};
|
|
309
332
|
}
|
|
310
|
-
function
|
|
333
|
+
function ne(e) {
|
|
311
334
|
const {
|
|
312
335
|
controlPlaneTenantId: t,
|
|
313
336
|
getControlPlaneAdapters: s,
|
|
@@ -365,7 +388,7 @@ function ae(e) {
|
|
|
365
388
|
}
|
|
366
389
|
};
|
|
367
390
|
}
|
|
368
|
-
function
|
|
391
|
+
function re(e) {
|
|
369
392
|
const {
|
|
370
393
|
controlPlaneTenantId: t,
|
|
371
394
|
getChildTenantIds: s,
|
|
@@ -426,7 +449,7 @@ function ne(e) {
|
|
|
426
449
|
}
|
|
427
450
|
};
|
|
428
451
|
}
|
|
429
|
-
function
|
|
452
|
+
function se(e) {
|
|
430
453
|
const {
|
|
431
454
|
controlPlaneTenantId: t,
|
|
432
455
|
getControlPlaneAdapters: s,
|
|
@@ -451,11 +474,11 @@ function re(e) {
|
|
|
451
474
|
const h = n ? n(g, c.id) : {
|
|
452
475
|
name: g.name,
|
|
453
476
|
description: g.description
|
|
454
|
-
},
|
|
477
|
+
}, A = await f.roles.create(c.id, {
|
|
455
478
|
...h,
|
|
456
479
|
is_system: !0
|
|
457
480
|
});
|
|
458
|
-
o.set(g.id,
|
|
481
|
+
o.set(g.id, A.id);
|
|
459
482
|
} catch (h) {
|
|
460
483
|
console.error(
|
|
461
484
|
`Failed to sync role "${g.name}" to new tenant "${c.id}":`,
|
|
@@ -474,10 +497,10 @@ function re(e) {
|
|
|
474
497
|
h.length > 0 && await f.rolePermissions.assign(
|
|
475
498
|
c.id,
|
|
476
499
|
g,
|
|
477
|
-
h.map((
|
|
500
|
+
h.map((A) => ({
|
|
478
501
|
role_id: g,
|
|
479
|
-
resource_server_identifier:
|
|
480
|
-
permission_name:
|
|
502
|
+
resource_server_identifier: A.resource_server_identifier,
|
|
503
|
+
permission_name: A.permission_name
|
|
481
504
|
}))
|
|
482
505
|
);
|
|
483
506
|
} catch (h) {
|
|
@@ -495,7 +518,7 @@ function re(e) {
|
|
|
495
518
|
}
|
|
496
519
|
};
|
|
497
520
|
}
|
|
498
|
-
function
|
|
521
|
+
function O(e, t) {
|
|
499
522
|
const s = new Q();
|
|
500
523
|
return s.openapi(
|
|
501
524
|
M({
|
|
@@ -515,7 +538,7 @@ function j(e, t) {
|
|
|
515
538
|
content: {
|
|
516
539
|
"application/json": {
|
|
517
540
|
schema: P.object({
|
|
518
|
-
tenants: P.array(
|
|
541
|
+
tenants: P.array(q),
|
|
519
542
|
start: P.number().optional(),
|
|
520
543
|
limit: P.number().optional(),
|
|
521
544
|
length: P.number().optional()
|
|
@@ -527,26 +550,26 @@ function j(e, t) {
|
|
|
527
550
|
}
|
|
528
551
|
}),
|
|
529
552
|
async (a) => {
|
|
530
|
-
var p, g, h,
|
|
553
|
+
var p, g, h, A;
|
|
531
554
|
const i = a.req.valid("query"), { page: n, per_page: u, include_totals: l, q: c } = i, r = a.var.user, f = ((r == null ? void 0 : r.scope) || "").split(" "), d = f.includes("auth:read");
|
|
532
555
|
if (console.log("User scopes:", f, "hasAuthRead:", d), d) {
|
|
533
|
-
const
|
|
556
|
+
const C = await a.env.data.tenants.list({
|
|
534
557
|
page: n,
|
|
535
558
|
per_page: u,
|
|
536
559
|
include_totals: l,
|
|
537
560
|
q: c
|
|
538
561
|
});
|
|
539
562
|
return l ? a.json({
|
|
540
|
-
tenants:
|
|
541
|
-
start: ((p =
|
|
542
|
-
limit: ((g =
|
|
543
|
-
length:
|
|
544
|
-
}) : a.json({ tenants:
|
|
563
|
+
tenants: C.tenants,
|
|
564
|
+
start: ((p = C.totals) == null ? void 0 : p.start) ?? 0,
|
|
565
|
+
limit: ((g = C.totals) == null ? void 0 : g.limit) ?? u,
|
|
566
|
+
length: C.tenants.length
|
|
567
|
+
}) : a.json({ tenants: C.tenants });
|
|
545
568
|
}
|
|
546
569
|
if (e.accessControl && (r != null && r.sub)) {
|
|
547
|
-
const
|
|
570
|
+
const C = e.accessControl.controlPlaneTenantId, $ = (await S(
|
|
548
571
|
(R) => a.env.data.userOrganizations.listUserOrganizations(
|
|
549
|
-
|
|
572
|
+
C,
|
|
550
573
|
r.sub,
|
|
551
574
|
R
|
|
552
575
|
),
|
|
@@ -567,18 +590,18 @@ function j(e, t) {
|
|
|
567
590
|
limit: v,
|
|
568
591
|
length: z
|
|
569
592
|
}) : a.json({ tenants: [] });
|
|
570
|
-
const y = m.map((R) => `id:${R}`).join(" OR "), _ = c ? `(${y}) AND (${c})` : y,
|
|
593
|
+
const y = m.map((R) => `id:${R}`).join(" OR "), _ = c ? `(${y}) AND (${c})` : y, b = await a.env.data.tenants.list({
|
|
571
594
|
q: _,
|
|
572
595
|
per_page: v,
|
|
573
596
|
include_totals: !1
|
|
574
597
|
// We calculate totals from accessibleTenantIds
|
|
575
598
|
});
|
|
576
599
|
return l ? a.json({
|
|
577
|
-
tenants:
|
|
600
|
+
tenants: b.tenants,
|
|
578
601
|
start: w,
|
|
579
602
|
limit: v,
|
|
580
603
|
length: z
|
|
581
|
-
}) : a.json({ tenants:
|
|
604
|
+
}) : a.json({ tenants: b.tenants });
|
|
582
605
|
}
|
|
583
606
|
const o = await a.env.data.tenants.list({
|
|
584
607
|
page: n,
|
|
@@ -589,7 +612,7 @@ function j(e, t) {
|
|
|
589
612
|
return l ? a.json({
|
|
590
613
|
tenants: o.tenants,
|
|
591
614
|
start: ((h = o.totals) == null ? void 0 : h.start) ?? 0,
|
|
592
|
-
limit: ((
|
|
615
|
+
limit: ((A = o.totals) == null ? void 0 : A.limit) ?? u,
|
|
593
616
|
length: o.tenants.length
|
|
594
617
|
}) : a.json({ tenants: o.tenants });
|
|
595
618
|
}
|
|
@@ -616,7 +639,7 @@ function j(e, t) {
|
|
|
616
639
|
201: {
|
|
617
640
|
content: {
|
|
618
641
|
"application/json": {
|
|
619
|
-
schema:
|
|
642
|
+
schema: q
|
|
620
643
|
}
|
|
621
644
|
},
|
|
622
645
|
description: "Tenant created"
|
|
@@ -633,7 +656,7 @@ function j(e, t) {
|
|
|
633
656
|
var c, r;
|
|
634
657
|
const i = a.var.user;
|
|
635
658
|
if (!(i != null && i.sub))
|
|
636
|
-
throw new
|
|
659
|
+
throw new T(401, {
|
|
637
660
|
message: "Authentication required to create tenants"
|
|
638
661
|
});
|
|
639
662
|
let n = a.req.valid("json");
|
|
@@ -678,11 +701,11 @@ function j(e, t) {
|
|
|
678
701
|
if (e.accessControl) {
|
|
679
702
|
const r = a.var.user, f = e.accessControl.controlPlaneTenantId;
|
|
680
703
|
if (!(r != null && r.sub))
|
|
681
|
-
throw new
|
|
704
|
+
throw new T(401, {
|
|
682
705
|
message: "Authentication required"
|
|
683
706
|
});
|
|
684
707
|
if (i === f)
|
|
685
|
-
throw new
|
|
708
|
+
throw new T(403, {
|
|
686
709
|
message: "Cannot delete the control plane"
|
|
687
710
|
});
|
|
688
711
|
if (!(await S(
|
|
@@ -693,12 +716,12 @@ function j(e, t) {
|
|
|
693
716
|
),
|
|
694
717
|
"organizations"
|
|
695
718
|
)).some((p) => p.name === i))
|
|
696
|
-
throw new
|
|
719
|
+
throw new T(403, {
|
|
697
720
|
message: "Access denied to this tenant"
|
|
698
721
|
});
|
|
699
722
|
}
|
|
700
723
|
if (!await a.env.data.tenants.get(i))
|
|
701
|
-
throw new
|
|
724
|
+
throw new T(404, {
|
|
702
725
|
message: "Tenant not found"
|
|
703
726
|
});
|
|
704
727
|
const u = {
|
|
@@ -709,7 +732,7 @@ function j(e, t) {
|
|
|
709
732
|
}
|
|
710
733
|
), s;
|
|
711
734
|
}
|
|
712
|
-
function
|
|
735
|
+
function ie(e) {
|
|
713
736
|
const t = [
|
|
714
737
|
{
|
|
715
738
|
pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
|
|
@@ -725,7 +748,7 @@ function se(e) {
|
|
|
725
748
|
}
|
|
726
749
|
return null;
|
|
727
750
|
}
|
|
728
|
-
async function
|
|
751
|
+
async function oe(e, t, s) {
|
|
729
752
|
try {
|
|
730
753
|
switch (s.type) {
|
|
731
754
|
case "resource_server": {
|
|
@@ -747,37 +770,37 @@ async function ie(e, t, s) {
|
|
|
747
770
|
return !1;
|
|
748
771
|
}
|
|
749
772
|
}
|
|
750
|
-
function
|
|
773
|
+
function ce(e) {
|
|
751
774
|
return {
|
|
752
775
|
resource_server: "resource server",
|
|
753
776
|
role: "role",
|
|
754
777
|
connection: "connection"
|
|
755
778
|
}[e];
|
|
756
779
|
}
|
|
757
|
-
function
|
|
780
|
+
function le() {
|
|
758
781
|
return async (e, t) => {
|
|
759
782
|
if (!["PATCH", "PUT", "DELETE"].includes(e.req.method))
|
|
760
783
|
return t();
|
|
761
|
-
const s =
|
|
784
|
+
const s = ie(e.req.path);
|
|
762
785
|
if (!s)
|
|
763
786
|
return t();
|
|
764
787
|
const a = e.var.tenant_id || e.req.header("x-tenant-id") || e.req.header("tenant-id");
|
|
765
788
|
if (!a)
|
|
766
789
|
return t();
|
|
767
|
-
if (await
|
|
768
|
-
throw new
|
|
769
|
-
message: `This ${
|
|
790
|
+
if (await oe(e.env.data, a, s))
|
|
791
|
+
throw new T(403, {
|
|
792
|
+
message: `This ${ce(s.type)} is a system resource and cannot be modified. Make changes in the control plane instead.`
|
|
770
793
|
});
|
|
771
794
|
return t();
|
|
772
795
|
};
|
|
773
796
|
}
|
|
774
|
-
function
|
|
797
|
+
function de(e) {
|
|
775
798
|
return async (t, s) => {
|
|
776
799
|
if (!e.accessControl)
|
|
777
800
|
return s();
|
|
778
801
|
const a = t.var.tenant_id, i = t.var.organization_id;
|
|
779
802
|
if (!a)
|
|
780
|
-
throw new
|
|
803
|
+
throw new T(400, {
|
|
781
804
|
message: "Tenant ID not found in request"
|
|
782
805
|
});
|
|
783
806
|
if (!Y(
|
|
@@ -785,13 +808,13 @@ function le(e) {
|
|
|
785
808
|
a,
|
|
786
809
|
e.accessControl.controlPlaneTenantId
|
|
787
810
|
))
|
|
788
|
-
throw new
|
|
811
|
+
throw new T(403, {
|
|
789
812
|
message: `Access denied to tenant ${a}`
|
|
790
813
|
});
|
|
791
814
|
return s();
|
|
792
815
|
};
|
|
793
816
|
}
|
|
794
|
-
function
|
|
817
|
+
function ue(e) {
|
|
795
818
|
return async (t, s) => {
|
|
796
819
|
if (!e.subdomainRouting)
|
|
797
820
|
return s();
|
|
@@ -820,19 +843,19 @@ function de(e) {
|
|
|
820
843
|
} catch {
|
|
821
844
|
}
|
|
822
845
|
if (!c)
|
|
823
|
-
throw new
|
|
846
|
+
throw new T(404, {
|
|
824
847
|
message: `Tenant not found for subdomain: ${l}`
|
|
825
848
|
});
|
|
826
849
|
return t.set("tenant_id", c), s();
|
|
827
850
|
};
|
|
828
851
|
}
|
|
829
|
-
function
|
|
852
|
+
function fe(e) {
|
|
830
853
|
return async (t, s) => {
|
|
831
854
|
if (!e.databaseIsolation)
|
|
832
855
|
return s();
|
|
833
856
|
const a = t.var.tenant_id;
|
|
834
857
|
if (!a)
|
|
835
|
-
throw new
|
|
858
|
+
throw new T(400, {
|
|
836
859
|
message: "Tenant ID not found in request"
|
|
837
860
|
});
|
|
838
861
|
try {
|
|
@@ -842,7 +865,7 @@ function ue(e) {
|
|
|
842
865
|
throw console.error(
|
|
843
866
|
`Failed to resolve database for tenant ${a}:`,
|
|
844
867
|
i
|
|
845
|
-
), new
|
|
868
|
+
), new T(500, {
|
|
846
869
|
message: "Failed to resolve tenant database"
|
|
847
870
|
});
|
|
848
871
|
}
|
|
@@ -850,13 +873,13 @@ function ue(e) {
|
|
|
850
873
|
};
|
|
851
874
|
}
|
|
852
875
|
function N(e) {
|
|
853
|
-
const t =
|
|
876
|
+
const t = ue(e), s = de(e), a = fe(e);
|
|
854
877
|
return async (i, n) => (await t(i, async () => {
|
|
855
878
|
}), await s(i, async () => {
|
|
856
879
|
}), await a(i, async () => {
|
|
857
880
|
}), n());
|
|
858
881
|
}
|
|
859
|
-
function
|
|
882
|
+
function _e(e) {
|
|
860
883
|
const t = I(e);
|
|
861
884
|
return {
|
|
862
885
|
name: "multi-tenancy",
|
|
@@ -868,7 +891,7 @@ function ge(e) {
|
|
|
868
891
|
routes: [
|
|
869
892
|
{
|
|
870
893
|
path: "/management",
|
|
871
|
-
handler:
|
|
894
|
+
handler: O(e, t)
|
|
872
895
|
}
|
|
873
896
|
],
|
|
874
897
|
// Called when plugin is registered
|
|
@@ -889,19 +912,19 @@ function I(e) {
|
|
|
889
912
|
tenants: a
|
|
890
913
|
};
|
|
891
914
|
}
|
|
892
|
-
function
|
|
915
|
+
function pe(e) {
|
|
893
916
|
const t = new U(), s = I(e);
|
|
894
|
-
return t.route("/tenants",
|
|
917
|
+
return t.route("/tenants", O(e, s)), t;
|
|
895
918
|
}
|
|
896
|
-
function
|
|
919
|
+
function Ae(e) {
|
|
897
920
|
return {
|
|
898
921
|
hooks: I(e),
|
|
899
922
|
middleware: N(e),
|
|
900
|
-
app:
|
|
923
|
+
app: pe(e),
|
|
901
924
|
config: e
|
|
902
925
|
};
|
|
903
926
|
}
|
|
904
|
-
function
|
|
927
|
+
function be(e) {
|
|
905
928
|
const {
|
|
906
929
|
controlPlaneTenantId: t = "control_plane",
|
|
907
930
|
syncResourceServers: s = !0,
|
|
@@ -919,7 +942,7 @@ function Te(e) {
|
|
|
919
942
|
}
|
|
920
943
|
}, c = I(l);
|
|
921
944
|
let r, f;
|
|
922
|
-
s && (r =
|
|
945
|
+
s && (r = ae({
|
|
923
946
|
controlPlaneTenantId: t,
|
|
924
947
|
getChildTenantIds: async () => (await S(
|
|
925
948
|
(m) => e.dataAdapter.tenants.list(m),
|
|
@@ -927,13 +950,13 @@ function Te(e) {
|
|
|
927
950
|
{ cursorField: "id", pageSize: 100 }
|
|
928
951
|
)).filter((m) => m.id !== t).map((m) => m.id),
|
|
929
952
|
getAdapters: async (w) => e.dataAdapter
|
|
930
|
-
}), f =
|
|
953
|
+
}), f = ne({
|
|
931
954
|
controlPlaneTenantId: t,
|
|
932
955
|
getControlPlaneAdapters: async () => e.dataAdapter,
|
|
933
956
|
getAdapters: async (w) => e.dataAdapter
|
|
934
957
|
}));
|
|
935
958
|
let d, o;
|
|
936
|
-
a && (d =
|
|
959
|
+
a && (d = re({
|
|
937
960
|
controlPlaneTenantId: t,
|
|
938
961
|
getChildTenantIds: async () => (await S(
|
|
939
962
|
(m) => e.dataAdapter.tenants.list(m),
|
|
@@ -941,7 +964,7 @@ function Te(e) {
|
|
|
941
964
|
{ cursorField: "id", pageSize: 100 }
|
|
942
965
|
)).filter((m) => m.id !== t).map((m) => m.id),
|
|
943
966
|
getAdapters: async (w) => e.dataAdapter
|
|
944
|
-
}), o =
|
|
967
|
+
}), o = se({
|
|
945
968
|
controlPlaneTenantId: t,
|
|
946
969
|
getControlPlaneAdapters: async () => e.dataAdapter,
|
|
947
970
|
getAdapters: async (w) => e.dataAdapter,
|
|
@@ -952,21 +975,21 @@ function Te(e) {
|
|
|
952
975
|
if (w)
|
|
953
976
|
try {
|
|
954
977
|
await w(...y);
|
|
955
|
-
} catch (
|
|
956
|
-
_.push(
|
|
978
|
+
} catch (b) {
|
|
979
|
+
_.push(b instanceof Error ? b : new Error(String(b)));
|
|
957
980
|
}
|
|
958
981
|
if (m)
|
|
959
982
|
try {
|
|
960
983
|
await m(...y);
|
|
961
|
-
} catch (
|
|
962
|
-
_.push(
|
|
984
|
+
} catch (b) {
|
|
985
|
+
_.push(b instanceof Error ? b : new Error(String(b)));
|
|
963
986
|
}
|
|
964
987
|
if (_.length === 1)
|
|
965
988
|
throw _[0];
|
|
966
989
|
if (_.length > 1)
|
|
967
990
|
throw new AggregateError(
|
|
968
991
|
_,
|
|
969
|
-
`Multiple hook errors: ${_.map((
|
|
992
|
+
`Multiple hook errors: ${_.map((b) => b.message).join("; ")}`
|
|
970
993
|
);
|
|
971
994
|
}, g = async (w, ...m) => {
|
|
972
995
|
const y = [];
|
|
@@ -974,9 +997,9 @@ function Te(e) {
|
|
|
974
997
|
if (_)
|
|
975
998
|
try {
|
|
976
999
|
await _(...m);
|
|
977
|
-
} catch (
|
|
1000
|
+
} catch (b) {
|
|
978
1001
|
y.push(
|
|
979
|
-
|
|
1002
|
+
b instanceof Error ? b : new Error(String(b))
|
|
980
1003
|
);
|
|
981
1004
|
}
|
|
982
1005
|
if (y.length === 1)
|
|
@@ -1065,7 +1088,7 @@ function Te(e) {
|
|
|
1065
1088
|
);
|
|
1066
1089
|
}
|
|
1067
1090
|
} : n == null ? void 0 : n.tenants
|
|
1068
|
-
},
|
|
1091
|
+
}, A = {
|
|
1069
1092
|
...c,
|
|
1070
1093
|
tenants: f || o ? {
|
|
1071
1094
|
...c.tenants,
|
|
@@ -1081,20 +1104,20 @@ function Te(e) {
|
|
|
1081
1104
|
);
|
|
1082
1105
|
}
|
|
1083
1106
|
} : c.tenants
|
|
1084
|
-
},
|
|
1107
|
+
}, C = O(
|
|
1085
1108
|
l,
|
|
1086
|
-
|
|
1087
|
-
),
|
|
1109
|
+
A
|
|
1110
|
+
), j = L({
|
|
1088
1111
|
...u,
|
|
1089
1112
|
entityHooks: h,
|
|
1090
1113
|
// Register tenant routes via the extension mechanism
|
|
1091
1114
|
// This ensures they go through the full middleware chain (caching, tenant, auth, entity hooks)
|
|
1092
1115
|
managementApiExtensions: [
|
|
1093
1116
|
...u.managementApiExtensions || [],
|
|
1094
|
-
{ path: "/tenants", router:
|
|
1117
|
+
{ path: "/tenants", router: C }
|
|
1095
1118
|
]
|
|
1096
|
-
}), { app: $, managementApp: z, ...F } =
|
|
1097
|
-
return v.onError((w, m) => w instanceof
|
|
1119
|
+
}), { app: $, managementApp: z, ...F } = j, v = new U();
|
|
1120
|
+
return v.onError((w, m) => w instanceof T ? w.getResponse() : (console.error(w), m.json({ message: "Internal Server Error" }, 500))), v.use("/api/v2/*", le()), v.route("/", $), {
|
|
1098
1121
|
app: v,
|
|
1099
1122
|
managementApp: z,
|
|
1100
1123
|
...F,
|
|
@@ -1103,26 +1126,26 @@ function Te(e) {
|
|
|
1103
1126
|
};
|
|
1104
1127
|
}
|
|
1105
1128
|
export {
|
|
1106
|
-
|
|
1129
|
+
Ce as MANAGEMENT_API_SCOPES,
|
|
1107
1130
|
X as createAccessControlHooks,
|
|
1108
|
-
|
|
1131
|
+
de as createAccessControlMiddleware,
|
|
1109
1132
|
Z as createDatabaseHooks,
|
|
1110
|
-
|
|
1111
|
-
|
|
1133
|
+
fe as createDatabaseMiddleware,
|
|
1134
|
+
pe as createMultiTenancy,
|
|
1112
1135
|
I as createMultiTenancyHooks,
|
|
1113
1136
|
N as createMultiTenancyMiddleware,
|
|
1114
|
-
|
|
1115
|
-
|
|
1137
|
+
_e as createMultiTenancyPlugin,
|
|
1138
|
+
le as createProtectSyncedMiddleware,
|
|
1116
1139
|
k as createProvisioningHooks,
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1140
|
+
ae as createResourceServerSyncHooks,
|
|
1141
|
+
re as createRoleSyncHooks,
|
|
1142
|
+
ue as createSubdomainMiddleware,
|
|
1143
|
+
ne as createTenantResourceServerSyncHooks,
|
|
1144
|
+
se as createTenantRoleSyncHooks,
|
|
1145
|
+
O as createTenantsOpenAPIRouter,
|
|
1146
|
+
Pe as fetchAll,
|
|
1147
|
+
be as init,
|
|
1148
|
+
Se as seed,
|
|
1149
|
+
Ae as setupMultiTenancy,
|
|
1127
1150
|
Y as validateTenantAccess
|
|
1128
1151
|
};
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "git",
|
|
12
12
|
"url": "https://github.com/markusahlstrand/authhero"
|
|
13
13
|
},
|
|
14
|
-
"version": "13.
|
|
14
|
+
"version": "13.12.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"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"zod": "^3.24.0",
|
|
44
44
|
"@authhero/adapter-interfaces": "0.114.0",
|
|
45
|
-
"authhero": "1.
|
|
45
|
+
"authhero": "1.2.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@hono/zod-openapi": "^0.19.10",
|