diff --git a/package.json b/package.json index caa90f8..ac7e9b8 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "axios": "^0.21.1", - "fhir-works-on-aws-interface": "^7.0.0", + "fhir-works-on-aws-interface": "^7.0.1", "jsonwebtoken": "^8.5.1", "jwks-rsa": "^1.12.1" }, diff --git a/src/smartHandler.test.ts b/src/smartHandler.test.ts index c0d41e3..86e43fc 100644 --- a/src/smartHandler.test.ts +++ b/src/smartHandler.test.ts @@ -51,8 +51,9 @@ const baseAuthZConfig = (): SMARTConfig => ({ jwksEndpoint: `${expectedIss}/jwks`, }); const apiUrl = 'https://fhir.server.com/dev'; -const patientId = 'Patient/1234'; -const practitionerId = 'Practitioner/1234'; +const id = 'id'; +const patientId = `Patient/${id}`; +const practitionerId = `Practitioner/${id}`; const patientIdentity = `${apiUrl}/${patientId}`; const practitionerIdentity = `${apiUrl}/${practitionerId}`; const externalPractitionerIdentity = `${apiUrl}/test/${practitionerId}`; @@ -85,7 +86,7 @@ const baseAccessNoScopes: any = { const validPatient = { resourceType: 'Patient', - id: '1234', + id, meta: { versionId: '1', lastUpdated: '2020-06-28T12:03:29.421+00:00', @@ -1062,6 +1063,7 @@ describe('getSearchFilterBasedOnIdentity', () => { const request: GetSearchFilterBasedOnIdentityRequest = { userIdentity, operation: 'search-type', + resourceType: 'Encounter', }; // OPERATE, CHECK @@ -1084,7 +1086,7 @@ describe('getSearchFilterBasedOnIdentity', () => { }; const request: GetSearchFilterBasedOnIdentityRequest = { userIdentity, - operation: 'search-type', + operation: 'search-system', }; // OPERATE, CHECK @@ -1115,7 +1117,9 @@ describe('getSearchFilterBasedOnIdentity', () => { }; const request: GetSearchFilterBasedOnIdentityRequest = { userIdentity, - operation: 'search-type', + operation: 'history-instance', + resourceType: 'Patient', + id: '1324', }; // OPERATE, CHECK @@ -1124,7 +1128,13 @@ describe('getSearchFilterBasedOnIdentity', () => { key: '_references', logicalOperator: 'OR', comparisonOperator: '==', - value: [patientIdentity, patientIdentity], + value: [patientIdentity], + }, + { + key: 'id', + logicalOperator: 'OR', + comparisonOperator: '==', + value: [id], }, ]; await expect(authZHandlerWithFakeApiUrl.getSearchFilterBasedOnIdentity(request)).resolves.toEqual( @@ -1141,11 +1151,35 @@ describe('getSearchFilterBasedOnIdentity', () => { }; const request: GetSearchFilterBasedOnIdentityRequest = { userIdentity, - operation: 'search-type', + operation: 'search-system', }; // OPERATE, CHECK const expectedFilter: [] = []; await expect(authZHandler.getSearchFilterBasedOnIdentity(request)).resolves.toEqual(expectedFilter); }); + test('External Practitioner identity', async () => { + // BUILD + const userIdentity = { + ...baseAccessNoScopes, + scopes: ['user/*.*', 'fhirUser'], + fhirUserObject: externalPractitionerFhirResource, + }; + const request: GetSearchFilterBasedOnIdentityRequest = { + userIdentity, + operation: 'search-type', + resourceType: 'Patient', + }; + + // OPERATE, CHECK + const expectedFilter = [ + { + key: '_references', + logicalOperator: 'OR', + comparisonOperator: '==', + value: [externalPractitionerIdentity], + }, + ]; + await expect(authZHandler.getSearchFilterBasedOnIdentity(request)).resolves.toEqual(expectedFilter); + }); }); diff --git a/src/smartHandler.ts b/src/smartHandler.ts index 52ffc7d..dc41fa8 100644 --- a/src/smartHandler.ts +++ b/src/smartHandler.ts @@ -121,37 +121,55 @@ export class SMARTHandler implements Authorization { } async getSearchFilterBasedOnIdentity(request: GetSearchFilterBasedOnIdentityRequest): Promise { - const values: string[] = []; + const references: Set = new Set(); + const ids: Set = new Set(); const { fhirUserObject, patientLaunchContext } = request.userIdentity; if (fhirUserObject) { const { hostname, resourceType, id } = fhirUserObject; - if (resourceType === 'Practitioner') { + if (hostname === this.apiUrl && resourceType === 'Practitioner') { return []; } - values.push(`${hostname}/${resourceType}/${id}`); + references.add(`${hostname}/${resourceType}/${id}`); if (hostname === this.apiUrl) { - values.push(`${resourceType}/${id}`); + references.add(`${resourceType}/${id}`); + } + if (request.resourceType && request.resourceType === resourceType) { + ids.add(id); } } if (patientLaunchContext) { const { hostname, resourceType, id } = patientLaunchContext; - values.push(`${hostname}/${resourceType}/${id}`); + references.add(`${hostname}/${resourceType}/${id}`); if (hostname === this.apiUrl) { - values.push(`${resourceType}/${id}`); + references.add(`${resourceType}/${id}`); + } + if (request.resourceType && request.resourceType === resourceType) { + ids.add(id); } } // Create a SearchFilter to limit access to only resources that are referring to the requesting user and/or context - return [ - { + const filters: SearchFilter[] = []; + if (references.size > 0) { + filters.push({ key: '_references', - value: values, + value: [...references], + comparisonOperator: '==', + logicalOperator: 'OR', + }); + } + if (ids.size > 0) { + filters.push({ + key: 'id', + value: [...ids], comparisonOperator: '==', - logicalOperator: 'OR', // logicalOperator can be either 'AND' or 'OR' since value is an array of one string - }, - ]; + logicalOperator: 'OR', + }); + } + + return filters; } async isBundleRequestAuthorized(request: AuthorizationBundleRequest): Promise { diff --git a/yarn.lock b/yarn.lock index 4bb7ca2..c29640d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -699,12 +699,12 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^4.11.1": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.0.tgz#92db8e7c357ed7d69632d6843ca70b71be3a721d" - integrity sha512-IJ5e2W7uFNfg4qh9eHkHRUCbgZ8VKtGwD07kannJvM5t/GU8P8+24NX8gi3Hf5jST5oWPY8kyV1s/WtfiZ4+Ww== + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.1.tgz#22dd301ce228aaab3416b14ead10b1db3e7d3180" + integrity sha512-5JriGbYhtqMS1kRcZTQxndz1lKMwwEXKbwZbkUZNnp6MJX0+OVXnG0kOlBZP4LUAxEyzu3cs+EXd/97MJXsGfw== dependencies: - "@typescript-eslint/experimental-utils" "4.14.0" - "@typescript-eslint/scope-manager" "4.14.0" + "@typescript-eslint/experimental-utils" "4.14.1" + "@typescript-eslint/scope-manager" "4.14.1" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -712,48 +712,48 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.0.tgz#5aa7b006736634f588a69ee343ca959cd09988df" - integrity sha512-6i6eAoiPlXMKRbXzvoQD5Yn9L7k9ezzGRvzC/x1V3650rUk3c3AOjQyGYyF9BDxQQDK2ElmKOZRD0CbtdkMzQQ== +"@typescript-eslint/experimental-utils@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.1.tgz#a5c945cb24dabb96747180e1cfc8487f8066f471" + integrity sha512-2CuHWOJwvpw0LofbyG5gvYjEyoJeSvVH2PnfUQSn0KQr4v8Dql2pr43ohmx4fdPQ/eVoTSFjTi/bsGEXl/zUUQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.14.0" - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/typescript-estree" "4.14.0" + "@typescript-eslint/scope-manager" "4.14.1" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/typescript-estree" "4.14.1" eslint-scope "^5.0.0" eslint-utils "^2.0.0" "@typescript-eslint/parser@^4.11.1": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.14.0.tgz#62d4cd2079d5c06683e9bfb200c758f292c4dee7" - integrity sha512-sUDeuCjBU+ZF3Lzw0hphTyScmDDJ5QVkyE21pRoBo8iDl7WBtVFS+WDN3blY1CH3SBt7EmYCw6wfmJjF0l/uYg== + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.14.1.tgz#3bd6c24710cd557d8446625284bcc9c6d52817c6" + integrity sha512-mL3+gU18g9JPsHZuKMZ8Z0Ss9YP1S5xYZ7n68Z98GnPq02pYNQuRXL85b9GYhl6jpdvUc45Km7hAl71vybjUmw== dependencies: - "@typescript-eslint/scope-manager" "4.14.0" - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/typescript-estree" "4.14.0" + "@typescript-eslint/scope-manager" "4.14.1" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/typescript-estree" "4.14.1" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.14.0.tgz#55a4743095d684e1f7b7180c4bac2a0a3727f517" - integrity sha512-/J+LlRMdbPh4RdL4hfP1eCwHN5bAhFAGOTsvE6SxsrM/47XQiPSgF5MDgLyp/i9kbZV9Lx80DW0OpPkzL+uf8Q== +"@typescript-eslint/scope-manager@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.14.1.tgz#8444534254c6f370e9aa974f035ced7fe713ce02" + integrity sha512-F4bjJcSqXqHnC9JGUlnqSa3fC2YH5zTtmACS1Hk+WX/nFB0guuynVK5ev35D4XZbdKjulXBAQMyRr216kmxghw== dependencies: - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/visitor-keys" "4.14.0" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/visitor-keys" "4.14.1" -"@typescript-eslint/types@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.14.0.tgz#d8a8202d9b58831d6fd9cee2ba12f8a5a5dd44b6" - integrity sha512-VsQE4VvpldHrTFuVPY1ZnHn/Txw6cZGjL48e+iBxTi2ksa9DmebKjAeFmTVAYoSkTk7gjA7UqJ7pIsyifTsI4A== +"@typescript-eslint/types@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.14.1.tgz#b3d2eb91dafd0fd8b3fce7c61512ac66bd0364aa" + integrity sha512-SkhzHdI/AllAgQSxXM89XwS1Tkic7csPdndUuTKabEwRcEfR8uQ/iPA3Dgio1rqsV3jtqZhY0QQni8rLswJM2w== -"@typescript-eslint/typescript-estree@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.0.tgz#4bcd67486e9acafc3d0c982b23a9ab8ac8911ed7" - integrity sha512-wRjZ5qLao+bvS2F7pX4qi2oLcOONIB+ru8RGBieDptq/SudYwshveORwCVU4/yMAd4GK7Fsf8Uq1tjV838erag== +"@typescript-eslint/typescript-estree@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.1.tgz#20d3b8c8e3cdc8f764bdd5e5b0606dd83da6075b" + integrity sha512-M8+7MbzKC1PvJIA8kR2sSBnex8bsR5auatLCnVlNTJczmJgqRn8M+sAlQfkEq7M4IY3WmaNJ+LJjPVRrREVSHQ== dependencies: - "@typescript-eslint/types" "4.14.0" - "@typescript-eslint/visitor-keys" "4.14.0" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/visitor-keys" "4.14.1" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" @@ -761,12 +761,12 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.14.0": - version "4.14.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.0.tgz#b1090d9d2955b044b2ea2904a22496849acbdf54" - integrity sha512-MeHHzUyRI50DuiPgV9+LxcM52FCJFYjJiWHtXlbyC27b80mfOwKeiKI+MHOTEpcpfmoPFm/vvQS88bYIx6PZTA== +"@typescript-eslint/visitor-keys@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.1.tgz#e93c2ff27f47ee477a929b970ca89d60a117da91" + integrity sha512-TAblbDXOI7bd0C/9PE1G+AFo7R5uc+ty1ArDoxmrC1ah61Hn6shURKy7gLdRb1qKJmjHkqu5Oq+e4Kt0jwf1IA== dependencies: - "@typescript-eslint/types" "4.14.0" + "@typescript-eslint/types" "4.14.1" eslint-visitor-keys "^2.0.0" abab@^2.0.3: @@ -1918,10 +1918,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fhir-works-on-aws-interface@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/fhir-works-on-aws-interface/-/fhir-works-on-aws-interface-7.0.0.tgz#2cd4ffb20b42dc3d4ec9a98d90437052841ec6d6" - integrity sha512-drZNzZ1zdRgGxOQFdGrEAHJwt77MnyrUw6jQCO9yoX7WBhdLOP0+K1E/4GTnn+AulB7/IJa9bPAKHm5PpybB7Q== +fhir-works-on-aws-interface@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fhir-works-on-aws-interface/-/fhir-works-on-aws-interface-7.0.1.tgz#f5fd41ae218d77c6bdc54452c3102a4990c5e4bc" + integrity sha512-Ax3yBruz5eeVaBxtEuLQQYdqEIDfqK6nfAT2iWUG7Ky9Warja/3Y4tAIYdiBhYaQ4anFQgz9nyYFQTQUH0JyLQ== figures@^3.0.0: version "3.2.0" @@ -1984,9 +1984,9 @@ flatted@^2.0.0: integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== follow-redirects@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== + version "1.13.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" + integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== for-in@^1.0.2: version "1.0.2" @@ -2045,9 +2045,9 @@ get-caller-file@^2.0.1: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.1, get-intrinsic@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" - integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg== + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.0.tgz#892e62931e6938c8a23ea5aaebcfb67bd97da97e" + integrity sha512-M11rgtQp5GZMZzDL7jLTNxbDfurpzuau5uqRWDPvlHjfvg3TdScAZo96GLvhMjImrmR8uAt0FS2RLoMrfWGKlg== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -4602,9 +4602,9 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tsutils@^3.17.1: - version "3.19.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.1.tgz#d8566e0c51c82f32f9c25a4d367cd62409a547a9" - integrity sha512-GEdoBf5XI324lu7ycad7s6laADfnAqCw6wLGI+knxvw9vsIYBaJfYdmeCEG3FMMUiSm3OGgNb+m6utsWf5h9Vw== + version "3.20.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" + integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg== dependencies: tslib "^1.8.1"