@enbox/dwn-sdk-js 0.4.17 → 0.4.18
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 +1 -1
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +349 -198
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/handlers/records-collection.js +200 -0
- package/dist/esm/src/handlers/records-collection.js.map +1 -0
- package/dist/esm/src/handlers/records-count.js +19 -196
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +19 -376
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +21 -341
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +1 -1
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +1 -1
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +3 -1
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/store/index-level.js +84 -32
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +45 -11
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +5 -2
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
- package/dist/esm/src/utils/records.js +14 -47
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js +15 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +116 -0
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +52 -1
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +76 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/store/index-level.spec.js +95 -1
- package/dist/esm/tests/store/index-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +70 -1
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/handlers/records-collection.d.ts +32 -0
- package/dist/types/src/handlers/records-collection.d.ts.map +1 -0
- package/dist/types/src/handlers/records-count.d.ts +0 -35
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +0 -61
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +1 -37
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +27 -3
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +1 -0
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +6 -1
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/records-types.d.ts +1 -1
- package/dist/types/src/types/records-types.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +6 -25
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/tests/features/records-nested-query-scope.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/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/handlers/records-collection.ts +269 -0
- package/src/handlers/records-count.ts +21 -236
- package/src/handlers/records-query.ts +22 -447
- package/src/handlers/records-subscribe.ts +26 -404
- package/src/interfaces/records-count.ts +1 -1
- package/src/interfaces/records-query.ts +1 -1
- package/src/interfaces/records-subscribe.ts +5 -2
- package/src/store/index-level.ts +145 -37
- package/src/store/message-store-level.ts +46 -11
- package/src/types/message-store.ts +6 -1
- package/src/types/records-types.ts +1 -1
- package/src/utils/record-limit-occupancy.ts +5 -2
- package/src/utils/records.ts +17 -52
|
@@ -65,11 +65,13 @@ export declare class Records {
|
|
|
65
65
|
*/
|
|
66
66
|
static normalizeFilter(filter: RecordsFilter): RecordsFilter;
|
|
67
67
|
/**
|
|
68
|
-
* Nested protocol-path
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* Nested protocol-path collection requests must select one or more direct
|
|
69
|
+
* parents, the target path itself, or one of its ancestor contexts. A
|
|
70
|
+
* bounded path-wide subscription may omit that scope because its initial
|
|
71
|
+
* page is explicitly capped; record-limited results still project each
|
|
72
|
+
* direct-parent population independently.
|
|
71
73
|
*/
|
|
72
|
-
static
|
|
74
|
+
static validateNestedProtocolPathScope(filter: RecordsFilter, errorCode: DwnErrorCode, operationName: string, allowBoundedPathWideSelection?: boolean): void;
|
|
73
75
|
static isStartsWithFilter(filter: RecordsWriteTagsFilter): filter is StartsWithFilter;
|
|
74
76
|
/**
|
|
75
77
|
* This will create individual keys for each of the tags that look like `tag.tag_property`
|
|
@@ -88,7 +90,6 @@ export declare class Records {
|
|
|
88
90
|
* @returns {Filter} a generic Filter able to be used with MessageStore.
|
|
89
91
|
*/
|
|
90
92
|
static convertFilter(filter: RecordsFilter, dateSort?: DateSort): Filter;
|
|
91
|
-
static buildUnpublishedControlRecordsFilter(filter: RecordsFilter, dateSort?: DateSort): Filter;
|
|
92
93
|
static buildControlRecordsFilters(filters: Filter[]): Filter[];
|
|
93
94
|
/**
|
|
94
95
|
* Validates the referential integrity of both author-delegated grant and owner-delegated grant.
|
|
@@ -116,10 +117,6 @@ export declare class Records {
|
|
|
116
117
|
* @returns a `MessageSort` for `MessageStore` sorting.
|
|
117
118
|
*/
|
|
118
119
|
static convertDateSort(dateSort?: DateSort): MessageSort;
|
|
119
|
-
/**
|
|
120
|
-
* Determines if signature payload contains a protocolRole and should be authorized as such.
|
|
121
|
-
*/
|
|
122
|
-
static shouldProtocolAuthorize(signaturePayload: GenericSignaturePayload): boolean;
|
|
123
120
|
/**
|
|
124
121
|
* Checks if the filter supports returning published records.
|
|
125
122
|
*/
|
|
@@ -140,21 +137,5 @@ export declare class Records {
|
|
|
140
137
|
* replay, and replay must never admit a delete that admission would now reject.
|
|
141
138
|
*/
|
|
142
139
|
static isDeleteBeatenByExistingTombstone(deleteToBePerformed: RecordsDeleteMessage, newestExistingMessage: GenericMessage): Promise<boolean>;
|
|
143
|
-
/**
|
|
144
|
-
* Checks whether or not the incoming records query filter should build an unpublished recipient MessageStore filter.
|
|
145
|
-
*
|
|
146
|
-
* @param filter The incoming RecordsFilter to evaluate against.
|
|
147
|
-
* @param recipient The recipient to check against the filter, typically the query/subscribe message author.
|
|
148
|
-
* @returns {boolean} True if the filter contains the recipient, or if the recipient filter is undefined/empty.
|
|
149
|
-
*/
|
|
150
|
-
static shouldBuildUnpublishedRecipientFilter(filter: RecordsFilter, recipient: string): boolean;
|
|
151
|
-
/**
|
|
152
|
-
* Checks whether or not the incoming records query filter should build an unpublished author MessageStore filter.
|
|
153
|
-
*
|
|
154
|
-
* @param filter The incoming RecordsFilter to evaluate against.
|
|
155
|
-
* @param author The author to check against the filter, typically the query/subscribe message author.
|
|
156
|
-
* @returns {boolean} True if the filter contains the author, or if the author filter is undefined/empty.
|
|
157
|
-
*/
|
|
158
|
-
static shouldBuildUnpublishedAuthorFilter(filter: RecordsFilter, author: string): boolean;
|
|
159
140
|
}
|
|
160
141
|
//# sourceMappingURL=records.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../../src/utils/records.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACtG,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,aAAa,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAInQ,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AASrD,OAAO,EAAY,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAc,+BAA+B,EAAE,MAAM,iBAAiB,CAAC;AAE9E,OAAO,EAAS,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGzD;;GAEG;AACH,qBAAa,OAAO;IAElB;;OAEG;WACW,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,IAAI,mBAAmB;IAQrF;;OAEG;WACiB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAM/G;;OAEG;WACiB,sBAAsB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAQjH;;;OAGG;WACiB,OAAO,CACzB,YAAY,EAAE,mBAAmB,EACjC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,cAAc,CAAC,UAAU,CAAC,GACvC,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;mBAoCjB,yBAAyB;IAkB9C,OAAO,CAAC,MAAM,CAAC,8BAA8B;IAqB7C;;OAEG;WACW,0BAA0B,CACtC,mBAAmB,EAAE,mBAAmB,GAAG,OAAO,+BAA+B,EACjF,mBAAmB,EAAE,mBAAmB,GACvC,MAAM,EAAE;IAiBX;;;;;;;OAOG;WACW,iDAAiD,CAAC,UAAU,EAAE,sBAAsB,GAAG,MAAM,EAAE;IAW7G;;;OAGG;WACiB,gBAAgB,CAAC,kBAAkB,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAoBxI;;;OAGG;WACW,oDAAoD,CAChE,yBAAyB,EAAE,MAAM,EAAE,EACnC,2BAA2B,EAAE,MAAM,EAAE,GACpC,IAAI;IAYP;;OAEG;WACW,+BAA+B,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS;IAkBhG;;;;;OAKG;WACW,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa;IAyBnE
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../../../src/utils/records.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-nested-query-scope.spec.d.ts","sourceRoot":"","sources":["../../../../tests/features/records-nested-query-scope.spec.ts"],"names":[],"mappings":"AAmBA,wBAAgB,2BAA2B,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"records-nested-query-scope.spec.d.ts","sourceRoot":"","sources":["../../../../tests/features/records-nested-query-scope.spec.ts"],"names":[],"mappings":"AAmBA,wBAAgB,2BAA2B,IAAI,IAAI,CAwalD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-record-limit.spec.d.ts","sourceRoot":"","sources":["../../../../tests/features/records-record-limit.spec.ts"],"names":[],"mappings":"AA4BA,wBAAgB,sBAAsB,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"records-record-limit.spec.d.ts","sourceRoot":"","sources":["../../../../tests/features/records-record-limit.spec.ts"],"names":[],"mappings":"AA4BA,wBAAgB,sBAAsB,IAAI,IAAI,CAoqC7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-count.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-count.spec.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"records-count.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-count.spec.ts"],"names":[],"mappings":"AA0BA,wBAAgB,uBAAuB,IAAI,IAAI,CAy3B9C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"records-subscribe.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-subscribe.spec.ts"],"names":[],"mappings":"AAkCA,wBAAgB,2BAA2B,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"records-subscribe.spec.d.ts","sourceRoot":"","sources":["../../../../tests/handlers/records-subscribe.spec.ts"],"names":[],"mappings":"AAkCA,wBAAgB,2BAA2B,IAAI,IAAI,CAwvDlD"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import type { HandlerDependencies } from '../types/method-handler.js';
|
|
2
|
+
import type { RecordsCount } from '../interfaces/records-count.js';
|
|
3
|
+
import type { RecordsQuery } from '../interfaces/records-query.js';
|
|
4
|
+
import type { RecordsSubscribe } from '../interfaces/records-subscribe.js';
|
|
5
|
+
import type { Filter, PaginationCursor } from '../types/query-types.js';
|
|
6
|
+
import type { RecordsFilter, RecordsQueryReplyEntry } from '../types/records-types.js';
|
|
7
|
+
|
|
8
|
+
import { authenticate } from '../core/auth.js';
|
|
9
|
+
import { DateSort } from '../types/records-types.js';
|
|
10
|
+
import { EncryptionControl } from '../core/encryption-control.js';
|
|
11
|
+
import { Message } from '../core/message.js';
|
|
12
|
+
import { ProtocolAuthorization } from '../core/protocol-authorization.js';
|
|
13
|
+
import { Records } from '../utils/records.js';
|
|
14
|
+
import { RecordsGrantAuthorization } from '../core/records-grant-authorization.js';
|
|
15
|
+
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
16
|
+
import { queryRecordsWithRecordLimitOccupancy, resolveRecordLimitOccupancy } from '../utils/record-limit-occupancy.js';
|
|
17
|
+
|
|
18
|
+
type RecordsCollectionRequest = RecordsCount | RecordsQuery | RecordsSubscribe;
|
|
19
|
+
|
|
20
|
+
export type RecordsCollectionVisibility = 'nonOwner' | 'owner' | 'published';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the visible population for a Records collection request, authenticating and
|
|
24
|
+
* authorizing every request that is not an anonymous published-records read.
|
|
25
|
+
*/
|
|
26
|
+
export async function resolveRecordsCollectionVisibility(
|
|
27
|
+
tenant: string,
|
|
28
|
+
request: RecordsCollectionRequest,
|
|
29
|
+
deps: HandlerDependencies,
|
|
30
|
+
): Promise<RecordsCollectionVisibility> {
|
|
31
|
+
const recordsFilter = request.message.descriptor.filter;
|
|
32
|
+
if (Records.filterIncludesPublishedRecords(recordsFilter) && request.author === undefined) {
|
|
33
|
+
return 'published';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await authenticate(request.message.authorization!, deps.didResolver);
|
|
37
|
+
|
|
38
|
+
if (Message.isSignedByAuthorDelegate(request.message) && !EncryptionControl.filterTargetsOnlyControlRecords(recordsFilter)) {
|
|
39
|
+
await request.authorizeDelegate(deps.validationStateReader);
|
|
40
|
+
} else if (EncryptionControl.filterTargetsOnlyControlRecords(recordsFilter)) {
|
|
41
|
+
await EncryptionControl.authorizeControlReadRequest({
|
|
42
|
+
tenant,
|
|
43
|
+
incomingMessage : request.message,
|
|
44
|
+
requester : Message.getRequester(request.message),
|
|
45
|
+
validationStateReader : deps.validationStateReader,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const permissionGrantId = Message.getPermissionGrantId(request.signaturePayload!);
|
|
50
|
+
if (permissionGrantId !== undefined) {
|
|
51
|
+
const permissionGrant = await deps.validationStateReader.fetchGrant(tenant, permissionGrantId);
|
|
52
|
+
await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
|
|
53
|
+
incomingMessage : request.message,
|
|
54
|
+
expectedGrantor : tenant,
|
|
55
|
+
expectedGrantee : request.author!,
|
|
56
|
+
permissionGrant,
|
|
57
|
+
validationStateReader : deps.validationStateReader,
|
|
58
|
+
});
|
|
59
|
+
} else if (invokesProtocolRole(request)) {
|
|
60
|
+
// A protocol filter alone does not require protocol authorization because unauthorized
|
|
61
|
+
// records are removed by the visibility filters. An invoked protocol role does.
|
|
62
|
+
await ProtocolAuthorization.authorizeQueryOrSubscribe(tenant, request, deps.validationStateReader);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return request.author === tenant ? 'owner' : 'nonOwner';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Builds latest-write filters shared by Records Query, Count, and Subscribe snapshots. */
|
|
69
|
+
export function buildRecordsSnapshotFilters(
|
|
70
|
+
request: RecordsCollectionRequest,
|
|
71
|
+
visibility: RecordsCollectionVisibility,
|
|
72
|
+
): Filter[] {
|
|
73
|
+
const descriptor = request.message.descriptor;
|
|
74
|
+
const dateSort = 'dateSort' in descriptor ? descriptor.dateSort : undefined;
|
|
75
|
+
return buildRecordsVisibilityFilters({
|
|
76
|
+
request,
|
|
77
|
+
visibility,
|
|
78
|
+
filter : descriptor.filter,
|
|
79
|
+
purpose : 'snapshot',
|
|
80
|
+
dateSort,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Builds Write-and-Delete filters for Records Subscribe event delivery. */
|
|
85
|
+
export function buildRecordsEventFilters(
|
|
86
|
+
request: RecordsSubscribe,
|
|
87
|
+
visibility: RecordsCollectionVisibility,
|
|
88
|
+
): Filter[] {
|
|
89
|
+
return buildRecordsVisibilityFilters({
|
|
90
|
+
request,
|
|
91
|
+
visibility,
|
|
92
|
+
filter : request.message.descriptor.filter,
|
|
93
|
+
purpose : 'event',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Queries one visible Records page after applying record-limit occupancy, current-audience
|
|
99
|
+
* projection, and encryption-control authorization in their required order.
|
|
100
|
+
*/
|
|
101
|
+
export async function queryVisibleRecordsPage(input: {
|
|
102
|
+
deps: HandlerDependencies;
|
|
103
|
+
request: RecordsQuery | RecordsSubscribe;
|
|
104
|
+
tenant: string;
|
|
105
|
+
visibility: RecordsCollectionVisibility;
|
|
106
|
+
}): Promise<{ messages: RecordsQueryReplyEntry[]; cursor?: PaginationCursor }> {
|
|
107
|
+
const { deps, request, tenant, visibility } = input;
|
|
108
|
+
const { dateSort, filter, messageTimestamp, pagination } = request.message.descriptor;
|
|
109
|
+
const filters = buildRecordsSnapshotFilters(request, visibility);
|
|
110
|
+
const recordLimit = await resolveRecordLimitOccupancy({
|
|
111
|
+
validationStateReader : deps.validationStateReader,
|
|
112
|
+
tenant,
|
|
113
|
+
recordsFilter : filter,
|
|
114
|
+
messageTimestamp,
|
|
115
|
+
});
|
|
116
|
+
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
117
|
+
const currentAudienceRecordIdCache = new Map<string, string | undefined>();
|
|
118
|
+
const requester = Message.getRequester(request.message);
|
|
119
|
+
const messageSort = Records.convertDateSort(dateSort ?? DateSort.CreatedAscending);
|
|
120
|
+
|
|
121
|
+
const queryProjectedPage = async (page = pagination): Promise<{
|
|
122
|
+
messages: RecordsQueryReplyEntry[];
|
|
123
|
+
cursor?: PaginationCursor;
|
|
124
|
+
}> => {
|
|
125
|
+
const result = await queryRecordsWithRecordLimitOccupancy({
|
|
126
|
+
messageStore : deps.messageStore,
|
|
127
|
+
tenant,
|
|
128
|
+
filters,
|
|
129
|
+
recordLimit,
|
|
130
|
+
messageSort,
|
|
131
|
+
pagination : page,
|
|
132
|
+
});
|
|
133
|
+
return EncryptionControl.projectCurrentAudienceRecordPage({
|
|
134
|
+
messageStore: deps.messageStore,
|
|
135
|
+
tenant,
|
|
136
|
+
filters,
|
|
137
|
+
currentAudienceRecordIdCache,
|
|
138
|
+
result,
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const filterVisibleControlRecords = (messages: RecordsQueryReplyEntry[]): Promise<RecordsQueryReplyEntry[]> =>
|
|
143
|
+
EncryptionControl.filterVisibleControlRecords({
|
|
144
|
+
tenant,
|
|
145
|
+
incomingMessage : request.message,
|
|
146
|
+
requester,
|
|
147
|
+
recordsWriteMessages : messages,
|
|
148
|
+
validationStateReader : deps.validationStateReader,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (controlFilters.length === 0) {
|
|
152
|
+
return queryProjectedPage();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (pagination?.limit === undefined || pagination.limit <= 0) {
|
|
156
|
+
const result = await queryProjectedPage();
|
|
157
|
+
return {
|
|
158
|
+
messages : await filterVisibleControlRecords(result.messages),
|
|
159
|
+
cursor : result.cursor,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const visibleMessages: RecordsQueryReplyEntry[] = [];
|
|
164
|
+
let cursor = pagination.cursor;
|
|
165
|
+
let nextCursor: PaginationCursor | undefined;
|
|
166
|
+
// Keeps visible-page pagination stable until #1100 moves control visibility into indexed store filters.
|
|
167
|
+
do {
|
|
168
|
+
const remainingLimit = pagination.limit - visibleMessages.length;
|
|
169
|
+
const result = await queryProjectedPage({ ...pagination, cursor, limit: remainingLimit });
|
|
170
|
+
visibleMessages.push(...await filterVisibleControlRecords(result.messages));
|
|
171
|
+
nextCursor = result.cursor;
|
|
172
|
+
cursor = result.cursor;
|
|
173
|
+
} while (visibleMessages.length < pagination.limit && cursor !== undefined);
|
|
174
|
+
|
|
175
|
+
return { messages: visibleMessages, cursor: nextCursor };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function buildRecordsVisibilityFilters(input: {
|
|
179
|
+
dateSort?: DateSort;
|
|
180
|
+
filter: RecordsFilter;
|
|
181
|
+
purpose: 'event' | 'snapshot';
|
|
182
|
+
request: RecordsCollectionRequest;
|
|
183
|
+
visibility: RecordsCollectionVisibility;
|
|
184
|
+
}): Filter[] {
|
|
185
|
+
const { dateSort, filter, purpose, request, visibility } = input;
|
|
186
|
+
if (visibility === 'owner') {
|
|
187
|
+
return [buildRecordsFilter({ dateSort, filter, purpose })];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (visibility === 'published') {
|
|
191
|
+
return [buildRecordsFilter({ dateSort, filter, purpose, published: true })];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const filters: Filter[] = [];
|
|
195
|
+
if (Records.filterIncludesPublishedRecords(filter)) {
|
|
196
|
+
filters.push(buildRecordsFilter({ dateSort, filter, purpose, published: true }));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!Records.filterIncludesUnpublishedRecords(filter)) {
|
|
200
|
+
return filters;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (EncryptionControl.isExactAudienceFilter(filter)) {
|
|
204
|
+
filters.push(buildRecordsFilter({ dateSort, filter, purpose, published: false }));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (shouldBuildUnpublishedAuthorFilter(filter, request.author!)) {
|
|
208
|
+
filters.push(buildRecordsFilter({ author: request.author, dateSort, filter, purpose, published: false }));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (invokesProtocolRole(request) || Message.getPermissionGrantId(request.signaturePayload!) !== undefined) {
|
|
212
|
+
filters.push(buildRecordsFilter({ dateSort, filter, purpose, published: false }));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (shouldBuildUnpublishedRecipientFilter(filter, request.author!)) {
|
|
216
|
+
filters.push(buildRecordsFilter({ dateSort, filter, purpose, published: false, recipient: request.author }));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return filters;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildRecordsFilter(input: {
|
|
223
|
+
author?: string;
|
|
224
|
+
dateSort?: DateSort;
|
|
225
|
+
filter: RecordsFilter;
|
|
226
|
+
purpose: 'event' | 'snapshot';
|
|
227
|
+
published?: boolean;
|
|
228
|
+
recipient?: string;
|
|
229
|
+
}): Filter {
|
|
230
|
+
const { author, dateSort, filter, purpose, published, recipient } = input;
|
|
231
|
+
const result: Filter = {
|
|
232
|
+
...Records.convertFilter(filter, purpose === 'snapshot' ? dateSort : undefined),
|
|
233
|
+
interface : DwnInterfaceName.Records,
|
|
234
|
+
method : purpose === 'snapshot' ? DwnMethodName.Write : [DwnMethodName.Write, DwnMethodName.Delete],
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
if (purpose === 'snapshot') {
|
|
238
|
+
result.isLatestBaseState = true;
|
|
239
|
+
}
|
|
240
|
+
if (author !== undefined) {
|
|
241
|
+
result.author = author;
|
|
242
|
+
}
|
|
243
|
+
if (published !== undefined) {
|
|
244
|
+
result.published = published;
|
|
245
|
+
}
|
|
246
|
+
if (recipient !== undefined) {
|
|
247
|
+
result.recipient = recipient;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function invokesProtocolRole(request: RecordsCollectionRequest): boolean {
|
|
254
|
+
return request.signaturePayload?.protocolRole !== undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function shouldBuildUnpublishedRecipientFilter(filter: RecordsFilter, recipient: string): boolean {
|
|
258
|
+
const { recipient: recipientFilter } = filter;
|
|
259
|
+
return Array.isArray(recipientFilter)
|
|
260
|
+
? recipientFilter.length === 0 || recipientFilter.includes(recipient)
|
|
261
|
+
: recipientFilter === undefined || recipientFilter === recipient;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function shouldBuildUnpublishedAuthorFilter(filter: RecordsFilter, author: string): boolean {
|
|
265
|
+
const { author: authorFilter } = filter;
|
|
266
|
+
return Array.isArray(authorFilter)
|
|
267
|
+
? authorFilter.length === 0 || authorFilter.includes(author)
|
|
268
|
+
: authorFilter === undefined || authorFilter === author;
|
|
269
|
+
}
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import type { Filter } from '../types/query-types.js';
|
|
2
2
|
import type { RecordLimitOccupancy } from '../types/message-store.js';
|
|
3
|
+
import type { RecordsCollectionVisibility } from './records-collection.js';
|
|
3
4
|
import type { HandlerDependencies, MethodHandler } from '../types/method-handler.js';
|
|
4
5
|
import type { RecordsCountMessage, RecordsCountReply } from '../types/records-types.js';
|
|
5
6
|
|
|
6
|
-
import { authenticate } from '../core/auth.js';
|
|
7
7
|
import { EncryptionControl } from '../core/encryption-control.js';
|
|
8
8
|
import { Message } from '../core/message.js';
|
|
9
9
|
import { messageReplyFromError } from '../core/message-reply.js';
|
|
10
|
-
import { ProtocolAuthorization } from '../core/protocol-authorization.js';
|
|
11
10
|
import { Records } from '../utils/records.js';
|
|
12
11
|
import { RecordsCount } from '../interfaces/records-count.js';
|
|
13
|
-
import {
|
|
12
|
+
import { buildRecordsSnapshotFilters, resolveRecordsCollectionVisibility } from './records-collection.js';
|
|
14
13
|
import {
|
|
15
14
|
countRecordsWithRecordLimitOccupancy,
|
|
16
15
|
queryRecordsWithRecordLimitOccupancy,
|
|
17
16
|
resolveRecordLimitOccupancy,
|
|
18
17
|
} from '../utils/record-limit-occupancy.js';
|
|
19
|
-
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
20
18
|
|
|
21
19
|
export class RecordsCountHandler implements MethodHandler {
|
|
22
20
|
|
|
@@ -29,34 +27,29 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
29
27
|
let recordsCount: RecordsCount;
|
|
30
28
|
try {
|
|
31
29
|
recordsCount = await RecordsCount.parse(message);
|
|
32
|
-
} catch (
|
|
33
|
-
return messageReplyFromError(
|
|
30
|
+
} catch (error) {
|
|
31
|
+
return messageReplyFromError(error, 400);
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
let count: number;
|
|
37
34
|
const requester = Message.getRequester(recordsCount.message);
|
|
35
|
+
let visibility: RecordsCollectionVisibility;
|
|
36
|
+
try {
|
|
37
|
+
visibility = await resolveRecordsCollectionVisibility(tenant, recordsCount, this.deps);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return messageReplyFromError(error, 401);
|
|
40
|
+
}
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
await RecordsCountHandler.authorizeRecordsCount(tenant, recordsCount, this.deps);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
return messageReplyFromError(e, 401);
|
|
50
|
-
}
|
|
42
|
+
const filters = buildRecordsSnapshotFilters(recordsCount, visibility);
|
|
43
|
+
const recordLimit = await resolveRecordLimitOccupancy({
|
|
44
|
+
validationStateReader : this.deps.validationStateReader,
|
|
45
|
+
tenant,
|
|
46
|
+
recordsFilter : recordsCount.message.descriptor.filter,
|
|
47
|
+
messageTimestamp : recordsCount.message.descriptor.messageTimestamp,
|
|
48
|
+
});
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
count = await this.countRecordsAsOwnerDelegate(tenant, recordsCount, requester);
|
|
56
|
-
} else {
|
|
57
|
-
count = await this.countRecordsAsNonOwner(tenant, recordsCount, requester);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
50
|
+
const count = recordsCount.author === tenant && requester === tenant
|
|
51
|
+
? await this.countProjectedRecords(tenant, filters, recordLimit)
|
|
52
|
+
: await this.countProjectedRecordsForRequester(tenant, recordsCount, requester, filters, recordLimit);
|
|
60
53
|
|
|
61
54
|
return {
|
|
62
55
|
status: { code: 200, detail: 'OK' },
|
|
@@ -64,90 +57,6 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
64
57
|
};
|
|
65
58
|
}
|
|
66
59
|
|
|
67
|
-
/**
|
|
68
|
-
* Counts records as the owner of the DWN with no additional filtering.
|
|
69
|
-
*/
|
|
70
|
-
private async countRecordsAsOwner(tenant: string, recordsCount: RecordsCount): Promise<number> {
|
|
71
|
-
const { filter } = recordsCount.message.descriptor;
|
|
72
|
-
const countFilter = {
|
|
73
|
-
...Records.convertFilter(filter),
|
|
74
|
-
interface : DwnInterfaceName.Records,
|
|
75
|
-
method : DwnMethodName.Write,
|
|
76
|
-
isLatestBaseState : true
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const recordLimit = await this.resolveRecordLimit(tenant, recordsCount);
|
|
80
|
-
return this.countProjectedRecords(tenant, [countFilter], recordLimit);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private async countRecordsAsOwnerDelegate(
|
|
84
|
-
tenant: string,
|
|
85
|
-
recordsCount: RecordsCount,
|
|
86
|
-
requester: string | undefined,
|
|
87
|
-
): Promise<number> {
|
|
88
|
-
const { filter } = recordsCount.message.descriptor;
|
|
89
|
-
const countFilter = {
|
|
90
|
-
...Records.convertFilter(filter),
|
|
91
|
-
interface : DwnInterfaceName.Records,
|
|
92
|
-
method : DwnMethodName.Write,
|
|
93
|
-
isLatestBaseState : true
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
return this.countProjectedRecordsForRequester(tenant, recordsCount, requester, [countFilter]);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Counts records as a non-owner, applying the same filter logic as RecordsQuery.
|
|
101
|
-
*/
|
|
102
|
-
private async countRecordsAsNonOwner(
|
|
103
|
-
tenant: string,
|
|
104
|
-
recordsCount: RecordsCount,
|
|
105
|
-
requester: string | undefined,
|
|
106
|
-
): Promise<number> {
|
|
107
|
-
const { filter } = recordsCount.message.descriptor;
|
|
108
|
-
const filters: Filter[] = [];
|
|
109
|
-
|
|
110
|
-
if (Records.filterIncludesPublishedRecords(filter)) {
|
|
111
|
-
filters.push(RecordsCountHandler.buildPublishedRecordsFilter(recordsCount));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (Records.filterIncludesUnpublishedRecords(filter)) {
|
|
115
|
-
if (EncryptionControl.isExactAudienceFilter(filter)) {
|
|
116
|
-
filters.push(Records.buildUnpublishedControlRecordsFilter(filter));
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (Records.shouldBuildUnpublishedAuthorFilter(filter, recordsCount.author!)) {
|
|
120
|
-
filters.push(RecordsCountHandler.buildUnpublishedRecordsByCountAuthorFilter(recordsCount));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (Records.shouldProtocolAuthorize(recordsCount.signaturePayload!)) {
|
|
124
|
-
filters.push(RecordsCountHandler.buildUnpublishedProtocolAuthorizedRecordsFilter(recordsCount));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (Message.getPermissionGrantId(recordsCount.signaturePayload!) !== undefined) {
|
|
128
|
-
filters.push(RecordsCountHandler.buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsCount));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (Records.shouldBuildUnpublishedRecipientFilter(filter, recordsCount.author!)) {
|
|
132
|
-
filters.push(RecordsCountHandler.buildUnpublishedRecordsForCountAuthorFilter(recordsCount));
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return this.countProjectedRecordsForRequester(tenant, recordsCount, requester, filters);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Counts only published records.
|
|
141
|
-
*/
|
|
142
|
-
private async countPublishedRecords(
|
|
143
|
-
tenant: string,
|
|
144
|
-
recordsCount: RecordsCount,
|
|
145
|
-
requester: string | undefined,
|
|
146
|
-
): Promise<number> {
|
|
147
|
-
const filter = RecordsCountHandler.buildPublishedRecordsFilter(recordsCount);
|
|
148
|
-
return this.countProjectedRecordsForRequester(tenant, recordsCount, requester, [filter]);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
60
|
private async countProjectedRecords(
|
|
152
61
|
tenant: string,
|
|
153
62
|
filters: Filter[],
|
|
@@ -186,8 +95,8 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
186
95
|
recordsCount: RecordsCount,
|
|
187
96
|
requester: string | undefined,
|
|
188
97
|
filters: Filter[],
|
|
98
|
+
recordLimit: RecordLimitOccupancy | undefined,
|
|
189
99
|
): Promise<number> {
|
|
190
|
-
const recordLimit = await this.resolveRecordLimit(tenant, recordsCount);
|
|
191
100
|
const controlFilters = Records.buildControlRecordsFilters(filters);
|
|
192
101
|
if (controlFilters.length === 0) {
|
|
193
102
|
return this.countProjectedRecords(tenant, filters, recordLimit);
|
|
@@ -220,128 +129,4 @@ export class RecordsCountHandler implements MethodHandler {
|
|
|
220
129
|
});
|
|
221
130
|
return totalCount - controlCount + visibleMessages.length;
|
|
222
131
|
}
|
|
223
|
-
|
|
224
|
-
private async resolveRecordLimit(
|
|
225
|
-
tenant: string,
|
|
226
|
-
recordsCount: RecordsCount,
|
|
227
|
-
): Promise<RecordLimitOccupancy | undefined> {
|
|
228
|
-
return resolveRecordLimitOccupancy({
|
|
229
|
-
validationStateReader : this.deps.validationStateReader,
|
|
230
|
-
tenant,
|
|
231
|
-
recordsFilter : recordsCount.message.descriptor.filter,
|
|
232
|
-
messageTimestamp : recordsCount.message.descriptor.messageTimestamp,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
private static buildPublishedRecordsFilter(recordsCount: RecordsCount): Filter {
|
|
237
|
-
const { filter } = recordsCount.message.descriptor;
|
|
238
|
-
return {
|
|
239
|
-
...Records.convertFilter(filter),
|
|
240
|
-
interface : DwnInterfaceName.Records,
|
|
241
|
-
method : DwnMethodName.Write,
|
|
242
|
-
published : true,
|
|
243
|
-
isLatestBaseState : true
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Creates a filter for unpublished records that are intended for the count author (where `recipient` is the author).
|
|
249
|
-
*/
|
|
250
|
-
private static buildUnpublishedRecordsForCountAuthorFilter(recordsCount: RecordsCount): Filter {
|
|
251
|
-
const { filter } = recordsCount.message.descriptor;
|
|
252
|
-
return {
|
|
253
|
-
...Records.convertFilter(filter),
|
|
254
|
-
interface : DwnInterfaceName.Records,
|
|
255
|
-
method : DwnMethodName.Write,
|
|
256
|
-
recipient : recordsCount.author!,
|
|
257
|
-
isLatestBaseState : true,
|
|
258
|
-
published : false
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Creates a filter for unpublished records that are within the specified protocol.
|
|
264
|
-
*/
|
|
265
|
-
private static buildUnpublishedProtocolAuthorizedRecordsFilter(recordsCount: RecordsCount): Filter {
|
|
266
|
-
const { filter } = recordsCount.message.descriptor;
|
|
267
|
-
return {
|
|
268
|
-
...Records.convertFilter(filter),
|
|
269
|
-
interface : DwnInterfaceName.Records,
|
|
270
|
-
method : DwnMethodName.Write,
|
|
271
|
-
isLatestBaseState : true,
|
|
272
|
-
published : false
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Creates a filter for unpublished records authorized by a permission grant.
|
|
278
|
-
*/
|
|
279
|
-
private static buildUnpublishedPermissionGrantAuthorizedRecordsFilter(recordsCount: RecordsCount): Filter {
|
|
280
|
-
const { filter } = recordsCount.message.descriptor;
|
|
281
|
-
return {
|
|
282
|
-
...Records.convertFilter(filter),
|
|
283
|
-
interface : DwnInterfaceName.Records,
|
|
284
|
-
method : DwnMethodName.Write,
|
|
285
|
-
isLatestBaseState : true,
|
|
286
|
-
published : false
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Creates a filter for only unpublished records where the author is the same as the count author.
|
|
292
|
-
*/
|
|
293
|
-
private static buildUnpublishedRecordsByCountAuthorFilter(recordsCount: RecordsCount): Filter {
|
|
294
|
-
const { filter } = recordsCount.message.descriptor;
|
|
295
|
-
return {
|
|
296
|
-
...Records.convertFilter(filter),
|
|
297
|
-
author : recordsCount.author!,
|
|
298
|
-
interface : DwnInterfaceName.Records,
|
|
299
|
-
method : DwnMethodName.Write,
|
|
300
|
-
isLatestBaseState : true,
|
|
301
|
-
published : false
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* @param messageStore Used to check if the grant has been revoked.
|
|
307
|
-
*/
|
|
308
|
-
private static async authorizeRecordsCount(
|
|
309
|
-
tenant: string,
|
|
310
|
-
recordsCount: RecordsCount,
|
|
311
|
-
deps: HandlerDependencies,
|
|
312
|
-
): Promise<void> {
|
|
313
|
-
|
|
314
|
-
if (Message.isSignedByAuthorDelegate(recordsCount.message) &&
|
|
315
|
-
!EncryptionControl.filterTargetsOnlyControlRecords(recordsCount.message.descriptor.filter)) {
|
|
316
|
-
await recordsCount.authorizeDelegate(deps.validationStateReader);
|
|
317
|
-
} else if (EncryptionControl.filterTargetsOnlyControlRecords(recordsCount.message.descriptor.filter)) {
|
|
318
|
-
await EncryptionControl.authorizeControlReadRequest({
|
|
319
|
-
tenant,
|
|
320
|
-
incomingMessage : recordsCount.message,
|
|
321
|
-
requester : Message.getRequester(recordsCount.message),
|
|
322
|
-
validationStateReader : deps.validationStateReader,
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
const permissionGrantId = Message.getPermissionGrantId(recordsCount.signaturePayload!);
|
|
327
|
-
if (permissionGrantId !== undefined) {
|
|
328
|
-
const permissionGrant = await deps.validationStateReader.fetchGrant(tenant, permissionGrantId);
|
|
329
|
-
await RecordsGrantAuthorization.authorizeQueryOrSubscribe({
|
|
330
|
-
incomingMessage : recordsCount.message,
|
|
331
|
-
expectedGrantor : tenant,
|
|
332
|
-
expectedGrantee : recordsCount.author!,
|
|
333
|
-
permissionGrant,
|
|
334
|
-
validationStateReader : deps.validationStateReader,
|
|
335
|
-
});
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// NOTE: not all RecordsCount messages require protocol authorization even if the filter includes protocol-related fields,
|
|
340
|
-
// this is because we dynamically filter out records that the caller is not authorized to see.
|
|
341
|
-
// Currently only run protocol authorization if message deliberately invokes a protocol role.
|
|
342
|
-
if (Records.shouldProtocolAuthorize(recordsCount.signaturePayload!)) {
|
|
343
|
-
await ProtocolAuthorization.authorizeQueryOrSubscribe(tenant, recordsCount, deps.validationStateReader);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
132
|
}
|