@matter/protocol 0.16.0-alpha.0-20250902-38a7cc156 → 0.16.0-alpha.0-20250909-aecad94f3
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/cjs/action/Interactable.d.ts +2 -2
- package/dist/cjs/action/Interactable.d.ts.map +1 -1
- package/dist/cjs/action/server/AccessControl.d.ts +43 -15
- package/dist/cjs/action/server/AccessControl.d.ts.map +1 -1
- package/dist/cjs/action/server/AccessControl.js +47 -36
- package/dist/cjs/action/server/AccessControl.js.map +1 -1
- package/dist/cjs/action/server/AttributeReadResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeReadResponse.js +24 -22
- package/dist/cjs/action/server/AttributeReadResponse.js.map +1 -1
- package/dist/cjs/action/server/AttributeWriteResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/AttributeWriteResponse.js +38 -26
- package/dist/cjs/action/server/AttributeWriteResponse.js.map +1 -1
- package/dist/cjs/action/server/CommandInvokeResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/CommandInvokeResponse.js +28 -19
- package/dist/cjs/action/server/CommandInvokeResponse.js.map +1 -1
- package/dist/cjs/action/server/EventReadResponse.d.ts.map +1 -1
- package/dist/cjs/action/server/EventReadResponse.js +22 -20
- package/dist/cjs/action/server/EventReadResponse.js.map +1 -1
- package/dist/cjs/fabric/Fabric.d.ts +1 -1
- package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
- package/dist/cjs/fabric/Fabric.js +4 -4
- package/dist/cjs/fabric/Fabric.js.map +1 -1
- package/dist/cjs/interaction/FabricAccessControl.d.ts +2 -2
- package/dist/cjs/interaction/FabricAccessControl.d.ts.map +1 -1
- package/dist/cjs/interaction/FabricAccessControl.js +0 -6
- package/dist/cjs/interaction/FabricAccessControl.js.map +1 -1
- package/dist/esm/action/Interactable.d.ts +2 -2
- package/dist/esm/action/Interactable.d.ts.map +1 -1
- package/dist/esm/action/server/AccessControl.d.ts +43 -15
- package/dist/esm/action/server/AccessControl.d.ts.map +1 -1
- package/dist/esm/action/server/AccessControl.js +48 -37
- package/dist/esm/action/server/AccessControl.js.map +1 -1
- package/dist/esm/action/server/AttributeReadResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeReadResponse.js +25 -23
- package/dist/esm/action/server/AttributeReadResponse.js.map +1 -1
- package/dist/esm/action/server/AttributeWriteResponse.d.ts.map +1 -1
- package/dist/esm/action/server/AttributeWriteResponse.js +39 -27
- package/dist/esm/action/server/AttributeWriteResponse.js.map +1 -1
- package/dist/esm/action/server/CommandInvokeResponse.d.ts.map +1 -1
- package/dist/esm/action/server/CommandInvokeResponse.js +29 -20
- package/dist/esm/action/server/CommandInvokeResponse.js.map +1 -1
- package/dist/esm/action/server/EventReadResponse.d.ts.map +1 -1
- package/dist/esm/action/server/EventReadResponse.js +23 -21
- package/dist/esm/action/server/EventReadResponse.js.map +1 -1
- package/dist/esm/fabric/Fabric.d.ts +1 -1
- package/dist/esm/fabric/Fabric.d.ts.map +1 -1
- package/dist/esm/fabric/Fabric.js +4 -4
- package/dist/esm/fabric/Fabric.js.map +1 -1
- package/dist/esm/interaction/FabricAccessControl.d.ts +2 -2
- package/dist/esm/interaction/FabricAccessControl.d.ts.map +1 -1
- package/dist/esm/interaction/FabricAccessControl.js +0 -6
- package/dist/esm/interaction/FabricAccessControl.js.map +1 -1
- package/package.json +6 -6
- package/src/action/Interactable.ts +2 -2
- package/src/action/server/AccessControl.ts +90 -53
- package/src/action/server/AttributeReadResponse.ts +35 -29
- package/src/action/server/AttributeWriteResponse.ts +50 -38
- package/src/action/server/CommandInvokeResponse.ts +33 -24
- package/src/action/server/EventReadResponse.ts +25 -21
- package/src/fabric/Fabric.ts +4 -4
- package/src/interaction/FabricAccessControl.ts +2 -8
|
@@ -4,13 +4,43 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { ImplementationError } from "#general";
|
|
7
8
|
import { Access, AccessLevel, DataModelPath, ElementTag, Schema, ValueModel } from "#model";
|
|
8
|
-
import { ClusterId, EndpointNumber, FabricIndex,
|
|
9
|
+
import { ClusterId, EndpointNumber, FabricIndex, Status } from "#types";
|
|
9
10
|
import { InvokeError, ReadError, SchemaImplementationError, WriteError } from "../errors.js";
|
|
10
11
|
import { Subject } from "./Subject.js";
|
|
11
12
|
|
|
12
13
|
const cache = new WeakMap<Schema, AccessControl>();
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Confirm that an access control session (or some variante thereof) is a {@link AccessControl.RemoteActorSession}.
|
|
17
|
+
*/
|
|
18
|
+
export function hasRemoteActor<T extends undefined | AccessControl.Session>(
|
|
19
|
+
session: T,
|
|
20
|
+
): session is Exclude<T, undefined | { subject?: undefined }> {
|
|
21
|
+
return session?.subject !== undefined;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Throws if a session is not a {@link AccessControl.RemoteActorSession}.
|
|
26
|
+
*/
|
|
27
|
+
export function assertRemoteActor<T extends undefined | AccessControl.Session>(
|
|
28
|
+
session: T,
|
|
29
|
+
): asserts session is Exclude<T, undefined | { subject?: undefined }> {
|
|
30
|
+
if (!hasRemoteActor(session)) {
|
|
31
|
+
throw new ImplementationError("This operation requires an authenticated remote session");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Confirm that an access control session (or some variante thereof) is a {@link AccessControl.LocalActorSession}.
|
|
37
|
+
*/
|
|
38
|
+
export function hasLocalActor<T extends undefined | AccessControl.Session>(
|
|
39
|
+
session: T,
|
|
40
|
+
): session is Exclude<T, { subject: Subject }> {
|
|
41
|
+
return session?.subject === undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
14
44
|
/**
|
|
15
45
|
* Enforces access control for a specific schema.
|
|
16
46
|
*/
|
|
@@ -86,13 +116,17 @@ export namespace AccessControl {
|
|
|
86
116
|
|
|
87
117
|
/**
|
|
88
118
|
* A function that asserts access control requirements are met.
|
|
119
|
+
*
|
|
120
|
+
* If {@link session} is undefined the function does not enforce access controls.
|
|
89
121
|
*/
|
|
90
|
-
export type Assertion = (session: Session, location: Location) => void;
|
|
122
|
+
export type Assertion = (session: Session | undefined, location: Location) => void;
|
|
91
123
|
|
|
92
124
|
/**
|
|
93
125
|
* A function that returns true if access control requirements are met.
|
|
126
|
+
*
|
|
127
|
+
* If {@link session} is undefined the function does not enforce access controls.
|
|
94
128
|
*/
|
|
95
|
-
export type Verification = (session: Session, location: Location) => boolean;
|
|
129
|
+
export type Verification = (session: Session | undefined, location: Location) => boolean;
|
|
96
130
|
|
|
97
131
|
/**
|
|
98
132
|
* Metadata that varies with position in the data model.
|
|
@@ -121,9 +155,9 @@ export namespace AccessControl {
|
|
|
121
155
|
}
|
|
122
156
|
|
|
123
157
|
/**
|
|
124
|
-
* Authorization metadata that varies
|
|
158
|
+
* Authorization metadata that varies by remote actor.
|
|
125
159
|
*/
|
|
126
|
-
export interface
|
|
160
|
+
export interface RemoteActorSession {
|
|
127
161
|
/**
|
|
128
162
|
* Determine whether authorized client has authority at a specific location.
|
|
129
163
|
*/
|
|
@@ -131,14 +165,16 @@ export namespace AccessControl {
|
|
|
131
165
|
|
|
132
166
|
/**
|
|
133
167
|
* The fabric of the authorized client.
|
|
168
|
+
*
|
|
169
|
+
* For PASE sessions this will be {@link FabricIndex.NO_FABRIC}.
|
|
134
170
|
*/
|
|
135
|
-
readonly fabric
|
|
171
|
+
readonly fabric: FabricIndex;
|
|
136
172
|
|
|
137
173
|
/**
|
|
138
|
-
* The authenticated
|
|
139
|
-
*
|
|
174
|
+
* The authenticated remote actor. This includes the relevant Node Id, Group ID and also potential relevant Case
|
|
175
|
+
* Authenticated Tags.
|
|
140
176
|
*/
|
|
141
|
-
readonly subject
|
|
177
|
+
readonly subject: Subject;
|
|
142
178
|
|
|
143
179
|
/**
|
|
144
180
|
* If this is true, fabric-scoped lists are filtered to the accessing fabric.
|
|
@@ -155,16 +191,21 @@ export namespace AccessControl {
|
|
|
155
191
|
* active.
|
|
156
192
|
*/
|
|
157
193
|
readonly command?: boolean;
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* If this is true then access levels are not enforced and all values are read/write. Datatypes are still
|
|
161
|
-
* enforced.
|
|
162
|
-
*
|
|
163
|
-
* Tracks "offline" rather than "online" because this makes the safer mode (full enforcement) the default.
|
|
164
|
-
*/
|
|
165
|
-
offline?: boolean;
|
|
166
194
|
}
|
|
167
195
|
|
|
196
|
+
/**
|
|
197
|
+
* A local actor session has no authenticated subject and access controls are bypassed.
|
|
198
|
+
*/
|
|
199
|
+
export type LocalActorSession = {
|
|
200
|
+
fabric?: undefined;
|
|
201
|
+
subject?: undefined;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The accessing session.
|
|
206
|
+
*/
|
|
207
|
+
export type Session = LocalActorSession | RemoteActorSession;
|
|
208
|
+
|
|
168
209
|
/**
|
|
169
210
|
* Authority status.
|
|
170
211
|
*/
|
|
@@ -200,7 +241,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
200
241
|
const limits = limitsFor(schema);
|
|
201
242
|
|
|
202
243
|
let mayRead: AccessControl.Verification = (session, location) => {
|
|
203
|
-
if (session
|
|
244
|
+
if (hasLocalActor(session) || session.command) {
|
|
204
245
|
return true;
|
|
205
246
|
}
|
|
206
247
|
|
|
@@ -208,7 +249,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
208
249
|
};
|
|
209
250
|
|
|
210
251
|
let mayWrite: AccessControl.Verification = (session, location) => {
|
|
211
|
-
if (session
|
|
252
|
+
if (hasLocalActor(session) || session.command) {
|
|
212
253
|
return true;
|
|
213
254
|
}
|
|
214
255
|
|
|
@@ -216,7 +257,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
216
257
|
};
|
|
217
258
|
|
|
218
259
|
let authorizeRead: AccessControl.Assertion = (session, location) => {
|
|
219
|
-
if (session
|
|
260
|
+
if (hasLocalActor(session) || session.command) {
|
|
220
261
|
return;
|
|
221
262
|
}
|
|
222
263
|
|
|
@@ -224,11 +265,11 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
224
265
|
return;
|
|
225
266
|
}
|
|
226
267
|
|
|
227
|
-
throw new ReadError(location, "Permission denied",
|
|
268
|
+
throw new ReadError(location, "Permission denied", Status.UnsupportedAccess);
|
|
228
269
|
};
|
|
229
270
|
|
|
230
271
|
let authorizeWrite: AccessControl.Assertion = (session, location) => {
|
|
231
|
-
if (session
|
|
272
|
+
if (hasLocalActor(session) || session.command) {
|
|
232
273
|
return;
|
|
233
274
|
}
|
|
234
275
|
|
|
@@ -236,7 +277,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
236
277
|
return;
|
|
237
278
|
}
|
|
238
279
|
|
|
239
|
-
throw new WriteError(location, "Permission denied",
|
|
280
|
+
throw new WriteError(location, "Permission denied", Status.UnsupportedAccess);
|
|
240
281
|
};
|
|
241
282
|
|
|
242
283
|
if (limits.timed) {
|
|
@@ -244,18 +285,18 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
244
285
|
const wrappedMayWrite = mayWrite;
|
|
245
286
|
|
|
246
287
|
authorizeWrite = (session, location) => {
|
|
247
|
-
if (
|
|
288
|
+
if (hasRemoteActor(session) && !session.timed) {
|
|
248
289
|
throw new WriteError(
|
|
249
290
|
location,
|
|
250
291
|
"Permission denied because interaction is not timed",
|
|
251
|
-
|
|
292
|
+
Status.NeedsTimedInteraction,
|
|
252
293
|
);
|
|
253
294
|
}
|
|
254
295
|
wrappedAuthorizeWrite?.(session, location);
|
|
255
296
|
};
|
|
256
297
|
|
|
257
298
|
mayWrite = (session, location) => {
|
|
258
|
-
if (
|
|
299
|
+
if (hasRemoteActor(session) && !session.timed) {
|
|
259
300
|
return false;
|
|
260
301
|
}
|
|
261
302
|
|
|
@@ -270,24 +311,20 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
270
311
|
const wrappedMayWrite = mayWrite;
|
|
271
312
|
|
|
272
313
|
authorizeRead = (session, location) => {
|
|
273
|
-
if (session
|
|
314
|
+
if (hasLocalActor(session) || session.command) {
|
|
274
315
|
return;
|
|
275
316
|
}
|
|
276
317
|
|
|
277
318
|
if (session.fabricFiltered) {
|
|
278
|
-
if (session.fabric
|
|
279
|
-
throw new ReadError(
|
|
280
|
-
location,
|
|
281
|
-
"Permission denied: No accessing fabric",
|
|
282
|
-
StatusCode.UnsupportedAccess,
|
|
283
|
-
);
|
|
319
|
+
if (!session.fabric) {
|
|
320
|
+
throw new ReadError(location, "Permission denied: No accessing fabric", Status.UnsupportedAccess);
|
|
284
321
|
}
|
|
285
322
|
|
|
286
323
|
if (location?.owningFabric !== undefined && location.owningFabric !== session.fabric) {
|
|
287
324
|
throw new ReadError(
|
|
288
325
|
location,
|
|
289
326
|
"Permission denied: Owning/accessing fabric mismatch",
|
|
290
|
-
|
|
327
|
+
Status.UnsupportedAccess,
|
|
291
328
|
);
|
|
292
329
|
}
|
|
293
330
|
}
|
|
@@ -296,11 +333,11 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
296
333
|
};
|
|
297
334
|
|
|
298
335
|
mayRead = (session, location) => {
|
|
299
|
-
if (session
|
|
336
|
+
if (hasLocalActor(session) || session.command) {
|
|
300
337
|
return true;
|
|
301
338
|
}
|
|
302
339
|
|
|
303
|
-
if (session.fabric
|
|
340
|
+
if (!session.fabric) {
|
|
304
341
|
return false;
|
|
305
342
|
}
|
|
306
343
|
|
|
@@ -312,12 +349,12 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
312
349
|
};
|
|
313
350
|
|
|
314
351
|
authorizeWrite = (session, location) => {
|
|
315
|
-
if (session
|
|
352
|
+
if (hasLocalActor(session) || session.command) {
|
|
316
353
|
return;
|
|
317
354
|
}
|
|
318
355
|
|
|
319
|
-
if (session.fabric
|
|
320
|
-
throw new WriteError(location, "Permission denied: No accessing fabric",
|
|
356
|
+
if (!session.fabric) {
|
|
357
|
+
throw new WriteError(location, "Permission denied: No accessing fabric", Status.UnsupportedAccess);
|
|
321
358
|
}
|
|
322
359
|
|
|
323
360
|
if (location?.owningFabric !== undefined && location.owningFabric !== session.fabric) {
|
|
@@ -328,11 +365,11 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
328
365
|
};
|
|
329
366
|
|
|
330
367
|
mayWrite = (session, location) => {
|
|
331
|
-
if (session
|
|
368
|
+
if (hasLocalActor(session) || session.command) {
|
|
332
369
|
return true;
|
|
333
370
|
}
|
|
334
371
|
|
|
335
|
-
if (session.fabric
|
|
372
|
+
if (!session.fabric) {
|
|
336
373
|
return false;
|
|
337
374
|
}
|
|
338
375
|
|
|
@@ -346,7 +383,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
346
383
|
|
|
347
384
|
if (!limits.readable) {
|
|
348
385
|
authorizeRead = (session, location) => {
|
|
349
|
-
if (session
|
|
386
|
+
if (hasLocalActor(session) || session.command) {
|
|
350
387
|
return;
|
|
351
388
|
}
|
|
352
389
|
|
|
@@ -354,20 +391,20 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
354
391
|
};
|
|
355
392
|
|
|
356
393
|
mayRead = session => {
|
|
357
|
-
return
|
|
394
|
+
return hasLocalActor(session) || !!session.command;
|
|
358
395
|
};
|
|
359
396
|
}
|
|
360
397
|
|
|
361
398
|
if (!limits.writable) {
|
|
362
399
|
authorizeWrite = (session, location) => {
|
|
363
|
-
if (session
|
|
400
|
+
if (hasLocalActor(session) || session.command) {
|
|
364
401
|
return;
|
|
365
402
|
}
|
|
366
403
|
throw new WriteError(location, "Permission denied: Value is read-only");
|
|
367
404
|
};
|
|
368
405
|
|
|
369
406
|
mayWrite = session => {
|
|
370
|
-
return
|
|
407
|
+
return hasLocalActor(session) || !!session.command;
|
|
371
408
|
};
|
|
372
409
|
}
|
|
373
410
|
|
|
@@ -378,7 +415,7 @@ function dataEnforcerFor(schema: Schema): AccessControl {
|
|
|
378
415
|
authorizeWrite,
|
|
379
416
|
mayWrite,
|
|
380
417
|
|
|
381
|
-
authorizeInvoke(_session: AccessControl.Session, location: AccessControl.Location) {
|
|
418
|
+
authorizeInvoke(_session: undefined | AccessControl.Session, location: AccessControl.Location) {
|
|
382
419
|
throw new SchemaImplementationError(location, "Permission denied: Invoke request but non-command schema");
|
|
383
420
|
},
|
|
384
421
|
|
|
@@ -413,7 +450,7 @@ function commandEnforcerFor(schema: Schema): AccessControl {
|
|
|
413
450
|
},
|
|
414
451
|
|
|
415
452
|
authorizeInvoke(session, location) {
|
|
416
|
-
if (session
|
|
453
|
+
if (hasLocalActor(session)) {
|
|
417
454
|
return;
|
|
418
455
|
}
|
|
419
456
|
|
|
@@ -425,23 +462,23 @@ function commandEnforcerFor(schema: Schema): AccessControl {
|
|
|
425
462
|
throw new InvokeError(
|
|
426
463
|
location,
|
|
427
464
|
"Invoke attempt without required timed context",
|
|
428
|
-
|
|
465
|
+
Status.TimedRequestMismatch,
|
|
429
466
|
);
|
|
430
467
|
}
|
|
431
468
|
|
|
432
|
-
if (fabric && session.fabric
|
|
433
|
-
throw new WriteError(location, "Permission denied: No accessing fabric",
|
|
469
|
+
if (fabric && !session.fabric) {
|
|
470
|
+
throw new WriteError(location, "Permission denied: No accessing fabric", Status.UnsupportedAccess);
|
|
434
471
|
}
|
|
435
472
|
|
|
436
473
|
if (session.authorityAt(limits.writeLevel, location) === AccessControl.Authority.Granted) {
|
|
437
474
|
return;
|
|
438
475
|
}
|
|
439
476
|
|
|
440
|
-
throw new InvokeError(location, "Permission denied",
|
|
477
|
+
throw new InvokeError(location, "Permission denied", Status.UnsupportedAccess);
|
|
441
478
|
},
|
|
442
479
|
|
|
443
480
|
mayInvoke(session, location) {
|
|
444
|
-
if (session
|
|
481
|
+
if (hasLocalActor(session)) {
|
|
445
482
|
return true;
|
|
446
483
|
}
|
|
447
484
|
|
|
@@ -453,7 +490,7 @@ function commandEnforcerFor(schema: Schema): AccessControl {
|
|
|
453
490
|
return false;
|
|
454
491
|
}
|
|
455
492
|
|
|
456
|
-
if (fabric && session.fabric
|
|
493
|
+
if (fabric && !session.fabric) {
|
|
457
494
|
return false;
|
|
458
495
|
}
|
|
459
496
|
|
|
@@ -8,7 +8,7 @@ import { InteractionSession } from "#action/Interactable.js";
|
|
|
8
8
|
import { AttributeTypeProtocol, ClusterProtocol, EndpointProtocol, NodeProtocol } from "#action/protocols.js";
|
|
9
9
|
import { Read } from "#action/request/Read.js";
|
|
10
10
|
import { ReadResult } from "#action/response/ReadResult.js";
|
|
11
|
-
import { AccessControl } from "#action/server/AccessControl.js";
|
|
11
|
+
import { AccessControl, hasLocalActor, hasRemoteActor } from "#action/server/AccessControl.js";
|
|
12
12
|
import { DataResponse, FallbackLimits, WildcardPathFlagsCodec } from "#action/server/DataResponse.js";
|
|
13
13
|
import { Val } from "#action/Val.js";
|
|
14
14
|
import { Diagnostic, InternalError, Logger } from "#general";
|
|
@@ -64,7 +64,7 @@ export class AttributeReadResponse<
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
*process({ dataVersionFilters, attributeRequests }: Read.Attributes): Generator<ReadResult.Chunk, void, void> {
|
|
67
|
-
const nodeId = this.session
|
|
67
|
+
const nodeId = hasLocalActor(this.session) ? NodeId.UNSPECIFIED_NODE_ID : this.nodeId;
|
|
68
68
|
|
|
69
69
|
// Index versions
|
|
70
70
|
if (dataVersionFilters?.length) {
|
|
@@ -204,32 +204,37 @@ export class AttributeReadResponse<
|
|
|
204
204
|
limits = attribute.limits;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
207
|
+
if (hasRemoteActor(this.session)) {
|
|
208
|
+
// Validate access. Order here prescribed by 1.4 core spec 8.4.3.2
|
|
209
|
+
// We need some fallback location if cluster is not defined
|
|
210
|
+
const location: AccessControl.Location = {
|
|
211
|
+
...(cluster?.location ?? {
|
|
212
|
+
path: DataModelPath.none,
|
|
213
|
+
endpoint: endpointId,
|
|
214
|
+
cluster: clusterId,
|
|
215
|
+
}),
|
|
216
|
+
owningFabric: this.session.fabric,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
const permission = this.session.authorityAt(limits.readLevel, location);
|
|
220
|
+
|
|
221
|
+
switch (permission) {
|
|
222
|
+
case AccessControl.Authority.Granted:
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case AccessControl.Authority.Unauthorized:
|
|
226
|
+
this.addStatus(path, Status.UnsupportedAccess);
|
|
227
|
+
return;
|
|
228
|
+
|
|
229
|
+
case AccessControl.Authority.Restricted:
|
|
230
|
+
this.addStatus(path, Status.AccessRestricted);
|
|
231
|
+
return;
|
|
232
|
+
|
|
233
|
+
default:
|
|
234
|
+
throw new InternalError(`Unsupported authorization state ${permission}`);
|
|
235
|
+
}
|
|
232
236
|
}
|
|
237
|
+
|
|
233
238
|
if (endpoint === undefined) {
|
|
234
239
|
this.addStatus(path, Status.UnsupportedEndpoint);
|
|
235
240
|
return;
|
|
@@ -380,8 +385,9 @@ export class AttributeReadResponse<
|
|
|
380
385
|
|
|
381
386
|
if (
|
|
382
387
|
!attribute.limits.readable ||
|
|
383
|
-
this.session
|
|
384
|
-
|
|
388
|
+
(hasRemoteActor(this.session) &&
|
|
389
|
+
this.session.authorityAt(attribute.limits.readLevel, this.#guardedCurrentCluster.location) !==
|
|
390
|
+
AccessControl.Authority.Granted)
|
|
385
391
|
) {
|
|
386
392
|
return;
|
|
387
393
|
}
|
|
@@ -8,7 +8,7 @@ import { InteractionSession } from "#action/Interactable.js";
|
|
|
8
8
|
import { AttributeTypeProtocol, ClusterProtocol, EndpointProtocol, NodeProtocol } from "#action/protocols.js";
|
|
9
9
|
import { Write } from "#action/request/Write.js";
|
|
10
10
|
import { WriteResult } from "#action/response/WriteResult.js";
|
|
11
|
-
import { AccessControl } from "#action/server/AccessControl.js";
|
|
11
|
+
import { AccessControl, hasRemoteActor } from "#action/server/AccessControl.js";
|
|
12
12
|
import { DataResponse, FallbackLimits } from "#action/server/DataResponse.js";
|
|
13
13
|
import { Diagnostic, InternalError, Logger } from "#general";
|
|
14
14
|
import { AttributeModel, DataModelPath, ElementTag, FabricIndex as FabricIndexField } from "#model";
|
|
@@ -190,30 +190,32 @@ export class AttributeWriteResponse<
|
|
|
190
190
|
limits = attribute.limits;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
193
|
+
if (hasRemoteActor(this.session)) {
|
|
194
|
+
// Validate access. Order here prescribed by 1.4 core spec 8.4.3.2
|
|
195
|
+
// We need some fallback location if cluster is not defined
|
|
196
|
+
const location = {
|
|
197
|
+
...(cluster?.location ?? {
|
|
198
|
+
path: DataModelPath.none,
|
|
199
|
+
endpoint: endpointId,
|
|
200
|
+
cluster: clusterId,
|
|
201
|
+
}),
|
|
202
|
+
owningFabric: this.session.fabric,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const permission = this.session.authorityAt(limits.writeLevel, location);
|
|
206
|
+
switch (permission) {
|
|
207
|
+
case AccessControl.Authority.Granted:
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
case AccessControl.Authority.Unauthorized:
|
|
211
|
+
return this.#asStatus(path, Status.UnsupportedAccess);
|
|
212
|
+
|
|
213
|
+
case AccessControl.Authority.Restricted:
|
|
214
|
+
return this.#asStatus(path, Status.AccessRestricted);
|
|
215
|
+
|
|
216
|
+
default:
|
|
217
|
+
throw new InternalError(`Unsupported authorization state ${permission}`);
|
|
218
|
+
}
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
if (endpoint === undefined) {
|
|
@@ -235,13 +237,15 @@ export class AttributeWriteResponse<
|
|
|
235
237
|
// see https://github.com/project-chip/connectedhomeip/issues/33735
|
|
236
238
|
// We have patched our tests for now
|
|
237
239
|
|
|
238
|
-
if (
|
|
239
|
-
this
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
this
|
|
244
|
-
|
|
240
|
+
if (hasRemoteActor(this.session)) {
|
|
241
|
+
if (limits.timed && !this.session.timed) {
|
|
242
|
+
this.#errorCount++;
|
|
243
|
+
return this.#asStatus(path, Status.NeedsTimedInteraction);
|
|
244
|
+
}
|
|
245
|
+
if (limits.fabricScoped && !this.session.fabric) {
|
|
246
|
+
this.#errorCount++;
|
|
247
|
+
return this.#asStatus(path, Status.UnsupportedAccess);
|
|
248
|
+
}
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
if (version !== undefined && version !== cluster.version) {
|
|
@@ -321,15 +325,23 @@ export class AttributeWriteResponse<
|
|
|
321
325
|
return;
|
|
322
326
|
}
|
|
323
327
|
|
|
324
|
-
if (
|
|
325
|
-
!attribute.limits.writable ||
|
|
326
|
-
this.session.authorityAt(attribute.limits.readLevel, this.#guardedCurrentCluster.location) !==
|
|
327
|
-
AccessControl.Authority.Granted ||
|
|
328
|
-
(attribute.limits.timed && !this.session.timed)
|
|
329
|
-
) {
|
|
328
|
+
if (!attribute.limits.writable) {
|
|
330
329
|
return;
|
|
331
330
|
}
|
|
332
331
|
|
|
332
|
+
if (hasRemoteActor(this.session)) {
|
|
333
|
+
if (
|
|
334
|
+
this.session.authorityAt(attribute.limits.readLevel, this.#guardedCurrentCluster.location) !==
|
|
335
|
+
AccessControl.Authority.Granted
|
|
336
|
+
) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (attribute.limits.timed && !this.session.timed) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
333
345
|
return this.writeValue(
|
|
334
346
|
attribute,
|
|
335
347
|
{
|
|
@@ -8,7 +8,7 @@ import { InteractionSession } from "#action/Interactable.js";
|
|
|
8
8
|
import { CommandInvokeHandler, CommandTypeProtocol, EndpointProtocol, NodeProtocol } from "#action/protocols.js";
|
|
9
9
|
import { Invoke } from "#action/request/Invoke.js";
|
|
10
10
|
import { InvokeResult } from "#action/response/InvokeResult.js";
|
|
11
|
-
import { AccessControl } from "#action/server/AccessControl.js";
|
|
11
|
+
import { AccessControl, hasRemoteActor } from "#action/server/AccessControl.js";
|
|
12
12
|
import { DataResponse, FallbackLimits } from "#action/server/DataResponse.js";
|
|
13
13
|
import { Diagnostic, InternalError, Logger } from "#general";
|
|
14
14
|
import { CommandModel, DataModelPath, ElementTag, FabricIndex as FabricIndexField } from "#model";
|
|
@@ -228,19 +228,21 @@ export class CommandInvokeResponse<
|
|
|
228
228
|
owningFabric: this.session.fabric,
|
|
229
229
|
};
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
231
|
+
if (hasRemoteActor(this.session)) {
|
|
232
|
+
const permission = this.session.authorityAt(limits.writeLevel, location);
|
|
233
|
+
switch (permission) {
|
|
234
|
+
case AccessControl.Authority.Granted:
|
|
235
|
+
break;
|
|
235
236
|
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
case AccessControl.Authority.Unauthorized:
|
|
238
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedAccess);
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
case AccessControl.Authority.Restricted:
|
|
241
|
+
return this.#addStatus(path, commandRef, Status.AccessRestricted);
|
|
241
242
|
|
|
242
|
-
|
|
243
|
-
|
|
243
|
+
default:
|
|
244
|
+
throw new InternalError(`Unsupported authorization state ${permission}`);
|
|
245
|
+
}
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
if (endpoint === undefined) {
|
|
@@ -253,14 +255,16 @@ export class CommandInvokeResponse<
|
|
|
253
255
|
return this.#addStatus(path, commandRef, Status.UnsupportedCommand);
|
|
254
256
|
}
|
|
255
257
|
|
|
256
|
-
if (
|
|
257
|
-
this
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
if (hasRemoteActor(this.session)) {
|
|
259
|
+
if (limits.fabricScoped && !this.session.fabric) {
|
|
260
|
+
this.#errorCount++;
|
|
261
|
+
return this.#addStatus(path, commandRef, Status.UnsupportedAccess);
|
|
262
|
+
}
|
|
260
263
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
if (limits.timed && !this.session.timed) {
|
|
265
|
+
this.#errorCount++;
|
|
266
|
+
return this.#addStatus(path, commandRef, Status.NeedsTimedInteraction);
|
|
267
|
+
}
|
|
264
268
|
}
|
|
265
269
|
|
|
266
270
|
// This path contributes an command value
|
|
@@ -308,12 +312,17 @@ export class CommandInvokeResponse<
|
|
|
308
312
|
|
|
309
313
|
const command = cluster.type.commands[commandId];
|
|
310
314
|
if (command !== undefined) {
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
315
|
+
if (hasRemoteActor(this.session)) {
|
|
316
|
+
if (
|
|
317
|
+
this.session.authorityAt(command.limits.writeLevel, cluster.location) !==
|
|
318
|
+
AccessControl.Authority.Granted
|
|
319
|
+
) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (command.limits.timed && !this.session.timed) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
317
326
|
}
|
|
318
327
|
|
|
319
328
|
await this.#invokeCommand(
|