Add tests for delete conflicts and authorizationinterceptor

This commit is contained in:
jamesagnew 2019-07-01 10:49:38 -04:00
parent 5c0e54fb86
commit 742389642d
16 changed files with 276 additions and 106 deletions

View File

@ -208,7 +208,8 @@ public class Constants {
public static final String PARAM_SEARCH_TOTAL_MODE = "_total";
public static final String CAPABILITYSTATEMENT_WEBSOCKET_URL = "http://hl7.org/fhir/StructureDefinition/capabilitystatement-websocket";
public static final String PARAMETER_CASCADE_DELETE = "_cascade";
public static final String HEADER_CASCADE_DELETE = "X-Cascade-Delete";
public static final String HEADER_CASCADE = "X-Cascade";
public static final String CASCADE_DELETE = "delete";
static {
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);

View File

@ -16,9 +16,7 @@ import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
/*
* #%L

View File

@ -27,7 +27,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
public class DeleteConflictList {
public class DeleteConflictList implements Iterable<DeleteConflict> {
private final List<DeleteConflict> myList = new ArrayList<>();
public void add(DeleteConflict theDeleteConflict) {
@ -38,6 +38,7 @@ public class DeleteConflictList {
return myList.isEmpty();
}
@Override
public Iterator<DeleteConflict> iterator() {
return myList.iterator();
}

View File

@ -42,8 +42,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class DeleteConflictService {
@ -88,6 +91,15 @@ public class DeleteConflictService {
if (resultList.isEmpty()) {
return null;
}
// Don't send two conflict events for the same source resource
Set<Long> sourceIds = new HashSet<>();
resultList = resultList
.stream()
.filter(t -> sourceIds.add(t.getSourceResourcePid()))
.collect(Collectors.toList());
return handleConflicts(theRequest, theDeleteConflicts, theEntity, theForValidate, resultList);
}

View File

@ -177,12 +177,12 @@ public class CascadingDeleteInterceptor {
protected boolean shouldCascade(RequestDetails theRequest) {
if (theRequest != null) {
String[] cascadeParameters = theRequest.getParameters().get(Constants.PARAMETER_CASCADE_DELETE);
if (cascadeParameters != null && Arrays.asList(cascadeParameters).contains("true")) {
if (cascadeParameters != null && Arrays.asList(cascadeParameters).contains(Constants.CASCADE_DELETE)) {
return true;
}
String cascadeHeader = theRequest.getHeader(Constants.HEADER_CASCADE_DELETE);
if ("true".equals(cascadeHeader)) {
String cascadeHeader = theRequest.getHeader(Constants.HEADER_CASCADE);
if (Constants.CASCADE_DELETE.equals(cascadeHeader)) {
return true;
}

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Condition;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
@ -16,7 +17,9 @@ import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import static org.junit.Assert.*;
@ -151,6 +154,35 @@ public class DeleteConflictServiceR4Test extends BaseJpaR4Test {
assertEquals(1 + DeleteConflictService.MAX_RETRY_ATTEMPTS, myDeleteInterceptor.myCallCount);
}
@Test
public void testNoDuplicateConstraintReferences() {
Patient patient = new Patient();
patient.setActive(true);
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
Condition condition = new Condition();
condition.setSubject(new Reference(patientId));
condition.setAsserter(new Reference(patientId));
myConditionDao.create(condition);
List<DeleteConflict> conflicts = new ArrayList<>();
myDeleteInterceptor.deleteConflictFunction = t -> {
for (DeleteConflict next : t) {
conflicts.add(next);
}
return new DeleteConflictOutcome().setShouldRetryCount(0);
};
try {
myPatientDao.delete(patientId);
fail();
} catch (ResourceVersionConflictException e) {
// good
}
assertEquals(1, conflicts.size());
}
private DeleteConflictOutcome deleteConflicts(DeleteConflictList theList) {
Iterator<DeleteConflict> iterator = theList.iterator();
while (iterator.hasNext()) {

View File

@ -456,7 +456,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
ourClient
.delete()
.resourceById(patientId)
.withAdditionalHeader(Constants.HEADER_CASCADE_DELETE, "true")
.withAdditionalHeader(Constants.HEADER_CASCADE, "true")
.execute();
fail();
} catch (ForbiddenOperationException e) {
@ -500,7 +500,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
ourClient
.delete()
.resourceById(patientId)
.withAdditionalHeader(Constants.HEADER_CASCADE_DELETE, "true")
.withAdditionalHeader(Constants.HEADER_CASCADE, "true")
.execute();
} finally {
@ -541,7 +541,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
ourClient
.delete()
.resourceById(patientId)
.withAdditionalHeader(Constants.HEADER_CASCADE_DELETE, "true")
.withAdditionalHeader(Constants.HEADER_CASCADE, "true")
.execute();
fail();
} catch (ForbiddenOperationException e) {

View File

@ -12,9 +12,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.DiagnosticReport;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -39,6 +37,7 @@ public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test
private IIdType myPatientId;
private CascadingDeleteInterceptor myDeleteInterceptor;
private IIdType myObservationId;
private IIdType myConditionId;
@Override
@Before
@ -69,6 +68,11 @@ public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test
dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
dr.addResult().setReference(myObservationId.getValue());
myDiagnosticReportId = ourClient.create().resource(dr).execute().getId().toUnqualifiedVersionless();
Condition condition = new Condition();
condition.setSubject(new Reference(myPatientId));
condition.setAsserter(new Reference(myPatientId));
myConditionId = ourClient.create().resource(condition).execute().getId().toUnqualifiedVersionless();
}
@Test
@ -106,13 +110,38 @@ public class CascadingDeleteInterceptorR4Test extends BaseResourceProviderR4Test
ourRestServer.getInterceptorService().registerInterceptor(myDeleteInterceptor);
HttpDelete delete = new HttpDelete(ourServerBase + "/" + myPatientId.getValue() + "?_cascade=true&_pretty=true");
HttpDelete delete = new HttpDelete(ourServerBase + "/" + myPatientId.getValue() + "?" + Constants.PARAMETER_CASCADE_DELETE + "=" + Constants.CASCADE_DELETE + "&_pretty=true");
delete.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW);
try (CloseableHttpResponse response = ourHttpClient.execute(delete)) {
assertEquals(200, response.getStatusLine().getStatusCode());
String deleteResponse = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
ourLog.info("Response: {}", deleteResponse);
assertThat(deleteResponse, containsString("Cascaded delete to 2 resources: [" + myDiagnosticReportId + "/_history/1, " + myObservationId + "/_history/1]"));
assertThat(deleteResponse, containsString("Cascaded delete to 3 resources: [" + myDiagnosticReportId + "/_history/1, " + myObservationId + "/_history/1, " + myConditionId + "/_history/1]"));
}
try {
ourLog.info("Reading {}", myPatientId);
ourClient.read().resource(Patient.class).withId(myPatientId).execute();
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Test
public void testDeleteCascadingByHeader() throws IOException {
createResources();
ourRestServer.getInterceptorService().registerInterceptor(myDeleteInterceptor);
HttpDelete delete = new HttpDelete(ourServerBase + "/" + myPatientId.getValue() + "?_pretty=true");
delete.addHeader(Constants.HEADER_CASCADE, Constants.CASCADE_DELETE);
delete.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW);
try (CloseableHttpResponse response = ourHttpClient.execute(delete)) {
assertEquals(200, response.getStatusLine().getStatusCode());
String deleteResponse = IOUtils.toString(response.getEntity().getContent(), Charsets.UTF_8);
ourLog.info("Response: {}", deleteResponse);
assertThat(deleteResponse, containsString("Cascaded delete to 3 resources: [" + myDiagnosticReportId + "/_history/1, " + myObservationId + "/_history/1, " + myConditionId + "/_history/1]"));
}
try {

View File

@ -40,10 +40,12 @@ import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/**
* This class is a base class for interceptors which can be used to
@ -299,6 +301,10 @@ public class AuthorizationInterceptor implements IRuleApplier {
case BOTH:
inputResource = theRequest.getResource();
inputResourceId = theRequest.getId();
if (inputResourceId == null && isNotBlank(theRequest.getResourceName())) {
inputResourceId = theRequest.getFhirContext().getVersion().newIdType();
inputResourceId.setParts(null, theRequest.getResourceName(), null, null);
}
break;
case OUT:
// inputResource = null;
@ -330,8 +336,8 @@ public class AuthorizationInterceptor implements IRuleApplier {
checkPointcutAndFailIfDeny(theRequestDetails, thePointcut, theResourceToDelete);
}
private void checkPointcutAndFailIfDeny(RequestDetails theRequestDetails, Pointcut thePointcut, IBaseResource theInputResource) {
applyRulesAndFailIfDeny(theRequestDetails.getRestOperationType(), theRequestDetails, theInputResource, null, null, thePointcut);
private void checkPointcutAndFailIfDeny(RequestDetails theRequestDetails, Pointcut thePointcut, @Nonnull IBaseResource theInputResource) {
applyRulesAndFailIfDeny(theRequestDetails.getRestOperationType(), theRequestDetails, theInputResource, theInputResource.getIdElement(), null, thePointcut);
}
private void checkOutgoingResourceAndFailIfDeny(RequestDetails theRequestDetails, IBaseResource theResponseObject, Pointcut thePointcut) {

View File

@ -34,4 +34,11 @@ public interface IAuthRuleBuilderAppliesTo<T> {
*/
T resourcesOfType(Class<? extends IBaseResource> theType);
/**
* Rule applies to resources of the given type
*
* @param theType E.g. "Patient"
*/
T resourcesOfType(String theType);
}

View File

@ -76,4 +76,5 @@ public interface IAuthRuleBuilderRuleOp extends IAuthRuleBuilderAppliesTo<IAuthR
* @throws NullPointerException If theId is null
*/
IAuthRuleFinished instances(Collection<IIdType> theIds);
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* #L%
*/
import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -29,12 +30,14 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
public class RuleBuilder implements IAuthRuleBuilder {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final ConcurrentHashMap<Class<? extends IBaseResource>, String> ourTypeToName = new ConcurrentHashMap<>();
private ArrayList<IAuthRule> myRules;
private IAuthRuleBuilderRule myAllow;
private IAuthRuleBuilderRule myDeny;
@ -257,7 +260,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private class RuleBuilderRuleConditional implements IAuthRuleBuilderRuleConditional {
private AppliesTypeEnum myAppliesTo;
private Set<?> myAppliesToTypes;
private Set<String> myAppliesToTypes;
private RestOperationTypeEnum myOperationType;
RuleBuilderRuleConditional(RestOperationTypeEnum theOperationType) {
@ -273,6 +276,13 @@ public class RuleBuilder implements IAuthRuleBuilder {
@Override
public IAuthRuleBuilderRuleConditionalClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
String typeName = toTypeName(theType);
return resourcesOfType(typeName);
}
@Override
public IAuthRuleBuilderRuleConditionalClassifier resourcesOfType(String theType) {
myAppliesTo = AppliesTypeEnum.TYPES;
myAppliesToTypes = Collections.singleton(theType);
return new RuleBuilderRuleConditionalClassifier();
@ -306,7 +316,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private RuleBuilderRuleOpClassifier myInstancesBuilder;
private boolean myOnCascade;
public RuleBuilderRuleOp(RuleOpEnum theRuleOp) {
RuleBuilderRuleOp(RuleOpEnum theRuleOp) {
myRuleOp = theRuleOp;
}
@ -345,8 +355,16 @@ public class RuleBuilder implements IAuthRuleBuilder {
}
}
@Override
public IAuthRuleBuilderRuleOpClassifier resourcesOfType(Class<? extends IBaseResource> theType) {
Validate.notNull(theType, "theType must not be null");
String resourceName = toTypeName(theType);
return resourcesOfType(resourceName);
}
@Override
public IAuthRuleBuilderRuleOpClassifier resourcesOfType(String theType) {
Validate.notNull(theType, "theType must not be null");
return new RuleBuilderRuleOpClassifier(AppliesTypeEnum.TYPES, Collections.singleton(theType));
}
@ -360,7 +378,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private class RuleBuilderRuleOpClassifier implements IAuthRuleBuilderRuleOpClassifier {
private final AppliesTypeEnum myAppliesTo;
private final Set<?> myAppliesToTypes;
private final Set<String> myAppliesToTypes;
private ClassifierTypeEnum myClassifierType;
private String myInCompartmentName;
private Collection<? extends IIdType> myInCompartmentOwners;
@ -370,7 +388,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
/**
* Constructor
*/
RuleBuilderRuleOpClassifier(AppliesTypeEnum theAppliesTo, Set<Class<? extends IBaseResource>> theAppliesToTypes) {
RuleBuilderRuleOpClassifier(AppliesTypeEnum theAppliesTo, Set<String> theAppliesToTypes) {
super();
myAppliesTo = theAppliesTo;
myAppliesToTypes = theAppliesToTypes;
@ -551,7 +569,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private final OperationRule myRule;
public RuleBuilderOperationNamedAndScoped(OperationRule theRule) {
RuleBuilderOperationNamedAndScoped(OperationRule theRule) {
myRule = theRule;
}
@ -599,7 +617,7 @@ public class RuleBuilder implements IAuthRuleBuilder {
private class PatchBuilder implements IAuthRuleBuilderPatch {
public PatchBuilder() {
PatchBuilder() {
super();
}
@ -625,4 +643,15 @@ public class RuleBuilder implements IAuthRuleBuilder {
}
}
private static String toTypeName(Class<? extends IBaseResource> theType) {
String retVal = ourTypeToName.get(theType);
if (retVal == null) {
ResourceDef resourceDef = theType.getAnnotation(ResourceDef.class);
retVal = resourceDef.name();
Validate.notBlank(retVal, "Could not determine resource type of class %s", theType);
ourTypeToName.put(theType, retVal);
}
return retVal;
}
}

View File

@ -32,7 +32,7 @@ import java.util.Set;
public class RuleImplConditional extends BaseRule implements IAuthRule {
private AppliesTypeEnum myAppliesTo;
private Set<?> myAppliesToTypes;
private Set<String> myAppliesToTypes;
private RestOperationTypeEnum myOperationType;
RuleImplConditional(String theRuleName) {
@ -47,7 +47,7 @@ public class RuleImplConditional extends BaseRule implements IAuthRule {
return null;
}
if (theInputResourceId != null) {
if (theInputResourceId != null && theInputResourceId.hasIdPart()) {
return null;
}
@ -63,12 +63,12 @@ public class RuleImplConditional extends BaseRule implements IAuthRule {
case TYPES:
if (myOperationType == RestOperationTypeEnum.DELETE) {
String resourceName = theRequestDetails.getResourceName();
Class<? extends IBaseResource> resourceType = theRequestDetails.getFhirContext().getResourceDefinition(resourceName).getImplementingClass();
if (!myAppliesToTypes.contains(resourceType)) {
if (!myAppliesToTypes.contains(resourceName)) {
return null;
}
} else {
if (theInputResource == null || !myAppliesToTypes.contains(theInputResource.getClass())) {
String inputResourceName = theRequestDetails.getFhirContext().getResourceDefinition(theInputResource).getName();
if (theInputResource == null || !myAppliesToTypes.contains(inputResourceName)) {
return null;
}
}
@ -95,7 +95,7 @@ public class RuleImplConditional extends BaseRule implements IAuthRule {
myAppliesTo = theAppliesTo;
}
void setAppliesToTypes(Set<?> theAppliesToTypes) {
void setAppliesToTypes(Set<String> theAppliesToTypes) {
myAppliesToTypes = theAppliesToTypes;
}

View File

@ -13,8 +13,8 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.BundleUtil.BundleEntryParts;
import ca.uhn.fhir.util.CollectionUtil;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.UrlUtil;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
@ -53,7 +53,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
class RuleImplOp extends BaseRule /* implements IAuthRule */ {
private AppliesTypeEnum myAppliesTo;
private Set<?> myAppliesToTypes;
private Set<String> myAppliesToTypes;
private String myClassifierCompartmentName;
private Collection<? extends IIdType> myClassifierCompartmentOwners;
private ClassifierTypeEnum myClassifierType;
@ -212,9 +212,18 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
if (myAppliesToDeleteCascade != (thePointcut == Pointcut.STORAGE_CASCADE_DELETE)) {
return null;
}
if (theInputResource == null) {
if (theInputResourceId == null) {
return null;
}
if (theInputResourceId.hasIdPart() == false) {
// This is a conditional DELETE, so we'll authorize it using STORAGE events instead
// so just let it through for now..
return newVerdict();
}
if (theInputResource== null && myClassifierCompartmentOwners != null && myClassifierCompartmentOwners.size() > 0) {
return newVerdict();
}
appliesToResource = theInputResource;
appliesToResourceId = Collections.singleton(theInputResourceId);
} else {
@ -242,6 +251,15 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
for (BundleEntryParts nextPart : inputResources) {
IBaseResource inputResource = nextPart.getResource();
IIdType inputResourceId = null;
if (isNotBlank(nextPart.getUrl())) {
UrlUtil.UrlParts parts = UrlUtil.parseUrl(nextPart.getUrl());
inputResourceId = theRequestDetails.getFhirContext().getVersion().newIdType();
inputResourceId.setParts(null, parts.getResourceType(), parts.getResourceId(), null);
}
RestOperationTypeEnum operation;
if (nextPart.getRequestType() == RequestTypeEnum.GET) {
continue;
@ -270,7 +288,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
}
}
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, null, null, thePointcut);
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(operation, theRequestDetails, inputResource, inputResourceId, null, thePointcut);
if (newVerdict == null) {
continue;
} else if (verdict == null) {
@ -377,7 +395,8 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
case TYPES:
if (appliesToResource != null) {
if (myClassifierType == ClassifierTypeEnum.ANY_ID) {
if (myAppliesToTypes.contains(appliesToResource.getClass()) == false) {
String type = theRequestDetails.getFhirContext().getResourceDefinition(appliesToResource).getName();
if (myAppliesToTypes.contains(type) == false) {
return null;
}
}
@ -385,21 +404,20 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
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) {
String nextRequestAppliesToResourceIdType = nextRequestAppliesToResourceId.getResourceType();
if (myAppliesToTypes.contains(nextRequestAppliesToResourceIdType) == false) {
return null;
}
}
}
}
if (appliesToResourceType != null) {
Class<? extends IBaseResource> type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceType).getImplementingClass();
if (myAppliesToTypes.contains(type)) {
if (myAppliesToTypes.contains(appliesToResourceType)) {
if (!applyTesters(theOperation, theRequestDetails, theInputResourceId, theInputResource, theOutputResource)) {
return null;
}
if (myClassifierType == ClassifierTypeEnum.ANY_ID) {
return new Verdict(PolicyEnum.ALLOW, this);
return newVerdict();
} else if (myClassifierType == ClassifierTypeEnum.IN_COMPARTMENT) {
// ok we'll check below
}
@ -554,19 +572,19 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
myAppliesTo = theAppliesTo;
}
public void setAppliesToTypes(Set<?> theAppliesToTypes) {
void setAppliesToTypes(Set<String> theAppliesToTypes) {
myAppliesToTypes = theAppliesToTypes;
}
public void setClassifierCompartmentName(String theClassifierCompartmentName) {
void setClassifierCompartmentName(String theClassifierCompartmentName) {
myClassifierCompartmentName = theClassifierCompartmentName;
}
public void setClassifierCompartmentOwners(Collection<? extends IIdType> theInCompartmentOwners) {
void setClassifierCompartmentOwners(Collection<? extends IIdType> theInCompartmentOwners) {
myClassifierCompartmentOwners = theInCompartmentOwners;
}
public void setClassifierType(ClassifierTypeEnum theClassifierType) {
void setClassifierType(ClassifierTypeEnum theClassifierType) {
myClassifierType = theClassifierType;
}
@ -590,7 +608,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return builder.toString();
}
public void setAppliesToDeleteCascade(boolean theAppliesToDeleteCascade) {
void setAppliesToDeleteCascade(boolean theAppliesToDeleteCascade) {
myAppliesToDeleteCascade = theAppliesToDeleteCascade;
}

View File

@ -255,7 +255,7 @@ public class AuthorizationInterceptorR4Test {
ourLog.info(response);
assertThat(response, containsString("Access denied by rule: Rule 1"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
assertFalse(ourHitMethod);
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$validate");
@ -299,7 +299,7 @@ public class AuthorizationInterceptorR4Test {
ourLog.info(response);
assertThat(response, containsString("Access denied by rule: Rule 1"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
assertFalse(ourHitMethod);
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANTA/Patient/1/$validate");
@ -626,12 +626,13 @@ public class AuthorizationInterceptorR4Test {
Bundle responseBundle = new Bundle();
responseBundle.setType(Bundle.BundleType.TRANSACTIONRESPONSE);
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
ourHitMethod = false;
Bundle bundle = new Bundle();
bundle.getEntry().clear();
ourReturn = Collections.singletonList(responseBundle);
ourDeleted = Collections.singletonList(createPatient(2));
bundle.setType(Bundle.BundleType.TRANSACTION);
bundle.addEntry().getRequest().setMethod(Bundle.HTTPVerb.DELETE).setUrl("Patient/2");
httpPost = new HttpPost("http://localhost:" + ourPort + "/");
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(bundle), ContentType.create(Constants.CT_FHIR_JSON_NEW, Charsets.UTF_8)));
@ -640,6 +641,7 @@ public class AuthorizationInterceptorR4Test {
assertEquals(responseString, 403, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
ourHitMethod = false;
bundle.getEntry().clear();
bundle.addEntry().getRequest().setMethod(Bundle.HTTPVerb.DELETE).setUrl("Patient/1");
ourReturn = Collections.singletonList(responseBundle);
@ -676,6 +678,40 @@ public class AuthorizationInterceptorR4Test {
assertTrue(ourHitMethod);
}
@Test
public void testDeleteByType() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("Rule 1").delete().resourcesOfType(Patient.class).withAnyId().andThen()
.build();
}
});
HttpDelete httpDelete;
HttpResponse status;
String responseString;
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient/1");
status = ourClient.execute(httpDelete);
responseString = extractResponseAndClose(status);
assertEquals(responseString, 204, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Observation/1");
status = ourClient.execute(httpDelete);
responseString = extractResponseAndClose(status);
assertEquals(responseString, 403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
/**
* #528
*/
@ -2575,52 +2611,52 @@ public class AuthorizationInterceptorR4Test {
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);
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);
ourHitMethod = false;
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=Patient/900,Patient/700");
@ -3043,15 +3079,15 @@ public class AuthorizationInterceptorR4Test {
HttpResponse status;
String response;
// // Wrong resource
// ourReturn = Collections.singletonList(createPatient(1));
// ourHitMethod = false;
// httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar");
// status = ourClient.execute(httpDelete);
// response = extractResponseAndClose(status);
// ourLog.info(response);
// assertEquals(403, status.getStatusLine().getStatusCode());
// assertTrue(ourHitMethod);
// Wrong resource
ourReturn = Collections.singletonList(createPatient(1));
ourHitMethod = false;
httpDelete = new HttpDelete("http://localhost:" + ourPort + "/Patient?foo=bar");
status = ourClient.execute(httpDelete);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Right resource
ourReturn = Collections.singletonList(createPatient(2));

View File

@ -577,10 +577,10 @@ delete from hfj_res_ver where res_id in (select res_id from hfj_resource where s
</p>
<ul>
<li>The request may include the following parameter:
<code>_cascade=true</code>
<code>_cascade=delete</code>
</li>
<li>The request may include the following header:
<code>X-Cascade-Delete: true</code>
<code>X-Cascade: delete</code>
</li>
</ul>
</section>