@icure/api 6.2.1 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,6 +45,10 @@ export declare class IccPatientXApi extends IccPatientApi {
45
45
  getPatient(patientId: string): never;
46
46
  getPatientRaw(patientId: string): Promise<models.Patient | any>;
47
47
  getPatientWithUser(user: models.User, patientId: string): Promise<models.Patient | any>;
48
+ getPotentiallyEncryptedPatientWithUser(user: models.User, patientId: string): Promise<{
49
+ patient: models.Patient;
50
+ decrypted: boolean;
51
+ }>;
48
52
  getPatients(body?: models.ListOfIds): never;
49
53
  getPatientsWithUser(user: models.User, body?: models.ListOfIds): Promise<Array<models.Patient> | any>;
50
54
  listDeletedPatients(startDate?: number, endDate?: number, desc?: boolean, startDocumentId?: string, limit?: number): never;
@@ -69,7 +73,9 @@ export declare class IccPatientXApi extends IccPatientApi {
69
73
  modifyPatientReferral(patientId: string, referralId: string, start?: number, end?: number): never;
70
74
  modifyPatientReferralWithUser(user: models.User, patientId: string, referralId: string, start?: number, end?: number): Promise<models.Patient | any>;
71
75
  encrypt(user: models.User, pats: Array<models.Patient>): Promise<Array<models.Patient>>;
76
+ private tryEncrypt;
72
77
  decrypt(user: models.User, patients: Array<models.Patient>, fillDelegations?: boolean): Promise<Array<models.Patient>>;
78
+ private tryDecryptOrReturnOriginal;
73
79
  /** By default, an encryptionKey will be added for every hcp in the autoDelegations of the provided user.
74
80
  * In optional field additionalDelegateIds, you can ask the method to create encryptionKeys for additional hcps */
75
81
  initEncryptionKeys(user: models.User, pat: models.Patient, additionalDelegateIds?: string[]): Promise<models.Patient>;
@@ -79,13 +79,23 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
79
79
  }
80
80
  initDelegationsAndEncryptionKeys(patient, user, secretForeignKey = undefined, delegates = []) {
81
81
  return __awaiter(this, void 0, void 0, function* () {
82
+ function updatePatientWithDataOwnerIfSame(dataOwner) {
83
+ if (dataOwner.id === patient.id) {
84
+ _.extend(patient, {
85
+ rev: dataOwner.rev,
86
+ publicKey: dataOwner.publicKey,
87
+ aesExchangeKeys: dataOwner.aesExchangeKeys,
88
+ hcPartyKeys: dataOwner.hcPartyKeys
89
+ });
90
+ }
91
+ }
82
92
  const dataOwnerId = this.dataOwnerApi.getDataOwnerOf(user);
83
93
  const dels = yield this.crypto.initObjectDelegations(patient, null, dataOwnerId, null);
94
+ updatePatientWithDataOwnerIfSame(dels.owner);
84
95
  const eks = yield this.crypto.initEncryptionKeys(patient, dataOwnerId);
85
- _.extend(patient, {
86
- delegations: dels.delegations,
87
- encryptionKeys: eks.encryptionKeys,
88
- });
96
+ updatePatientWithDataOwnerIfSame(eks.modifiedOwner);
97
+ patient.delegations = dels.delegations;
98
+ patient.encryptionKeys = eks.encryptionKeys;
89
99
  let promise = Promise.resolve(patient);
90
100
  _.uniq(delegates.concat(user.autoDelegations ? (user.autoDelegations.all || []).concat(user.autoDelegations.medicalInformation || []) : [])).forEach((delegateId) => (promise = promise.then((patient) => this.crypto.addDelegationsAndEncryptionKeys(null, patient, dataOwnerId, delegateId, dels.secretId, eks.secretId).catch((e) => {
91
101
  console.log(e);
@@ -183,7 +193,13 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
183
193
  getPatientWithUser(user, patientId) {
184
194
  return super
185
195
  .getPatient(patientId)
186
- .then((p) => this.decrypt(user, [p]))
196
+ .then((p) => this.tryDecryptOrReturnOriginal(user, [p], false))
197
+ .then((pats) => pats[0].patient);
198
+ }
199
+ getPotentiallyEncryptedPatientWithUser(user, patientId) {
200
+ return super
201
+ .getPatient(patientId)
202
+ .then((p) => this.tryDecryptOrReturnOriginal(user, [p], false))
187
203
  .then((pats) => pats[0]);
188
204
  }
189
205
  getPatients(body) {
@@ -261,7 +277,7 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
261
277
  }
262
278
  modifyPatientWithUser(user, body) {
263
279
  return body
264
- ? this.encrypt(user, [_.cloneDeep(this.completeNames(body))])
280
+ ? this.tryEncrypt(user, [_.cloneDeep(this.completeNames(body))], false)
265
281
  .then((pats) => super.modifyPatient(pats[0]))
266
282
  .then((p) => this.decrypt(user, [p]))
267
283
  .then((pats) => {
@@ -285,36 +301,43 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
285
301
  });
286
302
  }
287
303
  encrypt(user, pats) {
304
+ return this.tryEncrypt(user, pats, true);
305
+ }
306
+ tryEncrypt(user, pats, requireAccessibleKey) {
288
307
  const dataOwnerId = this.dataOwnerApi.getDataOwnerOf(user);
289
- const fixEncryptionKeys = (p) => {
290
- if (p.delegations && p.delegations[dataOwnerId])
291
- return this.initEncryptionKeys(user, p, Object.keys(p.delegations));
292
- else
293
- throw new Error(`Patient ${p.id} has no delegation or encryption key for hcp ${dataOwnerId}`);
294
- };
295
- return Promise.all(pats.map((p) => (p.encryptionKeys && Object.keys(p.encryptionKeys).some((k) => !!p.encryptionKeys[k].length)
296
- ? Promise.resolve(p)
297
- : this.initEncryptionKeys(user, p))
298
- .then((p) => __awaiter(this, void 0, void 0, function* () {
299
- try {
300
- const seks = yield this.crypto.extractKeysFromDelegationsForHcpHierarchy(dataOwnerId, p.id, p.encryptionKeys);
301
- return seks;
308
+ return Promise.all(pats.map((p) => __awaiter(this, void 0, void 0, function* () {
309
+ const patientWithInitialisedEks = (p.encryptionKeys && Object.values(p.encryptionKeys).some((v) => !!v.length)
310
+ ? p
311
+ : yield this.initEncryptionKeys(user, p));
312
+ const seks = yield this.crypto.extractKeysFromDelegationsForHcpHierarchy(dataOwnerId, patientWithInitialisedEks.id, patientWithInitialisedEks.encryptionKeys).catch((e) => { console.error(e); throw e; });
313
+ if (seks.extractedKeys.length) {
314
+ const key = yield this.crypto.AES.importKey('raw', (0, binary_utils_1.hex2ua)(seks.extractedKeys[0].replace(/-/g, '')));
315
+ return (0, utils_1.crypt)(patientWithInitialisedEks, (obj) => this.crypto.AES.encrypt(key, (0, binary_utils_1.utf8_2ua)(JSON.stringify(obj, (k, v) => {
316
+ return v instanceof ArrayBuffer || v instanceof Uint8Array
317
+ ? (0, binary_utils_1.b2a)(new Uint8Array(v).reduce((d, b) => d + String.fromCharCode(b), ''))
318
+ : v;
319
+ }))), this.encryptedKeys);
302
320
  }
303
- catch (e) {
304
- console.error(e);
305
- throw e;
321
+ else if (requireAccessibleKey) {
322
+ throw new Error(`Current data owner can't access any key of patient ${p.id}.`);
306
323
  }
307
- }))
308
- .then((seks) => this.crypto.AES.importKey('raw', (0, binary_utils_1.hex2ua)(seks.extractedKeys[0].replace(/-/g, ''))))
309
- .then((key) => (0, utils_1.crypt)(p, (obj) => this.crypto.AES.encrypt(key, (0, binary_utils_1.utf8_2ua)(JSON.stringify(obj, (k, v) => {
310
- return v instanceof ArrayBuffer || v instanceof Uint8Array
311
- ? (0, binary_utils_1.b2a)(new Uint8Array(v).reduce((d, b) => d + String.fromCharCode(b), ''))
312
- : v;
313
- }))), this.encryptedKeys))));
324
+ else {
325
+ const cryptedCopyWithRandomKey = yield (0, utils_1.crypt)(_.cloneDeep(patientWithInitialisedEks), (obj) => __awaiter(this, void 0, void 0, function* () { return Promise.resolve(new ArrayBuffer(1)); }), this.encryptedKeys);
326
+ if (!_.isEqual(_.omitBy(Object.assign(Object.assign({}, cryptedCopyWithRandomKey), { encryptedSelf: undefined }), _.isNil), _.omitBy(Object.assign(Object.assign({}, patientWithInitialisedEks), { encryptedSelf: undefined }), _.isNil))) {
327
+ throw Error("You can't modify encrypted data of a patient if you don't have access to his encryption key.");
328
+ }
329
+ return patientWithInitialisedEks;
330
+ }
331
+ })));
314
332
  }
333
+ // If patient can't be decrypted returns patient with encrypted data.
315
334
  decrypt(user, patients, fillDelegations = true) {
316
- const dataOwnerId = this.dataOwnerApi.getDataOwnerOf(user);
317
- return (user.healthcarePartyId ? this.hcpartyApi.getHealthcarePartyHierarchyIds(user.healthcarePartyId) : Promise.resolve([dataOwnerId])).then((ids) => __awaiter(this, void 0, void 0, function* () {
335
+ return this.tryDecryptOrReturnOriginal(user, patients, fillDelegations).then((ps) => ps.map((p) => p.patient));
336
+ }
337
+ tryDecryptOrReturnOriginal(user, patients, fillDelegations = true) {
338
+ return __awaiter(this, void 0, void 0, function* () {
339
+ const dataOwnerId = this.dataOwnerApi.getDataOwnerOf(user);
340
+ const ids = yield (user.healthcarePartyId ? this.hcpartyApi.getHealthcarePartyHierarchyIds(user.healthcarePartyId) : Promise.resolve([dataOwnerId]));
318
341
  //First check that we have no dangling delegation
319
342
  const patsWithMissingDelegations = patients.filter((p) => p.delegations &&
320
343
  ids.some((id) => p.delegations[id] && !p.delegations[id].length) &&
@@ -330,10 +353,9 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
330
353
  return Promise.all(patients
331
354
  .map((p) => acc[p.id] || p)
332
355
  .map((p) => __awaiter(this, void 0, void 0, function* () {
333
- var _a;
334
- return ((_a = (yield ids.reduce((decP, hcpId) => __awaiter(this, void 0, void 0, function* () {
335
- var _b;
336
- return ((_b = (yield decP)) !== null && _b !== void 0 ? _b : (p.encryptedSelf
356
+ const decryptedPatient = yield ids.reduce((decP, hcpId) => __awaiter(this, void 0, void 0, function* () {
357
+ var _a;
358
+ return ((_a = (yield decP)) !== null && _a !== void 0 ? _a : (p.encryptedSelf
337
359
  ? yield this.crypto
338
360
  .extractKeysFromDelegationsForHcpHierarchy(hcpId, p.id, _.size(p.encryptionKeys) ? p.encryptionKeys : p.delegations)
339
361
  .then(({ extractedKeys: sfks }) => {
@@ -362,9 +384,10 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
362
384
  }));
363
385
  })
364
386
  : p));
365
- }), Promise.resolve(undefined)))) !== null && _a !== void 0 ? _a : p);
387
+ }), Promise.resolve(undefined));
388
+ return decryptedPatient ? { patient: decryptedPatient, decrypted: true } : { patient: p, decrypted: false };
366
389
  })));
367
- }));
390
+ });
368
391
  }
369
392
  /** By default, an encryptionKey will be added for every hcp in the autoDelegations of the provided user.
370
393
  * In optional field additionalDelegateIds, you can ask the method to create encryptionKeys for additional hcps */
@@ -372,7 +395,7 @@ class IccPatientXApi extends icc_api_1.IccPatientApi {
372
395
  const dataOwnerId = this.dataOwnerApi.getDataOwnerOf(user);
373
396
  const userAutoDelegations = user.autoDelegations ? (user.autoDelegations.all || []).concat(user.autoDelegations.medicalInformation || []) : [];
374
397
  return this.crypto.initEncryptionKeys(pat, dataOwnerId).then((eks) => {
375
- let promise = Promise.resolve(_.extend(pat, {
398
+ let promise = Promise.resolve(_.extend(pat.id === eks.modifiedOwner.id ? eks.modifiedOwner : pat, {
376
399
  encryptionKeys: eks.encryptionKeys,
377
400
  }));
378
401
  new Set([...userAutoDelegations, ...(additionalDelegateIds || [])]).forEach((delegateId) => (promise = promise.then((patient) => this.crypto