@authhero/multi-tenancy 13.5.0 → 13.6.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.d.ts +1 -0
- package/dist/multi-tenancy.mjs +425 -381
- package/package.json +3 -3
package/dist/multi-tenancy.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var W=Object.defineProperty;var G=(t,e,s)=>e in t?W(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s;var M=(t,e,s)=>G(t,typeof e!="symbol"?e+"":e,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("hono"),C=require("authhero"),Q=require("zod"),$=require("@authhero/adapter-interfaces");function q(t){const{mainTenantId:e,requireOrganizationMatch:s=!0}=t;return{async onTenantAccessValidation(n,a){if(a===e)return!0;if(s){const c=n.var.organization_id;return c?c===a:!1}return!0}}}function D(t,e,s){return e===s?!0:t?t===e:!1}function z(t){return{async resolveDataAdapters(e){try{return await t.getAdapters(e)}catch(s){console.error(`Failed to resolve data adapters for tenant ${e}:`,s);return}}}}function F(t){return{async afterCreate(e,s){const{accessControl:n,databaseIsolation:a,settingsInheritance:c}=t;n&&e.ctx&&await L(e,s,n),a!=null&&a.onProvision&&await a.onProvision(s.id),(c==null?void 0:c.inheritFromMain)!==!1&&e.ctx&&await B(e,s,t)},async beforeDelete(e,s){const{accessControl:n,databaseIsolation:a}=t;if(n)try{const d=(await e.adapters.organizations.list(n.mainTenantId)).organizations.find(o=>o.name===s);d&&await e.adapters.organizations.remove(n.mainTenantId,d.id)}catch(c){console.warn(`Failed to remove organization for tenant ${s}:`,c)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(s)}catch(c){console.warn(`Failed to deprovision database for tenant ${s}:`,c)}}}}async function L(t,e,s){const{mainTenantId:n,defaultPermissions:a,defaultRoles:c,issuer:d,adminRoleName:o="Tenant Admin",adminRoleDescription:i="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s;await t.adapters.organizations.create(n,{id:e.id,name:e.id,display_name:e.friendly_name||e.id});let l=null;if(d&&(l=await Y(t,n,d,o,i)),r&&t.ctx){const f=t.ctx.var.user;if(f!=null&&f.sub)try{await t.adapters.userOrganizations.create(n,{user_id:f.sub,organization_id:e.id}),l&&await t.adapters.userRoles.create(n,f.sub,l,e.id)}catch(u){console.warn(`Failed to add creator ${f.sub} to organization ${e.id}:`,u)}}c&&c.length>0&&console.log(`Would assign roles ${c.join(", ")} to organization ${e.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${e.id}`)}async function Y(t,e,s,n,a){const d=(await t.adapters.roles.list(e,{})).roles.find(l=>l.name===n);if(d)return d.id;const o=await t.adapters.roles.create(e,{name:n,description:a}),i=`${s}api/v2/`,r=C.MANAGEMENT_API_SCOPES.map(l=>({role_id:o.id,resource_server_identifier:i,permission_name:l.value}));return await t.adapters.rolePermissions.assign(e,o.id,r),o.id}async function B(t,e,s){const{accessControl:n,settingsInheritance:a}=s;if(!n)return;const c=await t.adapters.tenants.get(n.mainTenantId);if(!c)return;let d={...c};const o=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const i of o)delete d[i];if(a!=null&&a.inheritedKeys){const i={};for(const r of a.inheritedKeys)r in c&&!o.includes(r)&&(i[r]=c[r]);d=i}if(a!=null&&a.excludedKeys)for(const i of a.excludedKeys)delete d[i];a!=null&&a.transformSettings&&(d=a.transformSettings(d,e.id)),Object.keys(d).length>0&&await t.adapters.tenants.update(e.id,d)}async function P(t,e,s={}){const{cursorField:n="id",sortOrder:a="asc",pageSize:c=100,maxItems:d=1e4,q:o}=s,i=[];let r,l=!0;for(;l;){let f=o||"";if(r){const w=`${n}:${a==="asc"?">":"<"}${r}`;f=f?`(${f}) AND ${w}`:w}const u={per_page:c,page:0,sort:{sort_by:n,sort_order:a},...f&&{q:f}},h=(await t(u))[e]||[];if(h.length===0)l=!1;else{i.push(...h);const _=h[h.length-1];if(_&&typeof _=="object"){const w=_[n];w!=null&&(r=String(w))}h.length<c&&(l=!1),d!==-1&&i.length>=d&&(console.warn(`fetchAll: Reached maxItems limit (${d}). There may be more items.`),l=!1)}}return i}function j(t){const{mainTenantId:e,getChildTenantIds:s,getAdapters:n,shouldSync:a=()=>!0,transformForSync:c}=t;async function d(r,l,f){return(await r.resourceServers.list(l,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function o(r,l){const f=await s();await Promise.all(f.map(async u=>{try{const m=await n(u),_={...c?c(r,u):{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(l==="create"){const w=await d(m,u,r.identifier);w&&w.id?await m.resourceServers.update(u,w.id,_):await m.resourceServers.create(u,_)}else{const w=await d(m,u,r.identifier);w&&w.id&&await m.resourceServers.update(u,w.id,_)}}catch(m){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${u}":`,m)}}))}async function i(r){const l=await s();await Promise.all(l.map(async f=>{try{const u=await n(f),m=await d(u,f,r);m&&m.id&&await u.resourceServers.remove(f,m.id)}catch(u){console.error(`Failed to delete resource server "${r}" from tenant "${f}":`,u)}}))}return{afterCreate:async(r,l)=>{r.tenantId===e&&a(l)&&await o(l,"create")},afterUpdate:async(r,l,f)=>{r.tenantId===e&&a(f)&&await o(f,"update")},afterDelete:async(r,l)=>{r.tenantId===e&&await i(l)}}}function O(t){const{mainTenantId:e,getMainTenantAdapters:s,getAdapters:n,shouldSync:a=()=>!0,transformForSync:c}=t;return{async afterCreate(d,o){if(o.id!==e)try{const i=await s(),r=await n(o.id),l=await P(f=>i.resourceServers.list(e,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(l.filter(f=>a(f)).map(async f=>{const u=f;try{const m=c?c(u,o.id):{name:u.name,identifier:u.identifier,scopes:u.scopes,signing_alg:u.signing_alg,signing_secret:u.signing_secret,token_lifetime:u.token_lifetime,token_lifetime_for_web:u.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:u.skip_consent_for_verifiable_first_party_clients,allow_offline_access:u.allow_offline_access,verificationKey:u.verificationKey,options:u.options};await r.resourceServers.create(o.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync resource server "${u.identifier}" to new tenant "${o.id}":`,m)}}))}catch(i){console.error(`Failed to sync resource servers to new tenant "${o.id}":`,i)}}}}var p=class extends Error{constructor(e=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});M(this,"res");M(this,"status");this.res=s==null?void 0:s.res,this.status=e}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function S(t,e){const s=new R.Hono;return s.get("/",async n=>{var l;if(t.accessControl&&await((l=e.onTenantAccessValidation)==null?void 0:l.call(e,n,t.accessControl.mainTenantId))===!1)throw new p(403,{message:"Access denied to tenant management"});const a=$.auth0QuerySchema.parse(n.req.query()),{page:c,per_page:d,include_totals:o,q:i}=a,r=await n.env.data.tenants.list({page:c,per_page:d,include_totals:o,q:i});return o?n.json(r):n.json(r.tenants)}),s.get("/:id",async n=>{var o;const a=n.req.param("id");if(await((o=e.onTenantAccessValidation)==null?void 0:o.call(e,n,a))===!1)throw new p(403,{message:"Access denied to this tenant"});const d=await n.env.data.tenants.get(a);if(!d)throw new p(404,{message:"Tenant not found"});return n.json(d)}),s.post("/",async n=>{var a,c,d,o;try{if(t.accessControl&&await((a=e.onTenantAccessValidation)==null?void 0:a.call(e,n,t.accessControl.mainTenantId))===!1)throw new p(403,{message:"Access denied to create tenants"});let i=$.tenantInsertSchema.parse(await n.req.json());const r={adapters:n.env.data,ctx:n};(c=e.tenants)!=null&&c.beforeCreate&&(i=await e.tenants.beforeCreate(r,i));const l=await n.env.data.tenants.create(i);return(d=e.tenants)!=null&&d.afterCreate&&await e.tenants.afterCreate(r,l),n.json(l,201)}catch(i){throw i instanceof Q.z.ZodError?new p(400,{message:"Validation error",cause:i}):i instanceof Error&&("code"in i&&i.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(o=i.message)!=null&&o.includes("UNIQUE constraint failed"))?new p(409,{message:"Tenant with this ID already exists"}):i}}),s.patch("/:id",async n=>{var m,h,_;const a=n.req.param("id");if(await((m=e.onTenantAccessValidation)==null?void 0:m.call(e,n,a))===!1)throw new p(403,{message:"Access denied to update this tenant"});const d=$.tenantInsertSchema.partial().parse(await n.req.json()),{id:o,...i}=d;if(!await n.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const l={adapters:n.env.data,ctx:n};let f=i;(h=e.tenants)!=null&&h.beforeUpdate&&(f=await e.tenants.beforeUpdate(l,a,i)),await n.env.data.tenants.update(a,f);const u=await n.env.data.tenants.get(a);if(!u)throw new p(404,{message:"Tenant not found after update"});return(_=e.tenants)!=null&&_.afterUpdate&&await e.tenants.afterUpdate(l,u),n.json(u)}),s.delete("/:id",async n=>{var o,i,r;const a=n.req.param("id");if(t.accessControl&&a===t.accessControl.mainTenantId)throw new p(400,{message:"Cannot delete the main tenant"});if(t.accessControl&&await((o=e.onTenantAccessValidation)==null?void 0:o.call(e,n,t.accessControl.mainTenantId))===!1)throw new p(403,{message:"Access denied to delete tenants"});if(!await n.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const d={adapters:n.env.data,ctx:n};return(i=e.tenants)!=null&&i.beforeDelete&&await e.tenants.beforeDelete(d,a),await n.env.data.tenants.remove(a),(r=e.tenants)!=null&&r.afterDelete&&await e.tenants.afterDelete(d,a),n.body(null,204)}),s}function Z(t){const e=[{pattern:/\/api\/v2\/resource-servers\/([^/]+)$/,type:"resource_server"},{pattern:/\/api\/v2\/roles\/([^/]+)$/,type:"role"},{pattern:/\/api\/v2\/connections\/([^/]+)$/,type:"connection"}];for(const{pattern:s,type:n}of e){const a=t.match(s);if(a&&a[1])return{type:n,id:a[1]}}return null}async function J(t,e,s){try{switch(s.type){case"resource_server":{const n=await t.resourceServers.get(e,s.id);return(n==null?void 0:n.is_system)===!0}case"role":{const n=await t.roles.get(e,s.id);return(n==null?void 0:n.is_system)===!0}case"connection":{const n=await t.connections.get(e,s.id);return(n==null?void 0:n.is_system)===!0}default:return!1}}catch{return!1}}function X(t){return{resource_server:"resource server",role:"role",connection:"connection"}[t]}function E(){return async(t,e)=>{if(!["PATCH","PUT","DELETE"].includes(t.req.method))return e();const s=Z(t.req.path);if(!s)return e();const n=t.var.tenant_id||t.req.header("x-tenant-id")||t.req.header("tenant-id");if(!n)return e();if(await J(t.env.data,n,s))throw new p(403,{message:`This ${X(s.type)} is a system resource and cannot be modified. Make changes in the main tenant instead.`});return e()}}function U(t){return async(e,s)=>{if(!t.accessControl)return s();const n=e.var.tenant_id,a=e.var.organization_id;if(!n)throw new p(400,{message:"Tenant ID not found in request"});if(!D(a,n,t.accessControl.mainTenantId))throw new p(403,{message:`Access denied to tenant ${n}`});return s()}}function N(t){return async(e,s)=>{if(!t.subdomainRouting)return s();const{baseDomain:n,reservedSubdomains:a=[],resolveSubdomain:c}=t.subdomainRouting,d=e.req.header("host")||"";let o=null;if(d.endsWith(n)){const r=d.slice(0,-(n.length+1));r&&!r.includes(".")&&(o=r)}if(o&&a.includes(o)&&(o=null),!o)return t.accessControl&&e.set("tenant_id",t.accessControl.mainTenantId),s();let i=null;if(c)i=await c(o);else if(t.subdomainRouting.useOrganizations!==!1&&t.accessControl)try{const r=await e.env.data.organizations.get(t.accessControl.mainTenantId,o);r&&(i=r.id)}catch{}if(!i)throw new p(404,{message:`Tenant not found for subdomain: ${o}`});return e.set("tenant_id",i),s()}}function K(t){return async(e,s)=>{if(!t.databaseIsolation)return s();const n=e.var.tenant_id;if(!n)throw new p(400,{message:"Tenant ID not found in request"});try{const a=await t.databaseIsolation.getAdapters(n);e.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${n}:`,a),new p(500,{message:"Failed to resolve tenant database"})}return s()}}function I(t){const e=N(t),s=U(t),n=K(t);return async(a,c)=>(await e(a,async()=>{}),await s(a,async()=>{}),await n(a,async()=>{}),c())}function k(t){const e=b(t);return{name:"multi-tenancy",middleware:I(t),hooks:e,routes:[{path:"/management",handler:S(t,e)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),t.accessControl&&console.log(` - Access control enabled (main tenant: ${t.accessControl.mainTenantId})`),t.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${t.subdomainRouting.baseDomain})`),t.databaseIsolation&&console.log(" - Database isolation enabled")}}}function b(t){const e=t.accessControl?q(t.accessControl):{},s=t.databaseIsolation?z(t.databaseIsolation):{},n=F(t);return{...e,...s,tenants:n}}function V(t){const e=new R.Hono,s=b(t);return e.route("/tenants",S(t,s)),e}function x(t){return{hooks:b(t),middleware:I(t),app:V(t),config:t}}function ee(t){const{mainTenantId:e="main",syncResourceServers:s=!0,multiTenancy:n,entityHooks:a,...c}=t,d={...n,accessControl:{mainTenantId:e,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...n==null?void 0:n.accessControl}},o=b(d);let i,r;s&&(i=j({mainTenantId:e,getChildTenantIds:async()=>(await P(y=>t.dataAdapter.tenants.list(y),"tenants",{cursorField:"id",pageSize:100})).filter(y=>y.id!==e).map(y=>y.id),getAdapters:async g=>t.dataAdapter}),r=O({mainTenantId:e,getMainTenantAdapters:async()=>t.dataAdapter,getAdapters:async g=>t.dataAdapter}));const l=async(g,y,...T)=>{const v=[];if(g)try{await g(...T)}catch(A){v.push(A instanceof Error?A:new Error(String(A)))}if(y)try{await y(...T)}catch(A){v.push(A instanceof Error?A:new Error(String(A)))}if(v.length===1)throw v[0];if(v.length>1)throw new AggregateError(v,`Multiple hook errors: ${v.map(A=>A.message).join("; ")}`)},f={...a,resourceServers:i?{...a==null?void 0:a.resourceServers,afterCreate:async(g,y)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterCreate,i==null?void 0:i.afterCreate,g,y)},afterUpdate:async(g,y,T)=>{var v;await l((v=a==null?void 0:a.resourceServers)==null?void 0:v.afterUpdate,i==null?void 0:i.afterUpdate,g,y,T)},afterDelete:async(g,y)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterDelete,i==null?void 0:i.afterDelete,g,y)}}:a==null?void 0:a.resourceServers,tenants:r?{...a==null?void 0:a.tenants,afterCreate:async(g,y)=>{var T;await l((T=a==null?void 0:a.tenants)==null?void 0:T.afterCreate,r==null?void 0:r.afterCreate,g,y)}}:a==null?void 0:a.tenants},u=C.init({...c,entityHooks:f}),{app:m,managementApp:h,..._}=u,w=new R.Hono;w.use("/api/v2/*",E()),w.route("/",m);const H=S(d,o);return w.route("/api/v2/tenants",H),{app:w,managementApp:h,..._,multiTenancyConfig:d,multiTenancyHooks:o}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>C.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>C.seed});exports.createAccessControlHooks=q;exports.createAccessControlMiddleware=U;exports.createDatabaseHooks=z;exports.createDatabaseMiddleware=K;exports.createMultiTenancy=V;exports.createMultiTenancyHooks=b;exports.createMultiTenancyMiddleware=I;exports.createMultiTenancyPlugin=k;exports.createProtectSyncedMiddleware=E;exports.createProvisioningHooks=F;exports.createResourceServerSyncHooks=j;exports.createSubdomainMiddleware=N;exports.createTenantResourceServerSyncHooks=O;exports.createTenantsRouter=S;exports.fetchAll=P;exports.init=ee;exports.setupMultiTenancy=x;exports.validateTenantAccess=D;
|
|
1
|
+
"use strict";var W=Object.defineProperty;var G=(e,n,s)=>n in e?W(e,n,{enumerable:!0,configurable:!0,writable:!0,value:s}):e[n]=s;var z=(e,n,s)=>G(e,typeof n!="symbol"?n+"":n,s);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I=require("hono"),S=require("authhero"),Q=require("zod"),$=require("@authhero/adapter-interfaces");function q(e){const{mainTenantId:n,requireOrganizationMatch:s=!0}=e;return{async onTenantAccessValidation(t,a){if(a===n)return!0;if(s){const o=t.var.organization_id;return o?o===a:!1}return!0}}}function P(e,n,s){return n===s?!0:e?e===n:!1}function D(e){return{async resolveDataAdapters(n){try{return await e.getAdapters(n)}catch(s){console.error(`Failed to resolve data adapters for tenant ${n}:`,s);return}}}}function F(e){return{async afterCreate(n,s){const{accessControl:t,databaseIsolation:a,settingsInheritance:o}=e;t&&n.ctx&&await V(n,s,t),a!=null&&a.onProvision&&await a.onProvision(s.id),(o==null?void 0:o.inheritFromMain)!==!1&&n.ctx&&await Y(n,s,e)},async beforeDelete(n,s){const{accessControl:t,databaseIsolation:a}=e;if(t)try{const d=(await n.adapters.organizations.list(t.mainTenantId)).organizations.find(i=>i.name===s);d&&await n.adapters.organizations.remove(t.mainTenantId,d.id)}catch(o){console.warn(`Failed to remove organization for tenant ${s}:`,o)}if(a!=null&&a.onDeprovision)try{await a.onDeprovision(s)}catch(o){console.warn(`Failed to deprovision database for tenant ${s}:`,o)}}}}async function V(e,n,s){const{mainTenantId:t,defaultPermissions:a,defaultRoles:o,issuer:d,adminRoleName:i="Tenant Admin",adminRoleDescription:c="Full access to all tenant management operations",addCreatorToOrganization:r=!0}=s;await e.adapters.organizations.create(t,{id:n.id,name:n.id,display_name:n.friendly_name||n.id});let l=null;if(d&&(l=await L(e,t,d,i,c)),r&&e.ctx){const f=e.ctx.var.user;if(f!=null&&f.sub)try{await e.adapters.userOrganizations.create(t,{user_id:f.sub,organization_id:n.id}),l&&await e.adapters.userRoles.create(t,f.sub,l,n.id)}catch(u){console.warn(`Failed to add creator ${f.sub} to organization ${n.id}:`,u)}}o&&o.length>0&&console.log(`Would assign roles ${o.join(", ")} to organization ${n.id}`),a&&a.length>0&&console.log(`Would grant permissions ${a.join(", ")} to organization ${n.id}`)}async function L(e,n,s,t,a){const d=(await e.adapters.roles.list(n,{})).roles.find(l=>l.name===t);if(d)return d.id;const i=await e.adapters.roles.create(n,{name:t,description:a}),c=`${s}api/v2/`,r=S.MANAGEMENT_API_SCOPES.map(l=>({role_id:i.id,resource_server_identifier:c,permission_name:l.value}));return await e.adapters.rolePermissions.assign(n,i.id,r),i.id}async function Y(e,n,s){const{accessControl:t,settingsInheritance:a}=s;if(!t)return;const o=await e.adapters.tenants.get(t.mainTenantId);if(!o)return;let d={...o};const i=["id","created_at","updated_at","friendly_name","audience","sender_email","sender_name"];for(const c of i)delete d[c];if(a!=null&&a.inheritedKeys){const c={};for(const r of a.inheritedKeys)r in o&&!i.includes(r)&&(c[r]=o[r]);d=c}if(a!=null&&a.excludedKeys)for(const c of a.excludedKeys)delete d[c];a!=null&&a.transformSettings&&(d=a.transformSettings(d,n.id)),Object.keys(d).length>0&&await e.adapters.tenants.update(n.id,d)}async function R(e,n,s={}){const{cursorField:t="id",sortOrder:a="asc",pageSize:o=100,maxItems:d=1e4,q:i}=s,c=[];let r,l=!0;for(;l;){let f=i||"";if(r){const w=`${t}:${a==="asc"?">":"<"}${r}`;f=f?`(${f}) AND ${w}`:w}const u={per_page:o,page:0,sort:{sort_by:t,sort_order:a},...f&&{q:f}},_=(await e(u))[n]||[];if(_.length===0)l=!1;else{c.push(..._);const g=_[_.length-1];if(g&&typeof g=="object"){const w=g[t];w!=null&&(r=String(w))}_.length<o&&(l=!1),d!==-1&&c.length>=d&&(console.warn(`fetchAll: Reached maxItems limit (${d}). There may be more items.`),l=!1)}}return c}function j(e){const{mainTenantId:n,getChildTenantIds:s,getAdapters:t,shouldSync:a=()=>!0,transformForSync:o}=e;async function d(r,l,f){return(await r.resourceServers.list(l,{q:`identifier:${f}`,per_page:1})).resource_servers[0]??null}async function i(r,l){const f=await s();await Promise.all(f.map(async u=>{try{const m=await t(u),g={...o?o(r,u):{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(l==="create"){const w=await d(m,u,r.identifier);w&&w.id?await m.resourceServers.update(u,w.id,g):await m.resourceServers.create(u,g)}else{const w=await d(m,u,r.identifier);w&&w.id&&await m.resourceServers.update(u,w.id,g)}}catch(m){console.error(`Failed to sync resource server "${r.identifier}" to tenant "${u}":`,m)}}))}async function c(r){const l=await s();await Promise.all(l.map(async f=>{try{const u=await t(f),m=await d(u,f,r);m&&m.id&&await u.resourceServers.remove(f,m.id)}catch(u){console.error(`Failed to delete resource server "${r}" from tenant "${f}":`,u)}}))}return{afterCreate:async(r,l)=>{r.tenantId===n&&a(l)&&await i(l,"create")},afterUpdate:async(r,l,f)=>{r.tenantId===n&&a(f)&&await i(f,"update")},afterDelete:async(r,l)=>{r.tenantId===n&&await c(l)}}}function U(e){const{mainTenantId:n,getMainTenantAdapters:s,getAdapters:t,shouldSync:a=()=>!0,transformForSync:o}=e;return{async afterCreate(d,i){if(i.id!==n)try{const c=await s(),r=await t(i.id),l=await R(f=>c.resourceServers.list(n,f),"resource_servers",{cursorField:"id",pageSize:100});await Promise.all(l.filter(f=>a(f)).map(async f=>{const u=f;try{const m=o?o(u,i.id):{name:u.name,identifier:u.identifier,scopes:u.scopes,signing_alg:u.signing_alg,signing_secret:u.signing_secret,token_lifetime:u.token_lifetime,token_lifetime_for_web:u.token_lifetime_for_web,skip_consent_for_verifiable_first_party_clients:u.skip_consent_for_verifiable_first_party_clients,allow_offline_access:u.allow_offline_access,verificationKey:u.verificationKey,options:u.options};await r.resourceServers.create(i.id,{...m,is_system:!0})}catch(m){console.error(`Failed to sync resource server "${u.identifier}" to new tenant "${i.id}":`,m)}}))}catch(c){console.error(`Failed to sync resource servers to new tenant "${i.id}":`,c)}}}}var p=class extends Error{constructor(n=500,s){super(s==null?void 0:s.message,{cause:s==null?void 0:s.cause});z(this,"res");z(this,"status");this.res=s==null?void 0:s.res,this.status=n}getResponse(){return this.res?new Response(this.res.body,{status:this.status,headers:this.res.headers}):new Response(this.message,{status:this.status})}};function M(e,n){const s=new I.Hono;return s.get("/",async t=>{var f,u;const a=$.auth0QuerySchema.parse(t.req.query()),{page:o,per_page:d,include_totals:i,q:c}=a,r=t.var.user;if(e.accessControl&&(r!=null&&r.sub)){const m=e.accessControl.mainTenantId,g=(await t.env.data.userOrganizations.listUserOrganizations(m,r.sub,{})).organizations.map(y=>y.id);g.includes(m)||g.push(m);const w=await t.env.data.tenants.list({page:o,per_page:d,include_totals:i,q:c}),A=w.tenants.filter(y=>g.includes(y.id));return i?t.json({tenants:A,start:((f=w.totals)==null?void 0:f.start)??0,limit:((u=w.totals)==null?void 0:u.limit)??d,length:A.length}):t.json(A)}const l=await t.env.data.tenants.list({page:o,per_page:d,include_totals:i,q:c});return i?t.json(l):t.json(l.tenants)}),s.get("/:id",async t=>{const a=t.req.param("id");if(e.accessControl){const d=t.var.user,i=e.accessControl.mainTenantId;if(a!==i){if(!(d!=null&&d.sub))throw new p(401,{message:"Authentication required"});if(!(await t.env.data.userOrganizations.listUserOrganizations(i,d.sub,{})).organizations.some(l=>l.id===a))throw new p(403,{message:"Access denied to this tenant"})}}const o=await t.env.data.tenants.get(a);if(!o)throw new p(404,{message:"Tenant not found"});return t.json(o)}),s.post("/",async t=>{var a,o,d;try{const i=t.var.user;if(!(i!=null&&i.sub))throw new p(401,{message:"Authentication required to create tenants"});let c=$.tenantInsertSchema.parse(await t.req.json());const r={adapters:t.env.data,ctx:t};(a=n.tenants)!=null&&a.beforeCreate&&(c=await n.tenants.beforeCreate(r,c));const l=await t.env.data.tenants.create(c);return(o=n.tenants)!=null&&o.afterCreate&&await n.tenants.afterCreate(r,l),t.json(l,201)}catch(i){throw i instanceof Q.z.ZodError?new p(400,{message:"Validation error",cause:i}):i instanceof Error&&("code"in i&&i.code==="SQLITE_CONSTRAINT_PRIMARYKEY"||(d=i.message)!=null&&d.includes("UNIQUE constraint failed"))?new p(409,{message:"Tenant with this ID already exists"}):i}}),s.patch("/:id",async t=>{var u,m;const a=t.req.param("id");if(e.accessControl){const _=t.var.user;if(!(_!=null&&_.sub))throw new p(401,{message:"Authentication required to update tenants"});const g=e.accessControl.mainTenantId;if(a!==g&&!(await t.env.data.userOrganizations.listUserOrganizations(g,_.sub,{})).organizations.some(y=>y.id===a))throw new p(403,{message:"Access denied to update this tenant"})}const o=$.tenantInsertSchema.partial().parse(await t.req.json()),{id:d,...i}=o;if(!await t.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const r={adapters:t.env.data,ctx:t};let l=i;(u=n.tenants)!=null&&u.beforeUpdate&&(l=await n.tenants.beforeUpdate(r,a,i)),await t.env.data.tenants.update(a,l);const f=await t.env.data.tenants.get(a);if(!f)throw new p(404,{message:"Tenant not found after update"});return(m=n.tenants)!=null&&m.afterUpdate&&await n.tenants.afterUpdate(r,f),t.json(f)}),s.delete("/:id",async t=>{var i,c;const a=t.req.param("id");if(e.accessControl&&a===e.accessControl.mainTenantId)throw new p(400,{message:"Cannot delete the main tenant"});if(e.accessControl){const r=t.var.user;if(!(r!=null&&r.sub))throw new p(401,{message:"Authentication required to delete tenants"});const l=e.accessControl.mainTenantId;if(!(await t.env.data.userOrganizations.listUserOrganizations(l,r.sub,{})).organizations.some(m=>m.id===a))throw new p(403,{message:"Access denied to delete this tenant"})}if(!await t.env.data.tenants.get(a))throw new p(404,{message:"Tenant not found"});const d={adapters:t.env.data,ctx:t};return(i=n.tenants)!=null&&i.beforeDelete&&await n.tenants.beforeDelete(d,a),await t.env.data.tenants.remove(a),(c=n.tenants)!=null&&c.afterDelete&&await n.tenants.afterDelete(d,a),t.body(null,204)}),s}function B(e){const n=[{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:t}of n){const a=e.match(s);if(a&&a[1])return{type:t,id:a[1]}}return null}async function Z(e,n,s){try{switch(s.type){case"resource_server":{const t=await e.resourceServers.get(n,s.id);return(t==null?void 0:t.is_system)===!0}case"role":{const t=await e.roles.get(n,s.id);return(t==null?void 0:t.is_system)===!0}case"connection":{const t=await e.connections.get(n,s.id);return(t==null?void 0:t.is_system)===!0}default:return!1}}catch{return!1}}function J(e){return{resource_server:"resource server",role:"role",connection:"connection"}[e]}function E(){return async(e,n)=>{if(!["PATCH","PUT","DELETE"].includes(e.req.method))return n();const s=B(e.req.path);if(!s)return n();const t=e.var.tenant_id||e.req.header("x-tenant-id")||e.req.header("tenant-id");if(!t)return n();if(await Z(e.env.data,t,s))throw new p(403,{message:`This ${J(s.type)} is a system resource and cannot be modified. Make changes in the main tenant instead.`});return n()}}function N(e){return async(n,s)=>{if(!e.accessControl)return s();const t=n.var.tenant_id,a=n.var.organization_id;if(!t)throw new p(400,{message:"Tenant ID not found in request"});if(!P(a,t,e.accessControl.mainTenantId))throw new p(403,{message:`Access denied to tenant ${t}`});return s()}}function k(e){return async(n,s)=>{if(!e.subdomainRouting)return s();const{baseDomain:t,reservedSubdomains:a=[],resolveSubdomain:o}=e.subdomainRouting,d=n.req.header("host")||"";let i=null;if(d.endsWith(t)){const r=d.slice(0,-(t.length+1));r&&!r.includes(".")&&(i=r)}if(i&&a.includes(i)&&(i=null),!i)return e.accessControl&&n.set("tenant_id",e.accessControl.mainTenantId),s();let c=null;if(o)c=await o(i);else if(e.subdomainRouting.useOrganizations!==!1&&e.accessControl)try{const r=await n.env.data.organizations.get(e.accessControl.mainTenantId,i);r&&(c=r.id)}catch{}if(!c)throw new p(404,{message:`Tenant not found for subdomain: ${i}`});return n.set("tenant_id",c),s()}}function K(e){return async(n,s)=>{if(!e.databaseIsolation)return s();const t=n.var.tenant_id;if(!t)throw new p(400,{message:"Tenant ID not found in request"});try{const a=await e.databaseIsolation.getAdapters(t);n.env.data=a}catch(a){throw console.error(`Failed to resolve database for tenant ${t}:`,a),new p(500,{message:"Failed to resolve tenant database"})}return s()}}function O(e){const n=k(e),s=N(e),t=K(e);return async(a,o)=>(await n(a,async()=>{}),await s(a,async()=>{}),await t(a,async()=>{}),o())}function X(e){const n=C(e);return{name:"multi-tenancy",middleware:O(e),hooks:n,routes:[{path:"/management",handler:M(e,n)}],onRegister:async()=>{console.log("Multi-tenancy plugin registered"),e.accessControl&&console.log(` - Access control enabled (main tenant: ${e.accessControl.mainTenantId})`),e.subdomainRouting&&console.log(` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`),e.databaseIsolation&&console.log(" - Database isolation enabled")}}}function C(e){const n=e.accessControl?q(e.accessControl):{},s=e.databaseIsolation?D(e.databaseIsolation):{},t=F(e);return{...n,...s,tenants:t}}function H(e){const n=new I.Hono,s=C(e);return n.route("/tenants",M(e,s)),n}function x(e){return{hooks:C(e),middleware:O(e),app:H(e),config:e}}function ee(e){const{mainTenantId:n="main",syncResourceServers:s=!0,multiTenancy:t,entityHooks:a,...o}=e,d={...t,accessControl:{mainTenantId:n,requireOrganizationMatch:!1,defaultPermissions:["tenant:admin"],...t==null?void 0:t.accessControl}},i=C(d);let c,r;s&&(c=j({mainTenantId:n,getChildTenantIds:async()=>(await R(h=>e.dataAdapter.tenants.list(h),"tenants",{cursorField:"id",pageSize:100})).filter(h=>h.id!==n).map(h=>h.id),getAdapters:async y=>e.dataAdapter}),r=U({mainTenantId:n,getMainTenantAdapters:async()=>e.dataAdapter,getAdapters:async y=>e.dataAdapter}));const l=async(y,h,...T)=>{const v=[];if(y)try{await y(...T)}catch(b){v.push(b instanceof Error?b:new Error(String(b)))}if(h)try{await h(...T)}catch(b){v.push(b instanceof Error?b:new Error(String(b)))}if(v.length===1)throw v[0];if(v.length>1)throw new AggregateError(v,`Multiple hook errors: ${v.map(b=>b.message).join("; ")}`)},f={...a,resourceServers:c?{...a==null?void 0:a.resourceServers,afterCreate:async(y,h)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterCreate,c==null?void 0:c.afterCreate,y,h)},afterUpdate:async(y,h,T)=>{var v;await l((v=a==null?void 0:a.resourceServers)==null?void 0:v.afterUpdate,c==null?void 0:c.afterUpdate,y,h,T)},afterDelete:async(y,h)=>{var T;await l((T=a==null?void 0:a.resourceServers)==null?void 0:T.afterDelete,c==null?void 0:c.afterDelete,y,h)}}:a==null?void 0:a.resourceServers,tenants:r?{...a==null?void 0:a.tenants,afterCreate:async(y,h)=>{var T;await l((T=a==null?void 0:a.tenants)==null?void 0:T.afterCreate,r==null?void 0:r.afterCreate,y,h)}}:a==null?void 0:a.tenants},u=S.init({...o,entityHooks:f}),{app:m,managementApp:_,...g}=u,w=new I.Hono;w.use("/api/v2/*",E()),w.route("/",m);const A=M(d,i);return w.route("/api/v2/tenants",A),{app:w,managementApp:_,...g,multiTenancyConfig:d,multiTenancyHooks:i}}Object.defineProperty(exports,"MANAGEMENT_API_SCOPES",{enumerable:!0,get:()=>S.MANAGEMENT_API_SCOPES});Object.defineProperty(exports,"seed",{enumerable:!0,get:()=>S.seed});exports.createAccessControlHooks=q;exports.createAccessControlMiddleware=N;exports.createDatabaseHooks=D;exports.createDatabaseMiddleware=K;exports.createMultiTenancy=H;exports.createMultiTenancyHooks=C;exports.createMultiTenancyMiddleware=O;exports.createMultiTenancyPlugin=X;exports.createProtectSyncedMiddleware=E;exports.createProvisioningHooks=F;exports.createResourceServerSyncHooks=j;exports.createSubdomainMiddleware=k;exports.createTenantResourceServerSyncHooks=U;exports.createTenantsRouter=M;exports.fetchAll=R;exports.init=ee;exports.setupMultiTenancy=x;exports.validateTenantAccess=P;
|
package/dist/multi-tenancy.d.ts
CHANGED
package/dist/multi-tenancy.mjs
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
var
|
|
2
|
-
var q = (
|
|
3
|
-
var
|
|
1
|
+
var O = Object.defineProperty;
|
|
2
|
+
var q = (e, n, s) => n in e ? O(e, n, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[n] = s;
|
|
3
|
+
var z = (e, n, s) => q(e, typeof n != "symbol" ? n + "" : n, s);
|
|
4
4
|
import { Hono as $ } from "hono";
|
|
5
|
-
import { MANAGEMENT_API_SCOPES as
|
|
5
|
+
import { MANAGEMENT_API_SCOPES as D, init as F } from "authhero";
|
|
6
6
|
import { MANAGEMENT_API_SCOPES as le, seed as ue } from "authhero";
|
|
7
|
-
import { z as
|
|
8
|
-
import { auth0QuerySchema as U, tenantInsertSchema as
|
|
9
|
-
function
|
|
10
|
-
const { mainTenantId:
|
|
7
|
+
import { z as P } from "zod";
|
|
8
|
+
import { auth0QuerySchema as U, tenantInsertSchema as I } from "@authhero/adapter-interfaces";
|
|
9
|
+
function j(e) {
|
|
10
|
+
const { mainTenantId: n, requireOrganizationMatch: s = !0 } = e;
|
|
11
11
|
return {
|
|
12
|
-
async onTenantAccessValidation(
|
|
13
|
-
if (a ===
|
|
12
|
+
async onTenantAccessValidation(t, a) {
|
|
13
|
+
if (a === n)
|
|
14
14
|
return !0;
|
|
15
15
|
if (s) {
|
|
16
|
-
const
|
|
17
|
-
return
|
|
16
|
+
const o = t.var.organization_id;
|
|
17
|
+
return o ? o === a : !1;
|
|
18
18
|
}
|
|
19
19
|
return !0;
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
function E(
|
|
24
|
-
return
|
|
23
|
+
function E(e, n, s) {
|
|
24
|
+
return n === s ? !0 : e ? e === n : !1;
|
|
25
25
|
}
|
|
26
|
-
function K(
|
|
26
|
+
function K(e) {
|
|
27
27
|
return {
|
|
28
|
-
async resolveDataAdapters(
|
|
28
|
+
async resolveDataAdapters(n) {
|
|
29
29
|
try {
|
|
30
|
-
return await
|
|
30
|
+
return await e.getAdapters(n);
|
|
31
31
|
} catch (s) {
|
|
32
32
|
console.error(
|
|
33
|
-
`Failed to resolve data adapters for tenant ${
|
|
33
|
+
`Failed to resolve data adapters for tenant ${n}:`,
|
|
34
34
|
s
|
|
35
35
|
);
|
|
36
36
|
return;
|
|
@@ -38,113 +38,113 @@ function K(t) {
|
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
|
-
function N(
|
|
41
|
+
function N(e) {
|
|
42
42
|
return {
|
|
43
|
-
async afterCreate(
|
|
44
|
-
const { accessControl:
|
|
45
|
-
|
|
43
|
+
async afterCreate(n, s) {
|
|
44
|
+
const { accessControl: t, databaseIsolation: a, settingsInheritance: o } = e;
|
|
45
|
+
t && n.ctx && await W(n, s, t), a != null && a.onProvision && await a.onProvision(s.id), (o == null ? void 0 : o.inheritFromMain) !== !1 && n.ctx && await Q(n, s, e);
|
|
46
46
|
},
|
|
47
|
-
async beforeDelete(
|
|
48
|
-
const { accessControl:
|
|
49
|
-
if (
|
|
47
|
+
async beforeDelete(n, s) {
|
|
48
|
+
const { accessControl: t, databaseIsolation: a } = e;
|
|
49
|
+
if (t)
|
|
50
50
|
try {
|
|
51
|
-
const d = (await
|
|
52
|
-
|
|
53
|
-
)).organizations.find((
|
|
54
|
-
d && await
|
|
55
|
-
|
|
51
|
+
const d = (await n.adapters.organizations.list(
|
|
52
|
+
t.mainTenantId
|
|
53
|
+
)).organizations.find((i) => i.name === s);
|
|
54
|
+
d && await n.adapters.organizations.remove(
|
|
55
|
+
t.mainTenantId,
|
|
56
56
|
d.id
|
|
57
57
|
);
|
|
58
|
-
} catch (
|
|
58
|
+
} catch (o) {
|
|
59
59
|
console.warn(
|
|
60
60
|
`Failed to remove organization for tenant ${s}:`,
|
|
61
|
-
|
|
61
|
+
o
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
if (a != null && a.onDeprovision)
|
|
65
65
|
try {
|
|
66
66
|
await a.onDeprovision(s);
|
|
67
|
-
} catch (
|
|
67
|
+
} catch (o) {
|
|
68
68
|
console.warn(
|
|
69
69
|
`Failed to deprovision database for tenant ${s}:`,
|
|
70
|
-
|
|
70
|
+
o
|
|
71
71
|
);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
async function
|
|
76
|
+
async function W(e, n, s) {
|
|
77
77
|
const {
|
|
78
|
-
mainTenantId:
|
|
78
|
+
mainTenantId: t,
|
|
79
79
|
defaultPermissions: a,
|
|
80
|
-
defaultRoles:
|
|
80
|
+
defaultRoles: o,
|
|
81
81
|
issuer: d,
|
|
82
|
-
adminRoleName:
|
|
83
|
-
adminRoleDescription:
|
|
82
|
+
adminRoleName: i = "Tenant Admin",
|
|
83
|
+
adminRoleDescription: c = "Full access to all tenant management operations",
|
|
84
84
|
addCreatorToOrganization: r = !0
|
|
85
85
|
} = s;
|
|
86
|
-
await
|
|
87
|
-
id:
|
|
88
|
-
name:
|
|
89
|
-
display_name:
|
|
86
|
+
await e.adapters.organizations.create(t, {
|
|
87
|
+
id: n.id,
|
|
88
|
+
name: n.id,
|
|
89
|
+
display_name: n.friendly_name || n.id
|
|
90
90
|
});
|
|
91
91
|
let l = null;
|
|
92
|
-
if (d && (l = await
|
|
92
|
+
if (d && (l = await k(
|
|
93
|
+
e,
|
|
93
94
|
t,
|
|
94
|
-
n,
|
|
95
95
|
d,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
)), r &&
|
|
99
|
-
const f =
|
|
96
|
+
i,
|
|
97
|
+
c
|
|
98
|
+
)), r && e.ctx) {
|
|
99
|
+
const f = e.ctx.var.user;
|
|
100
100
|
if (f != null && f.sub)
|
|
101
101
|
try {
|
|
102
|
-
await
|
|
102
|
+
await e.adapters.userOrganizations.create(t, {
|
|
103
103
|
user_id: f.sub,
|
|
104
|
-
organization_id:
|
|
105
|
-
}), l && await
|
|
106
|
-
|
|
104
|
+
organization_id: n.id
|
|
105
|
+
}), l && await e.adapters.userRoles.create(
|
|
106
|
+
t,
|
|
107
107
|
f.sub,
|
|
108
108
|
l,
|
|
109
|
-
|
|
109
|
+
n.id
|
|
110
110
|
// organizationId
|
|
111
111
|
);
|
|
112
112
|
} catch (u) {
|
|
113
113
|
console.warn(
|
|
114
|
-
`Failed to add creator ${f.sub} to organization ${
|
|
114
|
+
`Failed to add creator ${f.sub} to organization ${n.id}:`,
|
|
115
115
|
u
|
|
116
116
|
);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
|
|
120
|
-
`Would assign roles ${
|
|
119
|
+
o && o.length > 0 && console.log(
|
|
120
|
+
`Would assign roles ${o.join(", ")} to organization ${n.id}`
|
|
121
121
|
), a && a.length > 0 && console.log(
|
|
122
|
-
`Would grant permissions ${a.join(", ")} to organization ${
|
|
122
|
+
`Would grant permissions ${a.join(", ")} to organization ${n.id}`
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
|
-
async function
|
|
126
|
-
const d = (await
|
|
125
|
+
async function k(e, n, s, t, a) {
|
|
126
|
+
const d = (await e.adapters.roles.list(n, {})).roles.find((l) => l.name === t);
|
|
127
127
|
if (d)
|
|
128
128
|
return d.id;
|
|
129
|
-
const
|
|
130
|
-
name:
|
|
129
|
+
const i = await e.adapters.roles.create(n, {
|
|
130
|
+
name: t,
|
|
131
131
|
description: a
|
|
132
|
-
}),
|
|
133
|
-
role_id:
|
|
134
|
-
resource_server_identifier:
|
|
132
|
+
}), c = `${s}api/v2/`, r = D.map((l) => ({
|
|
133
|
+
role_id: i.id,
|
|
134
|
+
resource_server_identifier: c,
|
|
135
135
|
permission_name: l.value
|
|
136
136
|
}));
|
|
137
|
-
return await
|
|
137
|
+
return await e.adapters.rolePermissions.assign(n, i.id, r), i.id;
|
|
138
138
|
}
|
|
139
|
-
async function Q(
|
|
140
|
-
const { accessControl:
|
|
141
|
-
if (!
|
|
139
|
+
async function Q(e, n, s) {
|
|
140
|
+
const { accessControl: t, settingsInheritance: a } = s;
|
|
141
|
+
if (!t)
|
|
142
142
|
return;
|
|
143
|
-
const
|
|
144
|
-
if (!
|
|
143
|
+
const o = await e.adapters.tenants.get(t.mainTenantId);
|
|
144
|
+
if (!o)
|
|
145
145
|
return;
|
|
146
|
-
let d = { ...
|
|
147
|
-
const
|
|
146
|
+
let d = { ...o };
|
|
147
|
+
const i = [
|
|
148
148
|
"id",
|
|
149
149
|
"created_at",
|
|
150
150
|
"updated_at",
|
|
@@ -154,83 +154,83 @@ async function Q(t, e, s) {
|
|
|
154
154
|
"sender_email",
|
|
155
155
|
"sender_name"
|
|
156
156
|
];
|
|
157
|
-
for (const
|
|
158
|
-
delete d[
|
|
157
|
+
for (const c of i)
|
|
158
|
+
delete d[c];
|
|
159
159
|
if (a != null && a.inheritedKeys) {
|
|
160
|
-
const
|
|
160
|
+
const c = {};
|
|
161
161
|
for (const r of a.inheritedKeys)
|
|
162
|
-
r in
|
|
163
|
-
d =
|
|
162
|
+
r in o && !i.includes(r) && (c[r] = o[r]);
|
|
163
|
+
d = c;
|
|
164
164
|
}
|
|
165
165
|
if (a != null && a.excludedKeys)
|
|
166
|
-
for (const
|
|
167
|
-
delete d[
|
|
166
|
+
for (const c of a.excludedKeys)
|
|
167
|
+
delete d[c];
|
|
168
168
|
a != null && a.transformSettings && (d = a.transformSettings(
|
|
169
169
|
d,
|
|
170
|
-
|
|
171
|
-
)), Object.keys(d).length > 0 && await
|
|
170
|
+
n.id
|
|
171
|
+
)), Object.keys(d).length > 0 && await e.adapters.tenants.update(n.id, d);
|
|
172
172
|
}
|
|
173
|
-
async function
|
|
173
|
+
async function R(e, n, s = {}) {
|
|
174
174
|
const {
|
|
175
|
-
cursorField:
|
|
175
|
+
cursorField: t = "id",
|
|
176
176
|
sortOrder: a = "asc",
|
|
177
|
-
pageSize:
|
|
177
|
+
pageSize: o = 100,
|
|
178
178
|
maxItems: d = 1e4,
|
|
179
|
-
q:
|
|
180
|
-
} = s,
|
|
179
|
+
q: i
|
|
180
|
+
} = s, c = [];
|
|
181
181
|
let r, l = !0;
|
|
182
182
|
for (; l; ) {
|
|
183
|
-
let f =
|
|
183
|
+
let f = i || "";
|
|
184
184
|
if (r) {
|
|
185
|
-
const w = `${
|
|
185
|
+
const w = `${t}:${a === "asc" ? ">" : "<"}${r}`;
|
|
186
186
|
f = f ? `(${f}) AND ${w}` : w;
|
|
187
187
|
}
|
|
188
188
|
const u = {
|
|
189
|
-
per_page:
|
|
189
|
+
per_page: o,
|
|
190
190
|
page: 0,
|
|
191
191
|
// Always use page 0 since we're doing cursor-based pagination
|
|
192
192
|
sort: {
|
|
193
|
-
sort_by:
|
|
193
|
+
sort_by: t,
|
|
194
194
|
sort_order: a
|
|
195
195
|
},
|
|
196
196
|
...f && { q: f }
|
|
197
|
-
},
|
|
198
|
-
if (
|
|
197
|
+
}, _ = (await e(u))[n] || [];
|
|
198
|
+
if (_.length === 0)
|
|
199
199
|
l = !1;
|
|
200
200
|
else {
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
if (
|
|
204
|
-
const w =
|
|
201
|
+
c.push(..._);
|
|
202
|
+
const g = _[_.length - 1];
|
|
203
|
+
if (g && typeof g == "object") {
|
|
204
|
+
const w = g[t];
|
|
205
205
|
w != null && (r = String(w));
|
|
206
206
|
}
|
|
207
|
-
|
|
207
|
+
_.length < o && (l = !1), d !== -1 && c.length >= d && (console.warn(
|
|
208
208
|
`fetchAll: Reached maxItems limit (${d}). There may be more items.`
|
|
209
209
|
), l = !1);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
-
return
|
|
212
|
+
return c;
|
|
213
213
|
}
|
|
214
|
-
function
|
|
214
|
+
function V(e) {
|
|
215
215
|
const {
|
|
216
|
-
mainTenantId:
|
|
216
|
+
mainTenantId: n,
|
|
217
217
|
getChildTenantIds: s,
|
|
218
|
-
getAdapters:
|
|
218
|
+
getAdapters: t,
|
|
219
219
|
shouldSync: a = () => !0,
|
|
220
|
-
transformForSync:
|
|
221
|
-
} =
|
|
220
|
+
transformForSync: o
|
|
221
|
+
} = e;
|
|
222
222
|
async function d(r, l, f) {
|
|
223
223
|
return (await r.resourceServers.list(l, {
|
|
224
224
|
q: `identifier:${f}`,
|
|
225
225
|
per_page: 1
|
|
226
226
|
})).resource_servers[0] ?? null;
|
|
227
227
|
}
|
|
228
|
-
async function
|
|
228
|
+
async function i(r, l) {
|
|
229
229
|
const f = await s();
|
|
230
230
|
await Promise.all(
|
|
231
231
|
f.map(async (u) => {
|
|
232
232
|
try {
|
|
233
|
-
const m = await
|
|
233
|
+
const m = await t(u), g = { ...o ? o(r, u) : {
|
|
234
234
|
name: r.name,
|
|
235
235
|
identifier: r.identifier,
|
|
236
236
|
scopes: r.scopes,
|
|
@@ -252,8 +252,8 @@ function G(t) {
|
|
|
252
252
|
w && w.id ? await m.resourceServers.update(
|
|
253
253
|
u,
|
|
254
254
|
w.id,
|
|
255
|
-
|
|
256
|
-
) : await m.resourceServers.create(u,
|
|
255
|
+
g
|
|
256
|
+
) : await m.resourceServers.create(u, g);
|
|
257
257
|
} else {
|
|
258
258
|
const w = await d(
|
|
259
259
|
m,
|
|
@@ -263,7 +263,7 @@ function G(t) {
|
|
|
263
263
|
w && w.id && await m.resourceServers.update(
|
|
264
264
|
u,
|
|
265
265
|
w.id,
|
|
266
|
-
|
|
266
|
+
g
|
|
267
267
|
);
|
|
268
268
|
}
|
|
269
269
|
} catch (m) {
|
|
@@ -275,12 +275,12 @@ function G(t) {
|
|
|
275
275
|
})
|
|
276
276
|
);
|
|
277
277
|
}
|
|
278
|
-
async function
|
|
278
|
+
async function c(r) {
|
|
279
279
|
const l = await s();
|
|
280
280
|
await Promise.all(
|
|
281
281
|
l.map(async (f) => {
|
|
282
282
|
try {
|
|
283
|
-
const u = await
|
|
283
|
+
const u = await t(f), m = await d(
|
|
284
284
|
u,
|
|
285
285
|
f,
|
|
286
286
|
r
|
|
@@ -297,30 +297,30 @@ function G(t) {
|
|
|
297
297
|
}
|
|
298
298
|
return {
|
|
299
299
|
afterCreate: async (r, l) => {
|
|
300
|
-
r.tenantId ===
|
|
300
|
+
r.tenantId === n && a(l) && await i(l, "create");
|
|
301
301
|
},
|
|
302
302
|
afterUpdate: async (r, l, f) => {
|
|
303
|
-
r.tenantId ===
|
|
303
|
+
r.tenantId === n && a(f) && await i(f, "update");
|
|
304
304
|
},
|
|
305
305
|
afterDelete: async (r, l) => {
|
|
306
|
-
r.tenantId ===
|
|
306
|
+
r.tenantId === n && await c(l);
|
|
307
307
|
}
|
|
308
308
|
};
|
|
309
309
|
}
|
|
310
|
-
function
|
|
310
|
+
function G(e) {
|
|
311
311
|
const {
|
|
312
|
-
mainTenantId:
|
|
312
|
+
mainTenantId: n,
|
|
313
313
|
getMainTenantAdapters: s,
|
|
314
|
-
getAdapters:
|
|
314
|
+
getAdapters: t,
|
|
315
315
|
shouldSync: a = () => !0,
|
|
316
|
-
transformForSync:
|
|
317
|
-
} =
|
|
316
|
+
transformForSync: o
|
|
317
|
+
} = e;
|
|
318
318
|
return {
|
|
319
|
-
async afterCreate(d,
|
|
320
|
-
if (
|
|
319
|
+
async afterCreate(d, i) {
|
|
320
|
+
if (i.id !== n)
|
|
321
321
|
try {
|
|
322
|
-
const
|
|
323
|
-
(f) =>
|
|
322
|
+
const c = await s(), r = await t(i.id), l = await R(
|
|
323
|
+
(f) => c.resourceServers.list(n, f),
|
|
324
324
|
"resource_servers",
|
|
325
325
|
{ cursorField: "id", pageSize: 100 }
|
|
326
326
|
);
|
|
@@ -328,7 +328,7 @@ function L(t) {
|
|
|
328
328
|
l.filter((f) => a(f)).map(async (f) => {
|
|
329
329
|
const u = f;
|
|
330
330
|
try {
|
|
331
|
-
const m =
|
|
331
|
+
const m = o ? o(u, i.id) : {
|
|
332
332
|
name: u.name,
|
|
333
333
|
identifier: u.identifier,
|
|
334
334
|
scopes: u.scopes,
|
|
@@ -341,22 +341,22 @@ function L(t) {
|
|
|
341
341
|
verificationKey: u.verificationKey,
|
|
342
342
|
options: u.options
|
|
343
343
|
};
|
|
344
|
-
await r.resourceServers.create(
|
|
344
|
+
await r.resourceServers.create(i.id, {
|
|
345
345
|
...m,
|
|
346
346
|
is_system: !0
|
|
347
347
|
});
|
|
348
348
|
} catch (m) {
|
|
349
349
|
console.error(
|
|
350
|
-
`Failed to sync resource server "${u.identifier}" to new tenant "${
|
|
350
|
+
`Failed to sync resource server "${u.identifier}" to new tenant "${i.id}":`,
|
|
351
351
|
m
|
|
352
352
|
);
|
|
353
353
|
}
|
|
354
354
|
})
|
|
355
355
|
);
|
|
356
|
-
} catch (
|
|
356
|
+
} catch (c) {
|
|
357
357
|
console.error(
|
|
358
|
-
`Failed to sync resource servers to new tenant "${
|
|
359
|
-
|
|
358
|
+
`Failed to sync resource servers to new tenant "${i.id}":`,
|
|
359
|
+
c
|
|
360
360
|
);
|
|
361
361
|
}
|
|
362
362
|
}
|
|
@@ -368,11 +368,11 @@ var p = class extends Error {
|
|
|
368
368
|
* @param status - HTTP status code for the exception. Defaults to 500.
|
|
369
369
|
* @param options - Additional options for the exception.
|
|
370
370
|
*/
|
|
371
|
-
constructor(
|
|
371
|
+
constructor(n = 500, s) {
|
|
372
372
|
super(s == null ? void 0 : s.message, { cause: s == null ? void 0 : s.cause });
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
this.res = s == null ? void 0 : s.res, this.status =
|
|
373
|
+
z(this, "res");
|
|
374
|
+
z(this, "status");
|
|
375
|
+
this.res = s == null ? void 0 : s.res, this.status = n;
|
|
376
376
|
}
|
|
377
377
|
/**
|
|
378
378
|
* Returns the response object associated with the exception.
|
|
@@ -388,119 +388,163 @@ var p = class extends Error {
|
|
|
388
388
|
});
|
|
389
389
|
}
|
|
390
390
|
};
|
|
391
|
-
function S(
|
|
391
|
+
function S(e, n) {
|
|
392
392
|
const s = new $();
|
|
393
|
-
return s.get("/", async (
|
|
394
|
-
var
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
393
|
+
return s.get("/", async (t) => {
|
|
394
|
+
var f, u;
|
|
395
|
+
const a = U.parse(t.req.query()), { page: o, per_page: d, include_totals: i, q: c } = a, r = t.var.user;
|
|
396
|
+
if (e.accessControl && (r != null && r.sub)) {
|
|
397
|
+
const m = e.accessControl.mainTenantId, g = (await t.env.data.userOrganizations.listUserOrganizations(
|
|
398
|
+
m,
|
|
399
|
+
r.sub,
|
|
400
|
+
{}
|
|
401
|
+
)).organizations.map((y) => y.id);
|
|
402
|
+
g.includes(m) || g.push(m);
|
|
403
|
+
const w = await t.env.data.tenants.list({
|
|
404
|
+
page: o,
|
|
405
|
+
per_page: d,
|
|
406
|
+
include_totals: i,
|
|
407
|
+
q: c
|
|
408
|
+
}), C = w.tenants.filter(
|
|
409
|
+
(y) => g.includes(y.id)
|
|
410
|
+
);
|
|
411
|
+
return i ? t.json({
|
|
412
|
+
tenants: C,
|
|
413
|
+
start: ((f = w.totals) == null ? void 0 : f.start) ?? 0,
|
|
414
|
+
limit: ((u = w.totals) == null ? void 0 : u.limit) ?? d,
|
|
415
|
+
length: C.length
|
|
416
|
+
}) : t.json(C);
|
|
417
|
+
}
|
|
418
|
+
const l = await t.env.data.tenants.list({
|
|
419
|
+
page: o,
|
|
405
420
|
per_page: d,
|
|
406
|
-
include_totals:
|
|
407
|
-
q:
|
|
421
|
+
include_totals: i,
|
|
422
|
+
q: c
|
|
408
423
|
});
|
|
409
|
-
return
|
|
410
|
-
}), s.get("/:id", async (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
424
|
+
return i ? t.json(l) : t.json(l.tenants);
|
|
425
|
+
}), s.get("/:id", async (t) => {
|
|
426
|
+
const a = t.req.param("id");
|
|
427
|
+
if (e.accessControl) {
|
|
428
|
+
const d = t.var.user, i = e.accessControl.mainTenantId;
|
|
429
|
+
if (a !== i) {
|
|
430
|
+
if (!(d != null && d.sub))
|
|
431
|
+
throw new p(401, {
|
|
432
|
+
message: "Authentication required"
|
|
433
|
+
});
|
|
434
|
+
if (!(await t.env.data.userOrganizations.listUserOrganizations(
|
|
435
|
+
i,
|
|
436
|
+
d.sub,
|
|
437
|
+
{}
|
|
438
|
+
)).organizations.some((l) => l.id === a))
|
|
439
|
+
throw new p(403, {
|
|
440
|
+
message: "Access denied to this tenant"
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const o = await t.env.data.tenants.get(a);
|
|
445
|
+
if (!o)
|
|
419
446
|
throw new p(404, {
|
|
420
447
|
message: "Tenant not found"
|
|
421
448
|
});
|
|
422
|
-
return
|
|
423
|
-
}), s.post("/", async (
|
|
424
|
-
var a,
|
|
449
|
+
return t.json(o);
|
|
450
|
+
}), s.post("/", async (t) => {
|
|
451
|
+
var a, o, d;
|
|
425
452
|
try {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
)) === !1)
|
|
431
|
-
throw new p(403, {
|
|
432
|
-
message: "Access denied to create tenants"
|
|
453
|
+
const i = t.var.user;
|
|
454
|
+
if (!(i != null && i.sub))
|
|
455
|
+
throw new p(401, {
|
|
456
|
+
message: "Authentication required to create tenants"
|
|
433
457
|
});
|
|
434
|
-
let
|
|
435
|
-
await
|
|
458
|
+
let c = I.parse(
|
|
459
|
+
await t.req.json()
|
|
436
460
|
);
|
|
437
461
|
const r = {
|
|
438
|
-
adapters:
|
|
439
|
-
ctx:
|
|
462
|
+
adapters: t.env.data,
|
|
463
|
+
ctx: t
|
|
440
464
|
};
|
|
441
|
-
(
|
|
442
|
-
const l = await
|
|
443
|
-
return (
|
|
465
|
+
(a = n.tenants) != null && a.beforeCreate && (c = await n.tenants.beforeCreate(r, c));
|
|
466
|
+
const l = await t.env.data.tenants.create(c);
|
|
467
|
+
return (o = n.tenants) != null && o.afterCreate && await n.tenants.afterCreate(r, l), t.json(l, 201);
|
|
444
468
|
} catch (i) {
|
|
445
|
-
throw i instanceof
|
|
469
|
+
throw i instanceof P.ZodError ? new p(400, {
|
|
446
470
|
message: "Validation error",
|
|
447
471
|
cause: i
|
|
448
|
-
}) : i instanceof Error && ("code" in i && i.code === "SQLITE_CONSTRAINT_PRIMARYKEY" || (
|
|
472
|
+
}) : i instanceof Error && ("code" in i && i.code === "SQLITE_CONSTRAINT_PRIMARYKEY" || (d = i.message) != null && d.includes("UNIQUE constraint failed")) ? new p(409, {
|
|
449
473
|
message: "Tenant with this ID already exists"
|
|
450
474
|
}) : i;
|
|
451
475
|
}
|
|
452
|
-
}), s.patch("/:id", async (
|
|
453
|
-
var
|
|
454
|
-
const a =
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
476
|
+
}), s.patch("/:id", async (t) => {
|
|
477
|
+
var u, m;
|
|
478
|
+
const a = t.req.param("id");
|
|
479
|
+
if (e.accessControl) {
|
|
480
|
+
const _ = t.var.user;
|
|
481
|
+
if (!(_ != null && _.sub))
|
|
482
|
+
throw new p(401, {
|
|
483
|
+
message: "Authentication required to update tenants"
|
|
484
|
+
});
|
|
485
|
+
const g = e.accessControl.mainTenantId;
|
|
486
|
+
if (a !== g && !(await t.env.data.userOrganizations.listUserOrganizations(
|
|
487
|
+
g,
|
|
488
|
+
_.sub,
|
|
489
|
+
{}
|
|
490
|
+
)).organizations.some((y) => y.id === a))
|
|
491
|
+
throw new p(403, {
|
|
492
|
+
message: "Access denied to update this tenant"
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
const o = I.partial().parse(await t.req.json()), { id: d, ...i } = o;
|
|
496
|
+
if (!await t.env.data.tenants.get(a))
|
|
461
497
|
throw new p(404, {
|
|
462
498
|
message: "Tenant not found"
|
|
463
499
|
});
|
|
464
|
-
const
|
|
465
|
-
adapters:
|
|
466
|
-
ctx:
|
|
500
|
+
const r = {
|
|
501
|
+
adapters: t.env.data,
|
|
502
|
+
ctx: t
|
|
467
503
|
};
|
|
468
|
-
let
|
|
469
|
-
(
|
|
470
|
-
const
|
|
471
|
-
if (!
|
|
504
|
+
let l = i;
|
|
505
|
+
(u = n.tenants) != null && u.beforeUpdate && (l = await n.tenants.beforeUpdate(r, a, i)), await t.env.data.tenants.update(a, l);
|
|
506
|
+
const f = await t.env.data.tenants.get(a);
|
|
507
|
+
if (!f)
|
|
472
508
|
throw new p(404, {
|
|
473
509
|
message: "Tenant not found after update"
|
|
474
510
|
});
|
|
475
|
-
return (
|
|
476
|
-
}), s.delete("/:id", async (
|
|
477
|
-
var
|
|
478
|
-
const a =
|
|
479
|
-
if (
|
|
511
|
+
return (m = n.tenants) != null && m.afterUpdate && await n.tenants.afterUpdate(r, f), t.json(f);
|
|
512
|
+
}), s.delete("/:id", async (t) => {
|
|
513
|
+
var i, c;
|
|
514
|
+
const a = t.req.param("id");
|
|
515
|
+
if (e.accessControl && a === e.accessControl.mainTenantId)
|
|
480
516
|
throw new p(400, {
|
|
481
517
|
message: "Cannot delete the main tenant"
|
|
482
518
|
});
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
519
|
+
if (e.accessControl) {
|
|
520
|
+
const r = t.var.user;
|
|
521
|
+
if (!(r != null && r.sub))
|
|
522
|
+
throw new p(401, {
|
|
523
|
+
message: "Authentication required to delete tenants"
|
|
524
|
+
});
|
|
525
|
+
const l = e.accessControl.mainTenantId;
|
|
526
|
+
if (!(await t.env.data.userOrganizations.listUserOrganizations(
|
|
527
|
+
l,
|
|
528
|
+
r.sub,
|
|
529
|
+
{}
|
|
530
|
+
)).organizations.some((m) => m.id === a))
|
|
531
|
+
throw new p(403, {
|
|
532
|
+
message: "Access denied to delete this tenant"
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (!await t.env.data.tenants.get(a))
|
|
492
536
|
throw new p(404, {
|
|
493
537
|
message: "Tenant not found"
|
|
494
538
|
});
|
|
495
539
|
const d = {
|
|
496
|
-
adapters:
|
|
497
|
-
ctx:
|
|
540
|
+
adapters: t.env.data,
|
|
541
|
+
ctx: t
|
|
498
542
|
};
|
|
499
|
-
return (i =
|
|
543
|
+
return (i = n.tenants) != null && i.beforeDelete && await n.tenants.beforeDelete(d, a), await t.env.data.tenants.remove(a), (c = n.tenants) != null && c.afterDelete && await n.tenants.afterDelete(d, a), t.body(null, 204);
|
|
500
544
|
}), s;
|
|
501
545
|
}
|
|
502
|
-
function
|
|
503
|
-
const
|
|
546
|
+
function L(e) {
|
|
547
|
+
const n = [
|
|
504
548
|
{
|
|
505
549
|
pattern: /\/api\/v2\/resource-servers\/([^/]+)$/,
|
|
506
550
|
type: "resource_server"
|
|
@@ -508,27 +552,27 @@ function Y(t) {
|
|
|
508
552
|
{ pattern: /\/api\/v2\/roles\/([^/]+)$/, type: "role" },
|
|
509
553
|
{ pattern: /\/api\/v2\/connections\/([^/]+)$/, type: "connection" }
|
|
510
554
|
];
|
|
511
|
-
for (const { pattern: s, type:
|
|
512
|
-
const a =
|
|
555
|
+
for (const { pattern: s, type: t } of n) {
|
|
556
|
+
const a = e.match(s);
|
|
513
557
|
if (a && a[1])
|
|
514
|
-
return { type:
|
|
558
|
+
return { type: t, id: a[1] };
|
|
515
559
|
}
|
|
516
560
|
return null;
|
|
517
561
|
}
|
|
518
|
-
async function
|
|
562
|
+
async function Y(e, n, s) {
|
|
519
563
|
try {
|
|
520
564
|
switch (s.type) {
|
|
521
565
|
case "resource_server": {
|
|
522
|
-
const
|
|
523
|
-
return (
|
|
566
|
+
const t = await e.resourceServers.get(n, s.id);
|
|
567
|
+
return (t == null ? void 0 : t.is_system) === !0;
|
|
524
568
|
}
|
|
525
569
|
case "role": {
|
|
526
|
-
const
|
|
527
|
-
return (
|
|
570
|
+
const t = await e.roles.get(n, s.id);
|
|
571
|
+
return (t == null ? void 0 : t.is_system) === !0;
|
|
528
572
|
}
|
|
529
573
|
case "connection": {
|
|
530
|
-
const
|
|
531
|
-
return (
|
|
574
|
+
const t = await e.connections.get(n, s.id);
|
|
575
|
+
return (t == null ? void 0 : t.is_system) === !0;
|
|
532
576
|
}
|
|
533
577
|
default:
|
|
534
578
|
return !1;
|
|
@@ -537,100 +581,100 @@ async function B(t, e, s) {
|
|
|
537
581
|
return !1;
|
|
538
582
|
}
|
|
539
583
|
}
|
|
540
|
-
function
|
|
584
|
+
function B(e) {
|
|
541
585
|
return {
|
|
542
586
|
resource_server: "resource server",
|
|
543
587
|
role: "role",
|
|
544
588
|
connection: "connection"
|
|
545
|
-
}[
|
|
589
|
+
}[e];
|
|
546
590
|
}
|
|
547
|
-
function
|
|
548
|
-
return async (
|
|
549
|
-
if (!["PATCH", "PUT", "DELETE"].includes(
|
|
550
|
-
return
|
|
551
|
-
const s =
|
|
591
|
+
function Z() {
|
|
592
|
+
return async (e, n) => {
|
|
593
|
+
if (!["PATCH", "PUT", "DELETE"].includes(e.req.method))
|
|
594
|
+
return n();
|
|
595
|
+
const s = L(e.req.path);
|
|
552
596
|
if (!s)
|
|
553
|
-
return
|
|
554
|
-
const
|
|
555
|
-
if (!
|
|
556
|
-
return
|
|
557
|
-
if (await
|
|
597
|
+
return n();
|
|
598
|
+
const t = e.var.tenant_id || e.req.header("x-tenant-id") || e.req.header("tenant-id");
|
|
599
|
+
if (!t)
|
|
600
|
+
return n();
|
|
601
|
+
if (await Y(e.env.data, t, s))
|
|
558
602
|
throw new p(403, {
|
|
559
|
-
message: `This ${
|
|
603
|
+
message: `This ${B(s.type)} is a system resource and cannot be modified. Make changes in the main tenant instead.`
|
|
560
604
|
});
|
|
561
|
-
return
|
|
605
|
+
return n();
|
|
562
606
|
};
|
|
563
607
|
}
|
|
564
|
-
function
|
|
565
|
-
return async (
|
|
566
|
-
if (!
|
|
608
|
+
function J(e) {
|
|
609
|
+
return async (n, s) => {
|
|
610
|
+
if (!e.accessControl)
|
|
567
611
|
return s();
|
|
568
|
-
const
|
|
569
|
-
if (!
|
|
612
|
+
const t = n.var.tenant_id, a = n.var.organization_id;
|
|
613
|
+
if (!t)
|
|
570
614
|
throw new p(400, {
|
|
571
615
|
message: "Tenant ID not found in request"
|
|
572
616
|
});
|
|
573
617
|
if (!E(
|
|
574
618
|
a,
|
|
575
|
-
|
|
576
|
-
|
|
619
|
+
t,
|
|
620
|
+
e.accessControl.mainTenantId
|
|
577
621
|
))
|
|
578
622
|
throw new p(403, {
|
|
579
|
-
message: `Access denied to tenant ${
|
|
623
|
+
message: `Access denied to tenant ${t}`
|
|
580
624
|
});
|
|
581
625
|
return s();
|
|
582
626
|
};
|
|
583
627
|
}
|
|
584
|
-
function
|
|
585
|
-
return async (
|
|
586
|
-
if (!
|
|
628
|
+
function X(e) {
|
|
629
|
+
return async (n, s) => {
|
|
630
|
+
if (!e.subdomainRouting)
|
|
587
631
|
return s();
|
|
588
632
|
const {
|
|
589
|
-
baseDomain:
|
|
633
|
+
baseDomain: t,
|
|
590
634
|
reservedSubdomains: a = [],
|
|
591
|
-
resolveSubdomain:
|
|
592
|
-
} =
|
|
593
|
-
let o = null;
|
|
594
|
-
if (d.endsWith(n)) {
|
|
595
|
-
const r = d.slice(0, -(n.length + 1));
|
|
596
|
-
r && !r.includes(".") && (o = r);
|
|
597
|
-
}
|
|
598
|
-
if (o && a.includes(o) && (o = null), !o)
|
|
599
|
-
return t.accessControl && e.set("tenant_id", t.accessControl.mainTenantId), s();
|
|
635
|
+
resolveSubdomain: o
|
|
636
|
+
} = e.subdomainRouting, d = n.req.header("host") || "";
|
|
600
637
|
let i = null;
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
638
|
+
if (d.endsWith(t)) {
|
|
639
|
+
const r = d.slice(0, -(t.length + 1));
|
|
640
|
+
r && !r.includes(".") && (i = r);
|
|
641
|
+
}
|
|
642
|
+
if (i && a.includes(i) && (i = null), !i)
|
|
643
|
+
return e.accessControl && n.set("tenant_id", e.accessControl.mainTenantId), s();
|
|
644
|
+
let c = null;
|
|
645
|
+
if (o)
|
|
646
|
+
c = await o(i);
|
|
647
|
+
else if (e.subdomainRouting.useOrganizations !== !1 && e.accessControl)
|
|
604
648
|
try {
|
|
605
|
-
const r = await
|
|
606
|
-
|
|
607
|
-
|
|
649
|
+
const r = await n.env.data.organizations.get(
|
|
650
|
+
e.accessControl.mainTenantId,
|
|
651
|
+
i
|
|
608
652
|
);
|
|
609
|
-
r && (
|
|
653
|
+
r && (c = r.id);
|
|
610
654
|
} catch {
|
|
611
655
|
}
|
|
612
|
-
if (!
|
|
656
|
+
if (!c)
|
|
613
657
|
throw new p(404, {
|
|
614
|
-
message: `Tenant not found for subdomain: ${
|
|
658
|
+
message: `Tenant not found for subdomain: ${i}`
|
|
615
659
|
});
|
|
616
|
-
return
|
|
660
|
+
return n.set("tenant_id", c), s();
|
|
617
661
|
};
|
|
618
662
|
}
|
|
619
|
-
function
|
|
620
|
-
return async (
|
|
621
|
-
if (!
|
|
663
|
+
function H(e) {
|
|
664
|
+
return async (n, s) => {
|
|
665
|
+
if (!e.databaseIsolation)
|
|
622
666
|
return s();
|
|
623
|
-
const
|
|
624
|
-
if (!
|
|
667
|
+
const t = n.var.tenant_id;
|
|
668
|
+
if (!t)
|
|
625
669
|
throw new p(400, {
|
|
626
670
|
message: "Tenant ID not found in request"
|
|
627
671
|
});
|
|
628
672
|
try {
|
|
629
|
-
const a = await
|
|
630
|
-
|
|
673
|
+
const a = await e.databaseIsolation.getAdapters(t);
|
|
674
|
+
n.env.data = a;
|
|
631
675
|
} catch (a) {
|
|
632
676
|
throw console.error(
|
|
633
|
-
`Failed to resolve database for tenant ${
|
|
677
|
+
`Failed to resolve database for tenant ${t}:`,
|
|
634
678
|
a
|
|
635
679
|
), new p(500, {
|
|
636
680
|
message: "Failed to resolve tenant database"
|
|
@@ -639,188 +683,188 @@ function k(t) {
|
|
|
639
683
|
return s();
|
|
640
684
|
};
|
|
641
685
|
}
|
|
642
|
-
function
|
|
643
|
-
const
|
|
644
|
-
return async (a,
|
|
686
|
+
function M(e) {
|
|
687
|
+
const n = X(e), s = J(e), t = H(e);
|
|
688
|
+
return async (a, o) => (await n(a, async () => {
|
|
645
689
|
}), await s(a, async () => {
|
|
646
|
-
}), await
|
|
647
|
-
}),
|
|
690
|
+
}), await t(a, async () => {
|
|
691
|
+
}), o());
|
|
648
692
|
}
|
|
649
|
-
function re(
|
|
650
|
-
const
|
|
693
|
+
function re(e) {
|
|
694
|
+
const n = A(e);
|
|
651
695
|
return {
|
|
652
696
|
name: "multi-tenancy",
|
|
653
697
|
// Apply multi-tenancy middleware for subdomain routing, database resolution, etc.
|
|
654
|
-
middleware:
|
|
698
|
+
middleware: M(e),
|
|
655
699
|
// Provide lifecycle hooks
|
|
656
|
-
hooks:
|
|
700
|
+
hooks: n,
|
|
657
701
|
// Mount tenant management routes
|
|
658
702
|
routes: [
|
|
659
703
|
{
|
|
660
704
|
path: "/management",
|
|
661
|
-
handler: S(
|
|
705
|
+
handler: S(e, n)
|
|
662
706
|
}
|
|
663
707
|
],
|
|
664
708
|
// Called when plugin is registered
|
|
665
709
|
onRegister: async () => {
|
|
666
|
-
console.log("Multi-tenancy plugin registered"),
|
|
667
|
-
` - Access control enabled (main tenant: ${
|
|
668
|
-
),
|
|
669
|
-
` - Subdomain routing enabled (base domain: ${
|
|
670
|
-
),
|
|
710
|
+
console.log("Multi-tenancy plugin registered"), e.accessControl && console.log(
|
|
711
|
+
` - Access control enabled (main tenant: ${e.accessControl.mainTenantId})`
|
|
712
|
+
), e.subdomainRouting && console.log(
|
|
713
|
+
` - Subdomain routing enabled (base domain: ${e.subdomainRouting.baseDomain})`
|
|
714
|
+
), e.databaseIsolation && console.log(" - Database isolation enabled");
|
|
671
715
|
}
|
|
672
716
|
};
|
|
673
717
|
}
|
|
674
|
-
function
|
|
675
|
-
const
|
|
718
|
+
function A(e) {
|
|
719
|
+
const n = e.accessControl ? j(e.accessControl) : {}, s = e.databaseIsolation ? K(e.databaseIsolation) : {}, t = N(e);
|
|
676
720
|
return {
|
|
677
|
-
...
|
|
721
|
+
...n,
|
|
678
722
|
...s,
|
|
679
|
-
tenants:
|
|
723
|
+
tenants: t
|
|
680
724
|
};
|
|
681
725
|
}
|
|
682
|
-
function x(
|
|
683
|
-
const
|
|
684
|
-
return
|
|
726
|
+
function x(e) {
|
|
727
|
+
const n = new $(), s = A(e);
|
|
728
|
+
return n.route("/tenants", S(e, s)), n;
|
|
685
729
|
}
|
|
686
|
-
function ie(
|
|
730
|
+
function ie(e) {
|
|
687
731
|
return {
|
|
688
|
-
hooks:
|
|
689
|
-
middleware:
|
|
690
|
-
app: x(
|
|
691
|
-
config:
|
|
732
|
+
hooks: A(e),
|
|
733
|
+
middleware: M(e),
|
|
734
|
+
app: x(e),
|
|
735
|
+
config: e
|
|
692
736
|
};
|
|
693
737
|
}
|
|
694
|
-
function oe(
|
|
738
|
+
function oe(e) {
|
|
695
739
|
const {
|
|
696
|
-
mainTenantId:
|
|
740
|
+
mainTenantId: n = "main",
|
|
697
741
|
syncResourceServers: s = !0,
|
|
698
|
-
multiTenancy:
|
|
742
|
+
multiTenancy: t,
|
|
699
743
|
entityHooks: a,
|
|
700
|
-
...
|
|
701
|
-
} =
|
|
702
|
-
...
|
|
744
|
+
...o
|
|
745
|
+
} = e, d = {
|
|
746
|
+
...t,
|
|
703
747
|
accessControl: {
|
|
704
|
-
mainTenantId:
|
|
748
|
+
mainTenantId: n,
|
|
705
749
|
requireOrganizationMatch: !1,
|
|
706
750
|
defaultPermissions: ["tenant:admin"],
|
|
707
|
-
...
|
|
751
|
+
...t == null ? void 0 : t.accessControl
|
|
708
752
|
}
|
|
709
|
-
},
|
|
710
|
-
let
|
|
711
|
-
s && (
|
|
712
|
-
mainTenantId:
|
|
713
|
-
getChildTenantIds: async () => (await
|
|
714
|
-
(
|
|
753
|
+
}, i = A(d);
|
|
754
|
+
let c, r;
|
|
755
|
+
s && (c = V({
|
|
756
|
+
mainTenantId: n,
|
|
757
|
+
getChildTenantIds: async () => (await R(
|
|
758
|
+
(h) => e.dataAdapter.tenants.list(h),
|
|
715
759
|
"tenants",
|
|
716
760
|
{ cursorField: "id", pageSize: 100 }
|
|
717
|
-
)).filter((
|
|
718
|
-
getAdapters: async (
|
|
719
|
-
}), r =
|
|
720
|
-
mainTenantId:
|
|
721
|
-
getMainTenantAdapters: async () =>
|
|
722
|
-
getAdapters: async (
|
|
761
|
+
)).filter((h) => h.id !== n).map((h) => h.id),
|
|
762
|
+
getAdapters: async (y) => e.dataAdapter
|
|
763
|
+
}), r = G({
|
|
764
|
+
mainTenantId: n,
|
|
765
|
+
getMainTenantAdapters: async () => e.dataAdapter,
|
|
766
|
+
getAdapters: async (y) => e.dataAdapter
|
|
723
767
|
}));
|
|
724
|
-
const l = async (
|
|
725
|
-
const
|
|
726
|
-
if (
|
|
768
|
+
const l = async (y, h, ...v) => {
|
|
769
|
+
const T = [];
|
|
770
|
+
if (y)
|
|
727
771
|
try {
|
|
728
|
-
await
|
|
729
|
-
} catch (
|
|
730
|
-
|
|
772
|
+
await y(...v);
|
|
773
|
+
} catch (b) {
|
|
774
|
+
T.push(b instanceof Error ? b : new Error(String(b)));
|
|
731
775
|
}
|
|
732
|
-
if (
|
|
776
|
+
if (h)
|
|
733
777
|
try {
|
|
734
|
-
await
|
|
735
|
-
} catch (
|
|
736
|
-
|
|
778
|
+
await h(...v);
|
|
779
|
+
} catch (b) {
|
|
780
|
+
T.push(b instanceof Error ? b : new Error(String(b)));
|
|
737
781
|
}
|
|
738
|
-
if (
|
|
739
|
-
throw
|
|
740
|
-
if (
|
|
782
|
+
if (T.length === 1)
|
|
783
|
+
throw T[0];
|
|
784
|
+
if (T.length > 1)
|
|
741
785
|
throw new AggregateError(
|
|
742
|
-
|
|
743
|
-
`Multiple hook errors: ${
|
|
786
|
+
T,
|
|
787
|
+
`Multiple hook errors: ${T.map((b) => b.message).join("; ")}`
|
|
744
788
|
);
|
|
745
789
|
}, f = {
|
|
746
790
|
...a,
|
|
747
|
-
resourceServers:
|
|
791
|
+
resourceServers: c ? {
|
|
748
792
|
...a == null ? void 0 : a.resourceServers,
|
|
749
|
-
afterCreate: async (
|
|
750
|
-
var
|
|
793
|
+
afterCreate: async (y, h) => {
|
|
794
|
+
var v;
|
|
751
795
|
await l(
|
|
752
|
-
(
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
796
|
+
(v = a == null ? void 0 : a.resourceServers) == null ? void 0 : v.afterCreate,
|
|
797
|
+
c == null ? void 0 : c.afterCreate,
|
|
798
|
+
y,
|
|
799
|
+
h
|
|
756
800
|
);
|
|
757
801
|
},
|
|
758
|
-
afterUpdate: async (
|
|
759
|
-
var
|
|
802
|
+
afterUpdate: async (y, h, v) => {
|
|
803
|
+
var T;
|
|
760
804
|
await l(
|
|
761
|
-
(
|
|
762
|
-
|
|
763
|
-
g,
|
|
805
|
+
(T = a == null ? void 0 : a.resourceServers) == null ? void 0 : T.afterUpdate,
|
|
806
|
+
c == null ? void 0 : c.afterUpdate,
|
|
764
807
|
y,
|
|
765
|
-
h
|
|
808
|
+
h,
|
|
809
|
+
v
|
|
766
810
|
);
|
|
767
811
|
},
|
|
768
|
-
afterDelete: async (
|
|
769
|
-
var
|
|
812
|
+
afterDelete: async (y, h) => {
|
|
813
|
+
var v;
|
|
770
814
|
await l(
|
|
771
|
-
(
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
815
|
+
(v = a == null ? void 0 : a.resourceServers) == null ? void 0 : v.afterDelete,
|
|
816
|
+
c == null ? void 0 : c.afterDelete,
|
|
817
|
+
y,
|
|
818
|
+
h
|
|
775
819
|
);
|
|
776
820
|
}
|
|
777
821
|
} : a == null ? void 0 : a.resourceServers,
|
|
778
822
|
tenants: r ? {
|
|
779
823
|
...a == null ? void 0 : a.tenants,
|
|
780
|
-
afterCreate: async (
|
|
781
|
-
var
|
|
824
|
+
afterCreate: async (y, h) => {
|
|
825
|
+
var v;
|
|
782
826
|
await l(
|
|
783
|
-
(
|
|
827
|
+
(v = a == null ? void 0 : a.tenants) == null ? void 0 : v.afterCreate,
|
|
784
828
|
r == null ? void 0 : r.afterCreate,
|
|
785
|
-
|
|
786
|
-
|
|
829
|
+
y,
|
|
830
|
+
h
|
|
787
831
|
);
|
|
788
832
|
}
|
|
789
833
|
} : a == null ? void 0 : a.tenants
|
|
790
|
-
}, u =
|
|
791
|
-
...
|
|
834
|
+
}, u = F({
|
|
835
|
+
...o,
|
|
792
836
|
entityHooks: f
|
|
793
|
-
}), { app: m, managementApp:
|
|
794
|
-
w.use("/api/v2/*",
|
|
795
|
-
const
|
|
837
|
+
}), { app: m, managementApp: _, ...g } = u, w = new $();
|
|
838
|
+
w.use("/api/v2/*", Z()), w.route("/", m);
|
|
839
|
+
const C = S(
|
|
796
840
|
d,
|
|
797
|
-
|
|
841
|
+
i
|
|
798
842
|
);
|
|
799
|
-
return w.route("/api/v2/tenants",
|
|
843
|
+
return w.route("/api/v2/tenants", C), {
|
|
800
844
|
app: w,
|
|
801
|
-
managementApp:
|
|
802
|
-
...
|
|
845
|
+
managementApp: _,
|
|
846
|
+
...g,
|
|
803
847
|
multiTenancyConfig: d,
|
|
804
|
-
multiTenancyHooks:
|
|
848
|
+
multiTenancyHooks: i
|
|
805
849
|
};
|
|
806
850
|
}
|
|
807
851
|
export {
|
|
808
852
|
le as MANAGEMENT_API_SCOPES,
|
|
809
|
-
|
|
810
|
-
|
|
853
|
+
j as createAccessControlHooks,
|
|
854
|
+
J as createAccessControlMiddleware,
|
|
811
855
|
K as createDatabaseHooks,
|
|
812
|
-
|
|
856
|
+
H as createDatabaseMiddleware,
|
|
813
857
|
x as createMultiTenancy,
|
|
814
|
-
|
|
815
|
-
|
|
858
|
+
A as createMultiTenancyHooks,
|
|
859
|
+
M as createMultiTenancyMiddleware,
|
|
816
860
|
re as createMultiTenancyPlugin,
|
|
817
|
-
|
|
861
|
+
Z as createProtectSyncedMiddleware,
|
|
818
862
|
N as createProvisioningHooks,
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
863
|
+
V as createResourceServerSyncHooks,
|
|
864
|
+
X as createSubdomainMiddleware,
|
|
865
|
+
G as createTenantResourceServerSyncHooks,
|
|
822
866
|
S as createTenantsRouter,
|
|
823
|
-
|
|
867
|
+
R as fetchAll,
|
|
824
868
|
oe as init,
|
|
825
869
|
ue as seed,
|
|
826
870
|
ie as setupMultiTenancy,
|
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.6.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"
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"typescript": "^5.6.0",
|
|
38
38
|
"vite": "^6.0.0",
|
|
39
39
|
"vitest": "^2.1.0",
|
|
40
|
-
"@authhero/kysely-adapter": "10.
|
|
40
|
+
"@authhero/kysely-adapter": "10.72.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"zod": "^3.24.0",
|
|
44
44
|
"@authhero/adapter-interfaces": "0.112.0",
|
|
45
|
-
"authhero": "0.
|
|
45
|
+
"authhero": "0.304.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@hono/zod-openapi": "^0.19.10",
|