Require explicit declaration of authorizationinterceptor operation rules

on whether the response is authorized or not
This commit is contained in:
James Agnew 2018-11-23 14:25:46 -05:00
parent 364b6cc5fd
commit b41c222880
10 changed files with 244 additions and 107 deletions

View File

@ -34,11 +34,11 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
if (isBlank(authHeader)) {
return new RuleBuilder()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen()
.deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onServer().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().andThen()
.deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andAllowAllResponses().andThen()
.deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onServer().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyType().andAllowAllResponses().andThen()
.deny().operation().named(JpaConstants.OPERATION_EXPUNGE).onAnyInstance().andAllowAllResponses().andThen()
.allowAll()
.build();
}

View File

@ -108,5 +108,4 @@ abstract class BaseRule implements IAuthRule {
Verdict newVerdict() {
return new Verdict(myMode, this);
}
}

View File

@ -28,36 +28,36 @@ public interface IAuthRuleBuilderOperationNamed {
/**
* Rule applies to invocations of this operation at the <code>server</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onServer();
IAuthRuleBuilderOperationNamedAndScoped onServer();
/**
* Rule applies to invocations of this operation at the <code>type</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType);
IAuthRuleBuilderOperationNamedAndScoped onType(Class<? extends IBaseResource> theType);
/**
* Rule applies to invocations of this operation at the <code>type</code> level on any type
*/
IAuthRuleBuilderRuleOpClassifierFinished onAnyType();
IAuthRuleBuilderOperationNamedAndScoped onAnyType();
/**
* Rule applies to invocations of this operation at the <code>instance</code> level
*/
IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId);
IAuthRuleBuilderOperationNamedAndScoped onInstance(IIdType theInstanceId);
/**
* Rule applies to invocations of this operation at the <code>instance</code> level on any instance of the given type
*/
IAuthRuleBuilderRuleOpClassifierFinished onInstancesOfType(Class<? extends IBaseResource> theType);
IAuthRuleBuilderOperationNamedAndScoped onInstancesOfType(Class<? extends IBaseResource> theType);
/**
* Rule applies to invocations of this operation at the <code>instance</code> level on any instance
*/
IAuthRuleBuilderRuleOpClassifierFinished onAnyInstance();
IAuthRuleBuilderOperationNamedAndScoped onAnyInstance();
/**
* Rule applies to invocations of this operation at any level (server, type or instance)
*/
IAuthRuleBuilderRuleOpClassifierFinished atAnyLevel();
IAuthRuleBuilderOperationNamedAndScoped atAnyLevel();
}

View File

@ -0,0 +1,19 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
public interface IAuthRuleBuilderOperationNamedAndScoped {
/**
* Responses for this operation will not be checked
*/
IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses();
/**
* Responses for this operation must be authorized by other rules. For example, if this
* rule is authorizing the Patient $everything operation, there must be a separate
* rule (or rules) that actually authorize the user to read the
* resources being returned
*/
IAuthRuleBuilderRuleOpClassifierFinished andRequireExplicitResponseAuthorization();
}

View File

@ -41,6 +41,7 @@ class OperationRule extends BaseRule implements IAuthRule {
private boolean myAppliesToAnyType;
private boolean myAppliesToAnyInstance;
private boolean myAppliesAtAnyLevel;
private boolean myAllowAllResponses;
OperationRule(String theRuleName) {
super(theRuleName);
@ -50,6 +51,10 @@ class OperationRule extends BaseRule implements IAuthRule {
myAppliesAtAnyLevel = theAppliesAtAnyLevel;
}
public void allowAllResponses() {
myAllowAllResponses = true;
}
void appliesToAnyInstance() {
myAppliesToAnyInstance = true;
}
@ -114,9 +119,17 @@ class OperationRule extends BaseRule implements IAuthRule {
case EXTENDED_OPERATION_INSTANCE:
if (myAppliesToAnyInstance || myAppliesAtAnyLevel) {
applies = true;
} else if (theInputResourceId != null) {
} else {
IIdType requestResourceId = null;
if (theInputResourceId != null) {
requestResourceId = theInputResourceId;
}
if (requestResourceId == null && myAllowAllResponses) {
requestResourceId = theRequestDetails.getId();
}
if (requestResourceId != null) {
if (myAppliesToIds != null) {
String instanceId = theInputResourceId.toUnqualifiedVersionless().getValue();
String instanceId = requestResourceId .toUnqualifiedVersionless().getValue();
for (IIdType next : myAppliesToIds) {
if (next.toUnqualifiedVersionless().getValue().equals(instanceId)) {
applies = true;
@ -128,13 +141,14 @@ class OperationRule extends BaseRule implements IAuthRule {
// TODO: Convert to a map of strings and keep the result
for (Class<? extends IBaseResource> next : myAppliesToInstancesOfType) {
String resName = ctx.getResourceDefinition(next).getName();
if (resName.equals(theInputResourceId.getResourceType())) {
if (resName.equals(requestResourceId .getResourceType())) {
applies = true;
break;
}
}
}
}
}
break;
case CREATE:
break;

View File

@ -416,6 +416,28 @@ public class RuleBuilder implements IAuthRuleBuilder {
private class RuleBuilderRuleOperationNamed implements IAuthRuleBuilderOperationNamed {
private class RuleBuilderOperationNamedAndScoped implements IAuthRuleBuilderOperationNamedAndScoped {
private final OperationRule myRule;
public RuleBuilderOperationNamedAndScoped(OperationRule theRule) {
myRule = theRule;
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished andAllowAllResponses() {
myRule.allowAllResponses();
myRules.add(myRule);
return new RuleBuilderFinished(myRule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished andRequireExplicitResponseAuthorization() {
myRules.add(myRule);
return new RuleBuilderFinished(myRule);
}
}
private String myOperationName;
RuleBuilderRuleOperationNamed(String theOperationName) {
@ -434,31 +456,28 @@ public class RuleBuilder implements IAuthRuleBuilder {
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onAnyInstance() {
public IAuthRuleBuilderOperationNamedAndScoped onAnyInstance() {
OperationRule rule = createRule();
rule.appliesToAnyInstance();
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished atAnyLevel() {
public IAuthRuleBuilderOperationNamedAndScoped atAnyLevel() {
OperationRule rule = createRule();
rule.appliesAtAnyLevel(true);
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onAnyType() {
public IAuthRuleBuilderOperationNamedAndScoped onAnyType() {
OperationRule rule = createRule();
rule.appliesToAnyType();
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId) {
public IAuthRuleBuilderOperationNamedAndScoped onInstance(IIdType theInstanceId) {
Validate.notNull(theInstanceId, "theInstanceId must not be null");
Validate.notBlank(theInstanceId.getResourceType(), "theInstanceId does not have a resource type");
Validate.notBlank(theInstanceId.getIdPart(), "theInstanceId does not have an ID part");
@ -467,36 +486,32 @@ public class RuleBuilder implements IAuthRuleBuilder {
ArrayList<IIdType> ids = new ArrayList<>();
ids.add(theInstanceId);
rule.appliesToInstances(ids);
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onInstancesOfType(Class<? extends IBaseResource> theType) {
public IAuthRuleBuilderOperationNamedAndScoped onInstancesOfType(Class<? extends IBaseResource> theType) {
validateType(theType);
OperationRule rule = createRule();
rule.appliesToInstancesOfType(toTypeSet(theType));
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onServer() {
public IAuthRuleBuilderOperationNamedAndScoped onServer() {
OperationRule rule = createRule();
rule.appliesToServer();
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType) {
public IAuthRuleBuilderOperationNamedAndScoped onType(Class<? extends IBaseResource> theType) {
validateType(theType);
OperationRule rule = createRule();
rule.appliesToTypes(toTypeSet(theType));
myRules.add(rule);
return new RuleBuilderFinished(rule);
return new RuleBuilderOperationNamedAndScoped(rule);
}
private HashSet<Class<? extends IBaseResource>> toTypeSet(Class<? extends IBaseResource> theType) {

View File

@ -572,7 +572,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().withAnyName().onServer().andThen()
.allow("RULE 1").operation().withAnyName().onServer().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -598,7 +598,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -633,7 +633,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andThen()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.allow("Rule 2").read().allResources().inCompartment("Patient", new IdDt("Patient/1")).andThen()
.build();
}
@ -671,7 +671,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -705,7 +705,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onInstance(new IdDt("http://example.com/Patient/1/_history/2")).andThen()
.allow("RULE 1").operation().named("opName").onInstance(new IdDt("http://example.com/Patient/1/_history/2")).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -764,7 +764,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyInstance().andThen()
.allow("RULE 1").operation().named("opName").onAnyInstance().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -890,7 +890,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onServer().andThen()
.allow("RULE 1").operation().named("opName").onServer().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -937,7 +937,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andThen()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1006,7 +1006,7 @@ public class AuthorizationInterceptorDstu2Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyType().andThen()
.allow("RULE 1").operation().named("opName").onAnyType().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});

View File

@ -882,7 +882,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().withAnyName().onServer().andThen()
.allow("RULE 1").operation().withAnyName().onServer().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -908,7 +908,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").atAnyLevel().andThen()
.allow("RULE 1").operation().named("opName").atAnyLevel().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -964,7 +964,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andThen()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1020,7 +1020,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -1055,7 +1055,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andThen()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen()
.build();
}
@ -1093,7 +1093,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -1127,7 +1127,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onInstance(new IdType("http://example.com/Patient/1/_history/2")).andThen()
.allow("RULE 1").operation().named("opName").onInstance(new IdType("http://example.com/Patient/1/_history/2")).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1186,7 +1186,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyInstance().andThen()
.allow("RULE 1").operation().named("opName").onAnyInstance().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1311,7 +1311,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onServer().andThen()
.allow("RULE 1").operation().named("opName").onServer().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1358,7 +1358,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andThen()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1427,7 +1427,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyType().andThen()
.allow("RULE 1").operation().named("opName").onAnyType().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1495,7 +1495,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Organization.class).andThen()
.allow("RULE 1").operation().named("opName").onType(Organization.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1554,7 +1554,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Patient.class).forTenantIds("TENANTA").andThen()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().forTenantIds("TENANTA").andThen()
.build();
}
});
@ -1591,7 +1591,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("process-message").onType(MessageHeader.class).andThen()
.allow("RULE 1").operation().named("process-message").onType(MessageHeader.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1630,7 +1630,7 @@ public class AuthorizationInterceptorDstu3Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).withTester(new IAuthRuleTester() {
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().withTester(new IAuthRuleTester() {
@Override
public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) {
return theInputResourceId.getIdPart().equals("1");

View File

@ -36,11 +36,10 @@ import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.*;
import org.springframework.util.Base64Utils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -49,6 +48,7 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static javax.print.DocFlavor.READER.TEXT_HTML;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
@ -65,6 +65,7 @@ public class AuthorizationInterceptorR4Test {
private static List<Resource> ourReturn;
private static Server ourServer;
private static RestfulServer ourServlet;
private static String ourLastAcceptHeader;
@Before
public void before() {
@ -76,6 +77,7 @@ public class AuthorizationInterceptorR4Test {
ourReturn = null;
ourHitMethod = false;
ourConditionalCreateId = "1123";
ourLastAcceptHeader = null;
}
private Resource createCarePlan(Integer theId, String theSubjectId) {
@ -922,7 +924,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().withAnyName().onServer().andThen()
.allow("RULE 1").operation().withAnyName().onServer().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -948,7 +950,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").atAnyLevel().andThen()
.allow("RULE 1").operation().named("opName").atAnyLevel().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1004,7 +1006,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andThen()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1060,7 +1062,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -1090,12 +1092,13 @@ public class AuthorizationInterceptorR4Test {
}
@Test
@Ignore
public void testOperationByInstanceOfTypeWithInvalidReturnValue() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andThen()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.allow("Rule 2").read().allResources().inCompartment("Patient", new IdType("Patient/1")).andThen()
.build();
}
@ -1133,7 +1136,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class)
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization()
.build();
}
});
@ -1167,7 +1170,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onInstance(new IdType("http://example.com/Patient/1/_history/2")).andThen()
.allow("RULE 1").operation().named("opName").onInstance(new IdType("http://example.com/Patient/1/_history/2")).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1226,7 +1229,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyInstance().andThen()
.allow("RULE 1").operation().named("opName").onAnyInstance().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1289,6 +1292,31 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testOperationInstanceLevelWithHtmlResponse() throws IOException {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("binaryop").onInstancesOfType(Patient.class).andAllowAllResponses().andThen()
.build();
}
});
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$binaryop");
httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html");
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("text/html", status.getEntity().getContentType().getValue());
assertEquals("<html>TAGS</html>", IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8));
assertEquals("text/html", ourLastAcceptHeader);
}
}
@Test
public void testOperationNotAllowedWithWritePermissiom() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@ -1346,12 +1374,12 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testOperationServerLevel() throws Exception {
public void testOperationServerLevelAllowAllResponses() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onServer().andThen()
.allow("RULE 1").operation().named("opName").onServer().andAllowAllResponses().andThen()
.build();
}
});
@ -1392,13 +1420,48 @@ public class AuthorizationInterceptorR4Test {
assertFalse(ourHitMethod);
}
@Test
public void testOperationServerLevelRequireResponseAuthorization() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onServer().andRequireExplicitResponseAuthorization().andThen()
.allow().read().instance("Observation/10").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);
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(99, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
@Test
public void testOperationTypeLevel() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andThen()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1467,7 +1530,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onAnyType().andThen()
.allow("RULE 1").operation().named("opName").onAnyType().andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1535,7 +1598,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Organization.class).andThen()
.allow("RULE 1").operation().named("opName").onType(Organization.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1594,7 +1657,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Patient.class).forTenantIds("TENANTA").andThen()
.allow("RULE 1").operation().named("opName").onType(Patient.class).andRequireExplicitResponseAuthorization().forTenantIds("TENANTA").andThen()
.build();
}
});
@ -1631,7 +1694,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("process-message").onType(MessageHeader.class).andThen()
.allow("RULE 1").operation().named("process-message").onType(MessageHeader.class).andRequireExplicitResponseAuthorization().andThen()
.build();
}
});
@ -1670,7 +1733,7 @@ public class AuthorizationInterceptorR4Test {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).withTester(new IAuthRuleTester() {
.allow("Rule 1").operation().named("everything").onInstancesOfType(Patient.class).andRequireExplicitResponseAuthorization().withTester(new IAuthRuleTester() {
@Override
public boolean matches(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IIdType theInputResourceId, IBaseResource theInputResource) {
return theInputResourceId.getIdPart().equals("1");
@ -3264,6 +3327,7 @@ public class AuthorizationInterceptorR4Test {
@SuppressWarnings("unused")
public static class DummyPatientResourceProvider implements IResourceProvider {
@Create()
public MethodOutcome create(@ResourceParam Patient theResource, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) {
@ -3303,6 +3367,26 @@ public class AuthorizationInterceptorR4Test {
return retVal;
}
@Operation(name = "$binaryop", idempotent = true)
public Binary binaryOp(
@IdParam IIdType theId,
@OperationParam(name = "PARAM3", min = 0, max = 1) List<org.hl7.fhir.r4.model.StringType> theParam3,
HttpServletRequest theServletRequest
) {
ourLastAcceptHeader = theServletRequest.getHeader(ca.uhn.fhir.rest.api.Constants.HEADER_ACCEPT);
Binary retVal = new Binary();
if (ourLastAcceptHeader.contains("html")) {
retVal.setContentType("text/html");
retVal.setContent("<html>TAGS</html>".getBytes(Charsets.UTF_8));
} else {
retVal.setContentType("application/weird");
retVal.setContent(new byte[]{0,0,1,1,2,2,3,3,0,0});
}
return retVal;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;

View File

@ -76,7 +76,8 @@
ResourceIndexedSearchParams, IdHelperService, SearcchParamExtractorService, and MatchUrlService.
</action>
<action type="add">
Replaced explicit @Bean construction in BaseConfig.java with @ComponentScan. Beans with state are annotated with
Replaced explicit @Bean construction in BaseConfig.java with @ComponentScan. Beans with state are annotated
with
@Component and stateless beans are annotated as @Service. Also changed SearchBuilder.java and the
three Subscriber classes into @Scope("protoype") so their dependencies can be @Autowired injected
as opposed to constructor parameters.
@ -91,6 +92,11 @@
larger batches (20000 instead of 500) in order to reduce the amount of noise
in the logs.
</action>
<action type="add">
AuthorizationInterceptor now allows arbitrary FHIR $operations to be authorized,
including support for either allowing the operation response to proceed unchallenged,
or authorizing the contents of the response.
</action>
</release>
<release version="3.6.0" date="2018-11-12" description="Food">
<action type="add">