From cc0e836680797cdc7cc40d8a3a5baef16f45ac5a Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 10 Jun 2018 17:48:20 -0400 Subject: [PATCH] Auth interceptor early checks (#995) * Validator fix * Enhance AuthorizationInterceptor so that it tries to deny access earlier for compartment searches that are outside the allowable compartment. * FIx DSTU2 test that wasn't updated * More test fixes --- ...nInterceptorResourceProviderDstu3Test.java | 4 +- ...tionInterceptorResourceProviderR4Test.java | 4 +- .../auth/AuthorizationFlagsEnum.java | 35 + .../auth/AuthorizationInterceptor.java | 60 +- .../server/interceptor/auth/IAuthRule.java | 12 +- .../interceptor/auth/OperationRule.java | 55 +- .../server/interceptor/auth/RuleBuilder.java | 2 +- .../interceptor/auth/RuleImplConditional.java | 2 +- .../server/interceptor/auth/RuleImplOp.java | 595 +++-- .../AuthorizationInterceptorDstu2Test.java | 68 +- ...tionalRequestHeadersInterceptorR4Test.java | 2202 ----------------- .../AuthorizationInterceptorR4Test.java | 383 ++- .../fhir/r4/validation/InstanceValidator.java | 2 +- .../FhirInstanceValidatorR4Test.java | 9 + .../test/resources/r4/r4-caredove-bundle.json | 815 ++++++ src/changes/changes.xml | 24 + 16 files changed, 1680 insertions(+), 2592 deletions(-) create mode 100644 hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationFlagsEnum.java delete mode 100644 hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AdditionalRequestHeadersInterceptorR4Test.java create mode 100644 hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java index 353d2d5dca5..b0c365e905f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java @@ -47,7 +47,7 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou * See #778 */ @Test - public void testReadingObservationAccessRight() throws IOException { + public void testReadingObservationAccessRight() { Practitioner practitioner1 = new Practitioner(); final IIdType practitionerId1 = ourClient.create().resource(practitioner1).execute().getId().toUnqualifiedVersionless(); @@ -105,7 +105,7 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou * See #667 */ @Test - public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException { + public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() { Patient pt1 = new Patient(); pt1.setActive(true); final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java index d95368bd101..62231f1bc5b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java @@ -44,7 +44,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource * See #778 */ @Test - public void testReadingObservationAccessRight() throws IOException { + public void testReadingObservationAccessRight() { Practitioner practitioner1 = new Practitioner(); final IIdType practitionerId1 = myClient.create().resource(practitioner1).execute().getId().toUnqualifiedVersionless(); @@ -102,7 +102,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource * See #667 */ @Test - public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException { + public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() { Patient pt1 = new Patient(); pt1.setActive(true); final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationFlagsEnum.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationFlagsEnum.java new file mode 100644 index 00000000000..c1aa44f6eb3 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationFlagsEnum.java @@ -0,0 +1,35 @@ +package ca.uhn.fhir.rest.server.interceptor.auth; + +import java.util.Collection; + +/** + * @see AuthorizationInterceptor#setFlags(Collection) + */ +public enum AuthorizationFlagsEnum { + + /** + * If this flag is set, attempts to perform read operations + * (read/search/history) will be matched by the interceptor before + * the method handler is called. + *

+ * For example, suppose a rule set is in place that only allows read + * access to compartment Patient/123. With this flag set, + * any attempts + * to perform a FHIR read/search/history operation will be permitted + * to proceed to the method handler, and responses will be blocked + * by the AuthorizationInterceptor if the response contains a resource + * that is not in the given compartment. + *

+ *

+ * Setting this flag is less secure, since the interceptor can potentially leak + * information about the existence of data, but it is useful in some + * scenarios. + *

+ * + * @since This flag has existed since HAPI FHIR 3.5.0. Prior to this + * version, this flag was the default and there was no ability to + * proactively block compartment read access. + */ + NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS; + +} 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 9fd68eb1eb6..bf9c88d826f 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 @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.interceptor.auth; * 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. @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter; import ca.uhn.fhir.util.CoverageIgnore; +import com.google.common.collect.Lists; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -35,12 +36,12 @@ import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import static org.apache.commons.lang3.StringUtils.defaultString; @@ -56,9 +57,10 @@ import static org.apache.commons.lang3.StringUtils.defaultString; */ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter implements IRuleApplier { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AuthorizationInterceptor.class); + private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptor.class); private PolicyEnum myDefaultPolicy = PolicyEnum.DENY; + private Set myFlags = Collections.emptySet(); /** * Constructor @@ -92,11 +94,12 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter public Verdict applyRulesAndReturnDecision(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource) { List rules = buildRuleList(theRequestDetails); + Set flags = getFlags(); ourLog.trace("Applying {} rules to render an auth decision for operation {}", rules.size(), theOperation); Verdict verdict = null; for (IAuthRule nextRule : rules) { - verdict = nextRule.applyRule(theOperation, theRequestDetails, theInputResource, theInputResourceId, theOutputResource, this); + verdict = nextRule.applyRule(theOperation, theRequestDetails, theInputResource, theInputResourceId, theOutputResource, this, flags); if (verdict != null) { ourLog.trace("Rule {} returned decision {}", nextRule, verdict.getDecision()); break; @@ -105,7 +108,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter if (verdict == null) { ourLog.trace("No rules returned a decision, applying default {}", myDefaultPolicy); - return new Verdict(myDefaultPolicy, null); + return new Verdict(getDefaultPolicy(), null); } return verdict; @@ -206,6 +209,28 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter myDefaultPolicy = theDefaultPolicy; } + /** + * This property configures any flags affecting how authorization is + * applied. By default no flags are applied. + * + * @see #setFlags(Collection) + */ + public Set getFlags() { + return Collections.unmodifiableSet(myFlags); + } + + /** + * This property configures any flags affecting how authorization is + * applied. By default no flags are applied. + * + * @param theFlags The flags (must not be null) + * @see #setFlags(Collection) + */ + public AuthorizationInterceptor setFlags(AuthorizationFlagsEnum... theFlags) { + Validate.notNull(theFlags, "theFlags must not be null"); + return setFlags(Lists.newArrayList(theFlags)); + } + /** * Handle an access control verdict of {@link PolicyEnum#DENY}. *

@@ -325,6 +350,19 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter handleUserOperation(theRequest, theNewResource, RestOperationTypeEnum.UPDATE); } + /** + * This property configures any flags affecting how authorization is + * applied. By default no flags are applied. + * + * @param theFlags The flags (must not be null) + * @see #setFlags(AuthorizationFlagsEnum...) + */ + public AuthorizationInterceptor setFlags(Collection theFlags) { + Validate.notNull(theFlags, "theFlags must not be null"); + myFlags = new HashSet<>(theFlags); + return this; + } + private static UnsupportedOperationException failForDstu1() { return new UnsupportedOperationException("Use of this interceptor on DSTU1 servers is not supportd"); } @@ -333,7 +371,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter if (theResponseObject == null) { return Collections.emptyList(); } - + List retVal; boolean isContainer = false; @@ -342,11 +380,11 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter } else if (theResponseObject instanceof IBaseParameters) { isContainer = true; } - + if (!isContainer) { return Collections.singletonList(theResponseObject); } - + retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class); // Exclude the container diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRule.java index 9c6cf5db261..8f19e384c5a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRule.java @@ -27,6 +27,15 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import java.util.Set; + +/** + * Note: At this time, this interface is considered internal API to HAPI FHIR, + * and is subject to change without warning. Create your own implementations at + * your own risk. If you have use cases that are not met by the current + * implementation, please consider raising them on the HAPI FHIR + * Google Group. + */ public interface IAuthRule { /** @@ -44,9 +53,10 @@ public interface IAuthRule { * @param theRuleApplier * The rule applying module (this can be used by rules to apply the rule set to * nested objects in the request, such as nested requests in a transaction) + * @param theFlags * @return Returns a policy decision, or null if the rule does not apply */ - Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier); + Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier, Set theFlags); /** * Returns a name for this rule, to be used in logs and error messages diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java index bd80964f966..e9571bb555d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java @@ -29,10 +29,11 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.util.HashSet; import java.util.List; +import java.util.Set; class OperationRule extends BaseRule implements IAuthRule { - private RuleBuilder.ITenantApplicabilityChecker myTenentApplicabilityChecker; + private RuleBuilder.ITenantApplicabilityChecker myTenantApplicabilityChecker; private String myOperationName; private boolean myAppliesToServer; private HashSet> myAppliesToTypes; @@ -75,17 +76,25 @@ class OperationRule extends BaseRule implements IAuthRule { } @Override - public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier) { + public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier, Set theFlags) { FhirContext ctx = theRequestDetails.getServer().getFhirContext(); - if (myTenentApplicabilityChecker != null) { - if (!myTenentApplicabilityChecker.applies(theRequestDetails)) { + if (myTenantApplicabilityChecker != null) { + if (!myTenantApplicabilityChecker.applies(theRequestDetails)) { return null; } } boolean applies = false; switch (theOperation) { + case ADD_TAGS: + case DELETE_TAGS: + case GET_TAGS: + case GET_PAGE: + case GRAPHQL_REQUEST: + // These things can't be tracked by the AuthorizationInterceptor + // at this time + return null; case EXTENDED_OPERATION_SERVER: if (myAppliesToServer || myAppliesAtAnyLevel) { applies = true; @@ -130,6 +139,40 @@ class OperationRule extends BaseRule implements IAuthRule { } } break; + case CREATE: + break; + case DELETE: + break; + case HISTORY_INSTANCE: + break; + case HISTORY_SYSTEM: + break; + case HISTORY_TYPE: + break; + case READ: + break; + case SEARCH_SYSTEM: + break; + case SEARCH_TYPE: + break; + case TRANSACTION: + break; + case UPDATE: + break; + case VALIDATE: + break; + case VREAD: + break; + case METADATA: + break; + case META_ADD: + break; + case META: + break; + case META_DELETE: + break; + case PATCH: + break; default: return null; } @@ -160,8 +203,8 @@ class OperationRule extends BaseRule implements IAuthRule { myOperationName = theOperationName; } - public void setTenentApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenentApplicabilityChecker) { - myTenentApplicabilityChecker = theTenentApplicabilityChecker; + public void setTenantApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenantApplicabilityChecker) { + myTenantApplicabilityChecker = theTenantApplicabilityChecker; } } 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 052784c9a91..81d523e1e0c 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 @@ -174,7 +174,7 @@ public class RuleBuilder implements IAuthRuleBuilder { myOpRule.setTenantApplicabilityChecker(myTenantApplicabilityChecker); } if (myOperationRule != null) { - myOperationRule.setTenentApplicabilityChecker(myTenantApplicabilityChecker); + myOperationRule.setTenantApplicabilityChecker(myTenantApplicabilityChecker); } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java index 60695803f6f..7b9ac8140d6 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplConditional.java @@ -41,7 +41,7 @@ public class RuleImplConditional extends BaseRule implements IAuthRule { @Override public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, - IRuleApplier theRuleApplier) { + IRuleApplier theRuleApplier, Set theFlags) { if (theInputResourceId != null) { return null; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java index 6605793bbaa..327f5d10584 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java @@ -1,5 +1,28 @@ package ca.uhn.fhir.rest.server.interceptor.auth; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.RuntimeSearchParam; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import ca.uhn.fhir.util.BundleUtil; +import ca.uhn.fhir.util.BundleUtil.BundleEntryParts; +import ca.uhn.fhir.util.FhirTerser; +import org.apache.commons.codec.binary.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -11,9 +34,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * 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. @@ -22,26 +45,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * #L% */ -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; -import org.hl7.fhir.instance.model.api.IBaseBundle; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeResourceDefinition; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; -import ca.uhn.fhir.util.BundleUtil; -import ca.uhn.fhir.util.BundleUtil.BundleEntryParts; -import ca.uhn.fhir.util.FhirTerser; - class RuleImplOp extends BaseRule /* implements IAuthRule */ { private AppliesTypeEnum myAppliesTo; @@ -54,13 +57,16 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { private List myAppliesToInstances; private RuleBuilder.ITenantApplicabilityChecker myTenantApplicabilityChecker; + /** + * Constructor + */ public RuleImplOp(String theRuleName) { super(theRuleName); } @Override public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, - IRuleApplier theRuleApplier) { + IRuleApplier theRuleApplier, Set theFlags) { if (myTenantApplicabilityChecker != null) { if (!myTenantApplicabilityChecker.applies(theRequestDetails)) { @@ -73,232 +79,327 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { IBaseResource appliesToResource; IIdType appliesToResourceId = null; String appliesToResourceType = null; + Map appliesToSearchParams = null; switch (myOp) { - case READ: - if (theOutputResource == null) { - switch (theOperation) { - case READ: - case VREAD: - appliesToResourceId = theInputResourceId; - appliesToResourceType = theInputResourceId.getResourceType(); - break; - case SEARCH_SYSTEM: - case SEARCH_TYPE: - case HISTORY_INSTANCE: - case HISTORY_SYSTEM: - case HISTORY_TYPE: + case READ: + if (theOutputResource == null) { if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { return null; } - return new Verdict(PolicyEnum.ALLOW, this); - default: - return null; + + switch (theOperation) { + case READ: + case VREAD: + appliesToResourceId = theInputResourceId; + appliesToResourceType = theInputResourceId.getResourceType(); + break; + case SEARCH_SYSTEM: + case HISTORY_SYSTEM: + if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) { + return new Verdict(PolicyEnum.ALLOW, this); + } + break; + case SEARCH_TYPE: + if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) { + return new Verdict(PolicyEnum.ALLOW, this); + } + appliesToResourceType = theRequestDetails.getResourceName(); + appliesToSearchParams = theRequestDetails.getParameters(); + break; + case HISTORY_TYPE: + if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) { + return new Verdict(PolicyEnum.ALLOW, this); + } + appliesToResourceType = theRequestDetails.getResourceName(); + break; + case HISTORY_INSTANCE: + if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) { + return new Verdict(PolicyEnum.ALLOW, this); + } + appliesToResourceId = theInputResourceId; + break; + case GET_PAGE: + return new Verdict(PolicyEnum.ALLOW, this); + + // None of the following are checked on the way in + case ADD_TAGS: + case DELETE_TAGS: + case GET_TAGS: + case GRAPHQL_REQUEST: + case EXTENDED_OPERATION_SERVER: + case EXTENDED_OPERATION_TYPE: + case EXTENDED_OPERATION_INSTANCE: + case CREATE: + case DELETE: + case TRANSACTION: + case UPDATE: + case VALIDATE: + case METADATA: + case META_ADD: + case META: + case META_DELETE: + case PATCH: + default: + return null; + } + } + appliesToResource = theOutputResource; + if (theOutputResource != null) { + appliesToResourceId = theOutputResource.getIdElement(); } - } - appliesToResource = theOutputResource; - if (theOutputResource != null) { - appliesToResourceId = theOutputResource.getIdElement(); - } - break; - case WRITE: - if (theInputResource == null && theInputResourceId == null) { - return null; - } - switch (theOperation) { - case CREATE: - case UPDATE: - case ADD_TAGS: - case DELETE_TAGS: - case META_ADD: - case META_DELETE: - case PATCH: - appliesToResource = theInputResource; - appliesToResourceId = theInputResourceId; break; - default: - return null; - } - break; - case DELETE: - if (theOperation == RestOperationTypeEnum.DELETE) { - if (theInputResource == null) { - return newVerdict(); - } - appliesToResource = theInputResource; - } else { - return null; - } - break; - case BATCH: - case TRANSACTION: - if (!(theOperation == RestOperationTypeEnum.TRANSACTION)) { - return null; - } - if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) { - if (getMode() == PolicyEnum.DENY) { - return new Verdict(PolicyEnum.DENY, this); - } - List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); - Verdict verdict = null; - for (BundleEntryParts nextPart : inputResources) { - - IBaseResource inputResource = nextPart.getResource(); - RestOperationTypeEnum operation = null; - if (nextPart.getRequestType() == RequestTypeEnum.GET) { - continue; - } - if (nextPart.getRequestType() == RequestTypeEnum.POST) { - operation = RestOperationTypeEnum.CREATE; - } else if (nextPart.getRequestType() == RequestTypeEnum.PUT) { - operation = RestOperationTypeEnum.UPDATE; - } else { - throw new InvalidRequestException("Can not handle transaction with operation of type " + nextPart.getRequestType()); - } - - /* - * This is basically just being conservative - Be careful of transactions containing - * nested operations and nested transactions. We block the by default. At some point - * it would be nice to be more nuanced here. - */ - RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource()); - if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { - throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); - } - - Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null, null); - if (newVerdict == null) { - continue; - } else if (verdict == null) { - verdict = newVerdict; - } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { - verdict = newVerdict; - } - } - return verdict; - } else if (theOutputResource != null) { - - List outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(theOutputResource, theRequestDetails.getFhirContext()); - - Verdict verdict = null; - for (IBaseResource nextResource : outputResources) { - if (nextResource == null) { - continue; - } - Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource); - if (newVerdict == null) { - continue; - } else if (verdict == null) { - verdict = newVerdict; - } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { - verdict = newVerdict; - } - } - return verdict; - } else { - return null; - } - case ALLOW_ALL: - if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { - return null; - } - return new Verdict(PolicyEnum.ALLOW, this); - case DENY_ALL: - if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { - return null; - } - return new Verdict(PolicyEnum.DENY, this); - case METADATA: - if (theOperation == RestOperationTypeEnum.METADATA) { - if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { + case WRITE: + if (theInputResource == null && theInputResourceId == null) { return null; } - return newVerdict(); - } - return null; - default: - // Should not happen - throw new IllegalStateException("Unable to apply security to event of type " + theOperation); - } + switch (theOperation) { + case CREATE: + case UPDATE: + case ADD_TAGS: + case DELETE_TAGS: + case META_ADD: + case META_DELETE: + case PATCH: + appliesToResource = theInputResource; + appliesToResourceId = theInputResourceId; + break; + default: + return null; + } + break; + case DELETE: + if (theOperation == RestOperationTypeEnum.DELETE) { + if (theInputResource == null) { + return newVerdict(); + } + appliesToResource = theInputResource; + } else { + return null; + } + break; + case BATCH: + case TRANSACTION: + if (!(theOperation == RestOperationTypeEnum.TRANSACTION)) { + return null; + } + if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) { + if (getMode() == PolicyEnum.DENY) { + return new Verdict(PolicyEnum.DENY, this); + } + List inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource); + Verdict verdict = null; + for (BundleEntryParts nextPart : inputResources) { - switch (myAppliesTo) { - case INSTANCES: - if (appliesToResourceId != null) { - for (IIdType next : myAppliesToInstances) { - if (isNotBlank(next.getResourceType())) { - if (!next.getResourceType().equals(appliesToResourceId.getResourceType())) { + IBaseResource inputResource = nextPart.getResource(); + RestOperationTypeEnum operation = null; + if (nextPart.getRequestType() == RequestTypeEnum.GET) { continue; } + if (nextPart.getRequestType() == RequestTypeEnum.POST) { + operation = RestOperationTypeEnum.CREATE; + } else if (nextPart.getRequestType() == RequestTypeEnum.PUT) { + operation = RestOperationTypeEnum.UPDATE; + } else { + throw new InvalidRequestException("Can not handle transaction with operation of type " + nextPart.getRequestType()); + } + + /* + * This is basically just being conservative - Be careful of transactions containing + * nested operations and nested transactions. We block the by default. At some point + * it would be nice to be more nuanced here. + */ + RuntimeResourceDefinition resourceDef = ctx.getResourceDefinition(nextPart.getResource()); + if ("Parameters".equals(resourceDef.getName()) || "Bundle".equals(resourceDef.getName())) { + throw new InvalidRequestException("Can not handle transaction with nested resource of type " + resourceDef.getName()); + } + + Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null, null); + if (newVerdict == null) { + continue; + } else if (verdict == null) { + verdict = newVerdict; + } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { + verdict = newVerdict; + } } - if (!next.getIdPart().equals(appliesToResourceId.getIdPart())) { - continue; + return verdict; + } else if (theOutputResource != null) { + + List outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(theOutputResource, theRequestDetails.getFhirContext()); + + Verdict verdict = null; + for (IBaseResource nextResource : outputResources) { + if (nextResource == null) { + continue; + } + Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource); + if (newVerdict == null) { + continue; + } else if (verdict == null) { + verdict = newVerdict; + } else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) { + verdict = newVerdict; + } } - if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { - return null; - } - return newVerdict(); + return verdict; + } else { + return null; } - } - return null; - case ALL_RESOURCES: - if (appliesToResourceType != null) { + case ALLOW_ALL: if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { return null; } return new Verdict(PolicyEnum.ALLOW, this); - } - break; - case TYPES: - if (appliesToResource != null) { - if (myAppliesToTypes.contains(appliesToResource.getClass()) == false) { + case DENY_ALL: + if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { return null; } - } - if (appliesToResourceId != null && appliesToResourceId.hasResourceType()) { - Class type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceId.getResourceType()).getImplementingClass(); - if (myAppliesToTypes.contains(type) == false) { - return null; - } - } - if (appliesToResourceType != null) { - Class type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceType).getImplementingClass(); - if (myAppliesToTypes.contains(type)) { + return new Verdict(PolicyEnum.DENY, this); + case METADATA: + if (theOperation == RestOperationTypeEnum.METADATA) { if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { return null; } - return new Verdict(PolicyEnum.ALLOW, this); + return newVerdict(); } - } - break; - default: - throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); + return null; + default: + // Should not happen + throw new IllegalStateException("Unable to apply security to event of type " + theOperation); + } + + switch (myAppliesTo) { + case INSTANCES: + if (appliesToResourceId != null) { + for (IIdType next : myAppliesToInstances) { + if (isNotBlank(next.getResourceType())) { + if (!next.getResourceType().equals(appliesToResourceId.getResourceType())) { + continue; + } + } + if (!next.getIdPart().equals(appliesToResourceId.getIdPart())) { + continue; + } + if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { + return null; + } + return newVerdict(); + } + } + return null; + case ALL_RESOURCES: + if (appliesToResourceType != null) { + if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { + return null; + } + if (myClassifierType == ClassifierTypeEnum.ANY_ID) { + return new Verdict(PolicyEnum.ALLOW, this); + } + } + break; + case TYPES: + if (appliesToResource != null) { + if (myClassifierType == ClassifierTypeEnum.ANY_ID) { + if (myAppliesToTypes.contains(appliesToResource.getClass()) == false) { + return null; + } + } + } +// if (myClassifierType == ClassifierTypeEnum.ANY_ID) { + if (appliesToResourceId != null && appliesToResourceId.hasResourceType()) { + Class type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceId.getResourceType()).getImplementingClass(); + if (myAppliesToTypes.contains(type) == false) { + return null; + } + } +// } + if (appliesToResourceType != null) { + Class type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceType).getImplementingClass(); + if (myAppliesToTypes.contains(type)) { + if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { + return null; + } + if (myClassifierType == ClassifierTypeEnum.ANY_ID) { + return new Verdict(PolicyEnum.ALLOW, this); + } else if (myClassifierType == ClassifierTypeEnum.IN_COMPARTMENT) { + // ok we'll check below + } + } + } + break; + default: + throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); } switch (myClassifierType) { - case ANY_ID: - break; - case IN_COMPARTMENT: - FhirTerser t = ctx.newTerser(); - boolean foundMatch = false; - for (IIdType next : myClassifierCompartmentOwners) { - if (appliesToResource != null) { - if (t.isSourceInCompartmentForTarget(myClassifierCompartmentName, appliesToResource, next)) { - foundMatch = true; - break; + case ANY_ID: + break; + case IN_COMPARTMENT: + FhirTerser t = ctx.newTerser(); + boolean foundMatch = false; + for (IIdType next : myClassifierCompartmentOwners) { + if (appliesToResource != null) { + if (t.isSourceInCompartmentForTarget(myClassifierCompartmentName, appliesToResource, next)) { + foundMatch = true; + break; + } + } + if (appliesToResourceId != null && appliesToResourceId.hasResourceType() && appliesToResourceId.hasIdPart()) { + if (appliesToResourceId.toUnqualifiedVersionless().getValue().equals(next.toUnqualifiedVersionless().getValue())) { + foundMatch = true; + break; + } + } + + /* + * If we're trying to read a resource that could potentially be + * in the given compartment, we'll let the request through and + * catch any issues on the response. + * + * This is less than perfect, but it's the best we can do- + * If the user is allowed to see compartment "Patient/123" and + * the client is requesting to read a CarePlan, there is nothing + * in the request URL that indicates whether or not the CarePlan + * might be in the given compartment. + */ + if (isNotBlank(appliesToResourceType)) { + RuntimeResourceDefinition sourceDef = theRequestDetails.getFhirContext().getResourceDefinition(appliesToResourceType); + String compartmentOwnerResourceType = next.getResourceType(); + if (!StringUtils.equals(appliesToResourceType, compartmentOwnerResourceType)) { + List params = sourceDef.getSearchParamsForCompartmentName(compartmentOwnerResourceType); + if (params.isEmpty() == false) { + + /* + * If this is a search, we can at least check whether + * the client has requested a search parameter that + * would match the given compartment. In this case, this + * is a very effective mechanism. + */ + if (appliesToSearchParams != null && !theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) { + for (RuntimeSearchParam nextRuntimeSearchParam : params) { + String[] values = appliesToSearchParams.get(nextRuntimeSearchParam.getName()); + if (values != null) { + for (String nextParameterValue : values) { + if (nextParameterValue.equals(next.getValue())) { + return new Verdict(PolicyEnum.ALLOW, this); + } + } + } + } + } else { + return new Verdict(PolicyEnum.ALLOW, this); + } + break; + } + } } } - if (appliesToResourceId != null && appliesToResourceId.hasResourceType() && appliesToResourceId.hasIdPart()) { - if (appliesToResourceId.toUnqualifiedVersionless().getValue().equals(next.toUnqualifiedVersionless().getValue())) { - foundMatch = true; - break; - } + if (!foundMatch) { + return null; } - } - if (!foundMatch) { - return null; - } - break; - default: - throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); + break; + default: + throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo); } if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) { @@ -308,22 +409,12 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { return newVerdict(); } - public void setTenantApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenantApplicabilityChecker) { - myTenantApplicabilityChecker = theTenantApplicabilityChecker; + public TransactionAppliesToEnum getTransactionAppliesToOp() { + return myTransactionAppliesToOp; } - @Override - public String toString() { - ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); - builder.append("op", myOp); - builder.append("transactionAppliesToOp", myTransactionAppliesToOp); - builder.append("appliesTo", myAppliesTo); - builder.append("appliesToTypes", myAppliesToTypes); - builder.append("appliesToTenant", myTenantApplicabilityChecker); - builder.append("classifierCompartmentName", myClassifierCompartmentName); - builder.append("classifierCompartmentOwners", myClassifierCompartmentOwners); - builder.append("classifierType", myClassifierType); - return builder.toString(); + public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) { + myTransactionAppliesToOp = theOp; } private boolean requestAppliesToTransaction(FhirContext theContext, RuleOpEnum theOp, IBaseResource theInputResource) { @@ -334,23 +425,23 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { IBaseBundle request = (IBaseBundle) theInputResource; String bundleType = BundleUtil.getBundleType(theContext, request); switch (theOp) { - case TRANSACTION: - return "transaction".equals(bundleType); - case BATCH: - return "batch".equals(bundleType); - default: - return false; + case TRANSACTION: + return "transaction".equals(bundleType); + case BATCH: + return "batch".equals(bundleType); + default: + return false; } } - public TransactionAppliesToEnum getTransactionAppliesToOp() { - return myTransactionAppliesToOp; - } - public void setAppliesTo(AppliesTypeEnum theAppliesTo) { myAppliesTo = theAppliesTo; } + public void setAppliesToInstances(List theAppliesToInstances) { + myAppliesToInstances = theAppliesToInstances; + } + public void setAppliesToTypes(Set theAppliesToTypes) { myAppliesToTypes = theAppliesToTypes; } @@ -372,12 +463,22 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ { return this; } - public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) { - myTransactionAppliesToOp = theOp; + public void setTenantApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenantApplicabilityChecker) { + myTenantApplicabilityChecker = theTenantApplicabilityChecker; } - public void setAppliesToInstances(List theAppliesToInstances) { - myAppliesToInstances = theAppliesToInstances; + @Override + public String toString() { + ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + builder.append("op", myOp); + builder.append("transactionAppliesToOp", myTransactionAppliesToOp); + builder.append("appliesTo", myAppliesTo); + builder.append("appliesToTypes", myAppliesToTypes); + builder.append("appliesToTenant", myTenantApplicabilityChecker); + builder.append("classifierCompartmentName", myClassifierCompartmentName); + builder.append("classifierCompartmentOwners", myClassifierCompartmentOwners); + builder.append("classifierType", myClassifierType); + return builder.toString(); } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java index e9e911311ca..295fc423344 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu2Test.java @@ -15,6 +15,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.TimeUnit; +import ca.uhn.fhir.rest.param.ReferenceParam; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -65,7 +66,7 @@ public class AuthorizationInterceptorDstu2Test { @Before public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - for (IServerInterceptor next : new ArrayList(ourServlet.getInterceptors())) { + for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { ourServlet.unregisterInterceptor(next); } ourReturn = null; @@ -1166,8 +1167,8 @@ public class AuthorizationInterceptorDstu2Test { httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); status = ourClient.execute(httpGet); extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); } @@ -1177,8 +1178,8 @@ public class AuthorizationInterceptorDstu2Test { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); + .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) + .build(); } }); @@ -1187,13 +1188,13 @@ public class AuthorizationInterceptorDstu2Test { String respString; Bundle respBundle; - ourReturn = new ArrayList(); + ourReturn = new ArrayList<>(); for (int i = 0; i < 10; i++) { - ourReturn.add(createPatient(1)); + ourReturn.add(createObservation(i, "Patient/1")); } ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); status = ourClient.execute(httpGet); respString = extractResponseAndClose(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -1201,7 +1202,7 @@ public class AuthorizationInterceptorDstu2Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNotNull(respBundle.getLink("next")); // Load next page @@ -1215,7 +1216,7 @@ public class AuthorizationInterceptorDstu2Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/5", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNull(respBundle.getLink("next")); } @@ -1226,8 +1227,8 @@ public class AuthorizationInterceptorDstu2Test { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdDt("Patient/1")) - .build(); + .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdDt("Patient/1")) + .build(); } }); @@ -1236,16 +1237,16 @@ public class AuthorizationInterceptorDstu2Test { String respString; Bundle respBundle; - ourReturn = new ArrayList(); + ourReturn = new ArrayList<>(); for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(1)); + ourReturn.add(createObservation(i, "Patient/1")); } - for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(2)); + for (int i = 5; i < 10; i++) { + ourReturn.add(createObservation(i, "Patient/2")); } ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); status = ourClient.execute(httpGet); respString = extractResponseAndClose(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -1253,7 +1254,7 @@ public class AuthorizationInterceptorDstu2Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal().intValue()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNotNull(respBundle.getLink("next")); // Load next page @@ -1261,7 +1262,7 @@ public class AuthorizationInterceptorDstu2Test { ourHitMethod = false; httpGet = new HttpGet(respBundle.getLink("next").getUrl()); status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); + extractResponseAndClose(status); assertEquals(403, status.getStatusLine().getStatusCode()); assertFalse(ourHitMethod); @@ -1283,7 +1284,7 @@ public class AuthorizationInterceptorDstu2Test { HttpResponse status; String response; - ourReturn = Arrays.asList(createPatient(2)); + ourReturn = Collections.singletonList(createPatient(2)); ourHitMethod = false; httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); status = ourClient.execute(httpGet); @@ -1291,9 +1292,9 @@ public class AuthorizationInterceptorDstu2Test { ourLog.info(response); assertThat(response, containsString("Access denied by default policy (no applicable rules)")); assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertFalse(ourHitMethod); - ourReturn = Arrays.asList(createObservation(10, "Patient/2")); + ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); ourHitMethod = false; httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); status = ourClient.execute(httpGet); @@ -1303,7 +1304,7 @@ public class AuthorizationInterceptorDstu2Test { assertEquals(403, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); - ourReturn = Arrays.asList(createCarePlan(10, "Patient/2")); + ourReturn = Collections.singletonList(createCarePlan(10, "Patient/2")); ourHitMethod = false; httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); status = ourClient.execute(httpGet); @@ -1321,7 +1322,7 @@ public class AuthorizationInterceptorDstu2Test { ourLog.info(response); assertThat(response, containsString("Access denied by default policy (no applicable rules)")); assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertFalse(ourHitMethod); ourReturn = Arrays.asList(createPatient(2), createObservation(10, "Patient/1")); ourHitMethod = false; @@ -1331,7 +1332,7 @@ public class AuthorizationInterceptorDstu2Test { ourLog.info(response); assertThat(response, containsString("Access denied by default policy (no applicable rules)")); assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertFalse(ourHitMethod); } @@ -1359,7 +1360,7 @@ public class AuthorizationInterceptorDstu2Test { HttpPost httpPost; HttpResponse status; - ourReturn = Arrays.asList((IResource) output); + ourReturn = Collections.singletonList(output); ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); @@ -1419,6 +1420,7 @@ public class AuthorizationInterceptorDstu2Test { httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/1"))); status = ourClient.execute(httpPost); response = extractResponseAndClose(status); + ourLog.debug(response); assertEquals(201, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); } @@ -1469,7 +1471,7 @@ public class AuthorizationInterceptorDstu2Test { HttpDelete httpDelete; HttpResponse status; - ourReturn = Arrays.asList(createPatient(1)); + ourReturn = Collections.singletonList(createPatient(1)); ourHitMethod = false; httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); @@ -1497,7 +1499,7 @@ public class AuthorizationInterceptorDstu2Test { HttpDelete httpDelete; HttpResponse status; - ourReturn = Arrays.asList(createPatient(1)); + ourReturn = Collections.singletonList(createPatient(1)); ourHitMethod = false; httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); @@ -1525,7 +1527,7 @@ public class AuthorizationInterceptorDstu2Test { HttpResponse status; ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(2)); + ourReturn = Collections.singletonList(createPatient(2)); httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); status = ourClient.execute(httpDelete); extractResponseAndClose(status); @@ -1533,7 +1535,7 @@ public class AuthorizationInterceptorDstu2Test { assertTrue(ourHitMethod); ourHitMethod = false; - ourReturn = Arrays.asList(createPatient(1)); + ourReturn = Collections.singletonList(createPatient(1)); httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1"); status = ourClient.execute(httpDelete); extractResponseAndClose(status); @@ -1665,6 +1667,7 @@ public class AuthorizationInterceptorDstu2Test { httpPost.setEntity(createFhirResourceEntity(createPatient(null))); status = ourClient.execute(httpPost); response = extractResponseAndClose(status); + ourLog.debug(response); assertEquals(200, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); @@ -1703,6 +1706,7 @@ public class AuthorizationInterceptorDstu2Test { httpPost.setEntity(createFhirResourceEntity(createPatient(null))); status = ourClient.execute(httpPost); response = extractResponseAndClose(status); + ourLog.debug(response); assertEquals(200, status.getStatusLine().getStatusCode()); assertTrue(ourHitMethod); @@ -1718,7 +1722,7 @@ public class AuthorizationInterceptorDstu2Test { } @Test - public void testInvalidInstanceIds() throws Exception { + public void testInvalidInstanceIds() { try { new RuleBuilder().allow("Rule 1").write().instance((String) null); fail(); @@ -2009,7 +2013,7 @@ public class AuthorizationInterceptorDstu2Test { } @Search() - public List search() { + public List search(@OptionalParam(name="subject")ReferenceParam theSubject) { ourHitMethod = true; return ourReturn; } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AdditionalRequestHeadersInterceptorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AdditionalRequestHeadersInterceptorR4Test.java deleted file mode 100644 index 5989618acb0..00000000000 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/AdditionalRequestHeadersInterceptorR4Test.java +++ /dev/null @@ -1,2202 +0,0 @@ -package ca.uhn.fhir.rest.server.interceptor; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.AddProfileTagEnum; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.api.server.IRequestOperationCallback; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; -import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; -import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; -import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; -import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; -import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; -import ca.uhn.fhir.util.PortUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -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.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.*; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.hamcrest.Matchers.containsString; -import static org.junit.Assert.*; - -public class AdditionalRequestHeadersInterceptorR4Test { - - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"Access denied by default policy (no applicable rules)\"}]}"; - private static CloseableHttpClient ourClient; - private static String ourConditionalCreateId; - private static FhirContext ourCtx = FhirContext.forR4(); - private static boolean ourHitMethod; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AdditionalRequestHeadersInterceptorR4Test.class); - private static int ourPort; - private static List ourReturn; - private static Server ourServer; - private static RestfulServer ourServlet; - - @Before - public void before() { - ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) { - ourServlet.unregisterInterceptor(next); - } - ourReturn = null; - ourHitMethod = false; - ourConditionalCreateId = "1123"; - } - - private Resource createCarePlan(Integer theId, String theSubjectId) { - CarePlan retVal = new CarePlan(); - if (theId != null) { - retVal.setId(new IdType("CarePlan", (long) theId)); - } - retVal.setSubject(new Reference("Patient/" + theSubjectId)); - return retVal; - } - - private HttpEntity createFhirResourceEntity(IBaseResource theResource) { - String out = ourCtx.newJsonParser().encodeResourceToString(theResource); - return new StringEntity(out, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")); - } - - private Resource createObservation(Integer theId, String theSubjectId) { - Observation retVal = new Observation(); - if (theId != null) { - retVal.setId(new IdType("Observation", (long) theId)); - } - retVal.getCode().setText("OBS"); - retVal.setSubject(new Reference(theSubjectId)); - return retVal; - } - - private Resource createPatient(Integer theId) { - Patient retVal = new Patient(); - if (theId != null) { - retVal.setId(new IdType("Patient", (long) theId)); - } - retVal.addName().setFamily("FAM"); - return retVal; - } - - private Resource createPatient(Integer theId, int theVersion) { - Resource retVal = createPatient(theId); - retVal.setId(retVal.getIdElement().withVersion(Integer.toString(theVersion))); - return retVal; - } - - private String extractResponseAndClose(HttpResponse status) throws IOException { - if (status.getEntity() == null) { - return null; - } - String responseContent; - responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - return responseContent; - } - - @Test - public void testAllowAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .deny("Rule 1").read().resourcesOfType(Patient.class).withAnyId().andThen() - .allowAll("Default Rule") - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Rule 1")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testAllowByCompartmentWithAnyType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().allow("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().denyAll().build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testAllowByCompartmentWithType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().allow("Rule 1").read().resourcesOfType(CarePlan.class).inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().denyAll() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "FOO")); - 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) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(Bundle.BundleType.BATCH); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(Bundle.HTTPVerb.POST); - - Bundle output = new Bundle(); - output.setType(Bundle.BundleType.TRANSACTIONRESPONSE); - output.addEntry().getResponse().setLocation("/Patient/1"); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Collections.singletonList((Resource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - } - - @Test - public void testBatchWhenTransactionReadDenied() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(Bundle.BundleType.BATCH); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(Bundle.HTTPVerb.POST); - - Bundle output = new Bundle(); - output.setType(Bundle.BundleType.TRANSACTIONRESPONSE); - output.addEntry().setResource(createPatient(2)); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Collections.singletonList((Resource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - } - - @Test - public void testBatchWhenTransactionWrongBundleType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(Bundle.BundleType.COLLECTION); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(Bundle.HTTPVerb.POST); - - Bundle output = new Bundle(); - output.setType(Bundle.BundleType.TRANSACTIONRESPONSE); - output.addEntry().setResource(createPatient(1)); - - HttpPost httpPost; - HttpResponse status; - String response; - - ourReturn = Collections.singletonList((Resource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - } - - @Test - public void testDeleteByCompartment() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").delete().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").delete().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(1)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(204, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testDenyAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow().read().resourcesOfType(Patient.class).withAnyId().andThen() - .denyAll("Default Rule") - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by rule: Default Rule")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - /** - * #528 - */ - @Test - public void testDenyByCompartmentWithAnyType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().deny("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().allowAll().build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - /** - * #528 - */ - @Test - public void testDenyByCompartmentWithType() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder().deny("Rule 1").read().resourcesOfType(CarePlan.class).inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().allowAll() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "845bd9f1-3635-4866-a6c8-1ca085df5c1a")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createCarePlan(10, "FOO")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/135154"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testHistoryWithReadAll() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().allResources().withAnyId() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(2, 1)); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testMetadataAllow() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").metadata() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - } - - @Test - public void testMetadataDeny() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.ALLOW) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .deny("Rule 1").metadata() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - } - - @Test - public void testOperationAnyName() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().withAnyName().onServer().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeAllowed() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = new ArrayList<>(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - ourReturn = new ArrayList<>(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Encounter/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(false, ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeWithInvalidReturnValue() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // With a return value we don't allow - ourReturn = Collections.singletonList(createPatient(222)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - // With a return value we do - ourReturn = Collections.singletonList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - } - - @Test - public void testOperationByInstanceOfTypeWithReturnValue() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = new ArrayList<>(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Bundle")); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(true, ourHitMethod); - - ourReturn = new ArrayList<>(); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Encounter/1/$everything"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("OperationOutcome")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(false, ourHitMethod); - } - - @Test - public void testOperationInstanceLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onInstance(new IdType("http://example.com/Patient/1/_history/2")).andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testOperationInstanceLevelAnyInstance() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onAnyInstance().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Another Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/2/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testOperationNotAllowedWithWritePermissiom() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").write().allResources().withAnyId().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // System - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationServerLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onServer().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationTypeLevel() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onType(Patient.class).andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testOperationTypeLevelWildcard() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("RULE 1").operation().named("opName").onAnyType().andThen() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - // Server - ourHitMethod = false; - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Another type - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Wrong name - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - // Instance - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testReadByAnyId() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).withAnyId() - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/222"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(2), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testReadByCompartmentRight() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testReadPageRight() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String respString; - Bundle respBundle; - - ourReturn = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - ourReturn.add(createPatient(1)); - } - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNotNull(respBundle.getLink("next")); - - // Load next page - - ourHitMethod = false; - httpGet = new HttpGet(respBundle.getLink("next").getUrl()); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNull(respBundle.getLink("next")); - - } - - @Test - public void testReadPageWrong() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String respString; - Bundle respBundle; - - ourReturn = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(1)); - } - for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(2)); - } - - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); - assertEquals(5, respBundle.getEntry().size()); - assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); - assertNotNull(respBundle.getLink("next")); - - // Load next page - - ourHitMethod = false; - httpGet = new HttpGet(respBundle.getLink("next").getUrl()); - status = ourClient.execute(httpGet); - respString = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - } - - @Test - public void testReadByCompartmentWrong() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpGet httpGet; - HttpResponse status; - String response; - - ourReturn = Collections.singletonList(createPatient(2)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createCarePlan(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(1), createObservation(10, "Patient/2")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Arrays.asList(createPatient(2), createObservation(10, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - ourLog.info(response); - assertThat(response, containsString("Access denied by default policy (no applicable rules)")); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testTransactionWriteGood() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").transaction().withAnyOperation().andApplyNormalRules().andThen() - .allow("Rule 2").write().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() - .build(); - } - }); - - Bundle input = new Bundle(); - input.setType(Bundle.BundleType.TRANSACTION); - input.addEntry().setResource(createPatient(1)).getRequest().setUrl("/Patient").setMethod(Bundle.HTTPVerb.PUT); - - Bundle output = new Bundle(); - output.setType(Bundle.BundleType.TRANSACTIONRESPONSE); - output.addEntry().getResponse().setLocation("/Patient/1"); - - HttpPost httpPost; - HttpResponse status; - - ourReturn = Collections.singletonList((Resource) output); - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/"); - httpPost.setEntity(createFhirResourceEntity(input)); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - } - - @Test - public void testWriteByCompartmentCreate() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 1b").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1123")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - // Conditional - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.addHeader("If-None-Exist", "Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/2"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/1"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(201, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testWriteByCompartmentCreateConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").createConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(201, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDeleteConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").delete().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").deleteConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(1)); - - ourHitMethod = false; - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); - status = ourClient.execute(httpDelete); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(204, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDeleteConditionalWithoutDirectMatch() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 2").deleteConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourReturn = Collections.singletonList(createPatient(1)); - - ourHitMethod = false; - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar"); - status = ourClient.execute(httpDelete); - String response = extractResponseAndClose(status); - ourLog.info(response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentDoesntAllowDelete() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpDelete httpDelete; - HttpResponse status; - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(2)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - ourReturn = Collections.singletonList(createPatient(1)); - httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpDelete); - extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - } - - @Test - public void testWriteByCompartmentUpdate() throws Exception { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - String response; - HttpResponse status; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); - httpPost.setEntity(createFhirResourceEntity(createPatient(2))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/1"); - httpPost.setEntity(createFhirResourceEntity(createPatient(1))); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - // Conditional - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(1))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(99))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/10"); - httpPost.setEntity(createFhirResourceEntity(createObservation(10, "Patient/1"))); - status = ourClient.execute(httpPost); - extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/10"); - httpPost.setEntity(createFhirResourceEntity(createObservation(10, "Patient/2"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(ERR403, response); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToInvalid() throws Exception { - ourConditionalCreateId = "1123"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 3").updateConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertTrue(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToValid() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 3").updateConditional().resourcesOfType(Patient.class) - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @Test - public void testWriteByCompartmentUpdateConditionalResolvesToValidAllTypes() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 2").write().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")).andThen() - .allow("Rule 3").updateConditional().allResources() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation?foo=bar"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertTrue(ourHitMethod); - - } - - @Test - public void testInvalidInstanceIds() throws Exception { - try { - new RuleBuilder().allow("Rule 1").write().instance((String) null); - fail(); - } catch (NullPointerException e) { - assertEquals("theId must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(""); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("theId must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance("Observation/"); - fail(); - } catch (IllegalArgumentException e) { - assertEquals("theId must contain an ID part", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdType()); - fail(); - } catch (NullPointerException e) { - assertEquals("theId.getValue() must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdType("")); - fail(); - } catch (NullPointerException e) { - assertEquals("theId.getValue() must not be null or empty", e.getMessage()); - } - try { - new RuleBuilder().allow("Rule 1").write().instance(new IdType("Observation", (String) null)); - fail(); - } catch (NullPointerException e) { - assertEquals("theId must contain an ID part", e.getMessage()); - } - } - - @Test - public void testWritePatchByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().instance("Patient/900").andThen() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - String input = "[ { \"op\": \"replace\", \"path\": \"/gender\", \"value\": \"male\" } ]"; - - ourHitMethod = false; - httpPost = new HttpPatch("http://localhost:" + ourPort + "/Patient/900"); - httpPost.setEntity(new StringEntity(input, ContentType.parse("application/json-patch+json"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(204, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPatch("http://localhost:" + ourPort + "/Patient/999"); - httpPost.setEntity(new StringEntity(input, ContentType.parse("application/json-patch+json"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertFalse(ourHitMethod); - } - - @Test - public void testWriteByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").write().instance("Observation/900").andThen() - .allow("Rule 1").write().instance("901").andThen() - .build(); - } - }); - - HttpEntityEnclosingRequestBase httpPost; - HttpResponse status; - String response; - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/900"); - httpPost.setEntity(createFhirResourceEntity(createObservation(900, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/901"); - httpPost.setEntity(createFhirResourceEntity(createObservation(901, "Patient/12"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation"); - httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/900"))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - ourHitMethod = false; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(createFhirResourceEntity(createPatient(null))); - status = ourClient.execute(httpPost); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @Test - public void testReadByInstance() throws Exception { - ourConditionalCreateId = "1"; - - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { - @Override - public List buildRuleList(RequestDetails theRequestDetails) { - return new RuleBuilder() - .allow("Rule 1").read().instance("Observation/900").andThen() - .allow("Rule 1").read().instance("901").andThen() - .build(); - } - }); - - HttpResponse status; - String response; - HttpGet httpGet; - - ourReturn = Collections.singletonList(createObservation(900, "Patient/1")); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/900"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createPatient(901)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/901"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); - - ourReturn = Collections.singletonList(createPatient(1)); - ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=json"); - status = ourClient.execute(httpGet); - response = extractResponseAndClose(status); - assertEquals(403, status.getStatusLine().getStatusCode()); - assertEquals(ERR403, response); - assertFalse(ourHitMethod); - - } - - @AfterClass - public static void afterClassClearContext() throws Exception { - ourServer.stop(); - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @BeforeClass - public static void beforeClass() throws Exception { - - ourPort = PortUtil.findFreePort(); - ourServer = new Server(ourPort); - - DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider(); - DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider(); - DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider(); - DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider(); - PlainProvider plainProvider = new PlainProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv); - ourServlet.setPlainProviders(plainProvider); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - ourServer.start(); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyCarePlanResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return CarePlan.class; - } - - @Read(version = true) - public CarePlan read(@IdParam IdType theId) { - ourHitMethod = true; - return (CarePlan) ourReturn.get(0); - } - - @Search() - public List search() { - ourHitMethod = true; - return ourReturn; - } - } - - public static class DummyEncounterResourceProvider implements IResourceProvider { - - @Operation(name = "everything", idempotent = true) - public Bundle everything(@IdParam IdType theId) { - ourHitMethod = true; - Bundle retVal = new Bundle(); - for (Resource next : ourReturn) { - retVal.addEntry().setResource(next); - } - return retVal; - } - - @Override - public Class getResourceType() { - return Encounter.class; - } - } - - public static class DummyObservationResourceProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Observation theResource, @ConditionalUrlParam String theConditionalUrl) { - ourHitMethod = true; - theResource.setId("Observation/1/_history/1"); - MethodOutcome retVal = new MethodOutcome(); - retVal.setCreated(true); - retVal.setResource(theResource); - return retVal; - } - - @Delete() - public MethodOutcome delete(@IdParam IdType theId) { - ourHitMethod = true; - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Override - public Class getResourceType() { - return Observation.class; - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation(@IdParam IdType theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Read(version = true) - public Observation read(@IdParam IdType theId) { - ourHitMethod = true; - return (Observation) ourReturn.get(0); - } - - @Search() - public List search() { - ourHitMethod = true; - return ourReturn; - } - - @Update() - public MethodOutcome update(@IdParam IdType theId, @ResourceParam Observation theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - - if (isNotBlank(theConditionalUrl)) { - IdType actual = new IdType("Observation", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(actual); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(theId.withVersion("2")); - } - - MethodOutcome retVal = new MethodOutcome(); - retVal.setResource(theResource); - return retVal; - } - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - - if (isNotBlank(theConditionalUrl)) { - IdType actual = new IdType("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.CREATE); - } - - ourHitMethod = true; - theResource.setId("Patient/1/_history/1"); - MethodOutcome retVal = new MethodOutcome(); - retVal.setCreated(true); - retVal.setResource(theResource); - return retVal; - } - - @Delete() - public MethodOutcome delete(IRequestOperationCallback theRequestOperationCallback, @IdParam IdType theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - for (IBaseResource next : ourReturn) { - theRequestOperationCallback.resourceDeleted(next); - } - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Operation(name = "everything", idempotent = true) - public Bundle everything(@IdParam IdType theId) { - ourHitMethod = true; - Bundle retVal = new Bundle(); - for (Resource next : ourReturn) { - retVal.addEntry().setResource(next); - } - return retVal; - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @History() - public List history() { - ourHitMethod = true; - return (ourReturn); - } - - @History() - public List history(@IdParam IdType theId) { - ourHitMethod = true; - return (ourReturn); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation(@IdParam IdType theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName2", idempotent = true) - public Parameters operation2(@IdParam IdType theId) { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Operation(name = "opName2", idempotent = true) - public Parameters operation2() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Read(version = true) - public Patient read(@IdParam IdType theId) { - ourHitMethod = true; - return (Patient) ourReturn.get(0); - } - - @Search() - public List search() { - ourHitMethod = true; - return ourReturn; - } - - @Update() - public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) { - ourHitMethod = true; - - if (isNotBlank(theConditionalUrl)) { - IdType actual = new IdType("Patient", ourConditionalCreateId); - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, actual); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(actual); - } else { - ActionRequestDetails subRequest = new ActionRequestDetails(theRequestDetails, theResource); - subRequest.notifyIncomingRequestPreHandled(RestOperationTypeEnum.UPDATE); - theResource.setId(theId.withVersion("2")); - } - - MethodOutcome retVal = new MethodOutcome(); - retVal.setResource(theResource); - return retVal; - } - - @Patch() - public MethodOutcome patch(@IdParam IdType theId, @ResourceParam String theResource, PatchTypeEnum thePatchType) { - ourHitMethod = true; - - MethodOutcome retVal = new MethodOutcome(); - return retVal; - } - - @Validate - public MethodOutcome validate(@ResourceParam Patient theResource, @IdParam IdType theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, - @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile, RequestDetails theRequestDetails) { - ourHitMethod = true; - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OK"); - return new MethodOutcome(oo); - } - - @Validate - public MethodOutcome validate(@ResourceParam Patient theResource, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding, @Validate.Mode ValidationModeEnum theMode, - @Validate.Profile String theProfile, RequestDetails theRequestDetails) { - ourHitMethod = true; - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OK"); - return new MethodOutcome(oo); - } - - } - - public static class PlainProvider { - - @History() - public List history() { - ourHitMethod = true; - return (ourReturn); - } - - @Operation(name = "opName", idempotent = true) - public Parameters operation() { - ourHitMethod = true; - return (Parameters) new Parameters().setId("1"); - } - - @Transaction() - public Bundle search(@TransactionParam Bundle theInput) { - ourHitMethod = true; - return (Bundle) ourReturn.get(0); - } - - } - -} 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 61da96bb604..17febb9ea89 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 @@ -8,9 +8,11 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.server.IRequestOperationCallback; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.*; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; @@ -287,6 +289,68 @@ public class AuthorizationInterceptorR4Test { } + @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); + } + /** * #528 */ @@ -390,69 +454,6 @@ 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) { @@ -477,7 +478,7 @@ public class AuthorizationInterceptorR4Test { HttpPost httpPost; HttpResponse status; - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); @@ -510,7 +511,7 @@ public class AuthorizationInterceptorR4Test { HttpPost httpPost; HttpResponse status; - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); @@ -544,7 +545,7 @@ public class AuthorizationInterceptorR4Test { HttpResponse status; String response; - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); @@ -1841,13 +1842,86 @@ public class AuthorizationInterceptorR4Test { httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); status = ourClient.execute(httpGet); extractResponseAndClose(status); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); } @Test - public void testReadByCompartmentWrong() throws Exception { + public void testReadByCompartmentWrongAllTypesProactiveBlockEnabledNoResponse() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen() + .build(); + } + }.setFlags()); + + HttpGet httpGet; + HttpResponse status; + String response; + + ourReturn = Collections.emptyList(); + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(404, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(404, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/999/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + } + + @Test + public void testReadByCompartmentWrongProactiveBlockDisabled() throws Exception { ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { @@ -1856,7 +1930,7 @@ public class AuthorizationInterceptorR4Test { .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) .build(); } - }); + }.setFlags(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)); HttpGet httpGet; HttpResponse status; @@ -1870,7 +1944,7 @@ public class AuthorizationInterceptorR4Test { ourLog.info(response); assertThat(response, containsString("Access denied by default policy (no applicable rules)")); assertEquals(403, status.getStatusLine().getStatusCode()); - assertTrue(ourHitMethod); + assertFalse(ourHitMethod); ourReturn = Collections.singletonList(createObservation(10, "Patient/2")); ourHitMethod = false; @@ -1914,6 +1988,133 @@ public class AuthorizationInterceptorR4Test { } + @Test + public void testReadByCompartmentWrongProactiveBlockDisabledNoResponse() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() + .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) + .build(); + } + }.setFlags(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)); + + HttpGet httpGet; + HttpResponse status; + String response; + + ourReturn = Collections.emptyList(); + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(404, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + } + + @Test + public void testReadByCompartmentWrongProactiveBlockEnabledNoResponse() throws Exception { + ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List buildRuleList(RequestDetails theRequestDetails) { + return new RuleBuilder() + .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")).andThen() + .allow("Rule 2").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) + .build(); + } + }.setFlags()); + + HttpGet httpGet; + HttpResponse status; + String response; + + ourReturn = Collections.emptyList(); + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(404, status.getStatusLine().getStatusCode()); + assertTrue(ourHitMethod); + + // CarePlan could potentially be in the Patient/1 compartment but we don't + // have any rules explicitly allowing CarePlan so it's blocked + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/CarePlan/10"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + + ourHitMethod = false; + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/999/_history"); + status = ourClient.execute(httpGet); + response = extractResponseAndClose(status); + ourLog.info(response); + assertEquals(403, status.getStatusLine().getStatusCode()); + assertFalse(ourHitMethod); + } + @Test public void testReadByInstance() throws Exception { ourConditionalCreateId = "1"; @@ -1965,7 +2166,7 @@ public class AuthorizationInterceptorR4Test { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")) + .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) .build(); } }); @@ -1977,11 +2178,11 @@ public class AuthorizationInterceptorR4Test { ourReturn = new ArrayList<>(); for (int i = 0; i < 10; i++) { - ourReturn.add(createPatient(1)); + ourReturn.add(createObservation(i, "Patient/1")); } ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); status = ourClient.execute(httpGet); respString = extractResponseAndClose(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -1989,7 +2190,7 @@ public class AuthorizationInterceptorR4Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNotNull(respBundle.getLink("next")); // Load next page @@ -2003,7 +2204,7 @@ public class AuthorizationInterceptorR4Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/5", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNull(respBundle.getLink("next")); } @@ -2014,7 +2215,7 @@ public class AuthorizationInterceptorR4Test { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() - .allow("Rule 1").read().resourcesOfType(Patient.class).inCompartment("Patient", new IdType("Patient/1")) + .allow("Rule 1").read().resourcesOfType(Observation.class).inCompartment("Patient", new IdType("Patient/1")) .build(); } }); @@ -2026,14 +2227,14 @@ public class AuthorizationInterceptorR4Test { ourReturn = new ArrayList<>(); for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(1)); + ourReturn.add(createObservation(i, "Patient/1")); } - for (int i = 0; i < 5; i++) { - ourReturn.add(createPatient(2)); + for (int i = 5; i < 10; i++) { + ourReturn.add(createObservation(i, "Patient/2")); } ourHitMethod = false; - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=5&_format=json"); + httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_count=5&_format=json&subject=Patient/1"); status = ourClient.execute(httpGet); respString = extractResponseAndClose(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -2041,7 +2242,7 @@ public class AuthorizationInterceptorR4Test { respBundle = ourCtx.newJsonParser().parseResource(Bundle.class, respString); assertEquals(5, respBundle.getEntry().size()); assertEquals(10, respBundle.getTotal()); - assertEquals("Patient/1", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); + assertEquals("Observation/0", respBundle.getEntry().get(0).getResource().getIdElement().toUnqualifiedVersionless().getValue()); assertNotNull(respBundle.getLink("next")); // Load next page @@ -2078,7 +2279,7 @@ public class AuthorizationInterceptorR4Test { Bundle input = createTransactionWithPlaceholdersRequestBundle(); Bundle output = createTransactionWithPlaceholdersResponseBundle(); - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -2111,7 +2312,7 @@ public class AuthorizationInterceptorR4Test { Bundle input = createTransactionWithPlaceholdersRequestBundle(); Bundle output = createTransactionWithPlaceholdersResponseBundle(); - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -2146,7 +2347,7 @@ public class AuthorizationInterceptorR4Test { HttpPost httpPost; HttpResponse status; - ourReturn = Collections.singletonList((Resource) output); + ourReturn = Collections.singletonList(output); ourHitMethod = false; httpPost = new HttpPost("http://localhost:" + ourPort + "/"); httpPost.setEntity(createFhirResourceEntity(input)); @@ -2640,6 +2841,9 @@ public class AuthorizationInterceptorR4Test { @Read(version = true) public CarePlan read(@IdParam IdType theId) { ourHitMethod = true; + if (ourReturn.isEmpty()) { + throw new ResourceNotFoundException(theId); + } return (CarePlan) ourReturn.get(0); } @@ -2733,11 +2937,14 @@ public class AuthorizationInterceptorR4Test { @Read(version = true) public Observation read(@IdParam IdType theId) { ourHitMethod = true; + if (ourReturn.isEmpty()) { + throw new ResourceNotFoundException(theId); + } return (Observation) ourReturn.get(0); } @Search() - public List search() { + public List search(@OptionalParam(name = "subject") ReferenceParam theSubject) { ourHitMethod = true; return ourReturn; } @@ -2764,6 +2971,7 @@ public class AuthorizationInterceptorR4Test { } + @SuppressWarnings("unused") public static class DummyPatientResourceProvider implements IResourceProvider { @Create() @@ -2861,6 +3069,9 @@ public class AuthorizationInterceptorR4Test { @Read(version = true) public Patient read(@IdParam IdType theId) { ourHitMethod = true; + if (ourReturn.isEmpty()) { + throw new ResourceNotFoundException(theId); + } return (Patient) ourReturn.get(0); } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 83aba3e0cd3..359ba8fbabc 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -1917,7 +1917,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat bundle.getNamedChildren("entry", entries); Element match = null; for (Element we : entries) { - if (we.getChildValue("fullUrl").equals(targetUrl)) { + if (targetUrl.equals(we.getChildValue("fullUrl"))) { Element r = we.getNamedChild("resource"); if (version.isEmpty()) { rule(errors, IssueType.FORBIDDEN, -1, -1, path, match==null, "Multiple matches in bundle for reference " + ref); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java index 0ae6332859d..4c852810973 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/FhirInstanceValidatorR4Test.java @@ -317,6 +317,15 @@ public class FhirInstanceValidatorR4Test { } + @Test + public void testValidateBundleWithNoFullUrl() throws IOException { + String encoded = IOUtils.toString(FhirInstanceValidatorR4Test.class.getResourceAsStream("/r4/r4-caredove-bundle.json")); + + ValidationResult output = myVal.validateWithResult(encoded); + List errors = logResultsAndReturnNonInformationalOnes(output); + assertEquals(44, errors.size()); + } + @Test public void testBase64Valid() { Base64BinaryType value = new Base64BinaryType(new byte[] {2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1}); diff --git a/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json b/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json new file mode 100644 index 00000000000..d61690a4f95 --- /dev/null +++ b/hapi-fhir-validation/src/test/resources/r4/r4-caredove-bundle.json @@ -0,0 +1,815 @@ +{ + + "resourceType": "Bundle", + + "type": "transaction", + + "timestamp": "2018-03-09T15:21:51.2112Z", + + "entry": [ + + { + + "resource": { + + "resourceType": "ServiceRequest", + + "id": 1, + + "text": { + + "status": "generated", + + "div": "Human readable HTML narrative of entire ServiceRequest and related resources goes here. For brevity sake, it is omitted here, but can be included in an actual transmission" + + }, + + "status": "active", + + "intent": "proposal", + + "priority": "routine", + + "subject": { + + "reference": "Patient/1" + + }, + + "authoredOn": "2018-03-09T15:21:51Z", + + "requester": { + + "reference": "PractitionerRole/1" + + }, + + "performer": { + + "reference": "https://www.caredove.com/FHIR3/HealthcareService/8654" + + }, + + "reasonCode": [ + + { + + "text": "Reason for referral narrative goes here" + + } + + ], + + "supportingInfo": [ + + { + + "reference": "DocumentReference/1" + + } + + ], + + "note": [ + + { + + "text": "Allergies: Penicillin \nSocial History: History of family conflict \nlow social interaction \nFood Allergies: Peanuts" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "ServiceRequest" + + } + + }, + + { + + "resource": { + + "resourceType": "Patient", + + "id": 1, + + "identifier": [ + + { + + "type": { + + "coding": [ + + { + + "code": "JHN", + + "system": "http://hl7.org/fhir/v2/0203" + + } + + ], + + "text": "Ontario PHN" + + }, + + "value": "4455 044 033", + + "system": "http://ehealthontario.ca/API/FHIR/NamingSystem/ca-on-patient-hcn", + + "extension": [ + + { + + "url": "https://www.caredove.com/FHIR3/StructureDefinition/caredove-healthcardversion", + + "valueString": "H" + + } + + ] + + } + + ], + + "name": [ + + { + + "given": [ + + "John", + + "Scott" + + ], + + "family": "Smith" + + } + + ], + + "telecom": [ + + { + + "system": "phone", + + "value": "(555) 111-1111", + + "use": "mobile", + + "rank": 1 + + }, + + { + + "system": "phone", + + "value": "(555) 222-2222", + + "rank": 2 + + }, + + { + + "system": "email", + + "value": "testpatient@caredove.com" + + } + + ], + + "gender": "male", + + "birthDate": "1928-06-29", + + "address": [ + + { + + "use": "home", + + "type": "physical", + + "line": [ + + "Unit 2", + + "50 Albert St." + + ], + + "city": "Waterloo", + + "state": "ON", + + "postalCode": "K8N 1N1", + + "country": "Can" + + } + + ], + + "maritalStatus": { + + "coding": [ + + { + + "code": "M", + + "display": "Married" + + } + + ], + + "text": "Married" + + }, + + "contact": [ + + { + + "relationship": [ + + { + + "text": "Alternate Contact" + + } + + ], + + "name": { + + "given": [ + + "Shemergency", + + "Scott" + + ], + + "family": "McContact" + + }, + + "telecom": [ + + { + + "system": "phone", + + "value": "(555) 111-1111", + + "use": "mobile", + + "rank": 1 + + }, + + { + + "system": "phone", + + "value": "(555) 222-2222", + + "rank": 2 + + }, + + { + + "system": "email", + + "value": "testcontact@caredove.com" + + } + + ], + + "address": { + + "use": "home", + + "type": "physical", + + "line": [ + + "Unit 2", + + "50 Albert St." + + ], + + "city": "Waterloo", + + "state": "ON", + + "postalCode": "32819", + + "country": "Can" + + }, + + "gender": "female" + + } + + ], + + "communication": [ + + { + + "language": { + + "coding": [ + + { + + "code": "en", + + "display": "English" + + } + + ], + + "text": "English" + + }, + + "preferred": true + + } + + ], + + "generalPractitioner": [ + + { + + "reference": "PractitionerRole/2" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "Patient" + + } + + }, + + { + + "resource": { + + "resourceType": "PractitionerRole", + + "id": 1, + + "practitioner": { + + "reference": "Practitioner/1" + + }, + + "organization": { + + "reference": "Organization/1" + + }, + + "location": [ + + { + + "reference": "Location/1" + + } + + ], + + "telecom": [ + + { + + "system": "phone", + + "value": "(555) 111-1111", + + "use": "work" + + }, + + { + + "system": "email", + + "value": "testsender@caredove.com", + + "use": "work" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "PractitionerRole" + + } + + }, + + { + + "resource": { + + "resourceType": "Practitioner", + + "id": 1, + + "name": [ + + { + + "given": [ + + "Requesty" + + ], + + "family": "McSenderson" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "Practitioner" + + } + + }, + + { + + "resource": { + + "resourceType": "Organization", + + "id": 1, + + "name": "North Sender Clinic" + + }, + + "request": { + + "method": "POST", + + "url": "Organization" + + } + + }, + + { + + "resource": { + + "resourceType": "Location", + + "id": 1, + + "name": "Downtown Sender Hub", + + "address": { + + "use": "work", + + "type": "physical", + + "line": [ + + "Suite 11", + + "11 King st. West" + + ], + + "city": "Kitchener", + + "state": "ON", + + "postalCode": "N2L 1T1", + + "country": "Can" + + } + + }, + + "request": { + + "method": "POST", + + "url": "Location" + + } + + }, + + { + + "resource": { + + "resourceType": "PractitionerRole", + + "id": 2, + + "practitioner": { + + "reference": "Practitioner/2" + + }, + + "organization": { + + "reference": "Organization/2" + + }, + + "location": [ + + { + + "reference": "Location/2" + + } + + ], + + "telecom": [ + + { + + "system": "phone", + + "value": "(555) 222-2222", + + "use": "work" + + }, + + { + + "system": "email", + + "value": "familydoc@caredove.com", + + "use": "work" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "PractitionerRole" + + } + + }, + + { + + "resource": { + + "resourceType": "Practitioner", + + "id": 2, + + "name": [ + + { + + "given": [ + + "Dr. Prim" + + ], + + "family": "Caredoc" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "Practitioner" + + } + + }, + + { + + "resource": { + + "resourceType": "Organization", + + "id": 2, + + "name": "Star Family Health Team" + + }, + + "request": { + + "method": "POST", + + "url": "Organization" + + } + + }, + + { + + "resource": { + + "resourceType": "DocumentReference", + + "id": 1, + + "status": "current", + + "created": "2018-03-09T15:21:51.2112Z", + + "description": "Filename or Document Title goes here", + + "content": [ + + { + + "attachment": "NEEDS WORK - ATTACHMENT DATA TYPE", + + "format": "NEEDS WORK - FORMAT INFO" + + } + + ] + + }, + + "request": { + + "method": "POST", + + "url": "Practitioner" + + } + + }, + + { + + "resource": { + + "resourceType": "Location", + + "id": 2, + + "name": "West Side GP Office", + + "address": { + + "use": "work", + + "type": "physical", + + "line": [ + + "22 Weber st. East" + + ], + + "city": "Kitchener", + + "state": "ON", + + "postalCode": "N2L 2T2", + + "country": "Can" + + } + + }, + + "request": { + + "method": "POST", + + "url": "Location" + + } + + }, + + { + + "resource": { + + "resourceType": "Task", + + "id": 1, + + "basedOn" : { + + "reference" : "ServiceRequest/1" + + }, + + "status" : "requested", + + "businessStatus " : { + + "text" : "Waiting for preliminary review" + + }, + + "intent" : "proposal", + + "code" : { + + "text" : "Process Request" + + }, + + "description" : "Process and close this referral request", + + "authoredOn" : "2018-03-09T15:21:51Z", + + "lastModified" : "2018-03-09T15:21:51Z" + + }, + + "request": { + + "method": "POST", + + "url": "Task" + + } + + } + + ] + +} diff --git a/src/changes/changes.xml b/src/changes/changes.xml index f32569c36a6..88846479024 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -42,6 +42,30 @@ Spring-data (used by the JPA server) has been upgraded to version 2.0.7 (from version 1.11.6). Thanks to Roman Doboni for the pull request! + + A crash in the validator was fixed: Validating a Bundle that did not have fullUrl entries + in a required spot caused a NullPointerException. + + + AuthorizationInterceptor now examines requests more closely in order + to block requests early that are not possibly going to return + allowable results when compartment rules are used. For example, + if an AuthorizationInterceptor is configured to allow only + read]]> + access to compartment + Patient/123]]>, + a search for + Observation?subject=987]]> + will now be blocked before the method handler is called. Previously + the search was performed and the results were examined in order to + determine whether they were all in the appropriate compartment, but + this incurs a performance cost, and means that this search would + successfully return an empty Bundle if no matches were present. +
]]> + A new setting on AuthorizationInterceptor called + setFlags(flags)]]> + can be used to maintain the previous behaviour. +