Skip to content

Commit

Permalink
#449: W3C trace context ID in audit trail
Browse files Browse the repository at this point in the history
  • Loading branch information
unixoid committed Jul 9, 2024
1 parent 8d684f8 commit 9e535c0
Show file tree
Hide file tree
Showing 14 changed files with 76 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,15 @@ public boolean isEmpty() {
*/
@Getter
@Setter
String sourceUserName;
private String sourceUserName;

/**
* Contents of the HTTP header "traceparent:" as defined in the
* <a href="https://www.w3.org/TR/trace-context/">W3C Trace Context</a> specification.
*/
@Getter
@Setter
private String w3cTraceContextId;

/**
* @param serverSide specifies whether this audit dataset will be used on the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public DicomInstancesAccessedAuditBuilder(final AuditContext auditContext,
final EventActionCode eventActionCode,
final EventType eventType,
final PurposeOfUse... purposesOfUse) {
super(auditContext, new DicomInstancesAccessedBuilder(
super(auditContext, auditDataset, new DicomInstancesAccessedBuilder(
eventOutcomeIndicator,
eventOutcomeDescription,
eventActionCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public DicomInstancesTransferredAuditBuilder(final AuditContext auditContext,
final EventActionCode eventActionCode,
final EventType eventType,
final PurposeOfUse... purposesOfUse) {
super(auditContext, new DicomInstancesTransferredBuilder(
super(auditContext, auditDataset, new DicomInstancesTransferredBuilder(
eventOutcomeIndicator,
eventOutcomeDescription,
eventActionCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ public abstract class IHEAuditMessageBuilder<T extends IHEAuditMessageBuilder<T,

private final AuditContext auditContext;

public IHEAuditMessageBuilder(AuditContext auditContext, D delegate) {
public IHEAuditMessageBuilder(AuditContext auditContext, AuditDataset auditDataset, D delegate) {
super(delegate);
this.auditContext = requireNonNull(auditContext, "auditContext must be not null");
delegate.setAuditSource(auditContext);
if (auditDataset.getW3cTraceContextId() != null) {
addSwissW3CTraceContextIdParticipantObject(auditDataset.getW3cTraceContextId());
}
}

public AuditContext getAuditContext() {
Expand Down Expand Up @@ -214,4 +217,21 @@ public T addSecurityResourceParticipantObjects(ParticipantObjectIdType participa
return self();
}

/**
* Adds a Participant Object representing a W3C Trace Context ID (specific for the Swiss EPR).
*/
public T addSwissW3CTraceContextIdParticipantObject(String traceContextId) {
delegate.addParticipantObjectIdentification(
ParticipantObjectIdType.of("traceparent", "e-health-suisse", "traceparent"),
null,
null,
null,
requireNonNull(traceContextId, "trace context ID must not be null"),
ParticipantObjectTypeCode.Other,
ParticipantObjectTypeCodeRole.ProcessingElement,
null,
null);
return self();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public PHIExportBuilder(AuditContext auditContext,
EventActionCode eventActionCode,
EventType eventType,
PurposeOfUse... purposesOfUse) {
super(auditContext, new DataExportBuilder(
super(auditContext, auditDataset, new DataExportBuilder(
eventOutcomeIndicator,
eventOutcomeDescription,
eventActionCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public PHIImportBuilder(AuditContext auditContext,
EventActionCode eventActionCode,
EventType eventType,
PurposeOfUse... purposesOfUse) {
super(auditContext, new DataImportBuilder(
super(auditContext, auditDataset, new DataImportBuilder(
eventOutcomeIndicator,
eventOutcomeDescription,
eventActionCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public PatientRecordEventBuilder(AuditContext auditContext,
EventActionCode action,
EventType eventType,
PurposeOfUse... purposesOfUse) {
super(auditContext, new PatientRecordBuilder(
super(auditContext, auditDataset, new PatientRecordBuilder(
eventOutcomeIndicator,
eventOutcomeDescription,
action,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public QueryInformationBuilder(AuditContext auditContext,
AuditDataset auditDataset,
EventType eventType,
PurposeOfUse... purposesOfUse) {
super(auditContext, new QueryBuilder(
super(auditContext, auditDataset, new QueryBuilder(
auditDataset.getEventOutcomeIndicator(),
auditDataset.getEventOutcomeDescription(),
eventType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class GenericFhirAuditMessageBuilder extends
IHEAuditMessageBuilder<GenericFhirAuditMessageBuilder, CustomAuditMessageBuilder> {

public GenericFhirAuditMessageBuilder(AuditContext auditContext, GenericFhirAuditDataset auditDataset) {
super(auditContext, new CustomAuditMessageBuilder(
super(auditContext, auditDataset, new CustomAuditMessageBuilder(
auditDataset.getEventOutcomeIndicator(),
auditDataset.getEventOutcomeDescription(),
eventActionCode(auditDataset.getOperation()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class IHEAuditLogUsedBuilder extends
IHEAuditMessageBuilder<IHEAuditLogUsedBuilder, AuditLogUsedBuilder> {

public IHEAuditLogUsedBuilder(AuditContext auditContext, FhirAuditEventQueryAuditDataset auditDataset) {
super(auditContext, new AuditLogUsedBuilder(
super(auditContext, auditDataset, new AuditLogUsedBuilder(
auditDataset.getEventOutcomeIndicator(),
auditDataset.getEventOutcomeDescription()
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class IHEPatientRecordChangeLinkBuilder<T extends PatientRecordEventBuilder<T>>
private static final String URN_IHE_ITI_XPID_2017_PATIENT_IDENTIFIER_TYPE = "urn:ihe:iti:xpid:2017:patientIdentifierType";

IHEPatientRecordChangeLinkBuilder(AuditContext auditContext, AuditDataset auditDataset) {
super(auditContext, new PatientRecordBuilder(auditDataset.getEventOutcomeIndicator(), EventActionCode.Update, MllpEventTypeCode.XadPidLinkChange));
super(auditContext, auditDataset, new PatientRecordBuilder(auditDataset.getEventOutcomeIndicator(), EventActionCode.Update, MllpEventTypeCode.XadPidLinkChange));

// First the source, then the destination
if (auditDataset.isServerSide()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.apache.cxf.binding.soap.Soap11
import org.apache.cxf.binding.soap.Soap12
import org.apache.cxf.binding.soap.SoapMessage
import org.apache.cxf.headers.Header
import org.apache.cxf.message.Message
import org.apache.cxf.staxutils.StaxUtils
import org.openehealth.ipf.commons.audit.types.ActiveParticipantRoleId
import org.openehealth.ipf.commons.audit.types.PurposeOfUse
Expand Down Expand Up @@ -63,6 +64,11 @@ class BasicXuaProcessor implements XuaProcessor {

@Override
void enrichAuditDatasetFromXuaToken(SoapMessage message, Header.Direction headerDirection, WsAuditDataset auditDataset) {
extractXuaTokenElements(message, headerDirection, auditDataset)
extractW3cTraceContextId(message, auditDataset)
}

private static void extractXuaTokenElements(SoapMessage message, Header.Direction headerDirection, WsAuditDataset auditDataset) {
Element assertion = null

// check whether someone has already parsed the SAML2 assertion
Expand Down Expand Up @@ -97,7 +103,6 @@ class BasicXuaProcessor implements XuaProcessor {
auditDataset.humanUsers.addAll([iheUser, mainEpdUser, additionalEpdUser].findAll { !it.isEmpty() })
}


private static Element extractAssertionFromCxfMessage(SoapMessage message, Header.Direction headerDirection) {
Header header = message.getHeader(new QName(WSSE_NS, 'Security'))
return (header && (headerDirection == header.getDirection()) && (header.getObject() instanceof Element)) ?
Expand Down Expand Up @@ -169,4 +174,17 @@ class BasicXuaProcessor implements XuaProcessor {
}
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 (headerName.toLowerCase(Locale.ROOT) == 'traceparent') {
auditDataset.w3cTraceContextId = httpHeaders[headerName][0]
break
}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
import org.openehealth.ipf.commons.ihe.xacml20.model.PpqConstants;
import org.openehealth.ipf.commons.ihe.xacml20.stub.UnknownPolicySetIdFaultMessage;
import org.openehealth.ipf.commons.ihe.xacml20.stub.ehealthswiss.EprPolicyRepositoryResponse;
import org.openehealth.ipf.platform.camel.ihe.ws.AbstractWsEndpoint;
import org.openehealth.ipf.platform.camel.ihe.ws.StandardTestContainer;

import javax.xml.bind.JAXBElement;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;

Expand Down Expand Up @@ -92,16 +95,16 @@ public void testAddPolicyPPQ1Success() throws Exception {
}

private void testAddPolicy(String suffix, String statusCode, EventOutcomeIndicator outcomeIndicator) throws Exception {
var traceParentId = UUID.randomUUID().toString();
var response = (EprPolicyRepositoryResponse) send(getUri(suffix), loadFile("add-request-ppq.xml"),
EprPolicyRepositoryResponse.class);
EprPolicyRepositoryResponse.class,
Map.of(AbstractWsEndpoint.OUTGOING_HTTP_HEADERS, Map.of("TraceParent", traceParentId)));
assertEquals(statusCode, response.getStatus());

List messages = getAuditSender().getMessages();
List<AuditMessage> messages = getAuditSender().getMessages();
assertEquals(2, messages.size());

for (var object : messages) {
var message = (AuditMessage) object;

for (var message : messages) {
var event = message.getEventIdentification();
assertEquals(EventActionCode.Create, event.getEventActionCode());
assertEquals(outcomeIndicator, event.getEventOutcomeIndicator());
Expand All @@ -117,14 +120,19 @@ private void testAddPolicy(String suffix, String statusCode, EventOutcomeIndicat
assertEquals(1, message.getActiveParticipants().get(1).getRoleIDCodes().size());
checkCodeValueType(message.getActiveParticipants().get(1).getRoleIDCodes().get(0), new String[]{"110152", "DCM", "Destination Role ID"});

assertEquals(2, message.getParticipantObjectIdentifications().size());
assertEquals(3, message.getParticipantObjectIdentifications().size());

var participant = message.getParticipantObjectIdentifications().get(0);
assertEquals(ParticipantObjectTypeCode.Other, participant.getParticipantObjectTypeCode());
assertEquals(ParticipantObjectTypeCodeRole.ProcessingElement, participant.getParticipantObjectTypeCodeRole());
assertEquals(traceParentId, participant.getParticipantObjectID());

participant = message.getParticipantObjectIdentifications().get(1);
assertEquals(ParticipantObjectTypeCode.System, participant.getParticipantObjectTypeCode());
assertEquals(ParticipantObjectTypeCodeRole.SecurityResource, participant.getParticipantObjectTypeCodeRole());
assertEquals("urn:uuid:58bbfa76-4d65-4fa1-b0af-c862b52a20d4", participant.getParticipantObjectID());

participant = message.getParticipantObjectIdentifications().get(1);
participant = message.getParticipantObjectIdentifications().get(2);
assertEquals(ParticipantObjectTypeCode.Person, participant.getParticipantObjectTypeCode());
assertEquals(ParticipantObjectTypeCodeRole.Patient, participant.getParticipantObjectTypeCodeRole());
assertEquals("761337611194602836^^^&2.16.756.5.30.1.127.3.10.3&ISO", participant.getParticipantObjectID());
Expand Down
3 changes: 3 additions & 0 deletions src/site/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
<action issue="451" dev="unixoid" type="fix">
Improved translation between DSMLv2 and JSON
</action>
<action issue="449" dev="unixoid" type="add">
Add W3C Trace Context ID to Audit Trail records in the Swiss EPR context
</action>
<action issue="446" dev="Ivan1pl" type="fix">
Properly handle query payload when translating ATNA audit records to FHIR
</action>
Expand Down

0 comments on commit 9e535c0

Please sign in to comment.