@didcid/keymaster 0.4.3 → 0.4.5

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.
@@ -14,6 +14,23 @@ function base64urlToHex(b64) {
14
14
  const bytes = base64url.baseDecode(b64);
15
15
  return Buffer.from(bytes).toString('hex');
16
16
  }
17
+ function isPrivateHostname(hostname) {
18
+ return /^(localhost|127\.|10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(hostname);
19
+ }
20
+ const REMOTE_NAME_LOOKUP_TIMEOUT_MS = 2000;
21
+ function isRemoteNameReference(value) {
22
+ if (typeof value !== 'string') {
23
+ return false;
24
+ }
25
+ if (value.startsWith('did:') || value.startsWith('mailto:') || value.includes('://')) {
26
+ return false;
27
+ }
28
+ if (/[/?#]/.test(value)) {
29
+ return false;
30
+ }
31
+ const parts = value.split('@');
32
+ return parts.length === 2 && !!parts[0] && !!parts[1];
33
+ }
17
34
  const DefaultSchema = {
18
35
  "$schema": "http://json-schema.org/draft-07/schema#",
19
36
  "type": "object",
@@ -697,6 +714,24 @@ export default class Keymaster {
697
714
  const file = await this.generateFileAsset(filename, buffer);
698
715
  return this.mergeData(id, { file });
699
716
  }
717
+ async generateFileAssetFromStream(filename, stream, contentType, bytes) {
718
+ const cid = await this.gatekeeper.addDataStream(stream);
719
+ return { cid, filename, type: contentType, bytes };
720
+ }
721
+ async createFileStream(stream, options = {}) {
722
+ const filename = options.filename || 'file';
723
+ const contentType = options.contentType || 'application/octet-stream';
724
+ const bytes = options.bytes || 0;
725
+ const file = await this.generateFileAssetFromStream(filename, stream, contentType, bytes);
726
+ return this.createAsset({ file }, options);
727
+ }
728
+ async updateFileStream(id, stream, options = {}) {
729
+ const filename = options.filename || 'file';
730
+ const contentType = options.contentType || 'application/octet-stream';
731
+ const bytes = options.bytes || 0;
732
+ const file = await this.generateFileAssetFromStream(filename, stream, contentType, bytes);
733
+ return this.mergeData(id, { file });
734
+ }
700
735
  async getFile(id) {
701
736
  const asset = await this.resolveAsset(id);
702
737
  const file = asset.file;
@@ -901,6 +936,23 @@ export default class Keymaster {
901
936
  }
902
937
  return ok;
903
938
  }
939
+ async changeRegistry(id, registry) {
940
+ if (!registry) {
941
+ throw new InvalidParameterError('registry');
942
+ }
943
+ const did = await this.lookupDID(id);
944
+ const current = await this.resolveDID(did);
945
+ const currentRegistry = current.didDocumentRegistration?.registry;
946
+ if (registry === currentRegistry) {
947
+ return true;
948
+ }
949
+ return this.updateDID(did, {
950
+ didDocumentRegistration: {
951
+ ...current.didDocumentRegistration,
952
+ registry,
953
+ },
954
+ });
955
+ }
904
956
  async addToOwned(did, owner) {
905
957
  await this.mutateWallet(async (wallet) => {
906
958
  const id = await this.fetchIdInfo(owner, wallet);
@@ -959,8 +1011,47 @@ export default class Keymaster {
959
1011
  if (wallet.ids && name in wallet.ids) {
960
1012
  return wallet.ids[name].did;
961
1013
  }
1014
+ const remoteDid = await this.resolveRemoteName(name);
1015
+ if (remoteDid) {
1016
+ return remoteDid;
1017
+ }
962
1018
  throw new UnknownIDError();
963
1019
  }
1020
+ async resolveRemoteName(name) {
1021
+ if (!isRemoteNameReference(name)) {
1022
+ return null;
1023
+ }
1024
+ const [localName, domain] = name.split('@');
1025
+ let url;
1026
+ try {
1027
+ url = new URL(`https://${domain}/.well-known/names/${encodeURIComponent(localName)}`);
1028
+ }
1029
+ catch {
1030
+ return null;
1031
+ }
1032
+ if (isPrivateHostname(url.hostname)) {
1033
+ return null;
1034
+ }
1035
+ const controller = new AbortController();
1036
+ const timeout = setTimeout(() => controller.abort(), REMOTE_NAME_LOOKUP_TIMEOUT_MS);
1037
+ try {
1038
+ const response = await fetch(url.toString(), { signal: controller.signal });
1039
+ if (!response.ok) {
1040
+ return null;
1041
+ }
1042
+ const data = await response.json();
1043
+ if (typeof data.did !== 'string' || !isValidDID(data.did)) {
1044
+ return null;
1045
+ }
1046
+ return data.did;
1047
+ }
1048
+ catch {
1049
+ return null;
1050
+ }
1051
+ finally {
1052
+ clearTimeout(timeout);
1053
+ }
1054
+ }
964
1055
  async resolveDID(did, options) {
965
1056
  const actualDid = await this.lookupDID(did);
966
1057
  const docs = await this.gatekeeper.resolveDID(actualDid, options);
@@ -2896,9 +2987,16 @@ export default class Keymaster {
2896
2987
  }
2897
2988
  throw new InvalidParameterError(`Invalid recipient: ${id}`);
2898
2989
  }
2899
- if (isValidDID(id)) {
2900
- newList.push(id);
2901
- continue;
2990
+ try {
2991
+ const did = await this.lookupDID(id);
2992
+ const isAgent = await this.testAgent(did);
2993
+ if (isAgent) {
2994
+ newList.push(did);
2995
+ continue;
2996
+ }
2997
+ }
2998
+ catch {
2999
+ // Fall through to recipient-specific error below.
2902
3000
  }
2903
3001
  throw new InvalidParameterError(`Invalid recipient: ${id}`);
2904
3002
  }