@camstack/server 1.2.621 → 1.2.622

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.
@@ -232,7 +232,10 @@ function createTokenProcedures(deps) {
232
232
  assertLifetime(record.scopes, input.expiresIn);
233
233
  const moved = await service.setExpiry(input.id, expiryOf(input.expiresIn));
234
234
  if (moved === null) {
235
- throw new server_1.TRPCError({ code: 'NOT_FOUND', message: `Token expired or revoked: ${input.id}` });
235
+ throw new server_1.TRPCError({
236
+ code: 'NOT_FOUND',
237
+ message: `Token expired or revoked: ${input.id}`,
238
+ });
236
239
  }
237
240
  return summarizeToken(moved);
238
241
  }),
@@ -34,7 +34,9 @@ function mountAllCaps(services) {
34
34
  // to materialise a Moleculer-bridged proxy. The factory return
35
35
  // type is parametric — the runtime Proxy is structurally
36
36
  // compatible with every `InferProvider<typeof cap>` shape so a
37
- // single generic helper covers every mount.
37
+ // single generic helper covers every mount. This is the one type
38
+ // boundary: a single assertion to the unconstrained `T`, never a
39
+ // double cast through `unknown`.
38
40
  const remoteCapProxy = (capName, nodeId) => services.moleculer.createCapabilityProxy(capName, nodeId);
39
41
  return {
40
42
  accessories: (0, generated_cap_routers_js_1.createCapRouter_accessories)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'accessories'), remoteCapProxy),
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logPersonWithoutViewer = logPersonWithoutViewer;
4
+ exports.resetPersonWithoutViewerLog = resetPersonWithoutViewerLog;
5
+ const logged = new Set();
6
+ function logPersonWithoutViewer(logger, userId) {
7
+ if (logger == null || logged.has(userId))
8
+ return;
9
+ logged.add(userId);
10
+ logger.warn('auth: a person with no viewer grant was refused', { tags: { userId } });
11
+ }
12
+ /** Test seam — the set is process-wide. */
13
+ function resetPersonWithoutViewerLog() {
14
+ logged.clear();
15
+ }
@@ -29,6 +29,7 @@ exports.scopedEventVisibility = scopedEventVisibility;
29
29
  */
30
30
  const system_1 = require("@camstack/system");
31
31
  const types_1 = require("@camstack/types");
32
+ const person_session_scopes_js_1 = require("../../auth/person-session-scopes.js");
32
33
  const scope_access_js_1 = require("./scope-access.js");
33
34
  const share_view_access_js_1 = require("./share-view-access.js");
34
35
  /**
@@ -141,6 +142,12 @@ function projectListAllForPrincipal(data, isAdmin, scopes, fleet) {
141
142
  const viewable = (0, types_1.resolveViewableDeviceIds)(scopes, fleet, 'view');
142
143
  return (0, share_view_access_js_1.projectListAllForDeviceIds)(data, viewable);
143
144
  }
145
+ /** Grant rows a principal is decided by. A person contributes only its viewer row. */
146
+ function decisionScopes(user) {
147
+ const raw = user.scopes ?? [];
148
+ const scopes = user.interactive === true ? (0, person_session_scopes_js_1.personSessionScopes)(raw) : raw;
149
+ return (0, types_1.capabilityScopesOf)(scopes);
150
+ }
144
151
  /**
145
152
  * Build the event-stream predicate for a NON-share principal (F0.5).
146
153
  *
@@ -156,7 +163,7 @@ function projectListAllForPrincipal(data, isAdmin, scopes, fleet) {
156
163
  function scopedEventVisibility(user, lookup) {
157
164
  if (user.isAdmin)
158
165
  return null;
159
- const scopes = (0, types_1.capabilityScopesOf)(user.scopes ?? []);
166
+ const scopes = decisionScopes(user);
160
167
  if (isBroadEventViewer(scopes))
161
168
  return null;
162
169
  return (evt) => {
@@ -171,6 +171,7 @@ async function createTrpcContext(req, authService, addonRegistry, logger = null,
171
171
  lang: (0, request_lang_js_1.resolveRequestLang)((0, request_lang_js_1.requestLangInputFrom)(req.headers, readQuery(req))),
172
172
  deviceScopeLookup,
173
173
  deviceFleet: () => addonRegistry.getPersistedDeviceList(),
174
+ logger,
174
175
  };
175
176
  }
176
177
  /**
@@ -210,5 +211,6 @@ async function createWsTrpcContext(opts, authService, addonRegistry, logger = nu
210
211
  : {}),
211
212
  deviceScopeLookup,
212
213
  deviceFleet: () => addonRegistry.getPersistedDeviceList(),
214
+ logger,
213
215
  };
214
216
  }
@@ -8,6 +8,8 @@ exports.iterableSubscription = iterableSubscription;
8
8
  exports.iterableInterval = iterableInterval;
9
9
  const system_1 = require("@camstack/system");
10
10
  const types_1 = require("@camstack/types");
11
+ const person_session_scopes_js_1 = require("../../auth/person-session-scopes.js");
12
+ const person_without_viewer_js_1 = require("./person-without-viewer.js");
11
13
  const server_1 = require("@trpc/server");
12
14
  const superjson_1 = __importDefault(require("superjson"));
13
15
  const cap_route_error_formatter_js_1 = require("./cap-route-error-formatter.js");
@@ -118,8 +120,18 @@ exports.protectedProcedure = localized.use(async ({ ctx, next, path, getRawInput
118
120
  throw (0, user_error_js_1.trpcUserError)('UNAUTHORIZED', 'auth.session-ended', undefined, 'Session ended — sign in again');
119
121
  }
120
122
  const user = ctx.user;
121
- const verdict = (0, principal_authorization_js_1.authorizeProcedure)(user.scopes ?? [], path, await getRawInput(), ctx.deviceScopeLookup);
123
+ const rawScopes = user.scopes ?? [];
124
+ // A person is an admin or a viewer (D921). Grant rows on the user record
125
+ // are not a role: the session is decided by its viewer row alone, and a
126
+ // person with none reaches no labelled method. Tokens keep their rows.
127
+ const scopes = user.interactive === true && !user.isAdmin ? (0, person_session_scopes_js_1.personSessionScopes)(rawScopes) : rawScopes;
128
+ const verdict = (0, principal_authorization_js_1.authorizeProcedure)(scopes, path, await getRawInput(), ctx.deviceScopeLookup);
122
129
  if (!verdict.ok) {
130
+ if (user.interactive === true &&
131
+ !user.isAdmin &&
132
+ (0, types_1.viewerScopeOf)((0, types_1.capabilityScopesOf)(rawScopes)) === null) {
133
+ (0, person_without_viewer_js_1.logPersonWithoutViewer)(ctx.logger, user.id);
134
+ }
123
135
  throw new server_1.TRPCError({ code: 'FORBIDDEN', message: verdict.reason });
124
136
  }
125
137
  // Spread+reassign of `user` narrows downstream ctx from `User | null` to
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCredentialVerifier = createCredentialVerifier;
4
4
  const types_1 = require("@camstack/types");
5
5
  const trpc_error_principal_js_1 = require("../api/trpc/trpc-error-principal.js");
6
+ const person_session_scopes_js_1 = require("./person-session-scopes.js");
6
7
  const scope_escalation_js_1 = require("./scope-escalation.js");
7
8
  function stringClaim(value) {
8
9
  return typeof value === 'string' ? value : undefined;
@@ -217,7 +218,9 @@ function createCredentialVerifier(deps) {
217
218
  const capability = fresh ?? (0, types_1.normalizeTokenScopes)(payload.scopes ?? []);
218
219
  return {
219
220
  ...base,
220
- scopes: [...capability, SESSION_ADDON_ROUTES, types_1.PREFERENCES_SELF_SCOPE],
221
+ // The stored rows may still be a pre-viewer grant set. A person is
222
+ // decided by the viewer row alone; the rest is not a role.
223
+ scopes: (0, person_session_scopes_js_1.personSessionScopes)([...capability, SESSION_ADDON_ROUTES, types_1.PREFERENCES_SELF_SCOPE]),
221
224
  isAdmin: false,
222
225
  };
223
226
  };
@@ -25,9 +25,7 @@ async function sessionCookieTtl(deps, token) {
25
25
  function registerEmbedAuthRoute(fastify, deps) {
26
26
  fastify.get('/api/embed-auth', async (request, reply) => {
27
27
  const authHeader = request.headers.authorization;
28
- const token = authHeader?.startsWith('Bearer ')
29
- ? authHeader.slice('Bearer '.length)
30
- : undefined;
28
+ const token = authHeader?.startsWith('Bearer ') ? authHeader.slice('Bearer '.length) : undefined;
31
29
  if (!token)
32
30
  return reply.status(401).send({ error: 'Bearer token required' });
33
31
  const next = request.query?.next ?? '';
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.personSessionScopes = personSessionScopes;
4
+ /**
5
+ * A person's session (D921, amended 2026-10-24) is decided by its viewer row
6
+ * alone. Grant rows (`device`, `category`, `capability`, `addon`) are the
7
+ * vocabulary of tokens and account links; they never reach the matcher for a
8
+ * person. A person with no viewer row keeps only the session extras (addon
9
+ * routes, own preferences) and is refused every labelled method.
10
+ */
11
+ const types_1 = require("@camstack/types");
12
+ /** A session extra a person keeps. `'*'` is not one: only an admin session carries it. */
13
+ function isSessionExtra(scope) {
14
+ return (scope !== '*' &&
15
+ (scope.type === 'share-view' ||
16
+ scope.type === 'addon-routes' ||
17
+ scope.type === 'preferences-self'));
18
+ }
19
+ /** Drop every grant row but the viewer row, if the set holds one. */
20
+ function personSessionScopes(scopes) {
21
+ const viewer = (0, types_1.viewerScopeOf)((0, types_1.capabilityScopesOf)(scopes));
22
+ const extras = scopes.filter(isSessionExtra);
23
+ return viewer === null ? extras : [viewer, ...extras];
24
+ }
@@ -23,7 +23,10 @@
23
23
  "package": "moleculer",
24
24
  "version": "0.15.0",
25
25
  "reach": "bundled",
26
- "bundledInto": ["packages/system/dist/index.js", "packages/system/dist/index.mjs"],
26
+ "bundledInto": [
27
+ "packages/system/dist/builtins/system-config/system-config.addon.js",
28
+ "packages/system/dist/builtins/system-config/system-config.addon.mjs"
29
+ ],
27
30
  "markers": ["rest.buffer.byteLength"],
28
31
  "why": [
29
32
  "`moleculer` is NOT in the vite preset's externals, so rolldown inlines",
@@ -44,7 +47,12 @@
44
47
  "The marker is `rest.buffer.byteLength`, NOT the `pinsTooMuch` identifier",
45
48
  "the patch spells: rolldown inlines a single-use const, so `pinsTooMuch`",
46
49
  "is absent from every bundle including the correct ones. A marker that a",
47
- "bundler can erase cannot answer this question."
50
+ "bundler can erase cannot answer this question.",
51
+ "",
52
+ "The artifact is wherever rolldown puts the shared moleculer chunk, not",
53
+ "necessarily `index`: today it lands in the",
54
+ "`builtins/system-config/system-config.addon` entry (re-checked",
55
+ "2026-10-24 on a fresh build: the only TCP parser in `dist/`, patched)."
48
56
  ]
49
57
  },
50
58
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/server",
3
- "version": "1.2.621",
3
+ "version": "1.2.622",
4
4
  "license": "MIT",
5
5
  "private": false,
6
6
  "files": [
@@ -38,20 +38,20 @@
38
38
  ]
39
39
  },
40
40
  "dependencies": {
41
- "@camstack/addon-admin-ui": "1.2.458",
42
- "@camstack/addon-agent-ui": "1.2.262",
41
+ "@camstack/addon-admin-ui": "1.2.459",
42
+ "@camstack/addon-agent-ui": "1.2.263",
43
43
  "@camstack/addon-auth": "1.2.166",
44
- "@camstack/addon-benchmark": "1.2.262",
44
+ "@camstack/addon-benchmark": "1.2.263",
45
45
  "@camstack/addon-decoder-nodeav": "1.2.141",
46
46
  "@camstack/addon-notifiers": "1.2.619",
47
- "@camstack/addon-pipeline": "1.2.622",
48
- "@camstack/addon-pipeline-orchestrator": "1.2.623",
49
- "@camstack/addon-post-analysis": "1.2.628",
47
+ "@camstack/addon-pipeline": "1.2.623",
48
+ "@camstack/addon-pipeline-orchestrator": "1.2.624",
49
+ "@camstack/addon-post-analysis": "1.2.629",
50
50
  "@camstack/develop": "0.1.31",
51
51
  "@camstack/sdk": "1.2.151",
52
- "@camstack/system": "1.2.472",
53
- "@camstack/types": "1.2.404",
54
- "@camstack/ui-library": "1.2.319",
52
+ "@camstack/system": "1.2.473",
53
+ "@camstack/types": "1.2.405",
54
+ "@camstack/ui-library": "1.2.320",
55
55
  "@fastify/compress": "^9.0.0",
56
56
  "@fastify/cookie": "^11.0.2",
57
57
  "@fastify/cors": "^11.2.0",