@fedify/fedify 0.10.0-dev.190 → 0.10.0-dev.195
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/CHANGES.md +41 -1
- package/esm/deps/jsr.io/@std/encoding/0.224.3/base64url.js +83 -0
- package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.js +1 -1
- package/esm/federation/handler.js +1 -1
- package/esm/federation/middleware.js +435 -347
- package/esm/nodeinfo/types.js +1 -1
- package/esm/runtime/key.js +82 -0
- package/esm/testing/fixtures/w3id.org/security/multikey/v1 +35 -0
- package/esm/testing/fixtures/www.w3.org/ns/did/v1 +58 -0
- package/esm/vocab/application.yaml +15 -1
- package/esm/vocab/group.yaml +15 -1
- package/esm/vocab/multikey.yaml +33 -0
- package/esm/vocab/organization.yaml +15 -1
- package/esm/vocab/person.yaml +15 -1
- package/esm/vocab/service.yaml +15 -1
- package/esm/vocab/vocab.js +986 -13
- package/package.json +4 -1
- package/types/codegen/type.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/async/{0.224.1 → 0.224.2}/delay.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/encoding/0.224.3/base64url.d.ts +38 -0
- package/types/deps/jsr.io/@std/encoding/0.224.3/base64url.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/assert_path.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/basename.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/constants.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/dirname.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/normalize.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/normalize_string.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/_common/strip_trailing_separators.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/_util.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/basename.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/dirname.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/extname.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/join.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/path/1.0.0-rc.1/posix/normalize.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/semver/0.224.3/_constants.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.d.ts +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/mod.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/_util.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/case.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/closest_string.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/compare_similarity.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/levenshtein_distance.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/text/0.224.3/mod.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/text/{0.224.2 → 0.224.3}/word_similarity_sort.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/url/{0.224.0 → 0.224.1}/_strip.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/url/0.224.1/basename.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/url/0.224.1/dirname.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/url/0.224.1/extname.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/url/0.224.1/join.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/url/0.224.1/mod.d.ts.map +1 -0
- package/types/deps/jsr.io/@std/url/0.224.1/normalize.d.ts.map +1 -0
- package/types/federation/context.d.ts +6 -1
- package/types/federation/context.d.ts.map +1 -1
- package/types/federation/middleware.d.ts.map +1 -1
- package/types/nodeinfo/types.d.ts +1 -1
- package/types/runtime/key.d.ts +20 -0
- package/types/runtime/key.d.ts.map +1 -1
- package/types/vocab/vocab.d.ts +249 -0
- package/types/vocab/vocab.d.ts.map +1 -1
- package/types/deps/jsr.io/@std/assert/0.226.0/assert.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/assert/0.226.0/assertion_error.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/semver/0.224.2/_constants.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/text/0.224.2/mod.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/basename.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/dirname.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/extname.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/join.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/mod.d.ts.map +0 -1
- package/types/deps/jsr.io/@std/url/0.224.0/normalize.d.ts.map +0 -1
- /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.js +0 -0
- /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.js +0 -0
- /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.js +0 -0
- /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.js +0 -0
- /package/esm/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_constants.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.js +0 -0
- /package/esm/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.js +0 -0
- /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/common.d.ts +0 -0
- /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/encoding.d.ts +0 -0
- /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/language.d.ts +0 -0
- /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/_negotiation/media_type.d.ts +0 -0
- /package/types/deps/jsr.io/@std/http/{0.224.3 → 0.224.4}/negotiation.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_constants.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_shared.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/_test_comparator_set.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/can_parse.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/compare.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/constants.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/difference.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/equals.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/format_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_or_equal.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/greater_than_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/increment.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/is_semver.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_or_equal.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/less_than_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/max_satisfying.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/min_satisfying.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/not_equals.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/parse_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_intersects.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_max.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/range_min.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/satisfies.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/test_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/try_parse_range.d.ts +0 -0
- /package/types/deps/jsr.io/@std/semver/{0.224.2 → 0.224.3}/types.d.ts +0 -0
|
@@ -5,7 +5,7 @@ import { exportJwk, importJwk, validateCryptoKey } from "../sig/key.js";
|
|
|
5
5
|
import { getKeyOwner } from "../sig/owner.js";
|
|
6
6
|
import { handleNodeInfo, handleNodeInfoJrd } from "../nodeinfo/handler.js";
|
|
7
7
|
import { fetchDocumentLoader, getAuthenticatedDocumentLoader, kvCache, } from "../runtime/docloader.js";
|
|
8
|
-
import { Activity, CryptographicKey } from "../vocab/mod.js";
|
|
8
|
+
import { Activity, CryptographicKey, Multikey, } from "../vocab/mod.js";
|
|
9
9
|
import { handleWebFinger } from "../webfinger/handler.js";
|
|
10
10
|
import { buildCollectionSynchronizationHeader } from "./collection.js";
|
|
11
11
|
import { handleActor, handleCollection, handleInbox, handleObject, } from "./handler.js";
|
|
@@ -145,33 +145,12 @@ export class Federation {
|
|
|
145
145
|
}
|
|
146
146
|
logger.info("Successfully sent activity {activityId} to {inbox}.", { ...logData, activityId: activity?.id?.href });
|
|
147
147
|
}
|
|
148
|
-
async #getKeyPairsFromHandle(url, contextData, handle) {
|
|
149
|
-
const logger = getLogger(["fedify", "federation", "actor"]);
|
|
150
|
-
if (this.#actorCallbacks?.keyPairsDispatcher == null) {
|
|
151
|
-
throw new Error("No actor key pairs dispatcher registered.");
|
|
152
|
-
}
|
|
153
|
-
const path = this.#router.build("actor", { handle });
|
|
154
|
-
if (path == null) {
|
|
155
|
-
logger.warn("No actor dispatcher registered.");
|
|
156
|
-
return [];
|
|
157
|
-
}
|
|
158
|
-
const actorUri = new URL(path, url);
|
|
159
|
-
const keyPairs = await this.#actorCallbacks?.keyPairsDispatcher(contextData, handle);
|
|
160
|
-
if (keyPairs.length < 1) {
|
|
161
|
-
logger.warn("No key pairs found for actor {handle}.", { handle });
|
|
162
|
-
}
|
|
163
|
-
let i = 0;
|
|
164
|
-
const result = [];
|
|
165
|
-
for (const keyPair of keyPairs) {
|
|
166
|
-
result.push({
|
|
167
|
-
...keyPair,
|
|
168
|
-
keyId: new URL(i == 0 ? `#main-key` : `#key-${i + 1}`, actorUri),
|
|
169
|
-
});
|
|
170
|
-
i++;
|
|
171
|
-
}
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
148
|
createContext(urlOrRequest, contextData) {
|
|
149
|
+
return urlOrRequest instanceof Request
|
|
150
|
+
? this.#createContext(urlOrRequest, contextData)
|
|
151
|
+
: this.#createContext(urlOrRequest, contextData);
|
|
152
|
+
}
|
|
153
|
+
#createContext(urlOrRequest, contextData, opts = {}) {
|
|
175
154
|
const request = urlOrRequest instanceof Request ? urlOrRequest : null;
|
|
176
155
|
const url = urlOrRequest instanceof URL
|
|
177
156
|
? new URL(urlOrRequest)
|
|
@@ -183,304 +162,28 @@ export class Federation {
|
|
|
183
162
|
}
|
|
184
163
|
if (this.#treatHttps)
|
|
185
164
|
url.protocol = "https:";
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"SHA-256") {
|
|
194
|
-
return keyPair;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {handle}.", { handle });
|
|
198
|
-
return null;
|
|
199
|
-
};
|
|
200
|
-
const getAuthenticatedDocumentLoader = this.#authenticatedDocumentLoaderFactory;
|
|
201
|
-
const documentLoader = this.#documentLoader;
|
|
202
|
-
function getDocumentLoader(identity) {
|
|
203
|
-
if ("handle" in identity) {
|
|
204
|
-
const keyPair = getRsaKeyPairFromHandle(identity.handle);
|
|
205
|
-
if (keyPair == null)
|
|
206
|
-
return documentLoader;
|
|
207
|
-
return keyPair.then((pair) => pair == null ? documentLoader : getAuthenticatedDocumentLoader(pair));
|
|
208
|
-
}
|
|
209
|
-
return getAuthenticatedDocumentLoader(identity);
|
|
210
|
-
}
|
|
211
|
-
const context = {
|
|
165
|
+
const ctxOptions = {
|
|
166
|
+
url,
|
|
167
|
+
federation: this,
|
|
168
|
+
router: this.#router,
|
|
169
|
+
objectTypeIds: this.#objectTypeIds,
|
|
170
|
+
objectCallbacks: this.#objectCallbacks,
|
|
171
|
+
actorCallbacks: this.#actorCallbacks,
|
|
212
172
|
data: contextData,
|
|
213
|
-
documentLoader: this.#documentLoader,
|
|
173
|
+
documentLoader: opts.documentLoader ?? this.#documentLoader,
|
|
214
174
|
contextLoader: this.#contextLoader,
|
|
215
|
-
|
|
216
|
-
const path = this.#router.build("nodeInfo", {});
|
|
217
|
-
if (path == null) {
|
|
218
|
-
throw new RouterError("No NodeInfo dispatcher registered.");
|
|
219
|
-
}
|
|
220
|
-
return new URL(path, url);
|
|
221
|
-
},
|
|
222
|
-
getActorUri: (handle) => {
|
|
223
|
-
const path = this.#router.build("actor", { handle });
|
|
224
|
-
if (path == null) {
|
|
225
|
-
throw new RouterError("No actor dispatcher registered.");
|
|
226
|
-
}
|
|
227
|
-
return new URL(path, url);
|
|
228
|
-
},
|
|
229
|
-
getObjectUri: (
|
|
230
|
-
// deno-lint-ignore no-explicit-any
|
|
231
|
-
cls, values) => {
|
|
232
|
-
const callbacks = this.#objectCallbacks[cls.typeId.href];
|
|
233
|
-
if (callbacks == null) {
|
|
234
|
-
throw new RouterError("No object dispatcher registered.");
|
|
235
|
-
}
|
|
236
|
-
for (const param of callbacks.parameters) {
|
|
237
|
-
if (!(param in values)) {
|
|
238
|
-
throw new TypeError(`Missing parameter: ${param}`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
const path = this.#router.build(`object:${cls.typeId.href}`, values);
|
|
242
|
-
if (path == null) {
|
|
243
|
-
throw new RouterError("No object dispatcher registered.");
|
|
244
|
-
}
|
|
245
|
-
return new URL(path, url);
|
|
246
|
-
},
|
|
247
|
-
getOutboxUri: (handle) => {
|
|
248
|
-
const path = this.#router.build("outbox", { handle });
|
|
249
|
-
if (path == null) {
|
|
250
|
-
throw new RouterError("No outbox dispatcher registered.");
|
|
251
|
-
}
|
|
252
|
-
return new URL(path, url);
|
|
253
|
-
},
|
|
254
|
-
getInboxUri: (handle) => {
|
|
255
|
-
if (handle == null) {
|
|
256
|
-
const path = this.#router.build("sharedInbox", {});
|
|
257
|
-
if (path == null) {
|
|
258
|
-
throw new RouterError("No shared inbox path registered.");
|
|
259
|
-
}
|
|
260
|
-
return new URL(path, url);
|
|
261
|
-
}
|
|
262
|
-
const path = this.#router.build("inbox", { handle });
|
|
263
|
-
if (path == null) {
|
|
264
|
-
throw new RouterError("No inbox path registered.");
|
|
265
|
-
}
|
|
266
|
-
return new URL(path, url);
|
|
267
|
-
},
|
|
268
|
-
getFollowingUri: (handle) => {
|
|
269
|
-
const path = this.#router.build("following", { handle });
|
|
270
|
-
if (path == null) {
|
|
271
|
-
throw new RouterError("No following collection path registered.");
|
|
272
|
-
}
|
|
273
|
-
return new URL(path, url);
|
|
274
|
-
},
|
|
275
|
-
getFollowersUri: (handle) => {
|
|
276
|
-
const path = this.#router.build("followers", { handle });
|
|
277
|
-
if (path == null) {
|
|
278
|
-
throw new RouterError("No followers collection path registered.");
|
|
279
|
-
}
|
|
280
|
-
return new URL(path, url);
|
|
281
|
-
},
|
|
282
|
-
parseUri: (uri) => {
|
|
283
|
-
if (uri.origin !== url.origin)
|
|
284
|
-
return null;
|
|
285
|
-
const route = this.#router.route(uri.pathname);
|
|
286
|
-
if (route == null)
|
|
287
|
-
return null;
|
|
288
|
-
else if (route.name === "actor") {
|
|
289
|
-
return { type: "actor", handle: route.values.handle };
|
|
290
|
-
}
|
|
291
|
-
else if (route.name.startsWith("object:")) {
|
|
292
|
-
const typeId = route.name.replace(/^object:/, "");
|
|
293
|
-
return {
|
|
294
|
-
type: "object",
|
|
295
|
-
class: this.#objectTypeIds[typeId],
|
|
296
|
-
typeId: new URL(typeId),
|
|
297
|
-
values: route.values,
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
else if (route.name === "inbox") {
|
|
301
|
-
return { type: "inbox", handle: route.values.handle };
|
|
302
|
-
}
|
|
303
|
-
else if (route.name === "sharedInbox") {
|
|
304
|
-
return { type: "inbox" };
|
|
305
|
-
}
|
|
306
|
-
else if (route.name === "outbox") {
|
|
307
|
-
return { type: "outbox", handle: route.values.handle };
|
|
308
|
-
}
|
|
309
|
-
else if (route.name === "following") {
|
|
310
|
-
return { type: "following", handle: route.values.handle };
|
|
311
|
-
}
|
|
312
|
-
else if (route.name === "followers") {
|
|
313
|
-
return { type: "followers", handle: route.values.handle };
|
|
314
|
-
}
|
|
315
|
-
return null;
|
|
316
|
-
},
|
|
317
|
-
getHandleFromActorUri(actorUri) {
|
|
318
|
-
getLogger(["fedify", "federation"]).warn("Context.getHandleFromActorUri() is deprecated; " +
|
|
319
|
-
"use Context.parseUri() instead.");
|
|
320
|
-
const result = this.parseUri(actorUri);
|
|
321
|
-
if (result?.type === "actor")
|
|
322
|
-
return result.handle;
|
|
323
|
-
return null;
|
|
324
|
-
},
|
|
325
|
-
getActorKeyPairs: async (handle) => {
|
|
326
|
-
let keyPairs;
|
|
327
|
-
try {
|
|
328
|
-
keyPairs = await this.#getKeyPairsFromHandle(url, contextData, handle);
|
|
329
|
-
}
|
|
330
|
-
catch (_) {
|
|
331
|
-
getLogger(["fedify", "federation", "actor"])
|
|
332
|
-
.warn("No actor key pairs dispatcher registered.");
|
|
333
|
-
return [];
|
|
334
|
-
}
|
|
335
|
-
const owner = context.getActorUri(handle);
|
|
336
|
-
const result = [];
|
|
337
|
-
for (const keyPair of keyPairs) {
|
|
338
|
-
const newPair = {
|
|
339
|
-
...keyPair,
|
|
340
|
-
cryptographicKey: new CryptographicKey({
|
|
341
|
-
id: keyPair.keyId,
|
|
342
|
-
owner,
|
|
343
|
-
publicKey: keyPair.publicKey,
|
|
344
|
-
}),
|
|
345
|
-
};
|
|
346
|
-
result.push(newPair);
|
|
347
|
-
}
|
|
348
|
-
return result;
|
|
349
|
-
},
|
|
350
|
-
getActorKey: async (handle) => {
|
|
351
|
-
getLogger(["fedify", "federation", "actor"]).warn("Context.getActorKey() method is deprecated; " +
|
|
352
|
-
"use Context.getActorKeyPairs() method instead.");
|
|
353
|
-
let keyPair;
|
|
354
|
-
try {
|
|
355
|
-
keyPair = await getRsaKeyPairFromHandle(handle);
|
|
356
|
-
}
|
|
357
|
-
catch (_) {
|
|
358
|
-
return null;
|
|
359
|
-
}
|
|
360
|
-
if (keyPair == null)
|
|
361
|
-
return null;
|
|
362
|
-
return new CryptographicKey({
|
|
363
|
-
id: keyPair.keyId,
|
|
364
|
-
owner: context.getActorUri(handle),
|
|
365
|
-
publicKey: keyPair.publicKey,
|
|
366
|
-
});
|
|
367
|
-
},
|
|
368
|
-
getDocumentLoader,
|
|
369
|
-
sendActivity: async (sender, recipients, activity, options = {}) => {
|
|
370
|
-
let senderPair;
|
|
371
|
-
if ("handle" in sender) {
|
|
372
|
-
const keyPair = await getRsaKeyPairFromHandle(sender.handle);
|
|
373
|
-
if (keyPair == null) {
|
|
374
|
-
throw new Error(`No key pair found for actor ${sender.handle}`);
|
|
375
|
-
}
|
|
376
|
-
senderPair = keyPair;
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
senderPair = sender;
|
|
380
|
-
}
|
|
381
|
-
const opts = { ...options };
|
|
382
|
-
let expandedRecipients;
|
|
383
|
-
if (Array.isArray(recipients)) {
|
|
384
|
-
expandedRecipients = recipients;
|
|
385
|
-
}
|
|
386
|
-
else if (recipients === "followers") {
|
|
387
|
-
if (!("handle" in sender)) {
|
|
388
|
-
throw new Error("If recipients is 'followers', sender must be an actor handle.");
|
|
389
|
-
}
|
|
390
|
-
expandedRecipients = [];
|
|
391
|
-
for await (const recipient of this.#getFollowers(reqCtx, sender.handle)) {
|
|
392
|
-
expandedRecipients.push(recipient);
|
|
393
|
-
}
|
|
394
|
-
const collectionId = this.#router.build("followers", sender);
|
|
395
|
-
opts.collectionSync = collectionId == null
|
|
396
|
-
? undefined
|
|
397
|
-
: new URL(collectionId, url).href;
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
expandedRecipients = [recipients];
|
|
401
|
-
}
|
|
402
|
-
return await this.sendActivity(senderPair, expandedRecipients, activity, opts);
|
|
403
|
-
},
|
|
175
|
+
authenticatedDocumentLoaderFactory: this.#authenticatedDocumentLoaderFactory,
|
|
404
176
|
};
|
|
405
177
|
if (request == null)
|
|
406
|
-
return
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const timeWindow = this.#signatureTimeWindow;
|
|
410
|
-
const reqCtx = {
|
|
411
|
-
...context,
|
|
178
|
+
return new ContextImpl(ctxOptions);
|
|
179
|
+
return new RequestContextImpl({
|
|
180
|
+
...ctxOptions,
|
|
412
181
|
request,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
let rsaKey;
|
|
420
|
-
try {
|
|
421
|
-
rsaKey = await getRsaKeyPairFromHandle(handle);
|
|
422
|
-
}
|
|
423
|
-
catch (_) {
|
|
424
|
-
rsaKey = null;
|
|
425
|
-
}
|
|
426
|
-
return await this.#actorCallbacks.dispatcher({
|
|
427
|
-
...reqCtx,
|
|
428
|
-
getActor(handle2) {
|
|
429
|
-
getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
|
|
430
|
-
"the actor dispatcher ({actorDispatcherHandle}); " +
|
|
431
|
-
"this may cause an infinite loop.", { getActorHandle: handle2, actorDispatcherHandle: handle });
|
|
432
|
-
return reqCtx.getActor(handle2);
|
|
433
|
-
},
|
|
434
|
-
}, handle, rsaKey == null ? null : new CryptographicKey({
|
|
435
|
-
id: rsaKey.keyId,
|
|
436
|
-
owner: context.getActorUri(handle),
|
|
437
|
-
publicKey: rsaKey.publicKey,
|
|
438
|
-
}));
|
|
439
|
-
},
|
|
440
|
-
getObject: async (cls, values) => {
|
|
441
|
-
const callbacks = this.#objectCallbacks[cls.typeId.href];
|
|
442
|
-
if (callbacks == null) {
|
|
443
|
-
throw new Error("No object dispatcher registered.");
|
|
444
|
-
}
|
|
445
|
-
for (const param of callbacks.parameters) {
|
|
446
|
-
if (!(param in values)) {
|
|
447
|
-
throw new TypeError(`Missing parameter: ${param}`);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
return await callbacks.dispatcher({
|
|
451
|
-
...reqCtx,
|
|
452
|
-
getObject(cls2, values2) {
|
|
453
|
-
getLogger(["fedify", "federation"]).warn("RequestContext.getObject({getObjectClass}, " +
|
|
454
|
-
"{getObjectValues}) is invoked from the object dispatcher " +
|
|
455
|
-
"({actorDispatcherClass}, {actorDispatcherValues}); " +
|
|
456
|
-
"this may cause an infinite loop.", {
|
|
457
|
-
getObjectClass: cls2.name,
|
|
458
|
-
getObjectValues: values2,
|
|
459
|
-
actorDispatcherClass: cls.name,
|
|
460
|
-
actorDispatcherValues: values,
|
|
461
|
-
});
|
|
462
|
-
return reqCtx.getObject(cls2, values2);
|
|
463
|
-
},
|
|
464
|
-
}, values);
|
|
465
|
-
},
|
|
466
|
-
async getSignedKey() {
|
|
467
|
-
if (signedKey !== undefined)
|
|
468
|
-
return signedKey;
|
|
469
|
-
return signedKey = await verifyRequest(request, {
|
|
470
|
-
...context,
|
|
471
|
-
timeWindow,
|
|
472
|
-
});
|
|
473
|
-
},
|
|
474
|
-
async getSignedKeyOwner() {
|
|
475
|
-
if (signedKeyOwner !== undefined)
|
|
476
|
-
return signedKeyOwner;
|
|
477
|
-
const key = await this.getSignedKey();
|
|
478
|
-
if (key == null)
|
|
479
|
-
return signedKeyOwner = null;
|
|
480
|
-
return signedKeyOwner = await getKeyOwner(key, context);
|
|
481
|
-
},
|
|
482
|
-
};
|
|
483
|
-
return reqCtx;
|
|
182
|
+
signatureTimeWindow: this.#signatureTimeWindow,
|
|
183
|
+
followersCallbacks: this.#followersCallbacks,
|
|
184
|
+
invokedFromActorDispatcher: opts.invokedFromActorDispatcher,
|
|
185
|
+
invokedFromObjectDispatcher: opts.invokedFromObjectDispatcher,
|
|
186
|
+
});
|
|
484
187
|
}
|
|
485
188
|
/**
|
|
486
189
|
* Registers a NodeInfo dispatcher.
|
|
@@ -604,6 +307,18 @@ export class Federation {
|
|
|
604
307
|
"URI. Set the property with Context.getInboxUri().");
|
|
605
308
|
}
|
|
606
309
|
}
|
|
310
|
+
if (callbacks.keyPairsDispatcher != null) {
|
|
311
|
+
if (actor.publicKeyId == null) {
|
|
312
|
+
logger.warn("You configured a key pairs dispatcher, but the actor does " +
|
|
313
|
+
"not have a publicKey property. Set the property with " +
|
|
314
|
+
"Context.getActorKeyPairs(handle).");
|
|
315
|
+
}
|
|
316
|
+
if (actor.assertionMethodId == null) {
|
|
317
|
+
logger.warn("You configured a key pairs dispatcher, but the actor does " +
|
|
318
|
+
"not have an assertionMethod property. Set the property " +
|
|
319
|
+
"with Context.getActorKeyPairs(handle).");
|
|
320
|
+
}
|
|
321
|
+
}
|
|
607
322
|
return actor;
|
|
608
323
|
},
|
|
609
324
|
};
|
|
@@ -798,29 +513,6 @@ export class Federation {
|
|
|
798
513
|
};
|
|
799
514
|
return setters;
|
|
800
515
|
}
|
|
801
|
-
async *#getFollowers(context, handle) {
|
|
802
|
-
if (this.#followersCallbacks == null) {
|
|
803
|
-
throw new Error("No followers collection dispatcher registered.");
|
|
804
|
-
}
|
|
805
|
-
const result = await this.#followersCallbacks.dispatcher(context, handle, null);
|
|
806
|
-
if (result != null) {
|
|
807
|
-
for (const recipient of result.items)
|
|
808
|
-
yield recipient;
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
if (this.#followersCallbacks.firstCursor == null) {
|
|
812
|
-
throw new Error("No first cursor dispatcher registered for followers collection.");
|
|
813
|
-
}
|
|
814
|
-
let cursor = await this.#followersCallbacks.firstCursor(context, handle);
|
|
815
|
-
while (cursor != null) {
|
|
816
|
-
const result = await this.#followersCallbacks.dispatcher(context, handle, cursor);
|
|
817
|
-
if (result == null)
|
|
818
|
-
break;
|
|
819
|
-
for (const recipient of result.items)
|
|
820
|
-
yield recipient;
|
|
821
|
-
cursor = result.nextCursor ?? null;
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
516
|
/**
|
|
825
517
|
* Assigns the URL path for the inbox and starts setting inbox listeners.
|
|
826
518
|
*
|
|
@@ -1016,7 +708,7 @@ export class Federation {
|
|
|
1016
708
|
const response = onNotFound(request);
|
|
1017
709
|
return response instanceof Promise ? await response : response;
|
|
1018
710
|
}
|
|
1019
|
-
let context = this
|
|
711
|
+
let context = this.#createContext(request, contextData);
|
|
1020
712
|
switch (route.name.replace(/:.*$/, "")) {
|
|
1021
713
|
case "webfinger":
|
|
1022
714
|
return await handleWebFinger(request, {
|
|
@@ -1032,6 +724,9 @@ export class Federation {
|
|
|
1032
724
|
nodeInfoDispatcher: this.#nodeInfoDispatcher,
|
|
1033
725
|
});
|
|
1034
726
|
case "actor":
|
|
727
|
+
context = this.#createContext(request, contextData, {
|
|
728
|
+
invokedFromActorDispatcher: { handle: route.values.handle },
|
|
729
|
+
});
|
|
1035
730
|
return await handleActor(request, {
|
|
1036
731
|
handle: route.values.handle,
|
|
1037
732
|
context,
|
|
@@ -1044,6 +739,10 @@ export class Federation {
|
|
|
1044
739
|
case "object": {
|
|
1045
740
|
const typeId = route.name.replace(/^object:/, "");
|
|
1046
741
|
const callbacks = this.#objectCallbacks[typeId];
|
|
742
|
+
const cls = this.#objectTypeIds[typeId];
|
|
743
|
+
context = this.#createContext(request, contextData, {
|
|
744
|
+
invokedFromObjectDispatcher: { cls, values: route.values },
|
|
745
|
+
});
|
|
1047
746
|
return await handleObject(request, {
|
|
1048
747
|
values: route.values,
|
|
1049
748
|
context,
|
|
@@ -1065,12 +764,11 @@ export class Federation {
|
|
|
1065
764
|
onNotAcceptable,
|
|
1066
765
|
});
|
|
1067
766
|
case "inbox":
|
|
1068
|
-
context = {
|
|
1069
|
-
...context,
|
|
767
|
+
context = this.#createContext(request, contextData, {
|
|
1070
768
|
documentLoader: await context.getDocumentLoader({
|
|
1071
769
|
handle: route.values.handle,
|
|
1072
770
|
}),
|
|
1073
|
-
};
|
|
771
|
+
});
|
|
1074
772
|
// falls through
|
|
1075
773
|
case "sharedInbox":
|
|
1076
774
|
return await handleInbox(request, {
|
|
@@ -1121,6 +819,396 @@ export class Federation {
|
|
|
1121
819
|
}
|
|
1122
820
|
}
|
|
1123
821
|
}
|
|
822
|
+
class ContextImpl {
|
|
823
|
+
#url;
|
|
824
|
+
#federation;
|
|
825
|
+
#router;
|
|
826
|
+
#objectTypeIds;
|
|
827
|
+
objectCallbacks;
|
|
828
|
+
actorCallbacks;
|
|
829
|
+
data;
|
|
830
|
+
documentLoader;
|
|
831
|
+
contextLoader;
|
|
832
|
+
#authenticatedDocumentLoaderFactory;
|
|
833
|
+
constructor({ url, federation, router, objectTypeIds, objectCallbacks, actorCallbacks, data, documentLoader, contextLoader, authenticatedDocumentLoaderFactory, }) {
|
|
834
|
+
this.#url = url;
|
|
835
|
+
this.#federation = federation;
|
|
836
|
+
this.#router = router;
|
|
837
|
+
this.#objectTypeIds = objectTypeIds;
|
|
838
|
+
this.objectCallbacks = objectCallbacks;
|
|
839
|
+
this.actorCallbacks = actorCallbacks;
|
|
840
|
+
this.data = data;
|
|
841
|
+
this.documentLoader = documentLoader;
|
|
842
|
+
this.contextLoader = contextLoader;
|
|
843
|
+
this.#authenticatedDocumentLoaderFactory =
|
|
844
|
+
authenticatedDocumentLoaderFactory;
|
|
845
|
+
}
|
|
846
|
+
getNodeInfoUri() {
|
|
847
|
+
const path = this.#router.build("nodeInfo", {});
|
|
848
|
+
if (path == null) {
|
|
849
|
+
throw new RouterError("No NodeInfo dispatcher registered.");
|
|
850
|
+
}
|
|
851
|
+
return new URL(path, this.#url);
|
|
852
|
+
}
|
|
853
|
+
getActorUri(handle) {
|
|
854
|
+
const path = this.#router.build("actor", { handle });
|
|
855
|
+
if (path == null) {
|
|
856
|
+
throw new RouterError("No actor dispatcher registered.");
|
|
857
|
+
}
|
|
858
|
+
return new URL(path, this.#url);
|
|
859
|
+
}
|
|
860
|
+
getObjectUri(
|
|
861
|
+
// deno-lint-ignore no-explicit-any
|
|
862
|
+
cls, values) {
|
|
863
|
+
const callbacks = this.objectCallbacks[cls.typeId.href];
|
|
864
|
+
if (callbacks == null) {
|
|
865
|
+
throw new RouterError("No object dispatcher registered.");
|
|
866
|
+
}
|
|
867
|
+
for (const param of callbacks.parameters) {
|
|
868
|
+
if (!(param in values)) {
|
|
869
|
+
throw new TypeError(`Missing parameter: ${param}`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const path = this.#router.build(`object:${cls.typeId.href}`, values);
|
|
873
|
+
if (path == null) {
|
|
874
|
+
throw new RouterError("No object dispatcher registered.");
|
|
875
|
+
}
|
|
876
|
+
return new URL(path, this.#url);
|
|
877
|
+
}
|
|
878
|
+
getOutboxUri(handle) {
|
|
879
|
+
const path = this.#router.build("outbox", { handle });
|
|
880
|
+
if (path == null) {
|
|
881
|
+
throw new RouterError("No outbox dispatcher registered.");
|
|
882
|
+
}
|
|
883
|
+
return new URL(path, this.#url);
|
|
884
|
+
}
|
|
885
|
+
getInboxUri(handle) {
|
|
886
|
+
if (handle == null) {
|
|
887
|
+
const path = this.#router.build("sharedInbox", {});
|
|
888
|
+
if (path == null) {
|
|
889
|
+
throw new RouterError("No shared inbox path registered.");
|
|
890
|
+
}
|
|
891
|
+
return new URL(path, this.#url);
|
|
892
|
+
}
|
|
893
|
+
const path = this.#router.build("inbox", { handle });
|
|
894
|
+
if (path == null) {
|
|
895
|
+
throw new RouterError("No inbox path registered.");
|
|
896
|
+
}
|
|
897
|
+
return new URL(path, this.#url);
|
|
898
|
+
}
|
|
899
|
+
getFollowingUri(handle) {
|
|
900
|
+
const path = this.#router.build("following", { handle });
|
|
901
|
+
if (path == null) {
|
|
902
|
+
throw new RouterError("No following collection path registered.");
|
|
903
|
+
}
|
|
904
|
+
return new URL(path, this.#url);
|
|
905
|
+
}
|
|
906
|
+
getFollowersUri(handle) {
|
|
907
|
+
const path = this.#router.build("followers", { handle });
|
|
908
|
+
if (path == null) {
|
|
909
|
+
throw new RouterError("No followers collection path registered.");
|
|
910
|
+
}
|
|
911
|
+
return new URL(path, this.#url);
|
|
912
|
+
}
|
|
913
|
+
parseUri(uri) {
|
|
914
|
+
if (uri.origin !== this.#url.origin)
|
|
915
|
+
return null;
|
|
916
|
+
const route = this.#router.route(uri.pathname);
|
|
917
|
+
if (route == null)
|
|
918
|
+
return null;
|
|
919
|
+
else if (route.name === "actor") {
|
|
920
|
+
return { type: "actor", handle: route.values.handle };
|
|
921
|
+
}
|
|
922
|
+
else if (route.name.startsWith("object:")) {
|
|
923
|
+
const typeId = route.name.replace(/^object:/, "");
|
|
924
|
+
return {
|
|
925
|
+
type: "object",
|
|
926
|
+
class: this.#objectTypeIds[typeId],
|
|
927
|
+
typeId: new URL(typeId),
|
|
928
|
+
values: route.values,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
else if (route.name === "inbox") {
|
|
932
|
+
return { type: "inbox", handle: route.values.handle };
|
|
933
|
+
}
|
|
934
|
+
else if (route.name === "sharedInbox") {
|
|
935
|
+
return { type: "inbox" };
|
|
936
|
+
}
|
|
937
|
+
else if (route.name === "outbox") {
|
|
938
|
+
return { type: "outbox", handle: route.values.handle };
|
|
939
|
+
}
|
|
940
|
+
else if (route.name === "following") {
|
|
941
|
+
return { type: "following", handle: route.values.handle };
|
|
942
|
+
}
|
|
943
|
+
else if (route.name === "followers") {
|
|
944
|
+
return { type: "followers", handle: route.values.handle };
|
|
945
|
+
}
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
getHandleFromActorUri(actorUri) {
|
|
949
|
+
getLogger(["fedify", "federation"]).warn("Context.getHandleFromActorUri() is deprecated; " +
|
|
950
|
+
"use Context.parseUri() instead.");
|
|
951
|
+
const result = this.parseUri(actorUri);
|
|
952
|
+
if (result?.type === "actor")
|
|
953
|
+
return result.handle;
|
|
954
|
+
return null;
|
|
955
|
+
}
|
|
956
|
+
async getActorKeyPairs(handle) {
|
|
957
|
+
let keyPairs;
|
|
958
|
+
try {
|
|
959
|
+
keyPairs = await this.getKeyPairsFromHandle(this.#url, this.data, handle);
|
|
960
|
+
}
|
|
961
|
+
catch (_) {
|
|
962
|
+
getLogger(["fedify", "federation", "actor"])
|
|
963
|
+
.warn("No actor key pairs dispatcher registered.");
|
|
964
|
+
return [];
|
|
965
|
+
}
|
|
966
|
+
const owner = this.getActorUri(handle);
|
|
967
|
+
const result = [];
|
|
968
|
+
for (const keyPair of keyPairs) {
|
|
969
|
+
const newPair = {
|
|
970
|
+
...keyPair,
|
|
971
|
+
cryptographicKey: new CryptographicKey({
|
|
972
|
+
id: keyPair.keyId,
|
|
973
|
+
owner,
|
|
974
|
+
publicKey: keyPair.publicKey,
|
|
975
|
+
}),
|
|
976
|
+
multikey: new Multikey({
|
|
977
|
+
id: keyPair.keyId,
|
|
978
|
+
controller: owner,
|
|
979
|
+
publicKey: keyPair.publicKey,
|
|
980
|
+
}),
|
|
981
|
+
};
|
|
982
|
+
result.push(newPair);
|
|
983
|
+
}
|
|
984
|
+
return result;
|
|
985
|
+
}
|
|
986
|
+
async getKeyPairsFromHandle(url, contextData, handle) {
|
|
987
|
+
const logger = getLogger(["fedify", "federation", "actor"]);
|
|
988
|
+
if (this.actorCallbacks?.keyPairsDispatcher == null) {
|
|
989
|
+
throw new Error("No actor key pairs dispatcher registered.");
|
|
990
|
+
}
|
|
991
|
+
const path = this.#router.build("actor", { handle });
|
|
992
|
+
if (path == null) {
|
|
993
|
+
logger.warn("No actor dispatcher registered.");
|
|
994
|
+
return [];
|
|
995
|
+
}
|
|
996
|
+
const actorUri = new URL(path, url);
|
|
997
|
+
const keyPairs = await this.actorCallbacks?.keyPairsDispatcher(contextData, handle);
|
|
998
|
+
if (keyPairs.length < 1) {
|
|
999
|
+
logger.warn("No key pairs found for actor {handle}.", { handle });
|
|
1000
|
+
}
|
|
1001
|
+
let i = 0;
|
|
1002
|
+
const result = [];
|
|
1003
|
+
for (const keyPair of keyPairs) {
|
|
1004
|
+
result.push({
|
|
1005
|
+
...keyPair,
|
|
1006
|
+
keyId: new URL(
|
|
1007
|
+
// For backwards compatibility, the first key is always the #main-key:
|
|
1008
|
+
i == 0 ? `#main-key` : `#key-${i + 1}`, actorUri),
|
|
1009
|
+
});
|
|
1010
|
+
i++;
|
|
1011
|
+
}
|
|
1012
|
+
return result;
|
|
1013
|
+
}
|
|
1014
|
+
async getActorKey(handle) {
|
|
1015
|
+
getLogger(["fedify", "federation", "actor"]).warn("Context.getActorKey() method is deprecated; " +
|
|
1016
|
+
"use Context.getActorKeyPairs() method instead.");
|
|
1017
|
+
let keyPair;
|
|
1018
|
+
try {
|
|
1019
|
+
keyPair = await this.getRsaKeyPairFromHandle(handle);
|
|
1020
|
+
}
|
|
1021
|
+
catch (_) {
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
if (keyPair == null)
|
|
1025
|
+
return null;
|
|
1026
|
+
return new CryptographicKey({
|
|
1027
|
+
id: keyPair.keyId,
|
|
1028
|
+
owner: this.getActorUri(handle),
|
|
1029
|
+
publicKey: keyPair.publicKey,
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
async getRsaKeyPairFromHandle(handle) {
|
|
1033
|
+
const keyPairs = await this.getKeyPairsFromHandle(this.#url, this.data, handle);
|
|
1034
|
+
for (const keyPair of keyPairs) {
|
|
1035
|
+
const { privateKey } = keyPair;
|
|
1036
|
+
if (privateKey.algorithm.name === "RSASSA-PKCS1-v1_5" &&
|
|
1037
|
+
privateKey.algorithm.hash
|
|
1038
|
+
.name ===
|
|
1039
|
+
"SHA-256") {
|
|
1040
|
+
return keyPair;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
getLogger(["fedify", "federation", "actor"]).warn("No RSA-PKCS#1-v1.5 SHA-256 key found for actor {handle}.", { handle });
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
getDocumentLoader(identity) {
|
|
1047
|
+
if ("handle" in identity) {
|
|
1048
|
+
const keyPair = this.getRsaKeyPairFromHandle(identity.handle);
|
|
1049
|
+
return keyPair.then((pair) => pair == null
|
|
1050
|
+
? this.documentLoader
|
|
1051
|
+
: this.#authenticatedDocumentLoaderFactory(pair));
|
|
1052
|
+
}
|
|
1053
|
+
return this.#authenticatedDocumentLoaderFactory(identity);
|
|
1054
|
+
}
|
|
1055
|
+
async sendActivity(sender, recipients, activity, options = {}) {
|
|
1056
|
+
let senderPair;
|
|
1057
|
+
if ("handle" in sender) {
|
|
1058
|
+
const keyPair = await this.getRsaKeyPairFromHandle(sender.handle);
|
|
1059
|
+
if (keyPair == null) {
|
|
1060
|
+
throw new Error(`No key pair found for actor ${sender.handle}`);
|
|
1061
|
+
}
|
|
1062
|
+
senderPair = keyPair;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
senderPair = sender;
|
|
1066
|
+
}
|
|
1067
|
+
const opts = { ...options };
|
|
1068
|
+
let expandedRecipients;
|
|
1069
|
+
if (Array.isArray(recipients)) {
|
|
1070
|
+
expandedRecipients = recipients;
|
|
1071
|
+
}
|
|
1072
|
+
else if (recipients === "followers") {
|
|
1073
|
+
if (!("handle" in sender)) {
|
|
1074
|
+
throw new Error("If recipients is 'followers', sender must be an actor handle.");
|
|
1075
|
+
}
|
|
1076
|
+
expandedRecipients = [];
|
|
1077
|
+
for await (const recipient of this.getFollowers(sender.handle)) {
|
|
1078
|
+
expandedRecipients.push(recipient);
|
|
1079
|
+
}
|
|
1080
|
+
const collectionId = this.#router.build("followers", sender);
|
|
1081
|
+
opts.collectionSync = collectionId == null
|
|
1082
|
+
? undefined
|
|
1083
|
+
: new URL(collectionId, this.#url).href;
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
expandedRecipients = [recipients];
|
|
1087
|
+
}
|
|
1088
|
+
return await this.#federation.sendActivity(senderPair, expandedRecipients, activity, opts);
|
|
1089
|
+
}
|
|
1090
|
+
getFollowers(_handle) {
|
|
1091
|
+
throw new Error('"followers" recipients are not supported in Context. ' +
|
|
1092
|
+
"Use RequestContext instead.");
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
class RequestContextImpl extends ContextImpl {
|
|
1096
|
+
#options;
|
|
1097
|
+
#followersCallbacks;
|
|
1098
|
+
#signatureTimeWindow;
|
|
1099
|
+
#invokedFromActorDispatcher;
|
|
1100
|
+
#invokedFromObjectDispatcher;
|
|
1101
|
+
request;
|
|
1102
|
+
url;
|
|
1103
|
+
constructor(options) {
|
|
1104
|
+
super(options);
|
|
1105
|
+
this.#options = options;
|
|
1106
|
+
this.#followersCallbacks = options.followersCallbacks;
|
|
1107
|
+
this.#signatureTimeWindow = options.signatureTimeWindow;
|
|
1108
|
+
this.#invokedFromActorDispatcher = options.invokedFromActorDispatcher;
|
|
1109
|
+
this.#invokedFromObjectDispatcher = options.invokedFromObjectDispatcher;
|
|
1110
|
+
this.request = options.request;
|
|
1111
|
+
this.url = options.url;
|
|
1112
|
+
}
|
|
1113
|
+
async *getFollowers(handle) {
|
|
1114
|
+
if (this.#followersCallbacks == null) {
|
|
1115
|
+
throw new Error("No followers collection dispatcher registered.");
|
|
1116
|
+
}
|
|
1117
|
+
const result = await this.#followersCallbacks.dispatcher(this, handle, null);
|
|
1118
|
+
if (result != null) {
|
|
1119
|
+
for (const recipient of result.items)
|
|
1120
|
+
yield recipient;
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (this.#followersCallbacks.firstCursor == null) {
|
|
1124
|
+
throw new Error("No first cursor dispatcher registered for followers collection.");
|
|
1125
|
+
}
|
|
1126
|
+
let cursor = await this.#followersCallbacks.firstCursor(this, handle);
|
|
1127
|
+
while (cursor != null) {
|
|
1128
|
+
const result = await this.#followersCallbacks.dispatcher(this, handle, cursor);
|
|
1129
|
+
if (result == null)
|
|
1130
|
+
break;
|
|
1131
|
+
for (const recipient of result.items)
|
|
1132
|
+
yield recipient;
|
|
1133
|
+
cursor = result.nextCursor ?? null;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
async getActor(handle) {
|
|
1137
|
+
if (this.actorCallbacks == null ||
|
|
1138
|
+
this.actorCallbacks.dispatcher == null) {
|
|
1139
|
+
throw new Error("No actor dispatcher registered.");
|
|
1140
|
+
}
|
|
1141
|
+
if (this.#invokedFromActorDispatcher != null) {
|
|
1142
|
+
getLogger(["fedify", "federation"]).warn("RequestContext.getActor({getActorHandle}) is invoked from " +
|
|
1143
|
+
"the actor dispatcher ({actorDispatcherHandle}); " +
|
|
1144
|
+
"this may cause an infinite loop.", {
|
|
1145
|
+
getActorHandle: handle,
|
|
1146
|
+
actorDispatcherHandle: this.#invokedFromActorDispatcher.handle,
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
let rsaKey;
|
|
1150
|
+
try {
|
|
1151
|
+
rsaKey = await this.getRsaKeyPairFromHandle(handle);
|
|
1152
|
+
}
|
|
1153
|
+
catch (_) {
|
|
1154
|
+
rsaKey = null;
|
|
1155
|
+
}
|
|
1156
|
+
return await this.actorCallbacks.dispatcher(new RequestContextImpl({
|
|
1157
|
+
...this.#options,
|
|
1158
|
+
invokedFromActorDispatcher: { handle },
|
|
1159
|
+
}), handle, rsaKey == null ? null : new CryptographicKey({
|
|
1160
|
+
id: rsaKey.keyId,
|
|
1161
|
+
owner: this.getActorUri(handle),
|
|
1162
|
+
publicKey: rsaKey.publicKey,
|
|
1163
|
+
}));
|
|
1164
|
+
}
|
|
1165
|
+
async getObject(
|
|
1166
|
+
// deno-lint-ignore no-explicit-any
|
|
1167
|
+
cls, values) {
|
|
1168
|
+
const callbacks = this.objectCallbacks[cls.typeId.href];
|
|
1169
|
+
if (callbacks == null) {
|
|
1170
|
+
throw new Error("No object dispatcher registered.");
|
|
1171
|
+
}
|
|
1172
|
+
for (const param of callbacks.parameters) {
|
|
1173
|
+
if (!(param in values)) {
|
|
1174
|
+
throw new TypeError(`Missing parameter: ${param}`);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (this.#invokedFromObjectDispatcher != null) {
|
|
1178
|
+
getLogger(["fedify", "federation"]).warn("RequestContext.getObject({getObjectClass}, " +
|
|
1179
|
+
"{getObjectValues}) is invoked from the object dispatcher " +
|
|
1180
|
+
"({actorDispatcherClass}, {actorDispatcherValues}); " +
|
|
1181
|
+
"this may cause an infinite loop.", {
|
|
1182
|
+
getObjectClass: cls.name,
|
|
1183
|
+
getObjectValues: values,
|
|
1184
|
+
actorDispatcherClass: this.#invokedFromObjectDispatcher.cls.name,
|
|
1185
|
+
actorDispatcherValues: this.#invokedFromObjectDispatcher.values,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
return await callbacks.dispatcher(new RequestContextImpl({
|
|
1189
|
+
...this.#options,
|
|
1190
|
+
invokedFromObjectDispatcher: { cls, values },
|
|
1191
|
+
}), values);
|
|
1192
|
+
}
|
|
1193
|
+
#signedKey = undefined;
|
|
1194
|
+
async getSignedKey() {
|
|
1195
|
+
if (this.#signedKey !== undefined)
|
|
1196
|
+
return this.#signedKey;
|
|
1197
|
+
return this.#signedKey = await verifyRequest(this.request, {
|
|
1198
|
+
...this,
|
|
1199
|
+
timeWindow: this.#signatureTimeWindow,
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
#signedKeyOwner = undefined;
|
|
1203
|
+
async getSignedKeyOwner() {
|
|
1204
|
+
if (this.#signedKeyOwner !== undefined)
|
|
1205
|
+
return this.#signedKeyOwner;
|
|
1206
|
+
const key = await this.getSignedKey();
|
|
1207
|
+
if (key == null)
|
|
1208
|
+
return this.#signedKeyOwner = null;
|
|
1209
|
+
return this.#signedKeyOwner = await getKeyOwner(key, this);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1124
1212
|
function notFound(_request) {
|
|
1125
1213
|
return new Response("Not Found", { status: 404 });
|
|
1126
1214
|
}
|