From 0843a2b02ddc598a9cfe7e300dfb55bf495c6f00 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 10 Sep 2019 11:14:44 -0400 Subject: [PATCH] Avoid leaking details when canSeeReource rejects a resource from consent service --- ...sentInterceptorResourceProviderR4Test.java | 71 ++++++++++++++++++- .../consent/ConsentInterceptor.java | 2 +- src/changes/changes.xml | 5 ++ 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4Test.java index 907ec6ba5a1..ae4b221e433 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4Test.java @@ -313,7 +313,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid patient.setActive(true); // Reject output - consentService.setTarget(new ConsentSvcRejectSeeingAnything()); + consentService.setTarget(new ConsentSvcRejectCanSeeAnything()); HttpPost post = new HttpPost(ourServerBase + "/Patient"); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION); post.setEntity(toEntity(patient)); @@ -356,7 +356,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid ourRestServer.getInterceptorService().registerInterceptor(myConsentInterceptor); // Reject output - consentService.setTarget(new ConsentSvcRejectSeeingAnything()); + consentService.setTarget(new ConsentSvcRejectCanSeeAnything()); patient = new Patient(); patient.setId(id); patient.setActive(true); @@ -394,6 +394,32 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid } + @Test + public void testRejectWillSeeResource() throws IOException { + create50Observations(); + + ConsentSvcRejectWillSeeEvenNumbered consentService = new ConsentSvcRejectWillSeeEvenNumbered(); + myConsentInterceptor = new ConsentInterceptor(consentService, IConsentContextServices.NULL_IMPL); + ourRestServer.getInterceptorService().registerInterceptor(myConsentInterceptor); + + // Search for all + String url = ourServerBase + "/Observation?_pretty=true&_count=10"; + ourLog.info("HTTP GET {}", url); + HttpGet get = new HttpGet(url); + get.addHeader(Constants.HEADER_ACCEPT, Constants.CT_JSON); + try (CloseableHttpResponse status = ourHttpClient.execute(get)) { + String responseString = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); + ourLog.info("Response: {}", responseString); + assertEquals(200, status.getStatusLine().getStatusCode()); + + Bundle result = myFhirCtx.newJsonParser().parseResource(Bundle.class, responseString); + List resources = BundleUtil.toListOfResources(myFhirCtx, result); + List returnedIdValues = toUnqualifiedVersionlessIdValues(resources); + assertEquals(myObservationIdsOddOnly.subList(0, 5), returnedIdValues); + } + + } + @Test public void testGraphQL_Proceed() throws IOException { createPatientAndOrg(); @@ -727,7 +753,7 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid } - private static class ConsentSvcRejectSeeingAnything implements IConsentService { + private static class ConsentSvcRejectCanSeeAnything implements IConsentService { @Override public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { @@ -756,4 +782,43 @@ public class ConsentInterceptorResourceProviderR4Test extends BaseResourceProvid } + + + private static class ConsentSvcRejectWillSeeEvenNumbered implements IConsentService { + + @Override + public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + return ConsentOutcome.PROCEED; + } + + @Override + public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { + return ConsentOutcome.PROCEED; + } + + @Override + public ConsentOutcome willSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { + if (theResource.getIdElement().isIdPartValidLong()) { + Long resIdLong = theResource.getIdElement().getIdPartAsLong(); + if (resIdLong % 2 == 0) { + return new ConsentOutcome(ConsentOperationStatusEnum.REJECT); + } + } + return new ConsentOutcome(ConsentOperationStatusEnum.PROCEED); + } + + @Override + public void completeOperationSuccess(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + // nothing + } + + @Override + public void completeOperationFailure(RequestDetails theRequestDetails, BaseServerResponseException theException, IConsentContextServices theContextServices) { + // nothing + } + + + } + + } 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 33dcd671bb0..65dcbf4af0d 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 @@ -178,7 +178,7 @@ public class ConsentInterceptor { alreadySeenResources.put(newOperationOutcome, true); } else { String resourceId = nextResource.getIdElement().getValue(); - theRequestDetails.getFhirContext().newTerser().clear(nextResource); + thePreResourceShowDetails.setResource(i, null); nextResource.setId(resourceId); } break; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 46a90fc0c21..05576a136b0 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -121,6 +121,11 @@ The GraphQL provider did not wrap the respone in a "data" element as described in the FHIR specification. This has been corrected. + + When using the Consent Service and denying a resource via the "Will See Resource" method, the resource ID + and version were still returned to the user. This has been corrected so that no details about + the resource are leaked. +