From cdce5ec74337016e03324b1a546b33534c4aa529 Mon Sep 17 00:00:00 2001 From: souradeepsaha Date: Fri, 2 Jun 2023 13:23:02 -0400 Subject: [PATCH] Interim code for ConsentInterceptorTest --- .../hapi/fhir/docs/ConsentInterceptors.java | 5 ++- ...forbidden-operation-to-consentservice.yaml | 8 +++++ .../consent/ConsentInterceptor.java | 2 +- .../consent/ConsentOperationStatusEnum.java | 3 +- .../interceptor/ConsentInterceptorTest.java | 36 ++++++++++++++----- 5 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4941-adding-forbidden-operation-to-consentservice.yaml diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java index 497952dc5cd..aafbe9fbe68 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java @@ -52,13 +52,12 @@ public class ConsentInterceptors { @Override public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { // In this basic example, we will filter out lab results so that they - // are never disclosed to the user. A real interceptor might do something - // more nuanced. + // are never disclosed to the user. A real interceptor might do something more nuance or entirely + // forbid the operation by returning ConsentOutcome.FORBID; if (theResource instanceof Observation) { Observation obs = (Observation)theResource; if (obs.getCategoryFirstRep().hasCoding("http://hl7.org/fhir/codesystem-observation-category.html", "laboratory")) { return ConsentOutcome.REJECT; - //return ConsentOutcome.FORBID; } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4941-adding-forbidden-operation-to-consentservice.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4941-adding-forbidden-operation-to-consentservice.yaml new file mode 100644 index 00000000000..54f5caede1e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/4941-adding-forbidden-operation-to-consentservice.yaml @@ -0,0 +1,8 @@ +--- +type: add +issue: 4941 +jira: smile-6485 +todo: +title: "The `@Interceptor` annotation can now be placed at the method level. This is used only + as a marker, and does not change the behaviour or interceptors in any way. Thanks to + Dominique Villard for the pull request!" diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java index 2b7f7c40244..fe06c2f483f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java @@ -244,7 +244,6 @@ public class ConsentInterceptor { skipSubsequentServices = true; break; case REJECT: - authorizedResources.put(nextResource, Boolean.FALSE); thePreResourceAccessDetails.setDontReturnResourceAtIndex(resourceIdx); skipSubsequentServices = true; break; @@ -300,6 +299,7 @@ public class ConsentInterceptor { } continue; case FORBID: + throw toForbiddenOperationException(nextOutcome); case REJECT: if (nextOutcome.getOperationOutcome() != null) { IBaseOperationOutcome newOperationOutcome = nextOutcome.getOperationOutcome(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOperationStatusEnum.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOperationStatusEnum.java index a9062c182a6..1e8045725c2 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOperationStatusEnum.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOperationStatusEnum.java @@ -42,7 +42,8 @@ public enum ConsentOperationStatusEnum { /** * The requested operation cannot proceed, and an operation outcome suitable for - * the user is available + * the user is forbidden. This was added to allow for 403 forbidden error from + * ConsentServices. */ FORBID diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java index 62f51a32573..c707734789b 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java @@ -2,14 +2,11 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.api.Hook; -import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.param.StringParam; @@ -56,7 +53,6 @@ import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -67,18 +63,14 @@ import java.util.List; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; @ExtendWith(MockitoExtension.class) public class ConsentInterceptorTest { @@ -156,6 +148,32 @@ public class ConsentInterceptorTest { verify(myConsentSvc, timeout(2000).times(0)).completeOperationFailure(any(), any(), any()); } + @Test + public void testOutcomeForbidden() throws IOException { + Patient patientA = new Patient(); + patientA.setId("PT-1-0"); + patientA.setActive(true); + patientA.addName().setFamily("FAMILY").addGiven("GIVEN"); + patientA.addIdentifier().setSystem("SYSTEM").setValue("VALUEA"); + ourPatientProvider.store(patientA); + + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.canSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.FORBID); + + HttpPut httpPut = new HttpPut("http://localhost:" + myPort + "/Patient/PT-1-0"); + httpPut.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + httpPut.setHeader("Authorization", "ingestfa_client iaamSmile123"); + + httpPut.setEntity(new StringEntity("{\"resourceType\": \"Patient\", \"id\": \"PT-1-0\",\"text\": {\"status\": \"generated\",\"div\": \"

A valid patient resource for testing purposes

\" },\"gender\": \"male\"}")); + + try (CloseableHttpResponse status = myClient.execute(httpPut)) { + ourLog.info("RESULT {}", status); + assertEquals(403, status.getStatusLine().getStatusCode()); + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", responseContent); + } + } + @Test public void testTotalModeIgnoredForConsentQueries() throws IOException { Patient patientA = new Patient(); @@ -250,6 +268,8 @@ public class ConsentInterceptorTest { } + + @Test public void testMetadataCallHasChecksSkipped() throws IOException{ HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/metadata");