@modelprofile.com/authswitch 6.3.0 → 6.4.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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/authority-contract.d.ts +142 -0
- package/dist_ts/authority-contract.js +3 -0
- package/dist_ts/authority-runtime-contract.d.ts +32 -0
- package/dist_ts/authority-runtime-contract.js +2 -0
- package/dist_ts/classes.authoritybroker.d.ts +73 -0
- package/dist_ts/classes.authoritybroker.js +548 -0
- package/dist_ts/classes.authorityclient.d.ts +34 -0
- package/dist_ts/classes.authorityclient.js +200 -0
- package/dist_ts/classes.authoritydaemon.d.ts +26 -0
- package/dist_ts/classes.authoritydaemon.js +249 -0
- package/dist_ts/classes.authoritydatabase.d.ts +94 -0
- package/dist_ts/classes.authoritydatabase.js +736 -0
- package/dist_ts/classes.authorityframing.d.ts +8 -0
- package/dist_ts/classes.authorityframing.js +22 -0
- package/dist_ts/classes.authoritymodels.d.ts +215 -0
- package/dist_ts/classes.authoritymodels.js +818 -0
- package/dist_ts/classes.authoritysecrets.d.ts +9 -0
- package/dist_ts/classes.authoritysecrets.js +30 -0
- package/dist_ts/classes.authorityservice.d.ts +28 -0
- package/dist_ts/classes.authorityservice.js +71 -0
- package/dist_ts/index.d.ts +2 -0
- package/dist_ts/index.js +26 -1
- package/dist_ts/plugins.d.ts +9 -2
- package/dist_ts/plugins.js +10 -3
- package/package.json +17 -3
- package/readme.md +34 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/authority-contract.ts +109 -0
- package/ts/authority-runtime-contract.ts +27 -0
- package/ts/classes.authoritybroker.ts +530 -0
- package/ts/classes.authorityclient.ts +183 -0
- package/ts/classes.authoritydaemon.ts +219 -0
- package/ts/classes.authoritydatabase.ts +725 -0
- package/ts/classes.authorityframing.ts +21 -0
- package/ts/classes.authoritymodels.ts +367 -0
- package/ts/classes.authoritysecrets.ts +27 -0
- package/ts/classes.authorityservice.ts +83 -0
- package/ts/index.ts +25 -0
- package/ts/plugins.ts +9 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { IAuthSwitchAccountEvent, IAuthSwitchSnapshot, IReq_AuthSwitchBeginAdd, IReq_AuthSwitchBeginReauth, IReq_AuthSwitchCancelOperation, IReq_AuthSwitchEvents, IReq_AuthSwitchGetOperation, IReq_AuthSwitchRemoveAccount, IReq_AuthSwitchRenameAccount, IReq_AuthSwitchSnapshot } from './authority-contract.js';
|
|
2
|
+
import type { IReq_AuthSwitchBindAccount, IReq_AuthSwitchResolveAccess } from './authority-runtime-contract.js';
|
|
3
|
+
/** Node-only client for the per-user daemon. Each request has its own bounded Unix connection. */
|
|
4
|
+
export declare class AuthSwitchClient {
|
|
5
|
+
readonly socketPath: string;
|
|
6
|
+
readonly runtimeSocketPath: string;
|
|
7
|
+
private readonly target;
|
|
8
|
+
private readonly runtimeTarget;
|
|
9
|
+
constructor(socketPath: string, runtimeSocketPath: string);
|
|
10
|
+
private request;
|
|
11
|
+
snapshot(options?: IReq_AuthSwitchSnapshot['request'], signal?: AbortSignal): Promise<IAuthSwitchSnapshot>;
|
|
12
|
+
/** Assemble a consistent view from bounded database pages; restart if a writer changes the revision. */
|
|
13
|
+
snapshotAll(options?: {
|
|
14
|
+
limit?: number;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}): Promise<IAuthSwitchSnapshot>;
|
|
17
|
+
events(epoch: string, afterRevision: number, waitMs?: number, signal?: AbortSignal): Promise<IReq_AuthSwitchEvents['response']>;
|
|
18
|
+
/** Always obtains a fresh snapshot after reconnect or an event-log gap. */
|
|
19
|
+
subscribe(onSnapshot: (snapshot: IAuthSwitchSnapshot) => void | Promise<void>, onEvent: (event: IAuthSwitchAccountEvent) => void | Promise<void>, signal: AbortSignal): Promise<void>;
|
|
20
|
+
beginAddOpenAi(): Promise<IReq_AuthSwitchBeginAdd['response']['operation']>;
|
|
21
|
+
beginReauthOpenAi(accountId: string): Promise<IReq_AuthSwitchBeginReauth['response']['operation']>;
|
|
22
|
+
getOperation(operationId: string): Promise<IReq_AuthSwitchGetOperation['response']['operation']>;
|
|
23
|
+
cancelOperation(operationId: string): Promise<IReq_AuthSwitchCancelOperation['response']['operation']>;
|
|
24
|
+
renameAccount(accountId: string, expectedRevision: number, label: string): Promise<IReq_AuthSwitchRenameAccount['response']['account']>;
|
|
25
|
+
removeAccount(accountId: string, expectedRevision: number): Promise<IReq_AuthSwitchRemoveAccount['response']['account']>;
|
|
26
|
+
bindAccount(input: IReq_AuthSwitchBindAccount['request']): Promise<IReq_AuthSwitchBindAccount['response']>;
|
|
27
|
+
resolveAccess(input: IReq_AuthSwitchResolveAccess['request']): Promise<IReq_AuthSwitchResolveAccess['response']>;
|
|
28
|
+
}
|
|
29
|
+
/** Container-facing client. It can only resolve a previously issued binding capability. */
|
|
30
|
+
export declare class AuthSwitchRuntimeClient {
|
|
31
|
+
private readonly target;
|
|
32
|
+
constructor(runtimeSocketPath: string);
|
|
33
|
+
resolveAccess(input: IReq_AuthSwitchResolveAccess['request']): Promise<IReq_AuthSwitchResolveAccess['response']>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { AuthSwitchAuthorityFrameReader, authSwitchAuthorityFrameBytes, maxAuthSwitchAuthorityFrameBytes } from './classes.authorityframing.js';
|
|
3
|
+
const post = (path, payload, signal) => {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
if (signal?.aborted) {
|
|
6
|
+
reject(signal.reason);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const socket = plugins.net.createConnection(path);
|
|
10
|
+
let settled = false;
|
|
11
|
+
const frame = new AuthSwitchAuthorityFrameReader();
|
|
12
|
+
const finish = (error, response) => {
|
|
13
|
+
if (settled)
|
|
14
|
+
return;
|
|
15
|
+
settled = true;
|
|
16
|
+
signal?.removeEventListener('abort', abort);
|
|
17
|
+
socket.destroy();
|
|
18
|
+
if (error)
|
|
19
|
+
reject(error);
|
|
20
|
+
else if (response)
|
|
21
|
+
resolve(response);
|
|
22
|
+
else
|
|
23
|
+
reject(new Error('Authswitch daemon returned no response.'));
|
|
24
|
+
};
|
|
25
|
+
const abort = () => finish(signal?.reason ?? new Error('Account request was cancelled.'));
|
|
26
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
27
|
+
socket.setTimeout(35_000, () => finish(new Error('Authswitch daemon request timed out.')));
|
|
28
|
+
socket.on('connect', () => {
|
|
29
|
+
const raw = JSON.stringify(payload);
|
|
30
|
+
if (authSwitchAuthorityFrameBytes(raw) > maxAuthSwitchAuthorityFrameBytes) {
|
|
31
|
+
finish(new Error('Account request is too large.'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
socket.write(raw + '\n');
|
|
35
|
+
});
|
|
36
|
+
socket.on('data', chunk => {
|
|
37
|
+
if (!Buffer.isBuffer(chunk)) {
|
|
38
|
+
finish(new Error('Account response must be a byte frame.'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
let input;
|
|
42
|
+
try {
|
|
43
|
+
input = frame.add(chunk);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
finish(error);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (input === null)
|
|
50
|
+
return;
|
|
51
|
+
try {
|
|
52
|
+
finish(undefined, JSON.parse(input));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
finish(new Error('Account response could not be decoded.'));
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
socket.on('error', () => finish(new Error('Authswitch daemon is unavailable.')));
|
|
59
|
+
socket.on('close', () => finish(new Error('Authswitch daemon connection closed before its response.')));
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
/** Node-only client for the per-user daemon. Each request has its own bounded Unix connection. */
|
|
63
|
+
export class AuthSwitchClient {
|
|
64
|
+
socketPath;
|
|
65
|
+
runtimeSocketPath;
|
|
66
|
+
target;
|
|
67
|
+
runtimeTarget;
|
|
68
|
+
constructor(socketPath, runtimeSocketPath) {
|
|
69
|
+
this.socketPath = socketPath;
|
|
70
|
+
this.runtimeSocketPath = runtimeSocketPath;
|
|
71
|
+
this.target = new plugins.typedrequest.TypedTarget({
|
|
72
|
+
supportsAbortSignal: true,
|
|
73
|
+
postMethod: (payload, options) => post(this.socketPath, payload, options?.signal),
|
|
74
|
+
});
|
|
75
|
+
this.runtimeTarget = new plugins.typedrequest.TypedTarget({
|
|
76
|
+
supportsAbortSignal: true,
|
|
77
|
+
postMethod: (payload, options) => post(this.runtimeSocketPath, payload, options?.signal),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
request(method, request, timeoutMs = 35_000, runtime = false, signal) {
|
|
81
|
+
return new plugins.typedrequest.TypedRequest(runtime ? this.runtimeTarget : this.target, method)
|
|
82
|
+
.fire(request, { timeoutMs, maxRetries: 0, abortSignal: signal });
|
|
83
|
+
}
|
|
84
|
+
async snapshot(options = {}, signal) {
|
|
85
|
+
return (await this.request('authswitch.authority.snapshot', options, 35_000, false, signal)).snapshot;
|
|
86
|
+
}
|
|
87
|
+
/** Assemble a consistent view from bounded database pages; restart if a writer changes the revision. */
|
|
88
|
+
async snapshotAll(options = {}) {
|
|
89
|
+
while (true) {
|
|
90
|
+
if (options.signal?.aborted)
|
|
91
|
+
throw options.signal.reason ?? new Error('Account snapshot cancelled.');
|
|
92
|
+
let accountAfter;
|
|
93
|
+
let bindingAfter;
|
|
94
|
+
let aggregate;
|
|
95
|
+
let changed = false;
|
|
96
|
+
while (true) {
|
|
97
|
+
const page = await this.snapshot({ accountAfter, bindingAfter, limit: options.limit }, options.signal);
|
|
98
|
+
if (!aggregate)
|
|
99
|
+
aggregate = { ...page, accounts: [...page.accounts], bindings: [...page.bindings] };
|
|
100
|
+
else if (aggregate.epoch !== page.epoch || aggregate.revision !== page.revision) {
|
|
101
|
+
changed = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
aggregate.accounts.push(...page.accounts);
|
|
106
|
+
aggregate.bindings.push(...page.bindings);
|
|
107
|
+
}
|
|
108
|
+
accountAfter = page.nextAccountCursor ?? accountAfter ?? page.accounts.at(-1)?.id;
|
|
109
|
+
bindingAfter = page.nextBindingCursor ?? bindingAfter ?? page.bindings.at(-1)?.id;
|
|
110
|
+
if (!page.nextAccountCursor && !page.nextBindingCursor) {
|
|
111
|
+
return { ...aggregate, nextAccountCursor: null, nextBindingCursor: null };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!changed)
|
|
115
|
+
throw new Error('Authority snapshot could not complete.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
events(epoch, afterRevision, waitMs = 30_000, signal) {
|
|
119
|
+
return this.request('authswitch.authority.events', { epoch, afterRevision, waitMs }, waitMs + 5_000, false, signal);
|
|
120
|
+
}
|
|
121
|
+
/** Always obtains a fresh snapshot after reconnect or an event-log gap. */
|
|
122
|
+
async subscribe(onSnapshot, onEvent, signal) {
|
|
123
|
+
const retry = () => new Promise(resolve => {
|
|
124
|
+
const timer = setTimeout(() => { signal.removeEventListener('abort', aborted); resolve(); }, 250);
|
|
125
|
+
const aborted = () => { clearTimeout(timer); resolve(); };
|
|
126
|
+
signal.addEventListener('abort', aborted, { once: true });
|
|
127
|
+
});
|
|
128
|
+
let snapshot;
|
|
129
|
+
while (!signal.aborted) {
|
|
130
|
+
if (!snapshot) {
|
|
131
|
+
try {
|
|
132
|
+
snapshot = await this.snapshotAll({ signal });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
if (signal.aborted)
|
|
136
|
+
break;
|
|
137
|
+
await retry();
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
await onSnapshot(snapshot);
|
|
141
|
+
}
|
|
142
|
+
let result;
|
|
143
|
+
try {
|
|
144
|
+
result = await this.events(snapshot.epoch, snapshot.revision, 30_000, signal);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
if (signal.aborted)
|
|
148
|
+
break;
|
|
149
|
+
snapshot = undefined;
|
|
150
|
+
await retry();
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (result.resyncRequired || result.epoch !== snapshot.epoch) {
|
|
154
|
+
snapshot = undefined;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
for (const event of result.events)
|
|
158
|
+
await onEvent(event);
|
|
159
|
+
snapshot = { ...snapshot, revision: result.revision };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async beginAddOpenAi() {
|
|
163
|
+
return (await this.request('authswitch.authority.add', { providerId: 'openai', flow: 'device' })).operation;
|
|
164
|
+
}
|
|
165
|
+
async beginReauthOpenAi(accountId) {
|
|
166
|
+
return (await this.request('authswitch.authority.reauth', { accountId, flow: 'device' })).operation;
|
|
167
|
+
}
|
|
168
|
+
async getOperation(operationId) {
|
|
169
|
+
return (await this.request('authswitch.authority.operation', { operationId })).operation;
|
|
170
|
+
}
|
|
171
|
+
async cancelOperation(operationId) {
|
|
172
|
+
return (await this.request('authswitch.authority.cancel', { operationId })).operation;
|
|
173
|
+
}
|
|
174
|
+
async renameAccount(accountId, expectedRevision, label) {
|
|
175
|
+
return (await this.request('authswitch.authority.rename', { accountId, expectedRevision, label })).account;
|
|
176
|
+
}
|
|
177
|
+
async removeAccount(accountId, expectedRevision) {
|
|
178
|
+
return (await this.request('authswitch.authority.remove', { accountId, expectedRevision })).account;
|
|
179
|
+
}
|
|
180
|
+
bindAccount(input) {
|
|
181
|
+
return this.request('authswitch.authority.bind', input);
|
|
182
|
+
}
|
|
183
|
+
resolveAccess(input) {
|
|
184
|
+
return this.request('authswitch.authority.resolveAccess', input, 35_000, true);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/** Container-facing client. It can only resolve a previously issued binding capability. */
|
|
188
|
+
export class AuthSwitchRuntimeClient {
|
|
189
|
+
target;
|
|
190
|
+
constructor(runtimeSocketPath) {
|
|
191
|
+
this.target = new plugins.typedrequest.TypedTarget({
|
|
192
|
+
supportsAbortSignal: true,
|
|
193
|
+
postMethod: (payload, options) => post(runtimeSocketPath, payload, options?.signal),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
resolveAccess(input) {
|
|
197
|
+
return new plugins.typedrequest.TypedRequest(this.target, 'authswitch.authority.resolveAccess').fire(input, { timeoutMs: 35_000, maxRetries: 0 });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5hdXRob3JpdHljbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLmF1dGhvcml0eWNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQVF4QyxPQUFPLEVBQUUsOEJBQThCLEVBQUUsNkJBQTZCLEVBQ3BFLGdDQUFnQyxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFMUUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxJQUFZLEVBQUUsT0FBc0IsRUFBRSxNQUFvQixFQUEwQixFQUFFO0lBQ2hHLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUU7UUFDckMsSUFBSSxNQUFNLEVBQUUsT0FBTyxFQUFFLENBQUM7WUFBQyxNQUFNLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQUMsT0FBTztRQUFDLENBQUM7UUFDdkQsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNsRCxJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDcEIsTUFBTSxLQUFLLEdBQUcsSUFBSSw4QkFBOEIsRUFBRSxDQUFDO1FBQ25ELE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBZSxFQUFFLFFBQXdCLEVBQVEsRUFBRTtZQUNqRSxJQUFJLE9BQU87Z0JBQUUsT0FBTztZQUNwQixPQUFPLEdBQUcsSUFBSSxDQUFDO1lBQ2YsTUFBTSxFQUFFLG1CQUFtQixDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM1QyxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDakIsSUFBSSxLQUFLO2dCQUFFLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztpQkFDcEIsSUFBSSxRQUFRO2dCQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQzs7Z0JBQ2hDLE1BQU0sQ0FBQyxJ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
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AuthSwitchAuthorityBroker, type IAuthSwitchAuthorityBrokerOptions } from './classes.authoritybroker.js';
|
|
2
|
+
import { AuthSwitchAuthorityDatabase, type IAuthSwitchAuthorityDatabaseOptions } from './classes.authoritydatabase.js';
|
|
3
|
+
export interface IAuthSwitchAuthorityDaemonOptions extends IAuthSwitchAuthorityDatabaseOptions {
|
|
4
|
+
authoritySocketPath: string;
|
|
5
|
+
runtimeSocketPath: string;
|
|
6
|
+
broker?: IAuthSwitchAuthorityBrokerOptions;
|
|
7
|
+
}
|
|
8
|
+
/** TypedRequest handlers over a private one-request-per-connection Unix socket. */
|
|
9
|
+
export declare class AuthSwitchAuthorityDaemon {
|
|
10
|
+
private readonly options;
|
|
11
|
+
readonly database: AuthSwitchAuthorityDatabase;
|
|
12
|
+
readonly broker: AuthSwitchAuthorityBroker;
|
|
13
|
+
private readonly managementRouter;
|
|
14
|
+
private readonly runtimeRouter;
|
|
15
|
+
private managementServer?;
|
|
16
|
+
private runtimeServer?;
|
|
17
|
+
private readonly sockets;
|
|
18
|
+
constructor(options: IAuthSwitchAuthorityDaemonOptions);
|
|
19
|
+
private register;
|
|
20
|
+
start(): Promise<void>;
|
|
21
|
+
private listen;
|
|
22
|
+
/** A killed daemon leaves a pathname behind; only remove an unchanged, owned, refused Unix socket. */
|
|
23
|
+
private clearStaleSocket;
|
|
24
|
+
private accept;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { AuthSwitchAuthorityBroker } from './classes.authoritybroker.js';
|
|
3
|
+
import { AuthSwitchAuthorityDatabase } from './classes.authoritydatabase.js';
|
|
4
|
+
import { AuthSwitchAuthorityFrameReader, authSwitchAuthorityFrameBytes, maxAuthSwitchAuthorityFrameBytes } from './classes.authorityframing.js';
|
|
5
|
+
const hasKeys = (value, keys) => value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
6
|
+
&& Object.keys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
|
|
7
|
+
const invalid = () => { throw new plugins.typedrequest.TypedResponseError('Invalid authswitch authority request.'); };
|
|
8
|
+
/** TypedRequest handlers over a private one-request-per-connection Unix socket. */
|
|
9
|
+
export class AuthSwitchAuthorityDaemon {
|
|
10
|
+
options;
|
|
11
|
+
database;
|
|
12
|
+
broker;
|
|
13
|
+
managementRouter = new plugins.typedrequest.TypedRouter();
|
|
14
|
+
runtimeRouter = new plugins.typedrequest.TypedRouter();
|
|
15
|
+
managementServer;
|
|
16
|
+
runtimeServer;
|
|
17
|
+
sockets = new Set();
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.options = options;
|
|
20
|
+
this.database = new AuthSwitchAuthorityDatabase(options);
|
|
21
|
+
this.broker = new AuthSwitchAuthorityBroker(this.database, options.broker);
|
|
22
|
+
this.register();
|
|
23
|
+
}
|
|
24
|
+
register() {
|
|
25
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.snapshot', async (request) => {
|
|
26
|
+
if (request === null || typeof request !== 'object' || Array.isArray(request))
|
|
27
|
+
invalid();
|
|
28
|
+
const keys = Object.keys(request);
|
|
29
|
+
if (keys.some(key => !['accountAfter', 'bindingAfter', 'limit'].includes(key)))
|
|
30
|
+
invalid();
|
|
31
|
+
return { snapshot: await this.broker.snapshot(request) };
|
|
32
|
+
}));
|
|
33
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.events', async (request, tools) => {
|
|
34
|
+
if (!hasKeys(request, ['epoch', 'afterRevision', 'waitMs']))
|
|
35
|
+
invalid();
|
|
36
|
+
return this.broker.events(request.epoch, request.afterRevision, request.waitMs, tools?.abortSignal);
|
|
37
|
+
}));
|
|
38
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.add', async (request) => {
|
|
39
|
+
if (!hasKeys(request, ['providerId', 'flow']) || request.providerId !== 'openai' || request.flow !== 'device')
|
|
40
|
+
invalid();
|
|
41
|
+
return { operation: await this.broker.beginAddOpenAi() };
|
|
42
|
+
}));
|
|
43
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.reauth', async (request) => {
|
|
44
|
+
if (!hasKeys(request, ['accountId', 'flow']) || request.flow !== 'device' || typeof request.accountId !== 'string')
|
|
45
|
+
invalid();
|
|
46
|
+
return { operation: await this.broker.beginReauthOpenAi(request.accountId) };
|
|
47
|
+
}));
|
|
48
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.operation', async (request) => {
|
|
49
|
+
if (!hasKeys(request, ['operationId']) || typeof request.operationId !== 'string')
|
|
50
|
+
invalid();
|
|
51
|
+
return { operation: this.broker.getOperation(request.operationId) };
|
|
52
|
+
}));
|
|
53
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.cancel', async (request) => {
|
|
54
|
+
if (!hasKeys(request, ['operationId']) || typeof request.operationId !== 'string')
|
|
55
|
+
invalid();
|
|
56
|
+
return { operation: await this.broker.cancelOperation(request.operationId) };
|
|
57
|
+
}));
|
|
58
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.rename', async (request) => {
|
|
59
|
+
if (!hasKeys(request, ['accountId', 'expectedRevision', 'label']) || typeof request.accountId !== 'string'
|
|
60
|
+
|| typeof request.expectedRevision !== 'number' || typeof request.label !== 'string')
|
|
61
|
+
invalid();
|
|
62
|
+
return { account: await this.broker.renameAccount(request.accountId, request.expectedRevision, request.label) };
|
|
63
|
+
}));
|
|
64
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.remove', async (request) => {
|
|
65
|
+
if (!hasKeys(request, ['accountId', 'expectedRevision']) || typeof request.accountId !== 'string'
|
|
66
|
+
|| typeof request.expectedRevision !== 'number')
|
|
67
|
+
invalid();
|
|
68
|
+
return { account: await this.broker.removeAccount(request.accountId, request.expectedRevision) };
|
|
69
|
+
}));
|
|
70
|
+
this.managementRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.bind', async (request) => {
|
|
71
|
+
if (!hasKeys(request, ['accountId', 'runtime', 'scopeId', 'incarnationId'])
|
|
72
|
+
|| typeof request.accountId !== 'string' || typeof request.scopeId !== 'string'
|
|
73
|
+
|| typeof request.incarnationId !== 'string')
|
|
74
|
+
invalid();
|
|
75
|
+
return this.broker.bindAccount(request);
|
|
76
|
+
}));
|
|
77
|
+
this.runtimeRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('authswitch.authority.resolveAccess', async (request) => {
|
|
78
|
+
if (!hasKeys(request, ['bindingId', 'capability', 'minValidityMs'])
|
|
79
|
+
|| typeof request.bindingId !== 'string' || typeof request.capability !== 'string'
|
|
80
|
+
|| typeof request.minValidityMs !== 'number')
|
|
81
|
+
invalid();
|
|
82
|
+
return this.broker.resolveAccess(request.bindingId, request.capability, request.minValidityMs);
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
async start() {
|
|
86
|
+
if (this.managementServer || this.runtimeServer)
|
|
87
|
+
throw new Error('Authswitch authority daemon is already started.');
|
|
88
|
+
await this.database.start();
|
|
89
|
+
try {
|
|
90
|
+
await this.broker.start();
|
|
91
|
+
const managementDirectory = plugins.path.dirname(this.options.authoritySocketPath);
|
|
92
|
+
const runtimeDirectory = plugins.path.dirname(this.options.runtimeSocketPath);
|
|
93
|
+
const databaseDirectory = plugins.path.dirname(this.options.socketPath);
|
|
94
|
+
if (managementDirectory === runtimeDirectory || databaseDirectory === runtimeDirectory
|
|
95
|
+
|| managementDirectory.startsWith(runtimeDirectory + plugins.path.sep)
|
|
96
|
+
|| databaseDirectory.startsWith(runtimeDirectory + plugins.path.sep)
|
|
97
|
+
|| runtimeDirectory.startsWith(managementDirectory + plugins.path.sep)
|
|
98
|
+
|| runtimeDirectory.startsWith(databaseDirectory + plugins.path.sep)) {
|
|
99
|
+
throw new Error('Runtime socket directory must contain no management or database endpoint.');
|
|
100
|
+
}
|
|
101
|
+
this.managementServer = await this.listen(this.options.authoritySocketPath, this.managementRouter);
|
|
102
|
+
this.runtimeServer = await this.listen(this.options.runtimeSocketPath, this.runtimeRouter);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
try {
|
|
106
|
+
await this.close();
|
|
107
|
+
}
|
|
108
|
+
catch { /* Preserve startup failure. */ }
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async listen(path, router) {
|
|
113
|
+
const directory = plugins.path.dirname(path);
|
|
114
|
+
await plugins.fs.promises.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
115
|
+
const stat = await plugins.fs.promises.lstat(directory);
|
|
116
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.()) {
|
|
117
|
+
throw new Error('Authswitch authority socket directory is not private.');
|
|
118
|
+
}
|
|
119
|
+
await plugins.fs.promises.chmod(directory, 0o700);
|
|
120
|
+
await this.clearStaleSocket(path);
|
|
121
|
+
const server = plugins.net.createServer(socket => this.accept(socket, router));
|
|
122
|
+
try {
|
|
123
|
+
await new Promise((resolve, reject) => {
|
|
124
|
+
server.once('error', reject);
|
|
125
|
+
server.listen(path, () => { server.off('error', reject); resolve(); });
|
|
126
|
+
});
|
|
127
|
+
await plugins.fs.promises.chmod(path, 0o600);
|
|
128
|
+
return server;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (server.listening)
|
|
132
|
+
await new Promise(resolve => server.close(() => resolve()));
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** A killed daemon leaves a pathname behind; only remove an unchanged, owned, refused Unix socket. */
|
|
137
|
+
async clearStaleSocket(path) {
|
|
138
|
+
let before;
|
|
139
|
+
try {
|
|
140
|
+
before = await plugins.fs.promises.lstat(path);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (error.code === 'ENOENT')
|
|
144
|
+
return;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
if (!before.isSocket() || before.uid !== process.getuid?.())
|
|
148
|
+
throw new Error('Authswitch socket path is unsafe.');
|
|
149
|
+
const refused = await new Promise((resolve, reject) => {
|
|
150
|
+
const probe = plugins.net.createConnection(path);
|
|
151
|
+
probe.setTimeout(1_000, () => { probe.destroy(); reject(new Error('Authswitch socket probe timed out.')); });
|
|
152
|
+
probe.once('connect', () => { probe.destroy(); resolve(false); });
|
|
153
|
+
probe.once('error', error => {
|
|
154
|
+
probe.destroy();
|
|
155
|
+
if (error.code === 'ECONNREFUSED')
|
|
156
|
+
resolve(true);
|
|
157
|
+
else
|
|
158
|
+
reject(error);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
if (!refused)
|
|
162
|
+
throw new Error('Authswitch authority socket is already active.');
|
|
163
|
+
const after = await plugins.fs.promises.lstat(path);
|
|
164
|
+
if (!after.isSocket() || after.uid !== before.uid || after.dev !== before.dev || after.ino !== before.ino) {
|
|
165
|
+
throw new Error('Authswitch socket path changed during startup.');
|
|
166
|
+
}
|
|
167
|
+
await plugins.fs.promises.unlink(path);
|
|
168
|
+
}
|
|
169
|
+
accept(socket, router) {
|
|
170
|
+
this.sockets.add(socket);
|
|
171
|
+
const abort = new AbortController();
|
|
172
|
+
socket.on('close', () => { this.sockets.delete(socket); abort.abort(); });
|
|
173
|
+
const frame = new AuthSwitchAuthorityFrameReader();
|
|
174
|
+
let handled = false;
|
|
175
|
+
socket.setTimeout(35_000, () => socket.destroy());
|
|
176
|
+
socket.on('data', chunk => {
|
|
177
|
+
if (handled) {
|
|
178
|
+
socket.destroy();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (!Buffer.isBuffer(chunk)) {
|
|
182
|
+
socket.destroy();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
let input;
|
|
186
|
+
try {
|
|
187
|
+
input = frame.add(chunk);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
socket.destroy();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (input === null)
|
|
194
|
+
return;
|
|
195
|
+
handled = true;
|
|
196
|
+
let request;
|
|
197
|
+
try {
|
|
198
|
+
request = JSON.parse(input);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
socket.destroy();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
void router.routeAndAddResponse(request, { trustedLocalData: { authoritySocket: socket },
|
|
205
|
+
trustedAbortSignal: abort.signal })
|
|
206
|
+
.then(response => {
|
|
207
|
+
const raw = JSON.stringify(response);
|
|
208
|
+
if (authSwitchAuthorityFrameBytes(raw) > maxAuthSwitchAuthorityFrameBytes) {
|
|
209
|
+
socket.destroy();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
socket.end(raw + '\n');
|
|
213
|
+
}).catch(() => socket.destroy());
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async close() {
|
|
217
|
+
const errors = [];
|
|
218
|
+
try {
|
|
219
|
+
await this.broker.close();
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
errors.push(error);
|
|
223
|
+
}
|
|
224
|
+
const management = this.managementServer;
|
|
225
|
+
const runtime = this.runtimeServer;
|
|
226
|
+
this.managementServer = undefined;
|
|
227
|
+
this.runtimeServer = undefined;
|
|
228
|
+
for (const socket of this.sockets)
|
|
229
|
+
socket.destroy();
|
|
230
|
+
for (const server of [management, runtime]) {
|
|
231
|
+
if (server)
|
|
232
|
+
try {
|
|
233
|
+
await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
errors.push(error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
await this.database.stop();
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
errors.push(error);
|
|
244
|
+
}
|
|
245
|
+
if (errors.length)
|
|
246
|
+
throw new AggregateError(errors, 'Authswitch authority shutdown was incomplete.');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5hdXRob3JpdHlkYWVtb24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLmF1dGhvcml0eWRhZW1vbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQU94QyxPQUFPLEVBQUUseUJBQXlCLEVBQTBDLE1BQU0sOEJBQThCLENBQUM7QUFDakgsT0FBTyxFQUFFLDJCQUEyQixFQUE0QyxNQUFNLGdDQUFnQyxDQUFDO0FBQ3ZILE9BQU8sRUFBRSw4QkFBOEIsRUFBRSw2QkFBNkIsRUFDcEUsZ0NBQWdDLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQVExRSxNQUFNLE9BQU8sR0FBRyxDQUFDLEtBQWMsRUFBRSxJQUF1QixFQUFvQyxFQUFFLENBQzVGLEtBQUssS0FBSyxJQUFJLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUM7T0FDakUsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxNQUFNLEtBQUssSUFBSSxDQUFDLE1BQU0sSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztBQUMvRixNQUFNLE9BQU8sR0FBRyxHQUFVLEVBQUUsR0FBRyxNQUFNLElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxrQkFBa0IsQ0FBQyx1Q0FBdUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTdILG1GQUFtRjtBQUNuRixNQUFNLE9BQU8seUJBQXlCO0lBU1A7SUFSYixRQUFRLENBQThCO0lBQ3RDLE1BQU0sQ0FBNEI7SUFDakMsZ0JBQWdCLEdBQUcsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQzFELGFBQWEsR0FBRyxJQUFJLE9BQU8s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
|