Allow patch to proceed with AuthorizationInterceptor

This commit is contained in:
James Agnew 2017-07-12 11:14:14 -04:00
parent 73ddf0e0d9
commit 65cc41e376
6 changed files with 264 additions and 169 deletions

View File

@ -0,0 +1,30 @@
package example;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.OperationOutcome;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
public class PatchExamples {
//START SNIPPET: patch
@Patch
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
if (thePatchType == PatchTypeEnum.JSON_PATCH) {
// do something
}
if (thePatchType == PatchTypeEnum.XML_PATCH) {
// do something
}
OperationOutcome retVal = new OperationOutcome();
retVal.getText().setDivAsString("<div>OK</div>");
return retVal;
}
//END SNIPPET: patch
}

View File

@ -154,6 +154,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
case CREATE:
case UPDATE:
case PATCH:
// if (theRequestResource != null) {
// if (theRequestResource.getIdElement() != null) {
// if (theRequestResource.getIdElement().hasIdPart() == false) {

View File

@ -37,9 +37,7 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.method.IRequestOperationCallback;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.*;
@ -1817,6 +1815,43 @@ public class AuthorizationInterceptorDstu2Test {
}
}
@Test
public void testWritePatchByInstance() throws Exception {
ourConditionalCreateId = "1";
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 1").write().instance("Patient/900").andThen()
.build();
//@formatter:on
}
});
HttpEntityEnclosingRequestBase httpPost;
HttpResponse status;
String response;
String input = "[ { \"op\": \"replace\", \"path\": \"/gender\", \"value\": \"male\" } ]";
ourHitMethod = false;
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);
assertEquals(204, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
ourHitMethod = false;
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);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
@Test
public void testWriteByInstance() throws Exception {
@ -1874,7 +1909,6 @@ public class AuthorizationInterceptorDstu2Test {
}
@Test
public void testReadByInstance() throws Exception {
ourConditionalCreateId = "1";
@ -1922,7 +1956,6 @@ public class AuthorizationInterceptorDstu2Test {
}
@AfterClass
public static void afterClassClearContext() throws Exception {
ourServer.stop();
@ -2109,7 +2142,6 @@ public class AuthorizationInterceptorDstu2Test {
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
@ -2183,6 +2215,14 @@ public class AuthorizationInterceptorDstu2Test {
return retVal;
}
@Patch()
public MethodOutcome patch(@IdParam IdDt theId, @ResourceParam String theResource, PatchTypeEnum thePatchType) {
ourHitMethod = true;
MethodOutcome retVal = new MethodOutcome();
return retVal;
}
@Validate
public MethodOutcome validate(@ResourceParam Patient theResource, @IdParam IdDt theId, @ResourceParam String theRawResource, @ResourceParam EncodingEnum theEncoding,
@Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile, RequestDetails theRequestDetails) {
@ -2225,5 +2265,4 @@ public class AuthorizationInterceptorDstu2Test {
}
}

View File

@ -173,7 +173,6 @@ public class PatchDstu3Test {
return Patient.class;
}
//@formatter:off
@Patch
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
ourLastMethod = "patientPatch";
@ -184,7 +183,6 @@ public class PatchDstu3Test {
retVal.getText().setDivAsString("<div>OK</div>");
return retVal;
}
//@formatter:on
}

View File

@ -145,6 +145,10 @@
to display the request headers and response headers, and individual lines
may be highlighted.
</action>
<action type="fix">
AuthorizationInterceptor did not permit PATCH operations to proceed even
if the user had write access for the resource being patched.
</action>
</release>
<release version="2.5" date="2017-06-08">
<action type="fix">

View File

@ -1441,6 +1441,29 @@ If-Match: W/"3"]]></pre>
<a name="exceptions" />
</section>
<section name="Instance Level - Patch">
<p>
HAPI FHIR includes basic support for the
<a href="http://hl7.org/implement/standards/fhir/http.html#patch">
<b>patch</b>
</a>
operation. This support allows you to perform patches, but does not
include logic to actually implement resource patching in the server
framework (note that the JPA server does include a patch implementation).
</p>
<p>
The following snippet shows how to define a patch method on a server:
</p>
<macro name="snippet">
<param name="id" value="patch" />
<param name="file" value="examples/src/main/java/example/PatchExamples.java" />
</macro>
</patch>
<!-- ****************************************************************** -->
<!-- ****************************************************************** -->
<!-- ****************************************************************** -->