Skip to content

Commit

Permalink
#461: move FhirContextHolder to ipf-commons-ihe-fhir-core and add gen…
Browse files Browse the repository at this point in the history
…eric context propagation from micrometer library

Add instanceof pattern variables at various places
  • Loading branch information
ohr committed Dec 16, 2024
1 parent 9fba5d8 commit 24e1b7a
Show file tree
Hide file tree
Showing 69 changed files with 451 additions and 345 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.openehealth.ipf.boot.atna;

import org.openehealth.ipf.commons.audit.AuditContext;

/**
* Customizer that can be used to manipulate auto-configured {@link AuditContext} beans
*
* @author Christian Ohr
*/
public interface AuditContextCustomizer {

void customizeAuditContext(AuditContext auditContext);

AuditContextCustomizer NOOP = auditContext -> {
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class IpfAtnaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public AuditContext auditContext(IpfAtnaConfigurationProperties config,
AuditContextCustomizer auditContextCustomizer,
AuditTransmissionProtocol auditTransmissionProtocol,
AuditMessageQueue auditMessageQueue,
TlsParameters tlsParameters,
Expand All @@ -51,29 +52,38 @@ public AuditContext auditContext(IpfAtnaConfigurationProperties config,
WsAuditDatasetEnricher wsAuditDatasetEnricher,
FhirAuditDatasetEnricher fhirAuditDatasetEnricher,
@Value("${spring.application.name}") String appName) {
DefaultAuditContext auditContext;
if (config.getBalp() != null) {
return balpConfiguration(defaultContextConfiguration(new DefaultBalpAuditContext(), config,
auditTransmissionProtocol, auditMessageQueue, tlsParameters, auditMetadataProvider,
auditExceptionHandler, auditMessagePostProcessor, wsAuditDatasetEnricher,
fhirAuditDatasetEnricher, appName), config);
auditContext = new DefaultBalpAuditContext();
configureBalpAuditContext((DefaultBalpAuditContext) auditContext, config);
} else {
return defaultContextConfiguration(new DefaultAuditContext(), config, auditTransmissionProtocol,
auditMessageQueue, tlsParameters, auditMetadataProvider, auditExceptionHandler,
auditMessagePostProcessor, wsAuditDatasetEnricher, fhirAuditDatasetEnricher, appName);
auditContext = new DefaultAuditContext();
}
configureDefaultAuditContext(auditContext, config, auditTransmissionProtocol,
auditMessageQueue, tlsParameters, auditMetadataProvider, auditExceptionHandler,
auditMessagePostProcessor, wsAuditDatasetEnricher, fhirAuditDatasetEnricher, appName);
auditContextCustomizer.customizeAuditContext(auditContext);
return auditContext;
}

@Bean
@ConditionalOnMissingBean(AuditContextCustomizer.class)
public AuditContextCustomizer auditContextCustomizer() {
return AuditContextCustomizer.NOOP;
}

private <T extends DefaultAuditContext> T defaultContextConfiguration(T auditContext,
IpfAtnaConfigurationProperties config,
AuditTransmissionProtocol auditTransmissionProtocol,
AuditMessageQueue auditMessageQueue,
TlsParameters tlsParameters,
AuditMetadataProvider auditMetadataProvider,
AuditExceptionHandler auditExceptionHandler,
AuditMessagePostProcessor auditMessagePostProcessor,
WsAuditDatasetEnricher wsAuditDatasetEnricher,
FhirAuditDatasetEnricher fhirAuditDatasetEnricher,
@Value("${spring.application.name}") String appName) {

private void configureDefaultAuditContext(DefaultAuditContext auditContext,
IpfAtnaConfigurationProperties config,
AuditTransmissionProtocol auditTransmissionProtocol,
AuditMessageQueue auditMessageQueue,
TlsParameters tlsParameters,
AuditMetadataProvider auditMetadataProvider,
AuditExceptionHandler auditExceptionHandler,
AuditMessagePostProcessor auditMessagePostProcessor,
WsAuditDatasetEnricher wsAuditDatasetEnricher,
FhirAuditDatasetEnricher fhirAuditDatasetEnricher,
@Value("${spring.application.name}") String appName) {

auditContext.setAuditEnabled(config.isAuditEnabled());

Expand All @@ -100,65 +110,62 @@ private <T extends DefaultAuditContext> T defaultContextConfiguration(T auditCon
if (fhirAuditDatasetEnricher != FhirAuditDatasetEnricher.NONE) {
auditContext.setFhirAuditDatasetEnricher(fhirAuditDatasetEnricher);
}

return auditContext;
}

private DefaultBalpAuditContext balpConfiguration(DefaultBalpAuditContext auditContext, IpfAtnaConfigurationProperties config) {
if (config.getBalp() != null) {
auditContext.setAuditRepositoryContextPath(config.getBalp().getAuditRepositoryContextPath());
private void configureBalpAuditContext(DefaultBalpAuditContext auditContext, IpfAtnaConfigurationProperties config) {
auditContext.setAuditRepositoryContextPath(config.getBalp().getAuditRepositoryContextPath());

if (isNotBlank(config.getBalp().getAuditEventSerializationType())) {
auditContext.setSerializationStrategy(
config.getBalp().getAuditEventSerializationType().equalsIgnoreCase("json") ?
new BalpJsonSerializationStrategy() : new BalpXmlSerializationStrategy());
if (isNotBlank(config.getBalp().getAuditEventSerializationType())) {
auditContext.setSerializationStrategy(
config.getBalp().getAuditEventSerializationType().equalsIgnoreCase("json") ?
new BalpJsonSerializationStrategy() : new BalpXmlSerializationStrategy());
}
var oAuth = config.getBalp().getOauth();
var props = auditContext.getBalpJwtExtractorProperties();
if (oAuth != null) {
if (oAuth.getIdPath() != null) {
props.setIdPath(oAuth.getIdPath());
}
if (oAuth.getClientIdPath() != null) {
props.setClientIdPath(oAuth.getClientIdPath());
}
if (oAuth.getIssuerPath() != null) {
props.setIssuerPath(oAuth.getIssuerPath());
}
if (config.getBalp().getOauth() != null) {
if (config.getBalp().getOauth().getIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setIdPath(config.getBalp().getOauth().getIdPath());
}
if (config.getBalp().getOauth().getClientIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setClientIdPath(config.getBalp().getOauth().getClientIdPath());
}
if (config.getBalp().getOauth().getIssuerPath() != null) {
auditContext.getBalpJwtExtractorProperties().setIssuerPath(config.getBalp().getOauth().getIssuerPath());
}
if (config.getBalp().getOauth().getSubjectPath() != null) {
auditContext.getBalpJwtExtractorProperties().setSubjectPath(config.getBalp().getOauth().getSubjectPath());
}
if (config.getBalp().getOauth().getSubjectNamePath() != null) {
auditContext.getBalpJwtExtractorProperties().setSubjectNamePath(config.getBalp().getOauth().getSubjectNamePath());
}
if (config.getBalp().getOauth().getSubjectRolePath() != null) {
auditContext.getBalpJwtExtractorProperties().setSubjectRolePath(config.getBalp().getOauth().getSubjectRolePath());
}
if (config.getBalp().getOauth().getSubjectOrganizationIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setSubjectOrganizationIdPath(config.getBalp().getOauth().getSubjectOrganizationIdPath());
}
if (config.getBalp().getOauth().getPurposeOfUsePath() != null) {
auditContext.getBalpJwtExtractorProperties().setPurposeOfUsePath(config.getBalp().getOauth().getPurposeOfUsePath());
}
if (config.getBalp().getOauth().getHomeCommunityIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setHomeCommunityIdPath(config.getBalp().getOauth().getHomeCommunityIdPath());
}
if (config.getBalp().getOauth().getNationalProviderIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setNationalProviderIdPath(config.getBalp().getOauth().getNationalProviderIdPath());
}
if (config.getBalp().getOauth().getDocIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setDocIdPath(config.getBalp().getOauth().getDocIdPath());
}
if (config.getBalp().getOauth().getPatientIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setPatientIdPath(config.getBalp().getOauth().getPatientIdPath());
}
if (config.getBalp().getOauth().getPersonIdPath() != null) {
auditContext.getBalpJwtExtractorProperties().setPersonIdPath(config.getBalp().getOauth().getPersonIdPath());
}
if (config.getBalp().getOauth().getAcpPath() != null) {
auditContext.getBalpJwtExtractorProperties().setAcpPath(config.getBalp().getOauth().getAcpPath());
}
if (oAuth.getSubjectPath() != null) {
props.setSubjectPath(oAuth.getSubjectPath());
}
if (oAuth.getSubjectNamePath() != null) {
props.setSubjectNamePath(oAuth.getSubjectNamePath());
}
if (oAuth.getSubjectRolePath() != null) {
props.setSubjectRolePath(oAuth.getSubjectRolePath());
}
if (oAuth.getSubjectOrganizationIdPath() != null) {
props.setSubjectOrganizationIdPath(oAuth.getSubjectOrganizationIdPath());
}
if (oAuth.getPurposeOfUsePath() != null) {
props.setPurposeOfUsePath(oAuth.getPurposeOfUsePath());
}
if (oAuth.getHomeCommunityIdPath() != null) {
props.setHomeCommunityIdPath(oAuth.getHomeCommunityIdPath());
}
if (oAuth.getNationalProviderIdPath() != null) {
props.setNationalProviderIdPath(oAuth.getNationalProviderIdPath());
}
if (oAuth.getDocIdPath() != null) {
props.setDocIdPath(oAuth.getDocIdPath());
}
if (oAuth.getPatientIdPath() != null) {
props.setPatientIdPath(oAuth.getPatientIdPath());
}
if (oAuth.getPersonIdPath() != null) {
props.setPersonIdPath(oAuth.getPersonIdPath());
}
if (oAuth.getAcpPath() != null) {
props.setAcpPath(oAuth.getAcpPath());
}
}
return auditContext;
}

// The following beans configure aud strategies (formats, queues, exception handlers) and
Expand Down
9 changes: 4 additions & 5 deletions commons/audit/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>context-propagation</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
Expand All @@ -36,11 +40,6 @@
<artifactId>jakarta.jms-api</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<optional>true</optional>
</dependency>

<!-- dependencies for test -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.openehealth.ipf.commons.audit;

import io.micrometer.context.ContextRegistry;
import org.openehealth.ipf.commons.audit.handler.AuditExceptionHandler;
import org.openehealth.ipf.commons.audit.marshal.SerializationStrategy;
import org.openehealth.ipf.commons.audit.marshal.dicom.Current;
Expand All @@ -25,6 +26,7 @@
import org.openehealth.ipf.commons.audit.types.AuditSource;

import java.net.InetAddress;
import java.util.List;
import java.util.stream.Stream;

/**
Expand Down Expand Up @@ -169,6 +171,14 @@ default String getAuditValueIfMissing() {
*/
boolean isIncludeParticipantsFromResponse();

/**
* Returns a registry of ThreadLocalAccessors that are used to propagate context
* when asynchronously queuing audit records.
*
* @return context registry
*/
ContextRegistry getContextRegistry();

static AuditContext noAudit() {
return DefaultAuditContext.NO_AUDIT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ public SSLContext getSSLContext(boolean serverSide) {

if (keyManagers != null && certAlias != null) {
for (var i = 0; i < keyManagers.length; i++) {
if (keyManagers[i] instanceof X509KeyManager) {
keyManagers[i] = new AliasX509ExtendedKeyManager((X509KeyManager) keyManagers[i], certAlias);
if (keyManagers[i] instanceof X509KeyManager x509KeyManager) {
keyManagers[i] = new AliasX509ExtendedKeyManager(x509KeyManager, certAlias);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.openehealth.ipf.commons.audit;

import io.micrometer.context.ContextRegistry;
import io.micrometer.context.integration.Slf4jThreadLocalAccessor;
import lombok.Getter;
import lombok.Setter;
import org.openehealth.ipf.commons.audit.codes.AuditSourceType;
Expand All @@ -34,6 +36,12 @@
import java.net.UnknownHostException;

/**
* Default implementation of an AuditContext.
* <p>
* The {@link ContextRegistry} returned by {@link #getContextRegistry()} will always contain an instance
* of {@link Slf4jThreadLocalAccessor} in order to propagate MDC context to an
* potentially asynchronous auditor.
*
* @author Christian Ohr
* @since 3.5
*/
Expand Down Expand Up @@ -107,6 +115,10 @@ public class DefaultAuditContext implements AuditContext {
@Setter
private FhirAuditDatasetEnricher fhirAuditDatasetEnricher;

@Getter
private final ContextRegistry contextRegistry = new ContextRegistry()
.registerThreadLocalAccessor(new Slf4jThreadLocalAccessor());

public String getAuditRepositoryTransport() {
return auditTransmissionProtocol.getTransportName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import lombok.Setter;
import org.openehealth.ipf.commons.audit.AuditContext;
import org.openehealth.ipf.commons.audit.FhirContextHolder;
import org.openehealth.ipf.commons.audit.model.AuditMessage;

import java.util.stream.Stream;
Expand All @@ -30,8 +29,8 @@
* and send it to an ATNA repository using the configured {@link org.openehealth.ipf.commons.audit.protocol.AuditTransmissionProtocol},
* </p>
* <p>
* There may be other use cases such as forwarding AuditMessages as object into a Camel Route or in-memory storage
* or convert them into FHIR AuditEvents. In this case, implement your own {@link AuditMessageQueue}.
* There may be other use cases such as forwarding AuditMessages as object into a Camel Route or in-memory storage.
* In this case, implement your own {@link AuditMessageQueue}.
* </p>
*
* @author Christian Ohr
Expand All @@ -40,7 +39,6 @@
public abstract class AbstractAuditMessageQueue implements AuditMessageQueue {



@Setter
private boolean pretty = false;

Expand All @@ -51,7 +49,6 @@ public void audit(AuditContext auditContext, AuditMessage... auditMessages) {
.map(msg -> auditContext.getSerializationStrategy().marshal(msg, pretty))
.forEach(msg -> handle(auditContext, msg));
}
FhirContextHolder.remove();
}

protected abstract void handle(AuditContext auditContext, String auditRecord);
Expand Down
Loading

0 comments on commit 24e1b7a

Please sign in to comment.