@enbox/dwn-sdk-js 0.3.6 → 0.3.8
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 +36 -11
- 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/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 +15 -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 +1 -0
- 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/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 +62 -11
- 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
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { MessageStore } from '../types/message-store.js';
|
|
2
2
|
import type { PermissionGrant } from '../protocols/permission-grant.js';
|
|
3
|
+
import type { ProtocolScope } from '../utils/permission-scope.js';
|
|
3
4
|
import type { PermissionConditions, RecordsPermissionScope } from '../types/permission-types.js';
|
|
4
5
|
import type { RecordsCountMessage, RecordsDeleteMessage, RecordsQueryMessage, RecordsReadMessage, RecordsSubscribeMessage, RecordsWriteMessage } from '../types/records-types.js';
|
|
5
6
|
|
|
6
7
|
import { GrantAuthorization } from './grant-authorization.js';
|
|
7
8
|
import { PermissionConditionPublication } from '../types/permission-types.js';
|
|
9
|
+
import { PermissionScopeMatcher } from '../utils/permission-scope.js';
|
|
8
10
|
import { DwnError, DwnErrorCode } from './dwn-error.js';
|
|
9
11
|
|
|
10
12
|
export class RecordsGrantAuthorization {
|
|
@@ -90,11 +92,15 @@ export class RecordsGrantAuthorization {
|
|
|
90
92
|
// The grant's protocol must match the query/subscribe filter's protocol.
|
|
91
93
|
// NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
|
|
92
94
|
const permissionScope = permissionGrant.scope as RecordsPermissionScope;
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
+
const messageFilter = incomingMessage.descriptor.filter;
|
|
96
|
+
if (!PermissionScopeMatcher.matches(permissionScope, {
|
|
97
|
+
protocol : messageFilter.protocol,
|
|
98
|
+
protocolPath : messageFilter.protocolPath,
|
|
99
|
+
contextId : messageFilter.contextId,
|
|
100
|
+
})) {
|
|
95
101
|
throw new DwnError(
|
|
96
102
|
DwnErrorCode.RecordsGrantAuthorizationQueryOrSubscribeProtocolScopeMismatch,
|
|
97
|
-
`Grant
|
|
103
|
+
`Grant scope does not match Records ${incomingMessage.descriptor.method} filter`
|
|
98
104
|
);
|
|
99
105
|
}
|
|
100
106
|
}
|
|
@@ -123,16 +129,8 @@ export class RecordsGrantAuthorization {
|
|
|
123
129
|
messageStore
|
|
124
130
|
});
|
|
125
131
|
|
|
126
|
-
// The grant's protocol must match the protocol of the record being deleted.
|
|
127
132
|
// NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
|
|
128
|
-
|
|
129
|
-
const protocolOfRecordToDelete = recordsWriteToDelete.descriptor.protocol;
|
|
130
|
-
if (protocolOfRecordToDelete !== permissionScope.protocol) {
|
|
131
|
-
throw new DwnError(
|
|
132
|
-
DwnErrorCode.RecordsGrantAuthorizationDeleteProtocolScopeMismatch,
|
|
133
|
-
`Grant protocol scope ${permissionScope.protocol} does not match protocol in record to delete ${protocolOfRecordToDelete}`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
133
|
+
RecordsGrantAuthorization.verifyDeleteScope(recordsWriteToDelete, permissionGrant.scope as RecordsPermissionScope);
|
|
136
134
|
}
|
|
137
135
|
|
|
138
136
|
/**
|
|
@@ -142,32 +140,77 @@ export class RecordsGrantAuthorization {
|
|
|
142
140
|
recordsWriteMessage: RecordsWriteMessage,
|
|
143
141
|
grantScope: RecordsPermissionScope
|
|
144
142
|
): void {
|
|
143
|
+
const target = RecordsGrantAuthorization.getProtocolScopeTarget(recordsWriteMessage);
|
|
144
|
+
|
|
145
|
+
if (PermissionScopeMatcher.matches(grantScope, target)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
145
148
|
|
|
146
|
-
|
|
147
|
-
if (grantScope.protocol !== recordsWriteMessage.descriptor.protocol) {
|
|
149
|
+
if (grantScope.protocol !== target.protocol) {
|
|
148
150
|
throw new DwnError(
|
|
149
151
|
DwnErrorCode.RecordsGrantAuthorizationScopeProtocolMismatch,
|
|
150
152
|
`Grant scope specifies different protocol than what appears in the record`
|
|
151
153
|
);
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
RecordsGrantAuthorization.throwScopeMismatchAfterProtocolMatch(grantScope, target);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Verifies RecordsDelete scope while preserving the delete-specific protocol mismatch code.
|
|
161
|
+
*/
|
|
162
|
+
private static verifyDeleteScope(
|
|
163
|
+
recordsWriteMessage: RecordsWriteMessage,
|
|
164
|
+
grantScope: RecordsPermissionScope
|
|
165
|
+
): void {
|
|
166
|
+
const target = RecordsGrantAuthorization.getProtocolScopeTarget(recordsWriteMessage);
|
|
167
|
+
|
|
168
|
+
if (PermissionScopeMatcher.matches(grantScope, target)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (grantScope.protocol !== target.protocol) {
|
|
173
|
+
throw new DwnError(
|
|
174
|
+
DwnErrorCode.RecordsGrantAuthorizationDeleteProtocolScopeMismatch,
|
|
175
|
+
`Grant protocol scope ${grantScope.protocol} does not match protocol in record to delete ${target.protocol}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
RecordsGrantAuthorization.throwScopeMismatchAfterProtocolMatch(grantScope, target);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private static getProtocolScopeTarget(recordsWriteMessage: RecordsWriteMessage): ProtocolScope {
|
|
183
|
+
return {
|
|
184
|
+
protocol : recordsWriteMessage.descriptor.protocol,
|
|
185
|
+
protocolPath : recordsWriteMessage.descriptor.protocolPath,
|
|
186
|
+
contextId : recordsWriteMessage.contextId,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private static throwScopeMismatchAfterProtocolMatch(
|
|
191
|
+
grantScope: RecordsPermissionScope,
|
|
192
|
+
target: ProtocolScope
|
|
193
|
+
): never {
|
|
154
194
|
// If grant specifies a contextId, check that record falls under that contextId
|
|
155
195
|
if (grantScope.contextId !== undefined) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
);
|
|
161
|
-
}
|
|
196
|
+
throw new DwnError(
|
|
197
|
+
DwnErrorCode.RecordsGrantAuthorizationScopeContextIdMismatch,
|
|
198
|
+
`Grant scope specifies different contextId than what appears in the record`
|
|
199
|
+
);
|
|
162
200
|
}
|
|
163
201
|
|
|
164
202
|
// If grant specifies protocolPath, check that record is at that protocolPath
|
|
165
|
-
if (grantScope.protocolPath !== undefined && grantScope.protocolPath !==
|
|
203
|
+
if (grantScope.protocolPath !== undefined && grantScope.protocolPath !== target.protocolPath) {
|
|
166
204
|
throw new DwnError(
|
|
167
205
|
DwnErrorCode.RecordsGrantAuthorizationScopeProtocolPathMismatch,
|
|
168
206
|
`Grant scope specifies different protocolPath than what appears in the record`
|
|
169
207
|
);
|
|
170
208
|
}
|
|
209
|
+
|
|
210
|
+
throw new DwnError(
|
|
211
|
+
DwnErrorCode.RecordsGrantAuthorizationScopeMismatch,
|
|
212
|
+
`Grant scope does not match the record`
|
|
213
|
+
);
|
|
171
214
|
}
|
|
172
215
|
|
|
173
216
|
/**
|
|
@@ -7,10 +7,10 @@ import type { MessagesReadMessage, MessagesReadReply, MessagesReadReplyEntry } f
|
|
|
7
7
|
import { authenticate } from '../core/auth.js';
|
|
8
8
|
import { DataStream } from '../utils/data-stream.js';
|
|
9
9
|
import { Encoder } from '../utils/encoder.js';
|
|
10
|
+
import { Message } from '../core/message.js';
|
|
10
11
|
import { messageReplyFromError } from '../core/message-reply.js';
|
|
11
12
|
import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
|
|
12
13
|
import { MessagesRead } from '../interfaces/messages-read.js';
|
|
13
|
-
import { PermissionsProtocol } from '../protocols/permissions.js';
|
|
14
14
|
import { Records } from '../utils/records.js';
|
|
15
15
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
16
16
|
|
|
@@ -84,15 +84,17 @@ export class MessagesReadHandler implements MethodHandler {
|
|
|
84
84
|
if (messagesRead.author === tenant) {
|
|
85
85
|
// If the author is the tenant, no further authorization is needed
|
|
86
86
|
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const permissionGrantIds = Message.getPermissionGrantIds(messagesRead.signaturePayload!);
|
|
90
|
+
if (messagesRead.author !== undefined && permissionGrantIds.length > 0) {
|
|
91
|
+
const permissionGrants = await MessagesGrantAuthorization.fetchPermissionGrants(tenant, messageStore, permissionGrantIds);
|
|
90
92
|
await MessagesGrantAuthorization.authorizeMessagesRead({
|
|
91
93
|
messagesReadMessage : messagesRead.message,
|
|
92
94
|
messageToRead : matchedMessage,
|
|
93
95
|
expectedGrantor : tenant,
|
|
94
96
|
expectedGrantee : messagesRead.author,
|
|
95
|
-
|
|
97
|
+
permissionGrants,
|
|
96
98
|
messageStore
|
|
97
99
|
});
|
|
98
100
|
} else {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MessageStore } from '../types/message-store.js';
|
|
2
|
+
import type { PermissionGrant } from '../protocols/permission-grant.js';
|
|
3
|
+
import type { EventSubscription, ProgressGapInfo, SubscriptionEvent, SubscriptionListener, SubscriptionMessage } from '../types/subscriptions.js';
|
|
2
4
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
3
5
|
import type { MessagesSubscribeMessage, MessagesSubscribeReply } from '../types/messages-types.js';
|
|
4
|
-
import type { ProgressGapInfo, SubscriptionListener } from '../types/subscriptions.js';
|
|
5
6
|
|
|
6
7
|
import { authenticate } from '../core/auth.js';
|
|
7
8
|
import { Message } from '../core/message.js';
|
|
@@ -9,9 +10,23 @@ import { messageReplyFromError } from '../core/message-reply.js';
|
|
|
9
10
|
import { Messages } from '../utils/messages.js';
|
|
10
11
|
import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
|
|
11
12
|
import { MessagesSubscribe } from '../interfaces/messages-subscribe.js';
|
|
12
|
-
import {
|
|
13
|
+
import { Time } from '../utils/time.js';
|
|
13
14
|
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
|
|
14
15
|
|
|
16
|
+
type MessagesSubscribeAuthorization =
|
|
17
|
+
| { kind: 'owner' }
|
|
18
|
+
| {
|
|
19
|
+
kind: 'delegate';
|
|
20
|
+
expectedGrantor: string;
|
|
21
|
+
expectedGrantee: string;
|
|
22
|
+
permissionGrants: PermissionGrant[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type GuardedSubscriptionHandler = {
|
|
26
|
+
listener: SubscriptionListener;
|
|
27
|
+
setSubscription(subscription: EventSubscription): Promise<void>;
|
|
28
|
+
};
|
|
29
|
+
|
|
15
30
|
export class MessagesSubscribeHandler implements MethodHandler {
|
|
16
31
|
|
|
17
32
|
constructor(private readonly deps: HandlerDependencies) {}
|
|
@@ -39,22 +54,31 @@ export class MessagesSubscribeHandler implements MethodHandler {
|
|
|
39
54
|
return messageReplyFromError(e, 400);
|
|
40
55
|
}
|
|
41
56
|
|
|
57
|
+
let authorization: MessagesSubscribeAuthorization;
|
|
42
58
|
try {
|
|
43
59
|
await authenticate(message.authorization, this.deps.didResolver);
|
|
44
|
-
await MessagesSubscribeHandler.authorizeMessagesSubscribe(tenant, messagesSubscribe, this.deps.messageStore);
|
|
60
|
+
authorization = await MessagesSubscribeHandler.authorizeMessagesSubscribe(tenant, messagesSubscribe, this.deps.messageStore);
|
|
45
61
|
} catch (error) {
|
|
46
62
|
return messageReplyFromError(error, 401);
|
|
47
63
|
}
|
|
48
64
|
|
|
65
|
+
const guardedHandler = MessagesSubscribeHandler.createAuthorizationGuard({
|
|
66
|
+
authorization,
|
|
67
|
+
messagesSubscribe,
|
|
68
|
+
messageStore: this.deps.messageStore,
|
|
69
|
+
subscriptionHandler,
|
|
70
|
+
});
|
|
71
|
+
|
|
49
72
|
const { filters, cursor: eventLogCursor } = message.descriptor;
|
|
50
73
|
const messagesFilters = Messages.convertFilters(filters, this.deps.coreProtocols);
|
|
51
74
|
const messageCid = await Message.getCid(message);
|
|
52
75
|
|
|
53
76
|
try {
|
|
54
|
-
const subscription = await this.deps.eventLog.subscribe(tenant, messageCid,
|
|
77
|
+
const subscription = await this.deps.eventLog.subscribe(tenant, messageCid, guardedHandler.listener, {
|
|
55
78
|
cursor : eventLogCursor,
|
|
56
79
|
filters : messagesFilters,
|
|
57
80
|
});
|
|
81
|
+
await guardedHandler.setSubscription(subscription);
|
|
58
82
|
|
|
59
83
|
return {
|
|
60
84
|
status: { code: 200, detail: 'OK' },
|
|
@@ -72,21 +96,137 @@ export class MessagesSubscribeHandler implements MethodHandler {
|
|
|
72
96
|
}
|
|
73
97
|
}
|
|
74
98
|
|
|
75
|
-
private static async authorizeMessagesSubscribe(
|
|
99
|
+
private static async authorizeMessagesSubscribe(
|
|
100
|
+
tenant: string,
|
|
101
|
+
messagesSubscribe: MessagesSubscribe,
|
|
102
|
+
messageStore: MessageStore
|
|
103
|
+
): Promise<MessagesSubscribeAuthorization> {
|
|
76
104
|
// if `MessagesSubscribe` author is the same as the target tenant, we can directly grant access
|
|
77
105
|
if (messagesSubscribe.author === tenant) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
106
|
+
return { kind: 'owner' };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const permissionGrantIds = Message.getPermissionGrantIds(messagesSubscribe.signaturePayload!);
|
|
110
|
+
if (messagesSubscribe.author !== undefined && permissionGrantIds.length > 0) {
|
|
111
|
+
const permissionGrants = await MessagesGrantAuthorization.fetchPermissionGrants(tenant, messageStore, permissionGrantIds);
|
|
81
112
|
await MessagesGrantAuthorization.authorizeSubscribeOrSync({
|
|
82
113
|
incomingMessage : messagesSubscribe.message,
|
|
83
114
|
expectedGrantor : tenant,
|
|
84
115
|
expectedGrantee : messagesSubscribe.author,
|
|
85
|
-
|
|
116
|
+
permissionGrants,
|
|
86
117
|
messageStore
|
|
87
118
|
});
|
|
119
|
+
return {
|
|
120
|
+
kind : 'delegate',
|
|
121
|
+
expectedGrantor : tenant,
|
|
122
|
+
expectedGrantee : messagesSubscribe.author,
|
|
123
|
+
permissionGrants,
|
|
124
|
+
};
|
|
88
125
|
} else {
|
|
89
126
|
throw new DwnError(DwnErrorCode.MessagesSubscribeAuthorizationFailed, 'message failed authorization');
|
|
90
127
|
}
|
|
91
128
|
}
|
|
129
|
+
|
|
130
|
+
private static createAuthorizationGuard(input: {
|
|
131
|
+
authorization: MessagesSubscribeAuthorization;
|
|
132
|
+
messagesSubscribe: MessagesSubscribe;
|
|
133
|
+
messageStore: MessageStore;
|
|
134
|
+
subscriptionHandler: SubscriptionListener;
|
|
135
|
+
}): GuardedSubscriptionHandler {
|
|
136
|
+
const { authorization, messagesSubscribe, messageStore, subscriptionHandler } = input;
|
|
137
|
+
if (authorization.kind === 'owner') {
|
|
138
|
+
return {
|
|
139
|
+
listener : subscriptionHandler,
|
|
140
|
+
setSubscription : async (): Promise<void> => {},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let subscription: EventSubscription | undefined;
|
|
145
|
+
let closeRequested = false;
|
|
146
|
+
let terminalErrorEmitted = false;
|
|
147
|
+
let deliveryQueue: Promise<void> = Promise.resolve();
|
|
148
|
+
|
|
149
|
+
const closeSubscription = (): void => {
|
|
150
|
+
if (closeRequested) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
closeRequested = true;
|
|
154
|
+
Promise.resolve(subscription?.close()).catch(() => {});
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const emitTerminalAuthorizationError = (cursor: SubscriptionEvent['cursor']): void => {
|
|
158
|
+
if (terminalErrorEmitted) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
terminalErrorEmitted = true;
|
|
162
|
+
subscriptionHandler({
|
|
163
|
+
type : 'error',
|
|
164
|
+
cursor,
|
|
165
|
+
error : {
|
|
166
|
+
code : DwnErrorCode.MessagesSubscribeDeliveryAuthorizationFailed,
|
|
167
|
+
detail : 'subscription authorization failed during delivery',
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Deliberately do not cache delivery authorization here. Subscribe-open
|
|
173
|
+
// authorization validates static grant shape and filter scope; this per-event
|
|
174
|
+
// check revalidates dynamic grant state so expiry or revocation stops delivery
|
|
175
|
+
// before the next event is forwarded. Future throughput optimizations should
|
|
176
|
+
// split static and dynamic checks explicitly and document any bounded staleness
|
|
177
|
+
// introduced by caching revocation lookups.
|
|
178
|
+
const authorizeAndDeliverEvent = async (subMessage: SubscriptionEvent): Promise<void> => {
|
|
179
|
+
try {
|
|
180
|
+
await MessagesGrantAuthorization.authorizeSubscribeDelivery({
|
|
181
|
+
messagesSubscribeMessage : messagesSubscribe.message,
|
|
182
|
+
expectedGrantor : authorization.expectedGrantor,
|
|
183
|
+
expectedGrantee : authorization.expectedGrantee,
|
|
184
|
+
permissionGrants : authorization.permissionGrants,
|
|
185
|
+
messageStore,
|
|
186
|
+
deliveryTimestamp : Time.getCurrentTimestamp(),
|
|
187
|
+
});
|
|
188
|
+
} catch {
|
|
189
|
+
emitTerminalAuthorizationError(subMessage.cursor);
|
|
190
|
+
closeSubscription();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!closeRequested) {
|
|
195
|
+
subscriptionHandler(subMessage);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const deliverQueuedMessage = async (subMessage: SubscriptionMessage): Promise<void> => {
|
|
200
|
+
if (closeRequested) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (subMessage.type !== 'event') {
|
|
205
|
+
subscriptionHandler(subMessage);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await authorizeAndDeliverEvent(subMessage);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const enqueueDelivery = (subMessage: SubscriptionMessage): void => {
|
|
213
|
+
deliveryQueue = deliveryQueue
|
|
214
|
+
.then(() => deliverQueuedMessage(subMessage))
|
|
215
|
+
.catch(() => {});
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const listener: SubscriptionListener = (subMessage: SubscriptionMessage): void => {
|
|
219
|
+
enqueueDelivery(subMessage);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
listener,
|
|
224
|
+
setSubscription: async (eventSubscription: EventSubscription): Promise<void> => {
|
|
225
|
+
subscription = eventSubscription;
|
|
226
|
+
if (closeRequested) {
|
|
227
|
+
await eventSubscription.close();
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
}
|
|
92
232
|
}
|