@aiquants/auth-directory-drizzle 0.2.1 → 0.3.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/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- 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 +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -1173,6 +1173,18 @@ type DirectoryLinkStoreOptions = {
|
|
|
1173
1173
|
stalenessThresholdSec: number;
|
|
1174
1174
|
/** False when directory integration is not wired for this deployment. 未結線なら false。 */
|
|
1175
1175
|
isEnabled: boolean;
|
|
1176
|
+
/**
|
|
1177
|
+
* Milliseconds to reuse a loaded tenant-membership policy. 帰属方針を再利用するミリ秒。
|
|
1178
|
+
*
|
|
1179
|
+
* 既定は 0 (毎回引き直す)。帰属証明は **権限を 1 件も読む前に毎リクエスト走る** ため、
|
|
1180
|
+
* 証明グループを 1 つも宣言していない配備でも 3 表の結合が 1 往復増える。
|
|
1181
|
+
*
|
|
1182
|
+
* ⚠️ 既定を非 0 にしない。認可の判断材料を、頼まれてもいないのに黙って古くするライブラリは
|
|
1183
|
+
* 信用できない。有効化は配備側が「どれだけ古くてよいか」を決めて明示する。
|
|
1184
|
+
* 本ストア自身の書き込み (証明グループの増減・連携の作成解除) は自動で破棄するため、
|
|
1185
|
+
* 遅れが出るのは **記憶域を直接編集した場合だけ** である。
|
|
1186
|
+
*/
|
|
1187
|
+
policyCacheTtlMs?: number;
|
|
1176
1188
|
};
|
|
1177
1189
|
/** One external link row as the admin surface sees it. 管理面が見る外部リンク行。 */
|
|
1178
1190
|
type DirectoryLinkRow = AuthGroupExternalLink & {
|
package/dist/index.d.ts
CHANGED
|
@@ -1173,6 +1173,18 @@ type DirectoryLinkStoreOptions = {
|
|
|
1173
1173
|
stalenessThresholdSec: number;
|
|
1174
1174
|
/** False when directory integration is not wired for this deployment. 未結線なら false。 */
|
|
1175
1175
|
isEnabled: boolean;
|
|
1176
|
+
/**
|
|
1177
|
+
* Milliseconds to reuse a loaded tenant-membership policy. 帰属方針を再利用するミリ秒。
|
|
1178
|
+
*
|
|
1179
|
+
* 既定は 0 (毎回引き直す)。帰属証明は **権限を 1 件も読む前に毎リクエスト走る** ため、
|
|
1180
|
+
* 証明グループを 1 つも宣言していない配備でも 3 表の結合が 1 往復増える。
|
|
1181
|
+
*
|
|
1182
|
+
* ⚠️ 既定を非 0 にしない。認可の判断材料を、頼まれてもいないのに黙って古くするライブラリは
|
|
1183
|
+
* 信用できない。有効化は配備側が「どれだけ古くてよいか」を決めて明示する。
|
|
1184
|
+
* 本ストア自身の書き込み (証明グループの増減・連携の作成解除) は自動で破棄するため、
|
|
1185
|
+
* 遅れが出るのは **記憶域を直接編集した場合だけ** である。
|
|
1186
|
+
*/
|
|
1187
|
+
policyCacheTtlMs?: number;
|
|
1176
1188
|
};
|
|
1177
1189
|
/** One external link row as the admin surface sees it. 管理面が見る外部リンク行。 */
|
|
1178
1190
|
type DirectoryLinkRow = AuthGroupExternalLink & {
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var O=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var de=Object.prototype.hasOwnProperty;var ue=(t,o)=>{for(var g in o)O(t,g,{get:o[g],enumerable:!0})},le=(t,o,g,I)=>{if(o&&typeof o=="object"||typeof o=="function")for(let i of ae(o))!de.call(t,i)&&i!==g&&O(t,i,{get:()=>o[i],enumerable:!(I=se(o,i))||I.enumerable});return t};var pe=t=>le(O({},"__esModule",{value:!0}),t);var Ge={};ue(Ge,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>ne,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>we,MAX_IN_LIST:()=>Ke,MAX_PARAMETERS:()=>te,MEMBERSHIP_MODES:()=>H,UNHEALTHY_SYNC_OUTCOMES:()=>W,assertGroupMayBeLinked:()=>ye,assertMembershipMode:()=>P,chunk:()=>M,chunkByParameters:()=>L,createDirectoryLinkStore:()=>ge,createDirectorySync:()=>Ae,createDirectorySyncRepository:()=>xe,createPromotionRepository:()=>fe,createSyncRequestQueue:()=>ve,defaultExternalGroupGuardMessage:()=>ce,defaultSyncMessages:()=>Y,defineAuthDirectorySchema:()=>Se,findLedgerRowsForIdentity:()=>N,promoteLedgerForUser:()=>he,withExternalGroupGuards:()=>me});module.exports=pe(Ge);var Z=require("@aiquants/authz-core"),ce=(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 C(t,o,g,I){return new Set(t.filter(i=>i.roleId===o&&i.appKey===g&&i.resourceKey===I).map(i=>i.action))}var j=t=>Z.AUTHZ_ACTIONS.every(o=>t.has(o)),F=(t,o)=>t.find(g=>g.roleId===o)?.roleKey??`#${o}`;function me(t,o){let g=t;async function I(i,a,n){if(a.size===0)return;let c=await g.listGroupRoles(i),p=new Set;for(let u of c){if(!a.has(u.roleId)||p.has(u.groupId))continue;p.add(u.groupId);let f=await o.linkLookup.externalGroupKey(i,u.groupId);if(f!==null)throw new Error(o.message(u.roleKey||F(n,u.roleId),f))}}return{...t,async assignGroupRole(i,a,n,c){let p=await g.listGrants(i);if(j(C(p,n,o.adminAppKey,o.adminResourceKey))){let u=await o.linkLookup.externalGroupKey(i,a);if(u!==null)throw new Error(o.message(F(p,n),u))}return t.assignGroupRole(i,a,n,c)},async createGrant(i,a,n){let p=(await g.listResources(i)).find(u=>u.id===a.resourceId);if(p?.appKey===o.adminAppKey&&p?.resourceKey===o.adminResourceKey){let u=await g.listGrants(i),f=C(u,a.roleId,o.adminAppKey,o.adminResourceKey);f.add(a.action),j(f)&&await I(i,new Set([a.roleId]),u)}return t.createGrant(i,a,n)}}}async function ye(t,o,g,I){let i=(await t.listGroupRoles(o)).filter(n=>n.groupId===g);if(i.length===0)return;let a=await t.listGrants(o);for(let n of i)if(j(C(a,n.roleId,I.adminAppKey,I.adminResourceKey)))throw new Error(I.message(n.roleKey||F(a,n.roleId),n.groupKey))}var _=require("drizzle-orm");async function N(t,o,g){let{ledger:I,group:i}=o,{tenantId:a,externalUserId:n,email:c}=g;if(n===null&&c==="")return[];let p={ledgerId:I.id,groupId:I.groupId,tenantId:i.tenantId,externalUserId:I.externalUserId},u=async b=>(await t.select(p).from(I).innerJoin(i,(0,_.and)((0,_.eq)(i.id,I.groupId),(0,_.eq)(i.tenantId,a),(0,_.eq)(i.isActive,!0))).where(b)).map(S=>({ledgerId:Number(S.ledgerId),groupId:Number(S.groupId),tenantId:String(S.tenantId),externalUserId:S.externalUserId===null?null:String(S.externalUserId)})),f=b=>{let D=new Map;for(let S of b)D.set(S.ledgerId,{ledgerId:S.ledgerId,groupId:S.groupId,tenantId:S.tenantId});return[...D.values()].sort((S,r)=>S.ledgerId-r.ledgerId)},e=n===null?[]:await u((0,_.eq)(I.externalUserId,n));if(c==="")return f(e);let h=await u((0,_.eq)(I.email,c)),v=b=>n!==null&&b.externalUserId!==null&&b.externalUserId!==n;return f([...e,...h.filter(b=>!v(b))])}var q=require("@aiquants/auth-core"),ee=require("@aiquants/auth-directory-core"),x=require("@aiquants/authz-core"),d=require("drizzle-orm");var H=["direct","transitive"];function P(t){if(!H.includes(t))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${H.join(" | ")}, received ${JSON.stringify(t)}`);return t}function ge(t,o,g,I){let i=t,a=I.ensureConnection??(async()=>{}),{TDGroupExternalLink:n,TDGroupMemberEmail:c,TDTenantMembershipGroup:p,TDAllowlistGroup:u,TDDirectorySyncRequest:f}=o,{TMGroup:e,TDGroupMember:h}=g;async function v(r,s,y){if((await r.select({groupId:p.groupId}).from(p).where((0,d.and)((0,d.eq)(p.tenantId,s),(0,d.eq)(p.groupId,y),(0,d.eq)(p.isBreakGlass,!0)))).length>0)throw new Error(`Group ${y} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function b(r,s,y){let[m]=await r.select({id:e.id}).from(e).where((0,d.and)((0,d.eq)(e.id,y),(0,d.eq)(e.tenantId,s)));if(!m)throw new Error(`Group not found in tenant: ${y}`)}function D(r,s){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:P(String(r.membershipMode)),isSyncEnabled:!!r.isSyncEnabled,...r.syncedAt?{syncedAt:new Date(r.syncedAt).toISOString()}:{},...r.syncError?{syncError:String(r.syncError)}:{},unresolvedCount:s}}async function S(r,s){let y=await r.select({groupId:c.groupId,n:(0,d.count)()}).from(c).innerJoin(e,(0,d.and)((0,d.eq)(e.id,c.groupId),(0,d.eq)(e.tenantId,s))).where((0,d.isNull)(c.resolvedUserId)).groupBy(c.groupId);return new Map(y.map(m=>[Number(m.groupId),Number(m.n)]))}return{async listLinks(r){(0,x.assertTenantId)(r,"listLinks(tenantId)"),await a();let s=await i.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,d.eq)(n.tenantId,r)).orderBy((0,d.asc)(n.groupId)),y=await S(i,r);return s.map(m=>((0,x.assertTenantMatch)(r,String(m.tenantId),"listLinks(row.tenantId)"),D(m,y.get(Number(m.groupId))??0)))},async externalGroupKey(r,s){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await a();let[y]=await i.select({externalKey:n.externalKey}).from(n).where((0,d.and)((0,d.eq)(n.groupId,s),(0,d.eq)(n.tenantId,r)));return y?String(y.externalKey):null},async linkGroup(r,s,y,m){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let w=P(y.membershipMode),T=y.externalId.trim(),R=(0,q.normalizeAuthEmail)(y.externalKey);if(T===""||R==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await a(),await I.linkable.assertLinkable(r,s),await i.transaction(async K=>{await b(K,r,s),await v(K,r,s),await K.insert(n).output().values({groupId:s,tenantId:r,provider:y.provider,externalId:T,externalKey:R,membershipMode:w,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:m})})},async unlinkGroup(r,s){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await a(),await i.transaction(async y=>{await b(y,r,s),await y.delete(c).where((0,d.eq)(c.groupId,s)),await y.delete(u).where((0,d.and)((0,d.eq)(u.groupId,s),(0,d.eq)(u.tenantId,r))),await y.delete(n).where((0,d.and)((0,d.eq)(n.groupId,s),(0,d.eq)(n.tenantId,r)))})},async setSyncEnabled(r,s,y,m){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await a(),await i.update(n).set({isSyncEnabled:y,updatedAt:new Date,updatedBy:m}).output().where((0,d.and)((0,d.eq)(n.groupId,s),(0,d.eq)(n.tenantId,r)))},async requestSync(r,s,y){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await a(),await i.transaction(async m=>{s!==null&&await b(m,r,s),await m.insert(f).output().values({tenantId:r,groupId:s,requestedBy:y,requestedAt:new Date,createdAt:new Date,createdBy:y})})},async getStatus(r){(0,x.assertTenantId)(r,"getStatus(tenantId)"),await a();let s=await i.select({groupId:n.groupId,isSyncEnabled:n.isSyncEnabled,syncedAt:n.syncedAt,syncError:n.syncError}).from(n).where((0,d.eq)(n.tenantId,r)),[y]=await i.select({n:(0,d.count)()}).from(f).where((0,d.and)((0,d.eq)(f.tenantId,r),(0,d.isNull)(f.finishedAt))),m=s.filter(k=>!!k.isSyncEnabled),w=m.map(k=>k.syncedAt?new Date(k.syncedAt).getTime():null),T=w.length>0&&w.every(k=>k!==null)?Math.min(...w):null,K=s.length>0&&m.length===0||m.length>0&&(T===null||Date.now()-T>I.stalenessThresholdSec*1e3);return{isEnabled:I.isEnabled,linkedGroupCount:s.length,...T!==null?{oldestSyncedAt:new Date(T).toISOString()}:{},isStale:K,pausedGroupCount:s.length-m.length,stalenessThresholdSec:I.stalenessThresholdSec,erroredGroupIds:s.filter(k=>!!k.syncError).map(k=>Number(k.groupId)).sort((k,ie)=>k-ie),pendingRequestCount:Number(y?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)"),await a();let s=await i.select({groupId:p.groupId,isBreakGlass:p.isBreakGlass,externalLinkId:n.id}).from(p).innerJoin(e,(0,d.and)((0,d.eq)(e.id,p.groupId),(0,d.eq)(e.tenantId,r))).leftJoin(n,(0,d.eq)(n.groupId,p.groupId)).where((0,d.eq)(p.tenantId,r));if(s.length===0)return{kind:"no-proof-required"};let y=s.map(T=>Number(T.groupId)),m=s.filter(T=>!!T.isBreakGlass);for(let T of m)if(T.externalLinkId!==null)throw new Error(`Break-glass group ${Number(T.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let w={kind:"require-proof",proofGroupIds:y,breakGlassGroupIds:m.map(T=>Number(T.groupId))};return(0,ee.assertTenantMembershipPolicy)(w,`tenant ${r}`),w},async setTenantMembershipGroup(r,s,y,m){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await a(),await i.transaction(async w=>{if(await b(w,r,s),y){let[R]=await w.select({id:n.id}).from(n).where((0,d.and)((0,d.eq)(n.tenantId,r),(0,d.eq)(n.groupId,s)));if(R)throw new Error(`Group ${s} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}if((await w.select({groupId:p.groupId}).from(p).where((0,d.and)((0,d.eq)(p.tenantId,r),(0,d.eq)(p.groupId,s)))).length>0){await w.update(p).set({isBreakGlass:y,updatedAt:new Date,updatedBy:m}).output().where((0,d.and)((0,d.eq)(p.tenantId,r),(0,d.eq)(p.groupId,s)));return}await w.insert(p).output().values({tenantId:r,groupId:s,isBreakGlass:y,createdAt:new Date,createdBy:m})})},async removeTenantMembershipGroup(r,s){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await a(),await i.transaction(async y=>{let w=(await y.select({groupId:p.groupId,isBreakGlass:p.isBreakGlass}).from(p).where((0,d.eq)(p.tenantId,r))).filter(T=>Number(T.groupId)!==s);if(w.length>0&&!w.some(T=>!!T.isBreakGlass))throw new Error(`Removing group ${s} would leave tenant ${r} with a membership proof but no break-glass path.`);await y.delete(p).where((0,d.and)((0,d.eq)(p.tenantId,r),(0,d.eq)(p.groupId,s)))})},async setGroupActive(r,s,y,m){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await a(),await i.update(e).set({isActive:y,updatedAt:new Date,updatedBy:m}).output().where((0,d.and)((0,d.eq)(e.id,s),(0,d.eq)(e.tenantId,r)))},async listLedgerGroupIds(r,s){if((0,x.assertTenantId)(r,"listLedgerGroupIds(tenantId)"),!I.isEnabled)return[];await a();let y=await N(i,{ledger:c,group:e},{tenantId:r,externalUserId:s.externalUserId?.trim()||null,email:(0,q.normalizeAuthEmail)(s.email)});return[...new Set(y.map(m=>m.groupId))].sort((m,w)=>m-w)},async isEmailAllowedByGroup(r){if(!I.isEnabled)return!1;let s=(0,q.normalizeAuthEmail)(r);if(s==="")return!1;await a();let[y]=await i.select({id:c.id}).from(c).innerJoin(u,(0,d.eq)(u.groupId,c.groupId)).innerJoin(e,(0,d.and)((0,d.eq)(e.id,c.groupId),(0,d.eq)(e.isActive,!0))).where((0,d.eq)(c.email,s));return!!y},async listAllowlistGroups(r){return(0,x.assertTenantId)(r,"listAllowlistGroups(tenantId)"),await a(),(await i.select({id:u.id,groupId:u.groupId,groupKey:e.groupKey,groupName:e.name,description:u.description}).from(u).innerJoin(e,(0,d.and)((0,d.eq)(e.id,u.groupId),(0,d.eq)(e.tenantId,r))).where((0,d.eq)(u.tenantId,r)).orderBy((0,d.asc)(e.name))).map(y=>({id:Number(y.id),type:"group",groupId:Number(y.groupId),groupKey:String(y.groupKey),groupName:String(y.groupName),...y.description?{description:String(y.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,s,y,m){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await a(),await i.transaction(async w=>{await b(w,r,s);let[T]=await w.select({id:n.id}).from(n).where((0,d.and)((0,d.eq)(n.groupId,s),(0,d.eq)(n.tenantId,r)));if(!T)throw new Error(`Group ${s} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await w.insert(u).output().values({tenantId:r,groupId:s,description:y??null,createdAt:new Date,createdBy:m})})},async removeAllowlistGroup(r,s){(0,x.assertTenantId)(r,"removeAllowlistGroup(tenantId)"),await a(),await i.delete(u).where((0,d.and)((0,d.eq)(u.id,s),(0,d.eq)(u.tenantId,r)))},async countProjectedMembers(r,s){(0,x.assertTenantId)(r,"countProjectedMembers(tenantId)"),await a();let[y]=await i.select({n:(0,d.count)()}).from(h).innerJoin(e,(0,d.and)((0,d.eq)(e.id,h.groupId),(0,d.eq)(e.tenantId,r))).where((0,d.eq)(h.groupId,s));return Number(y?.n??0)}}}var re=require("@aiquants/auth-core"),z=require("@aiquants/authz-core");async function he(t,o){let g=(0,z.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 I=(0,re.normalizeAuthEmail)(o.email),i=o.externalUserId?.trim()||null;if(i===null&&I==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:g,externalUserId:i,email:I});if(a.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let u of a)(0,z.assertTenantMatch)(g,u.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let n=[...new Set(a.map(u=>u.ledgerId))].sort((u,f)=>u-f),c=[...new Set(a.map(u=>u.groupId))].sort((u,f)=>u-f);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:g,userId:o.userId,ledgerIds:n,groupIds:c})].sort((u,f)=>u-f),matchedLedgerIds:n}}var J=require("@aiquants/authz-core"),G=require("drizzle-orm");var te=2100;var Ke=500;function M(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 I=0;I<t.length;I+=o)g.push(t.slice(I,I+o));return g}function L(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 I=2100-g,i=Math.floor(I/o);if(i<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${I} available in one statement`);return M(t,i)}var be=4,Ie=100;function fe(t,o,g,I){let i=t,a=I.ensureConnection??(async()=>{}),n=I.now??(()=>new Date),c=o.TDGroupMemberEmail,{TMGroup:p,TDGroupMember:u}=g;return{async findLedgerMatches(f){return(0,J.assertTenantId)(f.tenantId,"findLedgerMatches(tenantId)"),await a(),N(i,{ledger:c,group:p},f)},async attachUserToGroups({tenantId:f,userId:e,ledgerIds:h,groupIds:v}){return(0,J.assertTenantId)(f,"attachUserToGroups(tenantId)"),await a(),h.length===0?[]:i.transaction(async b=>{let D=new Set;for(let m of M(v)){let w=await b.select({id:p.id}).from(p).where((0,G.and)((0,G.inArray)(p.id,m),(0,G.eq)(p.tenantId,f),(0,G.eq)(p.isActive,!0)));for(let T of w)D.add(Number(T.id))}let S=v.filter(m=>D.has(m));if(S.length===0)return[];let r=new Set;for(let m of M(S)){let w=await b.select({groupId:u.groupId}).from(u).where((0,G.and)((0,G.eq)(u.userId,e),(0,G.inArray)(u.groupId,m)));for(let T of w)r.add(Number(T.groupId))}let s=S.filter(m=>!r.has(m)),y=Math.max(1,2100-Ie-S.length);for(let m of M(h,y))await b.update(c).set({resolvedUserId:e}).output().where((0,G.and)((0,G.inArray)(c.id,m),(0,G.inArray)(c.groupId,S)));if(s.length>0){let m=n();for(let w of L(s,be))await b.insert(u).output().values(w.map(T=>({groupId:T,userId:e,createdAt:m,createdBy:I.actor})))}return s})}}}var $=require("@aiquants/authz-drizzle"),l=require("drizzle-orm/mssql-core"),ne="provider",we=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TMAllowlist"];function Se(t,o){let g=(0,l.mssqlSchema)(t),I=()=>({createdAt:(0,l.datetime2)("created_at"),createdBy:(0,l.nvarchar)("created_by",{length:50}),updatedAt:(0,l.datetime2)("updated_at"),updatedBy:(0,l.nvarchar)("updated_by",{length:50})}),i=()=>({createdAt:(0,l.datetime2)("created_at"),createdBy:(0,l.nvarchar)("created_by",{length:50})}),a=g.table("TDGroupExternalLink",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,l.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,l.nvarchar)($.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,l.nvarchar)(ne,{length:32}).notNull(),externalId:(0,l.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,l.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,l.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,l.bit)("is_sync_enabled").notNull(),syncedAt:(0,l.datetime2)("synced_at"),syncError:(0,l.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,l.int)("vanished_observations").notNull(),...I()},e=>[(0,l.primaryKey)({name:`${t}_TDGroupExternalLink_pk`,columns:[e.id]}),(0,l.uniqueIndex)(`${t}_TDGroupExternalLink_group_uq`).on(e.groupId),(0,l.uniqueIndex)(`${t}_TDGroupExternalLink_external_uq`).on(e.tenantId,e.provider,e.externalId),(0,l.foreignKey)({name:`${t}_TDGroupExternalLink_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),n=g.table("TDGroupMemberEmail",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,l.bigint)("group_id",{mode:"number"}).notNull(),email:(0,l.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,l.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,l.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,l.datetime2)("observed_at").notNull(),...I()},e=>[(0,l.primaryKey)({name:`${t}_TDGroupMemberEmail_pk`,columns:[e.id]}),(0,l.uniqueIndex)(`${t}_TDGroupMemberEmail_group_email_uq`).on(e.groupId,e.email),(0,l.foreignKey)({name:`${t}_TDGroupMemberEmail_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),c=g.table("TDTenantMembershipGroup",{tenantId:(0,l.nvarchar)($.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,l.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,l.bit)("is_break_glass").notNull(),...i()},e=>[(0,l.primaryKey)({name:`${t}_TDTenantMembershipGroup_pk`,columns:[e.tenantId,e.groupId]}),(0,l.foreignKey)({name:`${t}_TDTenantMembershipGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),p=g.table("TDAllowlistGroup",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,l.nvarchar)($.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,l.bigint)("group_id",{mode:"number"}).notNull(),description:(0,l.nvarchar)("description",{length:512}),...i()},e=>[(0,l.primaryKey)({name:`${t}_TDAllowlistGroup_pk`,columns:[e.id]}),(0,l.uniqueIndex)(`${t}_TDAllowlistGroup_tenant_group_uq`).on(e.tenantId,e.groupId),(0,l.foreignKey)({name:`${t}_TDAllowlistGroup_group_id_fk`,columns:[e.groupId],foreignColumns:[o.groupTable.id]})]),u=g.table("TDDirectorySyncRequest",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,l.nvarchar)($.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,l.bigint)("group_id",{mode:"number"}),requestedBy:(0,l.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,l.datetime2)("requested_at").notNull(),pickedAt:(0,l.datetime2)("picked_at"),finishedAt:(0,l.datetime2)("finished_at"),result:(0,l.nvarchar)("result",{length:32}),error:(0,l.nvarchar)("error",{length:2e3}),...i()},e=>[(0,l.primaryKey)({name:`${t}_TDDirectorySyncRequest_pk`,columns:[e.id]})]),f=g.table("TMAllowlist",{id:(0,l.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,l.nvarchar)($.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,l.nvarchar)("pattern",{length:256}).notNull(),type:(0,l.nvarchar)("type",{length:32}).notNull(),description:(0,l.nvarchar)("description",{length:512}),...i()},e=>[(0,l.primaryKey)({name:`${t}_TMAllowlist_pk`,columns:[e.id]}),(0,l.uniqueIndex)(`${t}_TMAllowlist_pattern_uq`).on(e.tenantId,e.pattern)]);return{TDGroupExternalLink:a,TDGroupMemberEmail:n,TDTenantMembershipGroup:c,TDAllowlistGroup:p,TDDirectorySyncRequest:u,TMAllowlist:f}}var B=require("@aiquants/auth-directory-core");var V=(t,o=20)=>t.length<=o?t.join(", "):`${t.slice(0,o).join(", ")} \u307B\u304B ${t.length-o} \u4EF6`,Y={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 (${V(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: ${V(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 ${V(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 W=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider"];function Ae(t){let{directory:o,repository:g,policy:I,now:i,logger:a}=t,n=t.messages??Y;if((0,B.assertBlastRadiusPolicy)(I),!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 c(e,h){try{await g.recordFailure(e,h)}catch(v){a.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,v)}}async function p(e){try{let h=await o.getGroup(e.externalId);return h===null?{kind:"missing"}:{kind:"ok",group:h,members:await o.listGroupMembers(h,e.membershipMode)}}catch(h){return{kind:"failed",error:h instanceof Error?h:new Error(String(h))}}}async function u(e){let h=await g.recordVanishedObservation(e,n.groupMissing(e.externalKey,e.vanishedObservations+1,t.vanishedThreshold));return h<t.vanishedThreshold?(a.warn(`[auth-directory] ${e.externalKey} missing upstream (${h}/${t.vanishedThreshold})`),{kind:"missing",link:e,observations:h}):(await g.markGroupInactive(e),await c(e,n.groupVanished(e.externalKey,h)),a.warn(`[auth-directory] ${e.externalKey} deactivated after ${h} consecutive absences`),{kind:"vanished",link:e})}async function f(e){if(e.provider!==t.provider)return await c(e,n.foreignProvider(e.provider,t.provider)),a.warn(`[auth-directory] skipping ${e.externalKey}: belongs to provider ${e.provider}`),{kind:"foreign-provider",link:e};let h=await p(e);if(h.kind==="missing")return u(e);if(h.kind==="failed")return await c(e,n.upstreamUnreadable(h.error.message)),a.error(`[auth-directory] upstream read failed for ${e.externalKey}:`,h.error),{kind:"upstream-failed",link:e,error:h.error};try{let[v,b]=await Promise.all([g.loadGroupSnapshot(e),g.resolveMembers(h.members)]);if(b.length!==h.members.length){let y=n.resolverIncomplete(h.members.length,b.length);return await c(e,y),a.error(`[auth-directory] ${y}`),{kind:"local-failed",link:e,error:new Error(y)}}let D=(0,B.reconcile)(v,b,I);if(D.aborted)return await c(e,n.planRefused(D.aborted)),a.warn(`[auth-directory] plan refused for ${e.externalKey}: ${D.aborted.reason}`),{kind:"aborted",link:e,plan:D};let S=D.ledgerUpserts.length>0||D.ledgerDeletes.length>0||D.projectionAdds.length>0||D.projectionRemoves.length>0,r=i(),s={refusals:[]};return S&&(s=await g.applyPlan(e,D,r)),await g.recordSuccess(e,h.group,r),s.refusals.length>0&&(await c(e,n.removalsRefused(s.refusals)),a.warn(`[auth-directory] ${s.refusals.length} removal(s) refused for ${e.externalKey}`)),D.unresolvedEmails.length>0&&a.info(`[auth-directory] ${e.externalKey}: ${D.unresolvedEmails.length} upstream member(s) have no local user yet`),S?{kind:"applied",link:e,plan:D}:{kind:"unchanged",link:e}}catch(v){let b=v instanceof Error?v:new Error(String(v));return await c(e,n.localWriteFailed(b.message)),a.error(`[auth-directory] local write failed for ${e.externalKey}:`,b),{kind:"local-failed",link:e,error:b}}}return{syncOne:f,async run(e={}){let h=await g.listSyncableLinks(e),v=[];for(let D of h)try{v.push(await f(D))}catch(S){let r=S instanceof Error?S:new Error(String(S));a.error(`[auth-directory] ${D.externalKey} ended abnormally:`,r),v.push({kind:"local-failed",link:D,error:r})}let b=v.filter(D=>W.includes(D.kind)).length;return a.info(`[auth-directory] run finished: ${v.length} link(s), ${b} unhealthy`),{outcomes:v,unhealthyCount:b}}}}var oe=require("@aiquants/authz-core"),E=require("drizzle-orm"),X=2e3;function ve(t,o,g){let I=t,i=g.ensureConnection??(async()=>{}),a=o.TDDirectorySyncRequest,n=g.maxErrorLength??X;if(!Number.isInteger(n)||n<1||n>X)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${X}, received ${String(g.maxErrorLength)}`);return{async claim(){await i();let c=await I.select({id:a.id}).from(a).where((0,E.isNull)(a.pickedAt)).orderBy((0,E.asc)(a.id));for(let p of c){let f=(await I.update(a).set({pickedAt:g.now()}).output().where((0,E.and)((0,E.eq)(a.id,Number(p.id)),(0,E.isNull)(a.pickedAt))))?.[0];if(!f)continue;let e=String(f.tenant_id??f.tenantId??""),h=f.group_id??f.groupId??null;return{id:Number(f.id),tenantId:(0,oe.assertTenantId)(e,"claim(row.tenantId)"),groupId:h===null?null:Number(h),requestedBy:String(f.requested_by??f.requestedBy??"")}}return null},async finish(c,p){await i();let u=p.error?p.error.slice(0,n):null;await I.update(a).set({finishedAt:g.now(),result:p.result,error:u}).output().where((0,E.eq)(a.id,c))},async listStranded(c){return await i(),(await I.select({id:a.id,pickedAt:a.pickedAt}).from(a).where((0,E.isNull)(a.finishedAt)).orderBy((0,E.asc)(a.id))).filter(u=>u.pickedAt!==null&&new Date(u.pickedAt)<c).map(u=>Number(u.id))}}}var Q=require("@aiquants/auth-core"),U=require("@aiquants/authz-core"),A=require("drizzle-orm");var Te=7,De=4;function xe(t,o,g,I){let i=t,a=I.ensureConnection??(async()=>{}),{TDGroupExternalLink:n,TDGroupMemberEmail:c}=o,{TMGroup:p,TDGroupMember:u}=g,f=I.actor;return{async listSyncableLinks(e){await a();let h=[(0,A.eq)(n.isSyncEnabled,!0)];return e.tenantId!==void 0&&h.push((0,A.eq)(n.tenantId,(0,U.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&h.push((0,A.eq)(n.groupId,e.groupId)),(await i.select({linkId:n.id,groupId:n.groupId,linkTenantId:n.tenantId,groupTenantId:p.tenantId,groupKey:p.groupKey,isGroupActive:p.isActive,provider:n.provider,externalId:n.externalId,externalKey:n.externalKey,membershipMode:n.membershipMode,vanishedObservations:n.vanishedObservations}).from(n).innerJoin(p,(0,A.eq)(p.id,n.groupId)).where((0,A.and)(...h))).map(b=>((0,U.assertTenantMatch)(String(b.groupTenantId),String(b.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(b.linkId),groupId:Number(b.groupId),groupKey:String(b.groupKey),tenantId:String(b.groupTenantId),provider:String(b.provider),externalId:String(b.externalId),externalKey:String(b.externalKey),membershipMode:P(String(b.membershipMode)),isGroupActive:!!b.isGroupActive,vanishedObservations:Number(b.vanishedObservations??0)}))},async loadGroupSnapshot(e){await a();let[h,v]=await Promise.all([i.select({email:c.email,externalUserId:c.externalUserId,resolvedUserId:c.resolvedUserId}).from(c).where((0,A.eq)(c.groupId,e.groupId)),i.select({userId:u.userId}).from(u).where((0,A.eq)(u.groupId,e.groupId))]);return{ledger:h.map(b=>({email:String(b.email),...b.externalUserId?{externalUserId:String(b.externalUserId)}:{},resolvedUserId:b.resolvedUserId===null?null:Number(b.resolvedUserId)})),projectedUserIds:v.map(b=>Number(b.userId))}},resolveMembers(e){return I.memberResolver.resolve(e)},async applyPlan(e,h,v){(0,U.assertTenantId)(e.tenantId,"applyPlan(link.tenantId)"),await a(),(h.ledgerUpserts.length>0||h.projectionAdds.length>0)&&await i.transaction(async S=>{let r=h.ledgerUpserts.map(m=>({...m,email:(0,Q.normalizeAuthEmail)(m.email)})),s=new Map;for(let m of M(r.map(w=>w.email))){let w=await S.select({id:c.id,email:c.email}).from(c).where((0,A.and)((0,A.eq)(c.groupId,e.groupId),(0,A.inArray)(c.email,m)));for(let T of w)s.set(String(T.email),Number(T.id))}let y=r.filter(m=>!s.has(m.email));for(let m of L(y,Te))await S.insert(c).output().values(m.map(w=>({groupId:e.groupId,email:w.email,externalUserId:w.externalUserId??null,resolvedUserId:w.resolvedUserId,observedAt:v,createdAt:v,createdBy:f})));for(let m of r){let w=s.get(m.email);w!==void 0&&await S.update(c).set({externalUserId:m.externalUserId??null,resolvedUserId:m.resolvedUserId,observedAt:v,updatedAt:v,updatedBy:f}).output().where((0,A.eq)(c.id,w))}for(let m of L(h.projectionAdds,De)){let w=new Set((await S.select({userId:u.userId}).from(u).where((0,A.and)((0,A.eq)(u.groupId,e.groupId),(0,A.inArray)(u.userId,m)))).map(R=>Number(R.userId))),T=m.filter(R=>!w.has(R));T.length>0&&await S.insert(u).output().values(T.map(R=>({groupId:e.groupId,userId:R,createdAt:v,createdBy:f})))}});let b=[];for(let S of h.projectionRemoves)try{await I.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:S}},"remove-group-member",r=>r.delete(u).where((0,A.and)((0,A.eq)(u.groupId,e.groupId),(0,A.eq)(u.userId,S))))}catch(r){b.push({userId:S,applied:!1,refusal:r instanceof Error?r.message:String(r)})}let D=b.map(S=>S.userId);return h.ledgerDeletes.length>0&&await i.transaction(async S=>{for(let r of M(h.ledgerDeletes)){let s=D.length>0?(0,A.or)((0,A.isNull)(c.resolvedUserId),(0,A.notInArray)(c.resolvedUserId,D)):void 0;await S.delete(c).where((0,A.and)((0,A.eq)(c.groupId,e.groupId),(0,A.inArray)(c.email,r),...s?[s]:[]))}}),{refusals:b}},async recordSuccess(e,h,v){await a(),await i.transaction(async b=>{await b.update(n).set({syncedAt:v,syncError:null,vanishedObservations:0,externalId:h.externalId,externalKey:(0,Q.normalizeAuthEmail)(h.externalKey),updatedAt:v,updatedBy:f}).output().where((0,A.eq)(n.id,e.linkId)),e.isGroupActive||await b.update(p).set({isActive:!0,updatedAt:v,updatedBy:f}).output().where((0,A.and)((0,A.eq)(p.id,e.groupId),(0,A.eq)(p.tenantId,e.tenantId)))})},async recordFailure(e,h){await a(),await i.update(n).set({syncError:h.slice(0,1e3),updatedAt:new Date,updatedBy:f}).output().where((0,A.eq)(n.id,e.linkId))},async recordVanishedObservation(e,h){await a();let v=e.vanishedObservations+1;return await i.update(n).set({vanishedObservations:v,syncError:h.slice(0,1e3),updatedAt:new Date,updatedBy:f}).output().where((0,A.eq)(n.id,e.linkId)),v},async markGroupInactive(e){await a(),await i.update(p).set({isActive:!1,updatedAt:new Date,updatedBy:f}).output().where((0,A.and)((0,A.eq)(p.id,e.groupId),(0,A.eq)(p.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 ue=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var ce=(t,o)=>{for(var y in o)F(t,y,{get:o[y],enumerable:!0})},me=(t,o,y,g)=>{if(o&&typeof o=="object"||typeof o=="function")for(let s of le(o))!pe.call(t,s)&&s!==y&&F(t,s,{get:()=>o[s],enumerable:!(g=ue(o,s))||g.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:()=>qe,MAX_PARAMETERS:()=>ie,MEMBERSHIP_MODES:()=>Y,UNHEALTHY_SYNC_OUTCOMES:()=>Z,assertGroupMayBeLinked:()=>be,assertMembershipMode:()=>K,chunk:()=>L,chunkByParameters:()=>N,createDirectoryLinkStore:()=>fe,createDirectorySync:()=>De,createDirectorySyncRepository:()=>Ee,createPromotionRepository:()=>Ae,createSyncRequestQueue:()=>xe,defaultExternalGroupGuardMessage:()=>ge,defaultSyncMessages:()=>Q,defineAuthDirectorySchema:()=>Te,findLedgerRowsForIdentity:()=>z,promoteLedgerForUser:()=>we,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,y,g){return new Set(t.filter(s=>s.roleId===o&&s.appKey===y&&s.resourceKey===g).map(s=>s.action))}var J=t=>te.AUTHZ_ACTIONS.every(o=>t.has(o)),V=(t,o)=>t.find(y=>y.roleId===o)?.roleKey??`#${o}`;function he(t,o){let y=t;async function g(s,a,m){if(a.size===0)return;let b=await y.listGroupRoles(s),f=new Set;for(let n of b){if(!a.has(n.roleId)||f.has(n.groupId))continue;f.add(n.groupId);let h=await o.linkLookup.externalGroupKey(s,n.groupId);if(h!==null)throw new Error(o.message(n.roleKey||V(m,n.roleId),h))}}return{...t,async assignGroupRole(s,a,m,b){let f=await y.listGrants(s);if(J(H(f,m,o.adminAppKey,o.adminResourceKey))){let n=await o.linkLookup.externalGroupKey(s,a);if(n!==null)throw new Error(o.message(V(f,m),n))}return t.assignGroupRole(s,a,m,b)},async createGrant(s,a,m){let f=(await y.listResources(s)).find(n=>n.id===a.resourceId);if(f?.appKey===o.adminAppKey&&f?.resourceKey===o.adminResourceKey){let n=await y.listGrants(s),h=H(n,a.roleId,o.adminAppKey,o.adminResourceKey);h.add(a.action),J(h)&&await g(s,new Set([a.roleId]),n)}return t.createGrant(s,a,m)}}}async function be(t,o,y,g){let s=(await t.listGroupRoles(o)).filter(m=>m.groupId===y);if(s.length===0)return;let a=await t.listGrants(o);for(let m of s)if(J(H(a,m.roleId,g.adminAppKey,g.adminResourceKey)))throw new Error(g.message(m.roleKey||V(a,m.roleId),m.groupKey))}var $=require("drizzle-orm");async function z(t,o,y){let{ledger:g,group:s}=o,{tenantId:a,externalUserId:m,email:b}=y;if(m===null&&b==="")return[];let f={ledgerId:g.id,groupId:g.groupId,tenantId:s.tenantId,externalUserId:g.externalUserId},n=async d=>(await t.select(f).from(g).innerJoin(s,(0,$.and)((0,$.eq)(s.id,g.groupId),(0,$.eq)(s.tenantId,a),(0,$.eq)(s.isActive,!0))).where(d)).map(w=>({ledgerId:Number(w.ledgerId),groupId:Number(w.groupId),tenantId:String(w.tenantId),externalUserId:w.externalUserId===null?null:String(w.externalUserId)})),h=d=>{let v=new Map;for(let w of d)v.set(w.ledgerId,{ledgerId:w.ledgerId,groupId:w.groupId,tenantId:w.tenantId});return[...v.values()].sort((w,T)=>w.ledgerId-T.ledgerId)},e=m===null?[]:await n((0,$.eq)(g.externalUserId,m));if(b==="")return h(e);let l=await n((0,$.eq)(g.email,b)),S=d=>m!==null&&d.externalUserId!==null&&d.externalUserId!==m;return h([...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 fe(t,o,y,g){let s=t,a=g.ensureConnection??(async()=>{}),m=g.policyCacheTtlMs??0;if(!Number.isFinite(m)||m<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(g.policyCacheTtlMs)}`);let b=new Map,f=r=>b.delete(r),{TDGroupExternalLink:n,TDGroupMemberEmail:h,TDTenantMembershipGroup:e,TDAllowlistGroup:l,TDDirectorySyncRequest:S}=o,{TMGroup:d,TDGroupMember:v}=y;async function w(r,i,p){if((await r.select({groupId:e.groupId}).from(e).where((0,u.and)((0,u.eq)(e.tenantId,i),(0,u.eq)(e.groupId,p),(0,u.eq)(e.isBreakGlass,!0)))).length>0)throw new Error(`Group ${p} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function T(r,i,p){let[I]=await r.select({id:d.id}).from(d).where((0,u.and)((0,u.eq)(d.id,p),(0,u.eq)(d.tenantId,i)));if(!I)throw new Error(`Group not found in tenant: ${p}`)}function k(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 p=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(p.map(I=>[Number(I.groupId),Number(I.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)),p=await _(s,r);return i.map(I=>((0,x.assertTenantMatch)(r,String(I.tenantId),"listLinks(row.tenantId)"),k(I,p.get(Number(I.groupId))??0)))},async externalGroupKey(r,i){(0,x.assertTenantId)(r,"externalGroupKey(tenantId)"),await a();let[p]=await s.select({externalKey:n.externalKey}).from(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)));return p?String(p.externalKey):null},async linkGroup(r,i,p,I){(0,x.assertTenantId)(r,"linkGroup(tenantId)");let D=K(p.membershipMode),G=p.externalId.trim(),E=(0,O.normalizeAuthEmail)(p.externalKey);if(G===""||E==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await a(),await g.linkable.assertLinkable(r,i),await s.transaction(async q=>{await T(q,r,i),await w(q,r,i),await q.insert(n).output().values({groupId:i,tenantId:r,provider:p.provider,externalId:G,externalKey:E,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:I})}),f(r)},async unlinkGroup(r,i){(0,x.assertTenantId)(r,"unlinkGroup(tenantId)"),await a(),await s.transaction(async p=>{await T(p,r,i),await p.delete(h).where((0,u.eq)(h.groupId,i)),await p.delete(l).where((0,u.and)((0,u.eq)(l.groupId,i),(0,u.eq)(l.tenantId,r))),await p.delete(n).where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))}),f(r)},async setSyncEnabled(r,i,p,I){(0,x.assertTenantId)(r,"setSyncEnabled(tenantId)"),await a(),await s.update(n).set({isSyncEnabled:p,updatedAt:new Date,updatedBy:I}).output().where((0,u.and)((0,u.eq)(n.groupId,i),(0,u.eq)(n.tenantId,r)))},async requestSync(r,i,p){(0,x.assertTenantId)(r,"requestSync(tenantId)"),await a(),await s.transaction(async I=>{i!==null&&await T(I,r,i),await I.insert(S).output().values({tenantId:r,groupId:i,requestedBy:p,requestedAt:new Date,createdAt:new Date,createdBy:p})})},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)),[p]=await s.select({n:(0,u.count)()}).from(S).where((0,u.and)((0,u.eq)(S.tenantId,r),(0,u.isNull)(S.finishedAt))),I=i.filter(R=>!!R.isSyncEnabled),D=I.map(R=>R.syncedAt?new Date(R.syncedAt).getTime():null),G=D.length>0&&D.every(R=>R!==null)?Math.min(...D):null,q=i.length>0&&I.length===0||I.length>0&&(G===null||Date.now()-G>g.stalenessThresholdSec*1e3);return{isEnabled:g.isEnabled,linkedGroupCount:i.length,...G!==null?{oldestSyncedAt:new Date(G).toISOString()}:{},isStale:q,pausedGroupCount:i.length-I.length,stalenessThresholdSec:g.stalenessThresholdSec,erroredGroupIds:i.filter(R=>!!R.syncError).map(R=>Number(R.groupId)).sort((R,de)=>R-de),pendingRequestCount:Number(p?.n??0)}},async loadTenantMembershipPolicy(r){(0,x.assertTenantId)(r,"loadTenantMembershipPolicy(tenantId)");let i=m>0?b.get(r):void 0;if(i&&Date.now()-i.loadedAt<m)return i.policy;await a();let p=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(p.length===0){let E={kind:"no-proof-required"};return m>0&&b.set(r,{policy:E,loadedAt:Date.now()}),E}let I=p.map(E=>Number(E.groupId)),D=p.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 G={kind:"require-proof",proofGroupIds:I,breakGlassGroupIds:D.map(E=>Number(E.groupId))};return(0,ne.assertTenantMembershipPolicy)(G,`tenant ${r}`),m>0&&b.set(r,{policy:G,loadedAt:Date.now()}),G},async setTenantMembershipGroup(r,i,p,I){(0,x.assertTenantId)(r,"setTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async D=>{if(await T(D,r,i),p){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:p,updatedAt:new Date,updatedBy:I}).output().where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i))),f(r);return}await D.insert(e).output().values({tenantId:r,groupId:i,isBreakGlass:p,createdAt:new Date,createdBy:I})}),f(r)},async removeTenantMembershipGroup(r,i){(0,x.assertTenantId)(r,"removeTenantMembershipGroup(tenantId)"),await a(),await s.transaction(async p=>{let D=(await p.select({groupId:e.groupId,isBreakGlass:e.isBreakGlass}).from(e).where((0,u.eq)(e.tenantId,r))).filter(G=>Number(G.groupId)!==i);if(D.length>0&&!D.some(G=>!!G.isBreakGlass))throw new Error(`Removing group ${i} would leave tenant ${r} with a membership proof but no break-glass path.`);await p.delete(e).where((0,u.and)((0,u.eq)(e.tenantId,r),(0,u.eq)(e.groupId,i)))}),f(r)},async setGroupActive(r,i,p,I){(0,x.assertTenantId)(r,"setGroupActive(tenantId)"),await a(),await s.update(d).set({isActive:p,updatedAt:new Date,updatedBy:I}).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)"),!g.isEnabled)return[];await a();let p=await z(s,{ledger:h,group:d},{tenantId:r,externalUserId:i.externalUserId?.trim()||null,email:(0,O.normalizeAuthEmail)(i.email)});return[...new Set(p.map(I=>I.groupId))].sort((I,D)=>I-D)},async isEmailAllowedByGroup(r){if(!g.isEnabled)return!1;let i=(0,O.normalizeAuthEmail)(r);if(i==="")return!1;await a();let[p]=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!!p},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(p=>({id:Number(p.id),type:"group",groupId:Number(p.groupId),groupKey:String(p.groupKey),groupName:String(p.groupName),...p.description?{description:String(p.description)}:{},owner:{kind:"tenant",tenantId:r},readOnly:!1}))},async addAllowlistGroup(r,i,p,I){(0,x.assertTenantId)(r,"addAllowlistGroup(tenantId)"),await a(),await s.transaction(async D=>{await T(D,r,i);let[G]=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(!G)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:p??null,createdAt:new Date,createdBy:I})})},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[p]=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(p?.n??0)}}}var oe=require("@aiquants/auth-core"),C=require("@aiquants/authz-core");async function we(t,o){let y=(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 g=(0,oe.normalizeAuthEmail)(o.email),s=o.externalUserId?.trim()||null;if(s===null&&g==="")return{addedGroupIds:[],matchedLedgerIds:[]};let a=await t.findLedgerMatches({tenantId:y,externalUserId:s,email:g});if(a.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let n of a)(0,C.assertTenantMatch)(y,n.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let m=[...new Set(a.map(n=>n.ledgerId))].sort((n,h)=>n-h),b=[...new Set(a.map(n=>n.groupId))].sort((n,h)=>n-h);return{addedGroupIds:[...await t.attachUserToGroups({tenantId:y,userId:o.userId,ledgerIds:m,groupIds:b})].sort((n,h)=>n-h),matchedLedgerIds:m}}var W=require("@aiquants/authz-core"),M=require("drizzle-orm");var ie=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 y=[];for(let g=0;g<t.length;g+=o)y.push(t.slice(g,g+o));return y}function N(t,o,y=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(y)||y<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(y)}`);let g=2100-y,s=Math.floor(g/o);if(s<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${o} parameters, which exceeds the ${g} available in one statement`);return L(t,s)}var Ie=4,Se=100;function Ae(t,o,y,g){let s=t,a=g.ensureConnection??(async()=>{}),m=g.now??(()=>new Date),b=o.TDGroupMemberEmail,{TMGroup:f,TDGroupMember:n}=y;return{async findLedgerMatches(h){return(0,W.assertTenantId)(h.tenantId,"findLedgerMatches(tenantId)"),await a(),z(s,{ledger:b,group:f},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:f.id}).from(f).where((0,M.and)((0,M.inArray)(f.id,r),(0,M.eq)(f.tenantId,h),(0,M.eq)(f.isActive,!0)));for(let p of i)v.add(Number(p.id))}let w=S.filter(r=>v.has(r));if(w.length===0)return[];let T=new Set;for(let r of L(w)){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 p of i)T.add(Number(p.groupId))}let k=w.filter(r=>!T.has(r)),_=Math.max(1,2100-Se-w.length);for(let r of L(l,_))await d.update(b).set({resolvedUserId:e}).output().where((0,M.and)((0,M.inArray)(b.id,r),(0,M.inArray)(b.groupId,w)));if(k.length>0){let r=m();for(let i of N(k,Ie))await d.insert(n).output().values(i.map(p=>({groupId:p,userId:e,createdAt:r,createdBy:g.actor})))}return k})}}}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 y=(0,c.mssqlSchema)(t),g=()=>({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=y.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(),...g()},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]})]),m=y.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(),...g()},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]})]),b=y.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]})]),f=y.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=y.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=y.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:m,TDTenantMembershipGroup:b,TDAllowlistGroup:f,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,y)=>`\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${y} \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",y=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(y)}\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:y,policy:g,now:s,logger:a}=t,m=t.messages??Q;if((0,j.assertBlastRadiusPolicy)(g),!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 b(e,l){try{await y.recordFailure(e,l)}catch(S){a.error(`[auth-directory] could not record the failure for ${e.externalKey}:`,S)}}async function f(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 y.recordVanishedObservation(e,m.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 y.markGroupInactive(e),await b(e,m.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 b(e,m.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 f(e);if(l.kind==="missing")return n(e);if(l.kind==="failed")return await b(e,m.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([y.loadGroupSnapshot(e),y.resolveMembers(l.members)]);if(d.length!==l.members.length){let _=m.resolverIncomplete(l.members.length,d.length);return await b(e,_),a.error(`[auth-directory] ${_}`),{kind:"local-failed",link:e,error:new Error(_)}}let v=(0,j.reconcile)(S,d,g);if(v.aborted)return await b(e,m.planRefused(v.aborted)),a.warn(`[auth-directory] plan refused for ${e.externalKey}: ${v.aborted.reason}`),{kind:"aborted",link:e,plan:v};let w=v.ledgerUpserts.length>0||v.ledgerDeletes.length>0||v.projectionAdds.length>0||v.projectionRemoves.length>0,T=s(),k={refusals:[]};return w&&(k=await y.applyPlan(e,v,T)),await y.recordSuccess(e,l.group,T),k.refusals.length>0&&(await b(e,m.removalsRefused(k.refusals)),a.warn(`[auth-directory] ${k.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`),w?{kind:"applied",link:e,plan:v}:{kind:"unchanged",link:e}}catch(S){let d=S instanceof Error?S:new Error(String(S));return await b(e,m.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 y.listSyncableLinks(e),S=[];for(let v of l)try{S.push(await h(v))}catch(w){let T=w instanceof Error?w:new Error(String(w));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,y){let g=t,s=y.ensureConnection??(async()=>{}),a=o.TDDirectorySyncRequest,m=y.maxErrorLength??ee;if(!Number.isInteger(m)||m<1||m>ee)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${ee}, received ${String(y.maxErrorLength)}`);return{async claim(){await s();let b=await g.select({id:a.id}).from(a).where((0,P.isNull)(a.pickedAt)).orderBy((0,P.asc)(a.id));for(let f of b){let h=(await g.update(a).set({pickedAt:y.now()}).output().where((0,P.and)((0,P.eq)(a.id,Number(f.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,ae.assertTenantId)(e,"claim(row.tenantId)"),groupId:l===null?null:Number(l),requestedBy:String(h.requested_by??h.requestedBy??"")}}return null},async finish(b,f){await s();let n=f.error?f.error.slice(0,m):null;await g.update(a).set({finishedAt:y.now(),result:f.result,error:n}).output().where((0,P.eq)(a.id,b))},async listStranded(b){return await s(),(await g.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)<b).map(n=>Number(n.id))}}}var re=require("@aiquants/auth-core"),B=require("@aiquants/authz-core"),A=require("drizzle-orm");var Ge=7,ke=4;function Ee(t,o,y,g){let s=t,a=g.ensureConnection??(async()=>{}),{TDGroupExternalLink:m,TDGroupMemberEmail:b}=o,{TMGroup:f,TDGroupMember:n}=y,h=g.actor;return{async listSyncableLinks(e){await a();let l=[(0,A.eq)(m.isSyncEnabled,!0)];return e.tenantId!==void 0&&l.push((0,A.eq)(m.tenantId,(0,B.assertTenantId)(e.tenantId,"listSyncableLinks(tenantId)"))),e.groupId!==void 0&&l.push((0,A.eq)(m.groupId,e.groupId)),(await s.select({linkId:m.id,groupId:m.groupId,linkTenantId:m.tenantId,groupTenantId:f.tenantId,groupKey:f.groupKey,isGroupActive:f.isActive,provider:m.provider,externalId:m.externalId,externalKey:m.externalKey,membershipMode:m.membershipMode,vanishedObservations:m.vanishedObservations}).from(m).innerJoin(f,(0,A.eq)(f.id,m.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:b.email,externalUserId:b.externalUserId,resolvedUserId:b.resolvedUserId}).from(b).where((0,A.eq)(b.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 g.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 w=>{let T=l.ledgerUpserts.map(r=>({...r,email:(0,re.normalizeAuthEmail)(r.email)})),k=new Map;for(let r of L(T.map(i=>i.email))){let i=await w.select({id:b.id,email:b.email}).from(b).where((0,A.and)((0,A.eq)(b.groupId,e.groupId),(0,A.inArray)(b.email,r)));for(let p of i)k.set(String(p.email),Number(p.id))}let _=T.filter(r=>!k.has(r.email));for(let r of N(_,Ge))await w.insert(b).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=k.get(r.email);i!==void 0&&await w.update(b).set({externalUserId:r.externalUserId??null,resolvedUserId:r.resolvedUserId,observedAt:S,updatedAt:S,updatedBy:h}).output().where((0,A.eq)(b.id,i))}for(let r of N(l.projectionAdds,ke)){let i=new Set((await w.select({userId:n.userId}).from(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.inArray)(n.userId,r)))).map(I=>Number(I.userId))),p=r.filter(I=>!i.has(I));p.length>0&&await w.insert(n).output().values(p.map(I=>({groupId:e.groupId,userId:I,createdAt:S,createdBy:h})))}});let d=[];for(let w of l.projectionRemoves)try{await g.lockoutGuard.guardedWrite(e.tenantId,{removeGroupMembership:{groupId:e.groupId,userId:w}},"remove-group-member",T=>T.delete(n).where((0,A.and)((0,A.eq)(n.groupId,e.groupId),(0,A.eq)(n.userId,w))))}catch(T){d.push({userId:w,applied:!1,refusal:T instanceof Error?T.message:String(T)})}let v=d.map(w=>w.userId);return l.ledgerDeletes.length>0&&await s.transaction(async w=>{for(let T of L(l.ledgerDeletes)){let k=v.length>0?(0,A.or)((0,A.isNull)(b.resolvedUserId),(0,A.notInArray)(b.resolvedUserId,v)):void 0;await w.delete(b).where((0,A.and)((0,A.eq)(b.groupId,e.groupId),(0,A.inArray)(b.email,T),...k?[k]:[]))}}),{refusals:d}},async recordSuccess(e,l,S){await a(),await s.transaction(async d=>{await d.update(m).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)(m.id,e.linkId)),e.isGroupActive||await d.update(f).set({isActive:!0,updatedAt:S,updatedBy:h}).output().where((0,A.and)((0,A.eq)(f.id,e.groupId),(0,A.eq)(f.tenantId,e.tenantId)))})},async recordFailure(e,l){await a(),await s.update(m).set({syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(m.id,e.linkId))},async recordVanishedObservation(e,l){await a();let S=e.vanishedObservations+1;return await s.update(m).set({vanishedObservations:S,syncError:l.slice(0,1e3),updatedAt:new Date,updatedBy:h}).output().where((0,A.eq)(m.id,e.linkId)),S},async markGroupInactive(e){await a(),await s.update(f).set({isActive:!1,updatedAt:new Date,updatedBy:h}).output().where((0,A.and)((0,A.eq)(f.id,e.groupId),(0,A.eq)(f.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
|