@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.
- package/dist/api/core/auth-tokens.procedures.js +4 -1
- package/dist/api/trpc/generated-cap-mounts.js +3 -1
- package/dist/api/trpc/person-without-viewer.js +15 -0
- package/dist/api/trpc/principal-visibility.js +8 -1
- package/dist/api/trpc/trpc.context.js +2 -0
- package/dist/api/trpc/trpc.middleware.js +13 -1
- package/dist/auth/credential-verifier.js +4 -1
- package/dist/auth/embed-auth-route.js +1 -3
- package/dist/auth/person-session-scopes.js +24 -0
- package/dist/closure-patches/patch-reach.json +10 -2
- package/package.json +10 -10
|
@@ -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({
|
|
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 = (
|
|
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
|
|
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
|
-
|
|
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": [
|
|
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.
|
|
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.
|
|
42
|
-
"@camstack/addon-agent-ui": "1.2.
|
|
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.
|
|
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.
|
|
48
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
49
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
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.
|
|
53
|
-
"@camstack/types": "1.2.
|
|
54
|
-
"@camstack/ui-library": "1.2.
|
|
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",
|