@twin.org/identity-connector-entity-storage 0.0.2-next.9 → 0.0.3-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/entities/identityDocument.js +45 -0
- package/dist/es/entities/identityDocument.js.map +1 -0
- package/dist/es/entities/identityProfile.js +37 -0
- package/dist/es/entities/identityProfile.js.map +1 -0
- package/dist/{esm/index.mjs → es/entityStorageIdentityConnector.js} +18 -355
- package/dist/es/entityStorageIdentityConnector.js.map +1 -0
- package/dist/es/entityStorageIdentityProfileConnector.js +203 -0
- package/dist/es/entityStorageIdentityProfileConnector.js.map +1 -0
- package/dist/es/entityStorageIdentityResolverConnector.js +65 -0
- package/dist/es/entityStorageIdentityResolverConnector.js.map +1 -0
- package/dist/es/index.js +12 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/IEntityStorageIdentityConnectorConstructorOptions.js +2 -0
- package/dist/es/models/IEntityStorageIdentityConnectorConstructorOptions.js.map +1 -0
- package/dist/es/models/IEntityStorageIdentityProfileConnectorConstructorOptions.js +4 -0
- package/dist/es/models/IEntityStorageIdentityProfileConnectorConstructorOptions.js.map +1 -0
- package/dist/es/models/IEntityStorageIdentityResolverConnectorConstructorOptions.js +4 -0
- package/dist/es/models/IEntityStorageIdentityResolverConnectorConstructorOptions.js.map +1 -0
- package/dist/es/schema.js +20 -0
- package/dist/es/schema.js.map +1 -0
- package/dist/types/entityStorageIdentityConnector.d.ts +6 -1
- package/dist/types/entityStorageIdentityProfileConnector.d.ts +6 -1
- package/dist/types/entityStorageIdentityResolverConnector.d.ts +6 -1
- package/dist/types/index.d.ts +9 -9
- package/dist/types/models/IEntityStorageIdentityConnectorConstructorOptions.d.ts +1 -1
- package/docs/changelog.md +57 -0
- package/docs/reference/classes/EntityStorageIdentityConnector.md +22 -4
- package/docs/reference/classes/EntityStorageIdentityProfileConnector.md +18 -0
- package/docs/reference/classes/EntityStorageIdentityResolverConnector.md +18 -0
- package/package.json +6 -8
- package/dist/cjs/index.cjs +0 -1326
package/dist/cjs/index.cjs
DELETED
|
@@ -1,1326 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var entity = require('@twin.org/entity');
|
|
4
|
-
var core = require('@twin.org/core');
|
|
5
|
-
var dataJsonLd = require('@twin.org/data-json-ld');
|
|
6
|
-
var entityStorageModels = require('@twin.org/entity-storage-models');
|
|
7
|
-
var identityModels = require('@twin.org/identity-models');
|
|
8
|
-
var standardsW3cDid = require('@twin.org/standards-w3c-did');
|
|
9
|
-
var vaultModels = require('@twin.org/vault-models');
|
|
10
|
-
var web = require('@twin.org/web');
|
|
11
|
-
|
|
12
|
-
// Copyright 2024 IOTA Stiftung.
|
|
13
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
14
|
-
/**
|
|
15
|
-
* Class describing the identity document.
|
|
16
|
-
*/
|
|
17
|
-
exports.IdentityDocument = class IdentityDocument {
|
|
18
|
-
/**
|
|
19
|
-
* The identity of the document.
|
|
20
|
-
*/
|
|
21
|
-
id;
|
|
22
|
-
/**
|
|
23
|
-
* The DID document.
|
|
24
|
-
*/
|
|
25
|
-
document;
|
|
26
|
-
/**
|
|
27
|
-
* The signature of the document.
|
|
28
|
-
*/
|
|
29
|
-
signature;
|
|
30
|
-
/**
|
|
31
|
-
* The controller of the document.
|
|
32
|
-
*/
|
|
33
|
-
controller;
|
|
34
|
-
};
|
|
35
|
-
__decorate([
|
|
36
|
-
entity.property({ type: "string", isPrimary: true }),
|
|
37
|
-
__metadata("design:type", String)
|
|
38
|
-
], exports.IdentityDocument.prototype, "id", void 0);
|
|
39
|
-
__decorate([
|
|
40
|
-
entity.property({ type: "object" }),
|
|
41
|
-
__metadata("design:type", Object)
|
|
42
|
-
], exports.IdentityDocument.prototype, "document", void 0);
|
|
43
|
-
__decorate([
|
|
44
|
-
entity.property({ type: "string" }),
|
|
45
|
-
__metadata("design:type", String)
|
|
46
|
-
], exports.IdentityDocument.prototype, "signature", void 0);
|
|
47
|
-
__decorate([
|
|
48
|
-
entity.property({ type: "string" }),
|
|
49
|
-
__metadata("design:type", String)
|
|
50
|
-
], exports.IdentityDocument.prototype, "controller", void 0);
|
|
51
|
-
exports.IdentityDocument = __decorate([
|
|
52
|
-
entity.entity()
|
|
53
|
-
], exports.IdentityDocument);
|
|
54
|
-
|
|
55
|
-
// Copyright 2024 IOTA Stiftung.
|
|
56
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
57
|
-
/**
|
|
58
|
-
* Class representing profile details for the identity.
|
|
59
|
-
*/
|
|
60
|
-
exports.IdentityProfile = class IdentityProfile {
|
|
61
|
-
/**
|
|
62
|
-
* The id for the identity.
|
|
63
|
-
*/
|
|
64
|
-
identity;
|
|
65
|
-
/**
|
|
66
|
-
* The public profile data.
|
|
67
|
-
*/
|
|
68
|
-
publicProfile;
|
|
69
|
-
/**
|
|
70
|
-
* The private profile data.
|
|
71
|
-
*/
|
|
72
|
-
privateProfile;
|
|
73
|
-
};
|
|
74
|
-
__decorate([
|
|
75
|
-
entity.property({ type: "string", isPrimary: true }),
|
|
76
|
-
__metadata("design:type", String)
|
|
77
|
-
], exports.IdentityProfile.prototype, "identity", void 0);
|
|
78
|
-
__decorate([
|
|
79
|
-
entity.property({ type: "object", optional: true }),
|
|
80
|
-
__metadata("design:type", Object)
|
|
81
|
-
], exports.IdentityProfile.prototype, "publicProfile", void 0);
|
|
82
|
-
__decorate([
|
|
83
|
-
entity.property({ type: "object", optional: true }),
|
|
84
|
-
__metadata("design:type", Object)
|
|
85
|
-
], exports.IdentityProfile.prototype, "privateProfile", void 0);
|
|
86
|
-
exports.IdentityProfile = __decorate([
|
|
87
|
-
entity.entity()
|
|
88
|
-
], exports.IdentityProfile);
|
|
89
|
-
|
|
90
|
-
// Copyright 2024 IOTA Stiftung.
|
|
91
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
92
|
-
/**
|
|
93
|
-
* Class for performing identity operations using entity storage.
|
|
94
|
-
*/
|
|
95
|
-
class EntityStorageIdentityConnector {
|
|
96
|
-
/**
|
|
97
|
-
* Runtime name for the class.
|
|
98
|
-
*/
|
|
99
|
-
static CLASS_NAME = "EntityStorageIdentityConnector";
|
|
100
|
-
/**
|
|
101
|
-
* The namespace supported by the identity connector.
|
|
102
|
-
*/
|
|
103
|
-
static NAMESPACE = "entity-storage";
|
|
104
|
-
/**
|
|
105
|
-
* The size of the revocation bitmap in bits (16Kb).
|
|
106
|
-
* @internal
|
|
107
|
-
*/
|
|
108
|
-
static _REVOCATION_BITS_SIZE = 131072;
|
|
109
|
-
/**
|
|
110
|
-
* The entity storage for identities.
|
|
111
|
-
* @internal
|
|
112
|
-
*/
|
|
113
|
-
_didDocumentEntityStorage;
|
|
114
|
-
/**
|
|
115
|
-
* The vault for the keys.
|
|
116
|
-
* @internal
|
|
117
|
-
*/
|
|
118
|
-
_vaultConnector;
|
|
119
|
-
/**
|
|
120
|
-
* Create a new instance of EntityStorageIdentityConnector.
|
|
121
|
-
* @param options The options for the identity connector.
|
|
122
|
-
*/
|
|
123
|
-
constructor(options) {
|
|
124
|
-
this._didDocumentEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.didDocumentEntityStorageType ?? "identity-document");
|
|
125
|
-
this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Build the key name to access the specified key in the vault.
|
|
129
|
-
* @param identity The identity of the user to access the vault keys.
|
|
130
|
-
* @returns The vault key.
|
|
131
|
-
* @internal
|
|
132
|
-
*/
|
|
133
|
-
static buildVaultKey(identity, key) {
|
|
134
|
-
return `${identity}/${key}`;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Verify the document in storage.
|
|
138
|
-
* @param didDocument The did document that was stored.
|
|
139
|
-
* @internal
|
|
140
|
-
*/
|
|
141
|
-
static async verifyDocument(didDocument, vaultConnector) {
|
|
142
|
-
const stringifiedDocument = core.JsonHelper.canonicalize(didDocument.document);
|
|
143
|
-
const docBytes = core.Converter.utf8ToBytes(stringifiedDocument);
|
|
144
|
-
const verified = await vaultConnector.verify(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, "did"), docBytes, core.Converter.base64ToBytes(didDocument.signature));
|
|
145
|
-
if (!verified) {
|
|
146
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "signatureVerificationFailed");
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Create a new document.
|
|
151
|
-
* @param controller The controller of the identity who can make changes.
|
|
152
|
-
* @returns The created document.
|
|
153
|
-
*/
|
|
154
|
-
async createDocument(controller) {
|
|
155
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
156
|
-
try {
|
|
157
|
-
const did = `did:${EntityStorageIdentityConnector.NAMESPACE}:${core.Converter.bytesToHex(core.RandomHelper.generate(32), true)}`;
|
|
158
|
-
await this._vaultConnector.createKey(EntityStorageIdentityConnector.buildVaultKey(did, "did"), vaultModels.VaultKeyType.Ed25519);
|
|
159
|
-
const bitString = new core.BitString(EntityStorageIdentityConnector._REVOCATION_BITS_SIZE);
|
|
160
|
-
const compressed = await core.Compression.compress(bitString.getBits(), core.CompressionType.Gzip);
|
|
161
|
-
const didDocument = {
|
|
162
|
-
"@context": standardsW3cDid.DidContexts.Context,
|
|
163
|
-
id: did,
|
|
164
|
-
service: [
|
|
165
|
-
{
|
|
166
|
-
id: `${did}#revocation`,
|
|
167
|
-
type: "BitstringStatusList",
|
|
168
|
-
serviceEndpoint: `data:application/octet-stream;base64,${core.Converter.bytesToBase64Url(compressed)}`
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
};
|
|
172
|
-
await this.updateDocument(controller, didDocument);
|
|
173
|
-
return didDocument;
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "createDocumentFailed", undefined, error);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Remove a document.
|
|
181
|
-
* @param controller The controller of the identity who can make changes.
|
|
182
|
-
* @param documentId The id of the document to remove.
|
|
183
|
-
* @returns Nothing.
|
|
184
|
-
*/
|
|
185
|
-
async removeDocument(controller, documentId) {
|
|
186
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
187
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "documentId", documentId);
|
|
188
|
-
try {
|
|
189
|
-
const didDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
190
|
-
if (core.Is.empty(didDocument)) {
|
|
191
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
192
|
-
}
|
|
193
|
-
await this._didDocumentEntityStorage.remove(documentId);
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "removeDocumentFailed", undefined, error);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Add a verification method to the document in JSON Web key Format.
|
|
201
|
-
* @param controller The controller of the identity who can make changes.
|
|
202
|
-
* @param documentId The id of the document to add the verification method to.
|
|
203
|
-
* @param verificationMethodType The type of the verification method to add.
|
|
204
|
-
* @param verificationMethodId The id of the verification method, if undefined uses the kid of the generated JWK.
|
|
205
|
-
* @returns The verification method.
|
|
206
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
207
|
-
* @throws NotSupportedError if the platform does not support multiple keys.
|
|
208
|
-
*/
|
|
209
|
-
async addVerificationMethod(controller, documentId, verificationMethodType, verificationMethodId) {
|
|
210
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
211
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "documentId", documentId);
|
|
212
|
-
core.Guards.arrayOneOf(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodType", verificationMethodType, Object.values(standardsW3cDid.DidVerificationMethodType));
|
|
213
|
-
let tempKeyId;
|
|
214
|
-
try {
|
|
215
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
216
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
217
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
218
|
-
}
|
|
219
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
220
|
-
const didDocument = didIdentityDocument.document;
|
|
221
|
-
let methodKeyPublic;
|
|
222
|
-
if (core.Is.stringValue(verificationMethodId)) {
|
|
223
|
-
// If there is a verification method id, we will try to get the key from the vault.
|
|
224
|
-
try {
|
|
225
|
-
const defaultMethodId = `${controller}/${verificationMethodId}`;
|
|
226
|
-
// If there is an existing key, we will use it.
|
|
227
|
-
const existingKey = await this._vaultConnector.getKey(defaultMethodId);
|
|
228
|
-
methodKeyPublic = existingKey.publicKey;
|
|
229
|
-
}
|
|
230
|
-
catch { }
|
|
231
|
-
}
|
|
232
|
-
if (core.Is.empty(methodKeyPublic)) {
|
|
233
|
-
// If there is no existing key, we will create a new one with a temporary name.
|
|
234
|
-
tempKeyId = `temp-vm-${core.Converter.bytesToBase64Url(core.RandomHelper.generate(16))}`;
|
|
235
|
-
methodKeyPublic = await this._vaultConnector.createKey(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, tempKeyId), vaultModels.VaultKeyType.Ed25519);
|
|
236
|
-
}
|
|
237
|
-
const jwkParams = {
|
|
238
|
-
alg: "EdDSA",
|
|
239
|
-
kty: "OKP",
|
|
240
|
-
crv: "Ed25519",
|
|
241
|
-
x: core.Converter.bytesToBase64Url(methodKeyPublic)
|
|
242
|
-
};
|
|
243
|
-
const kid = await web.Jwk.generateKid(jwkParams);
|
|
244
|
-
const methodId = `${documentId}#${verificationMethodId ?? kid}`;
|
|
245
|
-
if (core.Is.stringValue(tempKeyId)) {
|
|
246
|
-
// If we created a temporary key, we will rename it to the final method id.
|
|
247
|
-
await this._vaultConnector.renameKey(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, tempKeyId), EntityStorageIdentityConnector.buildVaultKey(didDocument.id, verificationMethodId ?? kid));
|
|
248
|
-
tempKeyId = undefined;
|
|
249
|
-
}
|
|
250
|
-
const methods = this.getAllMethods(didDocument);
|
|
251
|
-
const existingMethodIndex = methods.findIndex(m => {
|
|
252
|
-
if (core.Is.string(m.method)) {
|
|
253
|
-
return m.method === methodId;
|
|
254
|
-
}
|
|
255
|
-
return m.method.id === methodId;
|
|
256
|
-
});
|
|
257
|
-
if (existingMethodIndex !== -1) {
|
|
258
|
-
const methodArray = didDocument[methods[existingMethodIndex].arrayKey];
|
|
259
|
-
if (core.Is.array(methodArray)) {
|
|
260
|
-
methodArray.splice(existingMethodIndex, 1);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const didVerificationMethod = {
|
|
264
|
-
id: methodId,
|
|
265
|
-
controller: documentId,
|
|
266
|
-
type: "JsonWebKey",
|
|
267
|
-
publicKeyJwk: {
|
|
268
|
-
...jwkParams,
|
|
269
|
-
kid
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
didDocument[verificationMethodType] ??= [];
|
|
273
|
-
didDocument[verificationMethodType]?.push(didVerificationMethod);
|
|
274
|
-
await this.updateDocument(controller, didDocument);
|
|
275
|
-
return didVerificationMethod;
|
|
276
|
-
}
|
|
277
|
-
catch (error) {
|
|
278
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "addVerificationMethodFailed", undefined, error);
|
|
279
|
-
}
|
|
280
|
-
finally {
|
|
281
|
-
if (core.Is.stringValue(tempKeyId)) {
|
|
282
|
-
// If we created a temporary key and it is still in use, we will remove it from the vault.
|
|
283
|
-
try {
|
|
284
|
-
await this._vaultConnector.removeKey(tempKeyId);
|
|
285
|
-
}
|
|
286
|
-
catch { }
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
/**
|
|
291
|
-
* Remove a verification method from the document.
|
|
292
|
-
* @param controller The controller of the identity who can make changes.
|
|
293
|
-
* @param verificationMethodId The id of the verification method.
|
|
294
|
-
* @returns Nothing.
|
|
295
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
296
|
-
* @throws NotSupportedError if the platform does not support multiple revocable keys.
|
|
297
|
-
*/
|
|
298
|
-
async removeVerificationMethod(controller, verificationMethodId) {
|
|
299
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
300
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodId", verificationMethodId);
|
|
301
|
-
try {
|
|
302
|
-
const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
|
|
303
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
304
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", verificationMethodId);
|
|
305
|
-
}
|
|
306
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
307
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
308
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
309
|
-
}
|
|
310
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
311
|
-
const didDocument = didIdentityDocument.document;
|
|
312
|
-
const methods = this.getAllMethods(didDocument);
|
|
313
|
-
const existingMethodIndex = methods.findIndex(m => {
|
|
314
|
-
if (core.Is.string(m.method)) {
|
|
315
|
-
return m.method === verificationMethodId;
|
|
316
|
-
}
|
|
317
|
-
return m.method.id === verificationMethodId;
|
|
318
|
-
});
|
|
319
|
-
if (existingMethodIndex !== -1) {
|
|
320
|
-
const methodArray = didDocument[methods[existingMethodIndex].arrayKey];
|
|
321
|
-
if (core.Is.array(methodArray)) {
|
|
322
|
-
methodArray.splice(existingMethodIndex, 1);
|
|
323
|
-
if (methodArray.length === 0) {
|
|
324
|
-
delete didDocument[methods[existingMethodIndex].arrayKey];
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodNotFound", verificationMethodId);
|
|
330
|
-
}
|
|
331
|
-
await this.updateDocument(controller, didDocument);
|
|
332
|
-
}
|
|
333
|
-
catch (error) {
|
|
334
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "removeVerificationMethodFailed", undefined, error);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Add a service to the document.
|
|
339
|
-
* @param controller The controller of the identity who can make changes.
|
|
340
|
-
* @param documentId The id of the document to add the service to.
|
|
341
|
-
* @param serviceId The id of the service.
|
|
342
|
-
* @param serviceType The type of the service.
|
|
343
|
-
* @param serviceEndpoint The endpoint for the service.
|
|
344
|
-
* @returns The service.
|
|
345
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
346
|
-
*/
|
|
347
|
-
async addService(controller, documentId, serviceId, serviceType, serviceEndpoint) {
|
|
348
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
349
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "documentId", documentId);
|
|
350
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceId", serviceId);
|
|
351
|
-
if (core.Is.array(serviceType)) {
|
|
352
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceType", serviceType);
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceType", serviceType);
|
|
356
|
-
}
|
|
357
|
-
if (core.Is.array(serviceEndpoint)) {
|
|
358
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceEndpoint", serviceEndpoint);
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceEndpoint", serviceEndpoint);
|
|
362
|
-
}
|
|
363
|
-
try {
|
|
364
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
365
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
366
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
367
|
-
}
|
|
368
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
369
|
-
const didDocument = didIdentityDocument.document;
|
|
370
|
-
const fullServiceId = serviceId.includes("#") ? serviceId : `${documentId}#${serviceId}`;
|
|
371
|
-
if (core.Is.array(didDocument.service)) {
|
|
372
|
-
const existingServiceIndex = didDocument.service.findIndex(s => s.id === fullServiceId);
|
|
373
|
-
if (existingServiceIndex !== -1) {
|
|
374
|
-
didDocument.service?.splice(existingServiceIndex, 1);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
const didService = {
|
|
378
|
-
id: fullServiceId,
|
|
379
|
-
type: serviceType,
|
|
380
|
-
serviceEndpoint
|
|
381
|
-
};
|
|
382
|
-
didDocument.service ??= [];
|
|
383
|
-
didDocument.service.push(didService);
|
|
384
|
-
await this.updateDocument(controller, didDocument);
|
|
385
|
-
return didService;
|
|
386
|
-
}
|
|
387
|
-
catch (error) {
|
|
388
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "addServiceFailed", undefined, error);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* Remove a service from the document.
|
|
393
|
-
* @param controller The controller of the identity who can make changes.
|
|
394
|
-
* @param serviceId The id of the service.
|
|
395
|
-
* @returns Nothing.
|
|
396
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
397
|
-
*/
|
|
398
|
-
async removeService(controller, serviceId) {
|
|
399
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
400
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "serviceId", serviceId);
|
|
401
|
-
try {
|
|
402
|
-
const idParts = identityModels.DocumentHelper.parseId(serviceId);
|
|
403
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
404
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", serviceId);
|
|
405
|
-
}
|
|
406
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
407
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
408
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
409
|
-
}
|
|
410
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
411
|
-
const didDocument = didIdentityDocument.document;
|
|
412
|
-
if (core.Is.array(didDocument.service)) {
|
|
413
|
-
const existingServiceIndex = didDocument.service.findIndex(s => s.id === serviceId);
|
|
414
|
-
if (existingServiceIndex !== -1) {
|
|
415
|
-
didDocument.service?.splice(existingServiceIndex, 1);
|
|
416
|
-
if (didDocument.service?.length === 0) {
|
|
417
|
-
delete didDocument.service;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
else {
|
|
422
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "serviceNotFound", serviceId);
|
|
423
|
-
}
|
|
424
|
-
await this.updateDocument(controller, didDocument);
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "removeServiceFailed", undefined, error);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
/**
|
|
431
|
-
* Create a verifiable credential for a verification method.
|
|
432
|
-
* @param controller The controller of the identity who can make changes.
|
|
433
|
-
* @param verificationMethodId The verification method id to use.
|
|
434
|
-
* @param id The id of the credential.
|
|
435
|
-
* @param subject The credential subject to store in the verifiable credential.
|
|
436
|
-
* @param options Additional options for creating the verifiable credential.
|
|
437
|
-
* @param options.revocationIndex The bitmap revocation index of the credential, if undefined will not have revocation status.
|
|
438
|
-
* @param options.expirationDate The date the verifiable credential is valid until.
|
|
439
|
-
* @returns The created verifiable credential and its token.
|
|
440
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
441
|
-
*/
|
|
442
|
-
async createVerifiableCredential(controller, verificationMethodId, id, subject, options) {
|
|
443
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
444
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodId", verificationMethodId);
|
|
445
|
-
core.Guards.object(EntityStorageIdentityConnector.CLASS_NAME, "subject", subject);
|
|
446
|
-
if (!core.Is.undefined(options?.revocationIndex)) {
|
|
447
|
-
core.Guards.number(EntityStorageIdentityConnector.CLASS_NAME, "options.revocationIndex", options.revocationIndex);
|
|
448
|
-
}
|
|
449
|
-
try {
|
|
450
|
-
const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
|
|
451
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
452
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", verificationMethodId);
|
|
453
|
-
}
|
|
454
|
-
const issuerIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
455
|
-
if (core.Is.undefined(issuerIdentityDocument)) {
|
|
456
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
457
|
-
}
|
|
458
|
-
await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
|
|
459
|
-
const issuerDidDocument = issuerIdentityDocument.document;
|
|
460
|
-
const methods = this.getAllMethods(issuerDidDocument);
|
|
461
|
-
const methodAndArray = methods.find(m => {
|
|
462
|
-
if (core.Is.string(m.method)) {
|
|
463
|
-
return m.method === verificationMethodId;
|
|
464
|
-
}
|
|
465
|
-
return m.method.id === verificationMethodId;
|
|
466
|
-
});
|
|
467
|
-
if (!methodAndArray) {
|
|
468
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "methodMissing", {
|
|
469
|
-
method: verificationMethodId
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
const verificationDidMethod = methodAndArray.method;
|
|
473
|
-
if (!core.Is.stringValue(verificationDidMethod.publicKeyJwk?.x)) {
|
|
474
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "publicKeyJwkMissing", {
|
|
475
|
-
method: verificationMethodId
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
|
|
479
|
-
const subjectClone = core.ObjectHelper.clone(subject);
|
|
480
|
-
const finalTypes = [standardsW3cDid.DidTypes.VerifiableCredential];
|
|
481
|
-
const credContext = core.ObjectHelper.extractProperty(subjectClone, [
|
|
482
|
-
"@context"
|
|
483
|
-
]);
|
|
484
|
-
const credId = core.ObjectHelper.extractProperty(subjectClone, ["@id", "id"], false);
|
|
485
|
-
const credType = core.ObjectHelper.extractProperty(subjectClone, ["@type", "type"]);
|
|
486
|
-
if (core.Is.stringValue(credType)) {
|
|
487
|
-
finalTypes.push(credType);
|
|
488
|
-
}
|
|
489
|
-
const verifiableCredential = {
|
|
490
|
-
"@context": dataJsonLd.JsonLdProcessor.combineContexts(standardsW3cDid.DidContexts.ContextVCv1, credContext),
|
|
491
|
-
id,
|
|
492
|
-
type: finalTypes,
|
|
493
|
-
credentialSubject: subjectClone,
|
|
494
|
-
issuer: issuerDidDocument.id,
|
|
495
|
-
issuanceDate: new Date(Date.now()).toISOString(),
|
|
496
|
-
expirationDate: core.Is.date(options?.expirationDate)
|
|
497
|
-
? options?.expirationDate.toISOString()
|
|
498
|
-
: undefined,
|
|
499
|
-
credentialStatus: revocationService && !core.Is.undefined(options?.revocationIndex)
|
|
500
|
-
? {
|
|
501
|
-
id: revocationService.id,
|
|
502
|
-
type: core.Is.array(revocationService.type)
|
|
503
|
-
? revocationService.type[0]
|
|
504
|
-
: revocationService.type,
|
|
505
|
-
revocationBitmapIndex: options.revocationIndex.toString()
|
|
506
|
-
}
|
|
507
|
-
: undefined
|
|
508
|
-
};
|
|
509
|
-
const jwtHeader = {
|
|
510
|
-
kid: verificationDidMethod.id,
|
|
511
|
-
typ: "JWT",
|
|
512
|
-
alg: "EdDSA"
|
|
513
|
-
};
|
|
514
|
-
const jwtVc = core.ObjectHelper.pick(core.ObjectHelper.clone(verifiableCredential), [
|
|
515
|
-
"@context",
|
|
516
|
-
"type",
|
|
517
|
-
"credentialSubject",
|
|
518
|
-
"credentialStatus"
|
|
519
|
-
]);
|
|
520
|
-
if (core.Is.array(jwtVc.credentialSubject)) {
|
|
521
|
-
jwtVc.credentialSubject = jwtVc.credentialSubject.map(c => {
|
|
522
|
-
core.ObjectHelper.propertyDelete(c, "id");
|
|
523
|
-
return c;
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
core.ObjectHelper.propertyDelete(jwtVc.credentialSubject, "id");
|
|
528
|
-
}
|
|
529
|
-
const jwtPayload = {
|
|
530
|
-
iss: idParts.id,
|
|
531
|
-
nbf: Math.floor(Date.now() / 1000),
|
|
532
|
-
jti: verifiableCredential.id,
|
|
533
|
-
sub: credId,
|
|
534
|
-
vc: jwtVc
|
|
535
|
-
};
|
|
536
|
-
const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => vaultModels.VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
|
|
537
|
-
return {
|
|
538
|
-
verifiableCredential,
|
|
539
|
-
jwt: signature
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
catch (error) {
|
|
543
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "createVerifiableCredentialFailed", undefined, error);
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Check a verifiable credential is valid.
|
|
548
|
-
* @param credentialJwt The credential to verify.
|
|
549
|
-
* @returns The credential stored in the jwt and the revocation status.
|
|
550
|
-
*/
|
|
551
|
-
async checkVerifiableCredential(credentialJwt) {
|
|
552
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "credentialJwt", credentialJwt);
|
|
553
|
-
try {
|
|
554
|
-
const jwtDecoded = await web.Jwt.decode(credentialJwt);
|
|
555
|
-
const jwtHeader = jwtDecoded.header;
|
|
556
|
-
const jwtPayload = jwtDecoded.payload;
|
|
557
|
-
const jwtSignature = jwtDecoded.signature;
|
|
558
|
-
if (core.Is.undefined(jwtHeader) ||
|
|
559
|
-
core.Is.undefined(jwtPayload) ||
|
|
560
|
-
core.Is.undefined(jwtPayload.iss) ||
|
|
561
|
-
core.Is.undefined(jwtSignature)) {
|
|
562
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "jwkSignatureFailed");
|
|
563
|
-
}
|
|
564
|
-
const issuerDocumentId = jwtPayload.iss;
|
|
565
|
-
const issuerIdentityDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
|
|
566
|
-
if (core.Is.undefined(issuerIdentityDocument)) {
|
|
567
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", issuerDocumentId);
|
|
568
|
-
}
|
|
569
|
-
await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
|
|
570
|
-
const issuerDidDocument = issuerIdentityDocument.document;
|
|
571
|
-
const methods = this.getAllMethods(issuerDidDocument);
|
|
572
|
-
const methodAndArray = methods.find(m => {
|
|
573
|
-
if (core.Is.string(m.method)) {
|
|
574
|
-
return m.method === jwtHeader.kid;
|
|
575
|
-
}
|
|
576
|
-
return m.method.id === jwtHeader.kid;
|
|
577
|
-
});
|
|
578
|
-
if (!methodAndArray) {
|
|
579
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "methodMissing", {
|
|
580
|
-
method: jwtHeader.kid
|
|
581
|
-
});
|
|
582
|
-
}
|
|
583
|
-
const didMethod = methodAndArray.method;
|
|
584
|
-
if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
|
|
585
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "publicKeyJwkMissing", {
|
|
586
|
-
method: jwtHeader.kid
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
await web.Jwt.verifySignature(credentialJwt, await web.Jwk.toCryptoKey(didMethod.publicKeyJwk));
|
|
590
|
-
const verifiableCredential = jwtPayload.vc;
|
|
591
|
-
if (core.Is.object(verifiableCredential)) {
|
|
592
|
-
if (core.Is.string(jwtPayload.jti)) {
|
|
593
|
-
verifiableCredential.id = jwtPayload.jti;
|
|
594
|
-
}
|
|
595
|
-
verifiableCredential.issuer = issuerDocumentId;
|
|
596
|
-
if (core.Is.number(jwtPayload.nbf)) {
|
|
597
|
-
verifiableCredential.issuanceDate = new Date(jwtPayload.nbf * 1000).toISOString();
|
|
598
|
-
}
|
|
599
|
-
if (core.Is.array(verifiableCredential.credentialSubject)) {
|
|
600
|
-
verifiableCredential.credentialSubject = verifiableCredential.credentialSubject.map(c => {
|
|
601
|
-
core.ObjectHelper.propertySet(c, "id", jwtPayload.sub);
|
|
602
|
-
return c;
|
|
603
|
-
});
|
|
604
|
-
}
|
|
605
|
-
else if (core.Is.object(verifiableCredential.credentialSubject)) {
|
|
606
|
-
core.ObjectHelper.propertySet(verifiableCredential.credentialSubject, "id", jwtPayload.sub);
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const credentialStatus = verifiableCredential.credentialStatus;
|
|
610
|
-
let revoked = false;
|
|
611
|
-
if (core.Is.object(credentialStatus)) {
|
|
612
|
-
revoked = await this.checkRevocation(issuerDidDocument, credentialStatus.revocationBitmapIndex);
|
|
613
|
-
}
|
|
614
|
-
else if (core.Is.arrayValue(credentialStatus)) {
|
|
615
|
-
for (let i = 0; i < credentialStatus.length; i++) {
|
|
616
|
-
revoked = await this.checkRevocation(issuerDidDocument, credentialStatus[i].revocationBitmapIndex);
|
|
617
|
-
if (revoked) {
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return {
|
|
623
|
-
revoked,
|
|
624
|
-
verifiableCredential: revoked ? undefined : verifiableCredential
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
catch (error) {
|
|
628
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "checkingVerifiableCredentialFailed", undefined, error);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* Revoke verifiable credential(s).
|
|
633
|
-
* @param controller The controller of the identity who can make changes.
|
|
634
|
-
* @param issuerDocumentId The id of the document to update the revocation list for.
|
|
635
|
-
* @param credentialIndices The revocation bitmap index or indices to revoke.
|
|
636
|
-
* @returns Nothing.
|
|
637
|
-
*/
|
|
638
|
-
async revokeVerifiableCredentials(controller, issuerDocumentId, credentialIndices) {
|
|
639
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
640
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "issuerDocumentId", issuerDocumentId);
|
|
641
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "credentialIndices", credentialIndices);
|
|
642
|
-
try {
|
|
643
|
-
const issuerIdentityDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
|
|
644
|
-
if (core.Is.undefined(issuerIdentityDocument)) {
|
|
645
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", issuerDocumentId);
|
|
646
|
-
}
|
|
647
|
-
await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
|
|
648
|
-
const issuerDidDocument = issuerIdentityDocument.document;
|
|
649
|
-
const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
|
|
650
|
-
if (revocationService &&
|
|
651
|
-
core.Is.string(revocationService.serviceEndpoint) &&
|
|
652
|
-
revocationService.type === "BitstringStatusList") {
|
|
653
|
-
const revocationParts = revocationService.serviceEndpoint.split(",");
|
|
654
|
-
if (revocationParts.length === 2) {
|
|
655
|
-
const compressedRevocationBytes = core.Converter.base64UrlToBytes(revocationParts[1]);
|
|
656
|
-
const decompressed = await core.Compression.decompress(compressedRevocationBytes, core.CompressionType.Gzip);
|
|
657
|
-
const bitString = core.BitString.fromBits(decompressed, EntityStorageIdentityConnector._REVOCATION_BITS_SIZE);
|
|
658
|
-
for (const credentialIndex of credentialIndices) {
|
|
659
|
-
bitString.setBit(credentialIndex, true);
|
|
660
|
-
}
|
|
661
|
-
const compressed = await core.Compression.compress(bitString.getBits(), core.CompressionType.Gzip);
|
|
662
|
-
revocationService.serviceEndpoint = `data:application/octet-stream;base64,${core.Converter.bytesToBase64Url(compressed)}`;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
await this.updateDocument(controller, issuerDidDocument);
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "revokeVerifiableCredentialsFailed", undefined, error);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Unrevoke verifiable credential(s).
|
|
673
|
-
* @param controller The controller of the identity who can make changes.
|
|
674
|
-
* @param issuerDocumentId The id of the document to update the revocation list for.
|
|
675
|
-
* @param credentialIndices The revocation bitmap index or indices to un revoke.
|
|
676
|
-
* @returns Nothing.
|
|
677
|
-
*/
|
|
678
|
-
async unrevokeVerifiableCredentials(controller, issuerDocumentId, credentialIndices) {
|
|
679
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
680
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "issuerDocumentId", issuerDocumentId);
|
|
681
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "credentialIndices", credentialIndices);
|
|
682
|
-
try {
|
|
683
|
-
const issuerIdentityDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
|
|
684
|
-
if (core.Is.undefined(issuerIdentityDocument)) {
|
|
685
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", issuerDocumentId);
|
|
686
|
-
}
|
|
687
|
-
await EntityStorageIdentityConnector.verifyDocument(issuerIdentityDocument, this._vaultConnector);
|
|
688
|
-
const issuerDidDocument = issuerIdentityDocument.document;
|
|
689
|
-
const revocationService = issuerDidDocument.service?.find(s => s.id.endsWith("#revocation"));
|
|
690
|
-
if (revocationService &&
|
|
691
|
-
core.Is.string(revocationService.serviceEndpoint) &&
|
|
692
|
-
revocationService.type === "BitstringStatusList") {
|
|
693
|
-
const revocationParts = revocationService.serviceEndpoint.split(",");
|
|
694
|
-
if (revocationParts.length === 2) {
|
|
695
|
-
const compressedRevocationBytes = core.Converter.base64UrlToBytes(revocationParts[1]);
|
|
696
|
-
const decompressed = await core.Compression.decompress(compressedRevocationBytes, core.CompressionType.Gzip);
|
|
697
|
-
const bitString = core.BitString.fromBits(decompressed, EntityStorageIdentityConnector._REVOCATION_BITS_SIZE);
|
|
698
|
-
for (const credentialIndex of credentialIndices) {
|
|
699
|
-
bitString.setBit(credentialIndex, false);
|
|
700
|
-
}
|
|
701
|
-
const compressed = await core.Compression.compress(bitString.getBits(), core.CompressionType.Gzip);
|
|
702
|
-
revocationService.serviceEndpoint = `data:application/octet-stream;base64,${core.Converter.bytesToBase64Url(compressed)}`;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
await this.updateDocument(controller, issuerDidDocument);
|
|
706
|
-
}
|
|
707
|
-
catch (error) {
|
|
708
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "unrevokeVerifiableCredentialsFailed", undefined, error);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Create a verifiable presentation from the supplied verifiable credentials.
|
|
713
|
-
* @param controller The controller of the identity who can make changes.
|
|
714
|
-
* @param verificationMethodId The method to associate with the presentation.
|
|
715
|
-
* @param presentationId The id of the presentation.
|
|
716
|
-
* @param contexts The contexts for the data stored in the verifiable credential.
|
|
717
|
-
* @param types The types for the data stored in the verifiable credential.
|
|
718
|
-
* @param verifiableCredentials The credentials to use for creating the presentation in jwt format.
|
|
719
|
-
* @param expiresInMinutes The time in minutes for the presentation to expire.
|
|
720
|
-
* @returns The created verifiable presentation and its token.
|
|
721
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
722
|
-
*/
|
|
723
|
-
async createVerifiablePresentation(controller, verificationMethodId, presentationId, contexts, types, verifiableCredentials, expiresInMinutes) {
|
|
724
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
725
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodId", verificationMethodId);
|
|
726
|
-
if (core.Is.array(types)) {
|
|
727
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "types", types);
|
|
728
|
-
}
|
|
729
|
-
else if (core.Is.string(types)) {
|
|
730
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "types", types);
|
|
731
|
-
}
|
|
732
|
-
core.Guards.arrayValue(EntityStorageIdentityConnector.CLASS_NAME, "verifiableCredentials", verifiableCredentials);
|
|
733
|
-
if (!core.Is.undefined(expiresInMinutes)) {
|
|
734
|
-
core.Guards.integer(EntityStorageIdentityConnector.CLASS_NAME, "expiresInMinutes", expiresInMinutes);
|
|
735
|
-
}
|
|
736
|
-
try {
|
|
737
|
-
const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
|
|
738
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
739
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", verificationMethodId);
|
|
740
|
-
}
|
|
741
|
-
const holderIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
742
|
-
if (core.Is.undefined(holderIdentityDocument)) {
|
|
743
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
744
|
-
}
|
|
745
|
-
await EntityStorageIdentityConnector.verifyDocument(holderIdentityDocument, this._vaultConnector);
|
|
746
|
-
const holderDidDocument = holderIdentityDocument.document;
|
|
747
|
-
const methods = this.getAllMethods(holderDidDocument);
|
|
748
|
-
const methodAndArray = methods.find(m => {
|
|
749
|
-
if (core.Is.string(m.method)) {
|
|
750
|
-
return m.method === verificationMethodId;
|
|
751
|
-
}
|
|
752
|
-
return m.method.id === verificationMethodId;
|
|
753
|
-
});
|
|
754
|
-
if (!methodAndArray) {
|
|
755
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "methodMissing", {
|
|
756
|
-
method: verificationMethodId
|
|
757
|
-
});
|
|
758
|
-
}
|
|
759
|
-
const didMethod = methodAndArray.method;
|
|
760
|
-
if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
|
|
761
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "publicKeyJwkMissing", {
|
|
762
|
-
method: verificationMethodId
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
const finalTypes = [standardsW3cDid.DidTypes.VerifiablePresentation];
|
|
766
|
-
if (core.Is.array(types)) {
|
|
767
|
-
finalTypes.push(...types);
|
|
768
|
-
}
|
|
769
|
-
else if (core.Is.stringValue(types)) {
|
|
770
|
-
finalTypes.push(types);
|
|
771
|
-
}
|
|
772
|
-
const verifiablePresentation = {
|
|
773
|
-
"@context": dataJsonLd.JsonLdProcessor.combineContexts(standardsW3cDid.DidContexts.ContextVCv1, contexts),
|
|
774
|
-
id: presentationId,
|
|
775
|
-
type: finalTypes,
|
|
776
|
-
verifiableCredential: verifiableCredentials,
|
|
777
|
-
holder: idParts.id
|
|
778
|
-
};
|
|
779
|
-
const jwtHeader = {
|
|
780
|
-
kid: didMethod.id,
|
|
781
|
-
typ: "JWT",
|
|
782
|
-
alg: "EdDSA"
|
|
783
|
-
};
|
|
784
|
-
const jwtVp = core.ObjectHelper.pick(core.ObjectHelper.clone(verifiablePresentation), [
|
|
785
|
-
"@context",
|
|
786
|
-
"type",
|
|
787
|
-
"verifiableCredential"
|
|
788
|
-
]);
|
|
789
|
-
const jwtPayload = {
|
|
790
|
-
iss: idParts.id,
|
|
791
|
-
nbf: Math.floor(Date.now() / 1000),
|
|
792
|
-
vp: jwtVp
|
|
793
|
-
};
|
|
794
|
-
if (core.Is.integer(expiresInMinutes)) {
|
|
795
|
-
const expiresInSeconds = expiresInMinutes * 60;
|
|
796
|
-
jwtPayload.exp = Math.floor(Date.now() / 1000) + expiresInSeconds;
|
|
797
|
-
}
|
|
798
|
-
const signature = await web.Jwt.encodeWithSigner(jwtHeader, jwtPayload, async (header, payload) => vaultModels.VaultConnectorHelper.jwtSigner(this._vaultConnector, EntityStorageIdentityConnector.buildVaultKey(idParts.id, idParts.fragment ?? ""), header, payload));
|
|
799
|
-
return {
|
|
800
|
-
verifiablePresentation,
|
|
801
|
-
jwt: signature
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
catch (error) {
|
|
805
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "createVerifiablePresentationFailed", undefined, error);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
/**
|
|
809
|
-
* Check a verifiable presentation is valid.
|
|
810
|
-
* @param presentationJwt The presentation to verify.
|
|
811
|
-
* @returns The presentation stored in the jwt and the revocation status.
|
|
812
|
-
*/
|
|
813
|
-
async checkVerifiablePresentation(presentationJwt) {
|
|
814
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "presentationJwt", presentationJwt);
|
|
815
|
-
try {
|
|
816
|
-
const jwtDecoded = await web.Jwt.decode(presentationJwt);
|
|
817
|
-
const jwtHeader = jwtDecoded.header;
|
|
818
|
-
const jwtPayload = jwtDecoded.payload;
|
|
819
|
-
const jwtSignature = jwtDecoded.signature;
|
|
820
|
-
if (core.Is.undefined(jwtHeader) ||
|
|
821
|
-
core.Is.undefined(jwtPayload) ||
|
|
822
|
-
core.Is.undefined(jwtPayload.iss) ||
|
|
823
|
-
core.Is.undefined(jwtSignature)) {
|
|
824
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "jwkSignatureFailed");
|
|
825
|
-
}
|
|
826
|
-
const holderDocumentId = jwtPayload.iss;
|
|
827
|
-
const holderIdentityDocument = await this._didDocumentEntityStorage.get(holderDocumentId);
|
|
828
|
-
if (core.Is.undefined(holderIdentityDocument)) {
|
|
829
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", holderDocumentId);
|
|
830
|
-
}
|
|
831
|
-
await EntityStorageIdentityConnector.verifyDocument(holderIdentityDocument, this._vaultConnector);
|
|
832
|
-
const issuers = [];
|
|
833
|
-
const tokensRevoked = [];
|
|
834
|
-
const verifiablePresentation = jwtPayload?.vp;
|
|
835
|
-
if (core.Is.object(verifiablePresentation) &&
|
|
836
|
-
core.Is.array(verifiablePresentation.verifiableCredential)) {
|
|
837
|
-
for (const vcJwt of verifiablePresentation.verifiableCredential) {
|
|
838
|
-
let revoked = true;
|
|
839
|
-
if (core.Is.stringValue(vcJwt)) {
|
|
840
|
-
const jwt = await web.Jwt.decode(vcJwt);
|
|
841
|
-
if (core.Is.string(jwt.payload?.iss)) {
|
|
842
|
-
const issuerDocumentId = jwt.payload.iss;
|
|
843
|
-
verifiablePresentation.holder = issuerDocumentId;
|
|
844
|
-
const issuerDidDocument = await this._didDocumentEntityStorage.get(issuerDocumentId);
|
|
845
|
-
if (core.Is.undefined(issuerDidDocument)) {
|
|
846
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", issuerDocumentId);
|
|
847
|
-
}
|
|
848
|
-
await EntityStorageIdentityConnector.verifyDocument(issuerDidDocument, this._vaultConnector);
|
|
849
|
-
issuers.push({
|
|
850
|
-
"@context": standardsW3cDid.DidContexts.Context,
|
|
851
|
-
...issuerDidDocument
|
|
852
|
-
});
|
|
853
|
-
const vc = jwt.payload.vc;
|
|
854
|
-
if (core.Is.object(vc)) {
|
|
855
|
-
const credentialStatus = vc.credentialStatus;
|
|
856
|
-
if (core.Is.object(credentialStatus)) {
|
|
857
|
-
revoked = await this.checkRevocation({
|
|
858
|
-
"@context": standardsW3cDid.DidContexts.Context,
|
|
859
|
-
...issuerDidDocument
|
|
860
|
-
}, credentialStatus.revocationBitmapIndex);
|
|
861
|
-
}
|
|
862
|
-
else if (core.Is.arrayValue(credentialStatus)) {
|
|
863
|
-
for (let i = 0; i < credentialStatus.length; i++) {
|
|
864
|
-
revoked = await this.checkRevocation({
|
|
865
|
-
"@context": standardsW3cDid.DidContexts.Context,
|
|
866
|
-
...issuerDidDocument
|
|
867
|
-
}, credentialStatus[i].revocationBitmapIndex);
|
|
868
|
-
if (revoked) {
|
|
869
|
-
break;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
revoked = false;
|
|
878
|
-
}
|
|
879
|
-
tokensRevoked.push(revoked);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return {
|
|
883
|
-
revoked: tokensRevoked.some(Boolean),
|
|
884
|
-
verifiablePresentation,
|
|
885
|
-
issuers
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
catch (error) {
|
|
889
|
-
if (core.BaseError.isErrorMessage(error, /revoked/i)) {
|
|
890
|
-
return {
|
|
891
|
-
revoked: true
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "checkingVerifiablePresentationFailed", undefined, error);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Create a proof for arbitrary data with the specified verification method.
|
|
899
|
-
* @param controller The controller of the identity who can make changes.
|
|
900
|
-
* @param verificationMethodId The verification method id to use.
|
|
901
|
-
* @param proofType The type of proof to create.
|
|
902
|
-
* @param unsecureDocument The unsecure document to create the proof for.
|
|
903
|
-
* @returns The proof.
|
|
904
|
-
*/
|
|
905
|
-
async createProof(controller, verificationMethodId, proofType, unsecureDocument) {
|
|
906
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "controller", controller);
|
|
907
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "verificationMethodId", verificationMethodId);
|
|
908
|
-
core.Guards.arrayOneOf(EntityStorageIdentityConnector.CLASS_NAME, "proofType", proofType, Object.values(standardsW3cDid.ProofTypes));
|
|
909
|
-
core.Guards.object(EntityStorageIdentityConnector.CLASS_NAME, "unsecureDocument", unsecureDocument);
|
|
910
|
-
try {
|
|
911
|
-
const idParts = identityModels.DocumentHelper.parseId(verificationMethodId);
|
|
912
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
913
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", verificationMethodId);
|
|
914
|
-
}
|
|
915
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
916
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
917
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
918
|
-
}
|
|
919
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
920
|
-
const didDocument = didIdentityDocument.document;
|
|
921
|
-
const methods = this.getAllMethods(didDocument);
|
|
922
|
-
const methodAndArray = methods.find(m => {
|
|
923
|
-
if (core.Is.string(m.method)) {
|
|
924
|
-
return m.method === verificationMethodId;
|
|
925
|
-
}
|
|
926
|
-
return m.method.id === verificationMethodId;
|
|
927
|
-
});
|
|
928
|
-
if (!methodAndArray) {
|
|
929
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "methodMissing", {
|
|
930
|
-
method: verificationMethodId
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
const didMethod = methodAndArray.method;
|
|
934
|
-
if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
|
|
935
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "publicKeyJwkMissing", {
|
|
936
|
-
method: verificationMethodId
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
const vaultKey = EntityStorageIdentityConnector.buildVaultKey(didDocument.id, idParts.fragment ?? "");
|
|
940
|
-
const key = await this._vaultConnector.getKey(vaultKey);
|
|
941
|
-
const signedProof = await standardsW3cDid.ProofHelper.createProof(proofType, unsecureDocument, standardsW3cDid.ProofHelper.createUnsignedProof(proofType, verificationMethodId), await web.Jwk.fromEd25519Private(key.privateKey));
|
|
942
|
-
return signedProof;
|
|
943
|
-
}
|
|
944
|
-
catch (error) {
|
|
945
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "createProofFailed", undefined, error);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* Verify proof for arbitrary data with the specified verification method.
|
|
950
|
-
* @param document The document to verify.
|
|
951
|
-
* @param proof The proof to verify.
|
|
952
|
-
* @returns True if the proof is verified.
|
|
953
|
-
*/
|
|
954
|
-
async verifyProof(document, proof) {
|
|
955
|
-
core.Guards.object(EntityStorageIdentityConnector.CLASS_NAME, "document", document);
|
|
956
|
-
core.Guards.object(EntityStorageIdentityConnector.CLASS_NAME, "proof", proof);
|
|
957
|
-
core.Guards.stringValue(EntityStorageIdentityConnector.CLASS_NAME, "proof.verificationMethod", proof.verificationMethod);
|
|
958
|
-
try {
|
|
959
|
-
const idParts = identityModels.DocumentHelper.parseId(proof.verificationMethod);
|
|
960
|
-
if (core.Is.empty(idParts.fragment)) {
|
|
961
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "missingDid", proof.verificationMethod);
|
|
962
|
-
}
|
|
963
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(idParts.id);
|
|
964
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
965
|
-
throw new core.NotFoundError(EntityStorageIdentityConnector.CLASS_NAME, "documentNotFound", idParts.id);
|
|
966
|
-
}
|
|
967
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
968
|
-
const didDocument = didIdentityDocument.document;
|
|
969
|
-
const methods = this.getAllMethods(didDocument);
|
|
970
|
-
const methodAndArray = methods.find(m => {
|
|
971
|
-
if (core.Is.string(m.method)) {
|
|
972
|
-
return m.method === proof.verificationMethod;
|
|
973
|
-
}
|
|
974
|
-
return m.method.id === proof.verificationMethod;
|
|
975
|
-
});
|
|
976
|
-
if (!methodAndArray) {
|
|
977
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "methodMissing", {
|
|
978
|
-
method: proof.verificationMethod
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
const didMethod = methodAndArray.method;
|
|
982
|
-
if (!core.Is.stringValue(didMethod.publicKeyJwk?.x)) {
|
|
983
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "publicKeyJwkMissing", {
|
|
984
|
-
method: proof.verificationMethod
|
|
985
|
-
});
|
|
986
|
-
}
|
|
987
|
-
return standardsW3cDid.ProofHelper.verifyProof(document, proof, didMethod.publicKeyJwk);
|
|
988
|
-
}
|
|
989
|
-
catch (error) {
|
|
990
|
-
throw new core.GeneralError(EntityStorageIdentityConnector.CLASS_NAME, "verifyProofFailed", undefined, error);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Get all the methods from a document.
|
|
995
|
-
* @param document The document to get the methods from.
|
|
996
|
-
* @returns The methods.
|
|
997
|
-
* @internal
|
|
998
|
-
*/
|
|
999
|
-
getAllMethods(document) {
|
|
1000
|
-
const methods = [];
|
|
1001
|
-
const methodTypes = Object.values(standardsW3cDid.DidVerificationMethodType);
|
|
1002
|
-
for (const methodType of methodTypes) {
|
|
1003
|
-
const mt = document[methodType];
|
|
1004
|
-
if (core.Is.arrayValue(mt)) {
|
|
1005
|
-
methods.push(...mt.map(m => ({
|
|
1006
|
-
arrayKey: methodType,
|
|
1007
|
-
method: core.Is.string(m) ? { id: m } : m
|
|
1008
|
-
})));
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
return methods;
|
|
1012
|
-
}
|
|
1013
|
-
/**
|
|
1014
|
-
* Check if a revocation index is revoked.
|
|
1015
|
-
* @param document The document to check.
|
|
1016
|
-
* @param revocationBitmapIndex The revocation index to check.
|
|
1017
|
-
* @returns True if the index is revoked.
|
|
1018
|
-
* @internal
|
|
1019
|
-
*/
|
|
1020
|
-
async checkRevocation(document, revocationBitmapIndex) {
|
|
1021
|
-
if (core.Is.stringValue(revocationBitmapIndex)) {
|
|
1022
|
-
const revocationIndex = core.Coerce.number(revocationBitmapIndex);
|
|
1023
|
-
if (core.Is.number(revocationIndex)) {
|
|
1024
|
-
const revocationService = document.service?.find(s => s.id.endsWith("#revocation"));
|
|
1025
|
-
if (revocationService &&
|
|
1026
|
-
core.Is.string(revocationService.serviceEndpoint) &&
|
|
1027
|
-
revocationService.type === "BitstringStatusList") {
|
|
1028
|
-
const revocationParts = revocationService.serviceEndpoint.split(",");
|
|
1029
|
-
if (revocationParts.length === 2) {
|
|
1030
|
-
const compressedRevocationBytes = core.Converter.base64UrlToBytes(revocationParts[1]);
|
|
1031
|
-
const decompressed = await core.Compression.decompress(compressedRevocationBytes, core.CompressionType.Gzip);
|
|
1032
|
-
const bitString = core.BitString.fromBits(decompressed, EntityStorageIdentityConnector._REVOCATION_BITS_SIZE);
|
|
1033
|
-
return bitString.getBit(revocationIndex);
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
1040
|
-
/**
|
|
1041
|
-
* Update the document in storage.
|
|
1042
|
-
* @param controller The controller of the document.
|
|
1043
|
-
* @param didDocument The did document to store.
|
|
1044
|
-
* @internal
|
|
1045
|
-
*/
|
|
1046
|
-
async updateDocument(controller, didDocument) {
|
|
1047
|
-
const stringifiedDocument = core.JsonHelper.canonicalize(didDocument);
|
|
1048
|
-
const docBytes = core.Converter.utf8ToBytes(stringifiedDocument);
|
|
1049
|
-
const signature = await this._vaultConnector.sign(EntityStorageIdentityConnector.buildVaultKey(didDocument.id, "did"), docBytes);
|
|
1050
|
-
await this._didDocumentEntityStorage.set({
|
|
1051
|
-
id: didDocument.id,
|
|
1052
|
-
document: didDocument,
|
|
1053
|
-
signature: core.Converter.bytesToBase64(signature),
|
|
1054
|
-
controller
|
|
1055
|
-
});
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1060
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1061
|
-
/**
|
|
1062
|
-
* Class which implements the identity profile connector contract.
|
|
1063
|
-
*/
|
|
1064
|
-
class EntityStorageIdentityProfileConnector {
|
|
1065
|
-
/**
|
|
1066
|
-
* The namespace supported by the identity profile connector.
|
|
1067
|
-
*/
|
|
1068
|
-
static NAMESPACE = "entity-storage";
|
|
1069
|
-
/**
|
|
1070
|
-
* Runtime name for the class.
|
|
1071
|
-
*/
|
|
1072
|
-
static CLASS_NAME = "EntityStorageIdentityProfileConnector";
|
|
1073
|
-
/**
|
|
1074
|
-
* The storage connector for the profiles.
|
|
1075
|
-
* @internal
|
|
1076
|
-
*/
|
|
1077
|
-
_profileEntityStorage;
|
|
1078
|
-
/**
|
|
1079
|
-
* Create a new instance of EntityStorageIdentityProfileConnector.
|
|
1080
|
-
* @param options The options for the identity service.
|
|
1081
|
-
*/
|
|
1082
|
-
constructor(options) {
|
|
1083
|
-
this._profileEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.profileEntityStorageType ?? "identity-profile");
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* Create the profile properties for an identity.
|
|
1087
|
-
* @param identity The identity of the profile to create.
|
|
1088
|
-
* @param publicProfile The public profile data.
|
|
1089
|
-
* @param privateProfile The private profile data.
|
|
1090
|
-
* @returns Nothing.
|
|
1091
|
-
*/
|
|
1092
|
-
async create(identity, publicProfile, privateProfile) {
|
|
1093
|
-
core.Guards.stringValue(EntityStorageIdentityProfileConnector.CLASS_NAME, "identity", identity);
|
|
1094
|
-
try {
|
|
1095
|
-
const profile = await this._profileEntityStorage.get(identity);
|
|
1096
|
-
if (!core.Is.empty(profile)) {
|
|
1097
|
-
throw new core.AlreadyExistsError(EntityStorageIdentityProfileConnector.CLASS_NAME, "alreadyExists", identity);
|
|
1098
|
-
}
|
|
1099
|
-
await this._profileEntityStorage.set({
|
|
1100
|
-
identity,
|
|
1101
|
-
publicProfile,
|
|
1102
|
-
privateProfile
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
catch (error) {
|
|
1106
|
-
if (core.BaseError.someErrorClass(error, EntityStorageIdentityProfileConnector.CLASS_NAME)) {
|
|
1107
|
-
throw error;
|
|
1108
|
-
}
|
|
1109
|
-
throw new core.GeneralError(EntityStorageIdentityProfileConnector.CLASS_NAME, "createFailed", { identity }, error);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Get the profile properties for an identity.
|
|
1114
|
-
* @param identity The identity of the item to get.
|
|
1115
|
-
* @param publicPropertyNames The public properties to get for the profile, defaults to all.
|
|
1116
|
-
* @param privatePropertyNames The private properties to get for the profile, defaults to all.
|
|
1117
|
-
* @returns The items properties.
|
|
1118
|
-
*/
|
|
1119
|
-
async get(identity, publicPropertyNames, privatePropertyNames) {
|
|
1120
|
-
try {
|
|
1121
|
-
const profile = await this._profileEntityStorage.get(identity);
|
|
1122
|
-
if (!profile) {
|
|
1123
|
-
throw new core.NotFoundError(EntityStorageIdentityProfileConnector.CLASS_NAME, "identityNotFound", identity);
|
|
1124
|
-
}
|
|
1125
|
-
return this.pickProperties(profile, publicPropertyNames, privatePropertyNames);
|
|
1126
|
-
}
|
|
1127
|
-
catch (error) {
|
|
1128
|
-
if (core.BaseError.someErrorClass(error, EntityStorageIdentityProfileConnector.CLASS_NAME)) {
|
|
1129
|
-
throw error;
|
|
1130
|
-
}
|
|
1131
|
-
throw new core.GeneralError(EntityStorageIdentityProfileConnector.CLASS_NAME, "getFailed", { identity }, error);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Update the profile properties of an identity.
|
|
1136
|
-
* @param identity The identity to update.
|
|
1137
|
-
* @param publicProfile The public profile data.
|
|
1138
|
-
* @param privateProfile The private profile data.
|
|
1139
|
-
* @returns Nothing.
|
|
1140
|
-
*/
|
|
1141
|
-
async update(identity, publicProfile, privateProfile) {
|
|
1142
|
-
core.Guards.stringValue(EntityStorageIdentityProfileConnector.CLASS_NAME, "identity", identity);
|
|
1143
|
-
try {
|
|
1144
|
-
const profile = await this._profileEntityStorage.get(identity);
|
|
1145
|
-
if (core.Is.empty(profile)) {
|
|
1146
|
-
throw new core.NotFoundError(EntityStorageIdentityProfileConnector.CLASS_NAME, "notFound", identity);
|
|
1147
|
-
}
|
|
1148
|
-
profile.publicProfile = publicProfile ?? profile.publicProfile;
|
|
1149
|
-
profile.privateProfile = privateProfile ?? profile.privateProfile;
|
|
1150
|
-
await this._profileEntityStorage.set(profile);
|
|
1151
|
-
}
|
|
1152
|
-
catch (error) {
|
|
1153
|
-
if (core.BaseError.someErrorClass(error, EntityStorageIdentityProfileConnector.CLASS_NAME)) {
|
|
1154
|
-
throw error;
|
|
1155
|
-
}
|
|
1156
|
-
throw new core.GeneralError(EntityStorageIdentityProfileConnector.CLASS_NAME, "updateFailed", { identity }, error);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Delete the profile for an identity.
|
|
1161
|
-
* @param identity The identity to delete.
|
|
1162
|
-
* @returns Nothing.
|
|
1163
|
-
*/
|
|
1164
|
-
async remove(identity) {
|
|
1165
|
-
core.Guards.stringValue(EntityStorageIdentityProfileConnector.CLASS_NAME, "identity", identity);
|
|
1166
|
-
try {
|
|
1167
|
-
const profile = await this._profileEntityStorage.get(identity);
|
|
1168
|
-
if (core.Is.empty(profile)) {
|
|
1169
|
-
throw new core.NotFoundError(EntityStorageIdentityProfileConnector.CLASS_NAME, "notFound", identity);
|
|
1170
|
-
}
|
|
1171
|
-
await this._profileEntityStorage.remove(identity);
|
|
1172
|
-
}
|
|
1173
|
-
catch (error) {
|
|
1174
|
-
if (core.BaseError.someErrorClass(error, EntityStorageIdentityProfileConnector.CLASS_NAME)) {
|
|
1175
|
-
throw error;
|
|
1176
|
-
}
|
|
1177
|
-
throw new core.GeneralError(EntityStorageIdentityProfileConnector.CLASS_NAME, "removeFailed", { identity }, error);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
/**
|
|
1181
|
-
* Get a list of the requested types.
|
|
1182
|
-
* @param publicFilters The filters to apply to the identities public profiles.
|
|
1183
|
-
* @param privateFilters The filters to apply to the identities private profiles.
|
|
1184
|
-
* @param publicPropertyNames The public properties to get for the profile, defaults to all.
|
|
1185
|
-
* @param privatePropertyNames The private properties to get for the profile, defaults to all.
|
|
1186
|
-
* @param cursor The cursor for paged requests.
|
|
1187
|
-
* @param limit The maximum number of items in a page.
|
|
1188
|
-
* @returns The list of items and cursor for paging.
|
|
1189
|
-
*/
|
|
1190
|
-
async list(publicFilters, privateFilters, publicPropertyNames, privatePropertyNames, cursor, limit) {
|
|
1191
|
-
try {
|
|
1192
|
-
const conditions = [];
|
|
1193
|
-
if (core.Is.arrayValue(publicFilters)) {
|
|
1194
|
-
for (const filter of publicFilters) {
|
|
1195
|
-
conditions.push({
|
|
1196
|
-
property: `publicProfile.${filter.propertyName}`,
|
|
1197
|
-
value: filter.propertyValue,
|
|
1198
|
-
comparison: entity.ComparisonOperator.Equals
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
if (core.Is.arrayValue(privateFilters)) {
|
|
1203
|
-
for (const filter of privateFilters) {
|
|
1204
|
-
conditions.push({
|
|
1205
|
-
property: `privateProfile.${filter.propertyName}`,
|
|
1206
|
-
value: filter.propertyValue,
|
|
1207
|
-
comparison: entity.ComparisonOperator.Equals
|
|
1208
|
-
});
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
const result = await this._profileEntityStorage.query(core.Is.arrayValue(conditions)
|
|
1212
|
-
? {
|
|
1213
|
-
conditions
|
|
1214
|
-
}
|
|
1215
|
-
: undefined, undefined, undefined, cursor, limit);
|
|
1216
|
-
const items = [];
|
|
1217
|
-
for (const resultEntity of result.entities) {
|
|
1218
|
-
items.push({
|
|
1219
|
-
identity: resultEntity.identity ?? "",
|
|
1220
|
-
...this.pickProperties(resultEntity, publicPropertyNames, privatePropertyNames)
|
|
1221
|
-
});
|
|
1222
|
-
}
|
|
1223
|
-
return {
|
|
1224
|
-
items,
|
|
1225
|
-
cursor: result.cursor
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
catch (error) {
|
|
1229
|
-
throw new core.GeneralError(EntityStorageIdentityProfileConnector.CLASS_NAME, "listFailed", undefined, error);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Get the profile properties for an identity.
|
|
1234
|
-
* @param profile The profile to pick the properties from.
|
|
1235
|
-
* @param publicPropertyNames The public properties to get for the profile, defaults to all.
|
|
1236
|
-
* @param privatePropertyNames The private properties to get for the profile, defaults to all.
|
|
1237
|
-
* @returns The identity profile, will only return private data if you have correct permissions.
|
|
1238
|
-
* @internal
|
|
1239
|
-
*/
|
|
1240
|
-
pickProperties(profile, publicPropertyNames, privatePropertyNames) {
|
|
1241
|
-
return {
|
|
1242
|
-
publicProfile: core.Is.array(publicPropertyNames)
|
|
1243
|
-
? core.ObjectHelper.pick(profile.publicProfile, publicPropertyNames)
|
|
1244
|
-
: profile.publicProfile,
|
|
1245
|
-
privateProfile: core.Is.array(privatePropertyNames)
|
|
1246
|
-
? core.ObjectHelper.pick(profile.privateProfile, privatePropertyNames)
|
|
1247
|
-
: profile.privateProfile
|
|
1248
|
-
};
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
|
-
|
|
1252
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1253
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1254
|
-
/**
|
|
1255
|
-
* Class for performing identity operations using entity storage.
|
|
1256
|
-
*/
|
|
1257
|
-
class EntityStorageIdentityResolverConnector {
|
|
1258
|
-
/**
|
|
1259
|
-
* The namespace supported by the identity connector.
|
|
1260
|
-
*/
|
|
1261
|
-
static NAMESPACE = "entity-storage";
|
|
1262
|
-
/**
|
|
1263
|
-
* Runtime name for the class.
|
|
1264
|
-
*/
|
|
1265
|
-
static CLASS_NAME = "EntityStorageIdentityResolverConnector";
|
|
1266
|
-
/**
|
|
1267
|
-
* The entity storage for identities.
|
|
1268
|
-
* @internal
|
|
1269
|
-
*/
|
|
1270
|
-
_didDocumentEntityStorage;
|
|
1271
|
-
/**
|
|
1272
|
-
* The vault for the keys.
|
|
1273
|
-
* @internal
|
|
1274
|
-
*/
|
|
1275
|
-
_vaultConnector;
|
|
1276
|
-
/**
|
|
1277
|
-
* Create a new instance of EntityStorageIdentityResolverConnector.
|
|
1278
|
-
* @param options The options for the identity connector.
|
|
1279
|
-
*/
|
|
1280
|
-
constructor(options) {
|
|
1281
|
-
this._didDocumentEntityStorage = entityStorageModels.EntityStorageConnectorFactory.get(options?.didDocumentEntityStorageType ?? "identity-document");
|
|
1282
|
-
this._vaultConnector = vaultModels.VaultConnectorFactory.get(options?.vaultConnectorType ?? "vault");
|
|
1283
|
-
}
|
|
1284
|
-
/**
|
|
1285
|
-
* Resolve a document from its id.
|
|
1286
|
-
* @param documentId The id of the document to resolve.
|
|
1287
|
-
* @returns The resolved document.
|
|
1288
|
-
* @throws NotFoundError if the id can not be resolved.
|
|
1289
|
-
*/
|
|
1290
|
-
async resolveDocument(documentId) {
|
|
1291
|
-
core.Guards.stringValue(EntityStorageIdentityResolverConnector.CLASS_NAME, "documentId", documentId);
|
|
1292
|
-
try {
|
|
1293
|
-
const didIdentityDocument = await this._didDocumentEntityStorage.get(documentId);
|
|
1294
|
-
if (core.Is.undefined(didIdentityDocument)) {
|
|
1295
|
-
throw new core.NotFoundError(EntityStorageIdentityResolverConnector.CLASS_NAME, "documentNotFound", documentId);
|
|
1296
|
-
}
|
|
1297
|
-
await EntityStorageIdentityConnector.verifyDocument(didIdentityDocument, this._vaultConnector);
|
|
1298
|
-
return didIdentityDocument.document;
|
|
1299
|
-
}
|
|
1300
|
-
catch (error) {
|
|
1301
|
-
throw new core.GeneralError(EntityStorageIdentityResolverConnector.CLASS_NAME, "resolveDocumentFailed", undefined, error);
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
// Copyright 2024 IOTA Stiftung.
|
|
1307
|
-
// SPDX-License-Identifier: Apache-2.0.
|
|
1308
|
-
/**
|
|
1309
|
-
* Initialize the schema for the identity entity storage connector.
|
|
1310
|
-
* @param options Options for which entities to register.
|
|
1311
|
-
* @param options.includeDocument Whether to include the document entity, defaults to true.
|
|
1312
|
-
* @param options.includeProfile Whether to include the profile entity, defaults to true.
|
|
1313
|
-
*/
|
|
1314
|
-
function initSchema(options) {
|
|
1315
|
-
if (options?.includeDocument ?? true) {
|
|
1316
|
-
entity.EntitySchemaFactory.register("IdentityDocument", () => entity.EntitySchemaHelper.getSchema(exports.IdentityDocument));
|
|
1317
|
-
}
|
|
1318
|
-
if (options?.includeProfile ?? true) {
|
|
1319
|
-
entity.EntitySchemaFactory.register("IdentityProfile", () => entity.EntitySchemaHelper.getSchema(exports.IdentityProfile));
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
exports.EntityStorageIdentityConnector = EntityStorageIdentityConnector;
|
|
1324
|
-
exports.EntityStorageIdentityProfileConnector = EntityStorageIdentityProfileConnector;
|
|
1325
|
-
exports.EntityStorageIdentityResolverConnector = EntityStorageIdentityResolverConnector;
|
|
1326
|
-
exports.initSchema = initSchema;
|