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 1bb72b5ade5..497952dc5cd 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 @@ -58,6 +58,7 @@ public class ConsentInterceptors { 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-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 89e6898a0dd..2b7f7c40244 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 @@ -165,6 +165,7 @@ public class ConsentInterceptor { Validate.notNull(outcome, "Consent service returned null outcome"); switch (outcome.getStatus()) { + case FORBID: case REJECT: throw toForbiddenOperationException(outcome); case PROCEED: @@ -243,9 +244,12 @@ public class ConsentInterceptor { skipSubsequentServices = true; break; case REJECT: + authorizedResources.put(nextResource, Boolean.FALSE); thePreResourceAccessDetails.setDontReturnResourceAtIndex(resourceIdx); skipSubsequentServices = true; break; + case FORBID: + throw toForbiddenOperationException(outcome); } if (skipSubsequentServices) { @@ -295,6 +299,7 @@ public class ConsentInterceptor { thePreResourceShowDetails.setResource(i, newResource); } continue; + case FORBID: case REJECT: if (nextOutcome.getOperationOutcome() != null) { IBaseOperationOutcome newOperationOutcome = nextOutcome.getOperationOutcome(); @@ -345,6 +350,7 @@ public class ConsentInterceptor { } switch (outcome.getStatus()) { + case FORBID: case REJECT: if (outcome.getOperationOutcome() != null) { theResource.setResponseResource(outcome.getOperationOutcome()); @@ -393,6 +399,7 @@ public class ConsentInterceptor { boolean shouldReplaceResource = false; switch (childOutcome.getStatus()) { + case FORBID: case REJECT: replacementResource = childOutcome.getOperationOutcome(); shouldReplaceResource = true; 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 d0baf09ca5a..a9062c182a6 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 @@ -40,4 +40,10 @@ public enum ConsentOperationStatusEnum { */ AUTHORIZED, + /** + * The requested operation cannot proceed, and an operation outcome suitable for + * the user is available + */ + FORBID + } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOutcome.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOutcome.java index 77cc87a37b1..60bc8e074fc 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOutcome.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentOutcome.java @@ -38,6 +38,11 @@ public class ConsentOutcome { */ public static final ConsentOutcome AUTHORIZED = new ConsentOutcome(ConsentOperationStatusEnum.AUTHORIZED); + /** + * Convenience constant containing new ConsentOutcome(ConsentOperationStatusEnum.FORBID) + */ + public static final ConsentOutcome FORBID = new ConsentOutcome(ConsentOperationStatusEnum.FORBID); + private final ConsentOperationStatusEnum myStatus; private final IBaseOperationOutcome myOperationOutcome; private final IBaseResource myResource; 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 d472cb2a4a0..d7cc5a2ed42 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 @@ -28,8 +28,11 @@ import com.google.common.base.Charsets; import com.helger.commons.collection.iterate.EmptyEnumeration; import org.apache.commons.collections4.iterators.IteratorEnumeration; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpHeaders; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.StringEntity; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; @@ -153,6 +156,38 @@ public class ConsentInterceptorTest { verify(myConsentSvc, timeout(2000).times(0)).completeOperationFailure(any(), any(), any()); } + @Test + public void testConsentCanSeeResourceForbid() 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.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)) { + String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + System.out.println("here"); + ourLog.info("Response: {}", responseContent); + assertEquals(403, status.getStatusLine().getStatusCode()); + //String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + System.out.println("here"); + ourLog.info("Response: {}", responseContent); + } + + verify(myConsentSvc, timeout(2000).times(1)).completeOperationSuccess(any(), any()); + verify(myConsentSvc, timeout(2000).times(0)).completeOperationFailure(any(), any(), any()); + } + @Test public void testTotalModeIgnoredForConsentQueries() throws IOException { Patient patientA = new Patient();