@camstack/server 1.2.594 → 1.2.595
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/addon-root-inventory.js +50 -11
- package/dist/agent/addon-scan.js +44 -6
- package/dist/agent/agent-addon-runtime.js +8 -3
- package/dist/agent/agent-addon-updates.js +52 -12
- package/dist/agent/agent-cap-dispatch-service.js +5 -3
- package/dist/agent/agent-config.js +42 -7
- package/dist/agent/agent-deploy-swap.js +41 -5
- package/dist/agent/agent-group-runner.js +47 -8
- package/dist/agent/agent-http-auth.js +22 -10
- package/dist/agent/agent-http.js +113 -64
- package/dist/agent/agent-hub-addon-update.js +16 -10
- package/dist/agent/agent-hub-registration.js +7 -4
- package/dist/agent/agent-infra-capabilities.js +9 -5
- package/dist/agent/agent-json-file.js +42 -5
- package/dist/agent/agent-mesh-gate.js +8 -5
- package/dist/agent/agent-node-certificates.js +28 -25
- package/dist/agent/agent-pairing-local.js +47 -10
- package/dist/agent/agent-pairing-routes.js +20 -16
- package/dist/agent/agent-pairing-store.js +58 -18
- package/dist/agent/agent-reconnect.js +4 -1
- package/dist/agent/agent-service.js +71 -35
- package/dist/agent/agent-sniffing-listener.js +48 -11
- package/dist/agent/agent-undeploy.js +41 -5
- package/dist/agent/agent-update-routes.js +19 -14
- package/dist/agent/agent-update-service.js +80 -38
- package/dist/agent/apply-model-distribution.js +4 -1
- package/dist/agent/apply-stage-dev-uploads.js +9 -6
- package/dist/agent/derive-hub-url.js +8 -3
- package/dist/agent/fetch-bundle-from-hub.js +9 -6
- package/dist/agent/hubs-found.js +43 -6
- package/dist/agent/infra-boot-guard.js +4 -1
- package/dist/agent/infra-boot-plan.js +6 -2
- package/dist/agent/main.js +183 -148
- package/dist/agent/register-agent-cap-dispatch.js +6 -3
- package/dist/agent/serial-queue.js +7 -2
- package/dist/agent-status-page.js +4 -1
- package/dist/api/addon-upload.js +61 -22
- package/dist/api/agent-addon-delivery.js +11 -6
- package/dist/api/auth-whoami.js +4 -1
- package/dist/api/backup-download-pump.js +9 -5
- package/dist/api/backup-download.js +10 -7
- package/dist/api/core/addon-settings.router.js +19 -16
- package/dist/api/core/agent-pairing.router.js +79 -76
- package/dist/api/core/agents.router.js +24 -21
- package/dist/api/core/auth-admin-tokens.procedures.js +52 -48
- package/dist/api/core/auth-device-cert.procedures.js +67 -62
- package/dist/api/core/auth-limits.js +25 -16
- package/dist/api/core/auth-reauth.procedures.js +37 -32
- package/dist/api/core/auth-revoke-credentials.procedures.js +26 -22
- package/dist/api/core/auth-sessions.procedures.js +56 -52
- package/dist/api/core/auth.router.js +304 -300
- package/dist/api/core/cap-providers.js +121 -74
- package/dist/api/core/capabilities.router.js +37 -34
- package/dist/api/core/capability-preference-writes.js +17 -10
- package/dist/api/core/certificates.router.js +109 -106
- package/dist/api/core/cluster-nodes.router.js +18 -15
- package/dist/api/core/collection-preference.js +6 -2
- package/dist/api/core/event-bus-proxy.router.js +37 -34
- package/dist/api/core/failure-contributions.js +8 -4
- package/dist/api/core/hwaccel.router.js +20 -17
- package/dist/api/core/integration-secrets.js +12 -8
- package/dist/api/core/lifecycle-job-runner.js +49 -13
- package/dist/api/core/live-events.router.js +32 -29
- package/dist/api/core/load-contributions.js +8 -4
- package/dist/api/core/logging-settings.js +43 -30
- package/dist/api/core/logs.router.js +43 -40
- package/dist/api/core/notifications.router.js +18 -15
- package/dist/api/core/public-event-filter.js +8 -4
- package/dist/api/core/repl.router.js +21 -18
- package/dist/api/core/request-census-settings.js +16 -11
- package/dist/api/core/server-management.provider.js +4 -1
- package/dist/api/core/server-name.js +17 -9
- package/dist/api/core/site-location.js +24 -16
- package/dist/api/core/stream-probe.router.js +24 -21
- package/dist/api/core/system-events.router.js +26 -23
- package/dist/api/cors/cors-policy.js +29 -20
- package/dist/api/cors/register-cors-policy.js +14 -9
- package/dist/api/deploy-stage-registry.js +10 -6
- package/dist/api/dev-uploads-bundle.js +50 -12
- package/dist/api/health/adaptive-probe.routes.js +13 -9
- package/dist/api/health/health.routes.js +13 -8
- package/dist/api/hub-cluster-url.js +9 -3
- package/dist/api/model-distributor.js +50 -12
- package/dist/api/oauth2/consent-page.js +13 -10
- package/dist/api/oauth2/oauth2-routes.js +25 -17
- package/dist/api/oauth2/private-host.js +4 -1
- package/dist/api/server-upload-propagate.js +51 -15
- package/dist/api/server-upload.js +50 -13
- package/dist/api/sse-no-compression.js +4 -1
- package/dist/api/static/frame-policy.js +9 -4
- package/dist/api/static/precompressed-asset.js +57 -15
- package/dist/api/static/spa-mount.js +47 -8
- package/dist/api/static/spa-static.js +12 -5
- package/dist/api/tarball-manifest.js +44 -7
- package/dist/api/trpc/cap-mount-helpers.js +12 -7
- package/dist/api/trpc/cap-route-error-formatter.js +13 -10
- package/dist/api/trpc/cap-router-runtime.js +51 -40
- package/dist/api/trpc/client-ip.js +20 -9
- package/dist/api/trpc/core-cap-bridge.js +42 -32
- package/dist/api/trpc/device-collection-dispatch.js +27 -23
- package/dist/api/trpc/device-config-secret-redaction.js +40 -24
- package/dist/api/trpc/generated-cap-mounts.js +222 -218
- package/dist/api/trpc/generated-cap-routers.js +4856 -4676
- package/dist/api/trpc/http-request-census-hook.js +11 -8
- package/dist/api/trpc/http-request-census.js +41 -34
- package/dist/api/trpc/http-request-census.singleton.js +8 -4
- package/dist/api/trpc/localized-schema-paths.js +4 -1
- package/dist/api/trpc/principal-visibility.js +22 -14
- package/dist/api/trpc/remote-addon-update-grant.js +4 -1
- package/dist/api/trpc/scope-access.js +15 -10
- package/dist/api/trpc/server-provided-caps.js +7 -3
- package/dist/api/trpc/share-view-access.js +14 -6
- package/dist/api/trpc/trpc-error-device-tags.js +6 -3
- package/dist/api/trpc/trpc-error-principal.js +6 -2
- package/dist/api/trpc/trpc.context.js +25 -20
- package/dist/api/trpc/trpc.middleware.js +46 -38
- package/dist/api/trpc/trpc.router.js +99 -58
- package/dist/api/trpc/user-error.js +8 -5
- package/dist/api/trpc/ws-request-census.js +24 -17
- package/dist/api/trpc/ws-slow-consumer-guard.js +16 -11
- package/dist/api/upload-auth.js +9 -5
- package/dist/auth/addon-route-jwt-gate.js +7 -4
- package/dist/auth/addon-route-share-gate.js +12 -7
- package/dist/auth/app-confirm-page.js +22 -17
- package/dist/auth/app-confirm-routes.js +31 -26
- package/dist/auth/app-confirmations.js +44 -7
- package/dist/auth/app-page-shell.js +30 -17
- package/dist/auth/app-page-strings.js +17 -11
- package/dist/auth/app-passkey-page.js +21 -16
- package/dist/auth/app-passkey-routes.js +41 -37
- package/dist/auth/app-refused-route.js +18 -13
- package/dist/auth/auth-network-policy.js +15 -11
- package/dist/auth/auth-provider-gate.js +9 -4
- package/dist/auth/auth-rate-limit.js +7 -3
- package/dist/auth/client-address.js +9 -6
- package/dist/auth/device-enroll-proofs.js +43 -6
- package/dist/auth/external-link-intents.js +7 -3
- package/dist/auth/hub-sessions.js +12 -6
- package/dist/auth/native-link-completions.js +11 -7
- package/dist/auth/native-return.js +13 -9
- package/dist/auth/passkey-provider.js +32 -26
- package/dist/auth/second-factors.js +10 -6
- package/dist/auth/session-connections.js +12 -6
- package/dist/auth/session-cookie.js +23 -12
- package/dist/auth/session-lifetimes.js +12 -8
- package/dist/auth/session-mint.js +11 -4
- package/dist/auth/sso-finish-app.js +16 -13
- package/dist/auth/sso-finish-deps.js +27 -21
- package/dist/auth/sso-finish.js +38 -28
- package/dist/auth/sso-routes.js +25 -22
- package/dist/auth/used-bridge-ids.js +5 -1
- package/dist/boot/boot-config.js +55 -16
- package/dist/boot/init-sequence.js +4 -1
- package/dist/boot/integration-id-backfill.js +8 -3
- package/dist/boot/platform-keychain-key.js +42 -5
- package/dist/boot/post-boot.service.js +18 -14
- package/dist/boot/reconcile-lifecycle-jobs.js +6 -3
- package/dist/boot/retired-public-url-env.js +8 -4
- package/dist/bootstrap-packages.js +4 -1
- package/dist/closure-node-modules.js +38 -2
- package/dist/core/addon/addon-call-gateway.js +5 -1
- package/dist/core/addon/addon-package.service.js +123 -83
- package/dist/core/addon/addon-process-view.js +6 -2
- package/dist/core/addon/addon-realign-plan.js +4 -1
- package/dist/core/addon/addon-registry.service.js +187 -146
- package/dist/core/addon/addon-row-manifest.js +4 -1
- package/dist/core/addon/addon-search.service.js +7 -3
- package/dist/core/addon/addon-settings-provider.js +3 -1
- package/dist/core/addon/addon-uninstall.js +11 -4
- package/dist/core/addon/addon.tokens.js +5 -2
- package/dist/core/addon/device-meta-mirror.js +13 -8
- package/dist/core/addon/forked-custom-actions.js +11 -6
- package/dist/core/addon/integration-visibility.js +6 -3
- package/dist/core/addon/materialize-route-path.js +4 -1
- package/dist/core/addon/package-dir-utils.js +45 -8
- package/dist/core/addon/prune-misplaced-addons.js +43 -5
- package/dist/core/addon/runner-convergence.js +4 -1
- package/dist/core/addon/runner-serialization.js +4 -1
- package/dist/core/addon/runner-spawn-fanout.js +8 -3
- package/dist/core/addon/settings-door-wiring.js +8 -5
- package/dist/core/addon/single-flight-refresh.js +4 -1
- package/dist/core/addon-bridge/addon-bridge.service.js +44 -7
- package/dist/core/addon-pages/addon-pages.service.js +40 -3
- package/dist/core/addon-widgets/addon-widgets.service.js +42 -4
- package/dist/core/agent/agent-addon-backfill.js +17 -9
- package/dist/core/agent/agent-delivery-ledger.js +7 -3
- package/dist/core/agent/agent-liveness-event.js +6 -2
- package/dist/core/agent/agent-pairing-http.js +45 -41
- package/dist/core/agent/agent-registry.service.js +89 -50
- package/dist/core/agent/cluster-node-history-mirror.js +7 -3
- package/dist/core/agent/cluster-node-history-store.js +35 -31
- package/dist/core/agent/hub-agent-pairing.js +5 -1
- package/dist/core/agent/node-certificates-client.js +6 -3
- package/dist/core/agent/node-directory.js +4 -1
- package/dist/core/app-links/well-known-app-links.js +6 -2
- package/dist/core/auth/auth.service.js +8 -3
- package/dist/core/auth/handoff-code.service.js +49 -11
- package/dist/core/auth/record-kinds.js +19 -16
- package/dist/core/auth/share-token.service.js +79 -42
- package/dist/core/capability/capability.service.js +5 -1
- package/dist/core/config/config.schema.js +6 -1
- package/dist/core/config/config.service.js +6 -2
- package/dist/core/events/event-bus.service.js +7 -3
- package/dist/core/feature/feature.service.js +6 -2
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +10 -5
- package/dist/core/lifecycle/lifecycle-state-machine.js +5 -1
- package/dist/core/logging/log-ring-buffer.js +5 -1
- package/dist/core/logging/logging.service.js +9 -5
- package/dist/core/logging/scoped-logger.js +5 -1
- package/dist/core/moleculer/agent-readiness-pull.js +4 -1
- package/dist/core/moleculer/agent-registration-request.js +7 -3
- package/dist/core/moleculer/cap-call-fn.js +7 -4
- package/dist/core/moleculer/cap-route-authority.js +8 -3
- package/dist/core/moleculer/hub-mesh-guard.js +9 -5
- package/dist/core/moleculer/moleculer.service.js +86 -79
- package/dist/core/network/network-quality.service.js +6 -2
- package/dist/core/notification/notification-wrapper.service.js +7 -3
- package/dist/core/notification/toast-wrapper.service.js +7 -3
- package/dist/core/provider/provider.tokens.js +4 -1
- package/dist/core/repl/repl-engine.service.js +8 -4
- package/dist/core/runner-health/runner-boot-check.js +15 -7
- package/dist/core/runner-health/update-preflight.js +14 -9
- package/dist/core/server-update/server-update.service.js +49 -12
- package/dist/core/server-update/system-apply-closure-patches.js +42 -6
- package/dist/core/server-update/system-ensure-prebuilds.js +6 -3
- package/dist/core/server-update/system-exec-npm.js +5 -2
- package/dist/core/storage/fs-storage-backend.js +5 -1
- package/dist/core/storage/storage-location-manager.js +5 -1
- package/dist/core/storage/storage.service.js +6 -2
- package/dist/core/streaming/ffmpeg-stream-info.js +8 -3
- package/dist/core/streaming/stream-probe.service.js +16 -12
- package/dist/core/streaming/ttl-cache.js +5 -1
- package/dist/core/topology/topology-emitter.service.js +23 -19
- package/dist/core/update-availability-emitter.js +7 -3
- package/dist/core/update-availability-store.js +45 -8
- package/dist/core/updates/agent-installed-packages.js +9 -5
- package/dist/core/updates/restart-marker.js +8 -3
- package/dist/core/updates/update-check-scheduler.js +18 -13
- package/dist/core/updates/wrapper-update-checker.js +7 -3
- package/dist/first-boot-addon-plan.js +6 -2
- package/dist/framework-nodepath.js +38 -2
- package/dist/i18n/request-lang.js +11 -7
- package/dist/launcher.js +65 -30
- package/dist/lifecycle-journal-path.js +38 -2
- package/dist/main.js +324 -286
- package/dist/manual-boot.js +156 -117
- package/dist/node-role.js +4 -1
- package/dist/package-inventory.js +48 -7
- package/dist/pending-restore.js +43 -7
- package/dist/single-copy-cleanup-runner.js +45 -9
- package/dist/single-copy-cleanup.js +15 -7
- package/dist/sqlite-restore.js +48 -9
- package/dist/starter.js +39 -4
- package/dist/ui-serving/lazy-ui-serving.js +39 -3
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* The WebSocket plane of the transport census — hooked PER OPERATION.
|
|
3
4
|
*
|
|
@@ -50,8 +51,13 @@
|
|
|
50
51
|
* {@link UNKNOWN} instead: a call this instrument saw is never dropped for
|
|
51
52
|
* want of a name.
|
|
52
53
|
*/
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.WsCensusSession = exports.MAX_PENDING_WS_OPERATIONS = exports.WS_PATHLESS_PROCEDURE = void 0;
|
|
56
|
+
exports.parseTrpcWsFrame = parseTrpcWsFrame;
|
|
57
|
+
exports.attachWsCensusSession = attachWsCensusSession;
|
|
58
|
+
exports.identifyWsCensusSession = identifyWsCensusSession;
|
|
59
|
+
const client_ip_js_1 = require("./client-ip.js");
|
|
60
|
+
const http_request_census_js_1 = require("./http-request-census.js");
|
|
55
61
|
/** Keepalive frames the tRPC adapter answers before parsing. Not operations. */
|
|
56
62
|
const KEEPALIVE_FRAMES = new Set(['PING', 'PONG']);
|
|
57
63
|
/**
|
|
@@ -60,7 +66,7 @@ const KEEPALIVE_FRAMES = new Set(['PING', 'PONG']);
|
|
|
60
66
|
* tRPC rejects such a frame, but it still ARRIVED, and a census that drops it
|
|
61
67
|
* has a hole in the total the plane comparison rests on.
|
|
62
68
|
*/
|
|
63
|
-
|
|
69
|
+
exports.WS_PATHLESS_PROCEDURE = '(ws-no-path)';
|
|
64
70
|
/**
|
|
65
71
|
* Operations held for a connection whose principal has not resolved yet.
|
|
66
72
|
*
|
|
@@ -69,7 +75,7 @@ export const WS_PATHLESS_PROCEDURE = '(ws-no-path)';
|
|
|
69
75
|
* without limit. Past it, operations are counted immediately under
|
|
70
76
|
* {@link UNKNOWN} rather than dropped.
|
|
71
77
|
*/
|
|
72
|
-
|
|
78
|
+
exports.MAX_PENDING_WS_OPERATIONS = 128;
|
|
73
79
|
const NO_OPERATIONS = { calls: [], subscriptions: [], stops: 0 };
|
|
74
80
|
/**
|
|
75
81
|
* Read the operations out of one raw client frame.
|
|
@@ -78,7 +84,7 @@ const NO_OPERATIONS = { calls: [], subscriptions: [], stops: 0 };
|
|
|
78
84
|
* every one of them is a place where the count could be wrong in the direction
|
|
79
85
|
* that exonerates this plane.
|
|
80
86
|
*/
|
|
81
|
-
|
|
87
|
+
function parseTrpcWsFrame(raw) {
|
|
82
88
|
const text = raw.trim();
|
|
83
89
|
if (text.length === 0)
|
|
84
90
|
return NO_OPERATIONS;
|
|
@@ -102,7 +108,7 @@ export function parseTrpcWsFrame(raw) {
|
|
|
102
108
|
}
|
|
103
109
|
if (method !== 'query' && method !== 'mutation' && method !== 'subscription')
|
|
104
110
|
continue;
|
|
105
|
-
if (calls.length + subscriptions.length >= MAX_BATCH_PROCEDURES) {
|
|
111
|
+
if (calls.length + subscriptions.length >= http_request_census_js_1.MAX_BATCH_PROCEDURES) {
|
|
106
112
|
overflowed = true;
|
|
107
113
|
continue;
|
|
108
114
|
}
|
|
@@ -113,7 +119,7 @@ export function parseTrpcWsFrame(raw) {
|
|
|
113
119
|
calls.push(path);
|
|
114
120
|
}
|
|
115
121
|
if (overflowed)
|
|
116
|
-
calls.push(BATCH_OVERFLOW_PROCEDURE);
|
|
122
|
+
calls.push(http_request_census_js_1.BATCH_OVERFLOW_PROCEDURE);
|
|
117
123
|
return { calls, subscriptions, stops };
|
|
118
124
|
}
|
|
119
125
|
function parseJsonSafely(text) {
|
|
@@ -128,12 +134,12 @@ function parseJsonSafely(text) {
|
|
|
128
134
|
}
|
|
129
135
|
function readPath(params) {
|
|
130
136
|
if (typeof params !== 'object' || params === null)
|
|
131
|
-
return WS_PATHLESS_PROCEDURE;
|
|
137
|
+
return exports.WS_PATHLESS_PROCEDURE;
|
|
132
138
|
const path = Reflect.get(params, 'path');
|
|
133
|
-
return typeof path === 'string' && path.length > 0 ? path : WS_PATHLESS_PROCEDURE;
|
|
139
|
+
return typeof path === 'string' && path.length > 0 ? path : exports.WS_PATHLESS_PROCEDURE;
|
|
134
140
|
}
|
|
135
141
|
/** One connection's census state: its identity, and anything held for it. */
|
|
136
|
-
|
|
142
|
+
class WsCensusSession {
|
|
137
143
|
census;
|
|
138
144
|
userAgent;
|
|
139
145
|
ip;
|
|
@@ -143,8 +149,8 @@ export class WsCensusSession {
|
|
|
143
149
|
closed = false;
|
|
144
150
|
constructor(census, req) {
|
|
145
151
|
this.census = census;
|
|
146
|
-
this.userAgent = extractUserAgent(req);
|
|
147
|
-
this.ip = extractClientIp(req);
|
|
152
|
+
this.userAgent = (0, client_ip_js_1.extractUserAgent)(req);
|
|
153
|
+
this.ip = (0, client_ip_js_1.extractClientIp)(req);
|
|
148
154
|
}
|
|
149
155
|
/**
|
|
150
156
|
* Count one raw client frame.
|
|
@@ -169,9 +175,9 @@ export class WsCensusSession {
|
|
|
169
175
|
this.flushFrame(operations, this.principal);
|
|
170
176
|
return;
|
|
171
177
|
}
|
|
172
|
-
if (this.pending.length >= MAX_PENDING_WS_OPERATIONS) {
|
|
178
|
+
if (this.pending.length >= exports.MAX_PENDING_WS_OPERATIONS) {
|
|
173
179
|
// Never dropped — attributed to the named bucket instead.
|
|
174
|
-
this.flushFrame(operations, UNKNOWN);
|
|
180
|
+
this.flushFrame(operations, http_request_census_js_1.UNKNOWN);
|
|
175
181
|
return;
|
|
176
182
|
}
|
|
177
183
|
this.pending.push(operations);
|
|
@@ -188,7 +194,7 @@ export class WsCensusSession {
|
|
|
188
194
|
if (this.closed)
|
|
189
195
|
return;
|
|
190
196
|
this.closed = true;
|
|
191
|
-
this.drain(this.principal ?? UNKNOWN);
|
|
197
|
+
this.drain(this.principal ?? http_request_census_js_1.UNKNOWN);
|
|
192
198
|
}
|
|
193
199
|
drain(principal) {
|
|
194
200
|
if (this.pending.length === 0)
|
|
@@ -221,6 +227,7 @@ export class WsCensusSession {
|
|
|
221
227
|
}
|
|
222
228
|
}
|
|
223
229
|
}
|
|
230
|
+
exports.WsCensusSession = WsCensusSession;
|
|
224
231
|
/**
|
|
225
232
|
* Sessions by socket.
|
|
226
233
|
*
|
|
@@ -236,7 +243,7 @@ const sessions = new WeakMap();
|
|
|
236
243
|
* Registered from `main.ts` on the same `wss` `applyWSSHandler` is given. Both
|
|
237
244
|
* listeners see every frame; ours only counts.
|
|
238
245
|
*/
|
|
239
|
-
|
|
246
|
+
function attachWsCensusSession(census, client, req, logger) {
|
|
240
247
|
census.observeWsConnection();
|
|
241
248
|
const session = new WsCensusSession(census, req);
|
|
242
249
|
sessions.set(client, session);
|
|
@@ -273,7 +280,7 @@ export function attachWsCensusSession(census, client, req, logger) {
|
|
|
273
280
|
* harness or a future adapter that builds a context without a census session
|
|
274
281
|
* does not have to know this exists.
|
|
275
282
|
*/
|
|
276
|
-
|
|
283
|
+
function identifyWsCensusSession(client, principal) {
|
|
277
284
|
sessions.get(client)?.identify(principal);
|
|
278
285
|
}
|
|
279
286
|
/** Decode a `ws` frame payload to text. Returns `null` for a shape that cannot
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WsSlowConsumerGuard = exports.WS_SLOW_CONSUMER_POLL_MS = exports.WS_SLOW_CONSUMER_GRACE_MS = exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES = void 0;
|
|
4
|
+
exports.wsConnectionFacts = wsConnectionFacts;
|
|
5
|
+
const client_ip_js_1 = require("./client-ip.js");
|
|
2
6
|
/** Queued bytes above which a client is a slow consumer. */
|
|
3
|
-
|
|
7
|
+
exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
|
|
4
8
|
/** How long the queue may stay above the bound before the socket is terminated. */
|
|
5
|
-
|
|
9
|
+
exports.WS_SLOW_CONSUMER_GRACE_MS = 30_000;
|
|
6
10
|
/** How often every connection's queue is read. A getter per socket; free. */
|
|
7
|
-
|
|
11
|
+
exports.WS_SLOW_CONSUMER_POLL_MS = 5_000;
|
|
8
12
|
/** WebSocket close code for a socket that was never closed cleanly. */
|
|
9
13
|
const ABNORMAL_CLOSURE = 1006;
|
|
10
14
|
const UNIDENTIFIED = '(unidentified)';
|
|
11
|
-
|
|
15
|
+
class WsSlowConsumerGuard {
|
|
12
16
|
connections = new Set();
|
|
13
17
|
bySocket = new WeakMap();
|
|
14
18
|
logger;
|
|
@@ -19,9 +23,9 @@ export class WsSlowConsumerGuard {
|
|
|
19
23
|
timer = null;
|
|
20
24
|
constructor(options) {
|
|
21
25
|
this.logger = options.logger;
|
|
22
|
-
this.maxBufferedBytes = options.maxBufferedBytes ?? WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES;
|
|
23
|
-
this.graceMs = options.graceMs ?? WS_SLOW_CONSUMER_GRACE_MS;
|
|
24
|
-
this.pollMs = options.pollMs ?? WS_SLOW_CONSUMER_POLL_MS;
|
|
26
|
+
this.maxBufferedBytes = options.maxBufferedBytes ?? exports.WS_SLOW_CONSUMER_MAX_BUFFERED_BYTES;
|
|
27
|
+
this.graceMs = options.graceMs ?? exports.WS_SLOW_CONSUMER_GRACE_MS;
|
|
28
|
+
this.pollMs = options.pollMs ?? exports.WS_SLOW_CONSUMER_POLL_MS;
|
|
25
29
|
this.now = options.now ?? (() => Date.now());
|
|
26
30
|
}
|
|
27
31
|
/** Watch one connection until it closes. */
|
|
@@ -138,6 +142,7 @@ export class WsSlowConsumerGuard {
|
|
|
138
142
|
};
|
|
139
143
|
}
|
|
140
144
|
}
|
|
145
|
+
exports.WsSlowConsumerGuard = WsSlowConsumerGuard;
|
|
141
146
|
function safeCount(read) {
|
|
142
147
|
try {
|
|
143
148
|
return read();
|
|
@@ -151,10 +156,10 @@ function errorMessage(err) {
|
|
|
151
156
|
return err instanceof Error ? err.message : String(err);
|
|
152
157
|
}
|
|
153
158
|
/** The facts a real `ws` upgrade request provides. */
|
|
154
|
-
|
|
159
|
+
function wsConnectionFacts(req) {
|
|
155
160
|
return {
|
|
156
|
-
ip: extractClientIp(req),
|
|
157
|
-
userAgent: extractUserAgent(req),
|
|
161
|
+
ip: (0, client_ip_js_1.extractClientIp)(req),
|
|
162
|
+
userAgent: (0, client_ip_js_1.extractUserAgent)(req),
|
|
158
163
|
bytesRead: () => req.socket.bytesRead,
|
|
159
164
|
bytesWritten: () => req.socket.bytesWritten,
|
|
160
165
|
};
|
package/dist/api/upload-auth.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UPLOAD_TRPC_PATH = void 0;
|
|
4
|
+
exports.authorizeUploadRequest = authorizeUploadRequest;
|
|
5
|
+
const scope_access_js_1 = require("./trpc/scope-access.js");
|
|
6
|
+
exports.UPLOAD_TRPC_PATH = 'addons.installPackage';
|
|
3
7
|
/**
|
|
4
8
|
* Validate a `cst_*` scoped token via the `user-management` cap singleton.
|
|
5
9
|
* Returns null when the singleton isn't mounted yet (boot race) or the
|
|
@@ -16,7 +20,7 @@ async function validateScopedTokenViaCap(addonRegistry, token) {
|
|
|
16
20
|
return userMgmt.validateScopedToken({ token });
|
|
17
21
|
}
|
|
18
22
|
function isUploadAllowed(scoped) {
|
|
19
|
-
return checkScopeAccess(scoped.scopes, UPLOAD_TRPC_PATH).ok;
|
|
23
|
+
return (0, scope_access_js_1.checkScopeAccess)(scoped.scopes, exports.UPLOAD_TRPC_PATH).ok;
|
|
20
24
|
}
|
|
21
25
|
/**
|
|
22
26
|
* Run the full upload auth chain. Returns `{ok: true}` on success, or the
|
|
@@ -24,7 +28,7 @@ function isUploadAllowed(scoped) {
|
|
|
24
28
|
* - 401 `Unauthorized` when no Authorization header is present,
|
|
25
29
|
* - 403 `Forbidden: <reason>` for any recognised-but-insufficient token.
|
|
26
30
|
*/
|
|
27
|
-
|
|
31
|
+
async function authorizeUploadRequest(args) {
|
|
28
32
|
const { authHeader, authService, addonRegistry } = args;
|
|
29
33
|
if (!authHeader) {
|
|
30
34
|
return { ok: false, status: 401, error: 'Unauthorized' };
|
|
@@ -51,7 +55,7 @@ export async function authorizeUploadRequest(args) {
|
|
|
51
55
|
return { ok: true };
|
|
52
56
|
}
|
|
53
57
|
else {
|
|
54
|
-
authReason = `scoped token lacks create access on '${UPLOAD_TRPC_PATH}'`;
|
|
58
|
+
authReason = `scoped token lacks create access on '${exports.UPLOAD_TRPC_PATH}'`;
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
catch (err) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gateAddonJwt = gateAddonJwt;
|
|
1
4
|
/**
|
|
2
5
|
* The JWT half of the `/addon/:addonId/*` gate — shared by the HTTP data-plane
|
|
3
6
|
* branch and the addon-route branch so there is exactly ONE answer to "may this
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
* the per-camera switches and notification snooze. A 60-second authorization
|
|
12
15
|
* CODE and a 30-day refresh token passed the same way. See D103.
|
|
13
16
|
*/
|
|
14
|
-
|
|
17
|
+
const types_1 = require("@camstack/types");
|
|
15
18
|
function readScopes(raw) {
|
|
16
19
|
return Array.isArray(raw) ? raw : [];
|
|
17
20
|
}
|
|
@@ -24,8 +27,8 @@ function readScopes(raw) {
|
|
|
24
27
|
* an admin-only route keeps the response it already had; nothing that is not a
|
|
25
28
|
* session or an `oauth-access` token gets that far at all.
|
|
26
29
|
*/
|
|
27
|
-
|
|
28
|
-
const principal = classifyBearerPrincipal(input.payload);
|
|
30
|
+
function gateAddonJwt(input, logger) {
|
|
31
|
+
const principal = (0, types_1.classifyBearerPrincipal)(input.payload);
|
|
29
32
|
const isAdmin = input.payload.isAdmin === true;
|
|
30
33
|
if (principal.kind === 'not-a-credential') {
|
|
31
34
|
logger.warn('addon route: refused a bearer that is not an API credential', {
|
|
@@ -39,7 +42,7 @@ export function gateAddonJwt(input, logger) {
|
|
|
39
42
|
});
|
|
40
43
|
return { ok: false, status: 403, error: 'Admin required' };
|
|
41
44
|
}
|
|
42
|
-
const verdict = principalMayReachAddon({
|
|
45
|
+
const verdict = (0, types_1.principalMayReachAddon)({
|
|
43
46
|
principal,
|
|
44
47
|
scopes: readScopes(input.payload.scopes),
|
|
45
48
|
isAdmin,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SHARE_DEVICES_HEADER = exports.SHARE_KIND_HEADER = void 0;
|
|
4
|
+
exports.gateAddonShareToken = gateAddonShareToken;
|
|
5
|
+
exports.stripShareHeaders = stripShareHeaders;
|
|
6
|
+
exports.SHARE_KIND_HEADER = 'x-camstack-share-kind';
|
|
7
|
+
exports.SHARE_DEVICES_HEADER = 'x-camstack-share-devices';
|
|
8
|
+
function gateAddonShareToken(input) {
|
|
4
9
|
if (input.record === null)
|
|
5
10
|
return { ok: false, status: 401, error: 'Invalid token' };
|
|
6
11
|
const kind = input.record.scope.kind;
|
|
@@ -10,17 +15,17 @@ export function gateAddonShareToken(input) {
|
|
|
10
15
|
return {
|
|
11
16
|
ok: true,
|
|
12
17
|
headers: {
|
|
13
|
-
[SHARE_KIND_HEADER]: kind,
|
|
14
|
-
[SHARE_DEVICES_HEADER]: input.record.scope.deviceIds.join(','),
|
|
18
|
+
[exports.SHARE_KIND_HEADER]: kind,
|
|
19
|
+
[exports.SHARE_DEVICES_HEADER]: input.record.scope.deviceIds.join(','),
|
|
15
20
|
},
|
|
16
21
|
};
|
|
17
22
|
}
|
|
18
23
|
/** The forwarded header set without any share header the client supplied. */
|
|
19
|
-
|
|
24
|
+
function stripShareHeaders(headers) {
|
|
20
25
|
const out = {};
|
|
21
26
|
for (const [key, value] of Object.entries(headers)) {
|
|
22
27
|
const lower = key.toLowerCase();
|
|
23
|
-
if (lower === SHARE_KIND_HEADER || lower === SHARE_DEVICES_HEADER)
|
|
28
|
+
if (lower === exports.SHARE_KIND_HEADER || lower === exports.SHARE_DEVICES_HEADER)
|
|
24
29
|
continue;
|
|
25
30
|
out[key] = value;
|
|
26
31
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APP_CONFIRM_REFUSAL_CSP = exports.APP_CONFIRM_CSP = void 0;
|
|
4
|
+
exports.appConfirmPage = appConfirmPage;
|
|
5
|
+
exports.appConfirmRefusalPage = appConfirmRefusalPage;
|
|
1
6
|
/**
|
|
2
7
|
* The hub-rendered confirmation page of a native app sign-in (D858):
|
|
3
8
|
* "Sign in the CamStack app as <username>?".
|
|
@@ -11,8 +16,8 @@
|
|
|
11
16
|
* `location` the hub answers; Cancel is a plain link to the app with
|
|
12
17
|
* `error=cancelled`.
|
|
13
18
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
const app_page_shell_js_1 = require("./app-page-shell.js");
|
|
20
|
+
const app_page_strings_js_1 = require("./app-page-strings.js");
|
|
16
21
|
const PAGE_SCRIPT = `(function () {
|
|
17
22
|
var btn = document.getElementById('go');
|
|
18
23
|
var msg = document.getElementById('msg');
|
|
@@ -38,13 +43,13 @@ const PAGE_SCRIPT = `(function () {
|
|
|
38
43
|
});
|
|
39
44
|
})();`;
|
|
40
45
|
/** The CSP: the shell's style and this page's one script by hash, same-origin requests, no framing, no form posts. */
|
|
41
|
-
|
|
46
|
+
exports.APP_CONFIRM_CSP = (0, app_page_shell_js_1.appPageCsp)({ script: PAGE_SCRIPT });
|
|
42
47
|
/** CSP of the refusal page: the shell's style only, nothing runs. */
|
|
43
|
-
|
|
48
|
+
exports.APP_CONFIRM_REFUSAL_CSP = (0, app_page_shell_js_1.appPageCsp)();
|
|
44
49
|
/** The confirmation page for `username`; `cancelHref` is the app callback carrying `error=cancelled`. */
|
|
45
|
-
|
|
46
|
-
const t = appStrings(input.lang);
|
|
47
|
-
return renderAppPage({
|
|
50
|
+
function appConfirmPage(input) {
|
|
51
|
+
const t = (0, app_page_strings_js_1.appStrings)(input.lang);
|
|
52
|
+
return (0, app_page_shell_js_1.renderAppPage)({
|
|
48
53
|
lang: input.lang,
|
|
49
54
|
title: t.confirm.title,
|
|
50
55
|
heading: t.confirm.heading,
|
|
@@ -52,22 +57,22 @@ export function appConfirmPage(input) {
|
|
|
52
57
|
script: PAGE_SCRIPT,
|
|
53
58
|
scriptStrings: { 'confirm-expired': t.script.confirmExpired },
|
|
54
59
|
body: [
|
|
55
|
-
`<p>${escapeHtml(t.confirm.signedInAsBefore)}<strong>${escapeHtml(input.username)}</strong>${escapeHtml(t.confirm.signedInAsAfter)}</p>`,
|
|
56
|
-
appHint(t.confirm.hint),
|
|
57
|
-
appButton(t.confirm.continueLabel, `id="go" data-confirm="${escapeHtml(input.token)}"`),
|
|
58
|
-
appErrorLine('msg'),
|
|
59
|
-
appCancelLink(input.cancelHref, t.confirm.cancel),
|
|
60
|
-
`<noscript><p class="error">${escapeHtml(t.confirm.noscript)}</p></noscript>`,
|
|
60
|
+
`<p>${(0, app_page_shell_js_1.escapeHtml)(t.confirm.signedInAsBefore)}<strong>${(0, app_page_shell_js_1.escapeHtml)(input.username)}</strong>${(0, app_page_shell_js_1.escapeHtml)(t.confirm.signedInAsAfter)}</p>`,
|
|
61
|
+
(0, app_page_shell_js_1.appHint)(t.confirm.hint),
|
|
62
|
+
(0, app_page_shell_js_1.appButton)(t.confirm.continueLabel, `id="go" data-confirm="${(0, app_page_shell_js_1.escapeHtml)(input.token)}"`),
|
|
63
|
+
(0, app_page_shell_js_1.appErrorLine)('msg'),
|
|
64
|
+
(0, app_page_shell_js_1.appCancelLink)(input.cancelHref, t.confirm.cancel),
|
|
65
|
+
`<noscript><p class="error">${(0, app_page_shell_js_1.escapeHtml)(t.confirm.noscript)}</p></noscript>`,
|
|
61
66
|
].join(''),
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
/** A confirmation that is gone (expired, used, or opened in another browser): a sentence, nothing runs. */
|
|
65
|
-
|
|
66
|
-
const t = appStrings(lang).confirmExpired;
|
|
67
|
-
return renderAppPage({
|
|
70
|
+
function appConfirmRefusalPage(lang) {
|
|
71
|
+
const t = (0, app_page_strings_js_1.appStrings)(lang).confirmExpired;
|
|
72
|
+
return (0, app_page_shell_js_1.renderAppPage)({
|
|
68
73
|
lang,
|
|
69
74
|
title: t.title,
|
|
70
75
|
heading: t.title,
|
|
71
|
-
body: appParagraph(t.body) + appHint(t.hint),
|
|
76
|
+
body: (0, app_page_shell_js_1.appParagraph)(t.body) + (0, app_page_shell_js_1.appHint)(t.hint),
|
|
72
77
|
});
|
|
73
78
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APP_CONFIRM_PARAM = exports.APP_CONFIRM_PATH = void 0;
|
|
4
|
+
exports.appConfirmCookie = appConfirmCookie;
|
|
5
|
+
exports.registerAppConfirmRoutes = registerAppConfirmRoutes;
|
|
1
6
|
/**
|
|
2
7
|
* `/auth/app/confirm` - the last step of a native app sign-in (D858).
|
|
3
8
|
*
|
|
@@ -8,20 +13,20 @@
|
|
|
8
13
|
* the handoff code and answers `{ location }` at the app's scheme. Nothing is
|
|
9
14
|
* minted without the POST, and the token is useless in any other browser.
|
|
10
15
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
const types_1 = require("@camstack/types");
|
|
17
|
+
const zod_1 = require("zod");
|
|
18
|
+
const app_confirmations_js_1 = require("./app-confirmations.js");
|
|
19
|
+
const app_confirm_page_js_1 = require("./app-confirm-page.js");
|
|
20
|
+
const app_page_strings_js_1 = require("./app-page-strings.js");
|
|
21
|
+
const auth_rate_limit_js_1 = require("./auth-rate-limit.js");
|
|
22
|
+
const sso_finish_deps_js_1 = require("./sso-finish-deps.js");
|
|
23
|
+
exports.APP_CONFIRM_PATH = '/auth/app/confirm';
|
|
19
24
|
/** The query parameter carrying the token (masked in logged URLs). */
|
|
20
|
-
|
|
25
|
+
exports.APP_CONFIRM_PARAM = 'confirm';
|
|
21
26
|
/** `Set-Cookie` for the browser binding: HttpOnly, Lax (the entry may come from a cross-site redirect chain). */
|
|
22
|
-
|
|
27
|
+
function appConfirmCookie(value, secure) {
|
|
23
28
|
return [
|
|
24
|
-
`${APP_CONFIRM_COOKIE}=${value}`,
|
|
29
|
+
`${app_confirmations_js_1.APP_CONFIRM_COOKIE}=${value}`,
|
|
25
30
|
'Path=/',
|
|
26
31
|
'HttpOnly',
|
|
27
32
|
'SameSite=Lax',
|
|
@@ -29,7 +34,7 @@ export function appConfirmCookie(value, secure) {
|
|
|
29
34
|
...(secure ? ['Secure'] : []),
|
|
30
35
|
].join('; ');
|
|
31
36
|
}
|
|
32
|
-
const ConfirmBodySchema = z.object({ confirm: z.string().min(1).max(256) });
|
|
37
|
+
const ConfirmBodySchema = zod_1.z.object({ confirm: zod_1.z.string().min(1).max(256) });
|
|
33
38
|
function noStore(reply) {
|
|
34
39
|
reply.header('Cache-Control', 'no-store');
|
|
35
40
|
reply.header('Referrer-Policy', 'no-referrer');
|
|
@@ -43,11 +48,11 @@ function mint(deps, confirmation) {
|
|
|
43
48
|
state: app.state,
|
|
44
49
|
authMethod: confirmation.authMethod,
|
|
45
50
|
});
|
|
46
|
-
return appFlowLocation(app.redirect, { code: minted.code }, app.state);
|
|
51
|
+
return (0, types_1.appFlowLocation)(app.redirect, { code: minted.code }, app.state);
|
|
47
52
|
}
|
|
48
|
-
|
|
53
|
+
function registerAppConfirmRoutes(fastify, deps) {
|
|
49
54
|
const limited = (request, reply) => {
|
|
50
|
-
const client = clientKeyFromRequest(request, deps.network);
|
|
55
|
+
const client = (0, auth_rate_limit_js_1.clientKeyFromRequest)(request, deps.network);
|
|
51
56
|
if (client === null)
|
|
52
57
|
return false;
|
|
53
58
|
const verdict = deps.limiter.check(`app-confirm:${client}`);
|
|
@@ -57,43 +62,43 @@ export function registerAppConfirmRoutes(fastify, deps) {
|
|
|
57
62
|
void reply.status(429).send({ error: 'Too many attempts' });
|
|
58
63
|
return true;
|
|
59
64
|
};
|
|
60
|
-
fastify.get(APP_CONFIRM_PATH, async (request, reply) => {
|
|
65
|
+
fastify.get(exports.APP_CONFIRM_PATH, async (request, reply) => {
|
|
61
66
|
noStore(reply);
|
|
62
67
|
reply.type('text/html; charset=utf-8');
|
|
63
68
|
if (limited(request, reply))
|
|
64
69
|
return reply;
|
|
65
|
-
const token = request.query[APP_CONFIRM_PARAM];
|
|
70
|
+
const token = request.query[exports.APP_CONFIRM_PARAM];
|
|
66
71
|
const confirmation = token === undefined
|
|
67
72
|
? null
|
|
68
|
-
: deps.confirmations.peek(token, readCookie(request.headers.cookie, APP_CONFIRM_COOKIE));
|
|
73
|
+
: deps.confirmations.peek(token, (0, sso_finish_deps_js_1.readCookie)(request.headers.cookie, app_confirmations_js_1.APP_CONFIRM_COOKIE));
|
|
69
74
|
if (token === undefined || confirmation === null) {
|
|
70
75
|
deps.logger.warn('App sign-in confirmation page refused', {
|
|
71
76
|
meta: { reason: 'unknown-expired-or-other-browser' },
|
|
72
77
|
});
|
|
73
|
-
reply.header('Content-Security-Policy', APP_CONFIRM_REFUSAL_CSP);
|
|
78
|
+
reply.header('Content-Security-Policy', app_confirm_page_js_1.APP_CONFIRM_REFUSAL_CSP);
|
|
74
79
|
return reply
|
|
75
80
|
.status(400)
|
|
76
|
-
.send(appConfirmRefusalPage(resolveAppPageLang({ acceptLanguage: request.headers['accept-language'] })));
|
|
81
|
+
.send((0, app_confirm_page_js_1.appConfirmRefusalPage)((0, app_page_strings_js_1.resolveAppPageLang)({ acceptLanguage: request.headers['accept-language'] })));
|
|
77
82
|
}
|
|
78
|
-
reply.header('Content-Security-Policy', APP_CONFIRM_CSP);
|
|
79
|
-
return reply.send(appConfirmPage({
|
|
80
|
-
lang: resolveAppPageLang({
|
|
83
|
+
reply.header('Content-Security-Policy', app_confirm_page_js_1.APP_CONFIRM_CSP);
|
|
84
|
+
return reply.send((0, app_confirm_page_js_1.appConfirmPage)({
|
|
85
|
+
lang: (0, app_page_strings_js_1.resolveAppPageLang)({
|
|
81
86
|
lang: confirmation.app.lang,
|
|
82
87
|
acceptLanguage: request.headers['accept-language'],
|
|
83
88
|
}),
|
|
84
89
|
username: confirmation.username,
|
|
85
90
|
token,
|
|
86
|
-
cancelHref: appFlowLocation(confirmation.app.redirect, { error: 'cancelled' }, confirmation.app.state),
|
|
91
|
+
cancelHref: (0, types_1.appFlowLocation)(confirmation.app.redirect, { error: 'cancelled' }, confirmation.app.state),
|
|
87
92
|
}));
|
|
88
93
|
});
|
|
89
|
-
fastify.post(APP_CONFIRM_PATH, async (request, reply) => {
|
|
94
|
+
fastify.post(exports.APP_CONFIRM_PATH, async (request, reply) => {
|
|
90
95
|
noStore(reply);
|
|
91
96
|
if (limited(request, reply))
|
|
92
97
|
return reply;
|
|
93
98
|
const body = ConfirmBodySchema.safeParse(request.body);
|
|
94
99
|
if (!body.success)
|
|
95
100
|
return reply.status(400).send({ error: 'Malformed confirmation.' });
|
|
96
|
-
const confirmation = deps.confirmations.consume(body.data.confirm, readCookie(request.headers.cookie, APP_CONFIRM_COOKIE));
|
|
101
|
+
const confirmation = deps.confirmations.consume(body.data.confirm, (0, sso_finish_deps_js_1.readCookie)(request.headers.cookie, app_confirmations_js_1.APP_CONFIRM_COOKIE));
|
|
97
102
|
if (confirmation === null) {
|
|
98
103
|
deps.logger.warn('App sign-in confirmation refused', {
|
|
99
104
|
meta: { reason: 'unknown-used-expired-or-other-browser' },
|
|
@@ -1,4 +1,40 @@
|
|
|
1
|
-
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AppConfirmations = exports.APP_CONFIRM_COOKIE = exports.APP_CONFIRM_TTL_MS = void 0;
|
|
37
|
+
const types_1 = require("@camstack/types");
|
|
2
38
|
/**
|
|
3
39
|
* Pending "Sign in the CamStack app as <user>?" confirmations (D858).
|
|
4
40
|
*
|
|
@@ -16,18 +52,18 @@ import { userError } from '@camstack/types';
|
|
|
16
52
|
*
|
|
17
53
|
* In-memory only: a hub restart simply means the person signs in again.
|
|
18
54
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
55
|
+
const crypto = __importStar(require("node:crypto"));
|
|
56
|
+
exports.APP_CONFIRM_TTL_MS = 120_000;
|
|
21
57
|
const MAX_PENDING = 500;
|
|
22
58
|
/** The cookie that binds a confirmation to the browser that finished the ceremony. */
|
|
23
|
-
|
|
59
|
+
exports.APP_CONFIRM_COOKIE = 'camstack_app_confirm';
|
|
24
60
|
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
|
|
25
61
|
function sameDigest(a, b) {
|
|
26
62
|
const left = Buffer.from(a, 'utf8');
|
|
27
63
|
const right = Buffer.from(b, 'utf8');
|
|
28
64
|
return left.length === right.length && crypto.timingSafeEqual(left, right);
|
|
29
65
|
}
|
|
30
|
-
|
|
66
|
+
class AppConfirmations {
|
|
31
67
|
now;
|
|
32
68
|
/** Keyed by SHA-256(token). */
|
|
33
69
|
pending = new Map();
|
|
@@ -38,13 +74,13 @@ export class AppConfirmations {
|
|
|
38
74
|
create(confirmation) {
|
|
39
75
|
this.prune();
|
|
40
76
|
if (this.pending.size >= MAX_PENDING)
|
|
41
|
-
throw userError('server.app-confirmations-busy', undefined, 'Too many pending app confirmations');
|
|
77
|
+
throw (0, types_1.userError)('server.app-confirmations-busy', undefined, 'Too many pending app confirmations');
|
|
42
78
|
const token = crypto.randomBytes(32).toString('base64url');
|
|
43
79
|
const cookie = crypto.randomBytes(32).toString('base64url');
|
|
44
80
|
this.pending.set(sha256(token), {
|
|
45
81
|
...confirmation,
|
|
46
82
|
cookieHash: sha256(cookie),
|
|
47
|
-
expiresAt: this.now() + APP_CONFIRM_TTL_MS,
|
|
83
|
+
expiresAt: this.now() + exports.APP_CONFIRM_TTL_MS,
|
|
48
84
|
});
|
|
49
85
|
return { token, cookie };
|
|
50
86
|
}
|
|
@@ -90,3 +126,4 @@ export class AppConfirmations {
|
|
|
90
126
|
this.pending.delete(key);
|
|
91
127
|
}
|
|
92
128
|
}
|
|
129
|
+
exports.AppConfirmations = AppConfirmations;
|