Revert "Improve auth interceptor operation handling (#6278)" (#6381)

This reverts commit 2b3858572d.
This commit is contained in:
James Agnew 2024-10-18 09:01:04 -04:00 committed by GitHub
parent 2b3858572d
commit 422d4f0741
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 140 additions and 256 deletions

View File

@ -1,10 +0,0 @@
---
type: fix
issue: 6258
title: "The AuthorizationInterceptor handling for operations has been improved
so that operation rules now directly test the contents of response Bundle
or Parameters objects returned by the operation when configure to require
explicit response authorization. This fixes a regression in 7.4.0 where
operation responses could sometimes be denied even if appropriate
permissions were granted to view resources in a response bundle. Thanks to
Gijsbert van den Brink for reporting the issue with a sample test!"

View File

@ -20,15 +20,11 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IOperation;
import ca.uhn.fhir.rest.gclient.IOperationUnnamed;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; 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.IAuthRule;
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleBuilder;
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester; import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRuleTester;
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
@ -72,7 +68,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
@ -902,17 +897,8 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
} }
@ParameterizedTest @Test
@CsvSource({ public void testDiffOperation_AllowedByType_Instance() {
// ResourceId , RequireExplicitResponseAuthorization , ShouldSucceed
"Patient/A , true , true",
"Patient/A/_history/2 , true , true",
"Observation/B , true , false",
"Patient/A , false , true",
"Patient/A/_history/2 , false , true",
"Observation/B , false , true"
})
public void testDiffOperation_AllowedByType_Instance(String theResourceId, boolean theRequireExplicitResponseAuthorization, boolean theShouldSucceed) {
createPatient(withId("A"), withActiveTrue()); createPatient(withId("A"), withActiveTrue());
createPatient(withId("A"), withActiveFalse()); createPatient(withId("A"), withActiveFalse());
createObservation(withId("B"), withStatus("final")); createObservation(withId("B"), withStatus("final"));
@ -920,42 +906,30 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override @Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
RuleBuilder ruleBuilder = new RuleBuilder(); return new RuleBuilder()
if (theRequireExplicitResponseAuthorization) { .allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen()
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andRequireExplicitResponseAuthorization().andThen(); .allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen()
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andRequireExplicitResponseAuthorization().andThen(); .allow().read().resourcesOfType(Patient.class).withAnyId().andThen()
} else { .denyAll()
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen(); .build();
ruleBuilder.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen();
}
ruleBuilder.allow().read().resourcesOfType(Patient.class).withAnyId().andThen();
ruleBuilder.denyAll();
return ruleBuilder.build();
} }
}); });
Parameters diff; Parameters diff;
IOperation operation = myClient.operation(); diff = myClient.operation().onInstance("Patient/A").named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
IOperationUnnamed target; assertThat(diff.getParameter()).hasSize(1);
if (theResourceId.contains("_history")) {
target = operation.onInstanceVersion(new IdType(theResourceId));
} else {
target = operation.onInstance(theResourceId);
}
IOperationUntypedWithInput<Parameters> executable = target.named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class);
if (theShouldSucceed) { diff = myClient.operation().onInstanceVersion(new IdType("Patient/A/_history/2")).named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
diff = executable.execute(); assertThat(diff.getParameter()).hasSize(1);
assertThat(diff.getParameter()).hasSize(1);
} else { try {
try { myClient.operation().onInstance("Observation/B").named(ProviderConstants.DIFF_OPERATION_NAME).withNoParameters(Parameters.class).execute();
executable.execute(); fail();
fail(); } catch (ForbiddenOperationException e) {
} catch (ForbiddenOperationException e) { // good
// good
}
} }
} }
@Test @Test
@ -969,8 +943,8 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
@Override @Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder() return new RuleBuilder()
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andRequireExplicitResponseAuthorization().andThen() .allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onAnyInstance().andAllowAllResponses().andThen()
.allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andRequireExplicitResponseAuthorization().andThen() .allow().operation().named(ProviderConstants.DIFF_OPERATION_NAME).onServer().andAllowAllResponses().andThen()
.allow().read().resourcesOfType(Patient.class).withAnyId().andThen() .allow().read().resourcesOfType(Patient.class).withAnyId().andThen()
.denyAll() .denyAll()
.build(); .build();
@ -1003,72 +977,6 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
} }
@Test
public void testDocumentOperation_withExplicitAuthorization() {
IIdType patientId = createPatient();
IIdType compositionId = createResource("Composition", withSubject(patientId));
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().read().instance(patientId).andThen()
.allow().operation().named("$document").onInstance(compositionId).andRequireExplicitResponseAuthorization().andThen()
.allow().read().instance(compositionId).andThen()
.denyAll()
.build();
}
};
myServer.getRestfulServer().registerInterceptor(interceptor);
Bundle bundle = myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
assertEquals(2, bundle.getEntry().size());
}
@Test
public void testDocumentOperation_explicitAuthorizationNotNeeded() {
IIdType patientId = createPatient();
IIdType compositionId = createResource("Composition", withSubject(patientId));
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().operation().named("$document").onInstance(compositionId).andAllowAllResponses().andThen()
.denyAll()
.build();
}
};
myServer.getRestfulServer().registerInterceptor(interceptor);
Bundle bundle = myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
assertEquals(2, bundle.getEntry().size());
}
@Test
public void testDocumentOperation_withoutExplicitAuthorization() {
IIdType patientId = createPatient();
IIdType compositionId = createResource("Composition", withSubject(patientId));
AuthorizationInterceptor interceptor = new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().operation().named("$document").onInstance(compositionId).andRequireExplicitResponseAuthorization().andThen()
.allow().read().instance(compositionId).andThen()
.denyAll()
.build();
}
};
myServer.getRestfulServer().registerInterceptor(interceptor);
try {
myClient.operation().onInstanceVersion(compositionId).named("$document").withNoParameters(Parameters.class).returnResourceType(Bundle.class).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
}
@Test @Test
public void testGraphQL_AllowedByType_Instance() throws IOException { public void testGraphQL_AllowedByType_Instance() throws IOException {
@ -1297,17 +1205,8 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp)); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
} }
@ParameterizedTest @Test
@CsvSource({ public void testOperationEverything_SomeIncludedResourcesNotAuthorized() {
// RequireExplicitResponseAuthorization , AddNotExplicitlyAuthorizedResources , ShouldSucceed
"true , false , true",
"true , true , false",
"false , false , true",
"false , true , true",
})
public void testOperationEverything_SomeIncludedResourcesNotAuthorized(boolean theRequireExplicitResponseAuthorization, boolean theAddNotExplicitlyAuthorizedResources, boolean theShouldSucceed) {
int expectedCount = 2;
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.setActive(true); pt1.setActive(true);
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless(); final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
@ -1317,57 +1216,45 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
obs1.setSubject(new Reference(pid1)); obs1.setSubject(new Reference(pid1));
myClient.create().resource(obs1).execute(); myClient.create().resource(obs1).execute();
if (theAddNotExplicitlyAuthorizedResources) {
// Add an Encounter, which will be returned by $everything but that hasn't been
// explicitly authorized
Encounter enc = new Encounter();
enc.setSubject(new Reference(pid1));
myClient.create().resource(enc).execute();
Organization org = new Organization();
org.setName("Hello");
IIdType orgId = myClient.create().resource(org).execute().getId().toUnqualifiedVersionless();
expectedCount++;
pt1.setId(pid1);
pt1.setManagingOrganization(new Reference(orgId));
myClient.update().resource(pt1).execute();
expectedCount++;
}
myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { myServer.getRestfulServer().registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@SuppressWarnings("deprecation")
@Override @Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
IAuthRuleBuilder ruleBuilder = new RuleBuilder(); return new RuleBuilder()
if (theRequireExplicitResponseAuthorization) { .allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andRequireExplicitResponseAuthorization().andThen()
ruleBuilder.allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andRequireExplicitResponseAuthorization().andThen(); .allow().read().resourcesOfType(Patient.class).inCompartment("Patient", pid1).andThen()
} else { .allow().read().resourcesOfType(Observation.class).inCompartment("Patient", pid1).andThen()
ruleBuilder.allow().operation().named(JpaConstants.OPERATION_EVERYTHING).onInstance(pid1).andAllowAllResponsesWithAllResourcesAccess().andThen(); .allow().create().resourcesOfType(Encounter.class).withAnyId().andThen()
} .build();
ruleBuilder.allow().read().resourcesOfType(Patient.class).inCompartment("Patient", pid1).andThen();
ruleBuilder.allow().read().resourcesOfType(Observation.class).inCompartment("Patient", pid1).andThen();
return ruleBuilder.build();
} }
}); });
IOperationUntypedWithInput<Bundle> executable = myClient Bundle outcome = myClient
.operation() .operation()
.onInstance(pid1) .onInstance(pid1)
.named(JpaConstants.OPERATION_EVERYTHING) .named(JpaConstants.OPERATION_EVERYTHING)
.withNoParameters(Parameters.class) .withNoParameters(Parameters.class)
.returnResourceType(Bundle.class); .returnResourceType(Bundle.class)
.execute();
assertThat(outcome.getEntry()).hasSize(2);
if (theShouldSucceed) { // Add an Encounter, which will be returned by $everything but that hasn't been
Bundle outcome = executable.execute(); // explicitly authorized
assertThat(outcome.getEntry()).hasSize(expectedCount);
} else { Encounter enc = new Encounter();
try { enc.setSubject(new Reference(pid1));
executable.execute(); myClient.create().resource(enc).execute();
fail();
} catch (ForbiddenOperationException e) { try {
assertThat(e.getMessage()).contains("Access denied by default policy"); myClient
} .operation()
.onInstance(pid1)
.named(JpaConstants.OPERATION_EVERYTHING)
.withNoParameters(Parameters.class)
.returnResourceType(Bundle.class)
.execute();
fail();
} catch (ForbiddenOperationException e) {
assertThat(e.getMessage()).contains("Access denied by default policy");
} }
} }
@ -2142,7 +2029,6 @@ public class AuthorizationInterceptorJpaR4Test extends BaseResourceProviderR4Tes
super(theResourceTypes); super(theResourceTypes);
} }
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
List<IAuthRule> rules = new ArrayList<>(super.buildRuleList(theRequestDetails)); List<IAuthRule> rules = new ArrayList<>(super.buildRuleList(theRequestDetails));
List<IAuthRule> rulesToAdd = new RuleBuilder().allow().transaction().withAnyOperation().andApplyNormalRules().build(); List<IAuthRule> rulesToAdd = new RuleBuilder().allow().transaction().withAnyOperation().andApplyNormalRules().build();

View File

@ -164,8 +164,7 @@ public class AuthorizationInterceptor implements IRuleApplier {
rules.size(), rules.size(),
getPointcutNameOrEmpty(thePointcut), getPointcutNameOrEmpty(thePointcut),
getResourceTypeOrEmpty(theInputResource), getResourceTypeOrEmpty(theInputResource),
getResourceTypeOrEmpty(theOutputResource), getResourceTypeOrEmpty(theOutputResource));
thePointcut);
Verdict verdict = null; Verdict verdict = null;
for (IAuthRule nextRule : rules) { for (IAuthRule nextRule : rules) {
@ -529,7 +528,7 @@ public class AuthorizationInterceptor implements IRuleApplier {
case EXTENDED_OPERATION_TYPE: case EXTENDED_OPERATION_TYPE:
case EXTENDED_OPERATION_INSTANCE: { case EXTENDED_OPERATION_INSTANCE: {
if (theResponseObject != null) { if (theResponseObject != null) {
resources = toListOfResourcesAndExcludeContainerUnlessStandalone(theResponseObject, fhirContext); resources = toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
} }
break; break;
} }
@ -576,7 +575,7 @@ public class AuthorizationInterceptor implements IRuleApplier {
OUT, OUT,
} }
protected static List<IBaseResource> toListOfResourcesAndExcludeContainerUnlessStandalone( protected static List<IBaseResource> toListOfResourcesAndExcludeContainer(
IBaseResource theResponseObject, FhirContext fhirContext) { IBaseResource theResponseObject, FhirContext fhirContext) {
if (theResponseObject == null) { if (theResponseObject == null) {
return Collections.emptyList(); return Collections.emptyList();
@ -589,13 +588,6 @@ public class AuthorizationInterceptor implements IRuleApplier {
return Collections.singletonList(theResponseObject); return Collections.singletonList(theResponseObject);
} }
return toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
}
@Nonnull
public static List<IBaseResource> toListOfResourcesAndExcludeContainer(
IBaseResource theResponseObject, FhirContext fhirContext) {
List<IBaseResource> retVal;
retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class); retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
// Exclude the container // Exclude the container

View File

@ -27,9 +27,11 @@ public interface IAuthRuleBuilderOperationNamedAndScoped {
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses(); IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses();
/** /**
* @deprecated This is a synonym for {@link #andAllowAllResponses()}, use that method instead * Responses for this operation will not be checked and access to all resources is allowed. This
* is intended for operations which are known to fetch a graph of resources that is known to be
* safe, such as `$everything` which may access and fetch resources outside the patient's compartment
* but enforces safety in what it fetches via strict SQL queries.
*/ */
@Deprecated(since = "7.6.0")
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponsesWithAllResourcesAccess(); IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponsesWithAllResourcesAccess();
/** /**

View File

@ -85,7 +85,6 @@ class OperationRule extends BaseRule implements IAuthRule {
myAppliesToTypes = theAppliesToTypes; myAppliesToTypes = theAppliesToTypes;
} }
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
@Override @Override
public Verdict applyRule( public Verdict applyRule(
RestOperationTypeEnum theOperation, RestOperationTypeEnum theOperation,
@ -98,8 +97,23 @@ class OperationRule extends BaseRule implements IAuthRule {
Pointcut thePointcut) { Pointcut thePointcut) {
FhirContext ctx = theRequestDetails.getServer().getFhirContext(); FhirContext ctx = theRequestDetails.getServer().getFhirContext();
// Operation rules apply to the execution of the operation itself, not to side effects like
// loading resources (that will presumably be reflected in the response). Those loads need
// to be explicitly authorized
if (!myAllowAllResourcesAccess && isResourceAccess(thePointcut)) {
return null;
}
boolean applies = false; boolean applies = false;
switch (theOperation) { 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: case EXTENDED_OPERATION_SERVER:
if (myAppliesToServer || myAppliesAtAnyLevel) { if (myAppliesToServer || myAppliesAtAnyLevel) {
applies = true; applies = true;
@ -124,7 +138,10 @@ class OperationRule extends BaseRule implements IAuthRule {
applies = true; applies = true;
} else { } else {
IIdType requestResourceId = null; IIdType requestResourceId = null;
if (theRequestDetails.getId() != null) { if (theInputResourceId != null) {
requestResourceId = theInputResourceId;
}
if (requestResourceId == null && myAllowAllResponses) {
requestResourceId = theRequestDetails.getId(); requestResourceId = theRequestDetails.getId();
} }
if (requestResourceId != null) { if (requestResourceId != null) {
@ -151,6 +168,40 @@ class OperationRule extends BaseRule implements IAuthRule {
} }
} }
break; 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: default:
return null; return null;
} }
@ -163,33 +214,13 @@ class OperationRule extends BaseRule implements IAuthRule {
return null; return null;
} }
if (theOutputResource == null) { return newVerdict(
// This is the request part theOperation,
return newVerdict( theRequestDetails,
theOperation, theInputResource,
theRequestDetails, theInputResourceId,
theInputResource, theOutputResource,
theInputResourceId, theRuleApplier);
theOutputResource,
theRuleApplier);
} else {
// This is the response part, so we might want to check all of the
// resources in the response
if (myAllowAllResponses) {
return newVerdict(
theOperation,
theRequestDetails,
theInputResource,
theInputResourceId,
theOutputResource,
theRuleApplier);
} else {
List<IBaseResource> outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(
theOutputResource, theRequestDetails.getFhirContext());
return RuleImplOp.applyRulesToResponseResources(
theRequestDetails, theRuleApplier, thePointcut, outputResources);
}
}
} }
/** /**

View File

@ -828,48 +828,31 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return verdict; return verdict;
} else if (theOutputResource != null) { } else if (theOutputResource != null) {
return applyRulesToResponseBundle(theRequestDetails, theOutputResource, theRuleApplier, thePointcut);
List<IBaseResource> 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, thePointcut);
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 { } else {
return null; return null;
} }
} }
@Nullable
private static Verdict applyRulesToResponseBundle(
RequestDetails theRequestDetails,
IBaseResource theOutputResource,
IRuleApplier theRuleApplier,
Pointcut thePointcut) {
List<IBaseResource> outputResources =
AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(
theOutputResource, theRequestDetails.getFhirContext());
return applyRulesToResponseResources(theRequestDetails, theRuleApplier, thePointcut, outputResources);
}
@Nullable
public static Verdict applyRulesToResponseResources(
RequestDetails theRequestDetails,
IRuleApplier theRuleApplier,
Pointcut thePointcut,
List<IBaseResource> outputResources) {
Verdict verdict = null;
for (IBaseResource nextResource : outputResources) {
if (nextResource == null) {
continue;
}
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(
RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource, thePointcut);
if (newVerdict == null) {
continue;
} else if (verdict == null) {
verdict = newVerdict;
} else if (verdict.getDecision() == PolicyEnum.ALLOW && newVerdict.getDecision() == PolicyEnum.DENY) {
verdict = newVerdict;
}
}
return verdict;
}
private boolean isInvalidNestedBundleRequest(BundleEntryParts theEntry) { private boolean isInvalidNestedBundleRequest(BundleEntryParts theEntry) {
IBaseResource resource = theEntry.getResource(); IBaseResource resource = theEntry.getResource();
if (!(resource instanceof IBaseBundle)) { if (!(resource instanceof IBaseBundle)) {

View File

@ -4230,7 +4230,7 @@ public class AuthorizationInterceptorR4Test extends BaseValidationTestWithInline
RequestDetails requestDetails = new SystemRequestDetails(); RequestDetails requestDetails = new SystemRequestDetails();
requestDetails.setResourceName("Bundle"); requestDetails.setResourceName("Bundle");
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(searchSet, ourCtx); List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(searchSet, ourCtx);
assertEquals(1, resources.size()); assertEquals(1, resources.size());
assertTrue(resources.contains(bundle)); assertTrue(resources.contains(bundle));
} }
@ -4247,7 +4247,7 @@ public class AuthorizationInterceptorR4Test extends BaseValidationTestWithInline
RequestDetails requestDetails = new SystemRequestDetails(); RequestDetails requestDetails = new SystemRequestDetails();
requestDetails.setResourceName("Patient"); requestDetails.setResourceName("Patient");
List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainerUnlessStandalone(searchSet, ourCtx); List<IBaseResource> resources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(searchSet, ourCtx);
assertEquals(2, resources.size()); assertEquals(2, resources.size());
assertTrue(resources.contains(patient1)); assertTrue(resources.contains(patient1));
assertTrue(resources.contains(patient2)); assertTrue(resources.contains(patient2));