Make sure that sub-request transaction searches and reads preserve HTTP

headers
This commit is contained in:
James Agnew 2019-01-10 07:26:04 -07:00
parent b8f200f897
commit aee7b2b882
8 changed files with 98 additions and 17 deletions

View File

@ -233,7 +233,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
Entry nextRespEntry = response.getEntry().get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails(theRequestDetails);
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());

View File

@ -384,7 +384,7 @@ public class TransactionProcessor<BUNDLE extends IBaseBundle, BUNDLEENTRY> {
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
BUNDLEENTRY nextRespEntry = myVersionAdapter.getEntries(response).get(originalOrder);
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails(theRequestDetails);
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
requestDetails.setRequestType(RequestTypeEnum.GET);
requestDetails.setServer(theRequestDetails.getServer());

View File

@ -29,11 +29,25 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
public class ServletSubRequestDetails extends ServletRequestDetails {
private Map<String, ArrayList<String>> myHeaders = new HashMap<>();
private Map<String, List<String>> myHeaders = new HashMap<>();
/**
* Constructor
*
* @param theRequestDetails The parent request details
*/
public ServletSubRequestDetails(ServletRequestDetails theRequestDetails) {
if (theRequestDetails != null) {
Map<String, List<String>> headers = theRequestDetails.getHeaders();
for (Map.Entry<String, List<String>> next : headers.entrySet()) {
myHeaders.put(next.getKey().toLowerCase(), next.getValue());
}
}
}
public void addHeader(String theName, String theValue) {
String lowerCase = theName.toLowerCase();
ArrayList<String> list = myHeaders.get(lowerCase);
List<String> list = myHeaders.get(lowerCase);
if (list == null) {
list = new ArrayList<>();
myHeaders.put(lowerCase, list);
@ -43,7 +57,7 @@ public class ServletSubRequestDetails extends ServletRequestDetails {
@Override
public String getHeader(String theName) {
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
List<String> list = myHeaders.get(theName.toLowerCase());
if (list == null || list.isEmpty()) {
return null;
}
@ -52,7 +66,7 @@ public class ServletSubRequestDetails extends ServletRequestDetails {
@Override
public List<String> getHeaders(String theName) {
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
List<String> list = myHeaders.get(theName.toLowerCase());
if (list == null || list.isEmpty()) {
return null;
}

View File

@ -42,10 +42,7 @@ public class DaoSubscriptionProvider implements ISubscriptionProvider {
@Override
public IBundleProvider search(SearchParameterMap theMap) {
IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao();
RequestDetails req = new ServletSubRequestDetails();
req.setSubRequest(true);
return subscriptionDao.search(theMap, req);
return subscriptionDao.search(theMap);
}
@Override

View File

@ -72,12 +72,9 @@ public class DaoSubscriptionMatcher implements ISubscriptionMatcher {
RuntimeResourceDefinition responseResourceDef = subscriptionDao.validateCriteriaAndReturnResourceDefinition(theCriteria);
SearchParameterMap responseCriteriaUrl = myMatchUrlService.translateMatchUrl(theCriteria, responseResourceDef);
RequestDetails req = new ServletSubRequestDetails();
req.setSubRequest(true);
IFhirResourceDao<? extends IBaseResource> responseDao = myDaoRegistry.getResourceDao(responseResourceDef.getImplementingClass());
responseCriteriaUrl.setLoadSynchronousUpTo(1);
return responseDao.search(responseCriteriaUrl, req);
return responseDao.search(responseCriteriaUrl);
}
}

View File

@ -4,6 +4,8 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@ -28,6 +30,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
@ -148,6 +151,58 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
}
@Test
public void testReadInTransaction() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
IIdType id = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
String authHeader = theRequestDetails.getHeader("Authorization");
if (!"Bearer AAA".equals(authHeader)) {
throw new AuthenticationException("Invalid auth header: " + authHeader);
}
return new RuleBuilder()
.allow().transaction().withAnyOperation().andApplyNormalRules().andThen()
.allow().read().resourcesOfType(Patient.class).withAnyId()
.build();
}
});
SimpleRequestHeaderInterceptor interceptor = new SimpleRequestHeaderInterceptor("Authorization", "Bearer AAA");
try {
ourClient.registerInterceptor(interceptor);
Bundle bundle;
Bundle responseBundle;
// Read
bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
bundle.addEntry().getRequest().setMethod(Bundle.HTTPVerb.GET).setUrl(id.getValue());
responseBundle = ourClient.transaction().withBundle(bundle).execute();
patient = (Patient) responseBundle.getEntry().get(0).getResource();
assertEquals("Tester", patient.getNameFirstRep().getFamily());
// Search
bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
bundle.addEntry().getRequest().setMethod(Bundle.HTTPVerb.GET).setUrl("Patient?");
responseBundle = ourClient.transaction().withBundle(bundle).execute();
responseBundle = (Bundle) responseBundle.getEntry().get(0).getResource();
patient = (Patient) responseBundle.getEntry().get(0).getResource();
assertEquals("Tester", patient.getNameFirstRep().getFamily());
} finally {
ourClient.unregisterInterceptor(interceptor);
}
}
/**
* See #751
*/

View File

@ -34,9 +34,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.*;
import java.util.zip.GZIPInputStream;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -145,4 +143,18 @@ public class ServletRequestDetails extends RequestDetails {
this.myServletResponse = myServletResponse;
}
public Map<String,List<String>> getHeaders() {
Map<String, List<String>> retVal = new HashMap<>();
Enumeration<String> names = myServletRequest.getHeaderNames();
while (names.hasMoreElements()) {
String nextName = names.nextElement();
ArrayList<String> headerValues = new ArrayList<>();
retVal.put(nextName, headerValues);
Enumeration<String> valuesEnum = myServletRequest.getHeaders(nextName);
while (valuesEnum.hasMoreElements()) {
headerValues.add(valuesEnum.nextElement());
}
}
return Collections.unmodifiableMap(retVal);
}
}

View File

@ -277,6 +277,12 @@
part of the chain was lost when the chain was described in the server
CapabilityStatement. This has been corrected.
</action>
<action type="fix">
In the JPA server, search/read operations being performed within a transaction bundle
did not pass the client request HTTP headers to the sub-request. This meant that
AuthorizationInterceptor could not authorize these requests if it was depending on
headers being present.
</action>
</release>
<release version="3.6.0" date="2018-11-12" description="Food">
<action type="add">