From 0780b7e4729d369b63d843c84c56767a78cd94a6 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Thu, 14 Apr 2016 06:36:29 -0400 Subject: [PATCH] Add tests --- .../rest/method/TransactionMethodBinding.java | 19 +++ .../rest/server/interceptor/auth/Rule.java | 22 +++- .../server/interceptor/auth/RuleOpEnum.java | 3 +- .../java/ca/uhn/fhir/util/BundleUtil.java | 12 +- .../cli/LoadingValidationSupportDstu2.java | 2 +- .../cli/LoadingValidationSupportDstu3.java | 2 +- .../jpa/dao/FhirResourceDaoDstu1Test.java | 33 +++-- .../SearchWithGenericListDstu2Test.java | 10 +- .../AuthorizationInterceptorDstu2Test.java | 36 ++++++ .../SearchWithGenericListHl7OrgDstu2Test.java | 116 ++++++++++++++++++ 10 files changed, 230 insertions(+), 25 deletions(-) create mode 100644 hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java index 33ead0772c0..b3060600f6f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java @@ -41,12 +41,14 @@ import ca.uhn.fhir.rest.annotation.TransactionParam; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.client.BaseHttpClientInvocation; +import ca.uhn.fhir.rest.param.ResourceParameter; import ca.uhn.fhir.rest.param.TransactionParameter; import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle; import ca.uhn.fhir.rest.server.IBundleProvider; import ca.uhn.fhir.rest.server.IRestfulServer; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; public class TransactionMethodBinding extends BaseResourceReturningMethodBinding { @@ -176,6 +178,23 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding return retVal; } + + @Override + protected void populateActionRequestDetailsForInterceptor(RequestDetails theRequestDetails, ActionRequestDetails theDetails, Object[] theMethodParams) { + super.populateActionRequestDetailsForInterceptor(theRequestDetails, theDetails, theMethodParams); + + /* + * If the method has no parsed resource parameter, we parse here in order to have something for the interceptor. + */ + if (myTransactionParamIndex != -1) { + theDetails.setResource((IBaseResource) theMethodParams[myTransactionParamIndex]); + } else { + Class resourceType = getContext().getResourceDefinition("Bundle").getImplementingClass(); + theDetails.setResource(ResourceParameter.parseResourceFromRequest(theRequestDetails, this, resourceType)); + } + + } + public static BaseHttpClientInvocation createTransactionInvocation(Bundle theBundle, FhirContext theContext) { return new HttpPostClientInvocation(theContext, theBundle); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java index 5b0de067136..ed2ec222634 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/Rule.java @@ -23,12 +23,14 @@ package ca.uhn.fhir.rest.server.interceptor.auth; import java.util.Collection; import java.util.Set; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.method.RequestDetails; +import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.FhirTerser; class Rule implements IAuthRule { @@ -59,8 +61,13 @@ class Rule implements IAuthRule { case WRITE: appliesTo = theInputResource; break; + case BATCH: case TRANSACTION: - return myMode; + if (requestAppliesToTransaction(ctx, myOp, theInputResource)) { + return myMode; + } else { + return RuleVerdictEnum.NO_DECISION; + } case ALLOW_ALL: return RuleVerdictEnum.ALLOW; case DENY_ALL: @@ -111,6 +118,19 @@ class Rule implements IAuthRule { return myMode; } + private boolean requestAppliesToTransaction(FhirContext theContext, RuleOpEnum theOp, IBaseResource theInputResource) { + IBaseBundle request = (IBaseBundle) theInputResource; + String bundleType = BundleUtil.getBundleType(theContext, request); + switch (theOp) { + case TRANSACTION: + return "transaction".equals(bundleType); + case BATCH: + return "batch".equals(bundleType); + default: + return false; + } + } + @Override public String getName() { return myName; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java index de10093d799..52c1671b546 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleOpEnum.java @@ -26,5 +26,6 @@ enum RuleOpEnum { ALLOW_ALL, DENY_ALL, TRANSACTION, - METADATA + METADATA, + BATCH } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index 4549ddb8ca0..4cde0019218 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -31,7 +31,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; -import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; @@ -98,4 +97,15 @@ public class BundleUtil { return retVal; } + public static String getBundleType(FhirContext theContext, IBaseBundle theBundle) { + RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); + BaseRuntimeChildDefinition entryChild = def.getChildByName("type"); + List entries = entryChild.getAccessor().getValues(theBundle); + if (entries.size() > 0) { + IPrimitiveType typeElement = (IPrimitiveType) entries.get(0); + return typeElement.getValueAsString(); + } + return null; + } + } diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java index e48a1806e48..ccf2a6e7131 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java +++ b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java @@ -13,7 +13,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; public class LoadingValidationSupportDstu2 implements IValidationSupport { - private static FhirContext myCtx = FhirContext.forDstu2Hl7Org(); + private FhirContext myCtx = FhirContext.forDstu2Hl7Org(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class); diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java index 21caa278387..b8682333737 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java @@ -14,7 +14,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; public class LoadingValidationSupportDstu3 implements IValidationSupport { - private static FhirContext myCtx = FhirContext.forDstu3(); + private FhirContext myCtx = FhirContext.forDstu3(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu3.class); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1Test.java index f5b99c75d3d..0ff02fe277a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1Test.java @@ -31,11 +31,11 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @SuppressWarnings("unused") public class FhirResourceDaoDstu1Test extends BaseJpaTest { - private static AnnotationConfigApplicationContext ourCtx; + private static AnnotationConfigApplicationContext ourAppCtx; private static IFhirResourceDao ourDeviceDao; private static IFhirResourceDao ourDiagnosticReportDao; private static IFhirResourceDao ourEncounterDao; - private static FhirContext ourFhirCtx; + private static FhirContext ourCtx; private static IFhirResourceDao ourLocationDao; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu1Test.class); private static IFhirResourceDao ourObservationDao; @@ -126,21 +126,30 @@ public class FhirResourceDaoDstu1Test extends BaseJpaTest { @AfterClass public static void afterClass() { - ourCtx.close(); + ourAppCtx.close(); + ourAppCtx = null; + ourCtx = null; + ourDeviceDao = null; + ourDiagnosticReportDao = null; + ourEncounterDao = null; + ourLocationDao = null; + ourObservationDao = null; + ourOrganizationDao = null; + ourPatientDao = null; } @SuppressWarnings("unchecked") @BeforeClass public static void beforeClass() { - ourCtx = new AnnotationConfigApplicationContext(TestDstu1Config.class); - ourPatientDao = ourCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class); - ourObservationDao = ourCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class); - ourDiagnosticReportDao = ourCtx.getBean("myDiagnosticReportDaoDstu1", IFhirResourceDao.class); - ourDeviceDao = ourCtx.getBean("myDeviceDaoDstu1", IFhirResourceDao.class); - ourOrganizationDao = ourCtx.getBean("myOrganizationDaoDstu1", IFhirResourceDao.class); - ourLocationDao = ourCtx.getBean("myLocationDaoDstu1", IFhirResourceDao.class); - ourEncounterDao = ourCtx.getBean("myEncounterDaoDstu1", IFhirResourceDao.class); - ourFhirCtx = ourCtx.getBean(FhirContext.class); + ourAppCtx = new AnnotationConfigApplicationContext(TestDstu1Config.class); + ourDeviceDao = ourAppCtx.getBean("myDeviceDaoDstu1", IFhirResourceDao.class); + ourDiagnosticReportDao = ourAppCtx.getBean("myDiagnosticReportDaoDstu1", IFhirResourceDao.class); + ourEncounterDao = ourAppCtx.getBean("myEncounterDaoDstu1", IFhirResourceDao.class); + ourLocationDao = ourAppCtx.getBean("myLocationDaoDstu1", IFhirResourceDao.class); + ourObservationDao = ourAppCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class); + ourOrganizationDao = ourAppCtx.getBean("myOrganizationDaoDstu1", IFhirResourceDao.class); + ourPatientDao = ourAppCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class); + ourCtx = ourAppCtx.getBean(FhirContext.class); } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java index 0301effd313..9602a8cd5ca 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java @@ -5,7 +5,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -18,7 +17,6 @@ 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.IBaseResource; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -27,14 +25,9 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; -import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.resource.Patient; -import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.util.PortUtil; @@ -57,7 +50,7 @@ public class SearchWithGenericListDstu2Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo"); + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -65,6 +58,7 @@ public class SearchWithGenericListDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java index a4b6160810a..caf98d4e6d6 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java @@ -177,6 +177,42 @@ public class AuthorizationInterceptorDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); } + @Test + public void testBatchWhenTransactionAllowed() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + //@formatter:off + return new RuleBuilder() + .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() + .allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() + .allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen() + .build(); + //@formatter:on + } + }); + + Bundle input = new Bundle(); + input.setType(BundleTypeEnum.BATCH); + input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient"); + + Bundle output = new Bundle(); + output.setType(BundleTypeEnum.TRANSACTION_RESPONSE); + output.addEntry().getResponse().setLocation("/Patient/1"); + + HttpPost httpPost; + HttpResponse status; + String response; + + ourReturn = Arrays.asList((IResource)output); + ourHitMethod = false; + httpPost = new HttpPost("http://localhost:" + ourPort + "/"); + httpPost.setEntity(createFhirResourceEntity(input)); + status = ourClient.execute(httpPost); + extractResponseAndClose(status); + assertEquals(401, status.getStatusLine().getStatusCode()); + } + @Test public void testMetadataDeny() throws Exception { ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.ALLOW) { diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java new file mode 100644 index 00000000000..b32bcb8b0f7 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java @@ -0,0 +1,116 @@ +package ca.uhn.fhir.rest.server; + +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +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.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; +import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.util.PortUtil; + +public class SearchWithGenericListHl7OrgDstu2Test { + + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListHl7OrgDstu2Test.class); + private static int ourPort; + private static Server ourServer; + private static String ourLastMethod; + + @Before + public void before() { + ourLastMethod = null; + } + + /** + * See #291 + */ + @Test + public void testSearch() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertEquals("searchByIdentifier", ourLastMethod); + assertThat(responseContent, containsString("")); + } + + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ourPort = PortUtil.findFreePort(); + ourServer = new Server(ourPort); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(ourCtx); + servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + ourServer.start(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + //@formatter:off + @SuppressWarnings("rawtypes") + @Search() + public List searchByIdentifier( + @RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) { + ourLastMethod = "searchByIdentifier"; + ArrayList retVal = new ArrayList(); + retVal.add((Patient) new Patient().addName(new HumanNameDt().addFamily("FAMILY")).setId("1")); + return retVal; + } + //@formatter:on + + + } + +}