3418 get resource, response doesn't contain total if consent service enabled (#3420)
* throw an exception if parameter in use * fix string * add msg code * fix msg code * only check _total=accurate * extract validateParameter() * update validateParameter
This commit is contained in:
parent
dbc9427a05
commit
c9b87d7c67
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 3418
|
||||
title: "`GET` resource with `_total=accurate` and `_summary=count` if consent service enabled should throw an InvalidRequestException. This issue has been fixed."
|
|
@ -17,7 +17,7 @@ The consent interceptor has several primary purposes:
|
|||
|
||||
* It can apply consent directives (e.g. by reading relevant Consent resources)
|
||||
|
||||
* The consent service suppresses search the total being returned in Bundle.total for search results.
|
||||
* The consent service suppresses search the total being returned in Bundle.total for search results, even if the user explicitly requested them using the `_total=accurate` or `_summary=count` parameter.
|
||||
|
||||
The ConsentInterceptor requires a user-supplied instance of the [IConsentService](/hapi-fhir/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/consent/IConsentService.html) interface. The following shows a simple example of an IConsentService implementation:
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.rest.server.interceptor.consent;
|
|||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
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.Interceptor;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
|
@ -33,12 +34,14 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.util.ICachedSearchDetails;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.IModelVisitor2;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -100,6 +103,9 @@ public class ConsentInterceptor {
|
|||
if (isAllowListedRequest(theRequestDetails)) {
|
||||
return;
|
||||
}
|
||||
|
||||
validateParameter(theRequestDetails.getParameters());
|
||||
|
||||
ConsentOutcome outcome = myConsentService.startOperation(theRequestDetails, myContextConsentServices);
|
||||
Validate.notNull(outcome, "Consent service returned null outcome");
|
||||
|
||||
|
@ -357,4 +363,15 @@ public class ConsentInterceptor {
|
|||
private boolean isMetadataPath(RequestDetails theRequestDetails) {
|
||||
return URL_TOKEN_METADATA.equals(theRequestDetails.getRequestPath());
|
||||
}
|
||||
|
||||
private void validateParameter(Map<String, String[]> theParameterMap) {
|
||||
if (theParameterMap != null) {
|
||||
if (theParameterMap.containsKey("_total") && Arrays.stream(theParameterMap.get("_total")).anyMatch("accurate"::equals)) {
|
||||
throw new InvalidRequestException(Msg.code(2037) + "_total=accurate is not permitted on this server");
|
||||
}
|
||||
if (theParameterMap.containsKey("_summary") && Arrays.stream(theParameterMap.get("_summary")).anyMatch("count"::equals)) {
|
||||
throw new InvalidRequestException(Msg.code(2038) + "_summary=count is not permitted on this server");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,11 +148,64 @@ public class ConsentInterceptorTest {
|
|||
|
||||
HttpGet httpGet;
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_total=accurate");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseContent);
|
||||
assertThat(responseContent, containsString(Msg.code(2037) + "_total=accurate is not permitted on this server"));
|
||||
}
|
||||
|
||||
when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
when(myConsentSvc.canSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
when(myConsentSvc.willSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_total=estimated");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseContent);
|
||||
assertThat(responseContent, not(containsString("\"total\"")));
|
||||
}
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_total=accurate");
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_total=none");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseContent);
|
||||
assertThat(responseContent, not(containsString("\"total\"")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSummaryModeIgnoredTotalForConsentQueries() throws IOException {
|
||||
Patient patientA = new Patient();
|
||||
patientA.setId("Patient/A");
|
||||
patientA.setActive(true);
|
||||
patientA.addName().setFamily("FAMILY").addGiven("GIVEN");
|
||||
patientA.addIdentifier().setSystem("SYSTEM").setValue("VALUEA");
|
||||
ourPatientProvider.store(patientA);
|
||||
|
||||
Patient patientB = new Patient();
|
||||
patientB.setId("Patient/B");
|
||||
patientB.setActive(true);
|
||||
patientB.addName().setFamily("FAMILY").addGiven("GIVEN");
|
||||
patientB.addIdentifier().setSystem("SYSTEM").setValue("VALUEB");
|
||||
ourPatientProvider.store(patientB);
|
||||
|
||||
HttpGet httpGet;
|
||||
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=count");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
ourLog.info("Response: {}", responseContent);
|
||||
assertThat(responseContent, containsString(Msg.code(2038) + "_summary=count is not permitted on this server"));
|
||||
}
|
||||
|
||||
when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
when(myConsentSvc.canSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
when(myConsentSvc.willSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.PROCEED);
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=data");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
|
@ -161,7 +214,7 @@ public class ConsentInterceptorTest {
|
|||
}
|
||||
|
||||
when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.AUTHORIZED);
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_total=accurate");
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=data");
|
||||
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
|
||||
|
|
Loading…
Reference in New Issue