diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 9ecbaf53718..ff641a0462d 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 8084b87ab49..7e7c78f8d81 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 499e27e8696..4f884ec3447 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 04527eb7457..ec21cd36275 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -1038,7 +1038,7 @@ public enum Pointcut implements IPointcut { */ STORAGE_INITIATE_BULK_EXPORT( void.class, - "ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions", + "ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions", "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails" ), diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 4bcaee2dba3..7639f03f858 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index a65bcf0316c..fd3a69abf7f 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index fe12a999384..29268596771 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index dec4f77b747..5924752e470 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 275f85a528d..bbf746ea892 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 2347e49ef33..0fb003d3608 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 0ad1e150ce5..5c82a1d2cbd 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index da2334fcfe6..8717222389d 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 94f25973735..406e0dd03e4 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 4f89ab47a0e..fc9b786e414 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java index c2df9723207..2786aa20375 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.interceptor.auth.*; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; @@ -192,6 +193,19 @@ public class AuthorizationInterceptors { }; //END SNIPPET: patchAll + + //START SNIPPET: bulkExport + new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().systemExport().withResourceTypes(Lists.newArrayList("Patient", "Encounter", "Observation")) + .build(); + } + }; + //END SNIPPET: bulkExport + + } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2712-add-bulk-export-auth.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2712-add-bulk-export-auth.yaml new file mode 100644 index 00000000000..3d00867f27f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_5_0/2712-add-bulk-export-auth.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 2712 +title: "AuthorizationInterceptor can now be used to authorize bulk export requests" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/authorization_interceptor.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/authorization_interceptor.md index 0c29e74adae..5bedf72266f 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/authorization_interceptor.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/authorization_interceptor.md @@ -23,7 +23,7 @@ The AuthorizationInterceptor is used by subclassing it and then registering your The AuthorizationInterceptor works by examining the client request in order to determine whether "write" operations are legal, and looks at the response from the server in order to determine whether "read" operations are legal. -## Authorizing Read Operations +# Authorizing Read Operations When authorizing a read operation, the AuthorizationInterceptor always allows client code to execute and generate a response. It then examines the response that would be returned before actually returning it to the client, and if rules do not permit that data to be shown to the client the interceptor aborts the request. @@ -33,7 +33,7 @@ See the following diagram for an example of how this works. Write Authorization -## Authorizing Write Operations +# Authorizing Write Operations Write operations (create, update, etc.) are typically authorized by the interceptor by examining the parsed URL and making a decision about whether to authorize the operation before allowing Resource Provider code to proceed. This means that client code will not have a chance to execute and create resources that the client does not have permissions to create. @@ -41,9 +41,10 @@ See the following diagram for an example of how this works. Write Authorization + -## Authorizing Sub-Operations +# Authorizing Sub-Operations There are a number of situations where the REST framework doesn't actually know exactly what operation is going to be performed by the implementing server code. For example, if your server implements a conditional update operation, the server might not know which resource is actually being updated until the server code is executed. @@ -55,7 +56,7 @@ In this type of situation, it is important to manually notify the interceptor ch {{snippet:classpath:/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java|conditionalUpdate}} ``` -## Authorizing Patch Operations +# Authorizing Patch Operations The FHIR [patch](http://hl7.org/fhir/http.html#patch) operation presents a challenge for authorization, as the incoming request often contains very little detail about what is being modified. @@ -67,7 +68,7 @@ This should be combined with server support for [Authorizing Sub-Operations](#au {{snippet:classpath:/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java|patchAll}} ``` -## Authorizing Multitenant Servers +# Authorizing Multitenant Servers The AuthorizationInterceptor has the ability to direct individual rules as only applying to a single tenant in a multitenant server. The following example shows such a rule. @@ -75,3 +76,10 @@ The AuthorizationInterceptor has the ability to direct individual rules as only {{snippet:classpath:/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java|authorizeTenantAction}} ``` +# Authorizing Bulk Export Operations + +AuthorizationInterceptor can be used to provide nuanced control over the kinds of Bulk Export operations that a user can initiate when using the JPA Server. + +```java +{{snippet:classpath:/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java|bulkExport}} +``` diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 1379062ae1c..4b590f0d7f1 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.4.0 + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 2023f64239a..7f99e6ec35d 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-example/pom.xml b/hapi-fhir-jaxrsserver-example/pom.xml index ab6f6dd8c1d..a73291f4460 100644 --- a/hapi-fhir-jaxrsserver-example/pom.xml +++ b/hapi-fhir-jaxrsserver-example/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-jpaserver-api/pom.xml b/hapi-fhir-jpaserver-api/pom.xml index 830d4984cec..a3c87a29df8 100644 --- a/hapi-fhir-jpaserver-api/pom.xml +++ b/hapi-fhir-jpaserver-api/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index aa4ac4a7099..99919a96721 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/IBulkDataExportSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/IBulkDataExportSvc.java index ba0eba4f7a9..548d9aa7b2d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/IBulkDataExportSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/IBulkDataExportSvc.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.bulk.export.api; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportJobStatusEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import org.hl7.fhir.instance.model.api.IIdType; import javax.transaction.Transactional; @@ -36,6 +37,10 @@ public interface IBulkDataExportSvc { @Transactional(value = Transactional.TxType.NEVER) void purgeExpiredFiles(); + /** + * Deprecated - Use {@link #submitJob(BulkDataExportOptions, Boolean, RequestDetails)} instead + */ + @Deprecated JobInfo submitJob(BulkDataExportOptions theBulkDataExportOptions); JobInfo submitJob(BulkDataExportOptions theBulkDataExportOptions, Boolean useCache, RequestDetails theRequestDetails); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/BulkExportJobParametersBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/BulkExportJobParametersBuilder.java index 881e7215620..e78fa183bf3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/BulkExportJobParametersBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/BulkExportJobParametersBuilder.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.bulk.export.job; * #L% */ -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.rest.api.Constants; import org.springframework.batch.core.JobParametersBuilder; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/CreateBulkExportEntityTasklet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/CreateBulkExportEntityTasklet.java index 3a14e3e1145..9c10bcecba8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/CreateBulkExportEntityTasklet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/job/CreateBulkExportEntityTasklet.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.bulk.export.job; * #L% */ -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java index 0eead025c85..7515fad98d7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/provider/BulkDataExportProvider.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.bulk.export.provider; */ import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportResponseJson; import ca.uhn.fhir.jpa.model.util.JpaConstants; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportSvcImpl.java index b3ea9f4b156..ab862ce44f2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportSvcImpl.java @@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; import ca.uhn.fhir.jpa.batch.BatchJobsConfig; import ca.uhn.fhir.jpa.batch.api.IBatchJobSubmitter; -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; import ca.uhn.fhir.jpa.bulk.export.job.BulkExportJobConfig; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportJobStatusEnum; @@ -52,6 +52,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -80,9 +81,9 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import static ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions.ExportStyle.GROUP; -import static ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions.ExportStyle.PATIENT; -import static ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions.ExportStyle.SYSTEM; +import static ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions.ExportStyle.GROUP; +import static ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions.ExportStyle.PATIENT; +import static ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions.ExportStyle.SYSTEM; import static ca.uhn.fhir.util.UrlUtil.escapeUrlParam; import static ca.uhn.fhir.util.UrlUtil.escapeUrlParams; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -299,6 +300,7 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc { @Transactional @Override + @Deprecated public JobInfo submitJob(BulkDataExportOptions theBulkDataExportOptions) { return submitJob(theBulkDataExportOptions, true, null); } @@ -310,13 +312,6 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc { @Override public JobInfo submitJob(BulkDataExportOptions theBulkDataExportOptions, Boolean useCache, RequestDetails theRequestDetails) { - // Interceptor call: STORAGE_INITIATE_BULK_EXPORT - HookParams params = new HookParams() - .add(BulkDataExportOptions.class, theBulkDataExportOptions) - .add(RequestDetails.class, theRequestDetails) - .addIfMatchesType(ServletRequestDetails.class, theRequestDetails); - myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_INITIATE_BULK_EXPORT, params); - String outputFormat = Constants.CT_FHIR_NDJSON; if (isNotBlank(theBulkDataExportOptions.getOutputFormat())) { outputFormat = theBulkDataExportOptions.getOutputFormat(); @@ -325,6 +320,13 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc { throw new InvalidRequestException("Invalid output format: " + theBulkDataExportOptions.getOutputFormat()); } + // Interceptor call: STORAGE_INITIATE_BULK_EXPORT + HookParams params = new HookParams() + .add(BulkDataExportOptions.class, theBulkDataExportOptions) + .add(RequestDetails.class, theRequestDetails) + .addIfMatchesType(ServletRequestDetails.class, theRequestDetails); + CompositeInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequestDetails, Pointcut.STORAGE_INITIATE_BULK_EXPORT, params); + // TODO GGG KS can we encode BulkDataExportOptions as a JSON string as opposed to this request string. Feels like it would be a more extensible encoding... //Probably yes, but this will all need to be rebuilt when we remove this bridge entity StringBuilder requestBuilder = new StringBuilder(); @@ -445,7 +447,9 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc { } private JobInfo toSubmittedJobInfo(BulkExportJobEntity theJob) { - return new JobInfo().setJobId(theJob.getJobId()); + return new JobInfo() + .setJobId(theJob.getJobId()) + .setStatus(theJob.getStatus()); } private void updateExpiry(BulkExportJobEntity theJob) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java index 2dd4e1869b1..d3ddc2ca6f1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.bulk; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportResponseJson; import ca.uhn.fhir.jpa.bulk.export.model.BulkExportJobStatusEnum; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java index a387fe93414..3c37637466f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.batch.BatchJobsConfig; import ca.uhn.fhir.jpa.batch.api.IBatchJobSubmitter; -import ca.uhn.fhir.jpa.bulk.export.api.BulkDataExportOptions; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; import ca.uhn.fhir.jpa.bulk.export.job.BulkExportJobParametersBuilder; import ca.uhn.fhir.jpa.bulk.export.job.GroupBulkExportJobParametersBuilder; @@ -47,7 +47,6 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatchers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java index ea0fef2d9f0..83a302dc40a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchIncludeTest.java @@ -105,6 +105,38 @@ public class FhirResourceDaoR4SearchIncludeTest extends BaseJpaR4Test { "Organization/ORG-P" )); + } + + @Test + public void testRevIncludesPagedSyncSearch() { + int eocCount = 10; +// myDaoConfig.setMaximumIncludesToLoadPerPage(5); + + createOrganizationWithReferencingEpisodesOfCare(eocCount); + + SearchParameterMap map = new SearchParameterMap() + .add("_id", new TokenParam("ORG-0")) + .addRevInclude(EpisodeOfCare.INCLUDE_ORGANIZATION); + myCaptureQueriesListener.clear(); + IBundleProvider results = myOrganizationDao.search(map); + List ids = toUnqualifiedVersionlessIdValues(results); + myCaptureQueriesListener.logSelectQueries(); + assertThat(ids.toString(), ids, containsInAnyOrder( + "EpisodeOfCare/EOC-0", + "EpisodeOfCare/EOC-1", + "EpisodeOfCare/EOC-2", + "EpisodeOfCare/EOC-3", + "EpisodeOfCare/EOC-4", + "EpisodeOfCare/EOC-5", + "EpisodeOfCare/EOC-6", + "EpisodeOfCare/EOC-7", + "EpisodeOfCare/EOC-8", + "EpisodeOfCare/EOC-9", + "Organization/ORG-0" + )); + + + } private void createOrganizationWithReferencingEpisodesOfCare(int theEocCount) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorJpaR4Test.java index 65c04e59553..5e9a121e813 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorJpaR4Test.java @@ -1,11 +1,15 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportSvc; +import ca.uhn.fhir.jpa.bulk.export.model.BulkExportJobStatusEnum; +import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; @@ -16,7 +20,10 @@ import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester; import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; +import com.github.jsonldjava.shaded.com.google.common.collect.Lists; +import com.google.common.collect.Sets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; @@ -41,10 +48,13 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpServletRequest; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -62,6 +72,9 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptorJpaR4Test.class); + @Autowired + private IBulkDataExportSvc myBulkDataExportSvc; + @BeforeEach @Override public void before() throws Exception { @@ -69,6 +82,293 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes myDaoConfig.setAllowMultipleDelete(true); myDaoConfig.setExpungeEnabled(true); myDaoConfig.setDeleteExpungeEnabled(true); + ourRestServer.registerInterceptor(new BulkDataExportProvider()); + } + + @Override + @AfterEach + public void after() throws Exception { + super.after(); + myInterceptorRegistry.unregisterInterceptorsIf(t -> t instanceof AuthorizationInterceptor); + } + + @Test + public void testBulkExport_AuthorizeGroupId() { + + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().groupExportOnGroup(new IdType("Group/123")).andThen() + .build(); + } + }; + myInterceptorRegistry.registerInterceptor(authInterceptor); + + /* + * Matching group ID + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/123")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.GROUP); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + /* + * Non matching group ID + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.GROUP); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + /* + * Non group export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + } + + + @Test + public void testBulkExport_AuthorizePatientId() { + + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().patientExportOnGroup(new IdType("Group/123")).andThen() + .build(); + } + }; + myInterceptorRegistry.registerInterceptor(authInterceptor); + + /* + * Matching group ID + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/123")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + /* + * Non matching group ID + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + /* + * Non group export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.GROUP); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + + } + + + @Test + public void testBulkExport_AuthorizeSystem() { + + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().systemExport().andThen() + .build(); + } + }; + myInterceptorRegistry.registerInterceptor(authInterceptor); + + /* + * System export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + /* + * Patient export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + + + } + + + + @Test + public void testBulkExport_AuthorizeAny() { + + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().any().andThen() + .build(); + } + }; + myInterceptorRegistry.registerInterceptor(authInterceptor); + + /* + * System export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + /* + * Patient export + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setGroupId(new IdType("Group/456")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.PATIENT); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + + + } + + @Test + public void testBulkExport_SpecificResourceTypesEnforced() { + + AuthorizationInterceptor authInterceptor = new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow().bulkExport().systemExport().withResourceTypes(Lists.newArrayList("Patient", "Encounter")).andThen() + .build(); + } + }; + myInterceptorRegistry.registerInterceptor(authInterceptor); + + /* + * Appropriate Resources + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setResourceTypes(Sets.newHashSet("Patient", "Encounter")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + assertEquals(BulkExportJobStatusEnum.SUBMITTED, jobDetails.getStatus()); + } + + /* + * Inappropriate Resources + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Observation")); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + /* + * No Resources + */ + { + BulkDataExportOptions bulkDataExportOptions = new BulkDataExportOptions(); + bulkDataExportOptions.setExportStyle(BulkDataExportOptions.ExportStyle.SYSTEM); + + try { + ServletRequestDetails requestDetails = new ServletRequestDetails().setServletRequest(new MockHttpServletRequest()); + myBulkDataExportSvc.submitJob(bulkDataExportOptions, true, requestDetails); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + + } /** diff --git a/hapi-fhir-jpaserver-batch/pom.xml b/hapi-fhir-jpaserver-batch/pom.xml index 6fa133d2c90..e4d12c73646 100644 --- a/hapi-fhir-jpaserver-batch/pom.xml +++ b/hapi-fhir-jpaserver-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index b7ca224a3eb..d9e7d6b07ae 100644 --- a/hapi-fhir-jpaserver-cql/pom.xml +++ b/hapi-fhir-jpaserver-cql/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 90c372d0aa3..5d524187303 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-migrate/pom.xml b/hapi-fhir-jpaserver-migrate/pom.xml index fca68e1b52a..3a54e854af4 100644 --- a/hapi-fhir-jpaserver-migrate/pom.xml +++ b/hapi-fhir-jpaserver-migrate/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 2da93d226a1..8a56de87060 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 6a21dc59dda..fc29d256123 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 7718e82bc7b..c79d5911ba1 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 8c18f964561..8faa3c62398 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index a96584b030f..c165310295d 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index f845cafbddc..f3976a4ccee 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index a7d693d1c30..82e23a46bdb 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 87ba4a9d2d5..4888f646fbb 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/BulkDataExportOptions.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/bulk/BulkDataExportOptions.java similarity index 96% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/BulkDataExportOptions.java rename to hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/bulk/BulkDataExportOptions.java index 4f50d6fed97..37539ebe079 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/api/BulkDataExportOptions.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/bulk/BulkDataExportOptions.java @@ -1,8 +1,8 @@ -package ca.uhn.fhir.jpa.bulk.export.api; +package ca.uhn.fhir.rest.api.server.bulk; /*- * #%L - * HAPI FHIR JPA Server + * HAPI FHIR - Server Framework * %% * Copyright (C) 2014 - 2021 Smile CDR, Inc. * %% diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index e38c3163d5e..cb9bb3422b8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor; import com.google.common.collect.Lists; @@ -68,6 +69,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @Interceptor public class AuthorizationInterceptor implements IRuleApplier { + public static final String REQUEST_ATTRIBUTE_BULK_DATA_EXPORT_OPTIONS = AuthorizationInterceptor.class.getName() + "_BulkDataExportOptions"; private static final AtomicInteger ourInstanceCount = new AtomicInteger(0); private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptor.class); private final int myInstanceIndex = ourInstanceCount.incrementAndGet(); @@ -351,6 +353,16 @@ public class AuthorizationInterceptor implements IRuleApplier { applyRulesAndFailIfDeny(theRequestDetails.getRestOperationType(), theRequestDetails, null, null, null, thePointcut); } + @Hook(Pointcut.STORAGE_INITIATE_BULK_EXPORT) + public void initiateBulkExport(RequestDetails theRequestDetails, BulkDataExportOptions theBulkExportOptions, Pointcut thePointcut) { + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.EXTENDED_OPERATION_SERVER; + if (theRequestDetails != null) { + theRequestDetails.setAttribute(REQUEST_ATTRIBUTE_BULK_DATA_EXPORT_OPTIONS, theBulkExportOptions); + } + applyRulesAndFailIfDeny(restOperationType, theRequestDetails, null, null, null, thePointcut); + } + + private void checkPointcutAndFailIfDeny(RequestDetails theRequestDetails, Pointcut thePointcut, @Nonnull IBaseResource theInputResource) { applyRulesAndFailIfDeny(theRequestDetails.getRestOperationType(), theRequestDetails, theInputResource, theInputResource.getIdElement(), null, thePointcut); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/BaseRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/BaseRule.java index 23900e8f62c..21f4c9880eb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/BaseRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/BaseRule.java @@ -29,6 +29,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java index 0b13f8c77ed..bb82be4a8a8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRule.java @@ -121,4 +121,10 @@ public interface IAuthRuleBuilderRule { */ IAuthRuleBuilderGraphQL graphQL(); + /** + * This rule permits the user to initiate a FHIR bulk export + * + * @since 5.5.0 + */ + IAuthRuleBuilderRuleBulkExport bulkExport(); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java new file mode 100644 index 00000000000..a617c95276e --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java @@ -0,0 +1,75 @@ +package ca.uhn.fhir.rest.server.interceptor.auth; + +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import org.hl7.fhir.instance.model.api.IIdType; + +import javax.annotation.Nonnull; + +/** + * @since 5.5.0 + */ +public interface IAuthRuleBuilderRuleBulkExport { + + /** + * Allow/deny group-level export rule applies to the Group with the given resource ID, e.g. Group/123 + * + * @since 5.5.0 + */ + default IAuthRuleBuilderRuleBulkExportWithTarget groupExportOnGroup(@Nonnull IIdType theFocusResourceId) { + return groupExportOnGroup(theFocusResourceId.getValue()); + } + + /** + * Allow/deny group-level export rule applies to the Group with the given resource ID, e.g. Group/123 + * + * @since 5.5.0 + */ + IAuthRuleBuilderRuleBulkExportWithTarget groupExportOnGroup(@Nonnull String theFocusResourceId); + + /** + * Allow/deny patient-level export rule applies to the Group with the given resource ID, e.g. Group/123 + * + * @since 5.5.0 + */ + default IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnGroup(@Nonnull IIdType theFocusResourceId) { + return patientExportOnGroup(theFocusResourceId.getValue()); + } + + /** + * Allow/deny patient-level export rule applies to the Group with the given resource ID, e.g. Group/123 + * + * @since 5.5.0 + */ + IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnGroup(@Nonnull String theFocusResourceId); + + /** + * Allow/deny system-level export rule applies to the Group with the given resource ID, e.g. Group/123 + * + * @since 5.5.0 + */ + IAuthRuleBuilderRuleBulkExportWithTarget systemExport(); + + /** + * Allow/deny any bulk export operation + */ + IAuthRuleBuilderRuleBulkExportWithTarget any(); +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExportWithTarget.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExportWithTarget.java new file mode 100644 index 00000000000..3e42d216b19 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExportWithTarget.java @@ -0,0 +1,41 @@ +package ca.uhn.fhir.rest.server.interceptor.auth; + +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.util.Collection; + +/** + * @since 5.5.0 + */ +public interface IAuthRuleBuilderRuleBulkExportWithTarget extends IAuthRuleFinished { + + /** + * If specified, rule will only apply to bulk export requests that explicitly specify a list + * of resource types where the list is equal to, or a subset of the supplied collection. + * + * This this method is not called, there will be no restriction on the resource types + * that a user can initiate a bulk export on. + * + * @since 5.5.0 + */ + IAuthRuleBuilderRuleBulkExportWithTarget withResourceTypes(Collection theResourceTypes); + +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java index a9d131791f0..5b0154a7eb3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -311,6 +312,11 @@ public class RuleBuilder implements IAuthRuleBuilder { return new RuleBuilderGraphQL(); } + @Override + public IAuthRuleBuilderRuleBulkExport bulkExport() { + return new RuleBuilderBulkExport(); + } + private class RuleBuilderRuleConditional implements IAuthRuleBuilderRuleConditional { private AppliesTypeEnum myAppliesTo; @@ -702,6 +708,65 @@ public class RuleBuilder implements IAuthRuleBuilder { return new RuleBuilderFinished(rule); } } + + private class RuleBuilderBulkExport implements IAuthRuleBuilderRuleBulkExport { + + @Override + public IAuthRuleBuilderRuleBulkExportWithTarget groupExportOnGroup(@Nonnull String theFocusResourceId) { + RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); + rule.setAppliesToGroupExportOnGroup(theFocusResourceId); + rule.setMode(myRuleMode); + myRules.add(rule); + + return new RuleBuilderBulkExportWithTarget(rule); + } + + @Override + public IAuthRuleBuilderRuleBulkExportWithTarget patientExportOnGroup(@Nonnull String theFocusResourceId) { + RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); + rule.setAppliesToPatientExportOnGroup(theFocusResourceId); + rule.setMode(myRuleMode); + myRules.add(rule); + + return new RuleBuilderBulkExportWithTarget(rule); + } + + @Override + public IAuthRuleBuilderRuleBulkExportWithTarget systemExport() { + RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); + rule.setAppliesToSystem(); + rule.setMode(myRuleMode); + myRules.add(rule); + + return new RuleBuilderBulkExportWithTarget(rule); + } + + @Override + public IAuthRuleBuilderRuleBulkExportWithTarget any() { + RuleBulkExportImpl rule = new RuleBulkExportImpl(myRuleName); + rule.setAppliesToAny(); + rule.setMode(myRuleMode); + myRules.add(rule); + + return new RuleBuilderBulkExportWithTarget(rule); + } + + private class RuleBuilderBulkExportWithTarget extends RuleBuilderFinished implements IAuthRuleBuilderRuleBulkExportWithTarget { + private final RuleBulkExportImpl myRule; + + private RuleBuilderBulkExportWithTarget(RuleBulkExportImpl theRule) { + super(theRule); + myRule = theRule; + + } + + @Override + public IAuthRuleBuilderRuleBulkExportWithTarget withResourceTypes(Collection theResourceTypes) { + myRule.setResourceTypes(theResourceTypes); + return this; + } + } + } } private static String toTypeName(Class theType) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBulkExportImpl.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBulkExportImpl.java new file mode 100644 index 00000000000..dea9885e9b6 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBulkExportImpl.java @@ -0,0 +1,110 @@ +package ca.uhn.fhir.rest.server.interceptor.auth; + +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.bulk.BulkDataExportOptions; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class RuleBulkExportImpl extends BaseRule { + private String myGroupId; + private BulkDataExportOptions.ExportStyle myWantExportStyle; + private Collection myResourceTypes; + private boolean myWantAnyStyle; + + RuleBulkExportImpl(String theRuleName) { + super(theRuleName); + } + + @Override + public AuthorizationInterceptor.Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier, Set theFlags, Pointcut thePointcut) { + if (thePointcut != Pointcut.STORAGE_INITIATE_BULK_EXPORT) { + return null; + } + + if (theRequestDetails == null) { + return null; + } + + BulkDataExportOptions options = (BulkDataExportOptions) theRequestDetails.getAttribute(AuthorizationInterceptor.REQUEST_ATTRIBUTE_BULK_DATA_EXPORT_OPTIONS); + + if (!myWantAnyStyle && options.getExportStyle() != myWantExportStyle) { + return null; + } + + if (myResourceTypes != null && !myResourceTypes.isEmpty()) { + if (options.getResourceTypes() == null) { + return null; + } + for (String next : options.getResourceTypes()) { + if (!myResourceTypes.contains(next)) { + return null; + } + } + } + + if (myWantAnyStyle || myWantExportStyle == BulkDataExportOptions.ExportStyle.SYSTEM) { + return newVerdict(theOperation, theRequestDetails, theInputResource, theInputResourceId, theOutputResource); + } + + if (isNotBlank(myGroupId) && options.getGroupId() != null) { + String expectedGroupId = new IdDt(myGroupId).toUnqualifiedVersionless().getValue(); + String actualGroupId = options.getGroupId().toUnqualifiedVersionless().getValue(); + if (Objects.equals(expectedGroupId, actualGroupId)) { + return newVerdict(theOperation, theRequestDetails, theInputResource, theInputResourceId, theOutputResource); + } + } + + return null; + } + + public void setAppliesToGroupExportOnGroup(String theGroupId) { + myWantExportStyle = BulkDataExportOptions.ExportStyle.GROUP; + myGroupId = theGroupId; + } + + public void setAppliesToPatientExportOnGroup(String theGroupId) { + myWantExportStyle = BulkDataExportOptions.ExportStyle.PATIENT; + myGroupId = theGroupId; + } + + public void setAppliesToSystem() { + myWantExportStyle = BulkDataExportOptions.ExportStyle.SYSTEM; + } + + public void setResourceTypes(Collection theResourceTypes) { + myResourceTypes = theResourceTypes; + } + + public void setAppliesToAny() { + myWantAnyStyle = true; + } +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java_703256810379985 b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java_703256810379985 deleted file mode 100644 index 3ede68f6b6a..00000000000 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java_703256810379985 +++ /dev/null @@ -1,278 +0,0 @@ -package ca.uhn.fhir.rest.server.interceptor.auth; - -/*- - * #%L - * HAPI FHIR - Server Framework - * %% - * Copyright (C) 2014 - 2020 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.interceptor.api.Hook; -import ca.uhn.fhir.interceptor.api.Pointcut; -import ca.uhn.fhir.rest.api.QualifiedParamList; -import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.ParameterUtil; -import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.method.BaseMethodBinding; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.rest.server.servlet.ServletSubRequestDetails; -import ca.uhn.fhir.rest.server.util.ServletRequestUtil; -import ca.uhn.fhir.util.BundleUtil; -import ca.uhn.fhir.util.bundle.ModifiableBundleEntry; -import com.google.common.collect.ArrayListMultimap; -import org.apache.commons.collections4.ListUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.*; -import java.util.function.Consumer; - -/** - * This interceptor can be used to automatically narrow the scope of searches in order to - * automatically restrict the searches to specific compartments. - *

- * For example, this interceptor - * could be used to restrict a user to only viewing data belonging to Patient/123 (i.e. data - * in the Patient/123 compartment). In this case, a user performing a search - * for
- * http://baseurl/Observation?category=laboratory
- * would receive results as though they had requested
- * http://baseurl/Observation?subject=Patient/123&category=laboratory - *

- *

- * Note that this interceptor should be used in combination with {@link AuthorizationInterceptor} - * if you are restricting results because of a security restriction. This interceptor is not - * intended to be a failsafe way of preventing users from seeing the wrong data (that is the - * purpose of AuthorizationInterceptor). This interceptor is simply intended as a convenience to - * help users simplify their queries while not receiving security errors for to trying to access - * data they do not have access to see. - *

- * - * @see AuthorizationInterceptor - */ -public class SearchNarrowingInterceptor { - private static final Logger ourLog = LoggerFactory.getLogger(SearchNarrowingInterceptor.class); - - - /** - * Subclasses should override this method to supply the set of compartments that - * the user making the request should actually have access to. - *

- * Typically this is done by examining theRequestDetails to find - * out who the current user is and then building a list of Strings. - *

- * - * @param theRequestDetails The individual request currently being applied - * @return The list of allowed compartments and instances that should be used - * for search narrowing. If this method returns null, no narrowing will - * be performed - */ - protected AuthorizedList buildAuthorizedList(@SuppressWarnings("unused") RequestDetails theRequestDetails) { - return null; - } - - @Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED) - public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException { - // We don't support this operation type yet - Validate.isTrue(theRequestDetails.getRestOperationType() != RestOperationTypeEnum.SEARCH_SYSTEM); - - if (theRequestDetails.getRestOperationType() != RestOperationTypeEnum.SEARCH_TYPE) { - return true; - } - - FhirContext ctx = theRequestDetails.getServer().getFhirContext(); - RuntimeResourceDefinition resDef = ctx.getResourceDefinition(theRequestDetails.getResourceName()); - HashMap> parameterToOrValues = new HashMap<>(); - AuthorizedList authorizedList = buildAuthorizedList(theRequestDetails); - if (authorizedList == null) { - return true; - } - - /* - * Create a map of search parameter values that need to be added to the - * given request - */ - Collection compartments = authorizedList.getAllowedCompartments(); - if (compartments != null) { - processResourcesOrCompartments(theRequestDetails, resDef, parameterToOrValues, compartments, true); - } - Collection resources = authorizedList.getAllowedInstances(); - if (resources != null) { - processResourcesOrCompartments(theRequestDetails, resDef, parameterToOrValues, resources, false); - } - - /* - * Add any param values to the actual request - */ - if (parameterToOrValues.size() > 0) { - Map newParameters = new HashMap<>(theRequestDetails.getParameters()); - for (Map.Entry> nextEntry : parameterToOrValues.entrySet()) { - String nextParamName = nextEntry.getKey(); - List nextAllowedValues = nextEntry.getValue(); - - if (!newParameters.containsKey(nextParamName)) { - - /* - * If we don't already have a parameter of the given type, add one - */ - String nextValuesJoined = ParameterUtil.escapeAndJoinOrList(nextAllowedValues); - String[] paramValues = {nextValuesJoined}; - newParameters.put(nextParamName, paramValues); - - } else { - - /* - * If the client explicitly requested the given parameter already, we'll - * just update the request to have the intersection of the values that the client - * requested, and the values that the user is allowed to see - */ - String[] existingValues = newParameters.get(nextParamName); - boolean restrictedExistingList = false; - for (int i = 0; i < existingValues.length; i++) { - - String nextExistingValue = existingValues[i]; - List nextRequestedValues = QualifiedParamList.splitQueryStringByCommasIgnoreEscape(null, nextExistingValue); - List nextPermittedValues = ListUtils.intersection(nextRequestedValues, nextAllowedValues); - if (nextPermittedValues.size() > 0) { - restrictedExistingList = true; - existingValues[i] = ParameterUtil.escapeAndJoinOrList(nextPermittedValues); - } - - } - - /* - * If none of the values that were requested by the client overlap at all - * with the values that the user is allowed to see, we'll just add the permitted - * list as a new list. Ultimately this scenario actually means that the client - * shouldn't get *any* results back, and adding a new AND parameter (that doesn't - * overlap at all with the others) is one way of ensuring that. - */ - if (!restrictedExistingList) { - String[] newValues = Arrays.copyOf(existingValues, existingValues.length + 1); - newValues[existingValues.length] = ParameterUtil.escapeAndJoinOrList(nextAllowedValues); - newParameters.put(nextParamName, newValues); - } - } - - } - theRequestDetails.setParameters(newParameters); - } - - return true; - } - - @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED) - public void incomingRequestPreHandled(ServletRequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException { - if (theRequestDetails.getRestOperationType() != RestOperationTypeEnum.TRANSACTION) { - return; - } - - IBaseBundle bundle = (IBaseBundle) theRequestDetails.getResource(); - FhirContext ctx = theRequestDetails.getFhirContext(); - BundleEntryUrlProcessor processor = new BundleEntryUrlProcessor(ctx, theRequestDetails, theRequest, theResponse); - BundleUtil.processEntries(ctx, bundle, processor); - } - - private class BundleEntryUrlProcessor implements Consumer { - private final FhirContext myFhirContext; - private final ServletRequestDetails myRequestDetails; - private final HttpServletRequest myRequest; - private final HttpServletResponse myResponse; - - public BundleEntryUrlProcessor(FhirContext theFhirContext, ServletRequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) { - myFhirContext = theFhirContext; - myRequestDetails = theRequestDetails; - myRequest = theRequest; - myResponse = theResponse; - } - - @Override - public void accept(ModifiableBundleEntry theModifiableBundleEntry) { - ArrayListMultimap paramValues = ArrayListMultimap.create(); - - String url = theModifiableBundleEntry.getRequestUrl(); - - ServletSubRequestDetails subServletRequestDetails = ServletRequestUtil.getServletSubRequestDetails(myRequestDetails, url, paramValues); - BaseMethodBinding method = subServletRequestDetails.getServer().determineResourceMethod(subServletRequestDetails, url); - RestOperationTypeEnum restOperationType = method.getRestOperationType(); - subServletRequestDetails.setRestOperationType(restOperationType); - - incomingRequestPostProcessed(subServletRequestDetails, myRequest, myResponse); - - theModifiableBundleEntry.setRequestUrl(myFhirContext, ServletRequestUtil.extractUrl(subServletRequestDetails)); - } - } - - private void processResourcesOrCompartments(RequestDetails theRequestDetails, RuntimeResourceDefinition theResDef, HashMap> theParameterToOrValues, Collection theResourcesOrCompartments, boolean theAreCompartments) { - String lastCompartmentName = null; - String lastSearchParamName = null; - for (String nextCompartment : theResourcesOrCompartments) { - Validate.isTrue(StringUtils.countMatches(nextCompartment, '/') == 1, "Invalid compartment name (must be in form \"ResourceType/xxx\": %s", nextCompartment); - String compartmentName = nextCompartment.substring(0, nextCompartment.indexOf('/')); - - String searchParamName = null; - if (compartmentName.equalsIgnoreCase(lastCompartmentName)) { - - // Avoid doing a lookup for the same thing repeatedly - searchParamName = lastSearchParamName; - - } else { - - if (compartmentName.equalsIgnoreCase(theRequestDetails.getResourceName())) { - - searchParamName = "_id"; - - } else if (theAreCompartments) { - - List searchParams = theResDef.getSearchParamsForCompartmentName(compartmentName); - if (searchParams.size() > 0) { - - // Resources like Observation have several fields that add the resource to - // the compartment. In the case of Observation, it's subject, patient and performer. - // For this kind of thing, we'll prefer the one called "patient". - RuntimeSearchParam searchParam = - searchParams - .stream() - .filter(t -> t.getName().equalsIgnoreCase(compartmentName)) - .findFirst() - .orElse(searchParams.get(0)); - searchParamName = searchParam.getName(); - - } - } - - lastCompartmentName = compartmentName; - lastSearchParamName = searchParamName; - - } - - if (searchParamName != null) { - List orValues = theParameterToOrValues.computeIfAbsent(searchParamName, t -> new ArrayList<>()); - orValues.add(nextCompartment); - } - } - } - -} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index 8d90901b141..f4598b294d0 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -170,8 +170,9 @@ public class ServletRequestDetails extends RequestDetails { this.myServer = theServer; } - public void setServletRequest(HttpServletRequest myServletRequest) { + public ServletRequestDetails setServletRequest(HttpServletRequest myServletRequest) { this.myServletRequest = myServletRequest; + return this; } public void setServletResponse(HttpServletResponse myServletResponse) { diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index bfea5f3d479..f285a477c43 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 8f7f3137d4f..124da03bd6e 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index cd8128ce6b9..257a4961dbc 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 05de9259624..47a0b93571f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 60862d9254f..45b3b46cd46 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index ca0c46036e6..25b70dba51f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 97af4063644..0775b18ecce 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 6fb0070529c..830d229f8de 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index bfc9539faaf..94bcf37fc10 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 2cc4b879e82..10039ffdffc 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index efcf2dcafeb..b23232274d7 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index cf2b45ed0fc..dfce5a3abe4 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 9a6d34dbd2e..9090a3b2a58 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 540bfaf21dc..27d6a374d17 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 94a6cfa1d83..52a2de32a25 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index ec9e0c962f6..17c700593cd 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 569a5ff2911..44505558af6 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 62c427d12ae..6befb4fc67d 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index f2bd4f9bf9a..e06ee27e91d 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index f6f35ae631e..ed2d29adbde 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 68f1f6e2c97..b13dd6d025f 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 46238460a66..b05cc427088 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 037865e9504..45958f4ce29 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index e86b74b08a2..0389868aa73 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/restful-server-example/pom.xml b/restful-server-example/pom.xml index 0f26df131b1..887a9e46b58 100644 --- a/restful-server-example/pom.xml +++ b/restful-server-example/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../pom.xml diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 450eaad555f..11186536ee3 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 3a31c0c5b0f..b3f2eed945c 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index d83f6cf9097..03c4834d478 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.5.0-PRE1-SNAPSHOT + 5.5.0-PRE2-SNAPSHOT ../../pom.xml