was-teaching-server 0.9.1
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/CHANGELOG.md +1432 -0
- package/LICENSE +661 -0
- package/README.md +305 -0
- package/common/css/materialize.1.0.0.min.css +13 -0
- package/dist/auth-header-hooks.d.ts +84 -0
- package/dist/auth-header-hooks.d.ts.map +1 -0
- package/dist/auth-header-hooks.js +136 -0
- package/dist/auth-header-hooks.js.map +1 -0
- package/dist/authorize.d.ts +45 -0
- package/dist/authorize.d.ts.map +1 -0
- package/dist/authorize.js +76 -0
- package/dist/authorize.js.map +1 -0
- package/dist/backends/filesystem.d.ts +1179 -0
- package/dist/backends/filesystem.d.ts.map +1 -0
- package/dist/backends/filesystem.js +2727 -0
- package/dist/backends/filesystem.js.map +1 -0
- package/dist/backends/postgres.d.ts +833 -0
- package/dist/backends/postgres.d.ts.map +1 -0
- package/dist/backends/postgres.js +2164 -0
- package/dist/backends/postgres.js.map +1 -0
- package/dist/backends/postgresSchema.d.ts +36 -0
- package/dist/backends/postgresSchema.d.ts.map +1 -0
- package/dist/backends/postgresSchema.js +203 -0
- package/dist/backends/postgresSchema.js.map +1 -0
- package/dist/config.default.d.ts +360 -0
- package/dist/config.default.d.ts.map +1 -0
- package/dist/config.default.js +493 -0
- package/dist/config.default.js.map +1 -0
- package/dist/corsProxy.d.ts +45 -0
- package/dist/corsProxy.d.ts.map +1 -0
- package/dist/corsProxy.js +278 -0
- package/dist/corsProxy.js.map +1 -0
- package/dist/digest.d.ts +45 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +174 -0
- package/dist/digest.js.map +1 -0
- package/dist/errors.d.ts +654 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +960 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/atomicFile.d.ts +61 -0
- package/dist/lib/atomicFile.d.ts.map +1 -0
- package/dist/lib/atomicFile.js +150 -0
- package/dist/lib/atomicFile.js.map +1 -0
- package/dist/lib/backendRegistry.d.ts +59 -0
- package/dist/lib/backendRegistry.d.ts.map +1 -0
- package/dist/lib/backendRegistry.js +92 -0
- package/dist/lib/backendRegistry.js.map +1 -0
- package/dist/lib/backendUsage.d.ts +28 -0
- package/dist/lib/backendUsage.d.ts.map +1 -0
- package/dist/lib/backendUsage.js +55 -0
- package/dist/lib/backendUsage.js.map +1 -0
- package/dist/lib/backends.d.ts +142 -0
- package/dist/lib/backends.d.ts.map +1 -0
- package/dist/lib/backends.js +272 -0
- package/dist/lib/backends.js.map +1 -0
- package/dist/lib/blindedIndex.d.ts +156 -0
- package/dist/lib/blindedIndex.d.ts.map +1 -0
- package/dist/lib/blindedIndex.js +338 -0
- package/dist/lib/blindedIndex.js.map +1 -0
- package/dist/lib/collectionDescription.d.ts +33 -0
- package/dist/lib/collectionDescription.d.ts.map +1 -0
- package/dist/lib/collectionDescription.js +21 -0
- package/dist/lib/collectionDescription.js.map +1 -0
- package/dist/lib/cursor.d.ts +18 -0
- package/dist/lib/cursor.d.ts.map +1 -0
- package/dist/lib/cursor.js +50 -0
- package/dist/lib/cursor.js.map +1 -0
- package/dist/lib/edvEnvelope.d.ts +50 -0
- package/dist/lib/edvEnvelope.d.ts.map +1 -0
- package/dist/lib/edvEnvelope.js +106 -0
- package/dist/lib/edvEnvelope.js.map +1 -0
- package/dist/lib/encryption.d.ts +192 -0
- package/dist/lib/encryption.d.ts.map +1 -0
- package/dist/lib/encryption.js +383 -0
- package/dist/lib/encryption.js.map +1 -0
- package/dist/lib/etag.d.ts +33 -0
- package/dist/lib/etag.d.ts.map +1 -0
- package/dist/lib/etag.js +36 -0
- package/dist/lib/etag.js.map +1 -0
- package/dist/lib/exportManifest.d.ts +29 -0
- package/dist/lib/exportManifest.d.ts.map +1 -0
- package/dist/lib/exportManifest.js +77 -0
- package/dist/lib/exportManifest.js.map +1 -0
- package/dist/lib/importTar.d.ts +81 -0
- package/dist/lib/importTar.d.ts.map +1 -0
- package/dist/lib/importTar.js +301 -0
- package/dist/lib/importTar.js.map +1 -0
- package/dist/lib/isJson.d.ts +16 -0
- package/dist/lib/isJson.d.ts.map +1 -0
- package/dist/lib/isJson.js +17 -0
- package/dist/lib/isJson.js.map +1 -0
- package/dist/lib/keyEpoch.d.ts +38 -0
- package/dist/lib/keyEpoch.d.ts.map +1 -0
- package/dist/lib/keyEpoch.js +75 -0
- package/dist/lib/keyEpoch.js.map +1 -0
- package/dist/lib/keyedMutex.d.ts +28 -0
- package/dist/lib/keyedMutex.d.ts.map +1 -0
- package/dist/lib/keyedMutex.js +45 -0
- package/dist/lib/keyedMutex.js.map +1 -0
- package/dist/lib/kmsModule.d.ts +62 -0
- package/dist/lib/kmsModule.d.ts.map +1 -0
- package/dist/lib/kmsModule.js +327 -0
- package/dist/lib/kmsModule.js.map +1 -0
- package/dist/lib/kmsRecordCipher.d.ts +82 -0
- package/dist/lib/kmsRecordCipher.d.ts.map +1 -0
- package/dist/lib/kmsRecordCipher.js +226 -0
- package/dist/lib/kmsRecordCipher.js.map +1 -0
- package/dist/lib/pagination.d.ts +19 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +21 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/paths.d.ts +273 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +270 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/preconditions.d.ts +65 -0
- package/dist/lib/preconditions.d.ts.map +1 -0
- package/dist/lib/preconditions.js +112 -0
- package/dist/lib/preconditions.js.map +1 -0
- package/dist/lib/resourceFileName.d.ts +41 -0
- package/dist/lib/resourceFileName.d.ts.map +1 -0
- package/dist/lib/resourceFileName.js +58 -0
- package/dist/lib/resourceFileName.js.map +1 -0
- package/dist/lib/revocations.d.ts +49 -0
- package/dist/lib/revocations.d.ts.map +1 -0
- package/dist/lib/revocations.js +86 -0
- package/dist/lib/revocations.js.map +1 -0
- package/dist/lib/validateDid.d.ts +19 -0
- package/dist/lib/validateDid.d.ts.map +1 -0
- package/dist/lib/validateDid.js +35 -0
- package/dist/lib/validateDid.js.map +1 -0
- package/dist/lib/validateId.d.ts +66 -0
- package/dist/lib/validateId.d.ts.map +1 -0
- package/dist/lib/validateId.js +126 -0
- package/dist/lib/validateId.js.map +1 -0
- package/dist/plugin.d.ts +115 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +134 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policy.d.ts +78 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +115 -0
- package/dist/policy.js.map +1 -0
- package/dist/provisioning.d.ts +38 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +72 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/requests/BackendRequest.d.ts +61 -0
- package/dist/requests/BackendRequest.d.ts.map +1 -0
- package/dist/requests/BackendRequest.js +152 -0
- package/dist/requests/BackendRequest.js.map +1 -0
- package/dist/requests/CollectionRequest.d.ts +234 -0
- package/dist/requests/CollectionRequest.d.ts.map +1 -0
- package/dist/requests/CollectionRequest.js +705 -0
- package/dist/requests/CollectionRequest.js.map +1 -0
- package/dist/requests/KeyRequest.d.ts +115 -0
- package/dist/requests/KeyRequest.d.ts.map +1 -0
- package/dist/requests/KeyRequest.js +468 -0
- package/dist/requests/KeyRequest.js.map +1 -0
- package/dist/requests/KeystoreRequest.d.ts +99 -0
- package/dist/requests/KeystoreRequest.d.ts.map +1 -0
- package/dist/requests/KeystoreRequest.js +247 -0
- package/dist/requests/KeystoreRequest.js.map +1 -0
- package/dist/requests/PolicyRequest.d.ts +53 -0
- package/dist/requests/PolicyRequest.d.ts.map +1 -0
- package/dist/requests/PolicyRequest.js +124 -0
- package/dist/requests/PolicyRequest.js.map +1 -0
- package/dist/requests/ResourceRequest.d.ts +134 -0
- package/dist/requests/ResourceRequest.d.ts.map +1 -0
- package/dist/requests/ResourceRequest.js +548 -0
- package/dist/requests/ResourceRequest.js.map +1 -0
- package/dist/requests/RevocationRequest.d.ts +67 -0
- package/dist/requests/RevocationRequest.d.ts.map +1 -0
- package/dist/requests/RevocationRequest.js +187 -0
- package/dist/requests/RevocationRequest.js.map +1 -0
- package/dist/requests/SpaceRequest.d.ts +204 -0
- package/dist/requests/SpaceRequest.d.ts.map +1 -0
- package/dist/requests/SpaceRequest.js +520 -0
- package/dist/requests/SpaceRequest.js.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts +50 -0
- package/dist/requests/SpacesRepositoryRequest.d.ts.map +1 -0
- package/dist/requests/SpacesRepositoryRequest.js +159 -0
- package/dist/requests/SpacesRepositoryRequest.js.map +1 -0
- package/dist/requests/collectionContext.d.ts +21 -0
- package/dist/requests/collectionContext.d.ts.map +1 -0
- package/dist/requests/collectionContext.js +30 -0
- package/dist/requests/collectionContext.js.map +1 -0
- package/dist/requests/controllerConsent.d.ts +58 -0
- package/dist/requests/controllerConsent.d.ts.map +1 -0
- package/dist/requests/controllerConsent.js +73 -0
- package/dist/requests/controllerConsent.js.map +1 -0
- package/dist/requests/keystoreContext.d.ts +68 -0
- package/dist/requests/keystoreContext.d.ts.map +1 -0
- package/dist/requests/keystoreContext.js +87 -0
- package/dist/requests/keystoreContext.js.map +1 -0
- package/dist/requests/resourceInput.d.ts +19 -0
- package/dist/requests/resourceInput.d.ts.map +1 -0
- package/dist/requests/resourceInput.js +102 -0
- package/dist/requests/resourceInput.js.map +1 -0
- package/dist/requests/spaceContext.d.ts +127 -0
- package/dist/requests/spaceContext.d.ts.map +1 -0
- package/dist/requests/spaceContext.js +201 -0
- package/dist/requests/spaceContext.js.map +1 -0
- package/dist/routes.d.ts +55 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +306 -0
- package/dist/routes.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +60 -0
- package/dist/server.js.map +1 -0
- package/dist/start.d.ts +7 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +61 -0
- package/dist/start.js.map +1 -0
- package/dist/storage.d.ts +29 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +47 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +915 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/views/home.hbs +9 -0
- package/dist/views/templates/main.hbs +12 -0
- package/dist/zcap.d.ts +267 -0
- package/dist/zcap.d.ts.map +1 -0
- package/dist/zcap.js +478 -0
- package/dist/zcap.js.map +1 -0
- package/package.json +109 -0
- package/src/auth-header-hooks.ts +163 -0
- package/src/authorize.ts +117 -0
- package/src/backends/filesystem.ts +3626 -0
- package/src/backends/postgres.ts +3073 -0
- package/src/backends/postgresSchema.ts +231 -0
- package/src/config.default.ts +657 -0
- package/src/corsProxy.ts +326 -0
- package/src/declarations.d.ts +28 -0
- package/src/digest.ts +223 -0
- package/src/errors.ts +1122 -0
- package/src/index.ts +21 -0
- package/src/lib/atomicFile.ts +176 -0
- package/src/lib/backendRegistry.ts +139 -0
- package/src/lib/backendUsage.ts +79 -0
- package/src/lib/backends.ts +351 -0
- package/src/lib/blindedIndex.ts +466 -0
- package/src/lib/collectionDescription.ts +43 -0
- package/src/lib/cursor.ts +52 -0
- package/src/lib/edvEnvelope.ts +120 -0
- package/src/lib/encryption.ts +472 -0
- package/src/lib/etag.ts +40 -0
- package/src/lib/exportManifest.ts +115 -0
- package/src/lib/importTar.ts +381 -0
- package/src/lib/isJson.ts +18 -0
- package/src/lib/keyEpoch.ts +89 -0
- package/src/lib/keyedMutex.ts +45 -0
- package/src/lib/kmsModule.ts +451 -0
- package/src/lib/kmsRecordCipher.ts +292 -0
- package/src/lib/pagination.ts +22 -0
- package/src/lib/paths.ts +382 -0
- package/src/lib/preconditions.ts +145 -0
- package/src/lib/resourceFileName.ts +69 -0
- package/src/lib/revocations.ts +116 -0
- package/src/lib/validateDid.ts +41 -0
- package/src/lib/validateId.ts +148 -0
- package/src/plugin.ts +288 -0
- package/src/policy.ts +176 -0
- package/src/provisioning.ts +98 -0
- package/src/requests/BackendRequest.ts +219 -0
- package/src/requests/CollectionRequest.ts +899 -0
- package/src/requests/KeyRequest.ts +617 -0
- package/src/requests/KeystoreRequest.ts +325 -0
- package/src/requests/PolicyRequest.ts +169 -0
- package/src/requests/ResourceRequest.ts +649 -0
- package/src/requests/RevocationRequest.ts +267 -0
- package/src/requests/SpaceRequest.ts +686 -0
- package/src/requests/SpacesRepositoryRequest.ts +194 -0
- package/src/requests/collectionContext.ts +41 -0
- package/src/requests/controllerConsent.ts +108 -0
- package/src/requests/keystoreContext.ts +124 -0
- package/src/requests/resourceInput.ts +118 -0
- package/src/requests/spaceContext.ts +311 -0
- package/src/routes.ts +420 -0
- package/src/server.ts +69 -0
- package/src/start.ts +71 -0
- package/src/storage.ts +60 -0
- package/src/types.ts +1016 -0
- package/src/views/home.hbs +9 -0
- package/src/views/templates/main.hbs +12 -0
- package/src/zcap.ts +657 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { isJson } from '../lib/isJson.js';
|
|
3
|
+
import { MissingContentTypeError, InvalidRequestBodyError, PayloadTooLargeError } from '../errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* @param request {import('fastify').FastifyRequest}
|
|
6
|
+
* @param [dataBackend] {StorageBackend} the resolved data-plane backend the
|
|
7
|
+
* write targets (the Collection's selected backend); its `maxUploadBytes` and
|
|
8
|
+
* `describe().id` size and label the `payload-too-large` (413) for a multipart
|
|
9
|
+
* upload. Defaults to the server `storage` backend, read lazily only on the
|
|
10
|
+
* multipart path so non-multipart callers need not supply it.
|
|
11
|
+
* @returns {Promise<ResourceInput>}
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveResourceInput(request, dataBackend) {
|
|
14
|
+
const contentType = request.headers['content-type'];
|
|
15
|
+
if (!contentType) {
|
|
16
|
+
throw new MissingContentTypeError({ requestName: 'Write Resource' });
|
|
17
|
+
}
|
|
18
|
+
if (isJson({ contentType })) {
|
|
19
|
+
return { kind: 'json', contentType, data: request.body };
|
|
20
|
+
}
|
|
21
|
+
if (contentType.startsWith('multipart')) {
|
|
22
|
+
// Spec ("Content Types and Representations"): a multipart write MUST carry
|
|
23
|
+
// exactly one file part -- reject zero file parts, or more than one, as
|
|
24
|
+
// `invalid-request-body` (400). Iterate every part rather than calling
|
|
25
|
+
// `request.file()` (which silently takes the first file part and ignores any
|
|
26
|
+
// extras). `toBuffer()` drains each file part's stream, which both lets the
|
|
27
|
+
// iterator advance to detect a second (disallowed) part and yields the exact
|
|
28
|
+
// byte length to pre-flight the upload cap. Buffering the part in memory is
|
|
29
|
+
// acceptable for this multipart convenience path (the HTML form workflow) --
|
|
30
|
+
// large binaries should use the streaming raw-body path. The buffer is
|
|
31
|
+
// bounded by the backend's `maxUploadBytes` (the multipart `fileSize` limit
|
|
32
|
+
// set in `plugin.ts`): `toBuffer()` throws `FST_REQ_FILE_TOO_LARGE` at the
|
|
33
|
+
// boundary, mapped here to `payload-too-large` (413).
|
|
34
|
+
const backend = dataBackend ?? request.server.storage;
|
|
35
|
+
let file;
|
|
36
|
+
for await (const part of request.parts()) {
|
|
37
|
+
if (part.type !== 'file') {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (file) {
|
|
41
|
+
throw new InvalidRequestBodyError({
|
|
42
|
+
requestName: 'Write Resource',
|
|
43
|
+
detail: 'multipart request must carry exactly one file part.'
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
file = { mimetype: part.mimetype, bytes: await part.toBuffer() };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err.code === 'FST_REQ_FILE_TOO_LARGE') {
|
|
51
|
+
throw new PayloadTooLargeError({
|
|
52
|
+
maxUploadBytes: backend.maxUploadBytes,
|
|
53
|
+
backendId: backend.describe().id
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!file) {
|
|
60
|
+
throw new InvalidRequestBodyError({
|
|
61
|
+
requestName: 'Write Resource',
|
|
62
|
+
detail: 'multipart request is missing a file part.'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
kind: 'binary',
|
|
67
|
+
contentType: file.mimetype,
|
|
68
|
+
stream: Readable.from(file.bytes),
|
|
69
|
+
declaredBytes: file.bytes.length
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
// A `text/plain` body arrives already parsed as a **string** (Fastify's
|
|
73
|
+
// built-in text parser, which the `*` catch-all does not shadow); other
|
|
74
|
+
// `text/*` and binary media types arrive as a raw stream. Wrap a string (or
|
|
75
|
+
// Buffer) as a byte stream so it is written verbatim -- piping a string into
|
|
76
|
+
// the backend's `stream.pipeline` would iterate it by UTF-16 code unit (one
|
|
77
|
+
// write per character, splitting astral-plane characters into lone
|
|
78
|
+
// surrogates), corrupting the stored bytes.
|
|
79
|
+
const body = request.body;
|
|
80
|
+
if (typeof body === 'string' || Buffer.isBuffer(body)) {
|
|
81
|
+
const bytes = Buffer.from(body);
|
|
82
|
+
return {
|
|
83
|
+
kind: 'binary',
|
|
84
|
+
contentType,
|
|
85
|
+
stream: Readable.from(bytes),
|
|
86
|
+
declaredBytes: bytes.length
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// A raw (non-multipart) blob body carries its size in `Content-Length` when
|
|
90
|
+
// present; expose it as `declaredBytes` so the backend can pre-flight the
|
|
91
|
+
// quota check before streaming. Ignore an absent or malformed value.
|
|
92
|
+
const contentLength = Number(request.headers['content-length']);
|
|
93
|
+
return {
|
|
94
|
+
kind: 'binary',
|
|
95
|
+
contentType,
|
|
96
|
+
stream: body,
|
|
97
|
+
declaredBytes: Number.isInteger(contentLength) && contentLength >= 0
|
|
98
|
+
? contentLength
|
|
99
|
+
: undefined
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=resourceInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resourceInput.js","sourceRoot":"","sources":["../../src/requests/resourceInput.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared handler prelude: fetch a Space (for its controller) and authorize the
|
|
3
|
+
* request against a target. Nearly every handler repeats the same shape -- load
|
|
4
|
+
* the Space Description to get the controller key, build the capability's
|
|
5
|
+
* `invocationTarget` URL, then check authorization -- so it lives here.
|
|
6
|
+
*
|
|
7
|
+
* Two entry points, one per authorization model, so the call site names which it
|
|
8
|
+
* uses (rather than passing a flag):
|
|
9
|
+
*
|
|
10
|
+
* - `fetchSpaceAndAuthorize` -- **capability-or-policy**: capability invocation
|
|
11
|
+
* first, then the target's effective access-control policy as a fallback. For
|
|
12
|
+
* read/list endpoints that may be public-readable.
|
|
13
|
+
* - `fetchSpaceAndVerify` -- **capability-only**: capability invocation, no
|
|
14
|
+
* policy fallback. For write/privileged endpoints and the controller-managed
|
|
15
|
+
* policy resource.
|
|
16
|
+
*/
|
|
17
|
+
import type { FastifyRequest } from 'fastify';
|
|
18
|
+
import type { IDID, SpaceDescription, StorageBackend } from '../types.js';
|
|
19
|
+
/**
|
|
20
|
+
* Drops the cached Space Description for a Space. Call after any write that
|
|
21
|
+
* changes (or removes) it -- create/update/delete -- so the next read reflects
|
|
22
|
+
* the new state rather than a stale cached one.
|
|
23
|
+
* @param options {object}
|
|
24
|
+
* @param options.storage {StorageBackend} the request's storage backend
|
|
25
|
+
* @param options.spaceId {string}
|
|
26
|
+
* @returns {void}
|
|
27
|
+
*/
|
|
28
|
+
export declare function invalidateSpaceDescription({ storage, spaceId }: {
|
|
29
|
+
storage: StorageBackend;
|
|
30
|
+
spaceId: string;
|
|
31
|
+
}): void;
|
|
32
|
+
/**
|
|
33
|
+
* Loads a Space Description by its URL param, masking an unknown (or
|
|
34
|
+
* non-URL-safe) id as the 404 `SpaceNotFoundError` -- the WAS
|
|
35
|
+
* existence-masking convention. The verification-free half of
|
|
36
|
+
* `fetchSpaceAndVerify`, for handlers that need the controller before they can
|
|
37
|
+
* build their expected values (the revocation route, whose invocation verifies
|
|
38
|
+
* under the dual-root rule rather than the usual single root). The Space
|
|
39
|
+
* analogue of `fetchKeystore` (keystoreContext.ts).
|
|
40
|
+
*
|
|
41
|
+
* @param options {object}
|
|
42
|
+
* @param options.request {FastifyRequest} supplies `request.server.storage`
|
|
43
|
+
* @param options.spaceId {string}
|
|
44
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
45
|
+
* error titles
|
|
46
|
+
* @returns {Promise<SpaceDescription>}
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchSpace({ request, spaceId, requestName }: {
|
|
49
|
+
request: FastifyRequest;
|
|
50
|
+
spaceId: string;
|
|
51
|
+
requestName: string;
|
|
52
|
+
}): Promise<SpaceDescription>;
|
|
53
|
+
/** The verified context every handler builds before touching storage. */
|
|
54
|
+
export interface VerifiedSpaceContext {
|
|
55
|
+
/** the fetched Space Description (its controller authorized the request) */
|
|
56
|
+
spaceDescription: SpaceDescription;
|
|
57
|
+
/** the did:key that controls the Space */
|
|
58
|
+
spaceController: IDID;
|
|
59
|
+
/** the resolved invocationTarget URL the request was authorized against */
|
|
60
|
+
allowedTarget: string;
|
|
61
|
+
/**
|
|
62
|
+
* the Space's own URL -- the ancestor root target every space-family route
|
|
63
|
+
* also accepts for a delegated chain that attenuates down to the request
|
|
64
|
+
* URL (a Space- or Collection-scoped capability delegated by the controller)
|
|
65
|
+
*/
|
|
66
|
+
spaceRootTarget: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Fetches the Space and AUTHORIZES the request **capability-or-policy**:
|
|
70
|
+
* capability invocation first, then the target's effective access-control policy
|
|
71
|
+
* as a fallback (see authorize.ts). Use for read/list endpoints that may be
|
|
72
|
+
* public-readable. The action checked is the request's HTTP method. Returns the
|
|
73
|
+
* fetched Space Description so callers that also serve it (e.g. "Read Space")
|
|
74
|
+
* need not fetch it twice.
|
|
75
|
+
*
|
|
76
|
+
* @param options {object}
|
|
77
|
+
* @param options.request {FastifyRequest} supplies url, method, headers,
|
|
78
|
+
* logger, and `request.server` for serverUrl + storage
|
|
79
|
+
* @param options.spaceId {string}
|
|
80
|
+
* @param [options.collectionId] {string} policy-resolution level
|
|
81
|
+
* @param [options.resourceId] {string} policy-resolution level
|
|
82
|
+
* @param options.targetPath {string} the relative path of the capability's
|
|
83
|
+
* invocationTarget, resolved against serverUrl (e.g. `/space/${spaceId}`)
|
|
84
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
85
|
+
* error titles
|
|
86
|
+
* @returns {Promise<VerifiedSpaceContext>}
|
|
87
|
+
*/
|
|
88
|
+
export declare function fetchSpaceAndAuthorize({ request, spaceId, collectionId, resourceId, targetPath, requestName, allowTargetQuery }: {
|
|
89
|
+
request: FastifyRequest;
|
|
90
|
+
spaceId: string;
|
|
91
|
+
collectionId?: string;
|
|
92
|
+
resourceId?: string;
|
|
93
|
+
targetPath: string;
|
|
94
|
+
requestName: string;
|
|
95
|
+
/**
|
|
96
|
+
* When set, the capability-invocation path tolerates query parameters on
|
|
97
|
+
* the request URL that extend `targetPath` (e.g. List Collection's
|
|
98
|
+
* `?limit`/`cursor`), treating them as a RESTful attenuation of the same
|
|
99
|
+
* target rather than a different one. See `verifyZcap`.
|
|
100
|
+
*/
|
|
101
|
+
allowTargetQuery?: boolean;
|
|
102
|
+
}): Promise<VerifiedSpaceContext>;
|
|
103
|
+
/**
|
|
104
|
+
* Fetches the Space and VERIFIES the request **capability-only**: a valid
|
|
105
|
+
* capability invocation is required, with no access-control-policy fallback (see
|
|
106
|
+
* zcap.ts). Use for write/privileged endpoints and the controller-managed policy
|
|
107
|
+
* resource. The action checked is the request's HTTP method. The Space's
|
|
108
|
+
* revocation store is consulted on every delegated chain, so a revoked
|
|
109
|
+
* capability fails here as it does on the `/kms` routes.
|
|
110
|
+
*
|
|
111
|
+
* @param options {object}
|
|
112
|
+
* @param options.request {FastifyRequest} supplies url, method, headers,
|
|
113
|
+
* logger, and `request.server` for serverUrl + storage
|
|
114
|
+
* @param options.spaceId {string}
|
|
115
|
+
* @param options.targetPath {string} the relative path of the capability's
|
|
116
|
+
* invocationTarget, resolved against serverUrl (e.g. `/space/${spaceId}`)
|
|
117
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
118
|
+
* error titles
|
|
119
|
+
* @returns {Promise<VerifiedSpaceContext>}
|
|
120
|
+
*/
|
|
121
|
+
export declare function fetchSpaceAndVerify({ request, spaceId, targetPath, requestName }: {
|
|
122
|
+
request: FastifyRequest;
|
|
123
|
+
spaceId: string;
|
|
124
|
+
targetPath: string;
|
|
125
|
+
requestName: string;
|
|
126
|
+
}): Promise<VerifiedSpaceContext>;
|
|
127
|
+
//# sourceMappingURL=spaceContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceContext.d.ts","sourceRoot":"","sources":["../../src/requests/spaceContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAW7C,OAAO,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AA2BzE;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,OAAO,EACP,OAAO,EACR,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,IAAI,CAGP;AAkCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAAC,EAC/B,OAAO,EACP,OAAO,EACP,WAAW,EACZ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAW5B;AAED,yEAAyE;AACzE,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,0CAA0C;IAC1C,eAAe,EAAE,IAAI,CAAA;IACrB,2EAA2E;IAC3E,aAAa,EAAE,MAAM,CAAA;IACrB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAA;CACxB;AAuCD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAwB,EACzB,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmBhC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,mBAAmB,CAAC,EACxC,OAAO,EACP,OAAO,EACP,UAAU,EACV,WAAW,EACZ,EAAE;IACD,OAAO,EAAE,cAAc,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuBhC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { LruCache } from '@interop/lru-memoize';
|
|
2
|
+
import { handleZcapVerify } from '../zcap.js';
|
|
3
|
+
import { authorize } from '../authorize.js';
|
|
4
|
+
import { spacePath } from '../lib/paths.js';
|
|
5
|
+
import { isUrlSafeSegment } from '../lib/validateId.js';
|
|
6
|
+
import { SpaceNotFoundError } from '../errors.js';
|
|
7
|
+
import { SPACE_DESCRIPTION_CACHE_MAX, SPACE_DESCRIPTION_CACHE_TTL } from '../config.default.js';
|
|
8
|
+
/**
|
|
9
|
+
* One short-TTL memoization cache per storage backend, keyed by `spaceId`. The
|
|
10
|
+
* cache is scoped to the backend instance (rather than module-global) via a
|
|
11
|
+
* WeakMap so two backends in one process -- e.g. parallel test suites -- never
|
|
12
|
+
* serve each other's descriptions, and a cache is discarded with its backend.
|
|
13
|
+
*/
|
|
14
|
+
const descriptionCaches = new WeakMap();
|
|
15
|
+
/**
|
|
16
|
+
* Returns the (lazily created) Space Description cache for a backend.
|
|
17
|
+
* @param storage {StorageBackend}
|
|
18
|
+
* @returns {LruCache}
|
|
19
|
+
*/
|
|
20
|
+
function descriptionCacheFor(storage) {
|
|
21
|
+
let cache = descriptionCaches.get(storage);
|
|
22
|
+
if (!cache) {
|
|
23
|
+
cache = new LruCache({
|
|
24
|
+
max: SPACE_DESCRIPTION_CACHE_MAX,
|
|
25
|
+
ttl: SPACE_DESCRIPTION_CACHE_TTL
|
|
26
|
+
});
|
|
27
|
+
descriptionCaches.set(storage, cache);
|
|
28
|
+
}
|
|
29
|
+
return cache;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Drops the cached Space Description for a Space. Call after any write that
|
|
33
|
+
* changes (or removes) it -- create/update/delete -- so the next read reflects
|
|
34
|
+
* the new state rather than a stale cached one.
|
|
35
|
+
* @param options {object}
|
|
36
|
+
* @param options.storage {StorageBackend} the request's storage backend
|
|
37
|
+
* @param options.spaceId {string}
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
export function invalidateSpaceDescription({ storage, spaceId }) {
|
|
41
|
+
// Only touch a cache that already exists for this backend.
|
|
42
|
+
descriptionCaches.get(storage)?.delete(spaceId);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Fetches a Space Description or throws SpaceNotFoundError (404) when absent.
|
|
46
|
+
* Memoized per storage backend (short TTL) because this is read on every
|
|
47
|
+
* authorized handler; writes invalidate via `invalidateSpaceDescription`.
|
|
48
|
+
* @param options {object}
|
|
49
|
+
* @param options.storage {StorageBackend} the request's storage backend
|
|
50
|
+
* @param options.spaceId {string}
|
|
51
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
52
|
+
* error titles
|
|
53
|
+
* @returns {Promise<SpaceDescription>}
|
|
54
|
+
*/
|
|
55
|
+
async function getSpaceDescriptionOrThrow({ storage, spaceId, requestName }) {
|
|
56
|
+
const spaceDescription = await descriptionCacheFor(storage).memoize({
|
|
57
|
+
key: spaceId,
|
|
58
|
+
fn: () => storage.getSpaceDescription({ spaceId })
|
|
59
|
+
});
|
|
60
|
+
if (!spaceDescription) {
|
|
61
|
+
throw new SpaceNotFoundError({ requestName });
|
|
62
|
+
}
|
|
63
|
+
return spaceDescription;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Loads a Space Description by its URL param, masking an unknown (or
|
|
67
|
+
* non-URL-safe) id as the 404 `SpaceNotFoundError` -- the WAS
|
|
68
|
+
* existence-masking convention. The verification-free half of
|
|
69
|
+
* `fetchSpaceAndVerify`, for handlers that need the controller before they can
|
|
70
|
+
* build their expected values (the revocation route, whose invocation verifies
|
|
71
|
+
* under the dual-root rule rather than the usual single root). The Space
|
|
72
|
+
* analogue of `fetchKeystore` (keystoreContext.ts).
|
|
73
|
+
*
|
|
74
|
+
* @param options {object}
|
|
75
|
+
* @param options.request {FastifyRequest} supplies `request.server.storage`
|
|
76
|
+
* @param options.spaceId {string}
|
|
77
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
78
|
+
* error titles
|
|
79
|
+
* @returns {Promise<SpaceDescription>}
|
|
80
|
+
*/
|
|
81
|
+
export async function fetchSpace({ request, spaceId, requestName }) {
|
|
82
|
+
// A non-URL-safe id cannot name a stored Space and must not reach the
|
|
83
|
+
// filesystem layer: same 404 masking.
|
|
84
|
+
if (!isUrlSafeSegment(spaceId)) {
|
|
85
|
+
throw new SpaceNotFoundError({ requestName });
|
|
86
|
+
}
|
|
87
|
+
return await getSpaceDescriptionOrThrow({
|
|
88
|
+
storage: request.server.storage,
|
|
89
|
+
spaceId,
|
|
90
|
+
requestName
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Loads the Space and builds the capability's invocationTarget URL -- the part
|
|
95
|
+
* shared by both authorization models below. Does not check authorization.
|
|
96
|
+
*
|
|
97
|
+
* @param options {object}
|
|
98
|
+
* @param options.request {FastifyRequest} supplies `request.server` for
|
|
99
|
+
* serverUrl + storage
|
|
100
|
+
* @param options.spaceId {string}
|
|
101
|
+
* @param options.targetPath {string} the relative path of the capability's
|
|
102
|
+
* invocationTarget, resolved against serverUrl (e.g. `/space/${spaceId}`)
|
|
103
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
104
|
+
* error titles
|
|
105
|
+
* @returns {Promise<VerifiedSpaceContext>}
|
|
106
|
+
*/
|
|
107
|
+
async function fetchSpaceContext({ request, spaceId, targetPath, requestName }) {
|
|
108
|
+
const { serverUrl, storage } = request.server;
|
|
109
|
+
const spaceDescription = await getSpaceDescriptionOrThrow({
|
|
110
|
+
storage,
|
|
111
|
+
spaceId,
|
|
112
|
+
requestName
|
|
113
|
+
});
|
|
114
|
+
const spaceController = spaceDescription.controller;
|
|
115
|
+
const allowedTarget = new URL(targetPath, serverUrl).toString();
|
|
116
|
+
const spaceRootTarget = new URL(spacePath({ spaceId }), serverUrl).toString();
|
|
117
|
+
return { spaceDescription, spaceController, allowedTarget, spaceRootTarget };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Fetches the Space and AUTHORIZES the request **capability-or-policy**:
|
|
121
|
+
* capability invocation first, then the target's effective access-control policy
|
|
122
|
+
* as a fallback (see authorize.ts). Use for read/list endpoints that may be
|
|
123
|
+
* public-readable. The action checked is the request's HTTP method. Returns the
|
|
124
|
+
* fetched Space Description so callers that also serve it (e.g. "Read Space")
|
|
125
|
+
* need not fetch it twice.
|
|
126
|
+
*
|
|
127
|
+
* @param options {object}
|
|
128
|
+
* @param options.request {FastifyRequest} supplies url, method, headers,
|
|
129
|
+
* logger, and `request.server` for serverUrl + storage
|
|
130
|
+
* @param options.spaceId {string}
|
|
131
|
+
* @param [options.collectionId] {string} policy-resolution level
|
|
132
|
+
* @param [options.resourceId] {string} policy-resolution level
|
|
133
|
+
* @param options.targetPath {string} the relative path of the capability's
|
|
134
|
+
* invocationTarget, resolved against serverUrl (e.g. `/space/${spaceId}`)
|
|
135
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
136
|
+
* error titles
|
|
137
|
+
* @returns {Promise<VerifiedSpaceContext>}
|
|
138
|
+
*/
|
|
139
|
+
export async function fetchSpaceAndAuthorize({ request, spaceId, collectionId, resourceId, targetPath, requestName, allowTargetQuery = false }) {
|
|
140
|
+
const context = await fetchSpaceContext({
|
|
141
|
+
request,
|
|
142
|
+
spaceId,
|
|
143
|
+
targetPath,
|
|
144
|
+
requestName
|
|
145
|
+
});
|
|
146
|
+
await authorize({
|
|
147
|
+
request,
|
|
148
|
+
allowedTarget: context.allowedTarget,
|
|
149
|
+
spaceId,
|
|
150
|
+
collectionId,
|
|
151
|
+
resourceId,
|
|
152
|
+
spaceController: context.spaceController,
|
|
153
|
+
requestName,
|
|
154
|
+
allowTargetQuery,
|
|
155
|
+
attenuatedRootTarget: context.spaceRootTarget
|
|
156
|
+
});
|
|
157
|
+
return context;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Fetches the Space and VERIFIES the request **capability-only**: a valid
|
|
161
|
+
* capability invocation is required, with no access-control-policy fallback (see
|
|
162
|
+
* zcap.ts). Use for write/privileged endpoints and the controller-managed policy
|
|
163
|
+
* resource. The action checked is the request's HTTP method. The Space's
|
|
164
|
+
* revocation store is consulted on every delegated chain, so a revoked
|
|
165
|
+
* capability fails here as it does on the `/kms` routes.
|
|
166
|
+
*
|
|
167
|
+
* @param options {object}
|
|
168
|
+
* @param options.request {FastifyRequest} supplies url, method, headers,
|
|
169
|
+
* logger, and `request.server` for serverUrl + storage
|
|
170
|
+
* @param options.spaceId {string}
|
|
171
|
+
* @param options.targetPath {string} the relative path of the capability's
|
|
172
|
+
* invocationTarget, resolved against serverUrl (e.g. `/space/${spaceId}`)
|
|
173
|
+
* @param options.requestName {string} human-readable request name, used in
|
|
174
|
+
* error titles
|
|
175
|
+
* @returns {Promise<VerifiedSpaceContext>}
|
|
176
|
+
*/
|
|
177
|
+
export async function fetchSpaceAndVerify({ request, spaceId, targetPath, requestName }) {
|
|
178
|
+
const context = await fetchSpaceContext({
|
|
179
|
+
request,
|
|
180
|
+
spaceId,
|
|
181
|
+
targetPath,
|
|
182
|
+
requestName
|
|
183
|
+
});
|
|
184
|
+
const { url, method, headers } = request;
|
|
185
|
+
const { serverUrl, storage } = request.server;
|
|
186
|
+
await handleZcapVerify({
|
|
187
|
+
url,
|
|
188
|
+
allowedTarget: context.allowedTarget,
|
|
189
|
+
allowedAction: method,
|
|
190
|
+
method,
|
|
191
|
+
headers,
|
|
192
|
+
serverUrl,
|
|
193
|
+
spaceController: context.spaceController,
|
|
194
|
+
requestName,
|
|
195
|
+
logger: request.log,
|
|
196
|
+
attenuatedRootTarget: context.spaceRootTarget,
|
|
197
|
+
revocation: { storage, scope: { spaceId } }
|
|
198
|
+
});
|
|
199
|
+
return context;
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=spaceContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spaceContext.js","sourceRoot":"","sources":["../../src/requests/spaceContext.ts"],"names":[],"mappings":"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"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route layer: maps URL patterns to *Request handler methods. Each group first
|
|
3
|
+
* installs the `requireAuthHeadersOrPublicRead` then `parseAuthHeaders`
|
|
4
|
+
* onRequest hooks, and redirects slash/no-slash variants to the canonical form.
|
|
5
|
+
* (The WebKMS `/kms` group is the exception on both counts: it installs the
|
|
6
|
+
* strict `requireAuthHeaders` -- the webkms protocol has no public reads --
|
|
7
|
+
* and no slash redirects, since the protocol's URLs are exact.)
|
|
8
|
+
*/
|
|
9
|
+
import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
|
|
10
|
+
/**
|
|
11
|
+
* Registers SpacesRepository routes (POST/GET /spaces). Installs the
|
|
12
|
+
* `requireAuthHeadersOrPublicRead` then `parseAuthHeaders` onRequest hooks and
|
|
13
|
+
* the `handleError` error handler.
|
|
14
|
+
* @param app {import('fastify').FastifyInstance}
|
|
15
|
+
* @param options {object} Fastify plugin options
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
export declare function initSpacesRepositoryRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Registers Space routes (get/update/delete a Space, add/list Collections,
|
|
21
|
+
* export/import). Installs the auth hooks and the `handleError` error handler.
|
|
22
|
+
* @param app {import('fastify').FastifyInstance}
|
|
23
|
+
* @param options {object} Fastify plugin options
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
*/
|
|
26
|
+
export declare function initSpaceRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Registers Collection routes (get/update/delete a Collection, list its items,
|
|
29
|
+
* add a Resource). Installs the auth hooks and the `handleError` error handler.
|
|
30
|
+
* @param app {import('fastify').FastifyInstance}
|
|
31
|
+
* @param options {object} Fastify plugin options
|
|
32
|
+
* @returns {Promise<void>}
|
|
33
|
+
*/
|
|
34
|
+
export declare function initCollectionRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Registers Resource routes (create-by-id, get, delete a Resource). Installs the
|
|
37
|
+
* auth hooks and the `handleError` error handler.
|
|
38
|
+
* @param app {import('fastify').FastifyInstance}
|
|
39
|
+
* @param options {object} Fastify plugin options
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
export declare function initResourceRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Registers the WebKMS keystore and key routes (the `/kms` facet).
|
|
45
|
+
* Installs the same hook chain as the WAS groups
|
|
46
|
+
* except that the auth requirement is the strict `requireAuthHeaders`: every
|
|
47
|
+
* webkms route, GETs included, is zcap-invoked -- the protocol has no public
|
|
48
|
+
* reads. No slash-redirect variants either; the protocol's URLs are exact --
|
|
49
|
+
* only these shapes are registered.
|
|
50
|
+
* @param app {import('fastify').FastifyInstance}
|
|
51
|
+
* @param options {object} Fastify plugin options
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
export declare function initKmsRoutes(app: FastifyInstance, _options: FastifyPluginOptions): Promise<void>;
|
|
55
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EAGrB,MAAM,SAAS,CAAA;AAyEhB;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA8Ef;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAuDf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,eAAe,EACpB,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAkDf"}
|