diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
index c051b972827..e03259c05ab 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
@@ -1,6 +1,7 @@
package ca.uhn.fhir.util;
import static org.apache.commons.lang3.StringUtils.defaultString;
+import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
@@ -365,6 +366,22 @@ public class FhirTerser {
for (String nextPath : nextParam.getPathsSplit()) {
for (IBaseReference nextValue : getValues(theSource, nextPath, IBaseReference.class)) {
String nextRef = nextValue.getReferenceElement().toUnqualifiedVersionless().getValue();
+
+ /*
+ * If the reference isn't an explicit resource ID, but instead is just
+ * a resource object, we'll calculate its ID and treat the target
+ * as that.
+ */
+ if (isBlank(nextRef) && nextValue.getResource() != null) {
+ IBaseResource nextTarget = nextValue.getResource();
+ IIdType nextTargetId = nextTarget.getIdElement().toUnqualifiedVersionless();
+ if (!nextTargetId.hasResourceType()) {
+ String resourceType = myContext.getResourceDefinition(nextTarget).getName();
+ nextTargetId.setParts(null, resourceType, nextTargetId.getIdPart(), null);
+ }
+ nextRef = nextTargetId.getValue();
+ }
+
if (wantRef.equals(nextRef)) {
return true;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java
index 79b9d4094c3..858a20de253 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamDate.java
@@ -66,6 +66,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
* Constructor
*/
public ResourceIndexedSearchParamDate() {
+ super();
}
/**
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java
index 8bd8f60b096..35e9684f82d 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceIndexedSearchParamQuantity.java
@@ -200,10 +200,14 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
b.append("system", getSystem());
b.append("units", getUnits());
b.append("value", getValue());
+ b.append("missing", isMissing());
return b.build();
}
private static String toTruncatedString(BigDecimal theValue) {
+ if (theValue == null) {
+ return null;
+ }
return theValue.setScale(0, RoundingMode.FLOOR).toPlainString();
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java
new file mode 100644
index 00000000000..71abdeb99bb
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java
@@ -0,0 +1,164 @@
+package ca.uhn.fhir.jpa.provider.r4;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
+import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
+import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider;
+import ca.uhn.fhir.jpa.rp.r4.OrganizationResourceProvider;
+import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider;
+import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
+import ca.uhn.fhir.rest.api.Constants;
+import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
+import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
+import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
+import ca.uhn.fhir.util.TestUtil;
+import ca.uhn.fhir.validation.ResultSeverityEnum;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+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.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
+import org.hl7.fhir.r4.model.*;
+import org.hl7.fhir.r4.model.Bundle.BundleType;
+import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
+import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
+import org.junit.*;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+public class EmptyIndexesR4Test extends BaseJpaR4Test {
+ private static RestfulServer myRestServer;
+ private static IGenericClient ourClient;
+ private static FhirContext ourCtx;
+ private static CloseableHttpClient ourHttpClient;
+ private static Server ourServer;
+ private static String ourServerBase;
+ private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
+
+ @SuppressWarnings("deprecation")
+ @After
+ public void after() {
+ myRestServer.setUseBrowserFriendlyContentTypes(true);
+ ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
+ myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
+ }
+
+ @Before
+ public void before() {
+ mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
+ ourClient.registerInterceptor(mySimpleHeaderInterceptor);
+ myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
+ }
+
+ @Before
+ public void beforeStartServer() throws Exception {
+ if (myRestServer == null) {
+ PatientResourceProvider patientRp = new PatientResourceProvider();
+ patientRp.setDao(myPatientDao);
+
+ QuestionnaireResourceProviderR4 questionnaireRp = new QuestionnaireResourceProviderR4();
+ questionnaireRp.setDao(myQuestionnaireDao);
+
+ ObservationResourceProvider observationRp = new ObservationResourceProvider();
+ observationRp.setDao(myObservationDao);
+
+ OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
+ organizationRp.setDao(myOrganizationDao);
+
+ RestfulServer restServer = new RestfulServer(ourCtx);
+ restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
+
+ restServer.setPlainProviders(mySystemProvider);
+
+ int myPort = RandomServerPortProvider.findFreePort();
+ ourServer = new Server(myPort);
+
+ ServletContextHandler proxyHandler = new ServletContextHandler();
+ proxyHandler.setContextPath("/");
+
+ ourServerBase = "http://localhost:" + myPort + "/fhir/context";
+
+ ServletHolder servletHolder = new ServletHolder();
+ servletHolder.setServlet(restServer);
+ proxyHandler.addServlet(servletHolder, "/fhir/context/*");
+
+ ourCtx = FhirContext.forR4();
+ restServer.setFhirContext(ourCtx);
+
+ ourServer.setHandler(proxyHandler);
+ ourServer.start();
+
+ PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
+ HttpClientBuilder builder = HttpClientBuilder.create();
+ builder.setConnectionManager(connectionManager);
+ ourHttpClient = builder.build();
+
+ ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
+ ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
+ ourClient.setLogRequestAndResponse(true);
+ myRestServer = restServer;
+ }
+
+ myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
+ myRestServer.setPagingProvider(myPagingProvider);
+ }
+
+
+ @Test
+ public void testDontCreateNullIndexesWithOnlyString() {
+ Observation obs = new Observation();
+ obs.getCode().setText("ZXCVBNM ASDFGHJKL QWERTYUIOPASDFGHJKL");
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ runInTransaction(()->{
+ assertThat(myResourceIndexedSearchParamQuantityDao.findAll(), empty());
+ assertThat(myResourceIndexedSearchParamTokenDao.findAll(), empty());
+ });
+ }
+
+ @Test
+ public void testDontCreateNullIndexesWithOnlyToken() {
+ Observation obs = new Observation();
+ obs.getCode().addCoding().setSystem("FOO").setCode("BAR");
+ myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
+
+ runInTransaction(()->{
+ assertThat(myResourceIndexedSearchParamQuantityDao.findAll(), empty());
+ assertThat(myResourceIndexedSearchParamStringDao.findAll(), empty());
+ // code and combo-code
+ assertThat(myResourceIndexedSearchParamTokenDao.findAll().toString(), myResourceIndexedSearchParamTokenDao.findAll(), hasSize(2));
+ });
+ }
+
+
+ @AfterClass
+ public static void afterClassClearContext() throws Exception {
+ ourServer.stop();
+ TestUtil.clearAllStaticFieldsForUnitTest();
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java
index aa3ce0dd9de..d9301b73a9b 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java
@@ -1,37 +1,11 @@
package ca.uhn.fhir.jpa.provider.r4;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.*;
-
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.http.Header;
-import org.apache.http.client.methods.*;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.StringEntity;
-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.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
-import org.hl7.fhir.r4.model.*;
-import org.hl7.fhir.r4.model.Bundle.BundleType;
-import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
-import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.*;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
-
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
-import ca.uhn.fhir.jpa.rp.r4.*;
+import ca.uhn.fhir.jpa.rp.r4.ObservationResourceProvider;
+import ca.uhn.fhir.jpa.rp.r4.OrganizationResourceProvider;
+import ca.uhn.fhir.jpa.rp.r4.PatientResourceProvider;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
@@ -45,6 +19,35 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.validation.ResultSeverityEnum;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.Header;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+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.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.hapi.validation.FhirInstanceValidator;
+import org.hl7.fhir.r4.model.*;
+import org.hl7.fhir.r4.model.Bundle.BundleType;
+import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
+import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
+import org.junit.*;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
public class SystemProviderR4Test extends BaseJpaR4Test {
@@ -63,7 +66,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
myRestServer.setUseBrowserFriendlyContentTypes(true);
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
}
-
+
@Before
public void before() {
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
@@ -118,7 +121,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
ourClient.setLogRequestAndResponse(true);
myRestServer = restServer;
}
-
+
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
myRestServer.setPagingProvider(myPagingProvider);
}
@@ -154,7 +157,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
myRestServer.unregisterInterceptor(interceptor);
}
-
+
@Test
public void testEverythingReturnsCorrectFormatInPagingLink() throws Exception {
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
@@ -183,7 +186,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
myRestServer.unregisterInterceptor(interceptor);
}
-
+
@Test
public void testEverythingType() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
@@ -194,7 +197,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
http.close();
}
}
-
+
@Test
public void testGetOperationDefinition() {
OperationDefinition op = ourClient.read(OperationDefinition.class, "-s-get-resource-counts");
@@ -210,7 +213,8 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
- IOUtils.closeQuietly(http);;
+ IOUtils.closeQuietly(http);
+ ;
}
get = new HttpGet(ourServerBase + "/$perform-reindexing-pass");
@@ -220,7 +224,8 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
- IOUtils.closeQuietly(http);;
+ IOUtils.closeQuietly(http);
+ ;
}
}
@@ -237,6 +242,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
assertThat(http.getFirstHeader("Content-Type").getValue(), containsString("application/fhir+json"));
}
+
@Transactional(propagation = Propagation.NEVER)
@Test
public void testSuggestKeywords() throws Exception {
@@ -320,7 +326,7 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
}
}
-
+
@Test
public void testTransactionCount() throws Exception {
for (int i = 0; i < 20; i++) {
@@ -577,33 +583,33 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
//@formatter:off
String input = "\n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- " \n" +
- "";
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
//@formatter:off
HttpPost req = new HttpPost(ourServerBase);
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorR4Test.java
index 01b20c93a37..61da96bb604 100644
--- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorR4Test.java
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AuthorizationInterceptorR4Test.java
@@ -390,6 +390,69 @@ public class AuthorizationInterceptorR4Test {
assertTrue(ourHitMethod);
}
+ @Test
+ public void testAllowByCompartmentUsingUnqualifiedIds() throws Exception {
+ ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
+ @Override
+ public List buildRuleList(RequestDetails theRequestDetails) {
+ return new RuleBuilder().allow().read().resourcesOfType(CarePlan.class).inCompartment("Patient", new IdType("Patient/123")).andThen().denyAll()
+ .build();
+ }
+ });
+
+ HttpGet httpGet;
+ HttpResponse status;
+
+ Patient patient;
+ CarePlan carePlan;
+
+ // Unqualified
+ patient = new Patient();
+ patient.setId("123");
+ carePlan = new CarePlan();
+ carePlan.setStatus(CarePlan.CarePlanStatus.ACTIVE);
+ carePlan.getSubject().setResource(patient);
+
+ ourHitMethod = false;
+ ourReturn = Collections.singletonList(carePlan);
+ httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154");
+ status = ourClient.execute(httpGet);
+ extractResponseAndClose(status);
+ assertEquals(200, status.getStatusLine().getStatusCode());
+ assertTrue(ourHitMethod);
+
+ // Qualified
+ patient = new Patient();
+ patient.setId("Patient/123");
+ carePlan = new CarePlan();
+ carePlan.setStatus(CarePlan.CarePlanStatus.ACTIVE);
+ carePlan.getSubject().setResource(patient);
+
+ ourHitMethod = false;
+ ourReturn = Collections.singletonList(carePlan);
+ httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154");
+ status = ourClient.execute(httpGet);
+ extractResponseAndClose(status);
+ assertEquals(200, status.getStatusLine().getStatusCode());
+ assertTrue(ourHitMethod);
+
+ // Wrong one
+ patient = new Patient();
+ patient.setId("456");
+ carePlan = new CarePlan();
+ carePlan.setStatus(CarePlan.CarePlanStatus.ACTIVE);
+ carePlan.getSubject().setResource(patient);
+
+ ourHitMethod = false;
+ ourReturn = Collections.singletonList(carePlan);
+ httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154");
+ status = ourClient.execute(httpGet);
+ extractResponseAndClose(status);
+ assertEquals(403, status.getStatusLine().getStatusCode());
+ assertTrue(ourHitMethod);
+ }
+
+
@Test
public void testBatchWhenOnlyTransactionAllowed() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d2072f60f07..45a4149caf4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -248,6 +248,12 @@
the number of threads allocated to reindexing may now be adjusted via a
setting in the DaoConfig.
+
+ AuthorizationInterceptor did not correctly grant access to resources
+ by compartment when the reference on the target resource that pointed
+ to the compartment owner was defined using a resource object (ResourceReference#setResource)
+ instead of a reference (ResourceReference#setReference).
+