@truefoundry/tfy-auth-handler-lib 0.1.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/AuthCache.d.ts +40 -0
- package/dist/AuthCache.d.ts.map +1 -0
- package/dist/AuthCache.js +124 -0
- package/dist/AuthCache.js.map +1 -0
- package/dist/BucketWatcher.d.ts +69 -0
- package/dist/BucketWatcher.d.ts.map +1 -0
- package/dist/BucketWatcher.js +207 -0
- package/dist/BucketWatcher.js.map +1 -0
- package/dist/TenantCache.d.ts +90 -0
- package/dist/TenantCache.d.ts.map +1 -0
- package/dist/TenantCache.js +359 -0
- package/dist/TenantCache.js.map +1 -0
- package/dist/decompress.d.ts +2 -0
- package/dist/decompress.d.ts.map +1 -0
- package/dist/decompress.js +20 -0
- package/dist/decompress.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +12 -0
- package/dist/logger.js.map +1 -0
- package/dist/types.d.ts +267 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { JetStreamClient } from '@nats-io/jetstream';
|
|
2
|
+
import type { NatsConnection } from '@nats-io/nats-core';
|
|
3
|
+
import type { AuthCacheConfig, CachedAgent, CachedExternalIdentity, CachedExternalIdentityProvider, CachedGatewayConfig, CachedGuardrail, CachedMcpServer, CachedModel, CachedPAT, CachedRole, CachedServiceAccount, CachedTeam, CachedUser, CachedVirtualAccount, CheckAccessByResourceIdOptions, IAuthCache, ITenantCache } from './types';
|
|
4
|
+
export declare const AUTH_DATA_BUCKET_NAME = "auth-data";
|
|
5
|
+
export declare class AuthCache implements IAuthCache {
|
|
6
|
+
private readonly tenants;
|
|
7
|
+
private readonly watcher;
|
|
8
|
+
private readonly logger;
|
|
9
|
+
constructor(nc: NatsConnection | JetStreamClient, config?: AuthCacheConfig);
|
|
10
|
+
start(): Promise<void>;
|
|
11
|
+
stop(): Promise<void>;
|
|
12
|
+
isReady(): boolean;
|
|
13
|
+
getTenantCache(tenantName: string): ITenantCache | undefined;
|
|
14
|
+
checkAccessForResourceId(opts: CheckAccessByResourceIdOptions): boolean;
|
|
15
|
+
getUserTeams(tenantName: string, userEmail: string): string[];
|
|
16
|
+
searchUsers(tenantName: string, substring: string, limit?: number): CachedUser[];
|
|
17
|
+
searchTeams(tenantName: string, substring: string, limit?: number): CachedTeam[];
|
|
18
|
+
getUsers(tenantName: string): CachedUser[];
|
|
19
|
+
getTeams(tenantName: string): CachedTeam[];
|
|
20
|
+
getMcpServers(tenantName: string): CachedMcpServer[];
|
|
21
|
+
getRoles(tenantName: string): CachedRole[];
|
|
22
|
+
getExternalJwks(tenantName: string): object | null;
|
|
23
|
+
getPrimaryJwks(tenantName: string): object | null;
|
|
24
|
+
getAgents(tenantName: string): CachedAgent[];
|
|
25
|
+
getExternalIdentities(tenantName: string): CachedExternalIdentity[];
|
|
26
|
+
getModels(tenantName: string): CachedModel[];
|
|
27
|
+
getGuardrails(tenantName: string): CachedGuardrail[];
|
|
28
|
+
getGatewayConfigs(tenantName: string): CachedGatewayConfig[];
|
|
29
|
+
getServiceAccounts(tenantName: string): CachedServiceAccount[];
|
|
30
|
+
getVirtualAccounts(tenantName: string): CachedVirtualAccount[];
|
|
31
|
+
getPats(tenantName: string): CachedPAT[];
|
|
32
|
+
getExternalIdentityProviders(tenantName: string): CachedExternalIdentityProvider[];
|
|
33
|
+
getTenantNames(): string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Factory that returns the public IAuthCache interface, hiding the class
|
|
37
|
+
* internals from external callers.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createAuthCache(nc: NatsConnection | JetStreamClient, config?: AuthCacheConfig): IAuthCache;
|
|
40
|
+
//# sourceMappingURL=AuthCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthCache.d.ts","sourceRoot":"","sources":["../src/AuthCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKzD,OAAO,KAAK,EACV,eAAe,EAEf,WAAW,EACX,sBAAsB,EACtB,8BAA8B,EAC9B,mBAAmB,EACnB,eAAe,EACf,eAAe,EACf,WAAW,EACX,SAAS,EACT,UAAU,EACV,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,oBAAoB,EACpB,8BAA8B,EAC9B,UAAU,EACV,YAAY,EACb,MAAM,SAAS,CAAC;AAGjB,eAAO,MAAM,qBAAqB,cAAc,CAAC;AAKjD,qBAAa,SAAU,YAAW,UAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAE7B,EAAE,EAAE,cAAc,GAAG,eAAe,EAAE,MAAM,CAAC,EAAE,eAAe;IA8BpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,OAAO,IAAI,OAAO;IAIlB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAQ5D,wBAAwB,CAAC,IAAI,EAAE,8BAA8B,GAAG,OAAO;IAQvE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAI7D,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAIhF,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,EAAE;IAIhF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE;IAI1C,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE;IAI1C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE;IAIpD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE;IAI1C,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIlD,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIjD,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;IAI5C,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,EAAE;IAInE,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;IAI5C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE;IAIpD,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,mBAAmB,EAAE;IAI5D,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,EAAE;IAI9D,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,EAAE;IAI9D,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE;IAIxC,4BAA4B,CAAC,UAAU,EAAE,MAAM,GAAG,8BAA8B,EAAE;IAIlF,cAAc,IAAI,MAAM,EAAE;CAG3B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,GAAG,eAAe,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,UAAU,CAE1G"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthCache = exports.AUTH_DATA_BUCKET_NAME = void 0;
|
|
4
|
+
exports.createAuthCache = createAuthCache;
|
|
5
|
+
const BucketWatcher_1 = require("./BucketWatcher");
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
const TenantCache_1 = require("./TenantCache");
|
|
8
|
+
exports.AUTH_DATA_BUCKET_NAME = 'auth-data';
|
|
9
|
+
const DEFAULT_OPEN_RETRY_INTERVAL_MS = 5000;
|
|
10
|
+
const DEFAULT_OPEN_RETRY_MAX_ATTEMPTS = 60;
|
|
11
|
+
class AuthCache {
|
|
12
|
+
constructor(nc, config) {
|
|
13
|
+
this.tenants = new Map();
|
|
14
|
+
this.logger = config?.logger ?? (0, logger_1.createDefaultLogger)('AuthCache');
|
|
15
|
+
this.watcher = new BucketWatcher_1.BucketWatcher(nc, {
|
|
16
|
+
bucketName: config?.bucketName ?? exports.AUTH_DATA_BUCKET_NAME,
|
|
17
|
+
openRetryIntervalMs: config?.openRetryIntervalMs ?? DEFAULT_OPEN_RETRY_INTERVAL_MS,
|
|
18
|
+
openRetryMaxAttempts: config?.openRetryMaxAttempts ?? DEFAULT_OPEN_RETRY_MAX_ATTEMPTS,
|
|
19
|
+
}, {
|
|
20
|
+
handleUpdate: (tenantName, entity, data) => {
|
|
21
|
+
let tenant = this.tenants.get(tenantName);
|
|
22
|
+
if (!tenant) {
|
|
23
|
+
tenant = new TenantCache_1.TenantCache(config?.logger);
|
|
24
|
+
this.tenants.set(tenantName, tenant);
|
|
25
|
+
}
|
|
26
|
+
tenant.updateEntity(entity, data);
|
|
27
|
+
},
|
|
28
|
+
handleDeletion: (tenantName, entity) => {
|
|
29
|
+
this.tenants.get(tenantName)?.deleteEntity(entity);
|
|
30
|
+
},
|
|
31
|
+
handleInitialLoadComplete: () => {
|
|
32
|
+
this.logger.log('Initial load complete — cache is ready');
|
|
33
|
+
},
|
|
34
|
+
}, config?.logger);
|
|
35
|
+
}
|
|
36
|
+
async start() {
|
|
37
|
+
await this.watcher.start();
|
|
38
|
+
}
|
|
39
|
+
async stop() {
|
|
40
|
+
await this.watcher.stop();
|
|
41
|
+
}
|
|
42
|
+
isReady() {
|
|
43
|
+
return this.watcher.isReady();
|
|
44
|
+
}
|
|
45
|
+
getTenantCache(tenantName) {
|
|
46
|
+
return this.tenants.get(tenantName);
|
|
47
|
+
}
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Convenience methods — delegate to the appropriate TenantCache
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
checkAccessForResourceId(opts) {
|
|
52
|
+
const tenant = this.tenants.get(opts.tenantName);
|
|
53
|
+
if (!tenant) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return tenant.checkAccessForResourceId(opts);
|
|
57
|
+
}
|
|
58
|
+
getUserTeams(tenantName, userEmail) {
|
|
59
|
+
return this.tenants.get(tenantName)?.getUserTeams(userEmail) ?? [];
|
|
60
|
+
}
|
|
61
|
+
searchUsers(tenantName, substring, limit) {
|
|
62
|
+
return this.tenants.get(tenantName)?.searchUsers(substring, limit) ?? [];
|
|
63
|
+
}
|
|
64
|
+
searchTeams(tenantName, substring, limit) {
|
|
65
|
+
return this.tenants.get(tenantName)?.searchTeams(substring, limit) ?? [];
|
|
66
|
+
}
|
|
67
|
+
getUsers(tenantName) {
|
|
68
|
+
return this.tenants.get(tenantName)?.getUsers() ?? [];
|
|
69
|
+
}
|
|
70
|
+
getTeams(tenantName) {
|
|
71
|
+
return this.tenants.get(tenantName)?.getTeams() ?? [];
|
|
72
|
+
}
|
|
73
|
+
getMcpServers(tenantName) {
|
|
74
|
+
return this.tenants.get(tenantName)?.getMcpServers() ?? [];
|
|
75
|
+
}
|
|
76
|
+
getRoles(tenantName) {
|
|
77
|
+
return this.tenants.get(tenantName)?.getRoles() ?? [];
|
|
78
|
+
}
|
|
79
|
+
getExternalJwks(tenantName) {
|
|
80
|
+
return this.tenants.get(tenantName)?.getExternalJwks() ?? null;
|
|
81
|
+
}
|
|
82
|
+
getPrimaryJwks(tenantName) {
|
|
83
|
+
return this.tenants.get(tenantName)?.getPrimaryJwks() ?? null;
|
|
84
|
+
}
|
|
85
|
+
getAgents(tenantName) {
|
|
86
|
+
return this.tenants.get(tenantName)?.getAgents() ?? [];
|
|
87
|
+
}
|
|
88
|
+
getExternalIdentities(tenantName) {
|
|
89
|
+
return this.tenants.get(tenantName)?.getExternalIdentities() ?? [];
|
|
90
|
+
}
|
|
91
|
+
getModels(tenantName) {
|
|
92
|
+
return this.tenants.get(tenantName)?.getModels() ?? [];
|
|
93
|
+
}
|
|
94
|
+
getGuardrails(tenantName) {
|
|
95
|
+
return this.tenants.get(tenantName)?.getGuardrails() ?? [];
|
|
96
|
+
}
|
|
97
|
+
getGatewayConfigs(tenantName) {
|
|
98
|
+
return this.tenants.get(tenantName)?.getGatewayConfigs() ?? [];
|
|
99
|
+
}
|
|
100
|
+
getServiceAccounts(tenantName) {
|
|
101
|
+
return this.tenants.get(tenantName)?.getServiceAccounts() ?? [];
|
|
102
|
+
}
|
|
103
|
+
getVirtualAccounts(tenantName) {
|
|
104
|
+
return this.tenants.get(tenantName)?.getVirtualAccounts() ?? [];
|
|
105
|
+
}
|
|
106
|
+
getPats(tenantName) {
|
|
107
|
+
return this.tenants.get(tenantName)?.getPats() ?? [];
|
|
108
|
+
}
|
|
109
|
+
getExternalIdentityProviders(tenantName) {
|
|
110
|
+
return this.tenants.get(tenantName)?.getExternalIdentityProviders() ?? [];
|
|
111
|
+
}
|
|
112
|
+
getTenantNames() {
|
|
113
|
+
return Array.from(this.tenants.keys());
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.AuthCache = AuthCache;
|
|
117
|
+
/**
|
|
118
|
+
* Factory that returns the public IAuthCache interface, hiding the class
|
|
119
|
+
* internals from external callers.
|
|
120
|
+
*/
|
|
121
|
+
function createAuthCache(nc, config) {
|
|
122
|
+
return new AuthCache(nc, config);
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=AuthCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthCache.js","sourceRoot":"","sources":["../src/AuthCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { JetStreamClient } from '@nats-io/jetstream';
|
|
2
|
+
import type { NatsConnection } from '@nats-io/nats-core';
|
|
3
|
+
import type { AuthCacheLogger } from './types';
|
|
4
|
+
export interface BucketWatcherHandler {
|
|
5
|
+
handleUpdate(tenantName: string, entity: string, data: unknown): void;
|
|
6
|
+
handleDeletion(tenantName: string, entity: string): void;
|
|
7
|
+
handleInitialLoadComplete(): void;
|
|
8
|
+
}
|
|
9
|
+
export interface BucketWatcherConfig {
|
|
10
|
+
bucketName: string;
|
|
11
|
+
openRetryIntervalMs: number;
|
|
12
|
+
openRetryMaxAttempts: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Watches a NATS Object Store bucket for tenant data changes.
|
|
16
|
+
* Handles bucket open with retry, the watch loop with auto-restart on
|
|
17
|
+
* transient failures, and blob download + decompression. Parsed data is
|
|
18
|
+
* delivered to the BucketWatcherHandler.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BucketWatcher {
|
|
21
|
+
private readonly objm;
|
|
22
|
+
private readonly config;
|
|
23
|
+
private readonly handler;
|
|
24
|
+
private readonly logger;
|
|
25
|
+
private objStore;
|
|
26
|
+
private watcherStop;
|
|
27
|
+
private watchPromise;
|
|
28
|
+
private stopped;
|
|
29
|
+
/**
|
|
30
|
+
* Flipped to true when the watch() history replay delivers its
|
|
31
|
+
* end-of-history sentinel (null). Pods should NOT serve traffic until this
|
|
32
|
+
* is true.
|
|
33
|
+
*/
|
|
34
|
+
private initialLoadComplete;
|
|
35
|
+
constructor(nc: NatsConnection | JetStreamClient, config: BucketWatcherConfig, handler: BucketWatcherHandler, logger?: AuthCacheLogger);
|
|
36
|
+
start(): Promise<void>;
|
|
37
|
+
stop(): Promise<void>;
|
|
38
|
+
isReady(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The bucket is created by the hydration consumer in servicefoundry-server.
|
|
41
|
+
* On cold start the bucket may not exist yet, so we retry with backoff until
|
|
42
|
+
* it becomes available.
|
|
43
|
+
*/
|
|
44
|
+
private openBucketWithRetry;
|
|
45
|
+
/**
|
|
46
|
+
* Outer loop that survives transient failures (e.g. NATS reconnect).
|
|
47
|
+
* If the inner watch loop exits unexpectedly, we sleep briefly and retry.
|
|
48
|
+
*/
|
|
49
|
+
private startWatching;
|
|
50
|
+
/**
|
|
51
|
+
* watch({ includeHistory: true }) replays all existing objects first
|
|
52
|
+
* (with isUpdate=false), then streams live notifications (isUpdate=true).
|
|
53
|
+
*
|
|
54
|
+
* IMPORTANT: processWatchEvent (which calls getBlob) must NEVER be awaited
|
|
55
|
+
* inside the for-await loop. The watch uses a push consumer with flow
|
|
56
|
+
* control — if we block the iterator, the NATS server won't send more
|
|
57
|
+
* messages (including the blob chunks getBlob needs), causing a deadlock.
|
|
58
|
+
* All downloads are fired asynchronously and tracked via promises.
|
|
59
|
+
*/
|
|
60
|
+
private runWatchLoop;
|
|
61
|
+
/**
|
|
62
|
+
* Object key format: "{tenantName}.{entity}" where entity ∈ {authorization, users, teams}.
|
|
63
|
+
* Downloads the blob, decompresses (gzip), parses JSON, and delivers to the handler.
|
|
64
|
+
*/
|
|
65
|
+
private processWatchEvent;
|
|
66
|
+
private static parseObjectKey;
|
|
67
|
+
private sleep;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=BucketWatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BucketWatcher.d.ts","sourceRoot":"","sources":["../src/BucketWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI/C,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IACtE,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACzD,yBAAyB,IAAI,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,OAAO,CAAC,QAAQ,CAAkD;IAClE,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,OAAO,CAAS;IAExB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB,CAAS;gBAGlC,EAAE,EAAE,cAAc,GAAG,eAAe,EACpC,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,oBAAoB,EAC7B,MAAM,CAAC,EAAE,eAAe;IAQpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,OAAO,IAAI,OAAO;IAQlB;;;;OAIG;YACW,mBAAmB;IAsBjC;;;OAGG;YACW,aAAa;IAc3B;;;;;;;;;OASG;YACW,YAAY;IA6C1B;;;OAGG;YACW,iBAAiB;IAiC/B,OAAO,CAAC,MAAM,CAAC,cAAc;IAY7B,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.BucketWatcher = void 0;
|
|
27
|
+
const obj_1 = require("@nats-io/obj");
|
|
28
|
+
const Sentry = __importStar(require("@sentry/node"));
|
|
29
|
+
const decompress_1 = require("./decompress");
|
|
30
|
+
const logger_1 = require("./logger");
|
|
31
|
+
const WATCH_RESTART_DELAY_MS = 5000;
|
|
32
|
+
/**
|
|
33
|
+
* Watches a NATS Object Store bucket for tenant data changes.
|
|
34
|
+
* Handles bucket open with retry, the watch loop with auto-restart on
|
|
35
|
+
* transient failures, and blob download + decompression. Parsed data is
|
|
36
|
+
* delivered to the BucketWatcherHandler.
|
|
37
|
+
*/
|
|
38
|
+
class BucketWatcher {
|
|
39
|
+
constructor(nc, config, handler, logger) {
|
|
40
|
+
this.objStore = null;
|
|
41
|
+
this.watcherStop = null;
|
|
42
|
+
this.watchPromise = null;
|
|
43
|
+
this.stopped = false;
|
|
44
|
+
/**
|
|
45
|
+
* Flipped to true when the watch() history replay delivers its
|
|
46
|
+
* end-of-history sentinel (null). Pods should NOT serve traffic until this
|
|
47
|
+
* is true.
|
|
48
|
+
*/
|
|
49
|
+
this.initialLoadComplete = false;
|
|
50
|
+
this.objm = new obj_1.Objm(nc);
|
|
51
|
+
this.config = config;
|
|
52
|
+
this.handler = handler;
|
|
53
|
+
this.logger = logger ?? (0, logger_1.createDefaultLogger)('BucketWatcher');
|
|
54
|
+
}
|
|
55
|
+
async start() {
|
|
56
|
+
this.stopped = false;
|
|
57
|
+
await this.openBucketWithRetry();
|
|
58
|
+
this.watchPromise = this.startWatching();
|
|
59
|
+
}
|
|
60
|
+
async stop() {
|
|
61
|
+
this.stopped = true;
|
|
62
|
+
this.watcherStop?.();
|
|
63
|
+
if (this.watchPromise) {
|
|
64
|
+
await this.watchPromise;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
isReady() {
|
|
68
|
+
return this.initialLoadComplete;
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Bucket open with retry
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* The bucket is created by the hydration consumer in servicefoundry-server.
|
|
75
|
+
* On cold start the bucket may not exist yet, so we retry with backoff until
|
|
76
|
+
* it becomes available.
|
|
77
|
+
*/
|
|
78
|
+
async openBucketWithRetry() {
|
|
79
|
+
const { bucketName, openRetryIntervalMs, openRetryMaxAttempts } = this.config;
|
|
80
|
+
for (let attempt = 1; attempt <= openRetryMaxAttempts; attempt++) {
|
|
81
|
+
try {
|
|
82
|
+
this.objStore = await this.objm.open(bucketName);
|
|
83
|
+
this.logger.log(`Opened Object Store bucket="${bucketName}"`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
this.logger.warn(`Failed to open bucket="${bucketName}" (attempt ${attempt}/${openRetryMaxAttempts}):`, err);
|
|
88
|
+
if (attempt === openRetryMaxAttempts) {
|
|
89
|
+
throw new Error(`Failed to open Object Store bucket="${bucketName}" after ${openRetryMaxAttempts} attempts`);
|
|
90
|
+
}
|
|
91
|
+
await this.sleep(openRetryIntervalMs);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Watch loop — auto-restarts on unexpected exit
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
/**
|
|
99
|
+
* Outer loop that survives transient failures (e.g. NATS reconnect).
|
|
100
|
+
* If the inner watch loop exits unexpectedly, we sleep briefly and retry.
|
|
101
|
+
*/
|
|
102
|
+
async startWatching() {
|
|
103
|
+
while (!this.stopped) {
|
|
104
|
+
try {
|
|
105
|
+
await this.runWatchLoop();
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (this.stopped) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
this.logger.error(`Watch loop error, restarting in ${WATCH_RESTART_DELAY_MS}ms:`, err);
|
|
112
|
+
await this.sleep(WATCH_RESTART_DELAY_MS);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* watch({ includeHistory: true }) replays all existing objects first
|
|
118
|
+
* (with isUpdate=false), then streams live notifications (isUpdate=true).
|
|
119
|
+
*
|
|
120
|
+
* IMPORTANT: processWatchEvent (which calls getBlob) must NEVER be awaited
|
|
121
|
+
* inside the for-await loop. The watch uses a push consumer with flow
|
|
122
|
+
* control — if we block the iterator, the NATS server won't send more
|
|
123
|
+
* messages (including the blob chunks getBlob needs), causing a deadlock.
|
|
124
|
+
* All downloads are fired asynchronously and tracked via promises.
|
|
125
|
+
*/
|
|
126
|
+
async runWatchLoop() {
|
|
127
|
+
const watcher = await this.objStore.watch({ includeHistory: true });
|
|
128
|
+
this.watcherStop = () => {
|
|
129
|
+
watcher.stop();
|
|
130
|
+
};
|
|
131
|
+
const pendingDownloads = [];
|
|
132
|
+
for await (const info of watcher) {
|
|
133
|
+
if (this.stopped) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (info === null) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const isLiveUpdate = info.isUpdate === true;
|
|
140
|
+
if (!this.initialLoadComplete && isLiveUpdate) {
|
|
141
|
+
await Promise.all(pendingDownloads);
|
|
142
|
+
pendingDownloads.length = 0;
|
|
143
|
+
this.initialLoadComplete = true;
|
|
144
|
+
this.handler.handleInitialLoadComplete();
|
|
145
|
+
}
|
|
146
|
+
const download = this.processWatchEvent(info.name, info.deleted, info.size).catch((err) => {
|
|
147
|
+
this.logger.error(`Error processing "${info.name}":`, err);
|
|
148
|
+
Sentry.captureException(new Error(`Error processing "${info.name}"`, { cause: err }));
|
|
149
|
+
});
|
|
150
|
+
if (!this.initialLoadComplete) {
|
|
151
|
+
pendingDownloads.push(download);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!this.stopped) {
|
|
155
|
+
this.logger.warn('Watch iterator ended unexpectedly, will restart');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Event processing
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
/**
|
|
162
|
+
* Object key format: "{tenantName}.{entity}" where entity ∈ {authorization, users, teams}.
|
|
163
|
+
* Downloads the blob, decompresses (gzip), parses JSON, and delivers to the handler.
|
|
164
|
+
*/
|
|
165
|
+
async processWatchEvent(objectName, deleted, compressedSize) {
|
|
166
|
+
const parsed = BucketWatcher.parseObjectKey(objectName);
|
|
167
|
+
if (!parsed) {
|
|
168
|
+
throw new Error(`Unparseable object key: "${objectName}"`);
|
|
169
|
+
}
|
|
170
|
+
const { tenantName, entity } = parsed;
|
|
171
|
+
if (deleted) {
|
|
172
|
+
this.handler.handleDeletion(tenantName, entity);
|
|
173
|
+
this.logger.debug(`Deleted ${entity} for tenant="${tenantName}"`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const getBlobStartedAt = performance.now();
|
|
177
|
+
const blob = await this.objStore.getBlob(objectName);
|
|
178
|
+
const getBlobMs = performance.now() - getBlobStartedAt;
|
|
179
|
+
if (!blob) {
|
|
180
|
+
throw new Error(`No blob data for "${objectName}"`);
|
|
181
|
+
}
|
|
182
|
+
const deserializeStartedAt = performance.now();
|
|
183
|
+
const json = await (0, decompress_1.deserializeBlob)(blob);
|
|
184
|
+
const deserializeMs = performance.now() - deserializeStartedAt;
|
|
185
|
+
const parseStartedAt = performance.now();
|
|
186
|
+
const data = JSON.parse(json);
|
|
187
|
+
const jsonParseMs = performance.now() - parseStartedAt;
|
|
188
|
+
this.handler.handleUpdate(tenantName, entity, data);
|
|
189
|
+
this.logger.log(`Updated ${entity} for tenant="${tenantName}" (${compressedSize} bytes) ` +
|
|
190
|
+
`timings: getBlob=${getBlobMs.toFixed(2)}ms ` +
|
|
191
|
+
`decompress=${deserializeMs.toFixed(2)}ms jsonParse=${jsonParseMs.toFixed(2)}ms`);
|
|
192
|
+
}
|
|
193
|
+
static parseObjectKey(key) {
|
|
194
|
+
const lastDot = key.lastIndexOf('.');
|
|
195
|
+
if (lastDot === -1) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const tenantName = key.substring(0, lastDot);
|
|
199
|
+
const entity = key.substring(lastDot + 1);
|
|
200
|
+
return { tenantName, entity };
|
|
201
|
+
}
|
|
202
|
+
sleep(ms) {
|
|
203
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
exports.BucketWatcher = BucketWatcher;
|
|
207
|
+
//# sourceMappingURL=BucketWatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BucketWatcher.js","sourceRoot":"","sources":["../src/BucketWatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { AuthCacheLogger, AuthGrant, CachedAgent, CachedExternalIdentity, CachedExternalIdentityProvider, CachedGatewayConfig, CachedGuardrail, CachedMcpServer, CachedModel, CachedPAT, CachedRole, CachedServiceAccount, CachedTeam, CachedUser, CachedVirtualAccount, ITenantCache, TenantCheckAccessByResourceIdOptions } from './types';
|
|
2
|
+
import { AuthCacheEntity } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Holds all cached authorization data for a single tenant: grants, users,
|
|
5
|
+
* and teams. Exposes public query methods via ITenantCache and internal
|
|
6
|
+
* mutation methods used by the watcher layer.
|
|
7
|
+
*/
|
|
8
|
+
export declare class TenantCache implements ITenantCache {
|
|
9
|
+
private readonly logger;
|
|
10
|
+
constructor(logger?: AuthCacheLogger);
|
|
11
|
+
private grantIndex;
|
|
12
|
+
private cachedGrants;
|
|
13
|
+
private userTeamIndex;
|
|
14
|
+
private cachedUsers;
|
|
15
|
+
private cachedTeams;
|
|
16
|
+
/** Raw KVStoreV2Data blobs — kept as-is because they are JWK key sets, not entity arrays. */
|
|
17
|
+
private cachedExternalJwks;
|
|
18
|
+
private cachedPrimaryJwks;
|
|
19
|
+
private cachedMcpServers;
|
|
20
|
+
private cachedRoles;
|
|
21
|
+
private cachedAgents;
|
|
22
|
+
private cachedExternalIdentities;
|
|
23
|
+
private cachedModels;
|
|
24
|
+
private cachedGuardrails;
|
|
25
|
+
private cachedGatewayConfigs;
|
|
26
|
+
private cachedServiceAccounts;
|
|
27
|
+
private cachedVirtualAccounts;
|
|
28
|
+
private cachedPats;
|
|
29
|
+
private cachedExternalIdentityProviders;
|
|
30
|
+
updateEntity(entity: AuthCacheEntity, data: unknown): void;
|
|
31
|
+
deleteEntity(entity: AuthCacheEntity): void;
|
|
32
|
+
/**
|
|
33
|
+
* Builds the full index into a temporary map first, then swaps it in
|
|
34
|
+
* atomically so that concurrent reads never see a partially built or
|
|
35
|
+
* empty index.
|
|
36
|
+
*/
|
|
37
|
+
private updateAuthorization;
|
|
38
|
+
private updateUsers;
|
|
39
|
+
/**
|
|
40
|
+
* Stores the team list and builds a reverse index (userEmail → teamNames[])
|
|
41
|
+
* so that checkAccess can resolve team-inherited grants without the caller
|
|
42
|
+
* needing to supply team memberships.
|
|
43
|
+
*/
|
|
44
|
+
private updateTeams;
|
|
45
|
+
private updateExternalIdentities;
|
|
46
|
+
/** Kid → public key DTO map from `addExternalJWKSPublicKeysV2InNats` (`data` field). */
|
|
47
|
+
private updateExternalJwks;
|
|
48
|
+
/** Kid → public key DTO map from `addPrimaryJWKSPublicKeysV2InNats` (`data` field). */
|
|
49
|
+
private updatePrimaryJwks;
|
|
50
|
+
/** Virtual accounts from DB snapshot (`AuthInvalidationConsumerService`). */
|
|
51
|
+
private updateVirtualAccounts;
|
|
52
|
+
/** PATs from DB snapshot (`AuthInvalidationConsumerService`). */
|
|
53
|
+
private updatePats;
|
|
54
|
+
checkAccessForResourceId(opts: TenantCheckAccessByResourceIdOptions): boolean;
|
|
55
|
+
getUserTeams(userEmail: string): string[];
|
|
56
|
+
searchUsers(substring: string, limit?: number): CachedUser[];
|
|
57
|
+
searchTeams(substring: string, limit?: number): CachedTeam[];
|
|
58
|
+
getGrants(): AuthGrant[];
|
|
59
|
+
getUsers(): CachedUser[];
|
|
60
|
+
getTeams(): CachedTeam[];
|
|
61
|
+
getMcpServers(): CachedMcpServer[];
|
|
62
|
+
getRoles(): CachedRole[];
|
|
63
|
+
getExternalJwks(): object | null;
|
|
64
|
+
getPrimaryJwks(): object | null;
|
|
65
|
+
getAgents(): CachedAgent[];
|
|
66
|
+
getExternalIdentities(): CachedExternalIdentity[];
|
|
67
|
+
getModels(): CachedModel[];
|
|
68
|
+
getGuardrails(): CachedGuardrail[];
|
|
69
|
+
getGatewayConfigs(): CachedGatewayConfig[];
|
|
70
|
+
getServiceAccounts(): CachedServiceAccount[];
|
|
71
|
+
getVirtualAccounts(): CachedVirtualAccount[];
|
|
72
|
+
getPats(): CachedPAT[];
|
|
73
|
+
getExternalIdentityProviders(): CachedExternalIdentityProvider[];
|
|
74
|
+
private natsPayloadDataValues;
|
|
75
|
+
/** MCP servers and guardrails share the same `toNatsFormatIntegration` wire shape (llmgateway/utils). */
|
|
76
|
+
private parseIntegrationRows;
|
|
77
|
+
private updateMcpServers;
|
|
78
|
+
private updateGuardrails;
|
|
79
|
+
private updateRoles;
|
|
80
|
+
/** Value shape from `addAgentsV2InNats` (llmgateway/natsUtilsV2). */
|
|
81
|
+
private updateAgents;
|
|
82
|
+
/** Provider account rows from `addModelsV2InNats` (`toNatsFormatProviderAccount` + integrations). */
|
|
83
|
+
private updateModels;
|
|
84
|
+
/** KV value is `config.manifest` (`addGatewayConfigsV2InNats`). */
|
|
85
|
+
private updateGatewayConfigs;
|
|
86
|
+
private updateServiceAccounts;
|
|
87
|
+
private updateExternalIdentityProviders;
|
|
88
|
+
private getPermissionsByResourceId;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=TenantCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantCache.d.ts","sourceRoot":"","sources":["../src/TenantCache.ts"],"names":[],"mappings":"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"}
|