@voltro/protocol 0.71.1 → 0.73.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/CHANGELOG.md +468 -0
- package/THIRD-PARTY-NOTICES.md +29 -1
- package/dist/index.d.ts +146 -0
- package/dist/index.js +549 -536
- package/dist/inspect.js +1 -0
- package/dist/rest.js +1 -1
- package/dist/{scopes-CW9_LbDO.js → scopes-Dj7KCJab.js} +202 -132
- package/dist/session.js +6 -6
- package/package.json +4 -3
package/dist/inspect.js
CHANGED
|
@@ -129,6 +129,7 @@ var t = e.String.pipe(e.filter((e) => Number.isFinite(Date.parse(e)))), n = e.St
|
|
|
129
129
|
voltro_replica: e.replicaId
|
|
130
130
|
}
|
|
131
131
|
}))) };
|
|
132
|
+
else if (r.length === 1 && i.length === 1) p = i[0].data;
|
|
132
133
|
else throw Error("This view must compare observations per replica; its values cannot be safely combined.");
|
|
133
134
|
return {
|
|
134
135
|
...t,
|
package/dist/rest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as e, L as t } from "./scopes-Dj7KCJab.js";
|
|
2
2
|
import { C as n, S as r, T as i, _ as a, a as o, c as s, d as c, f as l, i as u, l as d, n as f, o as p, r as m, s as h, t as g, u as _, v, w as y } from "./restFormat-ChSVyufA.js";
|
|
3
3
|
import { Effect as b, ParseResult as x, Schema as S } from "effect";
|
|
4
4
|
import * as C from "effect/SchemaAST";
|
|
@@ -1,79 +1,108 @@
|
|
|
1
1
|
import { Context as e, Effect as t, Schema as n } from "effect";
|
|
2
2
|
import { RpcMiddleware as r } from "@effect/rpc";
|
|
3
|
-
//#region src/
|
|
4
|
-
var i = n.
|
|
3
|
+
//#region src/tenantSelection.ts
|
|
4
|
+
var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAccount", a = class extends n.TaggedError()("TenantSelectionRefused", {
|
|
5
|
+
subjectType: n.String,
|
|
6
|
+
subjectId: n.NullOr(n.String),
|
|
7
|
+
tenantId: n.String,
|
|
8
|
+
reason: n.Literal("not-a-member", "subject-has-no-tenant")
|
|
9
|
+
}) {}, o = (e, t, n) => {
|
|
10
|
+
if (!i(e)) throw new a({
|
|
11
|
+
subjectType: e.type,
|
|
12
|
+
subjectId: e.id,
|
|
13
|
+
tenantId: t,
|
|
14
|
+
reason: "subject-has-no-tenant"
|
|
15
|
+
});
|
|
16
|
+
if (!n.some((e) => e.tenantId === t)) throw new a({
|
|
17
|
+
subjectType: e.type,
|
|
18
|
+
subjectId: e.id,
|
|
19
|
+
tenantId: t,
|
|
20
|
+
reason: "not-a-member"
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
...e,
|
|
24
|
+
tenantId: t,
|
|
25
|
+
metadata: {
|
|
26
|
+
...e.metadata,
|
|
27
|
+
memberships: n.map((e) => e.role === void 0 ? { tenantId: e.tenantId } : {
|
|
28
|
+
tenantId: e.tenantId,
|
|
29
|
+
role: e.role
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}, s = n.Record({
|
|
5
34
|
key: n.String,
|
|
6
35
|
value: n.Unknown
|
|
7
|
-
}),
|
|
36
|
+
}), c = n.optional(n.Array(n.String)), l = n.Struct({
|
|
8
37
|
type: n.Literal("user"),
|
|
9
38
|
id: n.String,
|
|
10
39
|
tenantId: n.String,
|
|
11
|
-
scopes:
|
|
12
|
-
metadata: n.optional(
|
|
13
|
-
}),
|
|
40
|
+
scopes: c,
|
|
41
|
+
metadata: n.optional(s)
|
|
42
|
+
}), u = n.Struct({
|
|
14
43
|
type: n.Literal("apiKey"),
|
|
15
44
|
id: n.String,
|
|
16
45
|
tenantId: n.String,
|
|
17
|
-
scopes:
|
|
18
|
-
metadata: n.optional(
|
|
19
|
-
}),
|
|
46
|
+
scopes: c,
|
|
47
|
+
metadata: n.optional(s)
|
|
48
|
+
}), d = n.Struct({
|
|
20
49
|
type: n.Literal("serviceAccount"),
|
|
21
50
|
id: n.String,
|
|
22
51
|
tenantId: n.String,
|
|
23
52
|
auditActorId: n.optionalWith(n.NullOr(n.String), { exact: !0 }),
|
|
24
|
-
scopes:
|
|
25
|
-
metadata: n.optional(
|
|
26
|
-
}),
|
|
53
|
+
scopes: c,
|
|
54
|
+
metadata: n.optional(s)
|
|
55
|
+
}), f = n.Struct({
|
|
27
56
|
type: n.Literal("anonymous"),
|
|
28
57
|
id: n.Null,
|
|
29
58
|
tenantId: n.NullOr(n.String),
|
|
30
59
|
credentialRejected: n.optional(n.String)
|
|
31
|
-
}),
|
|
60
|
+
}), p = n.Struct({
|
|
32
61
|
type: n.Literal("system"),
|
|
33
62
|
id: n.String,
|
|
34
63
|
tenantId: n.Null,
|
|
35
|
-
scopes:
|
|
36
|
-
metadata: n.optional(
|
|
37
|
-
}),
|
|
64
|
+
scopes: c,
|
|
65
|
+
metadata: n.optional(s)
|
|
66
|
+
}), ee = n.Union(l, u, d, f, p), m = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes")), h = (e) => {
|
|
38
67
|
if (e.type === "anonymous") return e;
|
|
39
68
|
let { scopes: t, ...n } = e;
|
|
40
69
|
return n;
|
|
41
|
-
},
|
|
70
|
+
}, g = (e) => e.scopes ?? [], _ = (e, t) => ({
|
|
42
71
|
type: "anonymous",
|
|
43
72
|
id: null,
|
|
44
73
|
tenantId: e,
|
|
45
74
|
...t === void 0 ? {} : { credentialRejected: t }
|
|
46
|
-
}),
|
|
75
|
+
}), te = (e, t = ["admin:full"]) => ({
|
|
47
76
|
type: "system",
|
|
48
77
|
id: e,
|
|
49
78
|
tenantId: null,
|
|
50
79
|
scopes: t
|
|
51
|
-
}),
|
|
80
|
+
}), v = (e) => {
|
|
52
81
|
if (e.type === "system") return null;
|
|
53
82
|
if (e.type === "serviceAccount" && e.auditActorId !== void 0) return e.auditActorId;
|
|
54
83
|
if (e.type !== "apiKey") return e.id;
|
|
55
84
|
let t = e.metadata?.userId;
|
|
56
85
|
return typeof t == "string" && t !== "" ? t : e.id;
|
|
57
|
-
},
|
|
86
|
+
}, ne = (e, t) => {
|
|
58
87
|
let n = e;
|
|
59
88
|
return {
|
|
60
89
|
type: "serviceAccount",
|
|
61
90
|
id: e.id ?? "system",
|
|
62
91
|
tenantId: t,
|
|
63
|
-
auditActorId:
|
|
64
|
-
scopes:
|
|
92
|
+
auditActorId: v(e),
|
|
93
|
+
scopes: g(e),
|
|
65
94
|
...n.metadata === void 0 ? {} : { metadata: n.metadata }
|
|
66
95
|
};
|
|
67
|
-
},
|
|
96
|
+
}, re = (e) => e.type === "system", ie = "impersonation", ae = (e) => e.metadata?.impersonation ?? null, y = class extends e.Tag("@voltro/Subject")() {}, b = class extends e.Tag("@voltro/ConnectionInfo")() {}, oe = class extends r.Tag()("@voltro/AuthMiddleware", { provides: y }) {}, x = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: b }) {}, S = (e) => typeof e.mountRoutes == "function", se = (e) => Array.isArray(e) ? {
|
|
68
97
|
kind: "grant",
|
|
69
98
|
scopes: e
|
|
70
|
-
} : e,
|
|
71
|
-
if (t.kind === "vetoed") throw new
|
|
99
|
+
} : e, C = (e, t) => {
|
|
100
|
+
if (t.kind === "vetoed") throw new A({ reason: `${t.code}: ${t.message}` });
|
|
72
101
|
if (e.type === "anonymous") return {
|
|
73
102
|
subject: e,
|
|
74
103
|
removed: []
|
|
75
104
|
};
|
|
76
|
-
let n =
|
|
105
|
+
let n = g(e);
|
|
77
106
|
if (t.kind === "grant") {
|
|
78
107
|
if (t.scopes.length === 0) return {
|
|
79
108
|
subject: e,
|
|
@@ -96,14 +125,14 @@ var i = n.Record({
|
|
|
96
125
|
},
|
|
97
126
|
removed: i
|
|
98
127
|
};
|
|
99
|
-
},
|
|
128
|
+
}, w = 3e4, T = 1e4, E = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, ce = () => {
|
|
100
129
|
if (typeof process > "u") return;
|
|
101
130
|
let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
|
|
102
131
|
if (e === void 0 || e === "") return;
|
|
103
132
|
let t = Number(e);
|
|
104
133
|
return Number.isFinite(t) && t >= 0 ? t : void 0;
|
|
105
134
|
}, D = (e = {}) => {
|
|
106
|
-
let t = e.ttlMs ??
|
|
135
|
+
let t = e.ttlMs ?? ce() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
|
|
107
136
|
if (i.size <= n) return;
|
|
108
137
|
let e = r();
|
|
109
138
|
for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
|
|
@@ -139,138 +168,179 @@ var i = n.Record({
|
|
|
139
168
|
};
|
|
140
169
|
}
|
|
141
170
|
};
|
|
142
|
-
},
|
|
171
|
+
}, le = (e) => typeof e.resolve == "function", ue = (e) => {
|
|
143
172
|
let t = e.authorization ?? e.Authorization;
|
|
144
173
|
if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
|
|
174
|
+
}, de = async (e, t, n, r) => {
|
|
175
|
+
if (!i(e)) return e;
|
|
176
|
+
let s = {
|
|
177
|
+
type: e.type,
|
|
178
|
+
id: e.id
|
|
179
|
+
}, c;
|
|
180
|
+
try {
|
|
181
|
+
c = await n.store.read(s);
|
|
182
|
+
} catch (e) {
|
|
183
|
+
throw r(`tenant selection unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
184
|
+
}
|
|
185
|
+
if (c === null || c === e.tenantId) return e;
|
|
186
|
+
let l;
|
|
187
|
+
try {
|
|
188
|
+
l = await n.memberships(e, t);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
throw r(`memberships unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
return o(e, c, l);
|
|
194
|
+
} catch (t) {
|
|
195
|
+
if (!(t instanceof a)) throw t;
|
|
196
|
+
try {
|
|
197
|
+
await n.store.clear(s);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
throw r(`tenant selection unavailable: ${e instanceof Error ? e.message : String(e)}`);
|
|
200
|
+
}
|
|
201
|
+
return e;
|
|
202
|
+
}
|
|
145
203
|
}, O = (e, t) => {
|
|
146
|
-
let n =
|
|
204
|
+
let n = ue(t);
|
|
147
205
|
if (n === void 0) return e;
|
|
148
206
|
let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
|
|
149
207
|
if (r.length === 0) return e;
|
|
150
208
|
let i = new Set(r);
|
|
151
209
|
return [...r, ...e.filter((e) => !i.has(e))];
|
|
152
|
-
},
|
|
153
|
-
let n = t?.resolveScopes, r = t?.subjectGuards ?? []
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
210
|
+
}, k = (e, t) => {
|
|
211
|
+
let n = t?.resolveScopes, r = t?.subjectGuards ?? [];
|
|
212
|
+
if (t?.memberships === void 0 != (t?.tenantSelection === void 0)) throw Error("composeAuthStrategies: `memberships` and `tenantSelection` come together — a tenant selection is verified against the memberships and read from the store; configure both or neither.");
|
|
213
|
+
let i = t?.memberships !== void 0 && t.tenantSelection !== void 0 ? {
|
|
214
|
+
memberships: t.memberships,
|
|
215
|
+
store: t.tenantSelection
|
|
216
|
+
} : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && le(t.scopeCache) ? t.scopeCache : D(t?.scopeCache ?? {});
|
|
217
|
+
return async (s) => {
|
|
218
|
+
let c = t?.getStore?.(), l = c === void 0 ? s : {
|
|
219
|
+
...s,
|
|
220
|
+
store: c
|
|
221
|
+
}, u;
|
|
222
|
+
for (let s of O(e, l.headers)) {
|
|
223
|
+
let e = await s.resolve(l);
|
|
161
224
|
if (e.kind === "matched") {
|
|
162
|
-
let
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
225
|
+
let c = i === void 0 ? e.subject : await de(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
|
|
226
|
+
strategyId: s.id,
|
|
227
|
+
reason: e
|
|
228
|
+
}), new A({ reason: e }))), u = c;
|
|
229
|
+
if (a) {
|
|
230
|
+
let e = async () => {
|
|
231
|
+
let e = n === void 0 ? {
|
|
166
232
|
kind: "grant",
|
|
167
233
|
scopes: []
|
|
168
|
-
} :
|
|
169
|
-
if (
|
|
170
|
-
let
|
|
234
|
+
} : se(await n(c, l));
|
|
235
|
+
if (e.kind === "unavailable" || r.length === 0) return e;
|
|
236
|
+
let t = C(c, e).subject, i = {
|
|
171
237
|
origin: "request",
|
|
172
|
-
headers:
|
|
173
|
-
clientId:
|
|
174
|
-
store:
|
|
238
|
+
headers: l.headers,
|
|
239
|
+
clientId: l.clientId,
|
|
240
|
+
store: l.store
|
|
175
241
|
};
|
|
176
242
|
for (let e of r) {
|
|
177
|
-
let
|
|
178
|
-
if (!
|
|
243
|
+
let n = await e(t, i);
|
|
244
|
+
if (!n.ok) return {
|
|
179
245
|
kind: "vetoed",
|
|
180
|
-
code:
|
|
181
|
-
message:
|
|
246
|
+
code: n.code,
|
|
247
|
+
message: n.message
|
|
182
248
|
};
|
|
183
249
|
}
|
|
184
|
-
return
|
|
185
|
-
}, { decision:
|
|
186
|
-
decision: await
|
|
250
|
+
return e;
|
|
251
|
+
}, { decision: i, fresh: a } = o === null ? {
|
|
252
|
+
decision: await e(),
|
|
187
253
|
fresh: !0
|
|
188
|
-
} : await
|
|
189
|
-
if (
|
|
190
|
-
let e = `${
|
|
254
|
+
} : await o.resolve(E(c), e);
|
|
255
|
+
if (i.kind === "vetoed") {
|
|
256
|
+
let e = `${i.code}: ${i.message}`;
|
|
191
257
|
throw t?.onStrategyFailed?.({
|
|
192
|
-
strategyId:
|
|
258
|
+
strategyId: s.id,
|
|
193
259
|
reason: e
|
|
194
|
-
}), new
|
|
260
|
+
}), new A({ reason: e });
|
|
195
261
|
}
|
|
196
|
-
if (
|
|
197
|
-
let e = `scope resolution unavailable: ${
|
|
262
|
+
if (i.kind === "unavailable") {
|
|
263
|
+
let e = `scope resolution unavailable: ${i.reason}`;
|
|
198
264
|
throw t?.onStrategyFailed?.({
|
|
199
|
-
strategyId:
|
|
265
|
+
strategyId: s.id,
|
|
200
266
|
reason: e
|
|
201
|
-
}), new
|
|
267
|
+
}), new A({ reason: e });
|
|
202
268
|
}
|
|
203
|
-
let d =
|
|
204
|
-
|
|
205
|
-
strategyId:
|
|
206
|
-
subjectType:
|
|
207
|
-
subjectId:
|
|
269
|
+
let d = C(c, i);
|
|
270
|
+
a && d.removed.length > 0 && t?.onScopesNarrowed?.({
|
|
271
|
+
strategyId: s.id,
|
|
272
|
+
subjectType: c.type,
|
|
273
|
+
subjectId: c.id,
|
|
208
274
|
removed: d.removed,
|
|
209
|
-
granted:
|
|
210
|
-
}),
|
|
275
|
+
granted: i.scopes
|
|
276
|
+
}), u = d.subject;
|
|
211
277
|
}
|
|
212
|
-
return e.credentialExpiresAt === void 0 ? { subject:
|
|
213
|
-
subject:
|
|
278
|
+
return e.credentialExpiresAt === void 0 ? { subject: u } : {
|
|
279
|
+
subject: u,
|
|
214
280
|
credentialExpiresAt: e.credentialExpiresAt
|
|
215
281
|
};
|
|
216
282
|
}
|
|
217
283
|
if (e.kind === "failed") {
|
|
218
284
|
t?.onStrategyFailed?.({
|
|
219
|
-
strategyId:
|
|
285
|
+
strategyId: s.id,
|
|
220
286
|
reason: e.reason
|
|
221
|
-
}),
|
|
287
|
+
}), u = e.reason;
|
|
222
288
|
break;
|
|
223
289
|
}
|
|
224
290
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
291
|
+
if (t?.fallback) {
|
|
292
|
+
let e = await t.fallback(l);
|
|
293
|
+
return { subject: u !== void 0 && e.type === "anonymous" ? {
|
|
294
|
+
...e,
|
|
295
|
+
credentialRejected: u
|
|
296
|
+
} : e };
|
|
297
|
+
}
|
|
298
|
+
if (u !== void 0) throw new A({ reason: `credential rejected: ${u}` });
|
|
299
|
+
if (t?.anonymousTenantId !== void 0) return { subject: _(t.anonymousTenantId) };
|
|
300
|
+
let d = l.headers["x-tenant"] ?? null;
|
|
301
|
+
if (d === null && t?.anonymousTenantRequired === !0) throw new A({ reason: "tenant required (x-tenant header missing)" });
|
|
302
|
+
return { subject: _(d) };
|
|
233
303
|
};
|
|
234
|
-
},
|
|
235
|
-
if (e.type === "anonymous") throw new
|
|
236
|
-
},
|
|
304
|
+
}, A = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, j = (e, t) => {
|
|
305
|
+
if (e.type === "anonymous") throw new A(t === void 0 ? {} : { reason: t });
|
|
306
|
+
}, M = "admin:full", N = "apikeys:issue:self", P = "apikeys:issue:org", F = "apikeys:issue:other", I = "devices:manage", L = class extends n.TaggedError()("ScopeError", {
|
|
237
307
|
required: n.String,
|
|
238
308
|
message: n.String
|
|
239
|
-
}) {},
|
|
240
|
-
typeof e == "object" && e &&
|
|
241
|
-
},
|
|
309
|
+
}) {}, R = (e) => "scopes" in e && e.scopes ? e.scopes : [], z = /* @__PURE__ */ new WeakMap(), B = (e, t) => {
|
|
310
|
+
typeof e == "object" && e && z.set(e, t);
|
|
311
|
+
}, V = (e) => {
|
|
242
312
|
if (typeof e == "object" && e) {
|
|
243
|
-
let t =
|
|
313
|
+
let t = z.get(e);
|
|
244
314
|
if (t) return t;
|
|
245
315
|
}
|
|
246
|
-
return
|
|
247
|
-
},
|
|
248
|
-
let n =
|
|
316
|
+
return R(e);
|
|
317
|
+
}, H = (e, t) => {
|
|
318
|
+
let n = V(e);
|
|
249
319
|
return n.includes("admin:full") || n.includes(t);
|
|
250
|
-
},
|
|
251
|
-
let n =
|
|
320
|
+
}, U = (e, t) => {
|
|
321
|
+
let n = R(e);
|
|
252
322
|
return n.includes("admin:full") || n.includes(t);
|
|
253
|
-
},
|
|
323
|
+
}, fe = (e, n) => H(e, n) ? t.void : t.fail(new L({
|
|
254
324
|
required: n,
|
|
255
325
|
message: `missing required scope '${n}'`
|
|
256
|
-
})),
|
|
326
|
+
})), W = (e, t) => {
|
|
257
327
|
let n;
|
|
258
328
|
try {
|
|
259
329
|
n = typeof e.action == "function" ? e.action(t) : e.action;
|
|
260
330
|
} catch {
|
|
261
|
-
throw new
|
|
331
|
+
throw new L({
|
|
262
332
|
required: e.resourceType,
|
|
263
333
|
message: "policy action selector failed"
|
|
264
334
|
});
|
|
265
335
|
}
|
|
266
|
-
if (typeof n != "string" || n.trim() === "") throw new
|
|
336
|
+
if (typeof n != "string" || n.trim() === "") throw new L({
|
|
267
337
|
required: e.resourceType,
|
|
268
338
|
message: "policy action selector returned no action"
|
|
269
339
|
});
|
|
270
340
|
return n;
|
|
271
|
-
},
|
|
272
|
-
|
|
273
|
-
},
|
|
341
|
+
}, G = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", K = (e) => typeof e.open == "string", q = (e) => e.authenticated === !0, pe = (e) => ({ open: e }), J = (e) => typeof e == "string" ? [e] : e, Y, me = (e) => {
|
|
342
|
+
Y = e;
|
|
343
|
+
}, he = (e) => Y === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !G(e) && e.resource !== void 0)).map((e) => e.tag) : [], ge = (e) => [
|
|
274
344
|
`${e.length} procedure${e.length === 1 ? "" : "s"} declare a per-resource guard that nothing enforces:`,
|
|
275
345
|
...e.slice(0, 10).map((e) => ` ${e}`),
|
|
276
346
|
...e.length > 10 ? [` … and ${e.length - 10} more`] : [],
|
|
@@ -284,56 +354,56 @@ var i = n.Record({
|
|
|
284
354
|
" • setResourceScopeResolver((req) => Effect<boolean>) — answers \"does this",
|
|
285
355
|
" subject hold `req.scope` on `req.resource`\" from your own tables.",
|
|
286
356
|
" • Drop the `resource` extractor if a global scope check is what you meant."
|
|
287
|
-
].join("\n"),
|
|
357
|
+
].join("\n"), _e = () => Y, X, ve = (e) => {
|
|
288
358
|
X = e;
|
|
289
|
-
},
|
|
359
|
+
}, ye = () => X, Z = (e) => new L({
|
|
290
360
|
required: "access-decision",
|
|
291
361
|
message: `${e ?? "this procedure"} declares no access decision: it has neither \`guards:\` nor \`openAccess:\`, and the app runs with \`security.defaultDeny\`. Add the scope(s) a caller must hold (\`guards: [{ scope: '…' }]\`), or declare it open on purpose with \`openAccess: '<why anyone may call this>'\`.`
|
|
292
362
|
}), Q = (e, t) => {
|
|
293
363
|
let n = e.credentialRejected;
|
|
294
|
-
return n === void 0 ? t : new
|
|
364
|
+
return n === void 0 ? t : new A({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
|
|
295
365
|
}, $ = (e, t, n) => {
|
|
296
366
|
if (!t || t.length === 0) return n?.defaultDeny === !0 ? Z(n.procedure) : null;
|
|
297
367
|
for (let n of t) {
|
|
298
|
-
if (
|
|
299
|
-
if (
|
|
368
|
+
if (K(n)) continue;
|
|
369
|
+
if (q(n)) {
|
|
300
370
|
if (e.id !== null && e.id !== void 0) continue;
|
|
301
|
-
return Q(e, new
|
|
371
|
+
return Q(e, new L({
|
|
302
372
|
required: "authenticated",
|
|
303
373
|
message: "this procedure requires an authenticated caller"
|
|
304
374
|
}));
|
|
305
375
|
}
|
|
306
|
-
if (
|
|
307
|
-
if (
|
|
308
|
-
return Q(e, new
|
|
376
|
+
if (G(n)) {
|
|
377
|
+
if (H(e, "admin:full")) continue;
|
|
378
|
+
return Q(e, new L({
|
|
309
379
|
required: `${n.resourceType}:${n.action}`,
|
|
310
380
|
message: `relationship guard '${n.action}' on '${n.resourceType}' cannot be checked synchronously`
|
|
311
381
|
}));
|
|
312
382
|
}
|
|
313
|
-
let t =
|
|
383
|
+
let t = J(n.scope);
|
|
314
384
|
if (t.length === 0) continue;
|
|
315
|
-
let r = t.filter((t) =>
|
|
385
|
+
let r = t.filter((t) => H(e, t));
|
|
316
386
|
if (!((n.mode ?? "all") === "any" ? r.length > 0 : r.length === t.length)) {
|
|
317
|
-
let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !
|
|
318
|
-
return Q(e, new
|
|
387
|
+
let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !H(e, t));
|
|
388
|
+
return Q(e, new L({
|
|
319
389
|
required: r,
|
|
320
390
|
message: (n.mode ?? "all") === "any" ? `missing all of required scopes [${t.join(", ")}]` : `missing required scope '${r}'`
|
|
321
391
|
}));
|
|
322
392
|
}
|
|
323
393
|
}
|
|
324
394
|
return null;
|
|
325
|
-
},
|
|
395
|
+
}, be = (e, n, r) => {
|
|
326
396
|
let i;
|
|
327
397
|
try {
|
|
328
|
-
i =
|
|
398
|
+
i = W(n, r);
|
|
329
399
|
} catch (e) {
|
|
330
400
|
return t.succeed(e);
|
|
331
401
|
}
|
|
332
|
-
let a = (e) => new
|
|
402
|
+
let a = (e) => new L({
|
|
333
403
|
required: `${n.resourceType}:${i}`,
|
|
334
404
|
message: e
|
|
335
405
|
});
|
|
336
|
-
if (
|
|
406
|
+
if (H(e, "admin:full")) return t.succeed(null);
|
|
337
407
|
let o = X;
|
|
338
408
|
if (o === void 0) return t.succeed(a(`no policy resolver registered — cannot authorize '${i}' on '${n.resourceType}'`));
|
|
339
409
|
let s = n.resource(r);
|
|
@@ -343,29 +413,29 @@ var i = n.Record({
|
|
|
343
413
|
resourceType: n.resourceType,
|
|
344
414
|
resourceId: s
|
|
345
415
|
})).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((e) => e ? null : a(`no '${i}' relation on ${n.resourceType}:${s}`)));
|
|
346
|
-
},
|
|
416
|
+
}, xe = (e, n, r, i) => {
|
|
347
417
|
if (!n || n.length === 0) return t.succeed($(e, n, i));
|
|
348
|
-
let a =
|
|
349
|
-
return !n.some(
|
|
418
|
+
let a = Y;
|
|
419
|
+
return !n.some(G) && (!a || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
|
|
350
420
|
for (let i of n) {
|
|
351
|
-
if (
|
|
352
|
-
if (
|
|
421
|
+
if (K(i)) continue;
|
|
422
|
+
if (q(i)) {
|
|
353
423
|
if (e.id !== null && e.id !== void 0) continue;
|
|
354
|
-
return Q(e, new
|
|
424
|
+
return Q(e, new L({
|
|
355
425
|
required: "authenticated",
|
|
356
426
|
message: "this procedure requires an authenticated caller"
|
|
357
427
|
}));
|
|
358
428
|
}
|
|
359
|
-
if (
|
|
360
|
-
let t = yield*
|
|
429
|
+
if (G(i)) {
|
|
430
|
+
let t = yield* be(e, i, r);
|
|
361
431
|
if (t) return t;
|
|
362
432
|
continue;
|
|
363
433
|
}
|
|
364
|
-
let n =
|
|
434
|
+
let n = J(i.scope);
|
|
365
435
|
if (n.length === 0) continue;
|
|
366
436
|
let o = i.resource ? i.resource(r) : void 0, s = [];
|
|
367
437
|
for (let r of n) {
|
|
368
|
-
if (
|
|
438
|
+
if (H(e, r)) {
|
|
369
439
|
s.push(!0);
|
|
370
440
|
continue;
|
|
371
441
|
}
|
|
@@ -381,7 +451,7 @@ var i = n.Record({
|
|
|
381
451
|
let c = i.mode ?? "all";
|
|
382
452
|
if (!(c === "any" ? s.some(Boolean) : s.every(Boolean))) {
|
|
383
453
|
let e = c === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
|
|
384
|
-
return new
|
|
454
|
+
return new L({
|
|
385
455
|
required: e,
|
|
386
456
|
message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (o === void 0 ? "" : ` on resource '${o}'`)
|
|
387
457
|
});
|
|
@@ -391,4 +461,4 @@ var i = n.Record({
|
|
|
391
461
|
});
|
|
392
462
|
};
|
|
393
463
|
//#endregion
|
|
394
|
-
export {
|
|
464
|
+
export { o as $, T as A, k as B, B as C, oe as D, R as E, y as F, ae as G, re as H, A as I, h as J, E as K, _ as L, ie as M, ee as N, b as O, m as P, i as Q, C as R, W as S, me as T, D as U, S as V, O as W, ne as X, te as Y, a as Z, K as _, I as a, pe as b, $ as c, he as d, ye as f, q as g, U as h, N as i, w as j, x as k, xe as l, H as m, P as n, L as o, _e as p, v as q, F as r, ge as s, M as t, V as u, G as v, ve as w, fe as x, Z as y, j as z };
|
package/dist/session.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { E as e, J as t, P as n } from "./scopes-Dj7KCJab.js";
|
|
2
2
|
import { Schema as r } from "effect";
|
|
3
3
|
import { createHmac as i, timingSafeEqual as a } from "node:crypto";
|
|
4
4
|
import { createLogger as o } from "@voltro/logger";
|
|
5
5
|
//#region src/session.ts
|
|
6
6
|
var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
|
|
7
7
|
v: r.Literal(2),
|
|
8
|
-
subject:
|
|
8
|
+
subject: n,
|
|
9
9
|
exp: r.Number,
|
|
10
10
|
iat: r.Number,
|
|
11
11
|
kid: r.optional(r.String)
|
|
@@ -38,14 +38,14 @@ var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
|
|
|
38
38
|
}, C = 604800, w = .7, T = (e, t) => x(i("sha256", t).update(e).digest()), E = (e) => typeof e == "string" ? {
|
|
39
39
|
kid: "k0",
|
|
40
40
|
secret: e
|
|
41
|
-
} : e, D = (
|
|
41
|
+
} : e, D = (n, r, i = {}) => {
|
|
42
42
|
let a = E(r);
|
|
43
43
|
if (!a.secret) throw Error("signSession: secret must be a non-empty string");
|
|
44
|
-
let o =
|
|
45
|
-
if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${
|
|
44
|
+
let o = e(n);
|
|
45
|
+
if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${n.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
|
|
46
46
|
let s = i.ttlSeconds ?? C, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
|
|
47
47
|
v: 2,
|
|
48
|
-
subject: n
|
|
48
|
+
subject: t(n),
|
|
49
49
|
exp: c + s,
|
|
50
50
|
iat: c,
|
|
51
51
|
...typeof r == "string" ? {} : { kid: a.kid }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.73.0",
|
|
4
4
|
"description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -63,9 +63,10 @@
|
|
|
63
63
|
"node": ">=24.0.0"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
+
"@effect/experimental": "^0.61.1",
|
|
66
67
|
"@effect/sql": "^0.52.1",
|
|
67
|
-
"@voltro/database": "0.
|
|
68
|
-
"@voltro/logger": "0.
|
|
68
|
+
"@voltro/database": "0.73.0",
|
|
69
|
+
"@voltro/logger": "0.73.0",
|
|
69
70
|
"jose": "^6.2.10"
|
|
70
71
|
},
|
|
71
72
|
"peerDependencies": {
|