@enbox/dwn-sdk-js 0.3.7 → 0.3.9
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/browser.mjs +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +2591 -1435
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/constants.js +20 -0
- package/dist/esm/src/core/constants.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +24 -1
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +4 -4
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message.js +89 -4
- package/dist/esm/src/core/message.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +147 -55
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +76 -0
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/records-grant-authorization.js +40 -15
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/handlers/messages-read.js +5 -5
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +109 -7
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/messages-sync.js +341 -96
- package/dist/esm/src/handlers/messages-sync.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +81 -2
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +30 -0
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +3 -2
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +30 -0
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +3 -2
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +31 -0
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +21 -14
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/messages-read.js +6 -3
- package/dist/esm/src/interfaces/messages-read.js.map +1 -1
- package/dist/esm/src/interfaces/messages-subscribe.js +6 -3
- package/dist/esm/src/interfaces/messages-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/messages-sync.js +17 -3
- package/dist/esm/src/interfaces/messages-sync.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +5 -2
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +8 -4
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +5 -0
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +6 -2
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +5 -0
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +6 -3
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +5 -0
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +6 -3
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +28 -7
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/sync/records-projection.js +228 -0
- package/dist/esm/src/sync/records-projection.js.map +1 -0
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/permission-types.js.map +1 -1
- package/dist/esm/src/utils/permission-scope.js +37 -0
- package/dist/esm/src/utils/permission-scope.js.map +1 -0
- package/dist/esm/tests/core/grant-authorization.spec.js +26 -3
- package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +117 -0
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -0
- package/dist/esm/tests/features/permissions.spec.js +126 -0
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +14 -0
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +345 -12
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +326 -9
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +1053 -7
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +361 -0
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +75 -2
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +73 -0
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +75 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +41 -0
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/messages-get.spec.js +107 -5
- package/dist/esm/tests/interfaces/messages-get.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +13 -0
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +12 -0
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-query.spec.js +10 -0
- package/dist/esm/tests/interfaces/records-query.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-subscribe.spec.js +10 -0
- package/dist/esm/tests/interfaces/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +33 -0
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/sync/records-projection.spec.js +245 -0
- package/dist/esm/tests/sync/records-projection.spec.js.map +1 -0
- package/dist/esm/tests/test-suite.js +2 -0
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/permission-scope.spec.js +66 -0
- package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -0
- package/dist/esm/tests/utils/test-data-generator.js +5 -2
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +13 -0
- package/dist/types/src/core/constants.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +24 -1
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +1 -2
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/message.d.ts +41 -1
- package/dist/types/src/core/message.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +36 -4
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +12 -0
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/records-grant-authorization.d.ts +6 -0
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts +2 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-sync.d.ts +31 -0
- package/dist/types/src/handlers/messages-sync.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +3 -0
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +4 -0
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +4 -0
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +6 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-read.d.ts +1 -1
- package/dist/types/src/interfaces/messages-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts +1 -1
- package/dist/types/src/interfaces/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +4 -1
- package/dist/types/src/interfaces/messages-sync.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +1 -0
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +1 -0
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +1 -0
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +2 -0
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/sync/records-projection.d.ts +98 -0
- package/dist/types/src/sync/records-projection.d.ts.map +1 -0
- package/dist/types/src/types/message-types.d.ts +1 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +21 -3
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +4 -0
- package/dist/types/src/types/permission-types.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +4 -0
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/types/subscriptions.d.ts +18 -3
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/utils/permission-scope.d.ts +29 -0
- package/dist/types/src/utils/permission-scope.d.ts.map +1 -0
- package/dist/types/tests/core/records-grant-authorization.spec.d.ts +2 -0
- package/dist/types/tests/core/records-grant-authorization.spec.d.ts.map +1 -0
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/sync/records-projection.spec.d.ts +2 -0
- package/dist/types/tests/sync/records-projection.spec.d.ts.map +1 -0
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/permission-scope.spec.d.ts +2 -0
- package/dist/types/tests/utils/permission-scope.spec.d.ts.map +1 -0
- package/dist/types/tests/utils/test-data-generator.d.ts +5 -2
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/constants.ts +24 -0
- package/src/core/dwn-error.ts +24 -1
- package/src/core/grant-authorization.ts +7 -5
- package/src/core/message.ts +153 -6
- package/src/core/messages-grant-authorization.ts +282 -70
- package/src/core/protocol-authorization.ts +130 -0
- package/src/core/records-grant-authorization.ts +64 -21
- package/src/handlers/messages-read.ts +7 -5
- package/src/handlers/messages-subscribe.ts +149 -9
- package/src/handlers/messages-sync.ts +593 -102
- package/src/handlers/protocols-configure.ts +103 -2
- package/src/handlers/records-count.ts +33 -0
- package/src/handlers/records-delete.ts +3 -2
- package/src/handlers/records-query.ts +33 -0
- package/src/handlers/records-read.ts +3 -2
- package/src/handlers/records-subscribe.ts +34 -0
- package/src/handlers/records-write.ts +21 -15
- package/src/index.ts +7 -3
- package/src/interfaces/messages-read.ts +8 -5
- package/src/interfaces/messages-subscribe.ts +12 -9
- package/src/interfaces/messages-sync.ts +33 -12
- package/src/interfaces/protocols-configure.ts +8 -4
- package/src/interfaces/protocols-query.ts +13 -9
- package/src/interfaces/records-count.ts +7 -0
- package/src/interfaces/records-delete.ts +9 -5
- package/src/interfaces/records-query.ts +7 -0
- package/src/interfaces/records-read.ts +6 -3
- package/src/interfaces/records-subscribe.ts +7 -0
- package/src/interfaces/records-write.ts +25 -17
- package/src/protocols/permissions.ts +47 -9
- package/src/sync/records-projection.ts +328 -0
- package/src/types/message-types.ts +1 -0
- package/src/types/messages-types.ts +23 -3
- package/src/types/permission-types.ts +5 -1
- package/src/types/records-types.ts +5 -1
- package/src/types/subscriptions.ts +19 -3
- package/src/utils/permission-scope.ts +55 -0
|
@@ -3,11 +3,14 @@ import type { MessagesPermissionScope } from '../types/permission-types.js';
|
|
|
3
3
|
import type { MessageStore } from '../types/message-store.js';
|
|
4
4
|
import type { PermissionGrant } from '../protocols/permission-grant.js';
|
|
5
5
|
import type { ProtocolsConfigureMessage } from '../types/protocols-types.js';
|
|
6
|
+
import type { ProtocolScope } from '../utils/permission-scope.js';
|
|
6
7
|
import type { DataEncodedRecordsWriteMessage, RecordsDeleteMessage, RecordsWriteMessage } from '../types/records-types.js';
|
|
7
8
|
import type { MessagesReadMessage, MessagesSubscribeMessage, MessagesSyncMessage } from '../types/messages-types.js';
|
|
8
9
|
|
|
9
10
|
import { DwnInterfaceName } from '../enums/dwn-interface-method.js';
|
|
10
11
|
import { GrantAuthorization } from './grant-authorization.js';
|
|
12
|
+
import { isRecordsPrimaryProjectionExcludedProtocol } from './constants.js';
|
|
13
|
+
import { PermissionScopeMatcher } from '../utils/permission-scope.js';
|
|
11
14
|
import { PermissionsProtocol } from '../protocols/permissions.js';
|
|
12
15
|
import { Records } from '../utils/records.js';
|
|
13
16
|
import { RecordsWrite } from '../interfaces/records-write.js';
|
|
@@ -15,6 +18,16 @@ import { DwnError, DwnErrorCode } from './dwn-error.js';
|
|
|
15
18
|
|
|
16
19
|
export class MessagesGrantAuthorization {
|
|
17
20
|
|
|
21
|
+
public static async fetchPermissionGrants(
|
|
22
|
+
tenant: string,
|
|
23
|
+
messageStore: MessageStore,
|
|
24
|
+
permissionGrantIds: string[]
|
|
25
|
+
): Promise<PermissionGrant[]> {
|
|
26
|
+
return Promise.all(
|
|
27
|
+
permissionGrantIds.map(permissionGrantId => PermissionsProtocol.fetchGrant(tenant, messageStore, permissionGrantId))
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Authorizes a MessagesReadMessage using the given permission grant.
|
|
20
33
|
* @param messageStore Used to check if the given grant has been revoked; and to fetch related RecordsWrites if needed.
|
|
@@ -24,23 +37,29 @@ export class MessagesGrantAuthorization {
|
|
|
24
37
|
messageToRead: GenericMessage,
|
|
25
38
|
expectedGrantor: string,
|
|
26
39
|
expectedGrantee: string,
|
|
27
|
-
|
|
40
|
+
permissionGrants: PermissionGrant[],
|
|
28
41
|
messageStore: MessageStore,
|
|
29
42
|
}): Promise<void> {
|
|
30
43
|
const {
|
|
31
|
-
messagesReadMessage, messageToRead, expectedGrantor, expectedGrantee,
|
|
44
|
+
messagesReadMessage, messageToRead, expectedGrantor, expectedGrantee, permissionGrants, messageStore
|
|
32
45
|
} = input;
|
|
33
46
|
|
|
34
|
-
await
|
|
47
|
+
await MessagesGrantAuthorization.performBaseValidationForGrantSet({
|
|
35
48
|
incomingMessage: messagesReadMessage,
|
|
36
49
|
expectedGrantor,
|
|
37
50
|
expectedGrantee,
|
|
38
|
-
|
|
51
|
+
permissionGrants,
|
|
39
52
|
messageStore
|
|
40
53
|
});
|
|
41
54
|
|
|
42
|
-
const
|
|
43
|
-
|
|
55
|
+
for (const permissionGrant of permissionGrants) {
|
|
56
|
+
const scope = permissionGrant.scope as MessagesPermissionScope;
|
|
57
|
+
if (await MessagesGrantAuthorization.isScopeAuthorized(expectedGrantor, messageToRead, scope, messageStore)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new DwnError(DwnErrorCode.MessagesReadVerifyScopeFailed, 'record message failed scope authorization');
|
|
44
63
|
}
|
|
45
64
|
|
|
46
65
|
/**
|
|
@@ -51,98 +70,291 @@ export class MessagesGrantAuthorization {
|
|
|
51
70
|
incomingMessage: MessagesSubscribeMessage | MessagesSyncMessage,
|
|
52
71
|
expectedGrantor: string,
|
|
53
72
|
expectedGrantee: string,
|
|
54
|
-
|
|
73
|
+
permissionGrants: PermissionGrant[],
|
|
55
74
|
messageStore: MessageStore,
|
|
56
75
|
}): Promise<void> {
|
|
57
76
|
const {
|
|
58
|
-
incomingMessage, expectedGrantor, expectedGrantee,
|
|
77
|
+
incomingMessage, expectedGrantor, expectedGrantee, permissionGrants, messageStore
|
|
59
78
|
} = input;
|
|
60
79
|
|
|
61
|
-
await
|
|
80
|
+
await MessagesGrantAuthorization.performBaseValidationForGrantSet({
|
|
62
81
|
incomingMessage,
|
|
63
82
|
expectedGrantor,
|
|
64
83
|
expectedGrantee,
|
|
65
|
-
|
|
84
|
+
permissionGrants,
|
|
66
85
|
messageStore
|
|
67
86
|
});
|
|
68
87
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
const scopes = permissionGrants.map(permissionGrant => permissionGrant.scope as MessagesPermissionScope);
|
|
89
|
+
|
|
90
|
+
if ('action' in incomingMessage.descriptor) {
|
|
91
|
+
MessagesGrantAuthorization.authorizeSyncScope(incomingMessage as MessagesSyncMessage, scopes);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
MessagesGrantAuthorization.authorizeSubscribeScope(incomingMessage as MessagesSubscribeMessage, scopes);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private static authorizeSyncScope(
|
|
99
|
+
syncMessage: MessagesSyncMessage,
|
|
100
|
+
scopes: MessagesPermissionScope[]
|
|
101
|
+
): void {
|
|
102
|
+
const { projectionScopes, protocol } = syncMessage.descriptor;
|
|
103
|
+
|
|
104
|
+
if (projectionScopes === undefined) {
|
|
105
|
+
MessagesGrantAuthorization.authorizeProtocolSyncScope(scopes, protocol);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
MessagesGrantAuthorization.authorizeProjectionScopes(scopes, projectionScopes);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private static authorizeProtocolSyncScope(
|
|
113
|
+
scopes: MessagesPermissionScope[],
|
|
114
|
+
protocol: string | undefined
|
|
115
|
+
): void {
|
|
116
|
+
if (isRecordsPrimaryProjectionExcludedProtocol(protocol)) {
|
|
117
|
+
throw new DwnError(
|
|
118
|
+
DwnErrorCode.MessagesGrantAuthorizationProtocolSyncInfrastructureProtocol,
|
|
119
|
+
`Protocol-scoped MessagesSync cannot authorize infrastructure protocol ${protocol}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!MessagesGrantAuthorization.someScopeMatches(scopes, { protocol })) {
|
|
124
|
+
throw new DwnError(
|
|
125
|
+
DwnErrorCode.MessagesGrantAuthorizationMismatchedProtocol,
|
|
126
|
+
`No permission grant scope matches protocol ${protocol}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private static authorizeProjectionScopes(
|
|
132
|
+
scopes: MessagesPermissionScope[],
|
|
133
|
+
projectionScopes: ProtocolScope[],
|
|
134
|
+
): void {
|
|
135
|
+
for (const projectionScope of projectionScopes) {
|
|
136
|
+
if (isRecordsPrimaryProjectionExcludedProtocol(projectionScope.protocol)) {
|
|
137
|
+
throw new DwnError(
|
|
138
|
+
DwnErrorCode.MessagesGrantAuthorizationProjectionInfrastructureProtocol,
|
|
139
|
+
`Projected MessagesSync cannot authorize infrastructure protocol ${projectionScope.protocol}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (MessagesGrantAuthorization.someScopeMatches(scopes, projectionScope)) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
throw new DwnError(
|
|
148
|
+
DwnErrorCode.MessagesGrantAuthorizationProjectionScopeMismatch,
|
|
149
|
+
`No permission grant scope matches projection scope ${JSON.stringify(projectionScope)}`
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private static authorizeSubscribeScope(
|
|
155
|
+
subscribeMessage: MessagesSubscribeMessage,
|
|
156
|
+
scopes: MessagesPermissionScope[]
|
|
157
|
+
): void {
|
|
158
|
+
const { filters } = subscribeMessage.descriptor;
|
|
159
|
+
|
|
160
|
+
if (filters.length === 0 && !MessagesGrantAuthorization.hasUnscopedGrant(scopes)) {
|
|
161
|
+
throw new DwnError(
|
|
162
|
+
DwnErrorCode.MessagesGrantAuthorizationUnfilteredSubscribeProtocolScope,
|
|
163
|
+
`A protocol-scoped grant cannot authorize an unfiltered subscription`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const filter of filters) {
|
|
168
|
+
if (MessagesGrantAuthorization.someScopeMatches(scopes, { protocol: filter.protocol })) {
|
|
169
|
+
continue;
|
|
93
170
|
}
|
|
171
|
+
|
|
172
|
+
throw new DwnError(
|
|
173
|
+
DwnErrorCode.MessagesGrantAuthorizationSubscribeProtocolMismatch,
|
|
174
|
+
`No permission grant scope matches protocol ${filter.protocol}`
|
|
175
|
+
);
|
|
94
176
|
}
|
|
95
177
|
}
|
|
96
178
|
|
|
179
|
+
private static someScopeMatches(scopes: MessagesPermissionScope[], target: ProtocolScope): boolean {
|
|
180
|
+
return scopes.some(scope => PermissionScopeMatcher.matches(scope, target));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private static hasUnscopedGrant(scopes: MessagesPermissionScope[]): boolean {
|
|
184
|
+
return scopes.some(scope => scope.protocol === undefined);
|
|
185
|
+
}
|
|
186
|
+
|
|
97
187
|
/**
|
|
98
|
-
*
|
|
188
|
+
* Revalidates an already-open delegated MessagesSubscribe grant set at event
|
|
189
|
+
* delivery time. Subscription messages are signed at open time, but grant
|
|
190
|
+
* expiry and revocation must stop future delivery after the grant set becomes
|
|
191
|
+
* invalid.
|
|
99
192
|
*/
|
|
100
|
-
|
|
193
|
+
public static async authorizeSubscribeDelivery(input: {
|
|
194
|
+
messagesSubscribeMessage: MessagesSubscribeMessage,
|
|
195
|
+
expectedGrantor: string,
|
|
196
|
+
expectedGrantee: string,
|
|
197
|
+
permissionGrants: PermissionGrant[],
|
|
198
|
+
messageStore: MessageStore,
|
|
199
|
+
deliveryTimestamp: string,
|
|
200
|
+
}): Promise<void> {
|
|
201
|
+
const {
|
|
202
|
+
messagesSubscribeMessage,
|
|
203
|
+
expectedGrantor,
|
|
204
|
+
expectedGrantee,
|
|
205
|
+
permissionGrants,
|
|
206
|
+
messageStore,
|
|
207
|
+
deliveryTimestamp,
|
|
208
|
+
} = input;
|
|
209
|
+
|
|
210
|
+
const deliveryMessage: MessagesSubscribeMessage = {
|
|
211
|
+
...messagesSubscribeMessage,
|
|
212
|
+
descriptor: {
|
|
213
|
+
...messagesSubscribeMessage.descriptor,
|
|
214
|
+
messageTimestamp: deliveryTimestamp,
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
await MessagesGrantAuthorization.authorizeSubscribeOrSync({
|
|
219
|
+
incomingMessage: deliveryMessage,
|
|
220
|
+
expectedGrantor,
|
|
221
|
+
expectedGrantee,
|
|
222
|
+
permissionGrants,
|
|
223
|
+
messageStore,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Performs base validation on every invoked grant. The grant set is all-or-nothing:
|
|
229
|
+
* unresolved, revoked, expired, or interface/method-mismatched grants fail the request.
|
|
230
|
+
*/
|
|
231
|
+
private static async performBaseValidationForGrantSet(input: {
|
|
232
|
+
incomingMessage: MessagesReadMessage | MessagesSubscribeMessage | MessagesSyncMessage,
|
|
233
|
+
expectedGrantor: string,
|
|
234
|
+
expectedGrantee: string,
|
|
235
|
+
permissionGrants: PermissionGrant[],
|
|
236
|
+
messageStore: MessageStore,
|
|
237
|
+
}): Promise<void> {
|
|
238
|
+
const {
|
|
239
|
+
incomingMessage, expectedGrantor, expectedGrantee, permissionGrants, messageStore
|
|
240
|
+
} = input;
|
|
241
|
+
|
|
242
|
+
for (const permissionGrant of permissionGrants) {
|
|
243
|
+
await GrantAuthorization.performBaseValidation({
|
|
244
|
+
incomingMessage,
|
|
245
|
+
expectedGrantor,
|
|
246
|
+
expectedGrantee,
|
|
247
|
+
permissionGrant,
|
|
248
|
+
messageStore
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Determines whether the given record is inside a grant scope.
|
|
255
|
+
*/
|
|
256
|
+
private static async isScopeAuthorized(
|
|
101
257
|
tenant: string,
|
|
102
258
|
messageToGet: GenericMessage,
|
|
103
259
|
incomingScope: MessagesPermissionScope,
|
|
104
260
|
messageStore: MessageStore,
|
|
105
|
-
): Promise<
|
|
261
|
+
): Promise<boolean> {
|
|
106
262
|
if (incomingScope.protocol === undefined) {
|
|
107
|
-
|
|
108
|
-
return;
|
|
263
|
+
return true;
|
|
109
264
|
}
|
|
110
265
|
|
|
111
266
|
if (messageToGet.descriptor.interface === DwnInterfaceName.Records) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
267
|
+
return MessagesGrantAuthorization.isRecordsMessageScopeAuthorized(
|
|
268
|
+
tenant,
|
|
269
|
+
messageToGet as RecordsWriteMessage | RecordsDeleteMessage,
|
|
270
|
+
incomingScope,
|
|
271
|
+
messageStore
|
|
272
|
+
);
|
|
273
|
+
}
|
|
116
274
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
275
|
+
if (messageToGet.descriptor.interface === DwnInterfaceName.Protocols) {
|
|
276
|
+
return MessagesGrantAuthorization.isProtocolsConfigureScopeAuthorized(
|
|
277
|
+
messageToGet as ProtocolsConfigureMessage,
|
|
278
|
+
incomingScope
|
|
279
|
+
);
|
|
280
|
+
}
|
|
121
281
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// get the permission scope from the permission message
|
|
125
|
-
const permissionScope = await PermissionsProtocol.getScopeFromPermissionRecord(
|
|
126
|
-
tenant,
|
|
127
|
-
messageStore,
|
|
128
|
-
recordsWriteMessage as DataEncodedRecordsWriteMessage
|
|
129
|
-
);
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
130
284
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
285
|
+
private static async isRecordsMessageScopeAuthorized(
|
|
286
|
+
tenant: string,
|
|
287
|
+
recordsMessage: RecordsWriteMessage | RecordsDeleteMessage,
|
|
288
|
+
incomingScope: MessagesPermissionScope,
|
|
289
|
+
messageStore: MessageStore,
|
|
290
|
+
): Promise<boolean> {
|
|
291
|
+
const recordsWriteMessage = await MessagesGrantAuthorization.getAssociatedRecordsWrite(
|
|
292
|
+
tenant,
|
|
293
|
+
recordsMessage,
|
|
294
|
+
messageStore
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
if (recordsWriteMessage.descriptor.protocol === PermissionsProtocol.uri) {
|
|
298
|
+
return MessagesGrantAuthorization.isPermissionRecordScopeAuthorized(
|
|
299
|
+
tenant,
|
|
300
|
+
recordsWriteMessage,
|
|
301
|
+
incomingScope,
|
|
302
|
+
messageStore
|
|
303
|
+
);
|
|
144
304
|
}
|
|
145
305
|
|
|
146
|
-
|
|
306
|
+
return PermissionScopeMatcher.matches(incomingScope, MessagesGrantAuthorization.getRecordsScopeTarget(recordsWriteMessage));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private static async isPermissionRecordScopeAuthorized(
|
|
310
|
+
tenant: string,
|
|
311
|
+
recordsWriteMessage: RecordsWriteMessage,
|
|
312
|
+
incomingScope: MessagesPermissionScope,
|
|
313
|
+
messageStore: MessageStore,
|
|
314
|
+
): Promise<boolean> {
|
|
315
|
+
if (MessagesGrantAuthorization.isSubtreeScope(incomingScope)) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const permissionScope = await PermissionsProtocol.getScopeFromPermissionRecord(
|
|
320
|
+
tenant,
|
|
321
|
+
messageStore,
|
|
322
|
+
recordsWriteMessage as DataEncodedRecordsWriteMessage
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return PermissionsProtocol.hasProtocolScope(permissionScope)
|
|
326
|
+
&& PermissionScopeMatcher.matches(incomingScope, permissionScope);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private static isProtocolsConfigureScopeAuthorized(
|
|
330
|
+
protocolsConfigureMessage: ProtocolsConfigureMessage,
|
|
331
|
+
incomingScope: MessagesPermissionScope
|
|
332
|
+
): boolean {
|
|
333
|
+
// A delegate with any Messages.Read grant inside a protocol needs that
|
|
334
|
+
// protocol definition to interpret and validate the records it can read.
|
|
335
|
+
return incomingScope.protocol !== undefined &&
|
|
336
|
+
incomingScope.protocol === protocolsConfigureMessage.descriptor.definition.protocol;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private static async getAssociatedRecordsWrite(
|
|
340
|
+
tenant: string,
|
|
341
|
+
recordsMessage: RecordsWriteMessage | RecordsDeleteMessage,
|
|
342
|
+
messageStore: MessageStore,
|
|
343
|
+
): Promise<RecordsWriteMessage> {
|
|
344
|
+
if (Records.isRecordsWrite(recordsMessage)) {
|
|
345
|
+
return recordsMessage;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return RecordsWrite.fetchNewestRecordsWrite(messageStore, tenant, recordsMessage.descriptor.recordId);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private static getRecordsScopeTarget(recordsWriteMessage: RecordsWriteMessage): ProtocolScope {
|
|
352
|
+
const { protocol, protocolPath } = recordsWriteMessage.descriptor;
|
|
353
|
+
const { contextId } = recordsWriteMessage;
|
|
354
|
+
return { protocol, protocolPath, contextId };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private static isSubtreeScope(scope: MessagesPermissionScope): boolean {
|
|
358
|
+
return scope.protocolPath !== undefined || scope.contextId !== undefined;
|
|
147
359
|
}
|
|
148
|
-
}
|
|
360
|
+
}
|
|
@@ -14,6 +14,7 @@ import { getRuleSetAtPath } from '../utils/protocols.js';
|
|
|
14
14
|
import { SortDirection } from '../types/query-types.js';
|
|
15
15
|
import { DwnError, DwnErrorCode } from './dwn-error.js';
|
|
16
16
|
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
17
|
+
import { ProtocolAction, ProtocolActor } from '../types/protocols-types.js';
|
|
17
18
|
|
|
18
19
|
import { authorizeAgainstAllowedActions, verifyInvokedRole } from './protocol-authorization-action.js';
|
|
19
20
|
import { constructRecordChain, fetchInitialWrite, getGoverningTimestamp } from './record-chain.js';
|
|
@@ -113,6 +114,52 @@ export class ProtocolAuthorization {
|
|
|
113
114
|
await verifyRecordLimit(tenant, incomingMessage, ruleSet, messageStore);
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Revalidates a stored initial write against the protocol definition that governed its creation timestamp.
|
|
119
|
+
*
|
|
120
|
+
* This is used only for destructive config-history repair, so it deliberately validates config-owned
|
|
121
|
+
* structure and avoids live dependency checks. Missing grant, role, or parent records must not cause
|
|
122
|
+
* an already-admitted record to be hard-purged.
|
|
123
|
+
*/
|
|
124
|
+
public static async validateStoredInitialWrite(
|
|
125
|
+
tenant: string,
|
|
126
|
+
incomingMessage: RecordsWrite,
|
|
127
|
+
messageStore: MessageStore,
|
|
128
|
+
coreProtocols?: CoreProtocolRegistry,
|
|
129
|
+
): Promise<void> {
|
|
130
|
+
await ProtocolAuthorization.verifyStoredInitialWrite(incomingMessage);
|
|
131
|
+
|
|
132
|
+
const governingTimestamp = incomingMessage.message.descriptor.messageTimestamp;
|
|
133
|
+
const protocolDefinition = await ProtocolAuthorization.fetchProtocolDefinition(
|
|
134
|
+
tenant,
|
|
135
|
+
incomingMessage.message.descriptor.protocol,
|
|
136
|
+
messageStore,
|
|
137
|
+
governingTimestamp,
|
|
138
|
+
coreProtocols,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const boundFetchDefinition = ProtocolAuthorization.createBoundFetchDefinition(coreProtocols);
|
|
142
|
+
|
|
143
|
+
await verifyTypeWithComposition(
|
|
144
|
+
tenant, incomingMessage.message, protocolDefinition, messageStore, boundFetchDefinition, governingTimestamp
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const ruleSet = ProtocolAuthorization.getRuleSet(
|
|
148
|
+
incomingMessage.message.descriptor.protocolPath,
|
|
149
|
+
protocolDefinition,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
ProtocolAuthorization.verifyStoredInitialWriteRoleRecipientIfNeeded(incomingMessage, ruleSet);
|
|
153
|
+
verifySizeLimit(incomingMessage, ruleSet);
|
|
154
|
+
verifyTagsIfNeeded(incomingMessage, ruleSet);
|
|
155
|
+
await verifySquashEligibility(incomingMessage, ruleSet);
|
|
156
|
+
ProtocolAuthorization.verifyStoredInitialWriteCreateAction(tenant, incomingMessage, ruleSet);
|
|
157
|
+
|
|
158
|
+
// `verifyRecordLimit()` is not replayed here. It is stateful and counts the present
|
|
159
|
+
// latest live set, which would incorrectly reject the record being revalidated.
|
|
160
|
+
// Inbound writes continue to enforce record limits at admission time.
|
|
161
|
+
}
|
|
162
|
+
|
|
116
163
|
/**
|
|
117
164
|
* Performs protocol-based authorization against the incoming RecordsWrite message.
|
|
118
165
|
* @throws {Error} if authorization fails.
|
|
@@ -444,4 +491,87 @@ export class ProtocolAuthorization {
|
|
|
444
491
|
return ruleSet;
|
|
445
492
|
}
|
|
446
493
|
|
|
494
|
+
private static async verifyStoredInitialWrite(incomingMessage: RecordsWrite): Promise<void> {
|
|
495
|
+
if (await incomingMessage.isInitialWrite()) {
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
throw new DwnError(
|
|
500
|
+
DwnErrorCode.ProtocolAuthorizationInitialWriteRevalidationNotInitial,
|
|
501
|
+
'stored write revalidation only supports initial RecordsWrite messages'
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
private static verifyStoredInitialWriteRoleRecipientIfNeeded(
|
|
506
|
+
incomingMessage: RecordsWrite,
|
|
507
|
+
ruleSet: ProtocolRuleSet,
|
|
508
|
+
): void {
|
|
509
|
+
if (ruleSet.$role !== true || incomingMessage.message.descriptor.recipient !== undefined) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
throw new DwnError(
|
|
514
|
+
DwnErrorCode.ProtocolAuthorizationStoredInitialWriteRoleMissingRecipient,
|
|
515
|
+
'role records must have a recipient'
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private static verifyStoredInitialWriteCreateAction(
|
|
520
|
+
tenant: string,
|
|
521
|
+
incomingMessage: RecordsWrite,
|
|
522
|
+
ruleSet: ProtocolRuleSet,
|
|
523
|
+
): void {
|
|
524
|
+
if (ProtocolAuthorization.isStoredInitialWriteDirectlyAuthorized(tenant, incomingMessage)) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const actions = incomingMessage.message.descriptor.squash === true
|
|
529
|
+
? [ProtocolAction.Squash, ProtocolAction.Create]
|
|
530
|
+
: [ProtocolAction.Create];
|
|
531
|
+
const actionRules = ruleSet.$actions;
|
|
532
|
+
if (actionRules === undefined) {
|
|
533
|
+
throw new DwnError(
|
|
534
|
+
DwnErrorCode.ProtocolAuthorizationStoredInitialWriteActionRulesNotFound,
|
|
535
|
+
`no create action rule defined for stored RecordsWrite by author ${incomingMessage.author}`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const invokedRole = incomingMessage.signaturePayload?.protocolRole;
|
|
540
|
+
for (const actionRule of actionRules) {
|
|
541
|
+
if (!actionRule.can.some((allowedAction: string): boolean => actions.includes(allowedAction as ProtocolAction))) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (invokedRole !== undefined) {
|
|
546
|
+
if (actionRule.role === invokedRole) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (actionRule.who === ProtocolActor.Anyone) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Author/recipient-of rules depend on the parent chain. This repair path preserves
|
|
557
|
+
// instead of hard-purging when validity depends on mutable or missing dependency records.
|
|
558
|
+
if (actionRule.who === ProtocolActor.Author || actionRule.who === ProtocolActor.Recipient) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
throw new DwnError(
|
|
564
|
+
DwnErrorCode.ProtocolAuthorizationStoredInitialWriteActionNotAllowed,
|
|
565
|
+
`stored RecordsWrite by author ${incomingMessage.author} is not allowed by the governing protocol config`
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private static isStoredInitialWriteDirectlyAuthorized(tenant: string, incomingMessage: RecordsWrite): boolean {
|
|
570
|
+
return incomingMessage.owner !== undefined ||
|
|
571
|
+
incomingMessage.author === tenant ||
|
|
572
|
+
incomingMessage.isSignedByAuthorDelegate ||
|
|
573
|
+
incomingMessage.isSignedByOwnerDelegate ||
|
|
574
|
+
incomingMessage.signaturePayload?.permissionGrantId !== undefined;
|
|
575
|
+
}
|
|
576
|
+
|
|
447
577
|
}
|