oro-sdk 3.14.0 → 3.17.0

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.
@@ -1,21 +1,12 @@
1
- import { Grant, VaultIndex } from 'oro-sdk-apis';
1
+ import { Grant } from 'oro-sdk-apis';
2
2
  import { OroClient, Uuid } from '..';
3
3
  /**
4
4
  * @name filterGrantsWithLockboxMetadata
5
- * @description searches for the applied filters in the vault index
5
+ * @description searches for the existance of a consult uuid in each granted lockbox
6
6
  * @param oroClient
7
- * @param filter: the metadata filter applied to each the lockboxes
8
- * @param vaultIndex: the index to which the filter will be applied
9
- * @param forceRefresh
10
- * @returns the filtered grants
7
+ * @param filter: the consult uuid
8
+ * @returns the grants containing the consult uuid
11
9
  */
12
- export declare function filterGrantsWithLockboxMetadata(oroClient: OroClient, filter?: {
10
+ export declare function filterGrantsWithLockboxMetadata(oroClient: OroClient, filter: {
13
11
  consultationId: Uuid;
14
- }, vaultIndex?: VaultIndex, forceRefresh?: boolean): Promise<Grant[]>;
15
- /** Finds all grants for the logged user
16
- * requests a list of unique consultation ids for each lockbox the user has access to
17
- * builds and sets the index of consultations
18
- * @param oroClient
19
- * @returns the constructed vaultIndex
20
- */
21
- export declare function buildLegacyVaultIndex(oroClient: OroClient): Promise<VaultIndex>;
12
+ }): Promise<Grant[]>;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.14.0",
2
+ "version": "3.17.0",
3
3
  "main": "dist/index.js",
4
4
  "typings": "dist/index.d.ts",
5
5
  "files": [
@@ -54,7 +54,7 @@
54
54
  "form-data": "^4.0.0",
55
55
  "formdata-node": "^4.3.1",
56
56
  "idb-keyval": "^5.0.6",
57
- "oro-sdk-apis": "1.49.0",
57
+ "oro-sdk-apis": "1.51.0",
58
58
  "oro-toolbox": "0.0.6",
59
59
  "uuid": "^8.3.2"
60
60
  }
package/src/client.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ AllRoleType,
2
3
  AuthTokenRequest,
3
4
  Consult,
4
5
  ConsultRequest,
@@ -23,12 +24,15 @@ import {
23
24
  Meta,
24
25
  Metadata,
25
26
  MetadataCategory,
27
+ OtherRoleType,
26
28
  PersonalMeta,
27
29
  PopulatedWorkflowData,
28
30
  Practice,
29
31
  PracticeService,
32
+ PractitionnerRoleType,
30
33
  PreferenceMeta,
31
34
  RecoveryMeta,
35
+ RoleBasedScopes,
32
36
  SearchService,
33
37
  SecretShard,
34
38
  TellerService,
@@ -48,13 +52,14 @@ import {
48
52
  IncompleteAuthentication,
49
53
  LocalEncryptedData,
50
54
  MissingGrant,
55
+ MissingGrantFilter,
51
56
  MissingLockbox,
52
57
  MissingLockboxOwner,
53
58
  RecoveryData,
54
59
  RegisterPatientOutput,
55
60
  UserPreference,
56
61
  } from './models'
57
- import { buildLegacyVaultIndex, filterGrantsWithLockboxMetadata } from './sdk-revision'
62
+ import { filterGrantsWithLockboxMetadata } from './sdk-revision'
58
63
 
59
64
  export class OroClient {
60
65
  private rsa?: CryptoRSA
@@ -83,13 +88,12 @@ export class OroClient {
83
88
  public workflowClient: WorkflowService,
84
89
  public diagnosisClient: DiagnosisService,
85
90
  private authenticationCallback?: (err: Error) => void
86
- ) {}
91
+ ) { }
87
92
 
88
93
  /**
89
94
  * clears the vaultIndex and cached metadata grants
90
95
  */
91
96
  public async cleanIndex() {
92
- this.vaultIndex = undefined
93
97
  this.cachedMetadataGrants = {}
94
98
  this.cachedManifest = {}
95
99
  }
@@ -297,28 +301,6 @@ export class OroClient {
297
301
  return registerPatient(patientUuid, consult, workflow, this, this.toolbox.uuid(), recoveryQA, indexSearch)
298
302
  }
299
303
 
300
- /**
301
- * Builds the vault index for the logged user
302
- *
303
- * Steps:
304
- * 1. Retrieves, decrypts and sets the lockbox IndexSnapshot
305
- * 2. Retrieves, decrypts and adds all other index entries starting at the snapshot timestamp
306
- * 3. Updates the IndexSnapshot if changed
307
- * @deprecated
308
- * @returns the latest vault index
309
- */
310
- public async buildVaultIndex(forceRefresh: boolean = false) {
311
- if (!this.vaultIndex || forceRefresh) await buildLegacyVaultIndex(this)
312
- }
313
-
314
- /**
315
- * Setter for the vault index
316
- * @param index
317
- */
318
- public setVaultIndex(index: VaultIndex) {
319
- this.vaultIndex = index
320
- }
321
-
322
304
  /**
323
305
  * Fetches all grants, and consultations that exist in each lockbox
324
306
  * Then updates the index for the current user with the lockbox consult relationship
@@ -393,91 +375,25 @@ export class OroClient {
393
375
  }))
394
376
  .map(
395
377
  (e: IndexConsultLockbox) =>
396
- ({
397
- uuid: e.uuid,
398
- timestamp: e.timestamp,
399
- uniqueHash: e.uniqueHash,
400
- encryptedIndexEntry: CryptoRSA.jsonWithPubEncryptToBase64(
401
- {
402
- consultationId: e.consultationId,
403
- grant: e.grant,
404
- },
405
- rsaPub
406
- ),
407
- } as EncryptedIndexEntry)
378
+ ({
379
+ uuid: e.uuid,
380
+ timestamp: e.timestamp,
381
+ uniqueHash: e.uniqueHash,
382
+ encryptedIndexEntry: CryptoRSA.jsonWithPubEncryptToBase64(
383
+ {
384
+ consultationId: e.consultationId,
385
+ grant: e.grant,
386
+ },
387
+ rsaPub
388
+ ),
389
+ } as EncryptedIndexEntry)
408
390
  )
409
391
  break
410
- //// DEPRECATED : REMOVE ME : BEGIN ///////////////////////////////////////////
411
- case IndexKey.Consultation:
412
- encryptedIndex[key] = (entries[key] as IndexConsultLockbox[])
413
- .map((e) => ({
414
- ...e,
415
- uniqueHash: this.toolbox.hashStringToBase64(
416
- JSON.stringify({
417
- consultationId: e.consultationId,
418
- grant: e.grant,
419
- })
420
- ),
421
- }))
422
- .filter(
423
- (e) =>
424
- !this.vaultIndex ||
425
- !this.vaultIndex[IndexKey.Consultation]?.find((v) => v.uniqueHash === e.uniqueHash)
426
- )
427
- .map(
428
- (e: IndexConsultLockbox) =>
429
- ({
430
- uuid: e.uuid,
431
- timestamp: e.timestamp,
432
- uniqueHash: e.uniqueHash,
433
- encryptedIndexEntry: CryptoRSA.jsonWithPubEncryptToBase64(
434
- {
435
- consultationId: e.consultationId,
436
- grant: e.grant,
437
- },
438
- rsaPub
439
- ),
440
- } as EncryptedIndexEntry)
441
- )
442
- break
443
- //// DEPRECATED : REMOVE ME : END ///////////////////////////////////////////
444
392
  }
445
393
  }
446
394
  await this.vaultClient.vaultIndexPut(encryptedIndex, indexOwnerUuid)
447
395
  }
448
396
 
449
- /**
450
- * adds or updates the index snapshot for the logged user
451
- * @param index
452
- */
453
- public async indexSnapshotAdd(index: VaultIndex) {
454
- if (!this.rsa) throw IncompleteAuthentication
455
- let rsaPub: Uint8Array = this.rsa.public()
456
-
457
- let cleanedIndex: VaultIndex = {
458
- [IndexKey.Consultation]: index[IndexKey.Consultation]
459
- ?.filter((c) => c)
460
- .map((c) => {
461
- return {
462
- grant: c.grant,
463
- consultationId: c.consultationId,
464
- }
465
- }),
466
- }
467
-
468
- // the data of the snapshot should not contain the `IndexEntry` data
469
- // (will create conflicts while updating)
470
- let encryptedIndexEntry = CryptoRSA.jsonWithPubEncryptToBase64(cleanedIndex, rsaPub)
471
-
472
- // The encryptedIndexEntry can have the uuid and timstamp (for updating)
473
- let encryptedIndex: EncryptedIndexEntry = {
474
- uuid: index.uuid,
475
- timestamp: index.timestamp,
476
- encryptedIndexEntry,
477
- }
478
- this.vaultClient.vaultIndexSnapshotPut(encryptedIndex)
479
- }
480
-
481
397
  /**
482
398
  * @name grantLockbox
483
399
  * @description Grants a lockbox by retrieving the shared secret of the lockbox and encrypting it with the grantees public key
@@ -777,44 +693,60 @@ export class OroClient {
777
693
  * @param filter: the consultationId in which the grant exists
778
694
  * @returns decrypted lockboxes granted to user
779
695
  */
780
- public async getGrants(filter?: { consultationId: Uuid }, forceRefresh: boolean = false): Promise<Grant[]> {
696
+ public async getGrants(filter?: { consultationId: Uuid }): Promise<Grant[]> {
781
697
  if (!this.rsa) throw IncompleteAuthentication
782
698
 
783
699
  let filterString = JSON.stringify(filter)
784
700
  // retrieves cached grants
785
- // Note: if filters is set to empty, it will be stored in the `undefined` key
786
- if (!forceRefresh && this.cachedMetadataGrants[filterString]) return this.cachedMetadataGrants[filterString]
787
-
788
- // if there is a filter to apply, then the grant can be retrieved from the vault index, otherwise, all grants are fetched
701
+ if (this.cachedMetadataGrants[filterString]) return this.cachedMetadataGrants[filterString]
702
+
703
+ // We're using the account role to determine the way a grant is accessed
704
+ let currentAccountRole = await this.getAccountRole()
705
+
706
+ if ([OtherRoleType.Patient, OtherRoleType.User].every(requiredRole => currentAccountRole.includes(requiredRole))) {
707
+ let encryptedGrants
708
+ // if there are no grants with the applied filter from index, attempt for naive filter with backwards compatibility
709
+ if (filter) {
710
+ encryptedGrants = await filterGrantsWithLockboxMetadata(this, filter)
711
+ } else {
712
+ encryptedGrants = (await this.vaultClient.grantsGet()).grants
713
+ }
714
+ const decryptedGrants = await decryptGrants(encryptedGrants, this.rsa)
715
+ // sets the cached grant
716
+ this.cachedMetadataGrants[filterString] = decryptedGrants
717
+ console.info('[sdk:grant] Found grant for patient')
718
+ return decryptedGrants
719
+ }
720
+ // if not a patient, then a practitioner is trying to retrieve a grant, it **Must** contain a filter, otherwise too many grants are possible
721
+ if (!filter)
722
+ throw MissingGrantFilter
789
723
  // Note: will work only if the filter being applied is exclusively a consult id
790
- const grantsByConsultLockbox = filter
791
- ? await this.vaultClient
792
- .vaultIndexGet([IndexKey.ConsultationLockbox], [filter.consultationId])
793
- .then((res) => res[IndexKey.ConsultationLockbox])
794
- .catch((e) => {
795
- console.error(e)
796
- return []
797
- })
798
- : undefined
724
+ const grantsByConsultLockbox = await this.vaultClient
725
+ .vaultIndexGet([IndexKey.ConsultationLockbox], [filter.consultationId])
726
+ .then((res) => res[IndexKey.ConsultationLockbox])
727
+ .catch((e) => {
728
+ console.error(e)
729
+ return []
730
+ })
731
+
799
732
  const decryptedConsults = decryptConsultLockboxGrants(grantsByConsultLockbox ?? [], this.rsa)
800
733
  if (decryptedConsults.length > 0) {
801
734
  console.info('[sdk:index] Grants found in user`s constant time secure index')
802
- this.cachedMetadataGrants[JSON.stringify(filter)] = decryptedConsults
735
+ this.cachedMetadataGrants[filterString] = decryptedConsults
803
736
  return this.cachedMetadataGrants[filterString]
804
737
  }
805
738
 
806
- let encryptedGrants
807
- // if there are no grants with the applied filter from index, attempt for naive filter with backwards compatibility
808
- if (filter) {
809
- encryptedGrants = await filterGrantsWithLockboxMetadata(this, filter, this.vaultIndex, forceRefresh)
810
- } else {
811
- encryptedGrants = (await this.vaultClient.grantsGet()).grants
812
- }
739
+ // if we have no valid grants, then return nothing
740
+ return []
741
+ }
813
742
 
814
- const decryptedGrants = await decryptGrants(encryptedGrants, this.rsa)
815
- // sets the cached grant
816
- this.cachedMetadataGrants[filterString] = decryptedGrants
817
- return decryptedGrants
743
+ /**
744
+ * Fetches the role of the account that is logged in
745
+ *
746
+ * @returns the role based scopes defined by the whoami
747
+ */
748
+ async getAccountRole(): Promise<RoleBasedScopes[]> {
749
+ return (await this.guardClient.whoAmI()).scope.split(' ') as RoleBasedScopes[]
818
750
  }
819
751
 
820
752
  /**
@@ -1206,9 +1138,9 @@ export class OroClient {
1206
1138
  * @param practiceUuid the uuid of the practice to look consult into
1207
1139
  * @returns the list of consults
1208
1140
  */
1209
- public async getAssignedConsultations(practiceUuid: Uuid, forceRefresh: boolean = false): Promise<Consult[]> {
1141
+ public async getAssignedConsultations(practiceUuid: Uuid): Promise<Consult[]> {
1210
1142
  return Promise.all(
1211
- (await this.getGrants(undefined, forceRefresh)).map((grant) =>
1143
+ (await this.getGrants()).map((grant) =>
1212
1144
  this.getLockboxManifest(
1213
1145
  grant.lockboxUuid!,
1214
1146
  {
@@ -1216,8 +1148,7 @@ export class OroClient {
1216
1148
  documentType: DocumentType.PopulatedWorkflowData,
1217
1149
  },
1218
1150
  true,
1219
- undefined,
1220
- forceRefresh
1151
+ undefined
1221
1152
  ).then((manifest) =>
1222
1153
  Promise.all(
1223
1154
  manifest.map(
@@ -1278,7 +1209,7 @@ export class OroClient {
1278
1209
  ): Promise<PopulatedWorkflowData[]> {
1279
1210
  //TODO: make use of getPatientDocumentsList instead of doing it manually here
1280
1211
  return Promise.all(
1281
- (await this.getGrants({ consultationId }, forceRefresh))
1212
+ (await this.getGrants({ consultationId }))
1282
1213
  .map((grant) =>
1283
1214
  this.getLockboxManifest(
1284
1215
  grant.lockboxUuid!,
@@ -194,7 +194,7 @@ export async function registerPatient(
194
194
  await Promise.all([...grantPromises, ...consultIndexPromises])
195
195
 
196
196
 
197
- if(indexSearch) {
197
+ if (indexSearch) {
198
198
  await buildConsultSearchIndex(consult, workflow, oroClient).catch((err) => {
199
199
  console.error(
200
200
  '[SDK: registration] personal information not found or another error occured during search indexing',
@@ -265,7 +265,7 @@ async function getOrCreatePatientConsultationUuid(consult: ConsultRequest, oroCl
265
265
  * @returns the lockbox Uuid
266
266
  */
267
267
  async function getOrCreatePatientLockbox(oroClient: OroClient): Promise<Uuid> {
268
- let grants = await oroClient.getGrants(undefined, true)
268
+ let grants = await oroClient.getGrants()
269
269
  if (grants.length > 0) {
270
270
  console.log('The grant has already been created, skipping lockbox create step')
271
271
  return grants[0].lockboxUuid!
@@ -1,6 +1,7 @@
1
- export class IncompleteAuthentication extends Error {}
2
- export class MissingGrant extends Error {}
3
- export class MissingLockbox extends Error {}
4
- export class MissingLockboxOwner extends Error {}
5
- export class AssociatedLockboxNotFound extends Error {}
6
- export class WorkflowAnswersMissingError extends Error {}
1
+ export class IncompleteAuthentication extends Error { }
2
+ export class MissingGrant extends Error { }
3
+ export class MissingGrantFilter extends Error { }
4
+ export class MissingLockbox extends Error { }
5
+ export class MissingLockboxOwner extends Error { }
6
+ export class AssociatedLockboxNotFound extends Error { }
7
+ export class WorkflowAnswersMissingError extends Error { }
@@ -3,65 +3,27 @@ import { OroClient, Uuid } from '..'
3
3
 
4
4
  /**
5
5
  * @name filterGrantsWithLockboxMetadata
6
- * @description searches for the applied filters in the vault index
6
+ * @description searches for the existance of a consult uuid in each granted lockbox
7
7
  * @param oroClient
8
- * @param filter: the metadata filter applied to each the lockboxes
9
- * @param vaultIndex: the index to which the filter will be applied
10
- * @param forceRefresh
11
- * @returns the filtered grants
8
+ * @param filter: the consult uuid
9
+ * @returns the grants containing the consult uuid
12
10
  */
13
11
  export async function filterGrantsWithLockboxMetadata(
14
12
  oroClient: OroClient,
15
- filter?: { consultationId: Uuid },
16
- vaultIndex?: VaultIndex,
17
- forceRefresh = false
13
+ filter: { consultationId: Uuid },
18
14
  ): Promise<Grant[]> {
19
- if (!vaultIndex || forceRefresh) {
20
- vaultIndex = await buildLegacyVaultIndex(oroClient)
21
- }
22
- if (vaultIndex[IndexKey.Consultation] && filter) {
23
- let indexConsults = (vaultIndex[IndexKey.Consultation] ?? [])
24
- .filter((consultGrant: { consultationId: Uuid }) => consultGrant.consultationId === filter.consultationId)
25
- .map((consultGrant: { consultationId: Uuid; grant: Grant }) => consultGrant.grant as Grant)
26
- return indexConsults as Grant[]
27
- } else {
28
- // No grants exist and the index has already been built
29
- return []
30
- }
31
- }
32
-
33
- /** Finds all grants for the logged user
34
- * requests a list of unique consultation ids for each lockbox the user has access to
35
- * builds and sets the index of consultations
36
- * @param oroClient
37
- * @returns the constructed vaultIndex
38
- */
39
- export async function buildLegacyVaultIndex(oroClient: OroClient): Promise<VaultIndex> {
40
15
  let grants = await oroClient.getGrants()
41
- let consultGrants: IndexConsultLockbox[] = []
16
+ let filteredGrants = []
42
17
  for (let grant of grants) {
43
- let consults = (
44
- await oroClient.vaultClient.lockboxMetadataGet(grant.lockboxUuid!, ['consultationId'], [], {
45
- category: MetadataCategory.Consultation,
46
- })
47
- )[0] as Uuid[]
48
-
49
- consultGrants = [
50
- ...consultGrants,
51
- ...consults.map((consult: any) => ({
52
- ...consult,
53
- grant: {
54
- lockboxOwnerUuid: grant.lockboxOwnerUuid,
55
- lockboxUuid: grant.lockboxUuid,
56
- },
57
- })),
58
- ]
18
+ // Fetches in each lockbox the existance of a given consult id
19
+ let consultationIdExistsInMetadata = await oroClient.vaultClient.lockboxMetadataGet(grant.lockboxUuid!, ['consultationId'], [], {
20
+ category: MetadataCategory.Consultation,
21
+ consultationId: filter.consultationId
22
+ })
23
+ // If there are entries in the metadata, it means that the consult exists in the lockbox
24
+ if (consultationIdExistsInMetadata[0].length >= 0)
25
+ filteredGrants.push(grant)
59
26
  }
60
27
 
61
- let vaultIndex = {
62
- [IndexKey.Consultation]: consultGrants,
63
- }
64
- oroClient.setVaultIndex(vaultIndex)
65
- console.info('[sdk:index] Successfully Built Vault Index')
66
- return vaultIndex
28
+ return filteredGrants
67
29
  }