Add implementation. Add test. Add changelog

This commit is contained in:
Tadgh 2021-05-11 10:31:29 -04:00
parent 3015438a0c
commit 424ec2118b
5 changed files with 82 additions and 0 deletions

View File

@ -82,6 +82,10 @@ public class FhirServerConfigCommon {
logger.error("----FhiServerConfigCommon: getDataSource: setting driver error: " + e.getMessage());
}
dataSource.setUrl(dbUrl);
// A check for WS-2020-0287
assert dataSource.getJmxName() == null;
return dataSource;
}

View File

@ -0,0 +1,6 @@
---
type: change
issue: 2648
title: "The ConsentInterceptor no longer fully runs on calls to `/metadata` or during the `$meta` operation."

View File

@ -122,6 +122,9 @@ public enum DriverTypeEnum {
dataSource.setUsername(theUsername);
dataSource.setPassword(thePassword);
// A check for WS-2020-0287
assert dataSource.getJmxName() == null;
return newConnectionProperties(dataSource);
}

View File

@ -44,6 +44,8 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import static ca.uhn.fhir.rest.api.Constants.URL_TOKEN_METADATA;
@Interceptor
public class ConsentInterceptor {
private static final AtomicInteger ourInstanceCount = new AtomicInteger(0);
@ -51,6 +53,7 @@ public class ConsentInterceptor {
private final String myRequestAuthorizedKey = ConsentInterceptor.class.getName() + "_" + myInstanceIndex + "_AUTHORIZED";
private final String myRequestCompletedKey = ConsentInterceptor.class.getName() + "_" + myInstanceIndex + "_COMPLETED";
private final String myRequestSeenResourcesKey = ConsentInterceptor.class.getName() + "_" + myInstanceIndex + "_SEENRESOURCES";
public static final String META_OPERATION_NAME = "$meta";
private IConsentService myConsentService;
private IConsentContextServices myContextConsentServices;
@ -94,6 +97,9 @@ public class ConsentInterceptor {
@Hook(value = Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED)
public void interceptPreHandled(RequestDetails theRequestDetails) {
if (isAllowListedRequest(theRequestDetails)) {
return;
}
ConsentOutcome outcome = myConsentService.startOperation(theRequestDetails, myContextConsentServices);
Validate.notNull(outcome, "Consent service returned null outcome");
@ -129,6 +135,9 @@ public class ConsentInterceptor {
if (isRequestAuthorized(theRequestDetails)) {
return;
}
if (isAllowListedRequest(theRequestDetails)) {
return;
}
for (int i = 0; i < thePreResourceAccessDetails.size(); i++) {
IBaseResource nextResource = thePreResourceAccessDetails.getResource(i);
@ -150,6 +159,9 @@ public class ConsentInterceptor {
if (isRequestAuthorized(theRequestDetails)) {
return;
}
if (isAllowListedRequest(theRequestDetails)) {
return;
}
IdentityHashMap<IBaseResource, Boolean> alreadySeenResources = getAlreadySeenResourcesMap(theRequestDetails);
for (int i = 0; i < thePreResourceShowDetails.size(); i++) {
@ -198,6 +210,9 @@ public class ConsentInterceptor {
if (isRequestAuthorized(theRequestDetails)) {
return;
}
if (isAllowListedRequest(theRequestDetails)) {
return;
}
IdentityHashMap<IBaseResource, Boolean> alreadySeenResources = getAlreadySeenResourcesMap(theRequestDetails);
@ -330,4 +345,16 @@ public class ConsentInterceptor {
}
return new ForbiddenOperationException("Rejected by consent service", operationOutcome);
}
private boolean isAllowListedRequest(RequestDetails theRequestDetails) {
return isMetadataPath(theRequestDetails) || isMetaOperation(theRequestDetails);
}
private boolean isMetaOperation(RequestDetails theRequestDetails) {
return META_OPERATION_NAME.equals(theRequestDetails.getOperation());
}
private boolean isMetadataPath(RequestDetails theRequestDetails) {
return URL_TOKEN_METADATA.equals(theRequestDetails.getRequestPath());
}
}

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.rest.server.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.BundleInclusionRule;
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;
@ -11,11 +13,13 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptorTest;
import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor;
import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOperationStatusEnum;
import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome;
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService;
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.test.utilities.JettyUtil;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
@ -27,9 +31,11 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterAll;
@ -70,6 +76,7 @@ public class ConsentInterceptorTest {
private static Server ourServer;
private static DummyPatientResourceProvider ourPatientProvider;
private static IGenericClient ourFhirClient;
private static DummySystemProvider ourSystemProvider;
@Mock
private IConsentService myConsentSvc;
@ -163,6 +170,28 @@ public class ConsentInterceptorTest {
}
@Test
public void testMetadataCallHasChecksSkipped() throws IOException{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata");
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);
}
httpGet = new HttpGet("http://localhost:" + ourPort + "/$meta");
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);
}
verify(myConsentSvc, times(0)).canSeeResource(any(), any(), any());
verify(myConsentSvc, times(0)).willSeeResource(any(), any(), any());
verify(myConsentSvc, times(0)).startOperation(any(), any());
verify(myConsentSvc, times(2)).completeOperationSuccess(any(), any());
}
@Test
public void testSearch_SeeResourceAuthorizesOuterBundle() throws IOException {
ourPatientProvider.store((Patient) new Patient().setActive(true).setId("PTA"));
@ -457,11 +486,13 @@ public class ConsentInterceptorTest {
ourServer = new Server(0);
ourPatientProvider = new DummyPatientResourceProvider(ourCtx);
ourSystemProvider = new DummySystemProvider();
ServletHandler servletHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx);
ourServlet.setDefaultPrettyPrint(true);
ourServlet.setResourceProviders(ourPatientProvider);
ourServlet.registerProvider(ourSystemProvider);
ourServlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE);
ServletHolder servletHolder = new ServletHolder(ourServlet);
servletHandler.addServletWithMapping(servletHolder, "/*");
@ -478,4 +509,15 @@ public class ConsentInterceptorTest {
ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
}
private static class DummySystemProvider{
@Operation(name = "$meta", idempotent = true, returnParameters = {
@OperationParam(name = "return", typeName = "Meta")
})
public IBaseParameters meta(ServletRequestDetails theRequestDetails) {
Parameters retval = new Parameters();
retval.addParameter("Meta", "Yes");
return retval;
}
}
}