Add subscription narrowing interceptor and refactor RuleBuilder to be a
bit cleaner
This commit is contained in:
parent
fc09ed6966
commit
93bf2788ec
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
|
@ -11,15 +31,15 @@ import java.util.List;
|
|||
*/
|
||||
public class AuthorizedList {
|
||||
|
||||
private List<String> myCompartments;
|
||||
private List<String> myResources;
|
||||
private List<String> myAllowedCompartments;
|
||||
private List<String> myAllowedInstances;
|
||||
|
||||
List<String> getCompartments() {
|
||||
return myCompartments;
|
||||
List<String> getAllowedCompartments() {
|
||||
return myAllowedCompartments;
|
||||
}
|
||||
|
||||
List<String> getResources() {
|
||||
return myResources;
|
||||
List<String> getAllowedInstances() {
|
||||
return myAllowedInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,10 +50,10 @@ public class AuthorizedList {
|
|||
*/
|
||||
public AuthorizedList addCompartment(String theCompartment) {
|
||||
Validate.notNull(theCompartment, "theCompartment must not be null");
|
||||
if (myCompartments == null) {
|
||||
myCompartments = new ArrayList<>();
|
||||
if (myAllowedCompartments == null) {
|
||||
myAllowedCompartments = new ArrayList<>();
|
||||
}
|
||||
myCompartments.add(theCompartment);
|
||||
myAllowedCompartments.add(theCompartment);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -60,10 +80,10 @@ public class AuthorizedList {
|
|||
*/
|
||||
public AuthorizedList addResource(String theResource) {
|
||||
Validate.notNull(theResource, "theResource must not be null");
|
||||
if (myResources == null) {
|
||||
myResources = new ArrayList<>();
|
||||
if (myAllowedInstances == null) {
|
||||
myAllowedInstances = new ArrayList<>();
|
||||
}
|
||||
myResources.add(theResource);
|
||||
myAllowedInstances.add(theResource);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
|
|||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
|
@ -58,4 +60,5 @@ public interface IAuthRuleBuilderRuleOp extends IAuthRuleBuilderAppliesTo<IAuthR
|
|||
*/
|
||||
IAuthRuleFinished instance(IIdType theId);
|
||||
|
||||
IAuthRuleFinished instances(Collection<IIdType> theInstances);
|
||||
}
|
||||
|
|
|
@ -172,7 +172,6 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
private PolicyEnum myRuleMode;
|
||||
private String myRuleName;
|
||||
private RuleOpEnum myRuleOp;
|
||||
|
||||
RuleBuilderRule(PolicyEnum theRuleMode, String theRuleName) {
|
||||
myRuleMode = theRuleMode;
|
||||
|
@ -186,8 +185,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOp delete() {
|
||||
myRuleOp = RuleOpEnum.DELETE;
|
||||
return new RuleBuilderRuleOp();
|
||||
return new RuleBuilderRuleOp(RuleOpEnum.DELETE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -211,14 +209,12 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
@Override
|
||||
public IAuthRuleBuilderPatch patch() {
|
||||
myRuleOp = RuleOpEnum.PATCH;
|
||||
return new PatchBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOp read() {
|
||||
myRuleOp = RuleOpEnum.READ;
|
||||
return new RuleBuilderRuleOp();
|
||||
return new RuleBuilderRuleOp(RuleOpEnum.READ);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -233,8 +229,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOp write() {
|
||||
myRuleOp = RuleOpEnum.WRITE;
|
||||
return new RuleBuilderRuleOp();
|
||||
return new RuleBuilderRuleOp(RuleOpEnum.WRITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -245,7 +240,6 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
private class RuleBuilderRuleConditional implements IAuthRuleBuilderRuleConditional {
|
||||
|
||||
private AppliesTypeEnum myAppliesTo;
|
||||
|
||||
private Set<?> myAppliesToTypes;
|
||||
private RestOperationTypeEnum myOperationType;
|
||||
|
||||
|
@ -291,13 +285,15 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
private class RuleBuilderRuleOp implements IAuthRuleBuilderRuleOp {
|
||||
|
||||
private AppliesTypeEnum myAppliesTo;
|
||||
private Set<?> myAppliesToTypes;
|
||||
private final RuleOpEnum myRuleOp;
|
||||
|
||||
public RuleBuilderRuleOp(RuleOpEnum theRuleOp) {
|
||||
myRuleOp = theRuleOp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOpClassifier allResources() {
|
||||
myAppliesTo = AppliesTypeEnum.ALL_RESOURCES;
|
||||
return new RuleBuilderRuleOpClassifier();
|
||||
return new RuleBuilderRuleOpClassifier(AppliesTypeEnum.ALL_RESOURCES, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -312,15 +308,22 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
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(Collections.singletonList(theId)).finished();
|
||||
List<IIdType> instances = Collections.singletonList(theId);
|
||||
return instances(instances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleFinished instances(Collection<IIdType> theInstances) {
|
||||
Validate.notNull(theInstances, "theInstances must not be null");
|
||||
Validate.notEmpty(theInstances, "theInstances must not be empty");
|
||||
|
||||
return new RuleBuilderRuleOpClassifier(theInstances).finished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOpClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
|
||||
Validate.notNull(theType, "theType must not be null");
|
||||
myAppliesTo = AppliesTypeEnum.TYPES;
|
||||
myAppliesToTypes = Collections.singleton(theType);
|
||||
return new RuleBuilderRuleOpClassifier();
|
||||
return new RuleBuilderRuleOpClassifier(AppliesTypeEnum.TYPES, Collections.singleton(theType));
|
||||
}
|
||||
|
||||
private class RuleBuilderRuleOpClassifier implements IAuthRuleBuilderRuleOpClassifier {
|
||||
|
@ -328,21 +331,26 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
private ClassifierTypeEnum myClassifierType;
|
||||
private String myInCompartmentName;
|
||||
private Collection<? extends IIdType> myInCompartmentOwners;
|
||||
private List<IIdType> myAppliesToInstances;
|
||||
private Collection<IIdType> myAppliesToInstances;
|
||||
private final AppliesTypeEnum myAppliesTo;
|
||||
private final Set<?> myAppliesToTypes;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
RuleBuilderRuleOpClassifier() {
|
||||
RuleBuilderRuleOpClassifier(AppliesTypeEnum theAppliesTo, Set<Class<? extends IBaseResource>> theAppliesToTypes) {
|
||||
super();
|
||||
myAppliesTo = theAppliesTo;
|
||||
myAppliesToTypes=theAppliesToTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
RuleBuilderRuleOpClassifier(List<IIdType> theAppliesToInstances) {
|
||||
RuleBuilderRuleOpClassifier(Collection<IIdType> theAppliesToInstances) {
|
||||
myAppliesToInstances = theAppliesToInstances;
|
||||
myAppliesTo = AppliesTypeEnum.INSTANCES;
|
||||
myAppliesToTypes = null;
|
||||
}
|
||||
|
||||
private IAuthRuleBuilderRuleOpClassifierFinished finished() {
|
||||
|
@ -554,6 +562,10 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
private class PatchBuilder implements IAuthRuleBuilderPatch {
|
||||
|
||||
public PatchBuilder() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleFinished allRequests() {
|
||||
BaseRule rule = new RuleImplPatch(myRuleName)
|
||||
|
|
|
@ -20,10 +20,7 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -48,6 +45,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
||||
|
||||
private AppliesTypeEnum myAppliesTo;
|
||||
|
@ -57,7 +55,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
private ClassifierTypeEnum myClassifierType;
|
||||
private RuleOpEnum myOp;
|
||||
private TransactionAppliesToEnum myTransactionAppliesToOp;
|
||||
private List<IIdType> myAppliesToInstances;
|
||||
private Collection<IIdType> myAppliesToInstances;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -77,7 +75,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||
|
||||
IBaseResource appliesToResource;
|
||||
IIdType appliesToResourceId = null;
|
||||
Collection<IIdType> appliesToResourceId = null;
|
||||
String appliesToResourceType = null;
|
||||
Map<String, String[]> appliesToSearchParams = null;
|
||||
switch (myOp) {
|
||||
|
@ -90,7 +88,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
switch (theOperation) {
|
||||
case READ:
|
||||
case VREAD:
|
||||
appliesToResourceId = theInputResourceId;
|
||||
appliesToResourceId = Collections.singleton(theInputResourceId);
|
||||
appliesToResourceType = theInputResourceId.getResourceType();
|
||||
break;
|
||||
case SEARCH_SYSTEM:
|
||||
|
@ -105,6 +103,29 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
}
|
||||
appliesToResourceType = theRequestDetails.getResourceName();
|
||||
appliesToSearchParams = theRequestDetails.getParameters();
|
||||
|
||||
/*
|
||||
* If this is a search with an "_id" parameter, we can treat this
|
||||
* as a read for the given resource ID(s)
|
||||
*/
|
||||
if (theRequestDetails.getParameters().containsKey("_id")) {
|
||||
String[] idValues = theRequestDetails.getParameters().get("_id");
|
||||
appliesToResourceId = new ArrayList<>();
|
||||
for (String next : idValues) {
|
||||
IIdType nextId = ctx.getVersion().newIdType().setValue(next);
|
||||
if (nextId.hasIdPart()){
|
||||
if (!nextId.hasResourceType()) {
|
||||
nextId = nextId.withResourceType(appliesToResourceType);
|
||||
}
|
||||
if (nextId.getResourceType().equals(appliesToResourceType)) {
|
||||
appliesToResourceId.add(nextId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (appliesToResourceId.isEmpty()) {
|
||||
appliesToResourceId = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case HISTORY_TYPE:
|
||||
if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) {
|
||||
|
@ -116,7 +137,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
if (theFlags.contains(AuthorizationFlagsEnum.NO_NOT_PROACTIVELY_BLOCK_COMPARTMENT_READ_ACCESS)) {
|
||||
return new Verdict(PolicyEnum.ALLOW, this);
|
||||
}
|
||||
appliesToResourceId = theInputResourceId;
|
||||
appliesToResourceId = Collections.singleton(theInputResourceId);
|
||||
break;
|
||||
case GET_PAGE:
|
||||
return new Verdict(PolicyEnum.ALLOW, this);
|
||||
|
@ -145,7 +166,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
}
|
||||
appliesToResource = theOutputResource;
|
||||
if (theOutputResource != null) {
|
||||
appliesToResourceId = theOutputResource.getIdElement();
|
||||
appliesToResourceId = Collections.singleton(theOutputResource.getIdElement());
|
||||
}
|
||||
break;
|
||||
case WRITE:
|
||||
|
@ -160,7 +181,9 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
case META_ADD:
|
||||
case META_DELETE:
|
||||
appliesToResource = theInputResource;
|
||||
appliesToResourceId = theInputResourceId;
|
||||
if (theInputResourceId != null) {
|
||||
appliesToResourceId = Collections.singletonList(theInputResourceId);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
|
@ -291,19 +314,29 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
|
||||
switch (myAppliesTo) {
|
||||
case INSTANCES:
|
||||
if (appliesToResourceId != null) {
|
||||
if (appliesToResourceId != null && appliesToResourceId.size() > 0) {
|
||||
int haveMatches = 0;
|
||||
for (IIdType requestAppliesToResource : appliesToResourceId) {
|
||||
|
||||
for (IIdType next : myAppliesToInstances) {
|
||||
if (isNotBlank(next.getResourceType())) {
|
||||
if (!next.getResourceType().equals(appliesToResourceId.getResourceType())) {
|
||||
if (!next.getResourceType().equals(requestAppliesToResource.getResourceType())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!next.getIdPart().equals(appliesToResourceId.getIdPart())) {
|
||||
if (!next.getIdPart().equals(requestAppliesToResource.getIdPart())) {
|
||||
continue;
|
||||
}
|
||||
if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) {
|
||||
return null;
|
||||
}
|
||||
haveMatches++;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (haveMatches == appliesToResourceId.size()) {
|
||||
return newVerdict();
|
||||
}
|
||||
}
|
||||
|
@ -326,12 +359,16 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (appliesToResourceId != null && appliesToResourceId.hasResourceType()) {
|
||||
Class<? extends IBaseResource> type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceId.getResourceType()).getImplementingClass();
|
||||
if (appliesToResourceId != null) {
|
||||
for (IIdType nextRequestAppliesToResourceId : appliesToResourceId) {
|
||||
if (nextRequestAppliesToResourceId.hasResourceType()) {
|
||||
Class<? extends IBaseResource> type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(nextRequestAppliesToResourceId.getResourceType()).getImplementingClass();
|
||||
if (myAppliesToTypes.contains(type) == false) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (appliesToResourceType != null) {
|
||||
Class<? extends IBaseResource> type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceType).getImplementingClass();
|
||||
if (myAppliesToTypes.contains(type)) {
|
||||
|
@ -356,6 +393,16 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
case IN_COMPARTMENT:
|
||||
FhirTerser t = ctx.newTerser();
|
||||
boolean foundMatch = false;
|
||||
|
||||
if (appliesToResourceId != null && appliesToResourceId.size() > 0) {
|
||||
boolean haveOwnersForAll = appliesToResourceId
|
||||
.stream()
|
||||
.allMatch(n -> myClassifierCompartmentOwners.contains(n.toUnqualifiedVersionless()));
|
||||
if (haveOwnersForAll) {
|
||||
foundMatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (IIdType next : myClassifierCompartmentOwners) {
|
||||
if (appliesToResource != null) {
|
||||
if (t.isSourceInCompartmentForTarget(myClassifierCompartmentName, appliesToResource, next)) {
|
||||
|
@ -363,12 +410,6 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (appliesToResourceId != null && appliesToResourceId.hasResourceType() && appliesToResourceId.hasIdPart()) {
|
||||
if (appliesToResourceId.toUnqualifiedVersionless().getValue().equals(next.toUnqualifiedVersionless().getValue())) {
|
||||
foundMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the client has permission to read compartment
|
||||
|
@ -490,7 +531,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
|
|||
myAppliesTo = theAppliesTo;
|
||||
}
|
||||
|
||||
public void setAppliesToInstances(List<IIdType> theAppliesToInstances) {
|
||||
public void setAppliesToInstances(Collection<IIdType> theAppliesToInstances) {
|
||||
myAppliesToInstances = theAppliesToInstances;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
|
@ -51,9 +71,12 @@ public abstract class SearchNarrowingInterceptor extends InterceptorAdapter {
|
|||
* </p>
|
||||
*
|
||||
* @param theRequestDetails The individual request currently being applied
|
||||
* @return The list of allowed compartments and instances that should be used
|
||||
* for search narrowing. If this method returns <code>null</code>, no narrowing will
|
||||
* be performed
|
||||
*/
|
||||
protected AuthorizedList buildAuthorizedList(@SuppressWarnings("unused") RequestDetails theRequestDetails) {
|
||||
return new AuthorizedList();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -71,16 +94,19 @@ public abstract class SearchNarrowingInterceptor extends InterceptorAdapter {
|
|||
RuntimeResourceDefinition resDef = ctx.getResourceDefinition(theRequestDetails.getResourceName());
|
||||
HashMap<String, List<String>> parameterToOrValues = new HashMap<>();
|
||||
AuthorizedList authorizedList = buildAuthorizedList(theRequestDetails);
|
||||
if (authorizedList == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a map of search parameter values that need to be added to the
|
||||
* given request
|
||||
*/
|
||||
Collection<String> compartments = authorizedList.getCompartments();
|
||||
Collection<String> compartments = authorizedList.getAllowedCompartments();
|
||||
if (compartments != null) {
|
||||
processResourcesOrCompartments(theRequestDetails, resDef, parameterToOrValues, compartments, true);
|
||||
}
|
||||
Collection<String> resources = authorizedList.getResources();
|
||||
Collection<String> resources = authorizedList.getAllowedInstances();
|
||||
if (resources != null) {
|
||||
processResourcesOrCompartments(theRequestDetails, resDef, parameterToOrValues, resources, false);
|
||||
}
|
||||
|
@ -147,7 +173,7 @@ public abstract class SearchNarrowingInterceptor extends InterceptorAdapter {
|
|||
|
||||
private void processResourcesOrCompartments(RequestDetails theRequestDetails, RuntimeResourceDefinition theResDef, HashMap<String, List<String>> theParameterToOrValues, Collection<String> theResourcesOrCompartments, boolean theAreCompartments) {
|
||||
String lastCompartmentName = null;
|
||||
String lastSearchParamName=null;
|
||||
String lastSearchParamName = null;
|
||||
for (String nextCompartment : theResourcesOrCompartments) {
|
||||
Validate.isTrue(StringUtils.countMatches(nextCompartment, '/') == 1, "Invalid compartment name (must be in form \"ResourceType/xxx\": %s", nextCompartment);
|
||||
String compartmentName = nextCompartment.substring(0, nextCompartment.indexOf('/'));
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class RuleBuilderTest {
|
||||
|
||||
@Test
|
||||
public void testCollapseReadInstancesIntoSingleRule() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.rest.api.*;
|
|||
import ca.uhn.fhir.rest.api.server.IRequestOperationCallback;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
@ -629,7 +630,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(bundle), ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
|
||||
status = ourClient.execute(httpPost);
|
||||
responseString = extractResponseAndClose(status);
|
||||
assertEquals(responseString,403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(responseString, 403, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
bundle.getEntry().clear();
|
||||
|
@ -640,7 +641,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(bundle), ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
|
||||
status = ourClient.execute(httpPost);
|
||||
responseString = extractResponseAndClose(status);
|
||||
assertEquals(responseString,200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(responseString, 200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
|
@ -652,7 +653,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(bundle), ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
|
||||
status = ourClient.execute(httpPost);
|
||||
responseString = extractResponseAndClose(status);
|
||||
assertEquals(responseString,403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(responseString, 403, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
|
@ -664,7 +665,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(bundle), ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
|
||||
status = ourClient.execute(httpPost);
|
||||
responseString = extractResponseAndClose(status);
|
||||
assertEquals(responseString,200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(responseString, 200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
}
|
||||
|
||||
|
@ -2499,6 +2500,73 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadByInstanceAllowsTargetedSearch() throws Exception {
|
||||
ourConditionalCreateId = "1";
|
||||
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
return new RuleBuilder()
|
||||
.allow("Rule 1").read().instance("Patient/900").andThen()
|
||||
.allow("Rule 1").read().instance("Patient/700").andThen()
|
||||
.build();
|
||||
}
|
||||
});
|
||||
|
||||
HttpResponse status;
|
||||
String response;
|
||||
HttpGet httpGet;
|
||||
ourReturn = Collections.singletonList(createPatient(900));
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=900");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=Patient/900");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=901");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(ERR403, response);
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=Patient/901");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(ERR403, response);
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
// technically this is invalid, but just in case..
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_id=Patient/901");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(ERR403, response);
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation?_id=901");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertEquals(ERR403, response);
|
||||
assertFalse(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadPageRight() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
|
@ -3205,45 +3273,6 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
assertTrue(ourHitMethod);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider();
|
||||
DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider();
|
||||
DummyOrganizationResourceProvider orgProv = new DummyOrganizationResourceProvider();
|
||||
DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider();
|
||||
DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider();
|
||||
DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider();
|
||||
DummyMessageHeaderResourceProvider mshProv = new DummyMessageHeaderResourceProvider();
|
||||
PlainProvider plainProvider = new PlainProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
ourServlet = new RestfulServer(ourCtx);
|
||||
ourServlet.setFhirContext(ourCtx);
|
||||
ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv, mshProv);
|
||||
ourServlet.setPlainProviders(plainProvider);
|
||||
ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100));
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
public static class DummyCarePlanResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
|
@ -3314,7 +3343,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
}
|
||||
|
||||
@Operation(name = "process-message", idempotent = true)
|
||||
public Parameters operation0(@OperationParam(name="content") Bundle theInput) {
|
||||
public Parameters operation0(@OperationParam(name = "content") Bundle theInput) {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
@ -3397,7 +3426,9 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
}
|
||||
|
||||
@Search()
|
||||
public List<Resource> search(@OptionalParam(name = "subject") ReferenceParam theSubject) {
|
||||
public List<Resource> search(
|
||||
@OptionalParam(name = "_id") TokenAndListParam theIds,
|
||||
@OptionalParam(name = "subject") ReferenceParam theSubject) {
|
||||
ourHitMethod = true;
|
||||
return ourReturn;
|
||||
}
|
||||
|
@ -3591,7 +3622,7 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
@Transaction()
|
||||
public Bundle search(IRequestOperationCallback theRequestOperationCallback, @TransactionParam Bundle theInput) {
|
||||
ourHitMethod = true;
|
||||
if (ourDeleted != null){
|
||||
if (ourDeleted != null) {
|
||||
for (IBaseResource next : ourDeleted) {
|
||||
theRequestOperationCallback.resourceDeleted(next);
|
||||
}
|
||||
|
@ -3601,6 +3632,45 @@ public class AuthorizationInterceptorDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider();
|
||||
DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider();
|
||||
DummyOrganizationResourceProvider orgProv = new DummyOrganizationResourceProvider();
|
||||
DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider();
|
||||
DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider();
|
||||
DummyDiagnosticReportResourceProvider drProv = new DummyDiagnosticReportResourceProvider();
|
||||
DummyMessageHeaderResourceProvider mshProv = new DummyMessageHeaderResourceProvider();
|
||||
PlainProvider plainProvider = new PlainProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
ourServlet = new RestfulServer(ourCtx);
|
||||
ourServlet.setFhirContext(ourCtx);
|
||||
ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv, orgProv, drProv, mshProv);
|
||||
ourServlet.setPlainProviders(plainProvider);
|
||||
ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100));
|
||||
ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
ServletHolder servletHolder = new ServletHolder(ourServlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -68,6 +68,24 @@ public class SearchNarrowingInterceptorTest {
|
|||
ourNextCompartmentList = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnNull() {
|
||||
|
||||
ourNextCompartmentList = null;
|
||||
|
||||
ourClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.execute();
|
||||
|
||||
assertEquals("Patient.search", ourLastHitMethod);
|
||||
assertNull(ourLastCodeParam);
|
||||
assertNull(ourLastSubjectParam);
|
||||
assertNull(ourLastPerformerParam);
|
||||
assertNull(ourLastPatientParam);
|
||||
assertNull(ourLastIdParam);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNarrowObservationsByPatientContext_ClientRequestedNoParams() {
|
||||
|
||||
|
@ -259,7 +277,9 @@ public class SearchNarrowingInterceptorTest {
|
|||
private static class MySearchNarrowingInterceptor extends SearchNarrowingInterceptor {
|
||||
@Override
|
||||
protected AuthorizedList buildAuthorizedList(RequestDetails theRequestDetails) {
|
||||
Validate.notNull(ourNextCompartmentList);
|
||||
if (ourNextCompartmentList == null) {
|
||||
return null;
|
||||
}
|
||||
return ourNextCompartmentList;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue