diff --git a/src/smartHandler.test.ts b/src/smartHandler.test.ts index 62dc085..29be0e2 100644 --- a/src/smartHandler.test.ts +++ b/src/smartHandler.test.ts @@ -429,6 +429,16 @@ describe('verifyAccessToken', () => { { ...baseAccessNoScopes, scp: 'system/Patient.write' }, true, ], + [ + 'patientUserSystem_specificRead_search', + { accessToken: 'fake', operation: 'search-type', resourceType: 'Observation' }, + { + ...baseAccessNoScopes, + scp: 'user/Patient.read system/Patient.read patient/Patient.read', + ...patientFhirUser, + }, + false, + ], ]; const authZConfig = baseAuthZConfig(); diff --git a/src/smartScopeHelper.test.ts b/src/smartScopeHelper.test.ts index fe2f17a..fa6c368 100644 --- a/src/smartScopeHelper.test.ts +++ b/src/smartScopeHelper.test.ts @@ -396,6 +396,46 @@ describe('filterOutUnusableScope', () => { ).toEqual(['system/*.write']); }); + test('filter out all system scope out in type-search use case when resource type does not match scopes', () => { + const clonedScopeRule = emptyScopeRule(); + clonedScopeRule.system.read = ['search-type']; + clonedScopeRule.system.write = ['create']; + expect( + filterOutUnusableScope( + ['system/DocumentReference.read', 'system/Patient.read', 'system/Practitioner.write'], + clonedScopeRule, + 'search-type', + false, + 'Practitioner', + ), + ).toEqual([]); + }); + test('filter out all user scope out in type-search use case when resource type does not match scopes', () => { + const clonedScopeRule = emptyScopeRule(); + clonedScopeRule.user.read = ['search-type']; + expect( + filterOutUnusableScope( + ['user/DocumentReference.read', 'user/Patient.read'], + clonedScopeRule, + 'search-type', + false, + 'Practitioner', + ), + ).toEqual([]); + }); + test('filter out all patient scope out in type-search use case when resource type does not match scopes', () => { + const clonedScopeRule = emptyScopeRule(); + clonedScopeRule.user.read = ['search-type']; + expect( + filterOutUnusableScope( + ['patient/DocumentReference.read', 'patient/Patient.read'], + clonedScopeRule, + 'search-type', + false, + 'Practitioner', + ), + ).toEqual([]); + }); test('do not filter patient scope out in type-search use case', () => { const clonedScopeRule = emptyScopeRule(); clonedScopeRule.system.read = ['search-type']; diff --git a/src/smartScopeHelper.ts b/src/smartScopeHelper.ts index ec4fcb9..2113a57 100644 --- a/src/smartScopeHelper.ts +++ b/src/smartScopeHelper.ts @@ -157,7 +157,7 @@ export function filterOutUnusableScope( patientContext?: string, fhirUser?: string, ): string[] { - return scopes.filter( + const filteredScopes: string[] = scopes.filter( (scope: string) => ((patientContext && scope.startsWith('patient/')) || (fhirUser && scope.startsWith('user/')) || @@ -171,4 +171,17 @@ export function filterOutUnusableScope( bulkDataAuth, ), ); + + // We should only return the scopes iff there is at least 1 valid scope for the given resourceType + if ( + reqOperation === 'search-type' && + !filteredScopes.some((scope: string) => { + const smartScope = convertScopeToSmartScope(scope); + return smartScope.resourceType === '*' || smartScope.resourceType === reqResourceType; + }) + ) { + return []; + } + + return filteredScopes; }