Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
8a32e4bae5
|
@ -267,7 +267,10 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
*/
|
||||
protected void postProcessResult(RequestDetails theRequestDetails, ValidationResult theValidationResult) { }
|
||||
|
||||
protected void validate(T theRequest, RequestDetails theRequestDetails) {
|
||||
/**
|
||||
* Note: May return null
|
||||
*/
|
||||
protected ValidationResult validate(T theRequest, RequestDetails theRequestDetails) {
|
||||
FhirValidator validator = theRequestDetails.getServer().getFhirContext().newValidator();
|
||||
if (myValidatorModules != null) {
|
||||
for (IValidatorModule next : myValidatorModules) {
|
||||
|
@ -276,7 +279,7 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
}
|
||||
|
||||
if (theRequest == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
ValidationResult validationResult;
|
||||
|
@ -285,7 +288,7 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
} catch (Exception e) {
|
||||
if (myIgnoreValidatorExceptions) {
|
||||
ourLog.warn("Validator threw an exception during validation", e);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (e instanceof BaseServerResponseException) {
|
||||
throw (BaseServerResponseException)e;
|
||||
|
@ -312,7 +315,7 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
for (SingleValidationMessage next : validationResult.getMessages()) {
|
||||
if (next.getSeverity().ordinal() >= myFailOnSeverity) {
|
||||
fail(theRequestDetails, validationResult);
|
||||
return;
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +345,8 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
}
|
||||
|
||||
postProcessResult(theRequestDetails, validationResult);
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
private static class MyLookup extends StrLookup<String> {
|
||||
|
|
|
@ -12,7 +12,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* 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
|
||||
* 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,
|
||||
|
@ -27,6 +27,9 @@ import java.nio.charset.Charset;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
@ -51,6 +54,19 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestValidatingInterceptor.class);
|
||||
|
||||
/**
|
||||
* A {@link RequestDetails#getUserData() user data} entry will be created with this
|
||||
* key which contains the {@link ValidationResult} from validating the request.
|
||||
*/
|
||||
public static final String REQUEST_VALIDATION_RESULT = RequestValidatingInterceptor.class.getName() + "_REQUEST_VALIDATION_RESULT";
|
||||
|
||||
private boolean myAddValidationResultsToResponseOperationOutcome = true;
|
||||
|
||||
@Override
|
||||
ValidationResult doValidate(FhirValidator theValidator, String theRequest) {
|
||||
return theValidator.validateWithResult(theRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequestDetails);
|
||||
|
@ -67,11 +83,59 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
return true;
|
||||
}
|
||||
|
||||
validate(requestText, theRequestDetails);
|
||||
ValidationResult validationResult = validate(requestText, theRequestDetails);
|
||||
|
||||
// The JPA server will use this
|
||||
theRequestDetails.getUserData().put(REQUEST_VALIDATION_RESULT, validationResult);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true), the validation results
|
||||
* will be added to the OperationOutcome being returned to the client,
|
||||
* unless the response being returned is not an OperationOutcome
|
||||
* to begin with (e.g. if the client has requested
|
||||
* <code>Return: prefer=representation</code>)
|
||||
*/
|
||||
public boolean isAddValidationResultsToResponseOperationOutcome() {
|
||||
return myAddValidationResultsToResponseOperationOutcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
if (myAddValidationResultsToResponseOperationOutcome) {
|
||||
if (theResponseObject instanceof IBaseOperationOutcome) {
|
||||
IBaseOperationOutcome oo = (IBaseOperationOutcome) theResponseObject;
|
||||
|
||||
if (theRequestDetails != null) {
|
||||
ValidationResult validationResult = (ValidationResult) theRequestDetails.getUserData().get(RequestValidatingInterceptor.REQUEST_VALIDATION_RESULT);
|
||||
if (validationResult != null) {
|
||||
validationResult.populateOperationOutcome(oo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
String provideDefaultResponseHeaderName() {
|
||||
return DEFAULT_RESPONSE_HEADER_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to {@literal true} (default is true), the validation results
|
||||
* will be added to the OperationOutcome being returned to the client,
|
||||
* unless the response being returned is not an OperationOutcome
|
||||
* to begin with (e.g. if the client has requested
|
||||
* <code>Return: prefer=representation</code>)
|
||||
*/
|
||||
public void setAddValidationResultsToResponseOperationOutcome(boolean theAddValidationResultsToResponseOperationOutcome) {
|
||||
myAddValidationResultsToResponseOperationOutcome = theAddValidationResultsToResponseOperationOutcome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the response header to add validation failures to
|
||||
|
@ -84,17 +148,4 @@ public class RequestValidatingInterceptor extends BaseValidatingInterceptor<Stri
|
|||
super.setResponseHeaderName(theResponseHeaderName);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
String provideDefaultResponseHeaderName() {
|
||||
return DEFAULT_RESPONSE_HEADER_NAME;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
ValidationResult doValidate(FhirValidator theValidator, String theRequest) {
|
||||
return theValidator.validateWithResult(theRequest);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -301,18 +301,6 @@ public class AuthorizationInterceptor extends InterceptorAdapter implements ISer
|
|||
return true;
|
||||
}
|
||||
|
||||
private List<IBaseResource> toListOfResourcesAndExcludeContainer(IBaseResource theResponseObject, FhirContext fhirContext) {
|
||||
List<IBaseResource> resources;
|
||||
resources = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
|
||||
|
||||
// Exclude the container
|
||||
if (resources.size() > 0 && resources.get(0) == theResponseObject) {
|
||||
resources = resources.subList(1, resources.size());
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
@CoverageIgnore
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||
|
@ -350,6 +338,18 @@ public class AuthorizationInterceptor extends InterceptorAdapter implements ISer
|
|||
myDefaultPolicy = theDefaultPolicy;
|
||||
}
|
||||
|
||||
private List<IBaseResource> toListOfResourcesAndExcludeContainer(IBaseResource theResponseObject, FhirContext fhirContext) {
|
||||
List<IBaseResource> resources;
|
||||
resources = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
|
||||
|
||||
// Exclude the container
|
||||
if (resources.size() > 0 && resources.get(0) == theResponseObject) {
|
||||
resources = resources.subList(1, resources.size());
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
// private List<IBaseResource> toListOfResources(FhirContext fhirContext, IBaseBundle responseBundle) {
|
||||
// List<IBaseResource> retVal = BundleUtil.toListOfResources(fhirContext, responseBundle);
|
||||
// for (int i = 0; i < retVal.size(); i++) {
|
||||
|
@ -368,10 +368,10 @@ public class AuthorizationInterceptor extends InterceptorAdapter implements ISer
|
|||
}
|
||||
|
||||
private enum OperationExamineDirection {
|
||||
BOTH,
|
||||
IN,
|
||||
NONE,
|
||||
NONE,
|
||||
OUT,
|
||||
BOTH,
|
||||
}
|
||||
|
||||
public static class Verdict {
|
||||
|
|
|
@ -35,6 +35,11 @@ public interface IAuthRuleBuilderOperationNamed {
|
|||
*/
|
||||
IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType);
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>type</code> level on any type
|
||||
*/
|
||||
IAuthRuleFinished onAnyType();
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>instance</code> level
|
||||
*/
|
||||
|
@ -45,4 +50,9 @@ public interface IAuthRuleBuilderOperationNamed {
|
|||
*/
|
||||
IAuthRuleBuilderRuleOpClassifierFinished onInstancesOfType(Class<? extends IBaseResource> theType);
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>instance</code> level on any instance
|
||||
*/
|
||||
IAuthRuleFinished onAnyInstance();
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ class OperationRule extends BaseRule implements IAuthRule {
|
|||
private HashSet<Class<? extends IBaseResource>> myAppliesToTypes;
|
||||
private List<IIdType> myAppliesToIds;
|
||||
private HashSet<Class<? extends IBaseResource>> myAppliesToInstancesOfType;
|
||||
private boolean myAppliesToAnyType;
|
||||
private boolean myAppliesToAnyInstance;
|
||||
|
||||
/**
|
||||
* Must include the leading $
|
||||
|
@ -66,7 +68,9 @@ class OperationRule extends BaseRule implements IAuthRule {
|
|||
}
|
||||
break;
|
||||
case EXTENDED_OPERATION_TYPE:
|
||||
if (myAppliesToTypes != null) {
|
||||
if (myAppliesToAnyType) {
|
||||
applies = true;
|
||||
} else if (myAppliesToTypes != null) {
|
||||
// TODO: Convert to a map of strings and keep the result
|
||||
for (Class<? extends IBaseResource> next : myAppliesToTypes) {
|
||||
String resName = ctx.getResourceDefinition(next).getName();
|
||||
|
@ -78,7 +82,9 @@ class OperationRule extends BaseRule implements IAuthRule {
|
|||
}
|
||||
break;
|
||||
case EXTENDED_OPERATION_INSTANCE:
|
||||
if (theInputResourceId != null) {
|
||||
if (myAppliesToAnyInstance) {
|
||||
applies = true;
|
||||
} else if (theInputResourceId != null) {
|
||||
if (myAppliesToIds != null) {
|
||||
String instanceId = theInputResourceId.toUnqualifiedVersionless().getValue();
|
||||
for (IIdType next : myAppliesToIds) {
|
||||
|
@ -131,4 +137,12 @@ class OperationRule extends BaseRule implements IAuthRule {
|
|||
myAppliesToInstancesOfType = theAppliesToTypes;
|
||||
}
|
||||
|
||||
public void appliesToAnyInstance() {
|
||||
myAppliesToAnyInstance = true;
|
||||
}
|
||||
|
||||
public void appliesToAnyType() {
|
||||
myAppliesToAnyType = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -371,6 +371,22 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
return appliesToTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleFinished onAnyType() {
|
||||
OperationRule rule = createRule();
|
||||
rule.appliesToAnyType();
|
||||
myRules.add(rule);
|
||||
return new RuleBuilderFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleFinished onAnyInstance() {
|
||||
OperationRule rule = createRule();
|
||||
rule.appliesToAnyInstance();
|
||||
myRules.add(rule);
|
||||
return new RuleBuilderFinished();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -102,6 +102,14 @@ public class ValidationResult {
|
|||
*/
|
||||
public IBaseOperationOutcome toOperationOutcome() {
|
||||
IBaseOperationOutcome oo = (IBaseOperationOutcome) myCtx.getResourceDefinition("OperationOutcome").newInstance();
|
||||
populateOperationOutcome(oo);
|
||||
return oo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate an operation outcome with the results of the validation
|
||||
*/
|
||||
public void populateOperationOutcome(IBaseOperationOutcome theOperationOutcome) {
|
||||
for (SingleValidationMessage next : myMessages) {
|
||||
String location;
|
||||
if (isNotBlank(next.getLocationString())) {
|
||||
|
@ -112,15 +120,13 @@ public class ValidationResult {
|
|||
location = null;
|
||||
}
|
||||
String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null;
|
||||
OperationOutcomeUtil.addIssue(myCtx, oo, severity, next.getMessage(), location, ExceptionHandlingInterceptor.PROCESSING);
|
||||
OperationOutcomeUtil.addIssue(myCtx, theOperationOutcome, severity, next.getMessage(), location, ExceptionHandlingInterceptor.PROCESSING);
|
||||
}
|
||||
|
||||
if (myMessages.isEmpty()) {
|
||||
String message = myCtx.getLocalizer().getMessage(ValidationResult.class, "noIssuesDetected");
|
||||
OperationOutcomeUtil.addIssue(myCtx, oo, "information", message, null, "informational");
|
||||
OperationOutcomeUtil.addIssue(myCtx, theOperationOutcome, "information", message, null, "informational");
|
||||
}
|
||||
|
||||
return oo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -138,6 +138,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
|
@ -402,6 +403,36 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
ourClient.create().resource(input).execute().getResource();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateIncludesRequestValidatorInterceptorOutcome() throws IOException {
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
assertTrue(interceptor.isAddValidationResultsToResponseOperationOutcome());
|
||||
interceptor.setFailOnSeverity(null);
|
||||
|
||||
ourRestServer.registerInterceptor(interceptor);
|
||||
try {
|
||||
// Missing status, which is mandatory
|
||||
Observation obs = new Observation();
|
||||
obs.addIdentifier().setSystem("urn:foo").setValue("bar");
|
||||
IBaseResource outcome = ourClient.create().resource(obs).execute().getOperationOutcome();
|
||||
|
||||
String encodedOo = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info(encodedOo);
|
||||
assertThat(encodedOo, containsString("cvc-complex-type.2.4.b"));
|
||||
assertThat(encodedOo, containsString("Successfully created resource \\\"Observation/"));
|
||||
|
||||
interceptor.setAddValidationResultsToResponseOperationOutcome(false);
|
||||
outcome = ourClient.create().resource(obs).execute().getOperationOutcome();
|
||||
encodedOo = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome);
|
||||
ourLog.info(encodedOo);
|
||||
assertThat(encodedOo, not(containsString("cvc-complex-type.2.4.b")));
|
||||
assertThat(encodedOo, containsString("Successfully created resource \\\"Observation/"));
|
||||
|
||||
} finally {
|
||||
ourRestServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateConditional() {
|
||||
Patient patient = new Patient();
|
||||
|
@ -800,7 +831,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
|
||||
// Delete should now have no matches
|
||||
|
||||
|
||||
delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
|
||||
response = ourHttpClient.execute(delete);
|
||||
try {
|
||||
|
@ -3658,7 +3689,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertThat(resp, not(containsString("Resource has no id")));
|
||||
assertThat(resp, containsString("<pre>No issues detected during validation</pre>"));
|
||||
assertThat(resp,
|
||||
stringContainsInOrder("<issue>", "<severity value=\"information\"/>", "<code value=\"informational\"/>", "<diagnostics value=\"No issues detected during validation\"/>", "</issue>"));
|
||||
stringContainsInOrder("<issue>", "<severity value=\"information\"/>", "<code value=\"informational\"/>", "<diagnostics value=\"No issues detected during validation\"/>",
|
||||
"</issue>"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
response.close();
|
||||
|
@ -3684,7 +3716,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertThat(resp, not(containsString("Resource has no id")));
|
||||
assertThat(resp, containsString("<pre>No issues detected during validation</pre>"));
|
||||
assertThat(resp,
|
||||
stringContainsInOrder("<issue>", "<severity value=\"information\"/>", "<code value=\"informational\"/>", "<diagnostics value=\"No issues detected during validation\"/>", "</issue>"));
|
||||
stringContainsInOrder("<issue>", "<severity value=\"information\"/>", "<code value=\"informational\"/>", "<diagnostics value=\"No issues detected during validation\"/>",
|
||||
"</issue>"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
response.close();
|
||||
|
|
|
@ -5,6 +5,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -13,6 +14,7 @@ import static org.mockito.Mockito.when;
|
|||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -25,13 +27,17 @@ import org.hl7.fhir.dstu3.model.Bundle;
|
|||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu3.model.Organization;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
@ -64,21 +70,27 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
myServerInterceptor = mock(IServerInterceptor.class);
|
||||
myDaoInterceptor = mock(IServerInterceptor.class);
|
||||
|
||||
resetServerInterceptor();
|
||||
|
||||
myDaoConfig.getInterceptors().add(myDaoInterceptor);
|
||||
ourRestServer.registerInterceptor(myServerInterceptor);
|
||||
|
||||
ourRestServer.registerInterceptor(new InterceptorAdapter() {
|
||||
@Override
|
||||
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) {
|
||||
super.incomingRequestPreHandled(theOperation, theProcessedRequest);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void resetServerInterceptor() throws ServletException, IOException {
|
||||
reset(myServerInterceptor);
|
||||
when(myServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
myDaoConfig.getInterceptors().add(myDaoInterceptor);
|
||||
ourRestServer.registerInterceptor(myServerInterceptor);
|
||||
|
||||
ourRestServer.registerInterceptor(new InterceptorAdapter() {
|
||||
@Override
|
||||
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) {
|
||||
super.incomingRequestPreHandled(theOperation, theProcessedRequest);
|
||||
}});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -117,6 +129,55 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
assertNotNull(ardCaptor.getValue().getResource());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateResourceWithVersionedReference() throws IOException, ServletException {
|
||||
String methodName = "testCreateResourceWithVersionedReference";
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName("orgName");
|
||||
IIdType orgId = ourClient.create().resource(org).execute().getId().toUnqualified();
|
||||
assertNotNull(orgId.getVersionIdPartAsLong());
|
||||
|
||||
resetServerInterceptor();
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().setFamily(methodName);
|
||||
pt.setManagingOrganization(new Reference(orgId));
|
||||
|
||||
IParser parser = myFhirCtx.newXmlParser();
|
||||
parser.setDontStripVersionsFromReferencesAtPaths("Patient.managingOrganization");
|
||||
parser.setPrettyPrint(true);
|
||||
String resource = parser.encodeResourceToString(pt);
|
||||
|
||||
ourLog.info(resource);
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
try {
|
||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info("Response was: {}", resp);
|
||||
assertEquals(201, response.getStatusLine().getStatusCode());
|
||||
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
|
||||
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
verify(myServerInterceptor, times(1)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||
|
||||
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getValue());
|
||||
assertEquals("Patient", ardCaptor.getValue().getResourceType());
|
||||
assertNotNull(ardCaptor.getValue().getResource());
|
||||
|
||||
Patient patient;
|
||||
patient = (Patient) ardCaptor.getAllValues().get(0).getResource();
|
||||
assertEquals(orgId.getValue(), patient.getManagingOrganization().getReference());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateResourceInTransaction() throws IOException {
|
||||
String methodName = "testCreateResourceInTransaction";
|
||||
|
@ -144,7 +205,7 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
}
|
||||
|
||||
/*
|
||||
* Server Interceptor
|
||||
* Server Interceptor
|
||||
*/
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
|
@ -163,9 +224,9 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
verify(myServerInterceptor, times(1)).incomingRequestPostProcessed(rdCaptor.capture(), srCaptor.capture(), sRespCaptor.capture());
|
||||
|
||||
/*
|
||||
* DAO Interceptor
|
||||
* DAO Interceptor
|
||||
*/
|
||||
|
||||
|
||||
ardCaptor = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
opTypeCaptor = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
verify(myDaoInterceptor, times(2)).incomingRequestPreHandled(opTypeCaptor.capture(), ardCaptor.capture());
|
||||
|
@ -175,7 +236,7 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
assertEquals(RestOperationTypeEnum.CREATE, opTypeCaptor.getAllValues().get(1));
|
||||
assertEquals("Patient", ardCaptor.getAllValues().get(1).getResourceType());
|
||||
assertNotNull(ardCaptor.getAllValues().get(1).getResource());
|
||||
|
||||
|
||||
rdCaptor = ArgumentCaptor.forClass(RequestDetails.class);
|
||||
srCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
|
||||
sRespCaptor = ArgumentCaptor.forClass(HttpServletResponse.class);
|
||||
|
|
|
@ -99,12 +99,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private IResource createPatient(Integer theId, int theVersion) {
|
||||
IResource retVal = createPatient(theId);
|
||||
retVal.setId(retVal.getId().withVersion(Integer.toString(theVersion)));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private IResource createPatient(Integer theId) {
|
||||
Patient retVal = new Patient();
|
||||
if (theId != null) {
|
||||
|
@ -113,6 +107,12 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
retVal.addName().addFamily("FAM");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private IResource createPatient(Integer theId, int theVersion) {
|
||||
IResource retVal = createPatient(theId);
|
||||
retVal.setId(retVal.getId().withVersion(Integer.toString(theVersion)));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private String extractResponseAndClose(HttpResponse status) throws IOException {
|
||||
if (status.getEntity() == null) {
|
||||
|
@ -497,6 +497,46 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
assertTrue(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithReadAll() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("Rule 1").read().allResources().withAnyId()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
|
||||
ourReturn = Arrays.asList(createPatient(2, 1));
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetadataAllow() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
|
@ -747,6 +787,75 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationInstanceLevelAnyInstance() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
return new RuleBuilder()
|
||||
.allow("RULE 1").operation().named("opName").onAnyInstance().andThen()
|
||||
.build();
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createObservation(10, "Patient/2"));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
// Type
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
// Instance
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
// Another Instance
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/2/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
// Wrong name
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$opName2");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationNotAllowedWithWritePermissiom() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
|
@ -926,13 +1035,13 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testHistoryWithReadAll() throws Exception {
|
||||
public void testOperationTypeLevelWildcard() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("Rule 1").read().allResources().withAnyId()
|
||||
.allow("RULE 1").operation().named("opName").onAnyType().andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
|
@ -940,29 +1049,59 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
ourReturn = Arrays.asList(createPatient(2, 1));
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/_history");
|
||||
ourReturn = Arrays.asList(createObservation(10, "Patient/2"));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
response = extractResponseAndClose(status);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
// Type
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
// Another type
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history");
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
// Wrong name
|
||||
ourHitMethod = false;
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history");
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName2");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
|
||||
// Instance
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
response = extractResponseAndClose(status);
|
||||
ourLog.info(response);
|
||||
assertThat(response, containsString("Access denied by default policy"));
|
||||
assertEquals(403, status.getStatusLine().getStatusCode());
|
||||
assertFalse(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1711,19 +1850,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<IResource> history() {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<IResource> history(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
|
||||
@Delete()
|
||||
public MethodOutcome delete(IRequestOperationCallback theRequestOperationCallback, @IdParam IdDt theId, @ConditionalUrlParam String theConditionalUrl, RequestDetails theRequestDetails) {
|
||||
ourHitMethod = true;
|
||||
|
@ -1744,11 +1870,24 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<IResource> history() {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<IResource> history(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
@Operation(name = "opName", idempotent = true)
|
||||
public Parameters operation() {
|
||||
ourHitMethod = true;
|
||||
|
@ -1767,6 +1906,12 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
@Operation(name = "opName2", idempotent = true)
|
||||
public Parameters operation2() {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
@Read(version = true)
|
||||
public Patient read(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
|
@ -1821,6 +1966,12 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
|
||||
public static class PlainProvider {
|
||||
|
||||
@History()
|
||||
public List<IResource> history() {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
@Operation(name = "opName", idempotent = true)
|
||||
public Parameters operation() {
|
||||
ourHitMethod = true;
|
||||
|
@ -1833,12 +1984,6 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return (Bundle) ourReturn.get(0);
|
||||
}
|
||||
|
||||
@History()
|
||||
public List<IResource> history() {
|
||||
ourHitMethod = true;
|
||||
return (ourReturn);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -166,6 +166,18 @@
|
|||
JPA server interceptor methods for create/update/delete provided
|
||||
the wrong version ID to the interceptors
|
||||
</action>
|
||||
<action type="add">
|
||||
A post-processing hook for subclasses of BaseValidatingInterceptor is now available.
|
||||
</action>
|
||||
<action type="add" issue="585">
|
||||
AuthorizationInterceptor can now authorize (allow/deny) extended operations
|
||||
on instances and types by wildcard (on any type, or on any instance)
|
||||
</action>
|
||||
<action type="add" issue="595">
|
||||
When RequestValidatingInterceptor is used, the validation results
|
||||
are now populated into the OperationOutcome produced by
|
||||
create and update operations
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.2" date="2016-12-20">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue