Merge pull request #3015 from hapifhir/3014-prevent-delete-expunge-cascade
fix for issue SMILE-3128: prevent delete _expunge and _cascade
This commit is contained in:
commit
a562a4f493
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: change
|
||||||
|
jira: SMILE-3128
|
||||||
|
title: "Prevent _expunge and _cascade from being used on the same DELETE operation"
|
|
@ -86,6 +86,7 @@ import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
@ -136,12 +137,9 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -588,6 +586,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
throw new MethodNotAllowedException("_expunge is not enabled on this server: " + getConfig().cannotDeleteExpungeReason());
|
throw new MethodNotAllowedException("_expunge is not enabled on this server: " + getConfig().cannotDeleteExpungeReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theUrl.contains(Constants.PARAMETER_CASCADE_DELETE) || (theRequest.getHeader(Constants.HEADER_CASCADE) != null && theRequest.getHeader(Constants.HEADER_CASCADE).equals(Constants.CASCADE_DELETE))) {
|
||||||
|
throw new InvalidRequestException("_expunge cannot be used with _cascade");
|
||||||
|
}
|
||||||
|
|
||||||
List<String> urlsToDeleteExpunge = Collections.singletonList(theUrl);
|
List<String> urlsToDeleteExpunge = Collections.singletonList(theUrl);
|
||||||
try {
|
try {
|
||||||
JobExecution jobExecution = myDeleteExpungeJobSubmitter.submitJob(getConfig().getExpungeBatchSize(), urlsToDeleteExpunge, theRequest);
|
JobExecution jobExecution = myDeleteExpungeJobSubmitter.submitJob(getConfig().getExpungeBatchSize(), urlsToDeleteExpunge, theRequest);
|
||||||
|
|
|
@ -7,6 +7,8 @@ import ca.uhn.fhir.jpa.batch.writer.SqlExecutorWriter;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.test.utilities.BatchJobHelper;
|
import ca.uhn.fhir.test.utilities.BatchJobHelper;
|
||||||
import ca.uhn.fhir.util.BundleBuilder;
|
import ca.uhn.fhir.util.BundleBuilder;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -15,9 +17,11 @@ import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
import org.hl7.fhir.r4.model.Organization;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
import org.springframework.batch.core.BatchStatus;
|
import org.springframework.batch.core.BatchStatus;
|
||||||
import org.springframework.batch.core.JobExecution;
|
import org.springframework.batch.core.JobExecution;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -27,6 +31,9 @@ import java.util.List;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
class DeleteExpungeDaoTest extends BaseJpaR4Test {
|
class DeleteExpungeDaoTest extends BaseJpaR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -51,6 +58,41 @@ class DeleteExpungeDaoTest extends BaseJpaR4Test {
|
||||||
myDaoConfig.setExpungeBatchSize(defaultDaoConfig.getExpungeBatchSize());
|
myDaoConfig.setExpungeBatchSize(defaultDaoConfig.getExpungeBatchSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteCascadeExpungeReturns400() {
|
||||||
|
// Create new organization
|
||||||
|
Organization organization = new Organization();
|
||||||
|
organization.setName("FOO");
|
||||||
|
IIdType organizationId = myOrganizationDao.create(organization).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setManagingOrganization(new Reference(organizationId));
|
||||||
|
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
// Try to delete _cascade and _expunge on the organization
|
||||||
|
BaseServerResponseException e = assertThrows(BaseServerResponseException.class, () -> {
|
||||||
|
myOrganizationDao
|
||||||
|
.deleteByUrl("Organization?" + "_cascade=delete&" + JpaConstants.PARAM_DELETE_EXPUNGE + "=true", mySrd);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get not implemented HTTP 400 error
|
||||||
|
assertEquals(Constants.STATUS_HTTP_400_BAD_REQUEST, e.getStatusCode());
|
||||||
|
assertEquals("_expunge cannot be used with _cascade", e.getMessage());
|
||||||
|
|
||||||
|
|
||||||
|
// Try to delete with header 'X-Cascade' = delete
|
||||||
|
when(mySrd.getHeader(Constants.HEADER_CASCADE)).thenReturn(Constants.CASCADE_DELETE);
|
||||||
|
e = assertThrows(BaseServerResponseException.class, () -> {
|
||||||
|
myOrganizationDao
|
||||||
|
.deleteByUrl("Organization?" + JpaConstants.PARAM_DELETE_EXPUNGE + "=true", mySrd);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get not implemented HTTP 400 error
|
||||||
|
assertEquals(Constants.STATUS_HTTP_400_BAD_REQUEST, e.getStatusCode());
|
||||||
|
assertEquals("_expunge cannot be used with _cascade", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteExpungeThrowExceptionIfForeignKeyLinksExists() {
|
public void testDeleteExpungeThrowExceptionIfForeignKeyLinksExists() {
|
||||||
// setup
|
// setup
|
||||||
|
|
Loading…
Reference in New Issue