Skip to content

Commit

Permalink
Refactored ATNA dataset enrichment
Browse files Browse the repository at this point in the history
  • Loading branch information
unixoid committed Jul 28, 2024
1 parent 84c8591 commit 8bcc76d
Show file tree
Hide file tree
Showing 20 changed files with 210 additions and 153 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,7 @@

package org.openehealth.ipf.boot.atna;

import org.openehealth.ipf.commons.audit.AuditContext;
import org.openehealth.ipf.commons.audit.AuditMessagePostProcessor;
import org.openehealth.ipf.commons.audit.AuditMetadataProvider;
import org.openehealth.ipf.commons.audit.DefaultAuditContext;
import org.openehealth.ipf.commons.audit.DefaultAuditMetadataProvider;
import org.openehealth.ipf.commons.audit.DefaultBalpAuditContext;
import org.openehealth.ipf.commons.audit.TlsParameters;
import org.openehealth.ipf.commons.audit.*;
import org.openehealth.ipf.commons.audit.handler.AuditExceptionHandler;
import org.openehealth.ipf.commons.audit.handler.LoggingAuditExceptionHandler;
import org.openehealth.ipf.commons.audit.protocol.AuditTransmissionChannel;
Expand Down Expand Up @@ -57,15 +51,16 @@ public AuditContext auditContext(IpfAtnaConfigurationProperties config,
AuditMetadataProvider auditMetadataProvider,
AuditExceptionHandler auditExceptionHandler,
AuditMessagePostProcessor auditMessagePostProcessor,
WsAuditDatasetEnricher wsAuditDatasetEnricher,
@Value("${spring.application.name}") String appName) {
if (config.getBalp() != null) {
return balpConfiguration(defaultContextConfiguration(new DefaultBalpAuditContext(), config,
auditTransmissionProtocol, auditMessageQueue, tlsParameters, auditMetadataProvider,
auditExceptionHandler, auditMessagePostProcessor, appName), config);
auditExceptionHandler, auditMessagePostProcessor, wsAuditDatasetEnricher, appName), config);
} else {
return defaultContextConfiguration(new DefaultAuditContext(), config, auditTransmissionProtocol,
auditMessageQueue, tlsParameters, auditMetadataProvider, auditExceptionHandler,
auditMessagePostProcessor, appName);
auditMessagePostProcessor, wsAuditDatasetEnricher, appName);
}
}

Expand All @@ -77,6 +72,7 @@ private <T extends DefaultAuditContext> T defaultContextConfiguration(T auditCon
AuditMetadataProvider auditMetadataProvider,
AuditExceptionHandler auditExceptionHandler,
AuditMessagePostProcessor auditMessagePostProcessor,
WsAuditDatasetEnricher wsAuditDatasetEnricher,
@Value("${spring.application.name}") String appName) {

auditContext.setAuditEnabled(config.isAuditEnabled());
Expand All @@ -97,6 +93,11 @@ private <T extends DefaultAuditContext> T defaultContextConfiguration(T auditCon
auditContext.setAuditMessageQueue(auditMessageQueue);
auditContext.setAuditExceptionHandler(auditExceptionHandler);
auditContext.setAuditMessagePostProcessor(auditMessagePostProcessor);

if (wsAuditDatasetEnricher != WsAuditDatasetEnricher.NONE) {
auditContext.setWsAuditDatasetEnricher(wsAuditDatasetEnricher);
}

return auditContext;
}

Expand Down Expand Up @@ -213,6 +214,15 @@ public TlsParameters tlsParameters() {
return TlsParameters.getDefault();
}

@Bean
@ConditionalOnMissingBean
public WsAuditDatasetEnricher wsAuditDatasetEnricher(IpfAtnaConfigurationProperties config) throws Exception {
if (config.getWsAuditDatasetEnricherClass() != null) {
return config.getWsAuditDatasetEnricherClass().getConstructor().newInstance();
}
return WsAuditDatasetEnricher.NONE;
}

// Some audit event listeners

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import lombok.Getter;
import lombok.Setter;
import org.openehealth.ipf.commons.audit.AuditMessagePostProcessor;
import org.openehealth.ipf.commons.audit.WsAuditDatasetEnricher;
import org.openehealth.ipf.commons.audit.codes.AuditSourceType;
import org.openehealth.ipf.commons.audit.handler.AuditExceptionHandler;
import org.openehealth.ipf.commons.audit.handler.LoggingAuditExceptionHandler;
Expand Down Expand Up @@ -101,6 +102,12 @@ public class IpfAtnaConfigurationProperties {
@Getter @Setter
private String auditValueIfMissing = "UNKNOWN";

/**
* Class of the optional audit dataset enricher for Web Service based transactions.
*/
@Getter @Setter
private Class<? extends WsAuditDatasetEnricher> wsAuditDatasetEnricherClass;

@Getter @Setter
private Balp balp;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ public interface AuditContext {
*/
TlsParameters getTlsParameters();

/**
* @return Audit dataset enricher for Web Service based transactions.
*/
<T extends WsAuditDatasetEnricher> T getWsAuditDatasetEnricher();

/**
* @return a post-processor for audit messages (defaults to a NO-OP implementation
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ public class DefaultAuditContext implements AuditContext {
@Setter
private String auditValueIfMissing = "UNKNOWN";

@Setter
private WsAuditDatasetEnricher wsAuditDatasetEnricher;

public String getAuditRepositoryTransport() {
return auditTransmissionProtocol.getTransportName();
}
Expand Down Expand Up @@ -136,4 +139,11 @@ public InetAddress getAuditRepositoryAddress() {
throw new RuntimeException(e);
}
}

@SuppressWarnings("unchecked")
@Override
public <T extends WsAuditDatasetEnricher> T getWsAuditDatasetEnricher() {
return (T) wsAuditDatasetEnricher;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 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.commons.audit;

/**
* Marker interface for Web Service audit dataset enrichers.
*/
public interface WsAuditDatasetEnricher {

WsAuditDatasetEnricher NONE = new WsAuditDatasetEnricher() {};

}
1 change: 0 additions & 1 deletion commons/ihe/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
<module>hl7v3</module>
<module>hl7v3model</module>
<module>fhir</module>
<module>swissepr</module>
<module>hpd</module>
<module>xacml20</module>
</modules>
Expand Down
32 changes: 0 additions & 32 deletions commons/ihe/swissepr/pom.xml

This file was deleted.

This file was deleted.

4 changes: 4 additions & 0 deletions commons/ihe/ws/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
<groupId>jakarta.xml.ws</groupId>
<artifactId>jakarta.xml.ws-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-xml</artifactId>
</dependency>

<!-- Dependencies for tests -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2024 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.commons.ihe.ws.cxf.audit

import groovy.xml.slurpersupport.GPathResult
import org.apache.cxf.binding.soap.SoapMessage
import org.apache.cxf.headers.Header
import org.apache.cxf.message.Message
import org.openehealth.ipf.commons.audit.types.ActiveParticipantRoleId
import org.openehealth.ipf.commons.audit.types.PurposeOfUse
import org.openehealth.ipf.commons.ihe.core.atna.AuditDataset.HumanUser

/**
* WS audit dataset enricher which fulfills both IHE and Swiss EPR requirements.
*
* @author Dmytro Rud
*/
class SwissEprWsAuditDatasetEnricher extends XuaWsAuditDatasetEnricher {

static final String SWISS_USER_POU_OID = '2.16.756.5.30.1.127.3.10.5'
static final String SWISS_USER_ROLE_OID = '2.16.756.5.30.1.127.3.10.6'

@Override
void enrichAuditDataset(SoapMessage message, Header.Direction headerDirection, WsAuditDataset auditDataset) {
GPathResult xuaToken = extractXuaToken(message, headerDirection)
if (xuaToken != null) {
extractXuaTokenElements(xuaToken, auditDataset)
def iheUser = auditDataset.humanUsers[0]
conditionallyAddHumanUser(createMainEprUser(xuaToken, iheUser), auditDataset)
conditionallyAddHumanUser(createAdditionalEprUser(xuaToken, iheUser, auditDataset.purposesOfUse), auditDataset)
}

extractW3cTraceContextId(message, auditDataset)
}

private static HumanUser createMainEprUser(GPathResult xuaToken, HumanUser iheUser) {
def user = new HumanUser()
user.id = xuaToken.Subject.NameID[0].text()
user.name = xuaToken.AttributeStatement.Attribute.find { it.@Name == SUBJECT_NAME_ATTRIBUTE_NAME }.AttributeValue[0].text()
user.roles.addAll(iheUser.roles)
return user
}

private static HumanUser createAdditionalEprUser(GPathResult xuaToken, HumanUser iheUser, PurposeOfUse... purposesOfUse) {
def user = new HumanUser()
user.id = xuaToken.Subject.SubjectConfirmation.NameID[0].text()
user.name = xuaToken.Subject.SubjectConfirmation.SubjectConfirmationData.AttributeStatement.Attribute.find { it.@Name == SUBJECT_NAME_ATTRIBUTE_NAME }.AttributeValue[0].text()
switch (iheUser.roles.find { it.codeSystemName == SWISS_USER_ROLE_OID }?.code) {
case 'HCP':
if (purposesOfUse.find { (it.codeSystemName == SWISS_USER_POU_OID) && it.code.contains('AUTO') }) {
user.roles << ActiveParticipantRoleId.of('TCU', SWISS_USER_ROLE_OID, 'Technical User')
} else {
user.roles << ActiveParticipantRoleId.of('ASS', SWISS_USER_ROLE_OID, 'Assistant')
}
break
case 'PAT':
user.roles << ActiveParticipantRoleId.of('REP', SWISS_USER_ROLE_OID, 'Representative')
break
}
return user
}

private static void extractW3cTraceContextId(SoapMessage message, WsAuditDataset auditDataset) {
def httpHeaders = message.get(Message.PROTOCOL_HEADERS) as Map<String, List<String>>
if (httpHeaders != null) {
for (String headerName : httpHeaders.keySet()) {
if ('traceparent'.equalsIgnoreCase(headerName)) {
auditDataset.w3cTraceContextId = httpHeaders[headerName][0]
break
}
}
}
}

}
Loading

0 comments on commit 8bcc76d

Please sign in to comment.