@camstack/server 1.2.486 → 1.2.488
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/agent/agent-mesh-join.js +121 -0
- package/dist/agent/agent-service.js +4 -0
- package/dist/agent/main.js +62 -2
- package/dist/api/auth-whoami.js +1 -1
- package/dist/api/core/auth-admin-tokens.procedures.js +138 -0
- package/dist/api/core/auth-limits.js +77 -0
- package/dist/api/core/auth-reauth.procedures.js +108 -0
- package/dist/api/core/auth-sessions.procedures.js +166 -0
- package/dist/api/core/auth.router.js +281 -80
- package/dist/api/health/adaptive-probe.routes.js +1 -1
- package/dist/api/mesh/enroll.routes.js +28 -0
- package/dist/api/trpc/core-cap-bridge.js +88 -5
- package/dist/api/trpc/generated-cap-mounts.js +19 -1
- package/dist/api/trpc/generated-cap-routers.js +512 -205
- package/dist/api/trpc/trpc.context.js +86 -34
- package/dist/api/trpc/trpc.middleware.js +8 -11
- package/dist/api/trpc/trpc.router.js +6 -1
- package/dist/auth/auth-network-policy.js +75 -0
- package/dist/auth/auth-rate-limit.js +25 -20
- package/dist/auth/client-address.js +68 -0
- package/dist/auth/external-link-intents.js +12 -3
- package/dist/auth/hub-sessions.js +74 -0
- package/dist/auth/native-link-completions.js +68 -0
- package/dist/auth/native-return.js +33 -0
- package/dist/auth/session-lifetimes.js +46 -0
- package/dist/auth/session-mint.js +110 -13
- package/dist/auth/sso-finish-deps.js +56 -1
- package/dist/auth/sso-finish.js +132 -17
- package/dist/auth/sso-routes.js +95 -0
- package/dist/auth/used-bridge-ids.js +52 -0
- package/dist/core/addon/addon-registry.service.js +5 -0
- package/dist/core/agent/agent-registry.service.js +23 -0
- package/dist/core/auth/auth.service.js +50 -1
- package/dist/core/auth/handoff-code.service.js +9 -3
- package/dist/core/moleculer/hub-mesh-guard.js +132 -0
- package/dist/core/moleculer/moleculer.service.js +46 -0
- package/dist/main.js +97 -29
- package/package.json +13 -13
package/dist/main.js
CHANGED
|
@@ -59,6 +59,7 @@ const ws_1 = require("@trpc/server/adapters/ws");
|
|
|
59
59
|
const ws_2 = require("ws");
|
|
60
60
|
const addon_upload_1 = require("./api/addon-upload");
|
|
61
61
|
const auth_whoami_1 = require("./api/auth-whoami");
|
|
62
|
+
const enroll_routes_js_1 = require("./api/mesh/enroll.routes.js");
|
|
62
63
|
const backup_download_js_1 = require("./api/backup-download.js");
|
|
63
64
|
const cap_providers_1 = require("./api/core/cap-providers");
|
|
64
65
|
const logging_settings_1 = require("./api/core/logging-settings");
|
|
@@ -86,12 +87,17 @@ const ws_request_census_1 = require("./api/trpc/ws-request-census");
|
|
|
86
87
|
const ws_slow_consumer_guard_js_1 = require("./api/trpc/ws-slow-consumer-guard.js");
|
|
87
88
|
const addon_route_jwt_gate_js_1 = require("./auth/addon-route-jwt-gate.js");
|
|
88
89
|
const addon_route_share_gate_js_1 = require("./auth/addon-route-share-gate.js");
|
|
90
|
+
const auth_network_policy_js_1 = require("./auth/auth-network-policy.js");
|
|
91
|
+
const hub_sessions_js_1 = require("./auth/hub-sessions.js");
|
|
92
|
+
const session_lifetimes_js_1 = require("./auth/session-lifetimes.js");
|
|
89
93
|
const auth_rate_limit_1 = require("./auth/auth-rate-limit");
|
|
90
94
|
const external_link_intents_js_1 = require("./auth/external-link-intents.js");
|
|
91
95
|
const session_cookie_js_1 = require("./auth/session-cookie.js");
|
|
92
96
|
const session_mint_js_1 = require("./auth/session-mint.js");
|
|
93
|
-
const sso_finish_js_1 = require("./auth/sso-finish.js");
|
|
94
97
|
const sso_finish_deps_js_1 = require("./auth/sso-finish-deps.js");
|
|
98
|
+
const sso_routes_js_1 = require("./auth/sso-routes.js");
|
|
99
|
+
const used_bridge_ids_js_1 = require("./auth/used-bridge-ids.js");
|
|
100
|
+
const native_link_completions_js_1 = require("./auth/native-link-completions.js");
|
|
95
101
|
const boot_config_1 = require("./boot/boot-config");
|
|
96
102
|
const platform_keychain_key_1 = require("./boot/platform-keychain-key");
|
|
97
103
|
const integration_id_backfill_1 = require("./boot/integration-id-backfill");
|
|
@@ -410,6 +416,16 @@ async function bootstrap() {
|
|
|
410
416
|
storageService.setLocationManager(infra.locationManager);
|
|
411
417
|
// ConfigService — SettingsStore is wired later by the settings-store capability consumer
|
|
412
418
|
const config = app.get(config_service_1.ConfigService);
|
|
419
|
+
// The real client address behind a trusted proxy, and the opt-in trusted
|
|
420
|
+
// networks — read from settings per request (auth-network-policy.ts).
|
|
421
|
+
const authNetwork = (0, auth_network_policy_js_1.createAuthNetworkPolicy)((key) => config.get(key));
|
|
422
|
+
// Public addon routes that declare `rateLimit: 'auth'` (an IdP start /
|
|
423
|
+
// callback) and the SSO finish: 30 per minute per real client address.
|
|
424
|
+
const authRouteLimiter = (0, auth_rate_limit_1.createRateLimiter)({ windowMs: 60_000, max: 30 });
|
|
425
|
+
// Login sessions (local-auth's table, reached through its hub-internal
|
|
426
|
+
// surface): every session token minted from here on carries a `sid`, and
|
|
427
|
+
// every verify of one asks whether that session is still live.
|
|
428
|
+
const sessionLifetimes = () => (0, session_lifetimes_js_1.readSessionLifetimes)((key) => config.get(key));
|
|
413
429
|
// Register addon upload route (multipart — must be registered before tRPC).
|
|
414
430
|
// We pass AddonRegistryService so the handler can resolve the
|
|
415
431
|
// `user-management` cap singleton at request time (the only working
|
|
@@ -442,6 +458,7 @@ async function bootstrap() {
|
|
|
442
458
|
// Mirrors the addon-upload auth chain so the CLI can ping for
|
|
443
459
|
// token-still-valid without consuming a real cap.
|
|
444
460
|
await (0, auth_whoami_1.registerAuthWhoamiRoute)(fastify, uploadAuthService, uploadAddonRegistry);
|
|
461
|
+
(0, enroll_routes_js_1.registerMeshEnrollRoute)(fastify);
|
|
445
462
|
console.log('[bootstrap] Auth whoami route registered at GET /api/auth/whoami');
|
|
446
463
|
}
|
|
447
464
|
catch (err) {
|
|
@@ -498,6 +515,11 @@ async function bootstrap() {
|
|
|
498
515
|
addonRegistry.setDataPlaneRegistry(dataPlaneRegistry);
|
|
499
516
|
// ── Configure the CapabilityRegistry (created in AddonRegistryService constructor) ──
|
|
500
517
|
const capabilityRegistry = addonRegistry.getCapabilityRegistry();
|
|
518
|
+
const sessionBacking = (0, hub_sessions_js_1.sessionBackingFrom)(capabilityRegistry, sessionLifetimes);
|
|
519
|
+
// From here on every verify of a session-grade token — tRPC, addon routes,
|
|
520
|
+
// the session cookie, OAuth authorize, uploads, health — requires its
|
|
521
|
+
// session to be live (`hub-sessions.ts`).
|
|
522
|
+
app.get(auth_service_1.AuthService).setSessionCheck((0, hub_sessions_js_1.createSessionTokenCheck)(capabilityRegistry));
|
|
501
523
|
// Declare every shipped capability BEFORE app.init() so addon
|
|
502
524
|
// registerProvider() calls (in-process or via the Moleculer bridge
|
|
503
525
|
// from forked workers / agents) find their definition during
|
|
@@ -507,7 +529,7 @@ async function bootstrap() {
|
|
|
507
529
|
capabilityRegistry.declareCapability(capDef);
|
|
508
530
|
}
|
|
509
531
|
// Hub-internal `sso-bridge` provider — gives auth-provider addons
|
|
510
|
-
// (OIDC, SAML,
|
|
532
|
+
// (OIDC, SAML, …) a typed gateway to mint an HMAC-signed
|
|
511
533
|
// bridge token before redirecting to `/api/auth/sso/finish`. Without
|
|
512
534
|
// this, the finish endpoint would have to trust unsigned query params
|
|
513
535
|
// (anyone could craft `?isAdmin=1`). Backed by AuthService.
|
|
@@ -538,12 +560,41 @@ async function bootstrap() {
|
|
|
538
560
|
// Pending "link this sign-in account" intents (D782) — minted by
|
|
539
561
|
// `auth.beginExternalLink`, consumed once by `/api/auth/sso/finish`.
|
|
540
562
|
const externalLinkIntents = new external_link_intents_js_1.ExternalLinkIntents();
|
|
563
|
+
// Native-app links parked for the app to claim, and one-time codes that
|
|
564
|
+
// open the account page signed in, in a browser (D801).
|
|
565
|
+
const nativeLinkCompletions = new native_link_completions_js_1.NativeLinkCompletions();
|
|
566
|
+
// `jti`s of login bridges already finished — a bridge is single-use.
|
|
567
|
+
const usedBridgeIds = new used_bridge_ids_js_1.UsedBridgeIds();
|
|
568
|
+
const browserHandoffCodes = new handoff_code_service_1.HandoffCodeService();
|
|
569
|
+
const nativeLinkMinter = (0, session_mint_js_1.createSessionMinter)({
|
|
570
|
+
signer: app.get(auth_service_1.AuthService),
|
|
571
|
+
directory: () => capabilityRegistry.getSingleton('user-management'),
|
|
572
|
+
sessions: sessionBacking,
|
|
573
|
+
});
|
|
574
|
+
const nativeLinkWiring = () => ({
|
|
575
|
+
auth: app.get(auth_service_1.AuthService),
|
|
576
|
+
registry: capabilityRegistry,
|
|
577
|
+
moleculer: app.get(moleculer_service_1.MoleculerService),
|
|
578
|
+
minter: nativeLinkMinter,
|
|
579
|
+
linkIntents: externalLinkIntents,
|
|
580
|
+
nativeLinks: nativeLinkCompletions,
|
|
581
|
+
usedBridgeIds,
|
|
582
|
+
});
|
|
541
583
|
const buildRouter = () => (0, trpc_router_1.buildAppRouter)({
|
|
542
584
|
authService: app.get(auth_service_1.AuthService),
|
|
543
585
|
shareTokenService,
|
|
544
586
|
handoffCodeService,
|
|
545
587
|
externalLinkIntents,
|
|
588
|
+
nativeLink: {
|
|
589
|
+
completions: nativeLinkCompletions,
|
|
590
|
+
browserHandoffs: browserHandoffCodes,
|
|
591
|
+
// The SSO finish's own link step — the same conflict rule, the same
|
|
592
|
+
// store — run for the app's claim instead of the browser's return.
|
|
593
|
+
link: (input) => (0, sso_finish_deps_js_1.createSsoFinishDeps)(nativeLinkWiring(), undefined).identities.link(input),
|
|
594
|
+
},
|
|
546
595
|
configService: config,
|
|
596
|
+
authNetworkPolicy: authNetwork,
|
|
597
|
+
sessionBacking,
|
|
547
598
|
featureService: app.get(feature_service_1.FeatureService),
|
|
548
599
|
loggingService,
|
|
549
600
|
eventBus: app.get(event_bus_service_1.EventBusService),
|
|
@@ -582,7 +633,11 @@ async function bootstrap() {
|
|
|
582
633
|
beforeAddonBoot: () => {
|
|
583
634
|
try {
|
|
584
635
|
appRouter = buildRouter();
|
|
585
|
-
|
|
636
|
+
{
|
|
637
|
+
const meshBroker = app.get(moleculer_service_1.MoleculerService);
|
|
638
|
+
// D802: another node's call runs as `node:<id>`, never the hub's own principal.
|
|
639
|
+
meshBroker.registerCoreCapService((0, core_cap_bridge_1.buildCoreCapService)(appRouter, undefined, { ownNodeId: meshBroker.nodeId }));
|
|
640
|
+
}
|
|
586
641
|
console.log('[bootstrap] core-cap mesh bridge registered ($core-caps) — before addon boot');
|
|
587
642
|
}
|
|
588
643
|
catch (err) {
|
|
@@ -629,6 +684,7 @@ async function bootstrap() {
|
|
|
629
684
|
(0, adaptive_probe_routes_1.registerAdaptiveProbeRoute)(fastify, {
|
|
630
685
|
verifyToken: (token) => app.get(auth_service_1.AuthService).verifyToken(token),
|
|
631
686
|
limiter: (0, auth_rate_limit_1.createRateLimiter)({ windowMs: 60_000, max: adaptive_probe_routes_1.ADAPTIVE_PROBE_PER_IP_PER_MINUTE }),
|
|
687
|
+
network: authNetwork,
|
|
632
688
|
});
|
|
633
689
|
}
|
|
634
690
|
catch (err) {
|
|
@@ -661,7 +717,9 @@ async function bootstrap() {
|
|
|
661
717
|
// an operator armed earlier. The logger is attached now so a restored census
|
|
662
718
|
// has somewhere to report before the first request arrives.
|
|
663
719
|
const httpRequestCensus = (0, http_request_census_singleton_1.getHttpRequestCensus)();
|
|
664
|
-
|
|
720
|
+
const trpcLogger = loggingService.createLogger('tRPC');
|
|
721
|
+
httpRequestCensus.setLogger(trpcLogger);
|
|
722
|
+
(0, core_cap_bridge_1.setRemotePrivilegeLogSink)(trpcLogger);
|
|
665
723
|
try {
|
|
666
724
|
const authService = app.get(auth_service_1.AuthService);
|
|
667
725
|
// Normally already built and bridged onto the mesh in `beforeAddonBoot`
|
|
@@ -670,7 +728,11 @@ async function bootstrap() {
|
|
|
670
728
|
if (appRouter === null) {
|
|
671
729
|
appRouter = buildRouter();
|
|
672
730
|
try {
|
|
673
|
-
|
|
731
|
+
{
|
|
732
|
+
const meshBroker = app.get(moleculer_service_1.MoleculerService);
|
|
733
|
+
// D802: another node's call runs as `node:<id>`, never the hub's own principal.
|
|
734
|
+
meshBroker.registerCoreCapService((0, core_cap_bridge_1.buildCoreCapService)(appRouter, undefined, { ownNodeId: meshBroker.nodeId }));
|
|
735
|
+
}
|
|
674
736
|
console.log('[bootstrap] core-cap mesh bridge registered ($core-caps) — late fallback');
|
|
675
737
|
}
|
|
676
738
|
catch (err) {
|
|
@@ -820,37 +882,31 @@ async function bootstrap() {
|
|
|
820
882
|
reply.header('cache-control', 'public, max-age=86400');
|
|
821
883
|
return reply.send(fs.readFileSync(filePath));
|
|
822
884
|
});
|
|
823
|
-
// ── SSO finish
|
|
824
|
-
// OIDC
|
|
825
|
-
//
|
|
826
|
-
//
|
|
827
|
-
// `auth/sso-finish.ts
|
|
828
|
-
const ssoAuth = app.get(auth_service_1.AuthService);
|
|
829
|
-
const ssoLogger = app.get(logging_service_1.LoggingService).createLogger('sso-finish');
|
|
885
|
+
// ── SSO finish + browser handoff ────────────────────────────────
|
|
886
|
+
// OIDC redirects to the finish with an HMAC-signed, single-use LOGIN
|
|
887
|
+
// bridge bound to the browser that started it; the session is minted by
|
|
888
|
+
// THE session mint from the user record as it is now (D712). Rules in
|
|
889
|
+
// `auth/sso-finish.ts`, routes in `auth/sso-routes.ts`.
|
|
830
890
|
const ssoMinter = (0, session_mint_js_1.createSessionMinter)({
|
|
831
|
-
signer:
|
|
891
|
+
signer: app.get(auth_service_1.AuthService),
|
|
832
892
|
directory: () => capabilityRegistry.getSingleton('user-management'),
|
|
893
|
+
sessions: sessionBacking,
|
|
833
894
|
});
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
auth:
|
|
895
|
+
(0, sso_routes_js_1.registerSsoRoutes)(fastify, {
|
|
896
|
+
wiring: {
|
|
897
|
+
auth: app.get(auth_service_1.AuthService),
|
|
837
898
|
registry: capabilityRegistry,
|
|
838
899
|
moleculer: app.get(moleculer_service_1.MoleculerService),
|
|
839
900
|
minter: ssoMinter,
|
|
840
901
|
linkIntents: externalLinkIntents,
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
reply.code(302);
|
|
850
|
-
// Admin-ui paths are relative to the hub's mount (HA ingress etc.).
|
|
851
|
-
const ssoMount = (0, spa_mount_1.parseForwardedPrefix)(request.headers['x-forwarded-prefix']) ?? '';
|
|
852
|
-
reply.header('Location', `${ssoMount}${outcome.location}`);
|
|
853
|
-
return reply.send('');
|
|
902
|
+
nativeLinks: nativeLinkCompletions,
|
|
903
|
+
usedBridgeIds,
|
|
904
|
+
},
|
|
905
|
+
network: authNetwork,
|
|
906
|
+
limiter: authRouteLimiter,
|
|
907
|
+
browserHandoffCodes,
|
|
908
|
+
minter: ssoMinter,
|
|
909
|
+
logger: app.get(logging_service_1.LoggingService).createLogger('sso-finish'),
|
|
854
910
|
});
|
|
855
911
|
// ── Backup archive download ─────────────────────────────────────
|
|
856
912
|
// Streams an archive through the storage cap's chunked-download
|
|
@@ -1052,6 +1108,18 @@ async function bootstrap() {
|
|
|
1052
1108
|
}
|
|
1053
1109
|
return reply.status(404).send({ error: 'Not found' });
|
|
1054
1110
|
}
|
|
1111
|
+
// A route that asked for it (an IdP start / callback) is limited per
|
|
1112
|
+
// REAL client address before its handler runs.
|
|
1113
|
+
if (match.route.rateLimit === 'auth') {
|
|
1114
|
+
const routeClient = (0, auth_rate_limit_1.clientKeyFromRequest)(request, authNetwork);
|
|
1115
|
+
if (routeClient !== null) {
|
|
1116
|
+
const verdict = authRouteLimiter.check(`route:${addonId}:${routeClient}`);
|
|
1117
|
+
if (!verdict.allowed) {
|
|
1118
|
+
reply.header('retry-after', String(verdict.retryAfterSeconds));
|
|
1119
|
+
return reply.status(429).send({ error: 'Too many attempts' });
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1055
1123
|
// Auth check based on route.access
|
|
1056
1124
|
if (match.route.access !== 'public') {
|
|
1057
1125
|
const authHeader = request.headers.authorization;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.488",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
]
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
41
|
-
"@camstack/addon-agent-ui": "1.2.
|
|
42
|
-
"@camstack/addon-auth": "1.2.
|
|
43
|
-
"@camstack/addon-decoder-nodeav": "1.2.
|
|
44
|
-
"@camstack/addon-notifiers": "1.2.
|
|
45
|
-
"@camstack/addon-pipeline": "1.2.
|
|
46
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
47
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
48
|
-
"@camstack/sdk": "1.2.
|
|
49
|
-
"@camstack/system": "1.2.
|
|
50
|
-
"@camstack/types": "1.2.
|
|
51
|
-
"@camstack/ui-library": "1.2.
|
|
40
|
+
"@camstack/addon-admin-ui": "1.2.361",
|
|
41
|
+
"@camstack/addon-agent-ui": "1.2.196",
|
|
42
|
+
"@camstack/addon-auth": "1.2.135",
|
|
43
|
+
"@camstack/addon-decoder-nodeav": "1.2.122",
|
|
44
|
+
"@camstack/addon-notifiers": "1.2.129",
|
|
45
|
+
"@camstack/addon-pipeline": "1.2.347",
|
|
46
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.300",
|
|
47
|
+
"@camstack/addon-post-analysis": "1.2.348",
|
|
48
|
+
"@camstack/sdk": "1.2.128",
|
|
49
|
+
"@camstack/system": "1.2.383",
|
|
50
|
+
"@camstack/types": "1.2.333",
|
|
51
|
+
"@camstack/ui-library": "1.2.254",
|
|
52
52
|
"@fastify/compress": "^9.0.0",
|
|
53
53
|
"@fastify/cookie": "^11.0.2",
|
|
54
54
|
"@fastify/cors": "^11.2.0",
|