@keetanetwork/anchor 0.0.62 → 0.0.63
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/lib/anchor-metadata-server.d.ts +54 -0
- package/lib/anchor-metadata-server.d.ts.map +1 -0
- package/lib/anchor-metadata-server.js +62 -0
- package/lib/anchor-metadata-server.js.map +1 -0
- package/lib/error.d.ts +35 -0
- package/lib/error.d.ts.map +1 -1
- package/lib/error.generated.d.ts +3 -0
- package/lib/error.generated.d.ts.map +1 -0
- package/lib/error.generated.js +104 -0
- package/lib/error.generated.js.map +1 -0
- package/lib/error.js +66 -1
- package/lib/error.js.map +1 -1
- package/lib/http-server/common.d.ts +20 -2
- package/lib/http-server/common.d.ts.map +1 -1
- package/lib/http-server/common.js +47 -0
- package/lib/http-server/common.js.map +1 -1
- package/lib/http-server/index.d.ts +22 -0
- package/lib/http-server/index.d.ts.map +1 -1
- package/lib/http-server/index.js +23 -0
- package/lib/http-server/index.js.map +1 -1
- package/lib/metadata.types.d.ts +40 -0
- package/lib/metadata.types.d.ts.map +1 -1
- package/lib/metadata.types.js.map +1 -1
- package/lib/resolver.d.ts +19 -20
- package/lib/resolver.d.ts.map +1 -1
- package/lib/resolver.js +1420 -681
- package/lib/resolver.js.map +1 -1
- package/lib/utils/certificate-network.d.ts +84 -0
- package/lib/utils/certificate-network.d.ts.map +1 -0
- package/lib/utils/certificate-network.js +104 -0
- package/lib/utils/certificate-network.js.map +1 -0
- package/lib/utils/signing.d.ts +12 -0
- package/lib/utils/signing.d.ts.map +1 -1
- package/lib/utils/signing.js +145 -2
- package/lib/utils/signing.js.map +1 -1
- package/npm-shrinkwrap.json +4 -4
- package/package.json +1 -1
- package/services/asset-movement/common.d.ts.map +1 -1
- package/services/asset-movement/common.js +7 -53
- package/services/asset-movement/common.js.map +1 -1
- package/services/asset-movement/server.d.ts +6 -4
- package/services/asset-movement/server.d.ts.map +1 -1
- package/services/asset-movement/server.js +3 -3
- package/services/asset-movement/server.js.map +1 -1
- package/services/fx/common.js +7 -7
- package/services/fx/server.d.ts +9 -7
- package/services/fx/server.d.ts.map +1 -1
- package/services/fx/server.js +5 -5
- package/services/fx/server.js.map +1 -1
- package/services/kyc/server.d.ts +6 -4
- package/services/kyc/server.d.ts.map +1 -1
- package/services/kyc/server.js +3 -3
- package/services/kyc/server.js.map +1 -1
- package/services/notification/common.d.ts +2 -1
- package/services/notification/common.d.ts.map +1 -1
- package/services/notification/common.generated.js +20 -20
- package/services/notification/common.js +3 -2
- package/services/notification/common.js.map +1 -1
- package/services/notification/server.d.ts +7 -6
- package/services/notification/server.d.ts.map +1 -1
- package/services/notification/server.js +25 -62
- package/services/notification/server.js.map +1 -1
- package/services/storage/common.d.ts +2 -1
- package/services/storage/common.d.ts.map +1 -1
- package/services/storage/common.js +7 -2
- package/services/storage/common.js.map +1 -1
- package/services/storage/server.d.ts +7 -5
- package/services/storage/server.d.ts.map +1 -1
- package/services/storage/server.js +29 -99
- package/services/storage/server.js.map +1 -1
- package/services/username/common.d.ts +2 -1
- package/services/username/common.d.ts.map +1 -1
- package/services/username/common.generated.js +14 -48
- package/services/username/common.generated.js.map +1 -1
- package/services/username/common.js +3 -2
- package/services/username/common.js.map +1 -1
- package/services/username/server.d.ts +7 -6
- package/services/username/server.d.ts.map +1 -1
- package/services/username/server.js +38 -46
- package/services/username/server.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/services/storage/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/services/storage/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qCAAqC,CAAC;AAC3F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAKX,kBAAkB,EAClB,WAAW,EAKX,UAAU,EAIV,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,KAAK,qBAAqB,MAAM,gCAAgC,CAAC;AAsB7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAQhF,KAAK,OAAO,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAyHzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,MAAM,WAAW,8BAA+B,SAAQ,+BAA+B;IACtF;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAErD;;;OAGG;IACH,OAAO,EAAE,kBAAkB,CAAC;IAE5B;;OAEG;IACH,aAAa,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAE9B;;OAEG;IACH,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAElC;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAElC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAEjC;;;OAGG;IACH,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IAEpC;;OAEG;IACH,aAAa,CAAC,EAAE;QACf,sDAAsD;QACtD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,+CAA+C;QAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qEAAqE;QACrE,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACF;AAkBD,qBAAa,+BAAgC,SAAQ,yBAAyB,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,8BAA8B,CAAC;IAC1K,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,8BAA8B,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,kBAAkB,EAAE,CAAC;IAC1C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,KAAK,CAAC;IAC1C,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7C,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC,8BAA8B,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;gBACnF,MAAM,EAAE,8BAA8B;cAwDlC,UAAU,CAAC,aAAa,EAAE,8BAA8B,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC;cAuhBhG,oBAAoB,IAAI,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;CAuB5G"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import * as KeetaAnchorHTTPServer from '../../lib/http-server/index.js';
|
|
2
|
-
import { KeetaNet } from '../../client/index.js';
|
|
3
|
-
import { KeetaAnchorUserError } from '../../lib/error.js';
|
|
4
1
|
import { assertKeetaStorageAnchorDeleteResponse, assertKeetaStorageAnchorPutResponse, assertKeetaStorageAnchorGetRequest, assertKeetaStorageAnchorSearchRequest, assertKeetaStorageAnchorSearchResponse, assertKeetaStorageAnchorQuotaResponse, assertKeetaStorageAnchorUpdateMetadataRequest } from './common.generated.js';
|
|
5
2
|
import { getKeetaStorageAnchorDeleteRequestSigningData, getKeetaStorageAnchorPutRequestSigningData, getKeetaStorageAnchorGetRequestSigningData, getKeetaStorageAnchorSearchRequestSigningData, getKeetaStorageAnchorQuotaRequestSigningData, getKeetaStorageAnchorUpdateMetadataRequestSigningData, parseContainerPayload, Errors, CONTENT_TYPE_OCTET_STREAM, DEFAULT_SIGNED_URL_TTL_SECONDS } from './common.js';
|
|
3
|
+
import { KeetaAnchorMetadataServer } from '../../lib/anchor-metadata-server.js';
|
|
6
4
|
import { VerifySignedData } from '../../lib/utils/signing.js';
|
|
7
|
-
import {
|
|
5
|
+
import { parseSignatureFromURL, verifyBodyAuth, verifyURLAuth } from '../../lib/http-server/common.js';
|
|
8
6
|
import { arrayBufferLikeToBuffer, Buffer } from '../../lib/utils/buffer.js';
|
|
9
7
|
import { requiresValidation, findMatchingValidators } from './lib/validators.js';
|
|
10
8
|
import { EncryptedContainer, EncryptedContainerError } from '../../lib/encrypted-container.js';
|
|
@@ -61,68 +59,6 @@ function parsePath(pathPolicies, path) {
|
|
|
61
59
|
}
|
|
62
60
|
throw (new Errors.InvalidPath('Path does not match any policy'));
|
|
63
61
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Verify a signed request from POST body.
|
|
66
|
-
* Extracts account and signature from the request, verifies the signature,
|
|
67
|
-
* and returns the authenticated account.
|
|
68
|
-
*
|
|
69
|
-
* @typeParam T - Request type containing optional account and signed fields
|
|
70
|
-
*
|
|
71
|
-
* @param request - The request object containing account and signed fields
|
|
72
|
-
* @param getSigningData - Function to extract signable data from the request
|
|
73
|
-
*
|
|
74
|
-
* @returns The authenticated account
|
|
75
|
-
*
|
|
76
|
-
* @throws KeetaAnchorUserError if authentication is missing or invalid
|
|
77
|
-
*/
|
|
78
|
-
async function verifyBodyAuth(request, getSigningData) {
|
|
79
|
-
if (!request.account || !request.signed) {
|
|
80
|
-
throw (new KeetaAnchorUserError('Authentication required'));
|
|
81
|
-
}
|
|
82
|
-
const account = KeetaNet.lib.Account.fromPublicKeyString(request.account).assertAccount();
|
|
83
|
-
const signable = getSigningData(request);
|
|
84
|
-
const signed = assertHTTPSignedField(request.signed);
|
|
85
|
-
const valid = await VerifySignedData(account, signable, signed);
|
|
86
|
-
if (!valid) {
|
|
87
|
-
throw (new KeetaAnchorUserError('Invalid signature'));
|
|
88
|
-
}
|
|
89
|
-
return (account);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Verify a signed request from URL query parameters.
|
|
93
|
-
* Parses signature from URL, builds a request object, verifies the signature,
|
|
94
|
-
* and returns the authenticated account.
|
|
95
|
-
*
|
|
96
|
-
* @typeParam T - Request type to build from the account public key
|
|
97
|
-
*
|
|
98
|
-
* @param url - The URL containing signature query parameters
|
|
99
|
-
* @param getSigningData - Function to extract signable data from the request
|
|
100
|
-
* @param buildRequest - Function to build a request object from the account public key
|
|
101
|
-
*
|
|
102
|
-
* @returns The authenticated account
|
|
103
|
-
*
|
|
104
|
-
* @throws KeetaAnchorUserError if authentication is missing or invalid
|
|
105
|
-
*/
|
|
106
|
-
async function verifyURLAuth(url, getSigningData, buildRequest) {
|
|
107
|
-
let urlString;
|
|
108
|
-
if (typeof url === 'string') {
|
|
109
|
-
urlString = url;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
urlString = url.href;
|
|
113
|
-
}
|
|
114
|
-
const parsed = parseSignatureFromURL(urlString);
|
|
115
|
-
if (!parsed.account || !parsed.signedField) {
|
|
116
|
-
throw (new KeetaAnchorUserError('Authentication required'));
|
|
117
|
-
}
|
|
118
|
-
const request = buildRequest(parsed.account.publicKeyString.get());
|
|
119
|
-
const signable = getSigningData(request);
|
|
120
|
-
const valid = await VerifySignedData(parsed.account, signable, parsed.signedField);
|
|
121
|
-
if (!valid) {
|
|
122
|
-
throw (new KeetaAnchorUserError('Invalid signature'));
|
|
123
|
-
}
|
|
124
|
-
return (parsed.account);
|
|
125
|
-
}
|
|
126
62
|
/**
|
|
127
63
|
* Extract object path from wildcard route parameter.
|
|
128
64
|
* Prepends a leading slash to create a valid storage path.
|
|
@@ -144,27 +80,16 @@ function extractObjectPath(params) {
|
|
|
144
80
|
* Authorize access to an object path via URL-signed request.
|
|
145
81
|
* Combines path validation, signature verification, and access control.
|
|
146
82
|
*
|
|
147
|
-
* @typeParam T - Request type to build from path and account
|
|
148
|
-
*
|
|
149
|
-
* @param pathPolicies - Array of path policies to check against
|
|
150
|
-
* @param params - Route parameters containing the wildcard path
|
|
151
|
-
* @param url - The URL containing signature query parameters
|
|
152
|
-
* @param operation - The operation being authorized
|
|
153
|
-
* @param getSigningData - Function to extract signable data from the request
|
|
154
|
-
* @param buildRequest - Function to build a request object from path and account
|
|
155
|
-
*
|
|
156
|
-
* @returns The authenticated account and validated object path
|
|
157
|
-
*
|
|
158
83
|
* @throws InvalidPath if path is invalid or doesn't match any policy
|
|
159
84
|
* @throws AccessDenied if user doesn't have access to the path
|
|
160
85
|
* @throws KeetaAnchorUserError if signature is invalid
|
|
161
86
|
*/
|
|
162
|
-
async function authorizeURLAccess(pathPolicies, params, url, operation,
|
|
87
|
+
async function authorizeURLAccess(pathPolicies, params, url, operation, getSignable, certificateChain) {
|
|
163
88
|
const objectPath = extractObjectPath(params);
|
|
164
89
|
const { policy, parsed } = parsePath(pathPolicies, objectPath);
|
|
165
|
-
const account = await verifyURLAuth(url,
|
|
166
|
-
return (
|
|
167
|
-
});
|
|
90
|
+
const account = await verifyURLAuth(url, function (account) {
|
|
91
|
+
return (getSignable(objectPath, account));
|
|
92
|
+
}, certificateChain);
|
|
168
93
|
if (!policy.checkAccess(account, parsed, operation)) {
|
|
169
94
|
throw (new Errors.AccessDenied('Can only access your own namespace'));
|
|
170
95
|
}
|
|
@@ -184,7 +109,7 @@ const DEFAULT_TAG_VALIDATION = {
|
|
|
184
109
|
maxTagLength: 50,
|
|
185
110
|
pattern: /^[a-zA-Z0-9_-]+$/
|
|
186
111
|
};
|
|
187
|
-
export class KeetaNetStorageAnchorHTTPServer extends
|
|
112
|
+
export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorMetadataServer {
|
|
188
113
|
homepage;
|
|
189
114
|
backend;
|
|
190
115
|
anchorAccount;
|
|
@@ -254,6 +179,7 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
254
179
|
const publicCorsOrigin = this.publicCorsOrigin;
|
|
255
180
|
const authenticatedCorsOrigin = this.authenticatedCorsOrigin;
|
|
256
181
|
const pathPolicies = this.pathPolicies;
|
|
182
|
+
const requireCertificateChain = this.resolvedCertificateChainRequirement;
|
|
257
183
|
const tagValidation = this.tagValidation;
|
|
258
184
|
const logger = this.logger;
|
|
259
185
|
/**
|
|
@@ -355,9 +281,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
355
281
|
return (t.length > 0);
|
|
356
282
|
});
|
|
357
283
|
// Verify signature
|
|
358
|
-
const account = await verifyURLAuth(url,
|
|
359
|
-
return ({ path: objectPath, visibility, tags: rawTags });
|
|
360
|
-
});
|
|
284
|
+
const account = await verifyURLAuth(url, function () {
|
|
285
|
+
return (getKeetaStorageAnchorPutRequestSigningData({ path: objectPath, visibility, tags: rawTags }));
|
|
286
|
+
}, requireCertificateChain);
|
|
361
287
|
validateTags(rawTags);
|
|
362
288
|
const tags = rawTags;
|
|
363
289
|
// Validate path format, metadata, and ownership
|
|
@@ -458,9 +384,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
458
384
|
};
|
|
459
385
|
// GET /api/object/* - Retrieve an object
|
|
460
386
|
routes['GET /api/object/**'] = async function (params, _postData, _headers, url) {
|
|
461
|
-
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'get',
|
|
462
|
-
return (assertKeetaStorageAnchorGetRequest({ path, account:
|
|
463
|
-
});
|
|
387
|
+
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'get', function (path, signingAccount) {
|
|
388
|
+
return (getKeetaStorageAnchorGetRequestSigningData(assertKeetaStorageAnchorGetRequest({ path, account: signingAccount.publicKeyString.get() })));
|
|
389
|
+
}, requireCertificateChain);
|
|
464
390
|
if (policy.validateContext) {
|
|
465
391
|
policy.validateContext(parsed, { operation: 'get', account });
|
|
466
392
|
}
|
|
@@ -477,9 +403,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
477
403
|
};
|
|
478
404
|
// DELETE /api/object/* - Delete an object
|
|
479
405
|
routes['DELETE /api/object/**'] = async function (params, _postData, _headers, url) {
|
|
480
|
-
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'delete',
|
|
481
|
-
return ({ path, account:
|
|
482
|
-
});
|
|
406
|
+
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'delete', function (path, signingAccount) {
|
|
407
|
+
return (getKeetaStorageAnchorDeleteRequestSigningData({ path, account: signingAccount.publicKeyString.get() }));
|
|
408
|
+
}, requireCertificateChain);
|
|
483
409
|
if (policy.validateContext) {
|
|
484
410
|
policy.validateContext(parsed, { operation: 'delete', account });
|
|
485
411
|
}
|
|
@@ -492,9 +418,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
492
418
|
};
|
|
493
419
|
// GET /api/metadata/* - Get object metadata
|
|
494
420
|
routes['GET /api/metadata/**'] = async function (params, _postData, _headers, url) {
|
|
495
|
-
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'metadata',
|
|
496
|
-
return (assertKeetaStorageAnchorGetRequest({ path, account:
|
|
497
|
-
});
|
|
421
|
+
const { objectPath, policy, parsed, account } = await authorizeURLAccess(pathPolicies, params, url, 'metadata', function (path, signingAccount) {
|
|
422
|
+
return (getKeetaStorageAnchorGetRequestSigningData(assertKeetaStorageAnchorGetRequest({ path, account: signingAccount.publicKeyString.get() })));
|
|
423
|
+
}, requireCertificateChain);
|
|
498
424
|
if (policy.validateContext) {
|
|
499
425
|
policy.validateContext(parsed, { operation: 'metadata', account });
|
|
500
426
|
}
|
|
@@ -510,7 +436,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
510
436
|
visibility: request.visibility,
|
|
511
437
|
tags: request.tags
|
|
512
438
|
});
|
|
513
|
-
const account = await verifyBodyAuth(request, function () {
|
|
439
|
+
const account = await verifyBodyAuth(request, function () {
|
|
440
|
+
return (signable);
|
|
441
|
+
}, requireCertificateChain);
|
|
514
442
|
const { policy, parsed } = assertPathAccess(pathPolicies, account, objectPath, 'updateMetadata');
|
|
515
443
|
validateTags(request.tags);
|
|
516
444
|
const visibility = assertVisibility(request.visibility);
|
|
@@ -532,7 +460,7 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
532
460
|
// POST /api/search - Search for objects
|
|
533
461
|
routes['POST /api/search'] = async function (_params, postData) {
|
|
534
462
|
const request = assertKeetaStorageAnchorSearchRequest(postData);
|
|
535
|
-
const account = await verifyBodyAuth(request, getKeetaStorageAnchorSearchRequestSigningData);
|
|
463
|
+
const account = await verifyBodyAuth(request, getKeetaStorageAnchorSearchRequestSigningData, requireCertificateChain);
|
|
536
464
|
const accountPubKey = account.publicKeyString.get();
|
|
537
465
|
// Check if searching for public objects outside namespace
|
|
538
466
|
const searchingPublic = request.criteria.visibility === 'public';
|
|
@@ -558,7 +486,7 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
558
486
|
};
|
|
559
487
|
// GET /api/quota - Get quota status
|
|
560
488
|
routes['GET /api/quota'] = async function (_params, _postData, _headers, url) {
|
|
561
|
-
const account = await verifyURLAuth(url,
|
|
489
|
+
const account = await verifyURLAuth(url, function () { return (getKeetaStorageAnchorQuotaRequestSigningData({})); }, requireCertificateChain);
|
|
562
490
|
// Get current usage from backend and compute remaining using per-user or global limits
|
|
563
491
|
const owner = account.publicKeyString.get();
|
|
564
492
|
let userLimits = null;
|
|
@@ -666,8 +594,9 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
666
594
|
// #endregion
|
|
667
595
|
return (routes);
|
|
668
596
|
}
|
|
669
|
-
async
|
|
597
|
+
async buildServiceMetadata() {
|
|
670
598
|
const authRequired = { options: { authentication: { type: 'required', method: 'keeta-account' } } };
|
|
599
|
+
const acceptedIssuerDNs = this.acceptedIssuerDNs();
|
|
671
600
|
const operations = {
|
|
672
601
|
put: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },
|
|
673
602
|
get: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },
|
|
@@ -683,7 +612,8 @@ export class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.Keeta
|
|
|
683
612
|
anchorAccount: this.anchorAccount.publicKeyString.get(),
|
|
684
613
|
quotas: this.quotas,
|
|
685
614
|
signedUrlDefaultTTL: this.signedUrlDefaultTTL,
|
|
686
|
-
searchableFields: ['owner', 'tags', 'visibility', 'pathPrefix']
|
|
615
|
+
searchableFields: ['owner', 'tags', 'visibility', 'pathPrefix'],
|
|
616
|
+
...(acceptedIssuerDNs !== undefined && { acceptedIssuerDNs })
|
|
687
617
|
});
|
|
688
618
|
}
|
|
689
619
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/services/storage/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServiceMetadata } from '../../lib/resolver.ts';\nimport type { Signable } from '../../lib/utils/signing.js';\nimport type { NamespaceValidator } from './lib/validators.js';\nimport * as KeetaAnchorHTTPServer from '../../lib/http-server/index.js';\nimport { KeetaNet } from '../../client/index.js';\nimport {\n\tKeetaAnchorUserError\n} from '../../lib/error.js';\nimport type {\n\tKeetaStorageAnchorDeleteResponse,\n\tKeetaStorageAnchorPutResponse,\n\tKeetaStorageAnchorSearchResponse,\n\tKeetaStorageAnchorQuotaResponse,\n\tFullStorageBackend,\n\tQuotaConfig,\n\tQuotaLimits,\n\tStorageOperation,\n\tStorageObjectVisibility,\n\tStorageObjectMetadata,\n\tPathPolicy,\n\tSearchResults,\n\tStorageGetResult,\n\tSearchPagination\n} from './common.ts';\nimport {\n\tassertKeetaStorageAnchorDeleteResponse,\n\tassertKeetaStorageAnchorPutResponse,\n\tassertKeetaStorageAnchorGetRequest,\n\tassertKeetaStorageAnchorSearchRequest,\n\tassertKeetaStorageAnchorSearchResponse,\n\tassertKeetaStorageAnchorQuotaResponse,\n\tassertKeetaStorageAnchorUpdateMetadataRequest\n} from './common.generated.js';\nimport {\n\tgetKeetaStorageAnchorDeleteRequestSigningData,\n\tgetKeetaStorageAnchorPutRequestSigningData,\n\tgetKeetaStorageAnchorGetRequestSigningData,\n\tgetKeetaStorageAnchorSearchRequestSigningData,\n\tgetKeetaStorageAnchorQuotaRequestSigningData,\n\tgetKeetaStorageAnchorUpdateMetadataRequestSigningData,\n\tparseContainerPayload,\n\tErrors,\n\tCONTENT_TYPE_OCTET_STREAM,\n\tDEFAULT_SIGNED_URL_TTL_SECONDS\n} from './common.js';\nimport { VerifySignedData } from '../../lib/utils/signing.js';\nimport { assertHTTPSignedField, parseSignatureFromURL } from '../../lib/http-server/common.js';\nimport { arrayBufferLikeToBuffer, Buffer } from '../../lib/utils/buffer.js';\nimport { requiresValidation, findMatchingValidators } from './lib/validators.js';\nimport { EncryptedContainer, EncryptedContainerError } from '../../lib/encrypted-container.js';\nimport { assertVisibility } from './utils.js';\n\ntype Account = InstanceType<typeof KeetaNet.lib.Account>;\n\n/**\n * Build a standardized search response from search results.\n */\nfunction buildSearchResponse(results: SearchResults): KeetaStorageAnchorSearchResponse {\n\tconst response: KeetaStorageAnchorSearchResponse = {\n\t\tok: true,\n\t\tresults: results.results\n\t};\n\tif (results.nextCursor !== undefined) {\n\t\tresponse.nextCursor = results.nextCursor;\n\t}\n\treturn(response);\n}\n\n// #region Module-Level Helpers\n\n/**\n * Find a matching policy for a path, validate it, and check access.\n *\n * @param pathPolicies - Array of path policies to check against\n * @param account - The account to check access for\n * @param path - The path to check\n * @param operation - The operation being performed\n */\nfunction assertPathAccess(\n\tpathPolicies: PathPolicy<unknown>[],\n\taccount: Account,\n\tpath: string,\n\toperation: StorageOperation\n): { policy: PathPolicy<unknown>; parsed: unknown } {\n\tfor (const policy of pathPolicies) {\n\t\tconst parsed = policy.parse(path);\n\t\tif (parsed !== null) {\n\t\t\tpolicy.validate(path);\n\n\t\t\tif (!policy.checkAccess(account, parsed, operation)) {\n\t\t\t\tthrow(new Errors.AccessDenied('Can only access your own namespace'));\n\t\t\t}\n\n\t\t\treturn({ policy, parsed });\n\t\t}\n\t}\n\n\tthrow(new Errors.InvalidPath('Path does not match any policy'));\n}\n\n/**\n * Find a matching policy and parse a path.\n * Used for public endpoints where auth is optional.\n *\n * @param pathPolicies - Array of path policies to check against\n * @param path - The path to parse\n */\nfunction parsePath(\n\tpathPolicies: PathPolicy<unknown>[],\n\tpath: string\n): { policy: PathPolicy<unknown>; parsed: unknown } {\n\tfor (const policy of pathPolicies) {\n\t\tconst parsed = policy.parse(path);\n\t\tif (parsed !== null) {\n\t\t\tpolicy.validate(path);\n\t\t\treturn({ policy, parsed });\n\t\t}\n\t}\n\n\tthrow(new Errors.InvalidPath('Path does not match any policy'));\n}\n\n/**\n * Verify a signed request from POST body.\n * Extracts account and signature from the request, verifies the signature,\n * and returns the authenticated account.\n *\n * @typeParam T - Request type containing optional account and signed fields\n *\n * @param request - The request object containing account and signed fields\n * @param getSigningData - Function to extract signable data from the request\n *\n * @returns The authenticated account\n *\n * @throws KeetaAnchorUserError if authentication is missing or invalid\n */\nasync function verifyBodyAuth<T extends { account?: string; signed?: unknown }>(\n\trequest: T,\n\tgetSigningData: (req: T) => Signable\n): Promise<Account> {\n\tif (!request.account || !request.signed) {\n\t\tthrow(new KeetaAnchorUserError('Authentication required'));\n\t}\n\n\tconst account = KeetaNet.lib.Account.fromPublicKeyString(request.account).assertAccount();\n\tconst signable = getSigningData(request);\n\tconst signed = assertHTTPSignedField(request.signed);\n\n\tconst valid = await VerifySignedData(account, signable, signed);\n\tif (!valid) {\n\t\tthrow(new KeetaAnchorUserError('Invalid signature'));\n\t}\n\n\treturn(account);\n}\n\n/**\n * Verify a signed request from URL query parameters.\n * Parses signature from URL, builds a request object, verifies the signature,\n * and returns the authenticated account.\n *\n * @typeParam T - Request type to build from the account public key\n *\n * @param url - The URL containing signature query parameters\n * @param getSigningData - Function to extract signable data from the request\n * @param buildRequest - Function to build a request object from the account public key\n *\n * @returns The authenticated account\n *\n * @throws KeetaAnchorUserError if authentication is missing or invalid\n */\nasync function verifyURLAuth<T>(\n\turl: URL | string,\n\tgetSigningData: (req: T) => Signable,\n\tbuildRequest: (accountPubKey: string) => T\n): Promise<Account> {\n\tlet urlString: string;\n\tif (typeof url === 'string') {\n\t\turlString = url;\n\t} else {\n\t\turlString = url.href;\n\t}\n\n\tconst parsed = parseSignatureFromURL(urlString);\n\tif (!parsed.account || !parsed.signedField) {\n\t\tthrow(new KeetaAnchorUserError('Authentication required'));\n\t}\n\n\tconst request = buildRequest(parsed.account.publicKeyString.get());\n\tconst signable = getSigningData(request);\n\n\tconst valid = await VerifySignedData(parsed.account, signable, parsed.signedField);\n\tif (!valid) {\n\t\tthrow(new KeetaAnchorUserError('Invalid signature'));\n\t}\n\n\treturn(parsed.account);\n}\n\n/**\n * Extract object path from wildcard route parameter.\n * Prepends a leading slash to create a valid storage path.\n *\n * @param params - Route parameters containing the wildcard match\n *\n * @returns The object path with leading slash\n *\n * @throws InvalidPath if wildcard parameter is missing\n */\nfunction extractObjectPath(params: Map<string, string>): string {\n\tconst wildcardPath = params.get('**');\n\tif (!wildcardPath) {\n\t\tthrow(new Errors.InvalidPath());\n\t}\n\n\treturn('/' + wildcardPath);\n}\n\n/**\n * Authorize access to an object path via URL-signed request.\n * Combines path validation, signature verification, and access control.\n *\n * @typeParam T - Request type to build from path and account\n *\n * @param pathPolicies - Array of path policies to check against\n * @param params - Route parameters containing the wildcard path\n * @param url - The URL containing signature query parameters\n * @param operation - The operation being authorized\n * @param getSigningData - Function to extract signable data from the request\n * @param buildRequest - Function to build a request object from path and account\n *\n * @returns The authenticated account and validated object path\n *\n * @throws InvalidPath if path is invalid or doesn't match any policy\n * @throws AccessDenied if user doesn't have access to the path\n * @throws KeetaAnchorUserError if signature is invalid\n */\nasync function authorizeURLAccess<T>(\n\tpathPolicies: PathPolicy<unknown>[],\n\tparams: Map<string, string>,\n\turl: URL | string,\n\toperation: StorageOperation,\n\tgetSigningData: (req: T) => Signable,\n\tbuildRequest: (path: string, accountPubKey: string) => T\n): Promise<{ account: Account; objectPath: string; policy: PathPolicy<unknown>; parsed: unknown }> {\n\tconst objectPath = extractObjectPath(params);\n\tconst { policy, parsed } = parsePath(pathPolicies, objectPath);\n\n\tconst account = await verifyURLAuth(url, getSigningData, function(pubKey) {\n\t\treturn(buildRequest(objectPath, pubKey));\n\t});\n\n\tif (!policy.checkAccess(account, parsed, operation)) {\n\t\tthrow(new Errors.AccessDenied('Can only access your own namespace'));\n\t}\n\n\treturn({ account, objectPath, policy, parsed });\n}\n\n// #endregion\n\n/**\n * Configuration for the Storage Anchor\n *\n * The Storage Anchor provides encrypted object storage with the following operations:\n *\n * PUT (Create/Update):\n * 1. Client creates EncryptedContainer with data, shares with anchor for public objects\n * 2. Client signs request (path, visibility, tags) and sends to server\n * 3. Server reserves quota, validates, stores object, commits reservation\n *\n * GET (Retrieve):\n * 1. Client signs request (path) and sends to server\n * 2. Server verifies access, returns EncryptedContainer\n * 3. Client decrypts with their private key\n *\n * DELETE:\n * 1. Client signs request (path) and sends to server\n * 2. Server verifies ownership, removes object\n *\n * SEARCH:\n * 1. Client signs request with criteria (tags, prefix, etc.)\n * 2. Server returns matching metadata (scoped to user's namespace)\n *\n * PUBLIC ACCESS (Pre-signed URLs):\n * 1. Client generates pre-signed URL with expiry, signed by owner\n * 2. Anyone can fetch via URL (no auth headers)\n * 3. Server verifies signature, expiry, and visibility\n * 4. Server decrypts and returns plaintext content\n *\n *\n * +-------------------+ +---------------------+ +------------------+\n * | Client | | Storage Anchor | | Storage Backend |\n * +-------------------+ +---------------------+ +------------------+\n * | | |\n * (PUT) Create EncryptedContainer | |\n * | Sign(path, visibility, tags) | |\n * |---------------------------------->| |\n * | | reserveUpload() ---------------->|\n * | | validate, put() ---------------->|\n * | | commitUpload() ----------------->|\n * |<--------------------------------- | { ok: true, object: metadata } |\n * | | |\n * (GET) Sign(path) ------------------------>| |\n * | | get() -------------------------->|\n * |<--------------------------------- | EncryptedContainer (binary) |\n * | Decrypt with private key | |\n * | | |\n * (PUBLIC) Generate pre-signed URL | |\n * | URL with expires, signature | |\n * (Anyone) Fetch URL ---------------------->| |\n * | | verify signature, expiry |\n * | | get(), decrypt ----------------->|\n * |<--------------------------------- | Plaintext content |\n */\nexport interface KeetaAnchorStorageServerConfig extends KeetaAnchorHTTPServer.KeetaAnchorHTTPServerConfig {\n\t/**\n\t * The data to use for the index page (optional)\n\t */\n\thomepage?: string | (() => Promise<string> | string);\n\n\t/**\n\t * The storage backend to use for storing documents.\n\t * Must implement full capabilities: CRUD, search, and quota management.\n\t */\n\tbackend: FullStorageBackend;\n\n\t/**\n\t * The anchor's account for decrypting objects.\n\t */\n\tanchorAccount: Account;\n\n\t/**\n\t * Quota configuration for storage limits.\n\t * Partial values are merged with defaults.\n\t */\n\tquotas?: Partial<QuotaConfig>;\n\n\t/**\n\t * Namespace validators for special paths\n\t */\n\tvalidators?: NamespaceValidator[];\n\n\t/**\n\t * Default TTL in seconds for pre-signed URLs (default: 3600)\n\t */\n\tsignedUrlDefaultTTL?: number;\n\n\t/**\n\t * CORS origin for public endpoints (default: false).\n\t * - '*' allows all origins\n\t * - specific origin string restricts to that origin\n\t * - false (default) disables CORS headers on public responses\n\t */\n\tpublicCORSOrigin?: string | false;\n\n\t/**\n\t * CORS origin for authenticated object endpoints (default: '*').\n\t * - '*' allows all origins\n\t * - specific origin string restricts to that origin\n\t */\n\tauthenticatedCORSOrigin?: string;\n\n\t/**\n\t * Path policies for parsing, validating, and access control of storage paths.\n\t * Each policy handles a specific path pattern. First matching policy wins.\n\t */\n\tpathPolicies: PathPolicy<unknown>[];\n\n\t/**\n\t * Tag validation configuration.\n\t */\n\ttagValidation?: {\n\t\t/** Maximum number of tags per object (default: 10) */\n\t\tmaxTags?: number;\n\t\t/** Maximum length of each tag (default: 50) */\n\t\tmaxTagLength?: number;\n\t\t/** Pattern for valid tag characters (default: /^[a-zA-Z0-9_-]+$/) */\n\t\tpattern?: RegExp;\n\t};\n}\n\n// Default quota configuration\nconst DEFAULT_QUOTAS: QuotaConfig = {\n\tmaxObjectSize: 10 * 1024 * 1024, // 10MB\n\tmaxObjectsPerUser: 1000,\n\tmaxStoragePerUser: 100 * 1024 * 1024, // 100MB\n\tmaxSearchLimit: 100,\n\tmaxSignedUrlTTL: 86400 // 24 hours\n};\n\n// Default tag validation configuration\nconst DEFAULT_TAG_VALIDATION = {\n\tmaxTags: 10,\n\tmaxTagLength: 50,\n\tpattern: /^[a-zA-Z0-9_-]+$/\n};\n\nexport class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorHTTPServer.KeetaNetAnchorHTTPServer<KeetaAnchorStorageServerConfig> {\n\treadonly homepage: NonNullable<KeetaAnchorStorageServerConfig['homepage']>;\n\treadonly backend: FullStorageBackend;\n\treadonly anchorAccount: Account;\n\treadonly quotas: QuotaConfig;\n\treadonly validators: NamespaceValidator[];\n\treadonly signedUrlDefaultTTL: number;\n\treadonly publicCorsOrigin: string | false;\n\treadonly authenticatedCorsOrigin: string;\n\treadonly pathPolicies: PathPolicy<unknown>[];\n\treadonly tagValidation: Required<NonNullable<KeetaAnchorStorageServerConfig['tagValidation']>>;\n\n\tconstructor(config: KeetaAnchorStorageServerConfig) {\n\t\tsuper(config);\n\n\t\tthis.homepage = config.homepage ?? '';\n\t\tthis.backend = config.backend;\n\t\tthis.anchorAccount = config.anchorAccount;\n\t\tthis.quotas = { ...DEFAULT_QUOTAS, ...config.quotas };\n\t\tthis.validators = config.validators ?? [];\n\t\tthis.signedUrlDefaultTTL = config.signedUrlDefaultTTL ?? DEFAULT_SIGNED_URL_TTL_SECONDS;\n\t\tthis.publicCorsOrigin = config.publicCORSOrigin ?? false;\n\t\tthis.authenticatedCorsOrigin = config.authenticatedCORSOrigin ?? '*';\n\t\tthis.pathPolicies = config.pathPolicies;\n\t\tthis.tagValidation = {\n\t\t\tmaxTags: config.tagValidation?.maxTags ?? DEFAULT_TAG_VALIDATION.maxTags,\n\t\t\tmaxTagLength: config.tagValidation?.maxTagLength ?? DEFAULT_TAG_VALIDATION.maxTagLength,\n\t\t\tpattern: config.tagValidation?.pattern ?? DEFAULT_TAG_VALIDATION.pattern\n\t\t};\n\n\t\t// Validate anchorAccount has private key\n\t\tif (!this.anchorAccount.hasPrivateKey) {\n\t\t\tthrow(new Error('anchorAccount must have a private key'));\n\t\t}\n\n\t\t// Validate at least one path policy is provided\n\t\tif (this.pathPolicies.length === 0) {\n\t\t\tthrow(new Error('At least one path policy must be provided'));\n\t\t}\n\n\t\t// Validate quota configuration values are positive\n\t\tif (this.quotas.maxObjectSize <= 0) {\n\t\t\tthrow(new Error('quotas.maxObjectSize must be positive'));\n\t\t}\n\t\tif (this.quotas.maxObjectsPerUser <= 0) {\n\t\t\tthrow(new Error('quotas.maxObjectsPerUser must be positive'));\n\t\t}\n\t\tif (this.quotas.maxStoragePerUser <= 0) {\n\t\t\tthrow(new Error('quotas.maxStoragePerUser must be positive'));\n\t\t}\n\t\tif (this.quotas.maxSearchLimit <= 0) {\n\t\t\tthrow(new Error('quotas.maxSearchLimit must be positive'));\n\t\t}\n\t\tif (this.quotas.maxSignedUrlTTL <= 0) {\n\t\t\tthrow(new Error('quotas.maxSignedUrlTTL must be positive'));\n\t\t}\n\n\t\t// Validate tag validation configuration\n\t\tif (this.tagValidation.maxTags <= 0) {\n\t\t\tthrow(new Error('tagValidation.maxTags must be positive'));\n\t\t}\n\t\tif (this.tagValidation.maxTagLength <= 0) {\n\t\t\tthrow(new Error('tagValidation.maxTagLength must be positive'));\n\t\t}\n\t}\n\n\t// Note: We use this.* properties instead of config.*.\n\t// The config parameter is required by the abstract method signature but unused here.\n\tprotected async initRoutes(_ignoreConfig: KeetaAnchorStorageServerConfig): Promise<KeetaAnchorHTTPServer.Routes> {\n\t\tconst routes: KeetaAnchorHTTPServer.Routes = {};\n\t\tconst backend = this.backend;\n\t\tconst anchorAccount = this.anchorAccount;\n\t\tconst quotas = this.quotas;\n\t\tconst validators = this.validators;\n\t\tconst publicCorsOrigin = this.publicCorsOrigin;\n\t\tconst authenticatedCorsOrigin = this.authenticatedCorsOrigin;\n\t\tconst pathPolicies = this.pathPolicies;\n\t\tconst tagValidation = this.tagValidation;\n\t\tconst logger = this.logger;\n\n\t\t/**\n\t\t * Validate tags against configured limits.\n\t\t * @throws Errors.InvalidTag if any tag violates constraints\n\t\t */\n\t\tfunction validateTags(tags: string[]): void {\n\t\t\tconst { maxTags, maxTagLength, pattern: tagPattern } = tagValidation;\n\t\t\tfor (const tag of tags) {\n\t\t\t\tif (tag.length > maxTagLength) {\n\t\t\t\t\tthrow(new Errors.InvalidTag(`Tag exceeds maximum length of ${maxTagLength}: \"${tag}\"`));\n\t\t\t\t}\n\t\t\t\ttagPattern.lastIndex = 0;\n\t\t\t\tif (!tagPattern.test(tag)) {\n\t\t\t\t\tthrow(new Errors.InvalidTag(`Tag contains invalid characters: \"${tag}\"`));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tags.length > maxTags) {\n\t\t\t\tthrow(new Errors.InvalidTag(`Too many tags: ${tags.length} exceeds maximum of ${maxTags}`));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Build a JSON response with assertion.\n\t\t */\n\t\tfunction jsonResponse<T>(response: T, assertionHandler: (input: unknown) => T): { output: string } {\n\t\t\treturn({ output: JSON.stringify(assertionHandler(response)) });\n\t\t}\n\n\t\t/**\n\t\t * Get an object or throw DocumentNotFound.\n\t\t */\n\t\tasync function requireObject(path: string): Promise<StorageGetResult> {\n\t\t\tconst result = await backend.get(path);\n\t\t\tif (!result) {\n\t\t\t\tthrow(new Errors.DocumentNotFound());\n\t\t\t}\n\n\t\t\treturn(result);\n\t\t}\n\n\t\t/**\n\t\t * Enforce server-side search limit cap.\n\t\t */\n\t\tfunction enforceSearchLimit(pagination: SearchPagination | undefined): SearchPagination {\n\t\t\tconst requestedLimit = pagination?.limit ?? quotas.maxSearchLimit;\n\t\t\treturn({ ...pagination, limit: Math.min(requestedLimit, quotas.maxSearchLimit) });\n\t\t}\n\n\t\t/**\n\t\t * Validate search results match expected constraints.\n\t\t */\n\t\tfunction assertSearchResults(\n\t\t\tresults: SearchResults,\n\t\t\tconstraint: { visibility?: 'public'; owner?: string }\n\t\t): void {\n\t\t\tfor (const obj of results.results) {\n\t\t\t\tif (constraint.visibility && obj.visibility !== constraint.visibility) {\n\t\t\t\t\tthrow(new Errors.InvariantViolation(\n\t\t\t\t\t\t`Backend returned ${obj.visibility} object in ${constraint.visibility} search`\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t\tif (constraint.owner && obj.owner !== constraint.owner) {\n\t\t\t\t\tthrow(new Errors.InvariantViolation(\n\t\t\t\t\t\t`Backend returned object owned by ${obj.owner} in search for ${constraint.owner}`\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * If a homepage is provided, setup the route for it\n\t\t */\n\t\tconst homepage = this.homepage;\n\t\tif (homepage) {\n\t\t\troutes['GET /'] = async function() {\n\t\t\t\tlet homepageData: string;\n\t\t\t\tif (typeof homepage === 'string') {\n\t\t\t\t\thomepageData = homepage;\n\t\t\t\t} else {\n\t\t\t\t\thomepageData = await homepage();\n\t\t\t\t}\n\n\t\t\t\treturn({\n\t\t\t\t\toutput: homepageData,\n\t\t\t\t\tcontentType: 'text/html'\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\t// #region API Routes\n\n\t\t// PUT /api/object/* - Create or update an object\n\t\troutes['PUT /api/object/**'] = {\n\t\t\tbodyType: 'raw',\n\t\t\tmaxBodySize: quotas.maxObjectSize,\n\t\t\thandler: async function(params, postData, _headers, url) {\n\t\t\t\tconst objectPath = extractObjectPath(params);\n\n\t\t\t\t// Get metadata from query params\n\t\t\t\tconst parsedUrl = new URL(url);\n\t\t\t\tconst visibilityParam = parsedUrl.searchParams.get('visibility');\n\t\t\t\tconst tagsParam = parsedUrl.searchParams.get('tags');\n\n\t\t\t\t// Default to private when absent, assert valid value otherwise\n\t\t\t\tlet visibility: StorageObjectVisibility = 'private';\n\t\t\t\tif (visibilityParam !== null) {\n\t\t\t\t\tvisibility = assertVisibility(visibilityParam);\n\t\t\t\t}\n\n\t\t\t\tconst rawTags: string[] = (tagsParam ?? '')\n\t\t\t\t\t.split(',')\n\t\t\t\t\t.map(function(t) {\n\t\t\t\t\t\treturn(t.trim());\n\t\t\t\t\t})\n\t\t\t\t\t.filter(function(t) {\n\t\t\t\t\t\treturn(t.length > 0);\n\t\t\t\t\t});\n\n\t\t\t\t// Verify signature\n\t\t\t\tconst account = await verifyURLAuth(url, getKeetaStorageAnchorPutRequestSigningData, function() {\n\t\t\t\t\treturn({ path: objectPath, visibility, tags: rawTags });\n\t\t\t\t});\n\n\t\t\t\tvalidateTags(rawTags);\n\t\t\t\tconst tags = rawTags;\n\n\t\t\t\t// Validate path format, metadata, and ownership\n\t\t\t\tconst { policy, parsed } = assertPathAccess(pathPolicies, account, objectPath, 'put');\n\t\t\t\tconst owner = policy.getOwner(objectPath);\n\n\t\t\t\t// Resolve per-user quota limits, falling back to global config\n\t\t\t\tlet userLimits: QuotaLimits | null = null;\n\t\t\t\tif (backend.getQuotaLimits) {\n\t\t\t\t\tuserLimits = await backend.getQuotaLimits(owner);\n\t\t\t\t}\n\n\t\t\t\tconst effectiveLimits = userLimits ?? quotas;\n\t\t\t\t// Body is raw binary (EncryptedContainer)\n\t\t\t\tconst data = arrayBufferLikeToBuffer(postData);\n\t\t\t\tconst objectSize = data.byteLength;\n\t\t\t\tif (objectSize > effectiveLimits.maxObjectSize) {\n\t\t\t\t\tthrow(new Errors.QuotaExceeded({\n\t\t\t\t\t\tquotaType: 'maxObjectSize',\n\t\t\t\t\t\tlimit: effectiveLimits.maxObjectSize,\n\t\t\t\t\t\tcurrent: objectSize\n\t\t\t\t\t}));\n\t\t\t\t}\n\n\t\t\t\tconst needsValidation = requiresValidation(objectPath, validators);\n\t\t\t\tconst needsContextValidation = !!policy.validateContext;\n\t\t\t\tconst needsAnchorDecryption = needsValidation || visibility === 'public';\n\t\t\t\tif (needsContextValidation || needsAnchorDecryption) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst container = EncryptedContainer.fromEncryptedBuffer(data, [anchorAccount]);\n\n\t\t\t\t\t\tif (policy.validateContext) {\n\t\t\t\t\t\t\tpolicy.validateContext(parsed, { operation: 'put', account, metadata: { owner, tags, visibility }, container });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (needsAnchorDecryption) {\n\t\t\t\t\t\t\tconst plaintext = await container.getPlaintext();\n\t\t\t\t\t\t\tif (needsValidation) {\n\t\t\t\t\t\t\t\t// Extract content and mimeType from encrypted payload\n\t\t\t\t\t\t\t\tconst { content, mimeType } = parseContainerPayload(plaintext);\n\t\t\t\t\t\t\t\tconst matchingValidators = findMatchingValidators(objectPath, validators);\n\t\t\t\t\t\t\t\tfor (const validator of matchingValidators) {\n\t\t\t\t\t\t\t\t\tconst result = await validator.validate(objectPath, content, mimeType);\n\t\t\t\t\t\t\t\t\tif (!result.valid) {\n\t\t\t\t\t\t\t\t\t\tthrow(new Errors.ValidationFailed(result.error));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (Errors.InvalidMetadata.isInstance(e)) {\n\t\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Errors.ValidationFailed.isInstance(e)) {\n\t\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (EncryptedContainerError.isInstance(e)) {\n\t\t\t\t\t\t\tif (e.code.startsWith('MALFORMED_')) {\n\t\t\t\t\t\t\t\tthrow(new Errors.ValidationFailed(`Invalid encrypted container: ${e.message}`));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (e.code === 'NO_MATCHING_KEY' || e.code === 'DECRYPTION_FAILED') {\n\t\t\t\t\t\t\t\tthrow(new Errors.AnchorPrincipalRequired());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Reserve quota before upload\n\t\t\t\tconst reservation = await backend.reserveUpload(owner, objectPath, objectSize, {\n\t\t\t\t\tquotaLimits: {\n\t\t\t\t\t\tmaxObjectsPerUser: effectiveLimits.maxObjectsPerUser,\n\t\t\t\t\t\tmaxStoragePerUser: effectiveLimits.maxStoragePerUser\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tlet objectMetadata: StorageObjectMetadata;\n\t\t\t\ttry {\n\t\t\t\t\tobjectMetadata = await backend.put(objectPath, data, {\n\t\t\t\t\t\towner,\n\t\t\t\t\t\ttags,\n\t\t\t\t\t\tvisibility\n\t\t\t\t\t});\n\n\t\t\t\t\tawait backend.commitUpload(reservation.id);\n\t\t\t\t} catch (e) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait backend.releaseUpload(reservation.id);\n\t\t\t\t\t} catch (releaseError) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * This provides a hint for cleanup\n\t\t\t\t\t\t */\n\t\t\t\t\t\tlogger?.warn('Failed to release upload reservation', { reservationId: reservation.id, error: releaseError });\n\t\t\t\t\t}\n\t\t\t\t\tthrow(e);\n\t\t\t\t}\n\n\t\t\t\tconst response: KeetaStorageAnchorPutResponse = {\n\t\t\t\t\tok: true,\n\t\t\t\t\tobject: objectMetadata\n\t\t\t\t};\n\n\t\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorPutResponse));\n\t\t\t}\n\t\t};\n\n\t\t// GET /api/object/* - Retrieve an object\n\t\troutes['GET /api/object/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'get',\n\t\t\t\tgetKeetaStorageAnchorGetRequestSigningData,\n\t\t\t\tfunction(path, pubKey) {\n\t\t\t\t\treturn(assertKeetaStorageAnchorGetRequest({ path, account: pubKey }));\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'get', account });\n\t\t\t}\n\n\t\t\tconst headers: { [key: string]: string } = {};\n\t\t\tif (authenticatedCorsOrigin) {\n\t\t\t\theaders['Access-Control-Allow-Origin'] = authenticatedCorsOrigin;\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\treturn({\n\t\t\t\theaders,\n\t\t\t\toutput: result.data,\n\t\t\t\tcontentType: CONTENT_TYPE_OCTET_STREAM\n\t\t\t});\n\t\t};\n\n\t\t// DELETE /api/object/* - Delete an object\n\t\troutes['DELETE /api/object/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'delete',\n\t\t\t\tgetKeetaStorageAnchorDeleteRequestSigningData,\n\t\t\t\tfunction(path, pubKey) {\n\t\t\t\t\treturn({ path, account: pubKey });\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'delete', account });\n\t\t\t}\n\n\t\t\tconst deleted = await backend.delete(objectPath);\n\t\t\tconst response: KeetaStorageAnchorDeleteResponse = {\n\t\t\t\tok: true,\n\t\t\t\tdeleted\n\t\t\t};\n\n\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorDeleteResponse));\n\t\t};\n\n\t\t// GET /api/metadata/* - Get object metadata\n\t\troutes['GET /api/metadata/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'metadata',\n\t\t\t\tgetKeetaStorageAnchorGetRequestSigningData,\n\t\t\t\tfunction(path, pubKey) {\n\t\t\t\t\treturn(assertKeetaStorageAnchorGetRequest({ path, account: pubKey }));\n\t\t\t\t}\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'metadata', account });\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\treturn(jsonResponse({ ok: true, object: result.metadata }, assertKeetaStorageAnchorPutResponse));\n\t\t};\n\n\t\t// PUT /api/metadata/* - Update object metadata without re-uploading data\n\t\troutes['PUT /api/metadata/**'] = async function(params, postData) {\n\t\t\tconst objectPath = extractObjectPath(params);\n\t\t\tconst request = assertKeetaStorageAnchorUpdateMetadataRequest(postData);\n\n\t\t\tconst signable = getKeetaStorageAnchorUpdateMetadataRequestSigningData({\n\t\t\t\tpath: objectPath,\n\t\t\t\tvisibility: request.visibility,\n\t\t\t\ttags: request.tags\n\t\t\t});\n\t\t\tconst account = await verifyBodyAuth(request, function() { return(signable); });\n\n\t\t\tconst { policy, parsed } = assertPathAccess(pathPolicies, account, objectPath, 'updateMetadata');\n\n\t\t\tvalidateTags(request.tags);\n\n\t\t\tconst visibility = assertVisibility(request.visibility);\n\t\t\tconst tags = request.tags;\n\n\t\t\t// Confirm object exists and preserve existing owner\n\t\t\tconst existing = await requireObject(objectPath);\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'updateMetadata', account, metadata: { owner: existing.metadata.owner, tags, visibility }, current: existing.metadata });\n\t\t\t}\n\n\t\t\tif (!backend.updateMetadata) {\n\t\t\t\tthrow(new Errors.OperationNotSupported('updateMetadata'));\n\t\t\t}\n\n\t\t\tconst updated = await backend.updateMetadata(objectPath, { tags, visibility });\n\t\t\tif (!updated) {\n\t\t\t\tthrow(new Errors.DocumentNotFound());\n\t\t\t}\n\n\t\t\treturn(jsonResponse({ ok: true, object: updated }, assertKeetaStorageAnchorPutResponse));\n\t\t};\n\n\t\t// POST /api/search - Search for objects\n\t\troutes['POST /api/search'] = async function(_params, postData) {\n\t\t\tconst request = assertKeetaStorageAnchorSearchRequest(postData);\n\t\t\tconst account = await verifyBodyAuth(request, getKeetaStorageAnchorSearchRequestSigningData);\n\t\t\tconst accountPubKey = account.publicKeyString.get();\n\n\t\t\t// Check if searching for public objects outside namespace\n\t\t\tconst searchingPublic = request.criteria.visibility === 'public';\n\t\t\tif (searchingPublic) {\n\t\t\t\t// When searching for public objects, we allow searching outside the caller's namespace\n\t\t\t\t// but only for objects with visibility: 'public'\n\t\t\t\tconst scopedCriteria = {\n\t\t\t\t\t...request.criteria,\n\t\t\t\t\tvisibility: 'public' as const\n\t\t\t\t};\n\n\t\t\t\tconst results = await backend.search(\n\t\t\t\t\tscopedCriteria,\n\t\t\t\t\tenforceSearchLimit(request.pagination)\n\t\t\t\t);\n\n\t\t\t\tassertSearchResults(results, { visibility: 'public' });\n\n\t\t\t\treturn(jsonResponse(buildSearchResponse(results), assertKeetaStorageAnchorSearchResponse));\n\t\t\t}\n\n\t\t\t// Scope search to authenticated account's namespace\n\t\t\tconst scopedCriteria = {\n\t\t\t\t...request.criteria,\n\t\t\t\towner: accountPubKey\n\t\t\t};\n\n\t\t\tconst results = await backend.search(\n\t\t\t\tscopedCriteria,\n\t\t\t\tenforceSearchLimit(request.pagination)\n\t\t\t);\n\n\t\t\tassertSearchResults(results, { owner: accountPubKey });\n\n\t\t\treturn(jsonResponse(buildSearchResponse(results), assertKeetaStorageAnchorSearchResponse));\n\t\t};\n\n\t\t// GET /api/quota - Get quota status\n\t\troutes['GET /api/quota'] = async function(_params, _postData, _headers, url) {\n\t\t\tconst account = await verifyURLAuth(\n\t\t\t\turl,\n\t\t\t\tgetKeetaStorageAnchorQuotaRequestSigningData,\n\t\t\t\tfunction() { return({}); }\n\t\t\t);\n\n\t\t\t// Get current usage from backend and compute remaining using per-user or global limits\n\t\t\tconst owner = account.publicKeyString.get();\n\t\t\tlet userLimits: QuotaLimits | null = null;\n\t\t\tif (backend.getQuotaLimits) {\n\t\t\t\tuserLimits = await backend.getQuotaLimits(owner);\n\t\t\t}\n\n\t\t\tconst effectiveLimits = userLimits ?? quotas;\n\t\t\tconst backendStatus = await backend.getQuotaStatus(owner);\n\n\t\t\t// Compute remaining from config limits\n\t\t\tlet remainingObjects = Math.max(0, effectiveLimits.maxObjectsPerUser - backendStatus.objectCount);\n\t\t\tlet remainingSize = Math.max(0, effectiveLimits.maxStoragePerUser - backendStatus.totalSize);\n\n\t\t\t// If backend reports its own remaining values, use the tighter constraint\n\t\t\tif (backendStatus.remainingObjects !== undefined && backendStatus.remainingObjects > 0) {\n\t\t\t\tremainingObjects = Math.min(backendStatus.remainingObjects, remainingObjects);\n\t\t\t}\n\t\t\tif (backendStatus.remainingSize !== undefined && backendStatus.remainingSize > 0) {\n\t\t\t\tremainingSize = Math.min(backendStatus.remainingSize, remainingSize);\n\t\t\t}\n\n\t\t\tconst response: KeetaStorageAnchorQuotaResponse = {\n\t\t\t\tok: true,\n\t\t\t\tquota: {\n\t\t\t\t\tobjectCount: backendStatus.objectCount,\n\t\t\t\t\ttotalSize: backendStatus.totalSize,\n\t\t\t\t\tremainingObjects,\n\t\t\t\t\tremainingSize\n\t\t\t\t}\n\t\t\t};\n\n\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorQuotaResponse));\n\t\t};\n\n\t\t// GET /api/public/** - Public object access via pre-signed URL\n\t\troutes['GET /api/public/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst objectPath = extractObjectPath(params);\n\t\t\tconst { policy, parsed } = parsePath(pathPolicies, objectPath);\n\n\t\t\t// Parse signature using standard signed field convention\n\t\t\tconst urlParsed = parseSignatureFromURL(url);\n\t\t\tif (!urlParsed.signedField) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing required signature parameters'));\n\t\t\t}\n\n\t\t\t// Resolve signer: policy-specified or from URL account param (any-signer for public objects)\n\t\t\tconst signerAccount = policy.getAuthorizedSigner(parsed) ?? urlParsed.account ?? null;\n\t\t\tif (!signerAccount) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing signer'));\n\t\t\t}\n\t\t\tconst signerPubKey = signerAccount.publicKeyString.get();\n\n\t\t\t// Parse and validate expires param\n\t\t\tconst parsedUrl = typeof url === 'string' ? new URL(url) : url;\n\t\t\tconst expiresParam = parsedUrl.searchParams.get('expires');\n\t\t\tif (!expiresParam) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing expires parameter'));\n\t\t\t}\n\t\t\tconst expiresAt = parseInt(expiresParam, 10);\n\t\t\tif (!Number.isFinite(expiresAt)) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Invalid expires parameter'));\n\t\t\t}\n\t\t\tif (Date.now() > expiresAt * 1000) {\n\t\t\t\tthrow(new Errors.SignatureExpired());\n\t\t\t}\n\n\t\t\t// Enforce maximum TTL\n\t\t\tconst maxExpiresAt = Math.floor(Date.now() / 1000) + quotas.maxSignedUrlTTL;\n\t\t\tif (expiresAt > maxExpiresAt) {\n\t\t\t\tthrow(new Errors.SignatureExpired('Signed URL TTL exceeds maximum allowed'));\n\t\t\t}\n\n\t\t\t// Pre-validate signature is valid base64 with reasonable length\n\t\t\tconst signatureBuffer = Buffer.from(urlParsed.signedField.signature, 'base64');\n\t\t\tif (signatureBuffer.length < 64 || signatureBuffer.length > 256) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Invalid signature format'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Allow 5 minutes of clock skew for signature verification\n\t\t\t\tconst valid = await VerifySignedData(signerAccount, [objectPath, expiresAt, signerPubKey], urlParsed.signedField, {\n\t\t\t\t\tmaxSkewMs: 5 * 60 * 1000\n\t\t\t\t});\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow(new Errors.SignatureInvalid());\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (Errors.SignatureInvalid.isInstance(e)) {\n\t\t\t\t\tthrow(e);\n\t\t\t\t}\n\n\t\t\t\tthrow(new Errors.SignatureInvalid('Signature verification failed'));\n\t\t\t}\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'get', account: signerAccount });\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\tif (result.metadata.visibility !== 'public') {\n\t\t\t\tthrow(new Errors.AccessDenied('Object is not public'));\n\t\t\t}\n\n\t\t\t// Decrypt using anchor account and extract mimeType from encrypted payload\n\t\t\tconst data = arrayBufferLikeToBuffer(result.data);\n\t\t\tconst container = EncryptedContainer.fromEncryptedBuffer(data, [anchorAccount]);\n\t\t\tconst plaintext = await container.getPlaintext();\n\t\t\tconst { content, mimeType } = parseContainerPayload(plaintext);\n\n\t\t\tconst headers: { [key: string]: string } = {};\n\t\t\tif (publicCorsOrigin) {\n\t\t\t\theaders['Access-Control-Allow-Origin'] = publicCorsOrigin;\n\t\t\t}\n\n\t\t\treturn({\n\t\t\t\toutput: content,\n\t\t\t\tcontentType: mimeType,\n\t\t\t\theaders\n\t\t\t});\n\t\t};\n\n\t\t// #endregion\n\n\t\treturn(routes);\n\t}\n\n\tasync serviceMetadata(): Promise<NonNullable<ServiceMetadata['services']['storage']>[string]> {\n\t\tconst authRequired = { options: { authentication: { type: 'required' as const, method: 'keeta-account' as const }}};\n\t\tconst operations: NonNullable<ServiceMetadata['services']['storage']>[string]['operations'] = {\n\t\t\tput: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tget: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tdelete: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tmetadata: { url: (new URL('/api/metadata', this.url)).toString(), ...authRequired },\n\t\t\tupdateMetadata: { url: (new URL('/api/metadata', this.url)).toString(), ...authRequired },\n\t\t\tsearch: { url: (new URL('/api/search', this.url)).toString(), ...authRequired },\n\t\t\tpublic: (new URL('/api/public', this.url)).toString(),\n\t\t\tquota: { url: (new URL('/api/quota', this.url)).toString(), ...authRequired }\n\t\t};\n\n\t\treturn({\n\t\t\toperations,\n\t\t\tanchorAccount: this.anchorAccount.publicKeyString.get(),\n\t\t\tquotas: this.quotas,\n\t\t\tsignedUrlDefaultTTL: this.signedUrlDefaultTTL,\n\t\t\tsearchableFields: ['owner', 'tags', 'visibility', 'pathPrefix']\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/services/storage/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServiceMetadata } from '../../lib/resolver.ts';\nimport type { Signable } from '../../lib/utils/signing.js';\nimport type { NamespaceValidator } from './lib/validators.js';\nimport type { ResolvedCertificateChainRequirement } from '../../lib/utils/certificate-network.js';\nimport type { KeetaAnchorMetadataServerConfig } from '../../lib/anchor-metadata-server.js';\nimport type { KeetaNet } from '../../client/index.js';\nimport type {\n\tKeetaStorageAnchorDeleteResponse,\n\tKeetaStorageAnchorPutResponse,\n\tKeetaStorageAnchorSearchResponse,\n\tKeetaStorageAnchorQuotaResponse,\n\tFullStorageBackend,\n\tQuotaConfig,\n\tQuotaLimits,\n\tStorageOperation,\n\tStorageObjectVisibility,\n\tStorageObjectMetadata,\n\tPathPolicy,\n\tSearchResults,\n\tStorageGetResult,\n\tSearchPagination\n} from './common.ts';\nimport type * as KeetaAnchorHTTPServer from '../../lib/http-server/index.js';\nimport {\n\tassertKeetaStorageAnchorDeleteResponse,\n\tassertKeetaStorageAnchorPutResponse,\n\tassertKeetaStorageAnchorGetRequest,\n\tassertKeetaStorageAnchorSearchRequest,\n\tassertKeetaStorageAnchorSearchResponse,\n\tassertKeetaStorageAnchorQuotaResponse,\n\tassertKeetaStorageAnchorUpdateMetadataRequest\n} from './common.generated.js';\nimport {\n\tgetKeetaStorageAnchorDeleteRequestSigningData,\n\tgetKeetaStorageAnchorPutRequestSigningData,\n\tgetKeetaStorageAnchorGetRequestSigningData,\n\tgetKeetaStorageAnchorSearchRequestSigningData,\n\tgetKeetaStorageAnchorQuotaRequestSigningData,\n\tgetKeetaStorageAnchorUpdateMetadataRequestSigningData,\n\tparseContainerPayload,\n\tErrors,\n\tCONTENT_TYPE_OCTET_STREAM,\n\tDEFAULT_SIGNED_URL_TTL_SECONDS\n} from './common.js';\nimport { KeetaAnchorMetadataServer } from '../../lib/anchor-metadata-server.js';\nimport { VerifySignedData } from '../../lib/utils/signing.js';\nimport { parseSignatureFromURL, verifyBodyAuth, verifyURLAuth } from '../../lib/http-server/common.js';\nimport { arrayBufferLikeToBuffer, Buffer } from '../../lib/utils/buffer.js';\nimport { requiresValidation, findMatchingValidators } from './lib/validators.js';\nimport { EncryptedContainer, EncryptedContainerError } from '../../lib/encrypted-container.js';\nimport { assertVisibility } from './utils.js';\n\ntype Account = InstanceType<typeof KeetaNet.lib.Account>;\n\n/**\n * Build a standardized search response from search results.\n */\nfunction buildSearchResponse(results: SearchResults): KeetaStorageAnchorSearchResponse {\n\tconst response: KeetaStorageAnchorSearchResponse = {\n\t\tok: true,\n\t\tresults: results.results\n\t};\n\tif (results.nextCursor !== undefined) {\n\t\tresponse.nextCursor = results.nextCursor;\n\t}\n\treturn(response);\n}\n\n// #region Module-Level Helpers\n\n/**\n * Find a matching policy for a path, validate it, and check access.\n *\n * @param pathPolicies - Array of path policies to check against\n * @param account - The account to check access for\n * @param path - The path to check\n * @param operation - The operation being performed\n */\nfunction assertPathAccess(\n\tpathPolicies: PathPolicy<unknown>[],\n\taccount: Account,\n\tpath: string,\n\toperation: StorageOperation\n): { policy: PathPolicy<unknown>; parsed: unknown } {\n\tfor (const policy of pathPolicies) {\n\t\tconst parsed = policy.parse(path);\n\t\tif (parsed !== null) {\n\t\t\tpolicy.validate(path);\n\n\t\t\tif (!policy.checkAccess(account, parsed, operation)) {\n\t\t\t\tthrow(new Errors.AccessDenied('Can only access your own namespace'));\n\t\t\t}\n\n\t\t\treturn({ policy, parsed });\n\t\t}\n\t}\n\n\tthrow(new Errors.InvalidPath('Path does not match any policy'));\n}\n\n/**\n * Find a matching policy and parse a path.\n * Used for public endpoints where auth is optional.\n *\n * @param pathPolicies - Array of path policies to check against\n * @param path - The path to parse\n */\nfunction parsePath(\n\tpathPolicies: PathPolicy<unknown>[],\n\tpath: string\n): { policy: PathPolicy<unknown>; parsed: unknown } {\n\tfor (const policy of pathPolicies) {\n\t\tconst parsed = policy.parse(path);\n\t\tif (parsed !== null) {\n\t\t\tpolicy.validate(path);\n\t\t\treturn({ policy, parsed });\n\t\t}\n\t}\n\n\tthrow(new Errors.InvalidPath('Path does not match any policy'));\n}\n\n/**\n * Extract object path from wildcard route parameter.\n * Prepends a leading slash to create a valid storage path.\n *\n * @param params - Route parameters containing the wildcard match\n *\n * @returns The object path with leading slash\n *\n * @throws InvalidPath if wildcard parameter is missing\n */\nfunction extractObjectPath(params: Map<string, string>): string {\n\tconst wildcardPath = params.get('**');\n\tif (!wildcardPath) {\n\t\tthrow(new Errors.InvalidPath());\n\t}\n\n\treturn('/' + wildcardPath);\n}\n\n/**\n * Authorize access to an object path via URL-signed request.\n * Combines path validation, signature verification, and access control.\n *\n * @throws InvalidPath if path is invalid or doesn't match any policy\n * @throws AccessDenied if user doesn't have access to the path\n * @throws KeetaAnchorUserError if signature is invalid\n */\nasync function authorizeURLAccess(\n\tpathPolicies: PathPolicy<unknown>[],\n\tparams: Map<string, string>,\n\turl: URL | string,\n\toperation: StorageOperation,\n\tgetSignable: (objectPath: string, account: Account) => Signable,\n\tcertificateChain?: ResolvedCertificateChainRequirement\n): Promise<{ account: Account; objectPath: string; policy: PathPolicy<unknown>; parsed: unknown }> {\n\tconst objectPath = extractObjectPath(params);\n\tconst { policy, parsed } = parsePath(pathPolicies, objectPath);\n\n\tconst account = await verifyURLAuth(url, function(account) {\n\t\treturn(getSignable(objectPath, account));\n\t}, certificateChain);\n\n\tif (!policy.checkAccess(account, parsed, operation)) {\n\t\tthrow(new Errors.AccessDenied('Can only access your own namespace'));\n\t}\n\n\treturn({ account, objectPath, policy, parsed });\n}\n\n// #endregion\n\n/**\n * Configuration for the Storage Anchor\n *\n * The Storage Anchor provides encrypted object storage with the following operations:\n *\n * PUT (Create/Update):\n * 1. Client creates EncryptedContainer with data, shares with anchor for public objects\n * 2. Client signs request (path, visibility, tags) and sends to server\n * 3. Server reserves quota, validates, stores object, commits reservation\n *\n * GET (Retrieve):\n * 1. Client signs request (path) and sends to server\n * 2. Server verifies access, returns EncryptedContainer\n * 3. Client decrypts with their private key\n *\n * DELETE:\n * 1. Client signs request (path) and sends to server\n * 2. Server verifies ownership, removes object\n *\n * SEARCH:\n * 1. Client signs request with criteria (tags, prefix, etc.)\n * 2. Server returns matching metadata (scoped to user's namespace)\n *\n * PUBLIC ACCESS (Pre-signed URLs):\n * 1. Client generates pre-signed URL with expiry, signed by owner\n * 2. Anyone can fetch via URL (no auth headers)\n * 3. Server verifies signature, expiry, and visibility\n * 4. Server decrypts and returns plaintext content\n *\n *\n * +-------------------+ +---------------------+ +------------------+\n * | Client | | Storage Anchor | | Storage Backend |\n * +-------------------+ +---------------------+ +------------------+\n * | | |\n * (PUT) Create EncryptedContainer | |\n * | Sign(path, visibility, tags) | |\n * |---------------------------------->| |\n * | | reserveUpload() ---------------->|\n * | | validate, put() ---------------->|\n * | | commitUpload() ----------------->|\n * |<--------------------------------- | { ok: true, object: metadata } |\n * | | |\n * (GET) Sign(path) ------------------------>| |\n * | | get() -------------------------->|\n * |<--------------------------------- | EncryptedContainer (binary) |\n * | Decrypt with private key | |\n * | | |\n * (PUBLIC) Generate pre-signed URL | |\n * | URL with expires, signature | |\n * (Anyone) Fetch URL ---------------------->| |\n * | | verify signature, expiry |\n * | | get(), decrypt ----------------->|\n * |<--------------------------------- | Plaintext content |\n */\nexport interface KeetaAnchorStorageServerConfig extends KeetaAnchorMetadataServerConfig {\n\t/**\n\t * The data to use for the index page (optional)\n\t */\n\thomepage?: string | (() => Promise<string> | string);\n\n\t/**\n\t * The storage backend to use for storing documents.\n\t * Must implement full capabilities: CRUD, search, and quota management.\n\t */\n\tbackend: FullStorageBackend;\n\n\t/**\n\t * The anchor's account for decrypting objects.\n\t */\n\tanchorAccount: Account;\n\n\t/**\n\t * Quota configuration for storage limits.\n\t * Partial values are merged with defaults.\n\t */\n\tquotas?: Partial<QuotaConfig>;\n\n\t/**\n\t * Namespace validators for special paths\n\t */\n\tvalidators?: NamespaceValidator[];\n\n\t/**\n\t * Default TTL in seconds for pre-signed URLs (default: 3600)\n\t */\n\tsignedUrlDefaultTTL?: number;\n\n\t/**\n\t * CORS origin for public endpoints (default: false).\n\t * - '*' allows all origins\n\t * - specific origin string restricts to that origin\n\t * - false (default) disables CORS headers on public responses\n\t */\n\tpublicCORSOrigin?: string | false;\n\n\t/**\n\t * CORS origin for authenticated object endpoints (default: '*').\n\t * - '*' allows all origins\n\t * - specific origin string restricts to that origin\n\t */\n\tauthenticatedCORSOrigin?: string;\n\n\t/**\n\t * Path policies for parsing, validating, and access control of storage paths.\n\t * Each policy handles a specific path pattern. First matching policy wins.\n\t */\n\tpathPolicies: PathPolicy<unknown>[];\n\n\t/**\n\t * Tag validation configuration.\n\t */\n\ttagValidation?: {\n\t\t/** Maximum number of tags per object (default: 10) */\n\t\tmaxTags?: number;\n\t\t/** Maximum length of each tag (default: 50) */\n\t\tmaxTagLength?: number;\n\t\t/** Pattern for valid tag characters (default: /^[a-zA-Z0-9_-]+$/) */\n\t\tpattern?: RegExp;\n\t};\n}\n\n// Default quota configuration\nconst DEFAULT_QUOTAS: QuotaConfig = {\n\tmaxObjectSize: 10 * 1024 * 1024, // 10MB\n\tmaxObjectsPerUser: 1000,\n\tmaxStoragePerUser: 100 * 1024 * 1024, // 100MB\n\tmaxSearchLimit: 100,\n\tmaxSignedUrlTTL: 86400 // 24 hours\n};\n\n// Default tag validation configuration\nconst DEFAULT_TAG_VALIDATION = {\n\tmaxTags: 10,\n\tmaxTagLength: 50,\n\tpattern: /^[a-zA-Z0-9_-]+$/\n};\n\nexport class KeetaNetStorageAnchorHTTPServer extends KeetaAnchorMetadataServer<NonNullable<ServiceMetadata['services']['storage']>[string], KeetaAnchorStorageServerConfig> {\n\treadonly homepage: NonNullable<KeetaAnchorStorageServerConfig['homepage']>;\n\treadonly backend: FullStorageBackend;\n\treadonly anchorAccount: Account;\n\treadonly quotas: QuotaConfig;\n\treadonly validators: NamespaceValidator[];\n\treadonly signedUrlDefaultTTL: number;\n\treadonly publicCorsOrigin: string | false;\n\treadonly authenticatedCorsOrigin: string;\n\treadonly pathPolicies: PathPolicy<unknown>[];\n\treadonly tagValidation: Required<NonNullable<KeetaAnchorStorageServerConfig['tagValidation']>>;\n\tconstructor(config: KeetaAnchorStorageServerConfig) {\n\t\tsuper(config);\n\n\t\tthis.homepage = config.homepage ?? '';\n\t\tthis.backend = config.backend;\n\t\tthis.anchorAccount = config.anchorAccount;\n\t\tthis.quotas = { ...DEFAULT_QUOTAS, ...config.quotas };\n\t\tthis.validators = config.validators ?? [];\n\t\tthis.signedUrlDefaultTTL = config.signedUrlDefaultTTL ?? DEFAULT_SIGNED_URL_TTL_SECONDS;\n\t\tthis.publicCorsOrigin = config.publicCORSOrigin ?? false;\n\t\tthis.authenticatedCorsOrigin = config.authenticatedCORSOrigin ?? '*';\n\t\tthis.pathPolicies = config.pathPolicies;\n\t\tthis.tagValidation = {\n\t\t\tmaxTags: config.tagValidation?.maxTags ?? DEFAULT_TAG_VALIDATION.maxTags,\n\t\t\tmaxTagLength: config.tagValidation?.maxTagLength ?? DEFAULT_TAG_VALIDATION.maxTagLength,\n\t\t\tpattern: config.tagValidation?.pattern ?? DEFAULT_TAG_VALIDATION.pattern\n\t\t};\n\n\t\t// Validate anchorAccount has private key\n\t\tif (!this.anchorAccount.hasPrivateKey) {\n\t\t\tthrow(new Error('anchorAccount must have a private key'));\n\t\t}\n\n\t\t// Validate at least one path policy is provided\n\t\tif (this.pathPolicies.length === 0) {\n\t\t\tthrow(new Error('At least one path policy must be provided'));\n\t\t}\n\n\t\t// Validate quota configuration values are positive\n\t\tif (this.quotas.maxObjectSize <= 0) {\n\t\t\tthrow(new Error('quotas.maxObjectSize must be positive'));\n\t\t}\n\t\tif (this.quotas.maxObjectsPerUser <= 0) {\n\t\t\tthrow(new Error('quotas.maxObjectsPerUser must be positive'));\n\t\t}\n\t\tif (this.quotas.maxStoragePerUser <= 0) {\n\t\t\tthrow(new Error('quotas.maxStoragePerUser must be positive'));\n\t\t}\n\t\tif (this.quotas.maxSearchLimit <= 0) {\n\t\t\tthrow(new Error('quotas.maxSearchLimit must be positive'));\n\t\t}\n\t\tif (this.quotas.maxSignedUrlTTL <= 0) {\n\t\t\tthrow(new Error('quotas.maxSignedUrlTTL must be positive'));\n\t\t}\n\n\t\t// Validate tag validation configuration\n\t\tif (this.tagValidation.maxTags <= 0) {\n\t\t\tthrow(new Error('tagValidation.maxTags must be positive'));\n\t\t}\n\t\tif (this.tagValidation.maxTagLength <= 0) {\n\t\t\tthrow(new Error('tagValidation.maxTagLength must be positive'));\n\t\t}\n\t}\n\n\t// Note: We use this.* properties instead of config.*.\n\t// The config parameter is required by the abstract method signature but unused here.\n\tprotected async initRoutes(_ignoreConfig: KeetaAnchorStorageServerConfig): Promise<KeetaAnchorHTTPServer.Routes> {\n\t\tconst routes: KeetaAnchorHTTPServer.Routes = {};\n\t\tconst backend = this.backend;\n\t\tconst anchorAccount = this.anchorAccount;\n\t\tconst quotas = this.quotas;\n\t\tconst validators = this.validators;\n\t\tconst publicCorsOrigin = this.publicCorsOrigin;\n\t\tconst authenticatedCorsOrigin = this.authenticatedCorsOrigin;\n\t\tconst pathPolicies = this.pathPolicies;\n\t\tconst requireCertificateChain = this.resolvedCertificateChainRequirement;\n\t\tconst tagValidation = this.tagValidation;\n\t\tconst logger = this.logger;\n\n\t\t/**\n\t\t * Validate tags against configured limits.\n\t\t * @throws Errors.InvalidTag if any tag violates constraints\n\t\t */\n\t\tfunction validateTags(tags: string[]): void {\n\t\t\tconst { maxTags, maxTagLength, pattern: tagPattern } = tagValidation;\n\t\t\tfor (const tag of tags) {\n\t\t\t\tif (tag.length > maxTagLength) {\n\t\t\t\t\tthrow(new Errors.InvalidTag(`Tag exceeds maximum length of ${maxTagLength}: \"${tag}\"`));\n\t\t\t\t}\n\t\t\t\ttagPattern.lastIndex = 0;\n\t\t\t\tif (!tagPattern.test(tag)) {\n\t\t\t\t\tthrow(new Errors.InvalidTag(`Tag contains invalid characters: \"${tag}\"`));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (tags.length > maxTags) {\n\t\t\t\tthrow(new Errors.InvalidTag(`Too many tags: ${tags.length} exceeds maximum of ${maxTags}`));\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Build a JSON response with assertion.\n\t\t */\n\t\tfunction jsonResponse<T>(response: T, assertionHandler: (input: unknown) => T): { output: string } {\n\t\t\treturn({ output: JSON.stringify(assertionHandler(response)) });\n\t\t}\n\n\t\t/**\n\t\t * Get an object or throw DocumentNotFound.\n\t\t */\n\t\tasync function requireObject(path: string): Promise<StorageGetResult> {\n\t\t\tconst result = await backend.get(path);\n\t\t\tif (!result) {\n\t\t\t\tthrow(new Errors.DocumentNotFound());\n\t\t\t}\n\n\t\t\treturn(result);\n\t\t}\n\n\t\t/**\n\t\t * Enforce server-side search limit cap.\n\t\t */\n\t\tfunction enforceSearchLimit(pagination: SearchPagination | undefined): SearchPagination {\n\t\t\tconst requestedLimit = pagination?.limit ?? quotas.maxSearchLimit;\n\t\t\treturn({ ...pagination, limit: Math.min(requestedLimit, quotas.maxSearchLimit) });\n\t\t}\n\n\t\t/**\n\t\t * Validate search results match expected constraints.\n\t\t */\n\t\tfunction assertSearchResults(\n\t\t\tresults: SearchResults,\n\t\t\tconstraint: { visibility?: 'public'; owner?: string }\n\t\t): void {\n\t\t\tfor (const obj of results.results) {\n\t\t\t\tif (constraint.visibility && obj.visibility !== constraint.visibility) {\n\t\t\t\t\tthrow(new Errors.InvariantViolation(\n\t\t\t\t\t\t`Backend returned ${obj.visibility} object in ${constraint.visibility} search`\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t\tif (constraint.owner && obj.owner !== constraint.owner) {\n\t\t\t\t\tthrow(new Errors.InvariantViolation(\n\t\t\t\t\t\t`Backend returned object owned by ${obj.owner} in search for ${constraint.owner}`\n\t\t\t\t\t));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * If a homepage is provided, setup the route for it\n\t\t */\n\t\tconst homepage = this.homepage;\n\t\tif (homepage) {\n\t\t\troutes['GET /'] = async function() {\n\t\t\t\tlet homepageData: string;\n\t\t\t\tif (typeof homepage === 'string') {\n\t\t\t\t\thomepageData = homepage;\n\t\t\t\t} else {\n\t\t\t\t\thomepageData = await homepage();\n\t\t\t\t}\n\n\t\t\t\treturn({\n\t\t\t\t\toutput: homepageData,\n\t\t\t\t\tcontentType: 'text/html'\n\t\t\t\t});\n\t\t\t};\n\t\t}\n\n\t\t// #region API Routes\n\n\t\t// PUT /api/object/* - Create or update an object\n\t\troutes['PUT /api/object/**'] = {\n\t\t\tbodyType: 'raw',\n\t\t\tmaxBodySize: quotas.maxObjectSize,\n\t\t\thandler: async function(params, postData, _headers, url) {\n\t\t\t\tconst objectPath = extractObjectPath(params);\n\n\t\t\t\t// Get metadata from query params\n\t\t\t\tconst parsedUrl = new URL(url);\n\t\t\t\tconst visibilityParam = parsedUrl.searchParams.get('visibility');\n\t\t\t\tconst tagsParam = parsedUrl.searchParams.get('tags');\n\n\t\t\t\t// Default to private when absent, assert valid value otherwise\n\t\t\t\tlet visibility: StorageObjectVisibility = 'private';\n\t\t\t\tif (visibilityParam !== null) {\n\t\t\t\t\tvisibility = assertVisibility(visibilityParam);\n\t\t\t\t}\n\n\t\t\t\tconst rawTags: string[] = (tagsParam ?? '')\n\t\t\t\t\t.split(',')\n\t\t\t\t\t.map(function(t) {\n\t\t\t\t\t\treturn(t.trim());\n\t\t\t\t\t})\n\t\t\t\t\t.filter(function(t) {\n\t\t\t\t\t\treturn(t.length > 0);\n\t\t\t\t\t});\n\n\t\t\t\t// Verify signature\n\t\t\t\tconst account = await verifyURLAuth(url, function() {\n\t\t\t\t\treturn(getKeetaStorageAnchorPutRequestSigningData({ path: objectPath, visibility, tags: rawTags }));\n\t\t\t\t}, requireCertificateChain);\n\n\t\t\t\tvalidateTags(rawTags);\n\t\t\t\tconst tags = rawTags;\n\n\t\t\t\t// Validate path format, metadata, and ownership\n\t\t\t\tconst { policy, parsed } = assertPathAccess(pathPolicies, account, objectPath, 'put');\n\t\t\t\tconst owner = policy.getOwner(objectPath);\n\n\t\t\t\t// Resolve per-user quota limits, falling back to global config\n\t\t\t\tlet userLimits: QuotaLimits | null = null;\n\t\t\t\tif (backend.getQuotaLimits) {\n\t\t\t\t\tuserLimits = await backend.getQuotaLimits(owner);\n\t\t\t\t}\n\n\t\t\t\tconst effectiveLimits = userLimits ?? quotas;\n\t\t\t\t// Body is raw binary (EncryptedContainer)\n\t\t\t\tconst data = arrayBufferLikeToBuffer(postData);\n\t\t\t\tconst objectSize = data.byteLength;\n\t\t\t\tif (objectSize > effectiveLimits.maxObjectSize) {\n\t\t\t\t\tthrow(new Errors.QuotaExceeded({\n\t\t\t\t\t\tquotaType: 'maxObjectSize',\n\t\t\t\t\t\tlimit: effectiveLimits.maxObjectSize,\n\t\t\t\t\t\tcurrent: objectSize\n\t\t\t\t\t}));\n\t\t\t\t}\n\n\t\t\t\tconst needsValidation = requiresValidation(objectPath, validators);\n\t\t\t\tconst needsContextValidation = !!policy.validateContext;\n\t\t\t\tconst needsAnchorDecryption = needsValidation || visibility === 'public';\n\t\t\t\tif (needsContextValidation || needsAnchorDecryption) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst container = EncryptedContainer.fromEncryptedBuffer(data, [anchorAccount]);\n\n\t\t\t\t\t\tif (policy.validateContext) {\n\t\t\t\t\t\t\tpolicy.validateContext(parsed, { operation: 'put', account, metadata: { owner, tags, visibility }, container });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (needsAnchorDecryption) {\n\t\t\t\t\t\t\tconst plaintext = await container.getPlaintext();\n\t\t\t\t\t\t\tif (needsValidation) {\n\t\t\t\t\t\t\t\t// Extract content and mimeType from encrypted payload\n\t\t\t\t\t\t\t\tconst { content, mimeType } = parseContainerPayload(plaintext);\n\t\t\t\t\t\t\t\tconst matchingValidators = findMatchingValidators(objectPath, validators);\n\t\t\t\t\t\t\t\tfor (const validator of matchingValidators) {\n\t\t\t\t\t\t\t\t\tconst result = await validator.validate(objectPath, content, mimeType);\n\t\t\t\t\t\t\t\t\tif (!result.valid) {\n\t\t\t\t\t\t\t\t\t\tthrow(new Errors.ValidationFailed(result.error));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\tif (Errors.InvalidMetadata.isInstance(e)) {\n\t\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (Errors.ValidationFailed.isInstance(e)) {\n\t\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (EncryptedContainerError.isInstance(e)) {\n\t\t\t\t\t\t\tif (e.code.startsWith('MALFORMED_')) {\n\t\t\t\t\t\t\t\tthrow(new Errors.ValidationFailed(`Invalid encrypted container: ${e.message}`));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (e.code === 'NO_MATCHING_KEY' || e.code === 'DECRYPTION_FAILED') {\n\t\t\t\t\t\t\t\tthrow(new Errors.AnchorPrincipalRequired());\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthrow(e);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Reserve quota before upload\n\t\t\t\tconst reservation = await backend.reserveUpload(owner, objectPath, objectSize, {\n\t\t\t\t\tquotaLimits: {\n\t\t\t\t\t\tmaxObjectsPerUser: effectiveLimits.maxObjectsPerUser,\n\t\t\t\t\t\tmaxStoragePerUser: effectiveLimits.maxStoragePerUser\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tlet objectMetadata: StorageObjectMetadata;\n\t\t\t\ttry {\n\t\t\t\t\tobjectMetadata = await backend.put(objectPath, data, {\n\t\t\t\t\t\towner,\n\t\t\t\t\t\ttags,\n\t\t\t\t\t\tvisibility\n\t\t\t\t\t});\n\n\t\t\t\t\tawait backend.commitUpload(reservation.id);\n\t\t\t\t} catch (e) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait backend.releaseUpload(reservation.id);\n\t\t\t\t\t} catch (releaseError) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * This provides a hint for cleanup\n\t\t\t\t\t\t */\n\t\t\t\t\t\tlogger?.warn('Failed to release upload reservation', { reservationId: reservation.id, error: releaseError });\n\t\t\t\t\t}\n\t\t\t\t\tthrow(e);\n\t\t\t\t}\n\n\t\t\t\tconst response: KeetaStorageAnchorPutResponse = {\n\t\t\t\t\tok: true,\n\t\t\t\t\tobject: objectMetadata\n\t\t\t\t};\n\n\t\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorPutResponse));\n\t\t\t}\n\t\t};\n\n\t\t// GET /api/object/* - Retrieve an object\n\t\troutes['GET /api/object/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'get',\n\t\t\t\tfunction(path, signingAccount) {\n\t\t\t\t\treturn(getKeetaStorageAnchorGetRequestSigningData(assertKeetaStorageAnchorGetRequest({ path, account: signingAccount.publicKeyString.get() })));\n\t\t\t\t},\n\t\t\t\trequireCertificateChain\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'get', account });\n\t\t\t}\n\n\t\t\tconst headers: { [key: string]: string } = {};\n\t\t\tif (authenticatedCorsOrigin) {\n\t\t\t\theaders['Access-Control-Allow-Origin'] = authenticatedCorsOrigin;\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\treturn({\n\t\t\t\theaders,\n\t\t\t\toutput: result.data,\n\t\t\t\tcontentType: CONTENT_TYPE_OCTET_STREAM\n\t\t\t});\n\t\t};\n\n\t\t// DELETE /api/object/* - Delete an object\n\t\troutes['DELETE /api/object/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'delete',\n\t\t\t\tfunction(path, signingAccount) {\n\t\t\t\t\treturn(getKeetaStorageAnchorDeleteRequestSigningData({ path, account: signingAccount.publicKeyString.get() }));\n\t\t\t\t},\n\t\t\t\trequireCertificateChain\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'delete', account });\n\t\t\t}\n\n\t\t\tconst deleted = await backend.delete(objectPath);\n\t\t\tconst response: KeetaStorageAnchorDeleteResponse = {\n\t\t\t\tok: true,\n\t\t\t\tdeleted\n\t\t\t};\n\n\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorDeleteResponse));\n\t\t};\n\n\t\t// GET /api/metadata/* - Get object metadata\n\t\troutes['GET /api/metadata/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst { objectPath, policy, parsed, account } = await authorizeURLAccess(\n\t\t\t\tpathPolicies,\n\t\t\t\tparams,\n\t\t\t\turl,\n\t\t\t\t'metadata',\n\t\t\t\tfunction(path, signingAccount) {\n\t\t\t\t\treturn(getKeetaStorageAnchorGetRequestSigningData(assertKeetaStorageAnchorGetRequest({ path, account: signingAccount.publicKeyString.get() })));\n\t\t\t\t},\n\t\t\t\trequireCertificateChain\n\t\t\t);\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'metadata', account });\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\treturn(jsonResponse({ ok: true, object: result.metadata }, assertKeetaStorageAnchorPutResponse));\n\t\t};\n\n\t\t// PUT /api/metadata/* - Update object metadata without re-uploading data\n\t\troutes['PUT /api/metadata/**'] = async function(params, postData) {\n\t\t\tconst objectPath = extractObjectPath(params);\n\t\t\tconst request = assertKeetaStorageAnchorUpdateMetadataRequest(postData);\n\n\t\t\tconst signable = getKeetaStorageAnchorUpdateMetadataRequestSigningData({\n\t\t\t\tpath: objectPath,\n\t\t\t\tvisibility: request.visibility,\n\t\t\t\ttags: request.tags\n\t\t\t});\n\t\t\tconst account = await verifyBodyAuth(request, function() {\n\t\t\t\treturn(signable);\n\t\t\t}, requireCertificateChain);\n\n\t\t\tconst { policy, parsed } = assertPathAccess(pathPolicies, account, objectPath, 'updateMetadata');\n\n\t\t\tvalidateTags(request.tags);\n\n\t\t\tconst visibility = assertVisibility(request.visibility);\n\t\t\tconst tags = request.tags;\n\n\t\t\t// Confirm object exists and preserve existing owner\n\t\t\tconst existing = await requireObject(objectPath);\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'updateMetadata', account, metadata: { owner: existing.metadata.owner, tags, visibility }, current: existing.metadata });\n\t\t\t}\n\n\t\t\tif (!backend.updateMetadata) {\n\t\t\t\tthrow(new Errors.OperationNotSupported('updateMetadata'));\n\t\t\t}\n\n\t\t\tconst updated = await backend.updateMetadata(objectPath, { tags, visibility });\n\t\t\tif (!updated) {\n\t\t\t\tthrow(new Errors.DocumentNotFound());\n\t\t\t}\n\n\t\t\treturn(jsonResponse({ ok: true, object: updated }, assertKeetaStorageAnchorPutResponse));\n\t\t};\n\n\t\t// POST /api/search - Search for objects\n\t\troutes['POST /api/search'] = async function(_params, postData) {\n\t\t\tconst request = assertKeetaStorageAnchorSearchRequest(postData);\n\t\t\tconst account = await verifyBodyAuth(request, getKeetaStorageAnchorSearchRequestSigningData, requireCertificateChain);\n\t\t\tconst accountPubKey = account.publicKeyString.get();\n\n\t\t\t// Check if searching for public objects outside namespace\n\t\t\tconst searchingPublic = request.criteria.visibility === 'public';\n\t\t\tif (searchingPublic) {\n\t\t\t\t// When searching for public objects, we allow searching outside the caller's namespace\n\t\t\t\t// but only for objects with visibility: 'public'\n\t\t\t\tconst scopedCriteria = {\n\t\t\t\t\t...request.criteria,\n\t\t\t\t\tvisibility: 'public' as const\n\t\t\t\t};\n\n\t\t\t\tconst results = await backend.search(\n\t\t\t\t\tscopedCriteria,\n\t\t\t\t\tenforceSearchLimit(request.pagination)\n\t\t\t\t);\n\n\t\t\t\tassertSearchResults(results, { visibility: 'public' });\n\n\t\t\t\treturn(jsonResponse(buildSearchResponse(results), assertKeetaStorageAnchorSearchResponse));\n\t\t\t}\n\n\t\t\t// Scope search to authenticated account's namespace\n\t\t\tconst scopedCriteria = {\n\t\t\t\t...request.criteria,\n\t\t\t\towner: accountPubKey\n\t\t\t};\n\n\t\t\tconst results = await backend.search(\n\t\t\t\tscopedCriteria,\n\t\t\t\tenforceSearchLimit(request.pagination)\n\t\t\t);\n\n\t\t\tassertSearchResults(results, { owner: accountPubKey });\n\n\t\t\treturn(jsonResponse(buildSearchResponse(results), assertKeetaStorageAnchorSearchResponse));\n\t\t};\n\n\t\t// GET /api/quota - Get quota status\n\t\troutes['GET /api/quota'] = async function(_params, _postData, _headers, url) {\n\t\t\tconst account = await verifyURLAuth(\n\t\t\t\turl,\n\t\t\t\tfunction() { return(getKeetaStorageAnchorQuotaRequestSigningData({})); },\n\t\t\t\trequireCertificateChain\n\t\t\t);\n\n\t\t\t// Get current usage from backend and compute remaining using per-user or global limits\n\t\t\tconst owner = account.publicKeyString.get();\n\t\t\tlet userLimits: QuotaLimits | null = null;\n\t\t\tif (backend.getQuotaLimits) {\n\t\t\t\tuserLimits = await backend.getQuotaLimits(owner);\n\t\t\t}\n\n\t\t\tconst effectiveLimits = userLimits ?? quotas;\n\t\t\tconst backendStatus = await backend.getQuotaStatus(owner);\n\n\t\t\t// Compute remaining from config limits\n\t\t\tlet remainingObjects = Math.max(0, effectiveLimits.maxObjectsPerUser - backendStatus.objectCount);\n\t\t\tlet remainingSize = Math.max(0, effectiveLimits.maxStoragePerUser - backendStatus.totalSize);\n\n\t\t\t// If backend reports its own remaining values, use the tighter constraint\n\t\t\tif (backendStatus.remainingObjects !== undefined && backendStatus.remainingObjects > 0) {\n\t\t\t\tremainingObjects = Math.min(backendStatus.remainingObjects, remainingObjects);\n\t\t\t}\n\t\t\tif (backendStatus.remainingSize !== undefined && backendStatus.remainingSize > 0) {\n\t\t\t\tremainingSize = Math.min(backendStatus.remainingSize, remainingSize);\n\t\t\t}\n\n\t\t\tconst response: KeetaStorageAnchorQuotaResponse = {\n\t\t\t\tok: true,\n\t\t\t\tquota: {\n\t\t\t\t\tobjectCount: backendStatus.objectCount,\n\t\t\t\t\ttotalSize: backendStatus.totalSize,\n\t\t\t\t\tremainingObjects,\n\t\t\t\t\tremainingSize\n\t\t\t\t}\n\t\t\t};\n\n\t\t\treturn(jsonResponse(response, assertKeetaStorageAnchorQuotaResponse));\n\t\t};\n\n\t\t// GET /api/public/** - Public object access via pre-signed URL\n\t\troutes['GET /api/public/**'] = async function(params, _postData, _headers, url) {\n\t\t\tconst objectPath = extractObjectPath(params);\n\t\t\tconst { policy, parsed } = parsePath(pathPolicies, objectPath);\n\n\t\t\t// Parse signature using standard signed field convention\n\t\t\tconst urlParsed = parseSignatureFromURL(url);\n\t\t\tif (!urlParsed.signedField) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing required signature parameters'));\n\t\t\t}\n\n\t\t\t// Resolve signer: policy-specified or from URL account param (any-signer for public objects)\n\t\t\tconst signerAccount = policy.getAuthorizedSigner(parsed) ?? urlParsed.account ?? null;\n\t\t\tif (!signerAccount) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing signer'));\n\t\t\t}\n\t\t\tconst signerPubKey = signerAccount.publicKeyString.get();\n\n\t\t\t// Parse and validate expires param\n\t\t\tconst parsedUrl = typeof url === 'string' ? new URL(url) : url;\n\t\t\tconst expiresParam = parsedUrl.searchParams.get('expires');\n\t\t\tif (!expiresParam) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Missing expires parameter'));\n\t\t\t}\n\t\t\tconst expiresAt = parseInt(expiresParam, 10);\n\t\t\tif (!Number.isFinite(expiresAt)) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Invalid expires parameter'));\n\t\t\t}\n\t\t\tif (Date.now() > expiresAt * 1000) {\n\t\t\t\tthrow(new Errors.SignatureExpired());\n\t\t\t}\n\n\t\t\t// Enforce maximum TTL\n\t\t\tconst maxExpiresAt = Math.floor(Date.now() / 1000) + quotas.maxSignedUrlTTL;\n\t\t\tif (expiresAt > maxExpiresAt) {\n\t\t\t\tthrow(new Errors.SignatureExpired('Signed URL TTL exceeds maximum allowed'));\n\t\t\t}\n\n\t\t\t// Pre-validate signature is valid base64 with reasonable length\n\t\t\tconst signatureBuffer = Buffer.from(urlParsed.signedField.signature, 'base64');\n\t\t\tif (signatureBuffer.length < 64 || signatureBuffer.length > 256) {\n\t\t\t\tthrow(new Errors.SignatureInvalid('Invalid signature format'));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Allow 5 minutes of clock skew for signature verification\n\t\t\t\tconst valid = await VerifySignedData(signerAccount, [objectPath, expiresAt, signerPubKey], urlParsed.signedField, {\n\t\t\t\t\tmaxSkewMs: 5 * 60 * 1000\n\t\t\t\t});\n\t\t\t\tif (!valid) {\n\t\t\t\t\tthrow(new Errors.SignatureInvalid());\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\tif (Errors.SignatureInvalid.isInstance(e)) {\n\t\t\t\t\tthrow(e);\n\t\t\t\t}\n\n\t\t\t\tthrow(new Errors.SignatureInvalid('Signature verification failed'));\n\t\t\t}\n\n\t\t\tif (policy.validateContext) {\n\t\t\t\tpolicy.validateContext(parsed, { operation: 'get', account: signerAccount });\n\t\t\t}\n\n\t\t\tconst result = await requireObject(objectPath);\n\t\t\tif (result.metadata.visibility !== 'public') {\n\t\t\t\tthrow(new Errors.AccessDenied('Object is not public'));\n\t\t\t}\n\n\t\t\t// Decrypt using anchor account and extract mimeType from encrypted payload\n\t\t\tconst data = arrayBufferLikeToBuffer(result.data);\n\t\t\tconst container = EncryptedContainer.fromEncryptedBuffer(data, [anchorAccount]);\n\t\t\tconst plaintext = await container.getPlaintext();\n\t\t\tconst { content, mimeType } = parseContainerPayload(plaintext);\n\n\t\t\tconst headers: { [key: string]: string } = {};\n\t\t\tif (publicCorsOrigin) {\n\t\t\t\theaders['Access-Control-Allow-Origin'] = publicCorsOrigin;\n\t\t\t}\n\n\t\t\treturn({\n\t\t\t\toutput: content,\n\t\t\t\tcontentType: mimeType,\n\t\t\t\theaders\n\t\t\t});\n\t\t};\n\n\t\t// #endregion\n\n\t\treturn(routes);\n\t}\n\n\tprotected async buildServiceMetadata(): Promise<NonNullable<ServiceMetadata['services']['storage']>[string]> {\n\t\tconst authRequired = { options: { authentication: { type: 'required' as const, method: 'keeta-account' as const }}};\n\t\tconst acceptedIssuerDNs = this.acceptedIssuerDNs();\n\t\tconst operations: NonNullable<ServiceMetadata['services']['storage']>[string]['operations'] = {\n\t\t\tput: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tget: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tdelete: { url: (new URL('/api/object', this.url)).toString(), ...authRequired },\n\t\t\tmetadata: { url: (new URL('/api/metadata', this.url)).toString(), ...authRequired },\n\t\t\tupdateMetadata: { url: (new URL('/api/metadata', this.url)).toString(), ...authRequired },\n\t\t\tsearch: { url: (new URL('/api/search', this.url)).toString(), ...authRequired },\n\t\t\tpublic: (new URL('/api/public', this.url)).toString(),\n\t\t\tquota: { url: (new URL('/api/quota', this.url)).toString(), ...authRequired }\n\t\t};\n\n\t\treturn({\n\t\t\toperations,\n\t\t\tanchorAccount: this.anchorAccount.publicKeyString.get(),\n\t\t\tquotas: this.quotas,\n\t\t\tsignedUrlDefaultTTL: this.signedUrlDefaultTTL,\n\t\t\tsearchableFields: ['owner', 'tags', 'visibility', 'pathPrefix'],\n\t\t\t...(acceptedIssuerDNs !== undefined && { acceptedIssuerDNs })\n\t\t});\n\t}\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { lib as KeetaNetLib } from '@keetanetwork/keetanet-client';
|
|
|
2
2
|
import type { ToJSONSerializable } from '../../lib/utils/json.ts';
|
|
3
3
|
import type { HTTPSignedField } from '../../lib/http-server/common.js';
|
|
4
4
|
import type { Signable } from '../../lib/utils/signing.js';
|
|
5
|
-
import { KeetaAnchorUserError } from '../../lib/error.js';
|
|
5
|
+
import { KeetaAnchorCertificateRequiredError, KeetaAnchorUserError } from '../../lib/error.js';
|
|
6
6
|
export * from './common.generated.js';
|
|
7
7
|
export declare const USERNAME_DELIMITER = "$";
|
|
8
8
|
export declare const USERNAME_MIN_LENGTH = 1;
|
|
@@ -132,5 +132,6 @@ declare class KeetaUsernameAnchorUserNotFoundError extends KeetaAnchorUserError
|
|
|
132
132
|
export declare const Errors: {
|
|
133
133
|
UsernameAlreadyTaken: typeof KeetaUsernameAnchorUsernameAlreadyTakenError;
|
|
134
134
|
UserNotFound: typeof KeetaUsernameAnchorUserNotFoundError;
|
|
135
|
+
CertificateRequired: typeof KeetaAnchorCertificateRequiredError;
|
|
135
136
|
};
|
|
136
137
|
//# sourceMappingURL=common.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/services/username/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAkC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/services/username/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,IAAI,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,mCAAmC,EAAE,oBAAoB,EAAkC,MAAM,oBAAoB,CAAC;AAC/H,cAAc,uBAAuB,CAAC;AAEtC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACvC,MAAM,EAAE,wBAAwB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,uBAAuB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B,GAAG,IAAI,CAwDrG;AAED,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AACvE,MAAM,MAAM,4BAA4B,GAAG,GAAG,MAAM,GAAG,OAAO,kBAAkB,GAAG,MAAM,EAAE,CAAC;AAC5F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2CAA2C;IAAG,OAAO,EAAE,eAAe,CAAC;CAAE;AAE1F,MAAM,WAAW,4CAA4C;IAAG,QAAQ,EAAE,MAAM,CAAC;CAAE;AAEnF,MAAM,WAAW,0CAA0C;IAC1D,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,sCAAsC;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,MAAM,iCAAiC,GAAG,CAAC;IAChD,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,sCAAsC,EAAE,CAAC;CAClD,CAAC,GAAG,CAAC;IACL,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACd,CAAC,CAAA;AAEF,MAAM,MAAM,qCAAqC,GAAG,kBAAkB,CAAC,iCAAiC,CAAC,CAAC;AAE1G,MAAM,MAAM,kCAAkC,GAAG,CAAC;IACjD,EAAE,EAAE,IAAI,CAAC;CACT,GAAG,sCAAsC,CAAC,GAAG,CAAC;IAC9C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH,MAAM,MAAM,sCAAsC,GAAG,kBAAkB,CAAC,kCAAkC,CAAC,CAAC;AAE5G,MAAM,WAAW,+BAA+B;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;IACzB,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,sCAAsC;IACtD,QAAQ,CAAC,EAAE,IAAI,CAAC,wCAAwC,EAAE,MAAM,CAAC,GAAG;QACnE,MAAM,EAAE,eAAe,CAAC;KACxB,CAAC;IAEF,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;CACzB;AAGD,MAAM,WAAW,+BAAgC,SAAQ,sCAAsC;IAC9F,MAAM,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,MAAM,mCAAmC,GAAG,kBAAkB,CAAC,+BAA+B,CAAC,CAAC;AAEtG,MAAM,MAAM,oCAAoC,GAAG,CAAC;IACnD,EAAE,EAAE,IAAI,CAAC;CACT,CAAC,GAAG,CAAC;IACL,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAGH,MAAM,WAAW,wCAAwC;IACxD,OAAO,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,iCAAkC,SAAQ,wCAAwC;IAClG,MAAM,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,MAAM,qCAAqC,GAAG,kBAAkB,CAAC,iCAAiC,CAAC,CAAC;AAE1G,MAAM,MAAM,sCAAsC,GAAG,CAAC;IACrD,EAAE,EAAE,IAAI,CAAC;CACT,CAAC,GAAG,CAAC;IACL,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAeH,wBAAgB,kCAAkC,CAAC,kBAAkB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,4BAA4B,CAK/H;AA8BD,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,4BAA4B,CAOpG;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,4BAA4B,GAAG,kBAAkB,CAEzG;AAID,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,sCAAsC,GAAG,QAAQ,CAalG;AAED,MAAM,WAAW,wCAAwC;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,eAAe,CAAC;IACpB,IAAI,EAAE,eAAe,CAAC;CACtB;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,wCAAwC,GAAG,QAAQ,CAQvG;AAGD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,wCAAwC,GAAG,QAAQ,CAMtG;AAED,UAAU,sDAAsD;IAC/D,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,KAAK,gDAAgD,GAAG,UAAU,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,GAAG,sDAAsD,CAAC;AAE5J,cAAM,4CAA6C,SAAQ,oBAAqB,YAAW,sDAAsD;IAChJ,gBAAyB,IAAI,EAAE,MAAM,CAAkD;IACvF,OAAO,CAAC,QAAQ,CAAC,wDAAwD,CAAU;IACnF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,wDAAwD,CAA0C;IAC1H,SAAkB,QAAQ,UAAU;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,UAAU,EAAE,sDAAsD,EAAE,OAAO,CAAC,EAAE,MAAM;IAYhG,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,4CAA4C;IAIxF,MAAM,IAAI,gDAAgD;WAO7C,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,4CAA4C,CAAC;CAW5F;AAED,UAAU,8CAA8C;IACvD,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CACtC;AAED,KAAK,wCAAwC,GAAG,UAAU,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,GAAG,8CAA8C,CAAC;AAE5I,cAAM,oCAAqC,SAAQ,oBAAqB,YAAW,8CAA8C;IAChI,gBAAyB,IAAI,EAAE,MAAM,CAA0C;IAC/E,OAAO,CAAC,QAAQ,CAAC,gDAAgD,CAAU;IAC3E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gDAAgD,CAA0C;IAClH,SAAkB,QAAQ,UAAU;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAC;gBAElC,UAAU,EAAE,8CAA8C,EAAE,OAAO,CAAC,EAAE,MAAM;IAaxF,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oCAAoC;IAIhF,MAAM,IAAI,wCAAwC;WAOrC,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,oCAAoC,CAAC;CAuBpF;AAED,eAAO,MAAM,MAAM,EAAE;IACpB,oBAAoB,EAAE,OAAO,4CAA4C,CAAC;IAC1E,YAAY,EAAE,OAAO,oCAAoC,CAAC;IAC1D,mBAAmB,EAAE,OAAO,mCAAmC,CAAC;CAKhE,CAAC"}
|