Skip to content

Commit

Permalink
Added parallel support for $match and $idi-match operations. Added va…
Browse files Browse the repository at this point in the history
…lidation options for match operation for incoming Patient resource
  • Loading branch information
bstewartlg committed Nov 14, 2024
1 parent 7b3fb39 commit 07dd74d
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 79 deletions.
10 changes: 10 additions & 0 deletions src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings.IdStrategyEnum;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.packages.PackageInstallationSpec;
import ca.uhn.fhir.jpa.starter.operations.models.IdentityMatchValidationLevel;
import ca.uhn.fhir.rest.api.EncodingEnum;
import lombok.Getter;
import lombok.Setter;
Expand Down Expand Up @@ -970,4 +971,13 @@ public void setResource_dbhistory_enabled(Boolean resource_dbhistory_enabled) {
this.resource_dbhistory_enabled = resource_dbhistory_enabled;
}


// custom properties

@Getter @Setter
private String matchValidationHeader = "X-Match-Validation";

@Getter @Setter
private IdentityMatchValidationLevel matchValidationLevel = IdentityMatchValidationLevel.DEFAULT;

}
8 changes: 5 additions & 3 deletions src/main/java/ca/uhn/fhir/jpa/starter/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public ServletRegistrationBean<RestfulServer> hapiServletRegistration(RestfulSer
restfulServer.registerInterceptor(securityDiscoveryInterceptor);

IdentityMatchingAuthInterceptor authInterceptor = new IdentityMatchingAuthInterceptor(
securityConfig.getEnableAuthentication(), //securityConfig.getBypassHeader(),
securityConfig.getEnableAuthentication(), securityConfig.getBypassHeader(),
securityConfig.getIssuer(), securityConfig.getPublicKey(),
securityConfig.getIntrospectionUrl(), securityConfig.getClientId(), securityConfig.getClientSecret(),
securityConfig.getProtectedEndpoints(), securityConfig.getPublicEndpoints());
Expand All @@ -130,7 +130,8 @@ public ServletRegistrationBean<RestfulServer> hapiServletRegistration(RestfulSer
CorsInterceptor corsInterceptor = (CorsInterceptor) existingCorsInterceptor;

// Add custom header to the existing CORS configuration
// corsInterceptor.getConfig().addAllowedHeader(securityConfig.getBypassHeader());
corsInterceptor.getConfig().addAllowedHeader(securityConfig.getBypassHeader());
corsInterceptor.getConfig().addAllowedHeader(appProperties.getMatchValidationHeader());
}
else {
// Define your CORS configuration
Expand All @@ -140,7 +141,8 @@ public ServletRegistrationBean<RestfulServer> hapiServletRegistration(RestfulSer
config.addAllowedHeader("Accept");
config.addAllowedHeader("X-Requested-With");
config.addAllowedHeader("Content-Type");
// config.addAllowedHeader(securityConfig.getBypassHeader());
config.addAllowedHeader(securityConfig.getBypassHeader());
config.addAllowedHeader(appProperties.getMatchValidationHeader());

config.addAllowedOrigin("*");

Expand Down
325 changes: 253 additions & 72 deletions src/main/java/ca/uhn/fhir/jpa/starter/operations/IdentityMatching.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ca.uhn.fhir.jpa.starter.operations.models;

import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Patient;

import lombok.Getter;
import lombok.Setter;

public class IdentityMatchParams {

@Getter @Setter
Patient patient;

@Getter @Setter
BooleanType onlyCertainMatches = new BooleanType(false);

@Getter @Setter
IntegerType count;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ca.uhn.fhir.jpa.starter.operations.models;


/**
* The level of validation to perform on the Patient resource parameter when performing an identity match operation
*/
public enum IdentityMatchValidationLevel {

/**
* Requires that the Patient validates against an IDI-Patient profile specified in the meta.profile field.
* If no profile is provided, the Patient will be validated against the base IDI-Patient profile:
* http://hl7.org/fhir/us/identity-matching/StructureDefinition/IDI-Patient
*/
DEFAULT,

/**
* Validate the Patient resource against the most restrictive IDI-Patient profile specified in the meta.profile field.
* If an expected IDI-Patient profile is not found, the validation will fail.
*/
META_PROFILE,

/**
* Do not perform any validation of the Patient resource
*/
NONE


}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
@Interceptor
public class IdentityMatchingAuthInterceptor {
private boolean enableAuthentication = false;
private String bypassHeader;

private String issuer;

Expand All @@ -59,10 +60,10 @@ public class IdentityMatchingAuthInterceptor {
private List<String> publicEndpoints;

private final Logger _logger = LoggerFactory.getLogger(IdentityMatchingAuthInterceptor.class);
private final String allowPublicAccessHeader = "X-Allow-Public-Access";

public IdentityMatchingAuthInterceptor(boolean enableAuthentication, String issuer, String publicKey, String introspectUrl, String clientId, String clientSecret, List<String> protectedEndpoints, List<String> publicEndpoints) {
public IdentityMatchingAuthInterceptor(boolean enableAuthentication, String bypassHeader, String issuer, String publicKey, String introspectUrl, String clientId, String clientSecret, List<String> protectedEndpoints, List<String> publicEndpoints) {
this.enableAuthentication = enableAuthentication;
this.bypassHeader = bypassHeader;
this.issuer = issuer;
this.publicKey = publicKey;
this.introspectUrl = introspectUrl;
Expand All @@ -74,13 +75,18 @@ public IdentityMatchingAuthInterceptor(boolean enableAuthentication, String issu

@Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)
public boolean incomingRequestPostProcessed(RequestDetails details, HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {

if (!enableAuthentication) {
return true;
}

boolean authenticated = false;

//check for public access header, if not detected then proceed with authentication checks
// if public access header is present, circumvent authentication and allow public access to all endpoints
//
//*** THIS IS JUST FOR RI TESTING, THIS SHOULD NOT BE INCLUDED IN A PRODUCTION SYSTEM ***
String publicAccessHeader = request.getHeader(allowPublicAccessHeader);
String publicAccessHeader = request.getHeader(bypassHeader);
if(publicAccessHeader == null) {

// check if request path is an endpoint that needs validation
Expand Down Expand Up @@ -115,7 +121,7 @@ public boolean incomingRequestPostProcessed(RequestDetails details, HttpServletR
}
else { //public access header detected or a public access point was requested - allow request
authenticated = true;
_logger.info("The 'X-Allow-Public-Access' header was detected, ignoring security configuration.");
_logger.info("The '" + bypassHeader + "' header was detected, ignoring security configuration.");
}

if(!authenticated) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public class SecurityConfig {
@Getter @Setter
boolean fetchCert = true;

@Getter @Setter
String bypassHeader = "X-Allow-Public-Access";

public List<String> getProtectedEndpoints() {
if(this.protectedEndpoints.size() > 0) {
return List.of(this.protectedEndpoints.get(0).split("[;]"));
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ security:
# cert-file: classpath:client-cert.pfx
# cert-password: udap-test
fetch-cert: true
bypass-header: X-Allow-Public-Access
spring:
main:
allow-circular-references: true
Expand Down Expand Up @@ -94,6 +95,9 @@ spring:
# hibernate.search.backend.analysis.configurer: ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers$HapiElasticAnalysisConfigurer
hapi:
fhir:
match-validation-header: X-Match-Validation
match-validation-level: DEFAULT

### This flag when enabled to true, will avail evaluate measure operations from CR Module.
### Flag is false by default, can be passed as command line argument to override.
cr:
Expand Down

0 comments on commit 07dd74d

Please sign in to comment.