From ce1a48994146d20ab4efac633d3d4db5f14ec201 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Mon, 21 Dec 2020 14:41:15 -0500 Subject: [PATCH] feat: Add support for GetSearchFilterBasedOnIdentity (#9) --- package.json | 2 +- src/smartHandler.test.ts | 65 ++++++++++++++++++++++++++++++++++++++++ src/smartHandler.ts | 23 ++++++++++++++ yarn.lock | 8 ++--- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 763f50c..f3650fc 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "axios": "^0.20.0", - "fhir-works-on-aws-interface": "^4.0.0", + "fhir-works-on-aws-interface": "^6.0.0", "jsonwebtoken": "^8.5.1" }, "devDependencies": { diff --git a/src/smartHandler.test.ts b/src/smartHandler.test.ts index 53b584a..ab7186b 100644 --- a/src/smartHandler.test.ts +++ b/src/smartHandler.test.ts @@ -16,6 +16,7 @@ import { AllowedResourceTypesForOperationRequest, BASE_R4_RESOURCES, AuthorizationBundleRequest, + GetSearchFilterBasedOnIdentityRequest, } from 'fhir-works-on-aws-interface'; import { decode } from 'jsonwebtoken'; import { SMARTHandler } from './smartHandler'; @@ -1002,3 +1003,67 @@ describe('getAllowedResourceTypesForOperation', () => { ); }); }); + +describe('getSearchFilterBasedOnIdentity', () => { + const authZHandler: SMARTHandler = new SMARTHandler(authZConfig, apiUrl, '4.0.1'); + test('Patient identity', async () => { + // BUILD + const userIdentity = clone(patientIdentityWithoutScopes); + const request: GetSearchFilterBasedOnIdentityRequest = { + userIdentity, + operation: 'search-type', + }; + + // OPERATE, CHECK + const expectedFilter = [ + { + key: '_reference', + logicalOperator: 'OR', + comparisonOperator: '==', + value: [patientId, `${apiUrl}${patientId}`], + }, + ]; + await expect(authZHandler.getSearchFilterBasedOnIdentity(request)).resolves.toEqual(expectedFilter); + }); + + test('Patient identity; fhirUser hostname does not match server hostname', async () => { + // BUILD + const authZHandlerWithFakeApiUrl: SMARTHandler = new SMARTHandler( + authZConfig, + 'https://fhir.server-2.com/dev/', + '4.0.1', + ); + + const userIdentity = clone(patientIdentityWithoutScopes); + const request: GetSearchFilterBasedOnIdentityRequest = { + userIdentity, + operation: 'search-type', + }; + + // OPERATE, CHECK + const expectedFilter = [ + { + key: '_reference', + logicalOperator: 'OR', + comparisonOperator: '==', + value: [`${apiUrl}${patientId}`], + }, + ]; + await expect(authZHandlerWithFakeApiUrl.getSearchFilterBasedOnIdentity(request)).resolves.toEqual( + expectedFilter, + ); + }); + + test('Practitioner identity', async () => { + // BUILD + const userIdentity = clone(practitionerIdentityWithoutScopes); + const request: GetSearchFilterBasedOnIdentityRequest = { + userIdentity, + operation: 'search-type', + }; + + // OPERATE, CHECK + const expectedFilter: [] = []; + await expect(authZHandler.getSearchFilterBasedOnIdentity(request)).resolves.toEqual(expectedFilter); + }); +}); diff --git a/src/smartHandler.ts b/src/smartHandler.ts index 85016c6..c44e74f 100644 --- a/src/smartHandler.ts +++ b/src/smartHandler.ts @@ -21,6 +21,8 @@ import { FhirVersion, BASE_STU3_RESOURCES, BulkDataAuth, + GetSearchFilterBasedOnIdentityRequest, + SearchFilter, } from 'fhir-works-on-aws-interface'; import axios from 'axios'; import { @@ -143,6 +145,27 @@ export class SMARTHandler implements Authorization { } } + async getSearchFilterBasedOnIdentity(request: GetSearchFilterBasedOnIdentityRequest): Promise { + const fhirUser = this.getFhirUser(request.userIdentity); + const { hostname, resourceType, id } = fhirUser; + + // Create a SearchFilter to limit access to only resources that are referring to the requesting user + if (resourceType !== 'Practitioner') { + const searchFilter: SearchFilter = { + key: '_reference', + value: [`${hostname}${resourceType}/${id}`], + comparisonOperator: '==', + logicalOperator: 'OR', // logicalOperator can be either 'AND' or 'OR' since value is an array of one string + }; + if (hostname === this.apiUrl) { + searchFilter.value = [`${resourceType}/${id}`, `${hostname}${resourceType}/${id}`]; + } + return [searchFilter]; + } + + return []; + } + async isBundleRequestAuthorized(request: AuthorizationBundleRequest): Promise { const { scopes } = request.userIdentity; request.requests.forEach((req: BatchReadWriteRequest) => { diff --git a/yarn.lock b/yarn.lock index 19030b3..eaa22fc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1745,10 +1745,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fhir-works-on-aws-interface@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fhir-works-on-aws-interface/-/fhir-works-on-aws-interface-4.0.0.tgz#4d857dc62a867fba596a02fd481034a94ca93ac0" - integrity sha512-7onyEoHuwd+xQoMl6OlugCSahhtrYDNgmy4tYgcsGzTpnbDhQKpDbJUizW1vrpH9t/mPstSYxsBSc1WQ0rl2EA== +fhir-works-on-aws-interface@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fhir-works-on-aws-interface/-/fhir-works-on-aws-interface-6.0.0.tgz#e1edcd2013b4669986a22716109421bd8361fb68" + integrity sha512-TZNpvk6M8wOoLufVCJgLUFKRx83L1cx1u/7Hh2mc+zVudz/+H4EMCAIaLfXjMhdZVYK02hM2CbmcE1vBdwbudw== figures@^3.0.0: version "3.2.0"