Version bump to 3.3.0-SNAPSHOT

This commit is contained in:
James Agnew 2018-01-23 10:30:43 -05:00
parent 4fd3e20d06
commit 16038ece26
58 changed files with 873 additions and 465 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-standalone-overlay-example</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -17,6 +17,7 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
@SuppressWarnings("unused")
public class AuthorizationInterceptors {
public class PatientResourceProvider implements IResourceProvider
@ -35,7 +36,8 @@ public class AuthorizationInterceptors {
}
//START SNIPPET: patientAndAdmin
public class PatientAndAdminAuthorizationInterceptor extends AuthorizationInterceptor {
@SuppressWarnings("ConstantConditions")
public class PatientAndAdminAuthorizationInterceptor extends AuthorizationInterceptor {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
@ -127,5 +129,16 @@ public class AuthorizationInterceptors {
}
//END SNIPPET: conditionalUpdate
public void authorizeTenantAction() {
//START SNIPPET: authorizeTenantAction
new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().read().resourcesOfType(Patient.class).withAnyId().forTenantIds("TENANTA").andThen()
.build();
}
};
//END SNIPPET: authorizeTenantAction
}
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -26,42 +26,42 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
</dependencies>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -494,6 +494,17 @@
<groupId>de.juplo</groupId>
<artifactId>hibernate-maven-plugin</artifactId>
<executions>
<execution>
<id>derby107</id>
<phase>process-classes</phase>
<goals>
<goal>create</goal>
</goals>
<configuration>
<dialect>org.hibernate.dialect.DerbyTenSevenDialect</dialect>
<outputFile>${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database/persistence_create_derby107.sql</outputFile>
</configuration>
</execution>
<execution>
<id>postgres94</id>
<phase>process-classes</phase>

View File

@ -10,7 +10,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -20,6 +20,15 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* #L%
*/
import java.util.Collection;
public interface IAuthRuleBuilderRuleOpClassifierFinished extends IAuthRuleFinished {
// nothing
IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(String... theTenantId);
IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(Collection<String> theTenantId);
IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(String... theTenantIds);
IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(Collection<String> theTenantIds);
}

View File

@ -0,0 +1,25 @@
package ca.uhn.fhir.rest.server.interceptor.auth;
/*
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId extends IAuthRuleFinished {
}

View File

@ -20,23 +20,19 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* #L%
*/
import java.util.HashSet;
import java.util.List;
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.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.HashSet;
import java.util.List;
class OperationRule extends BaseRule implements IAuthRule {
public OperationRule(String theRuleName) {
super(theRuleName);
}
private RuleBuilder.ITenantApplicabilityChecker myTenentApplicabilityChecker;
private String myOperationName;
private boolean myAppliesToServer;
private HashSet<Class<? extends IBaseResource>> myAppliesToTypes;
@ -44,70 +40,92 @@ class OperationRule extends BaseRule implements IAuthRule {
private HashSet<Class<? extends IBaseResource>> myAppliesToInstancesOfType;
private boolean myAppliesToAnyType;
private boolean myAppliesToAnyInstance;
/**
* Must include the leading $
*/
public void setOperationName(String theOperationName) {
myOperationName = theOperationName;
public OperationRule(String theRuleName) {
super(theRuleName);
}
public String getOperationName() {
return myOperationName;
public void appliesToAnyInstance() {
myAppliesToAnyInstance = true;
}
public void appliesToAnyType() {
myAppliesToAnyType = true;
}
public void appliesToInstances(List<IIdType> theAppliesToIds) {
myAppliesToIds = theAppliesToIds;
}
public void appliesToInstancesOfType(HashSet<Class<? extends IBaseResource>> theAppliesToTypes) {
myAppliesToInstancesOfType = theAppliesToTypes;
}
public void appliesToServer() {
myAppliesToServer = true;
}
public void appliesToTypes(HashSet<Class<? extends IBaseResource>> theAppliesToTypes) {
myAppliesToTypes = theAppliesToTypes;
}
@Override
public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource, IRuleApplier theRuleApplier) {
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
if (myTenentApplicabilityChecker != null) {
if (!myTenentApplicabilityChecker.applies(theRequestDetails)) {
return null;
}
}
boolean applies = false;
switch (theOperation) {
case EXTENDED_OPERATION_SERVER:
if (myAppliesToServer) {
applies = true;
}
break;
case EXTENDED_OPERATION_TYPE:
if (myAppliesToAnyType) {
applies = true;
} else if (myAppliesToTypes != null) {
// TODO: Convert to a map of strings and keep the result
for (Class<? extends IBaseResource> next : myAppliesToTypes) {
String resName = ctx.getResourceDefinition(next).getName();
if (resName.equals(theRequestDetails.getResourceName())) {
applies = true;
break;
}
case EXTENDED_OPERATION_SERVER:
if (myAppliesToServer) {
applies = true;
}
}
break;
case EXTENDED_OPERATION_INSTANCE:
if (myAppliesToAnyInstance) {
applies = true;
} else if (theInputResourceId != null) {
if (myAppliesToIds != null) {
String instanceId = theInputResourceId.toUnqualifiedVersionless().getValue();
for (IIdType next : myAppliesToIds) {
if (next.toUnqualifiedVersionless().getValue().equals(instanceId)) {
applies = true;
break;
}
}
}
if (myAppliesToInstancesOfType != null) {
break;
case EXTENDED_OPERATION_TYPE:
if (myAppliesToAnyType) {
applies = true;
} else if (myAppliesToTypes != null) {
// TODO: Convert to a map of strings and keep the result
for (Class<? extends IBaseResource> next : myAppliesToInstancesOfType) {
for (Class<? extends IBaseResource> next : myAppliesToTypes) {
String resName = ctx.getResourceDefinition(next).getName();
if (resName.equals(theInputResourceId.getResourceType())) {
if (resName.equals(theRequestDetails.getResourceName())) {
applies = true;
break;
}
}
}
}
break;
default:
return null;
break;
case EXTENDED_OPERATION_INSTANCE:
if (myAppliesToAnyInstance) {
applies = true;
} else if (theInputResourceId != null) {
if (myAppliesToIds != null) {
String instanceId = theInputResourceId.toUnqualifiedVersionless().getValue();
for (IIdType next : myAppliesToIds) {
if (next.toUnqualifiedVersionless().getValue().equals(instanceId)) {
applies = true;
break;
}
}
}
if (myAppliesToInstancesOfType != null) {
// 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())) {
applies = true;
break;
}
}
}
}
break;
default:
return null;
}
if (!applies) {
@ -121,28 +139,19 @@ class OperationRule extends BaseRule implements IAuthRule {
return newVerdict();
}
public void appliesToServer() {
myAppliesToServer = true;
public String getOperationName() {
return myOperationName;
}
public void appliesToTypes(HashSet<Class<? extends IBaseResource>> theAppliesToTypes) {
myAppliesToTypes = theAppliesToTypes;
/**
* Must include the leading $
*/
public void setOperationName(String theOperationName) {
myOperationName = theOperationName;
}
public void appliesToInstances(List<IIdType> theAppliesToIds) {
myAppliesToIds = theAppliesToIds;
}
public void appliesToInstancesOfType(HashSet<Class<? extends IBaseResource>> theAppliesToTypes) {
myAppliesToInstancesOfType = theAppliesToTypes;
}
public void appliesToAnyInstance() {
myAppliesToAnyInstance = true;
}
public void appliesToAnyType() {
myAppliesToAnyType = true;
public void setTenentApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenentApplicabilityChecker) {
myTenentApplicabilityChecker = theTenentApplicabilityChecker;
}
}

View File

@ -19,21 +19,25 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* limitations under the License.
* #L%
*/
import java.util.*;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import java.util.*;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
public class RuleBuilder implements IAuthRuleBuilder {
public static final String[] EMPTY_STRING_ARRAY = new String[0];
private ArrayList<IAuthRule> myRules;
public RuleBuilder() {
myRules = new ArrayList<IAuthRule>();
myRules = new ArrayList<>();
}
@Override
@ -53,8 +57,9 @@ public class RuleBuilder implements IAuthRuleBuilder {
@Override
public IAuthRuleBuilderRuleOpClassifierFinished allowAll(String theRuleName) {
myRules.add(new RuleImplOp(theRuleName).setOp(RuleOpEnum.ALLOW_ALL));
return new RuleBuilderFinished();
RuleImplOp rule = new RuleImplOp(theRuleName);
myRules.add(rule.setOp(RuleOpEnum.ALLOW_ALL));
return new RuleBuilderFinished(rule);
}
@Override
@ -79,11 +84,26 @@ public class RuleBuilder implements IAuthRuleBuilder {
@Override
public IAuthRuleBuilderRuleOpClassifierFinished denyAll(String theRuleName) {
myRules.add(new RuleImplOp(theRuleName).setOp(RuleOpEnum.DENY_ALL));
return new RuleBuilderFinished();
RuleImplOp rule = new RuleImplOp(theRuleName);
myRules.add(rule.setOp(RuleOpEnum.DENY_ALL));
return new RuleBuilderFinished(rule);
}
private class RuleBuilderFinished implements IAuthRuleFinished, IAuthRuleBuilderRuleOpClassifierFinished {
private class RuleBuilderFinished implements IAuthRuleFinished, IAuthRuleBuilderRuleOpClassifierFinished, IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId {
private final RuleImplOp myOpRule;
private final OperationRule myOperationRule;
protected ITenantApplicabilityChecker myTenantApplicabilityChecker;
RuleBuilderFinished(RuleImplOp theRule) {
myOpRule = theRule;
myOperationRule = null;
}
public RuleBuilderFinished(OperationRule theRule) {
myOpRule = null;
myOperationRule = theRule;
}
@Override
public IAuthRuleBuilder andThen() {
@ -103,6 +123,43 @@ public class RuleBuilder implements IAuthRuleBuilder {
protected void doBuildRule() {
// nothing
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(String... theTenantIds) {
return forTenantIds(Arrays.asList(defaultIfNull(theTenantIds, EMPTY_STRING_ARRAY)));
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId forTenantIds(final Collection<String> theTenantIds) {
myTenantApplicabilityChecker = new ITenantApplicabilityChecker(){
@Override
public boolean applies(RequestDetails theRequest) {
return theTenantIds.contains(theRequest.getTenantId());
}
};
if (myOpRule != null) {
myOpRule.setTenantApplicabilityChecker(myTenantApplicabilityChecker);
}
if (myOperationRule != null) {
myOperationRule.setTenentApplicabilityChecker(myTenantApplicabilityChecker);
}
return this;
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(String... theTenantIds) {
return notForTenantIds(Arrays.asList(defaultIfNull(theTenantIds, EMPTY_STRING_ARRAY)));
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinishedWithTenantId notForTenantIds(Collection<String> theTenantIds) {
return null;// TODO: implement method body
}
}
public interface ITenantApplicabilityChecker
{
boolean applies(RequestDetails theRequest);
}
private class RuleBuilderRule implements IAuthRuleBuilderRule {
@ -111,7 +168,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private String myRuleName;
private RuleOpEnum myRuleOp;
public RuleBuilderRule(PolicyEnum theRuleMode, String theRuleName) {
RuleBuilderRule(PolicyEnum theRuleMode, String theRuleName) {
myRuleMode = theRuleMode;
myRuleName = theRuleName;
}
@ -138,7 +195,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
rule.setOp(RuleOpEnum.METADATA);
rule.setMode(myRuleMode);
myRules.add(rule);
return new RuleBuilderFinished();
return new RuleBuilderFinished(rule);
}
@Override
@ -176,7 +233,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private Set<?> myAppliesToTypes;
private RestOperationTypeEnum myOperationType;
public RuleBuilderRuleConditional(RestOperationTypeEnum theOperationType) {
RuleBuilderRuleConditional(RestOperationTypeEnum theOperationType) {
myOperationType = theOperationType;
}
@ -196,6 +253,10 @@ public class RuleBuilder implements IAuthRuleBuilder {
public class RuleBuilderRuleConditionalClassifier extends RuleBuilderFinished implements IAuthRuleBuilderRuleConditionalClassifier {
public RuleBuilderRuleConditionalClassifier() {
super((RuleImplOp) null);
}
@Override
protected void doBuildRule() {
RuleImplConditional rule = new RuleImplConditional(myRuleName);
@ -203,6 +264,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
rule.setOperationType(myOperationType);
rule.setAppliesTo(myAppliesTo);
rule.setAppliesToTypes(myAppliesToTypes);
rule.setTenantApplicabilityChecker(myTenantApplicabilityChecker);
myRules.add(rule);
}
@ -221,6 +283,21 @@ public class RuleBuilder implements IAuthRuleBuilder {
return new RuleBuilderRuleOpClassifier();
}
@Override
public IAuthRuleFinished instance(String theId) {
Validate.notBlank(theId, "theId must not be null or empty");
return instance(new IdDt(theId));
}
@Override
public IAuthRuleFinished instance(IIdType theId) {
Validate.notNull(theId, "theId must not be null");
Validate.notBlank(theId.getValue(), "theId.getValue() must not be null or empty");
Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
return new RuleBuilderRuleOpClassifier(Arrays.asList(theId)).finished();
}
@Override
public IAuthRuleBuilderRuleOpClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
@ -239,14 +316,14 @@ public class RuleBuilder implements IAuthRuleBuilder {
/**
* Constructor
*/
public RuleBuilderRuleOpClassifier() {
RuleBuilderRuleOpClassifier() {
super();
}
/**
* Constructor
*/
public RuleBuilderRuleOpClassifier(List<IIdType> theAppliesToInstances) {
RuleBuilderRuleOpClassifier(List<IIdType> theAppliesToInstances) {
myAppliesToInstances = theAppliesToInstances;
myAppliesTo = AppliesTypeEnum.INSTANCES;
}
@ -264,7 +341,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
rule.setClassifierCompartmentOwners(myInCompartmentOwners);
myRules.add(rule);
return new RuleBuilderFinished();
return new RuleBuilderFinished(rule);
}
@Override
@ -303,21 +380,6 @@ public class RuleBuilder implements IAuthRuleBuilder {
}
@Override
public IAuthRuleFinished instance(String theId) {
Validate.notBlank(theId, "theId must not be null or empty");
return instance(new IdDt(theId));
}
@Override
public IAuthRuleFinished instance(IIdType theId) {
Validate.notNull(theId, "theId must not be null");
Validate.notBlank(theId.getValue(), "theId.getValue() must not be null or empty");
Validate.notBlank(theId.getIdPart(), "theId must contain an ID part");
return new RuleBuilderRuleOpClassifier(Arrays.asList(theId)).finished();
}
}
private class RuleBuilderRuleOperation implements IAuthRuleBuilderOperation {
@ -352,6 +414,22 @@ public class RuleBuilder implements IAuthRuleBuilder {
return rule;
}
@Override
public IAuthRuleFinished onAnyInstance() {
OperationRule rule = createRule();
rule.appliesToAnyInstance();
myRules.add(rule);
return new RuleBuilderFinished(rule);
}
@Override
public IAuthRuleFinished onAnyType() {
OperationRule rule = createRule();
rule.appliesToAnyType();
myRules.add(rule);
return new RuleBuilderFinished(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId) {
Validate.notNull(theInstanceId, "theInstanceId must not be null");
@ -363,29 +441,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
ids.add(theInstanceId);
rule.appliesToInstances(ids);
myRules.add(rule);
return new RuleBuilderFinished();
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onServer() {
OperationRule rule = createRule();
rule.appliesToServer();
myRules.add(rule);
return new RuleBuilderFinished();
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType) {
validateType(theType);
OperationRule rule = createRule();
rule.appliesToTypes(toTypeSet(theType));
myRules.add(rule);
return new RuleBuilderFinished();
}
private void validateType(Class<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
return new RuleBuilderFinished(rule);
}
@Override
@ -395,7 +451,25 @@ public class RuleBuilder implements IAuthRuleBuilder {
OperationRule rule = createRule();
rule.appliesToInstancesOfType(toTypeSet(theType));
myRules.add(rule);
return new RuleBuilderFinished();
return new RuleBuilderFinished(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onServer() {
OperationRule rule = createRule();
rule.appliesToServer();
myRules.add(rule);
return new RuleBuilderFinished(rule);
}
@Override
public IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType) {
validateType(theType);
OperationRule rule = createRule();
rule.appliesToTypes(toTypeSet(theType));
myRules.add(rule);
return new RuleBuilderFinished(rule);
}
private HashSet<Class<? extends IBaseResource>> toTypeSet(Class<? extends IBaseResource> theType) {
@ -404,20 +478,8 @@ public class RuleBuilder implements IAuthRuleBuilder {
return appliesToTypes;
}
@Override
public IAuthRuleFinished onAnyType() {
OperationRule rule = createRule();
rule.appliesToAnyType();
myRules.add(rule);
return new RuleBuilderFinished();
}
@Override
public IAuthRuleFinished onAnyInstance() {
OperationRule rule = createRule();
rule.appliesToAnyInstance();
myRules.add(rule);
return new RuleBuilderFinished();
private void validateType(Class<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
}
}
@ -440,7 +502,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
rule.setOp(myRuleOp);
rule.setTransactionAppliesToOp(TransactionAppliesToEnum.ANY_OPERATION);
myRules.add(rule);
return new RuleBuilderFinished();
return new RuleBuilderFinished(rule);
}
}

View File

@ -20,28 +20,28 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* #L%
*/
import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
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 org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.Set;
public class RuleImplConditional extends BaseRule implements IAuthRule {
private AppliesTypeEnum myAppliesTo;
private Set<?> myAppliesToTypes;
private RestOperationTypeEnum myOperationType;
private RuleBuilder.ITenantApplicabilityChecker myTenantApplicabilityChecker;
public RuleImplConditional(String theRuleName) {
RuleImplConditional(String theRuleName) {
super(theRuleName);
}
@Override
public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource,
IRuleApplier theRuleApplier) {
IRuleApplier theRuleApplier) {
if (theInputResourceId != null) {
return null;
@ -49,20 +49,26 @@ public class RuleImplConditional extends BaseRule implements IAuthRule {
if (theOperation == myOperationType) {
switch (myAppliesTo) {
case ALL_RESOURCES:
case INSTANCES:
break;
case TYPES:
if (theInputResource == null || !myAppliesToTypes.contains(theInputResource.getClass())) {
return null;
}
break;
case ALL_RESOURCES:
case INSTANCES:
break;
case TYPES:
if (theInputResource == null || !myAppliesToTypes.contains(theInputResource.getClass())) {
return null;
}
break;
}
if (theRequestDetails.getConditionalUrl(myOperationType) == null) {
return null;
}
if (myTenantApplicabilityChecker != null) {
if (!myTenantApplicabilityChecker.applies(theRequestDetails)) {
return null;
}
}
return newVerdict();
}
@ -81,4 +87,8 @@ public class RuleImplConditional extends BaseRule implements IAuthRule {
myOperationType = theOperationType;
}
public void setTenantApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenantApplicabilityChecker) {
myTenantApplicabilityChecker = theTenantApplicabilityChecker;
}
}

View File

@ -52,6 +52,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
private RuleOpEnum myOp;
private TransactionAppliesToEnum myTransactionAppliesToOp;
private List<IIdType> myAppliesToInstances;
private RuleBuilder.ITenantApplicabilityChecker myTenantApplicabilityChecker;
public RuleImplOp(String theRuleName) {
super(theRuleName);
@ -60,6 +61,13 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
@Override
public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId, IBaseResource theOutputResource,
IRuleApplier theRuleApplier) {
if (myTenantApplicabilityChecker != null) {
if (!myTenantApplicabilityChecker.applies(theRequestDetails)) {
return null;
}
}
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
IBaseResource appliesToResource;
@ -275,6 +283,10 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return newVerdict();
}
public void setTenantApplicabilityChecker(RuleBuilder.ITenantApplicabilityChecker theTenantApplicabilityChecker) {
myTenantApplicabilityChecker = theTenantApplicabilityChecker;
}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -282,6 +294,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
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);

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -16,6 +16,7 @@ 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.rest.server.tenant.UrlBaseTenantIdentificationStrategy;
import ca.uhn.fhir.util.PortUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
@ -65,9 +66,10 @@ public class AuthorizationInterceptorR4Test {
@Before
public void before() {
ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER);
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourServlet.getInterceptors())) {
for (IServerInterceptor next : new ArrayList<>(ourServlet.getInterceptors())) {
ourServlet.unregisterInterceptor(next);
}
ourServlet.setTenantIdentificationStrategy(null);
ourReturn = null;
ourHitMethod = false;
ourConditionalCreateId = "1123";
@ -236,6 +238,50 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testAllowAllForTenant() throws Exception {
ourServlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny("Rule 1").read().resourcesOfType(Patient.class).withAnyId().forTenantIds("TENANTA").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 + "/TENANTA/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 + "/TENANTA/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 + "/TENANTA/Patient/1/$validate");
status = ourClient.execute(httpGet);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
/**
* #528
*/
@ -244,7 +290,10 @@ public class AuthorizationInterceptorR4Test {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder().allow("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).andThen().denyAll().build();
return new RuleBuilder()
.allow("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a"))
.andThen().denyAll()
.build();
}
});
@ -269,6 +318,40 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testAllowByCompartmentWithAnyTypeWithTenantId() throws Exception {
ourServlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").read().allResources().inCompartment("Patient", new IdType("Patient/845bd9f1-3635-4866-a6c8-1ca085df5c1a")).forTenantIds("TENANTA")
.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 + "/TENANTA/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 + "/TENANTA/CarePlan/135154");
status = ourClient.execute(httpGet);
extractResponseAndClose(status);
assertEquals(403, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
/**
* #528
*/
@ -521,6 +604,42 @@ public class AuthorizationInterceptorR4Test {
}
/**
* #528
*/
@Test
public void testDenyActionsNotOnTenant() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.ALLOW) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder().denyAll().notForTenantIds("TENANTA", "TENANTB").build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
ourReturn = Collections.singletonList(createPatient(2));
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTA/Patient/1");
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 + "/TENANTC/Patient/1");
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);
}
/**
* #528
*/
@ -660,7 +779,7 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testInvalidInstanceIds() throws Exception {
public void testInvalidInstanceIds() {
try {
new RuleBuilder().allow("Rule 1").write().instance((String) null);
fail();
@ -1176,6 +1295,43 @@ public class AuthorizationInterceptorR4Test {
assertFalse(ourHitMethod);
}
@Test
public void testOperationTypeLevelWithTenant() throws Exception {
ourServlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
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).forTenantIds("TENANTA").andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Right Tenant
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTA/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Wrong Tenant
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTC/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
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) {
@ -1307,6 +1463,79 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testReadByAnyIdWithTenantId() throws Exception {
ourServlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").read().resourcesOfType(Patient.class).withAnyId().forTenantIds("TENANTA")
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
ourReturn = Collections.singletonList(createPatient(2));
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTA/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 + "/TENANTB/Patient/1");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertThat(response, containsString("Access denied by default policy (no applicable rules)"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
ourReturn = Collections.singletonList(createPatient(2));
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTA/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 + "/TENANTA/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 + "/TENANTA/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 + "/TENANTA/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) {
@ -1438,7 +1667,7 @@ public class AuthorizationInterceptorR4Test {
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/900");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -1446,7 +1675,7 @@ public class AuthorizationInterceptorR4Test {
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/901");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -1551,7 +1780,7 @@ public class AuthorizationInterceptorR4Test {
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);
@ -1640,7 +1869,7 @@ public class AuthorizationInterceptorR4Test {
httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation");
httpPost.setEntity(createFhirResourceEntity(createObservation(null, "Patient/1")));
status = ourClient.execute(httpPost);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(201, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
@ -1886,7 +2115,7 @@ public class AuthorizationInterceptorR4Test {
httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar");
httpPost.setEntity(createFhirResourceEntity(createPatient(null)));
status = ourClient.execute(httpPost);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -1924,7 +2153,7 @@ public class AuthorizationInterceptorR4Test {
httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?foo=bar");
httpPost.setEntity(createFhirResourceEntity(createPatient(null)));
status = ourClient.execute(httpPost);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -1961,7 +2190,7 @@ public class AuthorizationInterceptorR4Test {
httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/900");
httpPost.setEntity(createFhirResourceEntity(createObservation(900, "Patient/12")));
status = ourClient.execute(httpPost);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -1969,7 +2198,7 @@ public class AuthorizationInterceptorR4Test {
httpPost = new HttpPut("http://localhost:" + ourPort + "/Observation/901");
httpPost.setEntity(createFhirResourceEntity(createObservation(901, "Patient/12")));
status = ourClient.execute(httpPost);
response = extractResponseAndClose(status);
extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -2008,7 +2237,6 @@ public class AuthorizationInterceptorR4Test {
HttpEntityEnclosingRequestBase httpPost;
HttpResponse status;
String response;
String input = "[ { \"op\": \"replace\", \"path\": \"/gender\", \"value\": \"male\" } ]";
@ -2016,7 +2244,7 @@ public class AuthorizationInterceptorR4Test {
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);
extractResponseAndClose(status);
assertEquals(204, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
@ -2024,7 +2252,7 @@ public class AuthorizationInterceptorR4Test {
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);
extractResponseAndClose(status);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
@ -2085,6 +2313,7 @@ public class AuthorizationInterceptorR4Test {
}
}
@SuppressWarnings("unused")
public static class DummyEncounterResourceProvider implements IResourceProvider {
@Operation(name = "everything", idempotent = true)
@ -2103,6 +2332,7 @@ public class AuthorizationInterceptorR4Test {
}
}
@SuppressWarnings("unused")
public static class DummyObservationResourceProvider implements IResourceProvider {
@Create()
@ -2118,8 +2348,7 @@ public class AuthorizationInterceptorR4Test {
@Delete()
public MethodOutcome delete(@IdParam IdType theId) {
ourHitMethod = true;
MethodOutcome retVal = new MethodOutcome();
return retVal;
return new MethodOutcome();
}
@Override
@ -2201,8 +2430,7 @@ public class AuthorizationInterceptorR4Test {
for (IBaseResource next : ourReturn) {
theRequestOperationCallback.resourceDeleted(next);
}
MethodOutcome retVal = new MethodOutcome();
return retVal;
return new MethodOutcome();
}
@Operation(name = "everything", idempotent = true)

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -11,7 +11,7 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<name>HAPI-FHIR</name>
<url>https://hapifhir.io</url>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -8,7 +8,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.2.0</version>
<version>3.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,6 +7,20 @@
</properties>
<body>
<release version="3.3.0" date="TBD">
<action type="add">
This release corrects an ineffiency in the JPA Server, but requires a schema
change in order to update. Prior to this version of HAPI FHIR, a CLOB column
containing the complete resource body was stored in two
tables: HFJ_RESOURCE and HFJ_RES_VER. Because the same content was stored in two
places, the database consumed more space than is needed to.
<![CDATA[<br/><br/>]]>
In order to reduce this duplication, the columns have been removed from the
HFJ_RESOURCE column. This means that on any database that is being upgraded
to HAPI FHIR 3.2.0, you will need to remove the columns
<![CDATA[<code>RES_TEXT</code> and <code>RES_ENCODING</code>]]> (or
set them to nullable if you want an easy means of rolling back). Naturally
you should back your database up prior to making this change.
</action>
<action type="fix">
Fix a crash in the JSON parser when parsing extensions on repeatable
elements (e.g. Patient.address.line) where there is an extension on the

View File

@ -215,6 +215,20 @@
</subsection>
<subsection name="Authorizing Multitenant Servers">
<p>
The AuthorizationInterceptor has the ability to direct individual
rules as only applying to a single tenant in a multitenant
server. The following example shows such a rule.
</p>
<macro name="snippet">
<param name="id" value="authorizeTenantAction" />
<param name="file" value="examples/src/main/java/example/AuthorizationInterceptors.java" />
</macro>
</subsection>
</section>
</body>