diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java index c817ea9c420..4f35d2e6a5e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java @@ -24,7 +24,24 @@ import ca.uhn.fhir.util.CoverageIgnore; */ /** - * Represents an HTTP 401 Client Unauthorized response, which means that the client needs to provide credentials, or has provided invalid credentials. + * Represents an HTTP 401 Client Unauthorized response, which + * means that the client needs to provide credentials, or has + * provided invalid credentials. + *

+ * For security failures, you should use + * {@link AuthenticationException} if you want to indicate that the + * user could not be authenticated (e.g. credential failures), also + * known as an authentication failure. + * You should use {@link ForbiddenOperationException} if you want to + * indicate that the authenticated user does not have permission to + * perform the requested operation, also known as an authorization + * failure. + *

+ *

+ * Note that a complete list of RESTful exceptions is available in the Package + * Summary. + *

+ */ @CoverageIgnore public class AuthenticationException extends BaseServerResponseException { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java index 0cfca6bfd0e..782d9f33a36 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java @@ -33,6 +33,16 @@ import ca.uhn.fhir.util.CoverageIgnore; * * *

+ * For security failures, you should use + * {@link AuthenticationException} if you want to indicate that the + * user could not be authenticated (e.g. credential failures), also + * known as an authentication failure. + * You should use {@link ForbiddenOperationException} if you want to + * indicate that the authenticated user does not have permission to + * perform the requested operation, also known as an authorization + * failure. + *

+ *

* Note that a complete list of RESTful exceptions is available in the Package * Summary. *

diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 504bec5f0e2..84e2358a42e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -903,12 +903,19 @@ public abstract class BaseHapiFhirResourceDao extends B updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate()); } + @Override + public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm) { + removeTag(theId, theTagType, theScheme, theTerm, null); + } + @Override public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails) { // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); - notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails); - + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId); + notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails); + } + StopWatch w = new StopWatch(); BaseHasResource entity = readEntity(theId); if (entity == null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java index 977ca4dbf7d..5c1101145ef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java @@ -170,7 +170,9 @@ public interface IFhirResourceDao extends IDao { */ void reindex(T theResource, ResourceTable theEntity); - void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails); + void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode, RequestDetails theRequestDetails); + + void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode); IBundleProvider search(Map theParams); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java index d0c1420293c..ffae43164bd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java @@ -26,6 +26,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.method.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; @@ -38,4 +39,5 @@ public interface IFhirResourceDaoPatient extends IFhirR IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails); + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 527f2828d6f..f8a56dfb10e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -2592,6 +2592,103 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } + @Test + public void testRemoveTag() { + + String methodName = "testResourceMetaOperation"; + IIdType id1, id2; + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue(methodName); + patient.addName().setFamily("Tester").addGiven("Joe"); + patient.getMeta().addTag(null, "Dog", "Puppies"); + + patient.getMeta().addSecurity().setSystem("seclabel:sys:1").setCode("seclabel:code:1").setDisplay("seclabel:dis:1"); + + patient.getMeta().addProfile(("http://profile/1")); + + id1 = myPatientDao.create(patient, mySrd).getId(); + } + { + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue(methodName); + patient.addName().setFamily("Tester").addGiven("Joe"); + + patient.getMeta().addTag("http://foo", "Cat", "Kittens"); + patient.getMeta().addSecurity().setSystem("seclabel:sys:2").setCode("seclabel:code:2").setDisplay("seclabel:dis:2"); + patient.getMeta().addProfile("http://profile/2"); + + id2 = myPatientDao.create(patient, mySrd).getId(); + } + { + Device device = new Device(); + device.addIdentifier().setSystem("urn:system").setValue(methodName); + device.getMeta().addTag("http://foo", "Foo", "Bars"); + device.getMeta().addSecurity().setSystem("seclabel:sys:3").setCode("seclabel:code:3").setDisplay("seclabel:dis:3"); + device.getMeta().addProfile("http://profile/3"); + myDeviceDao.create(device, mySrd); + } + + Meta meta; + + meta = myPatientDao.metaGetOperation(Meta.class, mySrd); + List published = meta.getTag(); + assertEquals(2, published.size()); + assertEquals(null, published.get(0).getSystem()); + assertEquals("Dog", published.get(0).getCode()); + assertEquals("Puppies", published.get(0).getDisplay()); + assertEquals("http://foo", published.get(1).getSystem()); + assertEquals("Cat", published.get(1).getCode()); + assertEquals("Kittens", published.get(1).getDisplay()); + List secLabels = meta.getSecurity(); + assertEquals(2, secLabels.size()); + assertEquals("seclabel:sys:1", secLabels.get(0).getSystemElement().getValue()); + assertEquals("seclabel:code:1", secLabels.get(0).getCodeElement().getValue()); + assertEquals("seclabel:dis:1", secLabels.get(0).getDisplayElement().getValue()); + assertEquals("seclabel:sys:2", secLabels.get(1).getSystemElement().getValue()); + assertEquals("seclabel:code:2", secLabels.get(1).getCodeElement().getValue()); + assertEquals("seclabel:dis:2", secLabels.get(1).getDisplayElement().getValue()); + List profiles = meta.getProfile(); + assertEquals(2, profiles.size()); + assertEquals("http://profile/1", profiles.get(0).getValue()); + assertEquals("http://profile/2", profiles.get(1).getValue()); + + meta = myPatientDao.metaGetOperation(Meta.class, id2, mySrd); + published = meta.getTag(); + assertEquals(1, published.size()); + assertEquals("http://foo", published.get(0).getSystem()); + assertEquals("Cat", published.get(0).getCode()); + assertEquals("Kittens", published.get(0).getDisplay()); + secLabels = meta.getSecurity(); + assertEquals(1, secLabels.size()); + assertEquals("seclabel:sys:2", secLabels.get(0).getSystemElement().getValue()); + assertEquals("seclabel:code:2", secLabels.get(0).getCodeElement().getValue()); + assertEquals("seclabel:dis:2", secLabels.get(0).getDisplayElement().getValue()); + profiles = meta.getProfile(); + assertEquals(1, profiles.size()); + assertEquals("http://profile/2", profiles.get(0).getValue()); + + myPatientDao.removeTag(id1, TagTypeEnum.TAG, null, "Dog"); + myPatientDao.removeTag(id1, TagTypeEnum.SECURITY_LABEL, "seclabel:sys:1", "seclabel:code:1"); + myPatientDao.removeTag(id1, TagTypeEnum.PROFILE, BaseHapiFhirDao.NS_JPA_PROFILE, "http://profile/1"); + + meta = myPatientDao.metaGetOperation(Meta.class, mySrd); + published = meta.getTag(); + assertEquals(1, published.size()); + assertEquals("http://foo", published.get(0).getSystem()); + assertEquals("Cat", published.get(0).getCode()); + assertEquals("Kittens", published.get(0).getDisplay()); + secLabels = meta.getSecurity(); + assertEquals(1, secLabels.size()); + assertEquals("seclabel:sys:2", secLabels.get(0).getSystemElement().getValue()); + assertEquals("seclabel:code:2", secLabels.get(0).getCodeElement().getValue()); + assertEquals("seclabel:dis:2", secLabels.get(0).getDisplayElement().getValue()); + profiles = meta.getProfile(); + assertEquals(1, profiles.size()); + assertEquals("http://profile/2", profiles.get(0).getValue()); + + } + @Test public void testReverseIncludes() { String methodName = "testReverseIncludes"; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c167e33d94f..eb010dfba1e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -63,6 +63,11 @@ of literal ones, meaning that the server will not try to resolve these URLs. Thanks to Eeva Turkka for the suggestion! + + Add a utility method to JPA server: + IFhirResourceDao#removeTag(IIdType, TagTypeEnum, String, String)]]>. This allows client code to remove tags + from a resource without having a servlet request object in context. +