@aiquants/auth-directory-drizzle 0.4.0 → 0.4.1
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var F=Object.defineProperty;var ue=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var ce=(t,o)=>{for(var h in o)F(t,h,{get:o[h],enumerable:!0})},me=(t,o,h,b)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of le(o))!pe.call(t,s)&&s!==h&&F(t,s,{get:()=>o[s],enumerable:!(b=ue(o,s))||b.enumerable});return t};var ye=t=>me(F({},"__esModule",{value:!0}),t);var Me={};ce(Me,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>se,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>ve,MAX_IN_LIST:()=>ze,MAX_PARAMETERS:()=>ie,MEMBERSHIP_MODES:()=>Y,UNHEALTHY_SYNC_OUTCOMES:()=>Z,assertGroupMayBeLinked:()=>be,assertMembershipMode:()=>K,chunk:()=>L,chunkByParameters:()=>N,createDirectoryLinkStore:()=>we,createDirectorySync:()=>De,createDirectorySyncRepository:()=>Ee,createPromotionRepository:()=>Ae,createSyncRequestQueue:()=>xe,defaultExternalGroupGuardMessage:()=>ge,defaultSyncMessages:()=>Q,defineAuthDirectorySchema:()=>Te,findLedgerRowsForIdentity:()=>q,promoteLedgerForUser:()=>fe,withExternalGroupGuards:()=>he});module.exports=ye(Me);var te=require("@aiquants/authz-core"),ge=(t,o)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${o}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${t}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function H(t,o,h,b){return new Set(t.filter(s=>s.roleId===o&&s.appKey===h&&s.resourceKey===b).map(s=>s.action))}var J=t=>te.AUTHZ_ACTIONS.every(o=>t.has(o)),V=(t,o)=>t.find(h=>h.roleId===o)?.roleKey??`#${o}`;function he(t,o){let h=t;async function b(s,a,p,y,w){if(y.size===0)return;let n=await s.listGroupRoles(p),g=new Set;for(let e of n){if(!y.has(e.roleId)||g.has(e.groupId))continue;g.add(e.groupId);let l=await o.linkLookup.externalGroupKey(p,e.groupId,a);if(l!==null)throw new Error(o.message(e.roleKey||V(w,e.roleId),l))}}return{...t,async assignGroupRole(s,a,p,y){await h.withTransaction(async(w,n)=>{let g=await w.listGrants(s);if(J(H(g,p,o.adminAppKey,o.adminResourceKey))){let e=await o.linkLookup.externalGroupKey(s,a,n);if(e!==null)throw new Error(o.message(V(g,p),e))}await w.assignGroupRole(s,a,p,y)})},async createGrant(s,a,p){await h.withTransaction(async(y,w)=>{let g=(await y.listResources(s)).find(e=>e.id===a.resourceId);if(g?.appKey===o.adminAppKey&&g?.resourceKey===o.adminResourceKey){let e=await y.listGrants(s),l=H(e,a.roleId,o.adminAppKey,o.adminResourceKey);l.add(a.action),J(l)&&await b(y,w,s,new Set([a.roleId]),e)}await y.createGrant(s,a,p)})}}}async function be(t,o,h,b){let s=(await t.listGroupRoles(o)).filter(p=>p.groupId===h);if(s.length===0)return;let a=await t.listGrants(o);for(let p of s)if(J(H(a,p.roleId,b.adminAppKey,b.adminResourceKey)))throw new Error(b.message(p.roleKey||V(a,p.roleId),p.groupKey))}var $=require("drizzle-orm");async function q(t,o,h){let{ledger:b,group:s}=o,{tenantId:a,externalUserId:p,email:y}=h;if(p===null&&y==="")return[];let w={ledgerId:b.id,groupId:b.groupId,tenantId:s.tenantId,externalUserId:b.externalUserId},n=async d=>(await t.select(w).from(b).innerJoin(s,(0,$.and)((0,$.eq)(s.id,b.groupId),(0,$.eq)(s.tenantId,a),(0,$.eq)(s.isActive,!0))).where(d)).map(I=>({ledgerId:Number(I.ledgerId),groupId:Number(I.groupId),tenantId:String(I.tenantId),externalUserId:I.externalUserId===null?null:String(I.externalUserId)})),g=d=>{let v=new Map;for(let I of d)v.set(I.ledgerId,{ledgerId:I.ledgerId,groupId:I.groupId,tenantId:I.tenantId});return[...v.values()].sort((I,T)=>I.ledgerId-T.ledgerId)},e=p===null?[]:await n((0,$.eq)(b.externalUserId,p));if(y==="")return g(e);let l=await n((0,$.eq)(b.email,y)),S=d=>p!==null&&d.externalUserId!==null&&d.externalUserId!==p;return g([...e,...l.filter(d=>!S(d))])}var O=require("@aiquants/auth-core"),ne=require("@aiquants/auth-directory-core"),x=require("@aiquants/authz-core"),u=require("drizzle-orm");var Y=["direct","transitive"];function K(t){if(!Y.includes(t))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${Y.join(" | ")}, received ${JSON.stringify(t)}`);return t}function we(t,o,h,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.policyCacheTtlMs??0;if(!Number.isFinite(p)||p<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(b.policyCacheTtlMs)}`);let y=new Map,w=r=>y.delete(r),{TDGroupExternalLink:n,TDGroupMemberEmail:g,TDTenantMembershipGroup:e,TDAllowlistGroup:l,TDDirectorySyncRequest:S}=o,{TMGroup:d,TDGroupMember:v}=h;async function I(r,i,m){if((await r.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,i),(0,u.eq)(e.groupId,m),(0,u.eq)(e.isBreakGlass,!0)))).length>0)throw new Error(`Group ${m} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function T(r,i,m){let[f]=await r.select({id:d.id}).from(d).where((0,u.and)((0,u.eq)(d.id,m),(0,u.eq)(d.tenantId,i)));if(!f)throw new Error(`Group not found in tenant: ${m}`)}function G(r,i){return{linkId:Number(r.linkId),groupId:Number(r.groupId),tenantId:String(r.tenantId),provider:String(r.provider),externalId:String(r.externalId),externalKey:String(r.externalKey),membershipMode:K(String(r.membershipMode)),isSyncEnabled:!!r.isSyncEnabled,...r.syncedAt?{syncedAt:new Date(r.syncedAt).toISOString()}:{},...r.syncError?{syncError:String(r.syncError)}:{},unresolvedCount:i}}async function _(r,i){let m=await r.select({groupId:g.groupId,n:(0,u.count)()}).from(g).innerJoin(d,(0,u.and)((0,u.eq)(d.id,g.groupId),(0,u.eq)(d.tenantId,i))).where((0,u.isNull)(g.resolvedUserId)).groupBy(g.groupId);return new Map(m.map(f=>[Number(f.groupId),Number(f.n)]))}return{async listLinks(r){(0,x.assertTenantId)(r,"listLinks(tenantId)"),await a();let i=await s.select({linkId:n.id,groupId:n.groupId,tenantId:n.tenantId,provider:n.provider,externalId:n.externalId,externalKey:n.externalKey,membershipMode:n.membershipMode,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)).orderBy((0,u.asc)(n.groupId)),m=await _(s,r);return i.map(f=>((0,x.assertTenantMatch)(r,String(f.tenantId),"listLinks(row.tenantId)"),G(f,m.get(Number(f.groupId))??0)))},async externalGroupKey(r,i,m){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await a();let[f]=await(m??s).select({externalKey:n.externalKey}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));return f?String(f.externalKey):null},async linkGroup(r,i,m,f){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let D=K(m.membershipMode),k=m.externalId.trim(),E=(0,O.normalizeAuthEmail)(m.externalKey);if(k===""||E==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await a(),await b.linkable.assertLinkable(r,i),await s.transaction(async z=>{await T(z,r,i),await I(z,r,i),await z.insert(n).output().values({groupId:i,tenantId:r,provider:m.provider,externalId:k,externalKey:E,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:f})}),w(r)},async unlinkGroup(r,i){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await a(),await s.transaction(async m=>{await T(m,r,i),await m.delete(g).where((0,u.eq)(g.groupId,i)),await m.delete(l).where((0,u.and)((0,u.eq)(l.groupId,i),(0,u.eq)(l.tenantId,r))),await m.delete(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))}),w(r)},async setSyncEnabled(r,i,m,f){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await a(),await s.update(n).set({isSyncEnabled:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))},async requestSync(r,i,m){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await a(),await s.transaction(async f=>{i!==null&&await T(f,r,i),await f.insert(S).output().values({tenantId:r,groupId:i,requestedBy:m,requestedAt:new Date,createdAt:new Date,createdBy:m})})},async getStatus(r){(0,x.assertTenantId)(r,"getStatus(tenantId)"),await a();let i=await s.select({groupId:n.groupId,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)),[m]=await s.select({n:(0,u.count)()}).from(S).where((0,u.and)((0,u.eq)(S.tenantId,r),(0,u.isNull)(S.finishedAt))),f=i.filter(R=>!!R.isSyncEnabled),D=f.map(R=>R.syncedAt?new Date(R.syncedAt).getTime():null),k=D.length>0&&D.every(R=>R!==null)?Math.min(...D):null,z=i.length>0&&f.length===0||f.length>0&&(k===null||Date.now()-k>b.stalenessThresholdSec*1e3);return{isEnabled:b.isEnabled,linkedGroupCount:i.length,...k!==null?{oldestSyncedAt:new Date(k).toISOString()}:{},isStale:z,pausedGroupCount:i.length-f.length,stalenessThresholdSec:b.stalenessThresholdSec,erroredGroupIds:i.filter(R=>!!R.syncError).map(R=>Number(R.groupId)).sort((R,de)=>R-de),pendingRequestCount:Number(m?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)");let i=p>0?y.get(r):void 0;if(i&&Date.now()-i.loadedAt<p)return i.policy;await a();let m=await s.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass,externalLinkId:n.id}).from(e).innerJoin(d,(0,u.and)((0,u.eq)(d.id,e.groupId),(0,u.eq)(d.tenantId,r))).leftJoin(n,(0,u.eq)(n.groupId,e.groupId)).where((0,u.eq)(e.tenantId,r));if(m.length===0){let E={kind:"no-proof-required"};return p>0&&y.set(r,{policy:E,loadedAt:Date.now()}),E}let f=m.map(E=>Number(E.groupId)),D=m.filter(E=>!!E.isBreakGlass);for(let E of D)if(E.externalLinkId!==null)throw new Error(`Break-glass group ${Number(E.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let k={kind:"require-proof",proofGroupIds:f,breakGlassGroupIds:D.map(E=>Number(E.groupId))};return(0,ne.assertTenantMembershipPolicy)(k,`tenant ${r}`),p>0&&y.set(r,{policy:k,loadedAt:Date.now()}),k},async setTenantMembershipGroup(r,i,m,f){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async D=>{if(await T(D,r,i),m){let[E]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.tenantId,r),(0,u.eq)(n.groupId,i)));if(E)throw new Error(`Group ${i} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}if((await D.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))).length>0){await D.update(e).set({isBreakGlass:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i))),w(r);return}await D.insert(e).output().values({tenantId:r,groupId:i,isBreakGlass:m,createdAt:new Date,createdBy:f})}),w(r)},async removeTenantMembershipGroup(r,i){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async m=>{let D=(await m.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass}).from(e).where((0,u.eq)(e.tenantId,r))).filter(k=>Number(k.groupId)!==i);if(D.length>0&&!D.some(k=>!!k.isBreakGlass))throw new Error(`Removing group ${i} would leave tenant ${r} with a membership proof but no break-glass path.`);await m.delete(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))}),w(r)},async setGroupActive(r,i,m,f){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await a(),await s.update(d).set({isActive:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(d.id,i),(0,u.eq)(d.tenantId,r)))},async listLedgerGroupIds(r,i){if((0,x.assertTenantId)(r,"listLedgerGroupIds(tenantId)"),!b.isEnabled)return[];await a();let m=await q(s,{ledger:g,group:d},{tenantId:r,externalUserId:i.externalUserId?.trim()||null,email:(0,O.normalizeAuthEmail)(i.email)});return[...new Set(m.map(f=>f.groupId))].sort((f,D)=>f-D)},async isEmailAllowedByGroup(r){if(!b.isEnabled)return!1;let i=(0,O.normalizeAuthEmail)(r);if(i==="")return!1;await a();let[m]=await s.select({id:g.id}).from(g).innerJoin(l,(0,u.eq)(l.groupId,g.groupId)).innerJoin(d,(0,u.and)((0,u.eq)(d.id,g.groupId),(0,u.eq)(d.isActive,!0))).where((0,u.eq)(g.email,i));return!!m},async listAllowlistGroups(r){return(0,x.assertTenantId)(r,"listAllowlistGroups(tenantId)"),await a(),(await s.select({id:l.id,groupId:l.groupId,groupKey:d.groupKey,groupName:d.name,description:l.description}).from(l).innerJoin(d,(0,u.and)((0,u.eq)(d.id,l.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(l.tenantId,r)).orderBy((0,u.asc)(d.name))).map(m=>({id:Number(m.id),type:"group",groupId:Number(m.groupId),groupKey:String(m.groupKey),groupName:String(m.groupName),...m.description?{description:String(m.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,i,m,f){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await a(),await s.transaction(async D=>{await T(D,r,i);let[k]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));if(!k)throw new Error(`Group ${i} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await D.insert(l).output().values({tenantId:r,groupId:i,description:m??null,createdAt:new Date,createdBy:f})})},async removeAllowlistGroup(r,i){(0,x.assertTenantId)(r,"removeAllowlistGroup(tenantId)"),await a(),await s.delete(l).where((0,u.and)((0,u.eq)(l.id,i),(0,u.eq)(l.tenantId,r)))},async countProjectedMembers(r,i){(0,x.assertTenantId)(r,"countProjectedMembers(tenantId)"),await a();let[m]=await s.select({n:(0,u.count)()}).from(v).innerJoin(d,(0,u.and)((0,u.eq)(d.id,v.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(v.groupId,i));return Number(m?.n??0)}}}var oe=require("@aiquants/auth-core"),C=require("@aiquants/authz-core");async function fe(t,o){let h=(0,C.assertTenantId)(o.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(o.userId)||o.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(o.userId)}`);let b=(0,oe.normalizeAuthEmail)(o.email),s=o.externalUserId?.trim()||null;if(s===null&&b==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:h,externalUserId:s,email:b});if(a.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let n of a)(0,C.assertTenantMatch)(h,n.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let p=[...new Set(a.map(n=>n.ledgerId))].sort((n,g)=>n-g),y=[...new Set(a.map(n=>n.groupId))].sort((n,g)=>n-g);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:h,userId:o.userId,ledgerIds:p,groupIds:y})].sort((n,g)=>n-g),matchedLedgerIds:p}}var W=require("@aiquants/authz-core"),M=require("drizzle-orm");var ie=2100;var ze=500;function L(t,o=500){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] chunk size must be a positive integer, received ${String(o)}`);let h=[];for(let b=0;b<t.length;b+=o)h.push(t.slice(b,b+o));return h}function N(t,o,h=100){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] parametersPerItem must be a positive integer, received ${String(o)}`);if(!Number.isInteger(h)||h<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(h)}`);let b=2100-h,s=Math.floor(b/o);if(s<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${b} available in one statement`);return L(t,s)}var Ie=4,Se=100;function Ae(t,o,h,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.now??(()=>new Date),y=o.TDGroupMemberEmail,{TMGroup:w,TDGroupMember:n}=h;return{async findLedgerMatches(g){return(0,W.assertTenantId)(g.tenantId,"findLedgerMatches(tenantId)"),await a(),q(s,{ledger:y,group:w},g)},async attachUserToGroups({tenantId:g,userId:e,ledgerIds:l,groupIds:S}){return(0,W.assertTenantId)(g,"attachUserToGroups(tenantId)"),await a(),l.length===0?[]:s.transaction(async d=>{let v=new Set;for(let r of L(S)){let i=await d.select({id:w.id}).from(w).where((0,M.and)((0,M.inArray)(w.id,r),(0,M.eq)(w.tenantId,g),(0,M.eq)(w.isActive,!0)));for(let m of i)v.add(Number(m.id))}let I=S.filter(r=>v.has(r));if(I.length===0)return[];let T=new Set;for(let r of L(I)){let i=await d.select({groupId:n.groupId}).from(n).where((0,M.and)((0,M.eq)(n.userId,e),(0,M.inArray)(n.groupId,r)));for(let m of i)T.add(Number(m.groupId))}let G=I.filter(r=>!T.has(r)),_=Math.max(1,2100-Se-I.length);for(let r of L(l,_))await d.update(y).set({resolvedUserId:e}).output().where((0,M.and)((0,M.inArray)(y.id,r),(0,M.inArray)(y.groupId,I)));if(G.length>0){let r=p();for(let i of N(G,Ie))await d.insert(n).output().values(i.map(m=>({groupId:m,userId:e,createdAt:r,createdBy:b.actor})))}return G})}}}var U=require("@aiquants/authz-drizzle"),c=require("drizzle-orm/mssql-core"),se="provider",ve=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TMAllowlist"];function Te(t,o){let h=(0,c.mssqlSchema)(t),b=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50}),updatedAt:(0,c.datetime2)("updated_at"),updatedBy:(0,c.nvarchar)("updated_by",{length:50})}),s=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50})}),a=h.table("TDGroupExternalLink",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,c.nvarchar)(se,{length:32}).notNull(),externalId:(0,c.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,c.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,c.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,c.bit)("is_sync_enabled").notNull(),syncedAt:(0,c.datetime2)("synced_at"),syncError:(0,c.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,c.int)("vanished_observations").notNull(),...b()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupExternalLink_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_group_uq`).on(e.groupId),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_external_uq`).on(e.tenantId,e.provider,e.externalId),(0,c.foreignKey)({name:`${t}_TDGroupExternalLink_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),p=h.table("TDGroupMemberEmail",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),email:(0,c.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,c.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,c.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,c.datetime2)("observed_at").notNull(),...b()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupMemberEmail_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupMemberEmail_group_email_uq`).on(e.groupId,e.email),(0,c.foreignKey)({name:`${t}_TDGroupMemberEmail_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),y=h.table("TDTenantMembershipGroup",{tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,c.bit)("is_break_glass").notNull(),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDTenantMembershipGroup_pk`,columns:[e.tenantId,e.groupId]}),(0,c.foreignKey)({name:`${t}_TDTenantMembershipGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),w=h.table("TDAllowlistGroup",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDAllowlistGroup_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDAllowlistGroup_tenant_group_uq`).on(e.tenantId,e.groupId),(0,c.foreignKey)({name:`${t}_TDAllowlistGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),n=h.table("TDDirectorySyncRequest",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}),requestedBy:(0,c.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,c.datetime2)("requested_at").notNull(),pickedAt:(0,c.datetime2)("picked_at"),finishedAt:(0,c.datetime2)("finished_at"),result:(0,c.nvarchar)("result",{length:32}),error:(0,c.nvarchar)("error",{length:2e3}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDDirectorySyncRequest_pk`,columns:[e.id]})]),g=h.table("TMAllowlist",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,c.nvarchar)("pattern",{length:256}).notNull(),type:(0,c.nvarchar)("type",{length:32}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TMAllowlist_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TMAllowlist_pattern_uq`).on(e.tenantId,e.pattern)]);return{TDGroupExternalLink:a,TDGroupMemberEmail:p,TDTenantMembershipGroup:y,TDAllowlistGroup:w,TDDirectorySyncRequest:n,TMAllowlist:g}}var j=require("@aiquants/auth-directory-core");var X=(t,o=20)=>t.length<=o?t.join(", "):`${t.slice(0,o).join(", ")} \u307B\u304B ${t.length-o} \u4EF6`,Q={upstreamUnreadable:t=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,localWriteFailed:t=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,resolverIncomplete:(t,o)=>`\u4E0A\u6D41\u304C ${t} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${o} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(t,o,h)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${t})\u3002\u9023\u7D9A ${o} \u56DE\u76EE\u3067\u3059\u3002${h} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(t,o)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${o} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${t})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:t=>{if(t.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059 (${X(t.offendingEmails)})\u3002`;let o=t.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",h=t.axis==="membership"?t.withheldProjectionRemoves.map(String):t.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${o}\u304C ${t.population} \u4EBA\u4E2D ${t.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${t.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${t.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${X(h)}\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalsRefused:t=>`${t.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${X(t.map(o=>String(o.userId)))})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(t,o)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${t} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${o} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var Z=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider"];function De(t){let{directory:o,repository:h,policy:b,now:s,logger:a}=t,p=t.messages??Q;if((0,j.assertBlastRadiusPolicy)(b),!Number.isInteger(t.vanishedThreshold)||t.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(t.vanishedThreshold)}`);if(typeof t.provider!="string"||t.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function y(e,l){try{await h.recordFailure(e,l)}catch(S){a.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,S)}}async function w(e){try{let l=await o.getGroup(e.externalId);return l===null?{kind:"missing"}:{kind:"ok",group:l,members:await o.listGroupMembers(l,e.membershipMode)}}catch(l){return{kind:"failed",error:l instanceof Error?l:new Error(String(l))}}}async function n(e){let l=await h.recordVanishedObservation(e,p.groupMissing(e.externalKey,e.vanishedObservations+1,t.vanishedThreshold));return l<t.vanishedThreshold?(a.warn(`[auth-directory] ${e.externalKey} missing upstream (${l}/${t.vanishedThreshold})`),{kind:"missing",link:e,observations:l}):(await h.markGroupInactive(e),await y(e,p.groupVanished(e.externalKey,l)),a.warn(`[auth-directory] ${e.externalKey} deactivated after ${l} consecutive absences`),{kind:"vanished",link:e})}async function g(e){if(e.provider!==t.provider)return await y(e,p.foreignProvider(e.provider,t.provider)),a.warn(`[auth-directory] skipping ${e.externalKey}: belongs to provider ${e.provider}`),{kind:"foreign-provider",link:e};let l=await w(e);if(l.kind==="missing")return n(e);if(l.kind==="failed")return await y(e,p.upstreamUnreadable(l.error.message)),a.error(`[auth-directory] upstream read failed for ${e.externalKey}:`,l.error),{kind:"upstream-failed",link:e,error:l.error};try{let[S,d]=await Promise.all([h.loadGroupSnapshot(e),h.resolveMembers(l.members)]);if(d.length!==l.members.length){let _=p.resolverIncomplete(l.members.length,d.length);return await y(e,_),a.error(`[auth-directory] ${_}`),{kind:"local-failed",link:e,error:new Error(_)}}let v=(0,j.reconcile)(S,d,b);if(v.aborted)return await y(e,p.planRefused(v.aborted)),a.warn(`[auth-directory] plan refused for ${e.externalKey}: ${v.aborted.reason}`),{kind:"aborted",link:e,plan:v};let I=v.ledgerUpserts.length>0||v.ledgerDeletes.length>0||v.projectionAdds.length>0||v.projectionRemoves.length>0,T=s(),G={refusals:[]};return I&&(G=await h.applyPlan(e,v,T)),await h.recordSuccess(e,l.group,T),G.refusals.length>0&&(await y(e,p.removalsRefused(G.refusals)),a.warn(`[auth-directory] ${G.refusals.length} removal(s) refused for ${e.externalKey}`)),v.unresolvedEmails.length>0&&a.info(`[auth-directory] ${e.externalKey}: ${v.unresolvedEmails.length} upstream member(s) have no local user yet`),I?{kind:"applied",link:e,plan:v}:{kind:"unchanged",link:e}}catch(S){let d=S instanceof Error?S:new Error(String(S));return await y(e,p.localWriteFailed(d.message)),a.error(`[auth-directory] local write failed for ${e.externalKey}:`,d),{kind:"local-failed",link:e,error:d}}}return{syncOne:g,async run(e={}){let l=await h.listSyncableLinks(e),S=[];for(let v of l)try{S.push(await g(v))}catch(I){let T=I instanceof Error?I:new Error(String(I));a.error(`[auth-directory] ${v.externalKey} ended abnormally:`,T),S.push({kind:"local-failed",link:v,error:T})}let d=S.filter(v=>Z.includes(v.kind)).length;return a.info(`[auth-directory] run finished: ${S.length} link(s), ${d} unhealthy`),{outcomes:S,unhealthyCount:d}}}}var ae=require("@aiquants/authz-core"),P=require("drizzle-orm"),ee=2e3;function xe(t,o,h){let b=t,s=h.ensureConnection??(async()=>{}),a=o.TDDirectorySyncRequest,p=h.maxErrorLength??ee;if(!Number.isInteger(p)||p<1||p>ee)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${ee}, received ${String(h.maxErrorLength)}`);return{async claim(){await s();let y=await b.select({id:a.id}).from(a).where((0,P.isNull)(a.pickedAt)).orderBy((0,P.asc)(a.id));for(let w of y){let g=(await b.update(a).set({pickedAt:h.now()}).output().where((0,P.and)((0,P.eq)(a.id,Number(w.id)),(0,P.isNull)(a.pickedAt))))?.[0];if(!g)continue;let e=String(g.tenant_id??g.tenantId??""),l=g.group_id??g.groupId??null;return{id:Number(g.id),tenantId:(0,ae.assertTenantId)(e,"claim(row.tenantId)"),groupId:l===null?null:Number(l),requestedBy:String(g.requested_by??g.requestedBy??"")}}return null},async finish(y,w){await s();let n=w.error?w.error.slice(0,p):null;await b.update(a).set({finishedAt:h.now(),result:w.result,error:n}).output().where((0,P.eq)(a.id,y))},async listStranded(y){return await s(),(await b.select({id:a.id,pickedAt:a.pickedAt}).from(a).where((0,P.isNull)(a.finishedAt)).orderBy((0,P.asc)(a.id))).filter(n=>n.pickedAt!==null&&new Date(n.pickedAt)<y).map(n=>Number(n.id))}}}var re=require("@aiquants/auth-core"),B=require("@aiquants/authz-core"),A=require("drizzle-orm");var ke=7,Ge=4;function Ee(t,o,h,b){let s=t,a=b.ensureConnection??(async()=>{}),{TDGroupExternalLink:p,TDGroupMemberEmail:y}=o,{TMGroup:w,TDGroupMember:n}=h,g=b.actor;return{async listSyncableLinks(e){await a();let l=[(0,A.eq)(p.isSyncEnabled,!0)];return e.tenantId!==void 0&&l.push((0,A.eq)(p.tenantId,(0,B.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&l.push((0,A.eq)(p.groupId,e.groupId)),(await s.select({linkId:p.id,groupId:p.groupId,linkTenantId:p.tenantId,groupTenantId:w.tenantId,groupKey:w.groupKey,isGroupActive:w.isActive,provider:p.provider,externalId:p.externalId,externalKey:p.externalKey,membershipMode:p.membershipMode,vanishedObservations:p.vanishedObservations}).from(p).innerJoin(w,(0,A.eq)(w.id,p.groupId)).where((0,A.and)(...l))).map(d=>((0,B.assertTenantMatch)(String(d.groupTenantId),String(d.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(d.linkId),groupId:Number(d.groupId),groupKey:String(d.groupKey),tenantId:String(d.groupTenantId),provider:String(d.provider),externalId:String(d.externalId),externalKey:String(d.externalKey),membershipMode:K(String(d.membershipMode)),isGroupActive:!!d.isGroupActive,vanishedObservations:Number(d.vanishedObservations??0)}))},async loadGroupSnapshot(e){await a();let[l,S]=await Promise.all([s.select({email:y.email,externalUserId:y.externalUserId,resolvedUserId:y.resolvedUserId}).from(y).where((0,A.eq)(y.groupId,e.groupId)),s.select({userId:n.userId}).from(n).where((0,A.eq)(n.groupId,e.groupId))]);return{ledger:l.map(d=>({email:String(d.email),...d.externalUserId?{externalUserId:String(d.externalUserId)}:{},resolvedUserId:d.resolvedUserId===null?null:Number(d.resolvedUserId)})),projectedUserIds:S.map(d=>Number(d.userId))}},resolveMembers(e){return b.memberResolver.resolve(e)},async applyPlan(e,l,S){(0,B.assertTenantId)(e.tenantId,"applyPlan(link.tenantId)"),await a(),(l.ledgerUpserts.length>0||l.projectionAdds.length>0)&&await s.transaction(async I=>{let T=l.ledgerUpserts.map(r=>({...r,email:(0,re.normalizeAuthEmail)(r.email)})),G=new Map;for(let r of L(T.map(i=>i.email))){let i=await I.select({id:y.id,email:y.email}).from(y).where((0,A.and)((0,A.eq)(y.groupId,e.groupId),(0,A.inArray)(y.email,r)));for(let m of i)G.set(String(m.email),Number(m.id))}let _=T.filter(r=>!G.has(r.email));for(let r of N(_,ke))await I.insert(y).output().values(r.map(i=>({groupId:e.groupId,email:i.email,externalUserId:i.externalUserId??null,resolvedUserId:i.resolvedUserId,observedAt:S,createdAt:S,createdBy:g})));for(let r of T){let i=G.get(r.email);i!==void 0&&await I.update(y).set({externalUserId:r.externalUserId??null,resolvedUserId:r.resolvedUserId,observedAt:S,updatedAt:S,updatedBy:g}).output().where((0,A.eq)(y.id,i))}for(let r of N(l.projectionAdds,Ge)){let i=new Set((await I.select({userId:n.userId}).from(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.inArray)(n.userId,r)))).map(f=>Number(f.userId))),m=r.filter(f=>!i.has(f));m.length>0&&await I.insert(n).output().values(m.map(f=>({groupId:e.groupId,userId:f,createdAt:S,createdBy:g})))}});let d=[];for(let I of l.projectionRemoves)try{await b.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:I}},"remove-group-member",T=>T.delete(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.eq)(n.userId,I))))}catch(T){d.push({userId:I,applied:!1,refusal:T instanceof Error?T.message:String(T)})}let v=d.map(I=>I.userId);return l.ledgerDeletes.length>0&&await s.transaction(async I=>{for(let T of L(l.ledgerDeletes)){let G=v.length>0?(0,A.or)((0,A.isNull)(y.resolvedUserId),(0,A.notInArray)(y.resolvedUserId,v)):void 0;await I.delete(y).where((0,A.and)((0,A.eq)(y.groupId,e.groupId),(0,A.inArray)(y.email,T),...G?[G]:[]))}}),{refusals:d}},async recordSuccess(e,l,S){await a(),await s.transaction(async d=>{await d.update(p).set({syncedAt:S,syncError:null,vanishedObservations:0,externalId:l.externalId,externalKey:(0,re.normalizeAuthEmail)(l.externalKey),updatedAt:S,updatedBy:g}).output().where((0,A.eq)(p.id,e.linkId)),e.isGroupActive||await d.update(w).set({isActive:!0,updatedAt:S,updatedBy:g}).output().where((0,A.and)((0,A.eq)(w.id,e.groupId),(0,A.eq)(w.tenantId,e.tenantId)))})},async recordFailure(e,l){await a(),await s.update(p).set({syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:g}).output().where((0,A.eq)(p.id,e.linkId))},async recordVanishedObservation(e,l){await a();let S=e.vanishedObservations+1;return await s.update(p).set({vanishedObservations:S,syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:g}).output().where((0,A.eq)(p.id,e.linkId)),S},async markGroupInactive(e){await a(),await s.update(w).set({isActive:!1,updatedAt:new Date,updatedBy:g}).output().where((0,A.and)((0,A.eq)(w.id,e.groupId),(0,A.eq)(w.tenantId,e.tenantId)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MAX_IN_LIST,MAX_PARAMETERS,MEMBERSHIP_MODES,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,chunk,chunkByParameters,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,withExternalGroupGuards});
|
|
1
|
+
"use strict";var F=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var pe=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var me=(t,o)=>{for(var g in o)F(t,g,{get:o[g],enumerable:!0})},ye=(t,o,g,b)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of pe(o))!ce.call(t,s)&&s!==g&&F(t,s,{get:()=>o[s],enumerable:!(b=le(o,s))||b.enumerable});return t};var ge=t=>ye(F({},"__esModule",{value:!0}),t);var Re={};me(Re,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>ae,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>Te,MAX_IN_LIST:()=>qe,MAX_PARAMETERS:()=>se,MEMBERSHIP_MODES:()=>Y,UNHEALTHY_SYNC_OUTCOMES:()=>Z,assertGroupMayBeLinked:()=>we,assertMembershipMode:()=>K,chunk:()=>L,chunkByParameters:()=>N,createDirectoryLinkStore:()=>fe,createDirectorySync:()=>xe,createDirectorySyncRepository:()=>Me,createPromotionRepository:()=>ve,createSyncRequestQueue:()=>ke,defaultExternalGroupGuardMessage:()=>he,defaultSyncMessages:()=>Q,defineAuthDirectorySchema:()=>De,findLedgerRowsForIdentity:()=>q,promoteLedgerForUser:()=>Ie,withExternalGroupGuards:()=>be});module.exports=ge(Re);var te=require("@aiquants/authz-core"),he=(t,o)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${o}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${t}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function H(t,o,g,b){return new Set(t.filter(s=>s.roleId===o&&s.appKey===g&&s.resourceKey===b).map(s=>s.action))}var J=t=>te.AUTHZ_ACTIONS.every(o=>t.has(o)),V=(t,o)=>t.find(g=>g.roleId===o)?.roleKey??`#${o}`;function be(t,o){return ne(t,o)}function ne(t,o){let g=t;async function b(s,a,p,y,w){if(y.size===0)return;let n=await s.listGroupRoles(p),h=new Set;for(let e of n){if(!y.has(e.roleId)||h.has(e.groupId))continue;h.add(e.groupId);let l=await o.linkLookup.externalGroupKey(p,e.groupId,a);if(l!==null)throw new Error(o.message(e.roleKey||V(w,e.roleId),l))}}return{...t,withTransaction:(s,a)=>g.withTransaction((p,y)=>s(ne(p,o),y),a),async assignGroupRole(s,a,p,y){await g.withTransaction(async(w,n)=>{let h=await w.listGrants(s);if(J(H(h,p,o.adminAppKey,o.adminResourceKey))){let e=await o.linkLookup.externalGroupKey(s,a,n);if(e!==null)throw new Error(o.message(V(h,p),e))}await w.assignGroupRole(s,a,p,y)})},async createGrant(s,a,p){await g.withTransaction(async(y,w)=>{let h=(await y.listResources(s)).find(e=>e.id===a.resourceId);if(h?.appKey===o.adminAppKey&&h?.resourceKey===o.adminResourceKey){let e=await y.listGrants(s),l=H(e,a.roleId,o.adminAppKey,o.adminResourceKey);l.add(a.action),J(l)&&await b(y,w,s,new Set([a.roleId]),e)}await y.createGrant(s,a,p)})}}}async function we(t,o,g,b){let s=(await t.listGroupRoles(o)).filter(p=>p.groupId===g);if(s.length===0)return;let a=await t.listGrants(o);for(let p of s)if(J(H(a,p.roleId,b.adminAppKey,b.adminResourceKey)))throw new Error(b.message(p.roleKey||V(a,p.roleId),p.groupKey))}var $=require("drizzle-orm");async function q(t,o,g){let{ledger:b,group:s}=o,{tenantId:a,externalUserId:p,email:y}=g;if(p===null&&y==="")return[];let w={ledgerId:b.id,groupId:b.groupId,tenantId:s.tenantId,externalUserId:b.externalUserId},n=async d=>(await t.select(w).from(b).innerJoin(s,(0,$.and)((0,$.eq)(s.id,b.groupId),(0,$.eq)(s.tenantId,a),(0,$.eq)(s.isActive,!0))).where(d)).map(I=>({ledgerId:Number(I.ledgerId),groupId:Number(I.groupId),tenantId:String(I.tenantId),externalUserId:I.externalUserId===null?null:String(I.externalUserId)})),h=d=>{let v=new Map;for(let I of d)v.set(I.ledgerId,{ledgerId:I.ledgerId,groupId:I.groupId,tenantId:I.tenantId});return[...v.values()].sort((I,T)=>I.ledgerId-T.ledgerId)},e=p===null?[]:await n((0,$.eq)(b.externalUserId,p));if(y==="")return h(e);let l=await n((0,$.eq)(b.email,y)),S=d=>p!==null&&d.externalUserId!==null&&d.externalUserId!==p;return h([...e,...l.filter(d=>!S(d))])}var B=require("@aiquants/auth-core"),oe=require("@aiquants/auth-directory-core"),x=require("@aiquants/authz-core"),u=require("drizzle-orm");var Y=["direct","transitive"];function K(t){if(!Y.includes(t))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${Y.join(" | ")}, received ${JSON.stringify(t)}`);return t}function fe(t,o,g,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.policyCacheTtlMs??0;if(!Number.isFinite(p)||p<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(b.policyCacheTtlMs)}`);let y=new Map,w=r=>y.delete(r),{TDGroupExternalLink:n,TDGroupMemberEmail:h,TDTenantMembershipGroup:e,TDAllowlistGroup:l,TDDirectorySyncRequest:S}=o,{TMGroup:d,TDGroupMember:v}=g;async function I(r,i,m){if((await r.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,i),(0,u.eq)(e.groupId,m),(0,u.eq)(e.isBreakGlass,!0)))).length>0)throw new Error(`Group ${m} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function T(r,i,m){let[f]=await r.select({id:d.id}).from(d).where((0,u.and)((0,u.eq)(d.id,m),(0,u.eq)(d.tenantId,i)));if(!f)throw new Error(`Group not found in tenant: ${m}`)}function G(r,i){return{linkId:Number(r.linkId),groupId:Number(r.groupId),tenantId:String(r.tenantId),provider:String(r.provider),externalId:String(r.externalId),externalKey:String(r.externalKey),membershipMode:K(String(r.membershipMode)),isSyncEnabled:!!r.isSyncEnabled,...r.syncedAt?{syncedAt:new Date(r.syncedAt).toISOString()}:{},...r.syncError?{syncError:String(r.syncError)}:{},unresolvedCount:i}}async function _(r,i){let m=await r.select({groupId:h.groupId,n:(0,u.count)()}).from(h).innerJoin(d,(0,u.and)((0,u.eq)(d.id,h.groupId),(0,u.eq)(d.tenantId,i))).where((0,u.isNull)(h.resolvedUserId)).groupBy(h.groupId);return new Map(m.map(f=>[Number(f.groupId),Number(f.n)]))}return{async listLinks(r){(0,x.assertTenantId)(r,"listLinks(tenantId)"),await a();let i=await s.select({linkId:n.id,groupId:n.groupId,tenantId:n.tenantId,provider:n.provider,externalId:n.externalId,externalKey:n.externalKey,membershipMode:n.membershipMode,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)).orderBy((0,u.asc)(n.groupId)),m=await _(s,r);return i.map(f=>((0,x.assertTenantMatch)(r,String(f.tenantId),"listLinks(row.tenantId)"),G(f,m.get(Number(f.groupId))??0)))},async externalGroupKey(r,i,m){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await a();let[f]=await(m??s).select({externalKey:n.externalKey}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));return f?String(f.externalKey):null},async linkGroup(r,i,m,f){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let D=K(m.membershipMode),k=m.externalId.trim(),E=(0,B.normalizeAuthEmail)(m.externalKey);if(k===""||E==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await a(),await b.linkable.assertLinkable(r,i),await s.transaction(async z=>{await T(z,r,i),await I(z,r,i),await z.insert(n).output().values({groupId:i,tenantId:r,provider:m.provider,externalId:k,externalKey:E,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:f})}),w(r)},async unlinkGroup(r,i){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await a(),await s.transaction(async m=>{await T(m,r,i),await m.delete(h).where((0,u.eq)(h.groupId,i)),await m.delete(l).where((0,u.and)((0,u.eq)(l.groupId,i),(0,u.eq)(l.tenantId,r))),await m.delete(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))}),w(r)},async setSyncEnabled(r,i,m,f){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await a(),await s.update(n).set({isSyncEnabled:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))},async requestSync(r,i,m){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await a(),await s.transaction(async f=>{i!==null&&await T(f,r,i),await f.insert(S).output().values({tenantId:r,groupId:i,requestedBy:m,requestedAt:new Date,createdAt:new Date,createdBy:m})})},async getStatus(r){(0,x.assertTenantId)(r,"getStatus(tenantId)"),await a();let i=await s.select({groupId:n.groupId,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,u.eq)(n.tenantId,r)),[m]=await s.select({n:(0,u.count)()}).from(S).where((0,u.and)((0,u.eq)(S.tenantId,r),(0,u.isNull)(S.finishedAt))),f=i.filter(R=>!!R.isSyncEnabled),D=f.map(R=>R.syncedAt?new Date(R.syncedAt).getTime():null),k=D.length>0&&D.every(R=>R!==null)?Math.min(...D):null,z=i.length>0&&f.length===0||f.length>0&&(k===null||Date.now()-k>b.stalenessThresholdSec*1e3);return{isEnabled:b.isEnabled,linkedGroupCount:i.length,...k!==null?{oldestSyncedAt:new Date(k).toISOString()}:{},isStale:z,pausedGroupCount:i.length-f.length,stalenessThresholdSec:b.stalenessThresholdSec,erroredGroupIds:i.filter(R=>!!R.syncError).map(R=>Number(R.groupId)).sort((R,ue)=>R-ue),pendingRequestCount:Number(m?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)");let i=p>0?y.get(r):void 0;if(i&&Date.now()-i.loadedAt<p)return i.policy;await a();let m=await s.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass,externalLinkId:n.id}).from(e).innerJoin(d,(0,u.and)((0,u.eq)(d.id,e.groupId),(0,u.eq)(d.tenantId,r))).leftJoin(n,(0,u.eq)(n.groupId,e.groupId)).where((0,u.eq)(e.tenantId,r));if(m.length===0){let E={kind:"no-proof-required"};return p>0&&y.set(r,{policy:E,loadedAt:Date.now()}),E}let f=m.map(E=>Number(E.groupId)),D=m.filter(E=>!!E.isBreakGlass);for(let E of D)if(E.externalLinkId!==null)throw new Error(`Break-glass group ${Number(E.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let k={kind:"require-proof",proofGroupIds:f,breakGlassGroupIds:D.map(E=>Number(E.groupId))};return(0,oe.assertTenantMembershipPolicy)(k,`tenant ${r}`),p>0&&y.set(r,{policy:k,loadedAt:Date.now()}),k},async setTenantMembershipGroup(r,i,m,f){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async D=>{if(await T(D,r,i),m){let[E]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.tenantId,r),(0,u.eq)(n.groupId,i)));if(E)throw new Error(`Group ${i} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}if((await D.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))).length>0){await D.update(e).set({isBreakGlass:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i))),w(r);return}await D.insert(e).output().values({tenantId:r,groupId:i,isBreakGlass:m,createdAt:new Date,createdBy:f})}),w(r)},async removeTenantMembershipGroup(r,i){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async m=>{let D=(await m.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass}).from(e).where((0,u.eq)(e.tenantId,r))).filter(k=>Number(k.groupId)!==i);if(D.length>0&&!D.some(k=>!!k.isBreakGlass))throw new Error(`Removing group ${i} would leave tenant ${r} with a membership proof but no break-glass path.`);await m.delete(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))}),w(r)},async setGroupActive(r,i,m,f){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await a(),await s.update(d).set({isActive:m,updatedAt:new Date,updatedBy:f}).output().where((0,u.and)((0,u.eq)(d.id,i),(0,u.eq)(d.tenantId,r)))},async listLedgerGroupIds(r,i){if((0,x.assertTenantId)(r,"listLedgerGroupIds(tenantId)"),!b.isEnabled)return[];await a();let m=await q(s,{ledger:h,group:d},{tenantId:r,externalUserId:i.externalUserId?.trim()||null,email:(0,B.normalizeAuthEmail)(i.email)});return[...new Set(m.map(f=>f.groupId))].sort((f,D)=>f-D)},async isEmailAllowedByGroup(r){if(!b.isEnabled)return!1;let i=(0,B.normalizeAuthEmail)(r);if(i==="")return!1;await a();let[m]=await s.select({id:h.id}).from(h).innerJoin(l,(0,u.eq)(l.groupId,h.groupId)).innerJoin(d,(0,u.and)((0,u.eq)(d.id,h.groupId),(0,u.eq)(d.isActive,!0))).where((0,u.eq)(h.email,i));return!!m},async listAllowlistGroups(r){return(0,x.assertTenantId)(r,"listAllowlistGroups(tenantId)"),await a(),(await s.select({id:l.id,groupId:l.groupId,groupKey:d.groupKey,groupName:d.name,description:l.description}).from(l).innerJoin(d,(0,u.and)((0,u.eq)(d.id,l.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(l.tenantId,r)).orderBy((0,u.asc)(d.name))).map(m=>({id:Number(m.id),type:"group",groupId:Number(m.groupId),groupKey:String(m.groupKey),groupName:String(m.groupName),...m.description?{description:String(m.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,i,m,f){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await a(),await s.transaction(async D=>{await T(D,r,i);let[k]=await D.select({id:n.id}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));if(!k)throw new Error(`Group ${i} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await D.insert(l).output().values({tenantId:r,groupId:i,description:m??null,createdAt:new Date,createdBy:f})})},async removeAllowlistGroup(r,i){(0,x.assertTenantId)(r,"removeAllowlistGroup(tenantId)"),await a(),await s.delete(l).where((0,u.and)((0,u.eq)(l.id,i),(0,u.eq)(l.tenantId,r)))},async countProjectedMembers(r,i){(0,x.assertTenantId)(r,"countProjectedMembers(tenantId)"),await a();let[m]=await s.select({n:(0,u.count)()}).from(v).innerJoin(d,(0,u.and)((0,u.eq)(d.id,v.groupId),(0,u.eq)(d.tenantId,r))).where((0,u.eq)(v.groupId,i));return Number(m?.n??0)}}}var ie=require("@aiquants/auth-core"),C=require("@aiquants/authz-core");async function Ie(t,o){let g=(0,C.assertTenantId)(o.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(o.userId)||o.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(o.userId)}`);let b=(0,ie.normalizeAuthEmail)(o.email),s=o.externalUserId?.trim()||null;if(s===null&&b==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:g,externalUserId:s,email:b});if(a.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let n of a)(0,C.assertTenantMatch)(g,n.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let p=[...new Set(a.map(n=>n.ledgerId))].sort((n,h)=>n-h),y=[...new Set(a.map(n=>n.groupId))].sort((n,h)=>n-h);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:g,userId:o.userId,ledgerIds:p,groupIds:y})].sort((n,h)=>n-h),matchedLedgerIds:p}}var W=require("@aiquants/authz-core"),M=require("drizzle-orm");var se=2100;var qe=500;function L(t,o=500){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] chunk size must be a positive integer, received ${String(o)}`);let g=[];for(let b=0;b<t.length;b+=o)g.push(t.slice(b,b+o));return g}function N(t,o,g=100){if(!Number.isInteger(o)||o<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] parametersPerItem must be a positive integer, received ${String(o)}`);if(!Number.isInteger(g)||g<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(g)}`);let b=2100-g,s=Math.floor(b/o);if(s<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${b} available in one statement`);return L(t,s)}var Se=4,Ae=100;function ve(t,o,g,b){let s=t,a=b.ensureConnection??(async()=>{}),p=b.now??(()=>new Date),y=o.TDGroupMemberEmail,{TMGroup:w,TDGroupMember:n}=g;return{async findLedgerMatches(h){return(0,W.assertTenantId)(h.tenantId,"findLedgerMatches(tenantId)"),await a(),q(s,{ledger:y,group:w},h)},async attachUserToGroups({tenantId:h,userId:e,ledgerIds:l,groupIds:S}){return(0,W.assertTenantId)(h,"attachUserToGroups(tenantId)"),await a(),l.length===0?[]:s.transaction(async d=>{let v=new Set;for(let r of L(S)){let i=await d.select({id:w.id}).from(w).where((0,M.and)((0,M.inArray)(w.id,r),(0,M.eq)(w.tenantId,h),(0,M.eq)(w.isActive,!0)));for(let m of i)v.add(Number(m.id))}let I=S.filter(r=>v.has(r));if(I.length===0)return[];let T=new Set;for(let r of L(I)){let i=await d.select({groupId:n.groupId}).from(n).where((0,M.and)((0,M.eq)(n.userId,e),(0,M.inArray)(n.groupId,r)));for(let m of i)T.add(Number(m.groupId))}let G=I.filter(r=>!T.has(r)),_=Math.max(1,2100-Ae-I.length);for(let r of L(l,_))await d.update(y).set({resolvedUserId:e}).output().where((0,M.and)((0,M.inArray)(y.id,r),(0,M.inArray)(y.groupId,I)));if(G.length>0){let r=p();for(let i of N(G,Se))await d.insert(n).output().values(i.map(m=>({groupId:m,userId:e,createdAt:r,createdBy:b.actor})))}return G})}}}var U=require("@aiquants/authz-drizzle"),c=require("drizzle-orm/mssql-core"),ae="provider",Te=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TMAllowlist"];function De(t,o){let g=(0,c.mssqlSchema)(t),b=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50}),updatedAt:(0,c.datetime2)("updated_at"),updatedBy:(0,c.nvarchar)("updated_by",{length:50})}),s=()=>({createdAt:(0,c.datetime2)("created_at"),createdBy:(0,c.nvarchar)("created_by",{length:50})}),a=g.table("TDGroupExternalLink",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,c.nvarchar)(ae,{length:32}).notNull(),externalId:(0,c.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,c.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,c.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,c.bit)("is_sync_enabled").notNull(),syncedAt:(0,c.datetime2)("synced_at"),syncError:(0,c.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,c.int)("vanished_observations").notNull(),...b()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupExternalLink_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_group_uq`).on(e.groupId),(0,c.uniqueIndex)(`${t}_TDGroupExternalLink_external_uq`).on(e.tenantId,e.provider,e.externalId),(0,c.foreignKey)({name:`${t}_TDGroupExternalLink_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),p=g.table("TDGroupMemberEmail",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),email:(0,c.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,c.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,c.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,c.datetime2)("observed_at").notNull(),...b()},e=>[(0,c.primaryKey)({name:`${t}_TDGroupMemberEmail_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDGroupMemberEmail_group_email_uq`).on(e.groupId,e.email),(0,c.foreignKey)({name:`${t}_TDGroupMemberEmail_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),y=g.table("TDTenantMembershipGroup",{tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,c.bit)("is_break_glass").notNull(),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDTenantMembershipGroup_pk`,columns:[e.tenantId,e.groupId]}),(0,c.foreignKey)({name:`${t}_TDTenantMembershipGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),w=g.table("TDAllowlistGroup",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDAllowlistGroup_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TDAllowlistGroup_tenant_group_uq`).on(e.tenantId,e.groupId),(0,c.foreignKey)({name:`${t}_TDAllowlistGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),n=g.table("TDDirectorySyncRequest",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,c.bigint)("group_id",{mode:"number"}),requestedBy:(0,c.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,c.datetime2)("requested_at").notNull(),pickedAt:(0,c.datetime2)("picked_at"),finishedAt:(0,c.datetime2)("finished_at"),result:(0,c.nvarchar)("result",{length:32}),error:(0,c.nvarchar)("error",{length:2e3}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TDDirectorySyncRequest_pk`,columns:[e.id]})]),h=g.table("TMAllowlist",{id:(0,c.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,c.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,c.nvarchar)("pattern",{length:256}).notNull(),type:(0,c.nvarchar)("type",{length:32}).notNull(),description:(0,c.nvarchar)("description",{length:512}),...s()},e=>[(0,c.primaryKey)({name:`${t}_TMAllowlist_pk`,columns:[e.id]}),(0,c.uniqueIndex)(`${t}_TMAllowlist_pattern_uq`).on(e.tenantId,e.pattern)]);return{TDGroupExternalLink:a,TDGroupMemberEmail:p,TDTenantMembershipGroup:y,TDAllowlistGroup:w,TDDirectorySyncRequest:n,TMAllowlist:h}}var j=require("@aiquants/auth-directory-core");var X=(t,o=20)=>t.length<=o?t.join(", "):`${t.slice(0,o).join(", ")} \u307B\u304B ${t.length-o} \u4EF6`,Q={upstreamUnreadable:t=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,localWriteFailed:t=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${t}`,resolverIncomplete:(t,o)=>`\u4E0A\u6D41\u304C ${t} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${o} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(t,o,g)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${t})\u3002\u9023\u7D9A ${o} \u56DE\u76EE\u3067\u3059\u3002${g} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(t,o)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${o} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${t})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:t=>{if(t.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u3059 (${X(t.offendingEmails)})\u3002`;let o=t.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",g=t.axis==="membership"?t.withheldProjectionRemoves.map(String):t.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${o}\u304C ${t.population} \u4EBA\u4E2D ${t.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${t.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${t.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${X(g)}\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalsRefused:t=>`${t.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${X(t.map(o=>String(o.userId)))})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(t,o)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${t} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${o} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var Z=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider"];function xe(t){let{directory:o,repository:g,policy:b,now:s,logger:a}=t,p=t.messages??Q;if((0,j.assertBlastRadiusPolicy)(b),!Number.isInteger(t.vanishedThreshold)||t.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(t.vanishedThreshold)}`);if(typeof t.provider!="string"||t.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function y(e,l){try{await g.recordFailure(e,l)}catch(S){a.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,S)}}async function w(e){try{let l=await o.getGroup(e.externalId);return l===null?{kind:"missing"}:{kind:"ok",group:l,members:await o.listGroupMembers(l,e.membershipMode)}}catch(l){return{kind:"failed",error:l instanceof Error?l:new Error(String(l))}}}async function n(e){let l=await g.recordVanishedObservation(e,p.groupMissing(e.externalKey,e.vanishedObservations+1,t.vanishedThreshold));return l<t.vanishedThreshold?(a.warn(`[auth-directory] ${e.externalKey} missing upstream (${l}/${t.vanishedThreshold})`),{kind:"missing",link:e,observations:l}):(await g.markGroupInactive(e),await y(e,p.groupVanished(e.externalKey,l)),a.warn(`[auth-directory] ${e.externalKey} deactivated after ${l} consecutive absences`),{kind:"vanished",link:e})}async function h(e){if(e.provider!==t.provider)return await y(e,p.foreignProvider(e.provider,t.provider)),a.warn(`[auth-directory] skipping ${e.externalKey}: belongs to provider ${e.provider}`),{kind:"foreign-provider",link:e};let l=await w(e);if(l.kind==="missing")return n(e);if(l.kind==="failed")return await y(e,p.upstreamUnreadable(l.error.message)),a.error(`[auth-directory] upstream read failed for ${e.externalKey}:`,l.error),{kind:"upstream-failed",link:e,error:l.error};try{let[S,d]=await Promise.all([g.loadGroupSnapshot(e),g.resolveMembers(l.members)]);if(d.length!==l.members.length){let _=p.resolverIncomplete(l.members.length,d.length);return await y(e,_),a.error(`[auth-directory] ${_}`),{kind:"local-failed",link:e,error:new Error(_)}}let v=(0,j.reconcile)(S,d,b);if(v.aborted)return await y(e,p.planRefused(v.aborted)),a.warn(`[auth-directory] plan refused for ${e.externalKey}: ${v.aborted.reason}`),{kind:"aborted",link:e,plan:v};let I=v.ledgerUpserts.length>0||v.ledgerDeletes.length>0||v.projectionAdds.length>0||v.projectionRemoves.length>0,T=s(),G={refusals:[]};return I&&(G=await g.applyPlan(e,v,T)),await g.recordSuccess(e,l.group,T),G.refusals.length>0&&(await y(e,p.removalsRefused(G.refusals)),a.warn(`[auth-directory] ${G.refusals.length} removal(s) refused for ${e.externalKey}`)),v.unresolvedEmails.length>0&&a.info(`[auth-directory] ${e.externalKey}: ${v.unresolvedEmails.length} upstream member(s) have no local user yet`),I?{kind:"applied",link:e,plan:v}:{kind:"unchanged",link:e}}catch(S){let d=S instanceof Error?S:new Error(String(S));return await y(e,p.localWriteFailed(d.message)),a.error(`[auth-directory] local write failed for ${e.externalKey}:`,d),{kind:"local-failed",link:e,error:d}}}return{syncOne:h,async run(e={}){let l=await g.listSyncableLinks(e),S=[];for(let v of l)try{S.push(await h(v))}catch(I){let T=I instanceof Error?I:new Error(String(I));a.error(`[auth-directory] ${v.externalKey} ended abnormally:`,T),S.push({kind:"local-failed",link:v,error:T})}let d=S.filter(v=>Z.includes(v.kind)).length;return a.info(`[auth-directory] run finished: ${S.length} link(s), ${d} unhealthy`),{outcomes:S,unhealthyCount:d}}}}var de=require("@aiquants/authz-core"),P=require("drizzle-orm"),ee=2e3;function ke(t,o,g){let b=t,s=g.ensureConnection??(async()=>{}),a=o.TDDirectorySyncRequest,p=g.maxErrorLength??ee;if(!Number.isInteger(p)||p<1||p>ee)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${ee}, received ${String(g.maxErrorLength)}`);return{async claim(){await s();let y=await b.select({id:a.id}).from(a).where((0,P.isNull)(a.pickedAt)).orderBy((0,P.asc)(a.id));for(let w of y){let h=(await b.update(a).set({pickedAt:g.now()}).output().where((0,P.and)((0,P.eq)(a.id,Number(w.id)),(0,P.isNull)(a.pickedAt))))?.[0];if(!h)continue;let e=String(h.tenant_id??h.tenantId??""),l=h.group_id??h.groupId??null;return{id:Number(h.id),tenantId:(0,de.assertTenantId)(e,"claim(row.tenantId)"),groupId:l===null?null:Number(l),requestedBy:String(h.requested_by??h.requestedBy??"")}}return null},async finish(y,w){await s();let n=w.error?w.error.slice(0,p):null;await b.update(a).set({finishedAt:g.now(),result:w.result,error:n}).output().where((0,P.eq)(a.id,y))},async listStranded(y){return await s(),(await b.select({id:a.id,pickedAt:a.pickedAt}).from(a).where((0,P.isNull)(a.finishedAt)).orderBy((0,P.asc)(a.id))).filter(n=>n.pickedAt!==null&&new Date(n.pickedAt)<y).map(n=>Number(n.id))}}}var re=require("@aiquants/auth-core"),O=require("@aiquants/authz-core"),A=require("drizzle-orm");var Ge=7,Ee=4;function Me(t,o,g,b){let s=t,a=b.ensureConnection??(async()=>{}),{TDGroupExternalLink:p,TDGroupMemberEmail:y}=o,{TMGroup:w,TDGroupMember:n}=g,h=b.actor;return{async listSyncableLinks(e){await a();let l=[(0,A.eq)(p.isSyncEnabled,!0)];return e.tenantId!==void 0&&l.push((0,A.eq)(p.tenantId,(0,O.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&l.push((0,A.eq)(p.groupId,e.groupId)),(await s.select({linkId:p.id,groupId:p.groupId,linkTenantId:p.tenantId,groupTenantId:w.tenantId,groupKey:w.groupKey,isGroupActive:w.isActive,provider:p.provider,externalId:p.externalId,externalKey:p.externalKey,membershipMode:p.membershipMode,vanishedObservations:p.vanishedObservations}).from(p).innerJoin(w,(0,A.eq)(w.id,p.groupId)).where((0,A.and)(...l))).map(d=>((0,O.assertTenantMatch)(String(d.groupTenantId),String(d.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(d.linkId),groupId:Number(d.groupId),groupKey:String(d.groupKey),tenantId:String(d.groupTenantId),provider:String(d.provider),externalId:String(d.externalId),externalKey:String(d.externalKey),membershipMode:K(String(d.membershipMode)),isGroupActive:!!d.isGroupActive,vanishedObservations:Number(d.vanishedObservations??0)}))},async loadGroupSnapshot(e){await a();let[l,S]=await Promise.all([s.select({email:y.email,externalUserId:y.externalUserId,resolvedUserId:y.resolvedUserId}).from(y).where((0,A.eq)(y.groupId,e.groupId)),s.select({userId:n.userId}).from(n).where((0,A.eq)(n.groupId,e.groupId))]);return{ledger:l.map(d=>({email:String(d.email),...d.externalUserId?{externalUserId:String(d.externalUserId)}:{},resolvedUserId:d.resolvedUserId===null?null:Number(d.resolvedUserId)})),projectedUserIds:S.map(d=>Number(d.userId))}},resolveMembers(e){return b.memberResolver.resolve(e)},async applyPlan(e,l,S){(0,O.assertTenantId)(e.tenantId,"applyPlan(link.tenantId)"),await a(),(l.ledgerUpserts.length>0||l.projectionAdds.length>0)&&await s.transaction(async I=>{let T=l.ledgerUpserts.map(r=>({...r,email:(0,re.normalizeAuthEmail)(r.email)})),G=new Map;for(let r of L(T.map(i=>i.email))){let i=await I.select({id:y.id,email:y.email}).from(y).where((0,A.and)((0,A.eq)(y.groupId,e.groupId),(0,A.inArray)(y.email,r)));for(let m of i)G.set(String(m.email),Number(m.id))}let _=T.filter(r=>!G.has(r.email));for(let r of N(_,Ge))await I.insert(y).output().values(r.map(i=>({groupId:e.groupId,email:i.email,externalUserId:i.externalUserId??null,resolvedUserId:i.resolvedUserId,observedAt:S,createdAt:S,createdBy:h})));for(let r of T){let i=G.get(r.email);i!==void 0&&await I.update(y).set({externalUserId:r.externalUserId??null,resolvedUserId:r.resolvedUserId,observedAt:S,updatedAt:S,updatedBy:h}).output().where((0,A.eq)(y.id,i))}for(let r of N(l.projectionAdds,Ee)){let i=new Set((await I.select({userId:n.userId}).from(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.inArray)(n.userId,r)))).map(f=>Number(f.userId))),m=r.filter(f=>!i.has(f));m.length>0&&await I.insert(n).output().values(m.map(f=>({groupId:e.groupId,userId:f,createdAt:S,createdBy:h})))}});let d=[];for(let I of l.projectionRemoves)try{await b.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:I}},"remove-group-member",T=>T.delete(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.eq)(n.userId,I))))}catch(T){d.push({userId:I,applied:!1,refusal:T instanceof Error?T.message:String(T)})}let v=d.map(I=>I.userId);return l.ledgerDeletes.length>0&&await s.transaction(async I=>{for(let T of L(l.ledgerDeletes)){let G=v.length>0?(0,A.or)((0,A.isNull)(y.resolvedUserId),(0,A.notInArray)(y.resolvedUserId,v)):void 0;await I.delete(y).where((0,A.and)((0,A.eq)(y.groupId,e.groupId),(0,A.inArray)(y.email,T),...G?[G]:[]))}}),{refusals:d}},async recordSuccess(e,l,S){await a(),await s.transaction(async d=>{await d.update(p).set({syncedAt:S,syncError:null,vanishedObservations:0,externalId:l.externalId,externalKey:(0,re.normalizeAuthEmail)(l.externalKey),updatedAt:S,updatedBy:h}).output().where((0,A.eq)(p.id,e.linkId)),e.isGroupActive||await d.update(w).set({isActive:!0,updatedAt:S,updatedBy:h}).output().where((0,A.and)((0,A.eq)(w.id,e.groupId),(0,A.eq)(w.tenantId,e.tenantId)))})},async recordFailure(e,l){await a(),await s.update(p).set({syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(p.id,e.linkId))},async recordVanishedObservation(e,l){await a();let S=e.vanishedObservations+1;return await s.update(p).set({vanishedObservations:S,syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(p.id,e.linkId)),S},async markGroupInactive(e){await a(),await s.update(w).set({isActive:!1,updatedAt:new Date,updatedBy:h}).output().where((0,A.and)((0,A.eq)(w.id,e.groupId),(0,A.eq)(w.tenantId,e.tenantId)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MAX_IN_LIST,MAX_PARAMETERS,MEMBERSHIP_MODES,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,chunk,chunkByParameters,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,withExternalGroupGuards});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|