1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.openehealth.ipf.platform.camel.ihe.mllp.core.intercept;
18
19 import ca.uhn.hl7v2.model.Message;
20 import ca.uhn.hl7v2.util.Terser;
21 import org.apache.camel.Exchange;
22 import org.apache.camel.component.mina2.Mina2Constants;
23 import org.apache.commons.lang3.ArrayUtils;
24 import org.apache.commons.lang3.StringUtils;
25 import org.apache.mina.core.session.IoSession;
26 import org.apache.mina.filter.ssl.SslFilter;
27 import org.openehealth.ipf.commons.audit.AuditContext;
28 import org.openehealth.ipf.commons.audit.codes.EventOutcomeIndicator;
29 import org.openehealth.ipf.commons.ihe.hl7v2.audit.AuditUtils;
30 import org.openehealth.ipf.commons.ihe.hl7v2.audit.MllpAuditDataset;
31 import org.openehealth.ipf.modules.hl7.message.MessageUtils;
32 import org.openehealth.ipf.platform.camel.ihe.atna.interceptor.AuditInterceptor;
33 import org.openehealth.ipf.platform.camel.ihe.core.InterceptorSupport;
34 import org.openehealth.ipf.platform.camel.ihe.mllp.core.MllpTransactionEndpoint;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import javax.naming.ldap.LdapName;
39 import javax.naming.ldap.Rdn;
40 import javax.net.ssl.SSLSession;
41 import java.security.Principal;
42
43 import static java.util.Objects.requireNonNull;
44 import static org.openehealth.ipf.platform.camel.core.util.Exchanges.resultMessage;
45
46
47
48
49
50
51 public abstract class MllpAuditInterceptorSupport<AuditDatasetType extends MllpAuditDataset> extends InterceptorSupport<MllpTransactionEndpoint<AuditDatasetType>>
52 implements AuditInterceptor<AuditDatasetType, MllpTransactionEndpoint<AuditDatasetType>> {
53
54 private static final Logger LOG = LoggerFactory.getLogger(MllpAuditInterceptorSupport.class);
55 private final AuditContext auditContext;
56
57 public MllpAuditInterceptorSupport(AuditContext auditContext) {
58 this.auditContext = requireNonNull(auditContext);
59 }
60
61 @Override
62 public void process(Exchange exchange) throws Exception {
63 Message msg = exchange.getIn().getBody(Message.class);
64
65
66 if (!isAuditable(msg)) {
67 getWrappedProcessor().process(exchange);
68 return;
69 }
70
71 AuditDatasetType auditDataset = createAndEnrichAuditDatasetFromRequest(exchange, msg);
72 determineParticipantsAddresses(exchange, auditDataset);
73 extractSslClientUser(exchange, auditDataset);
74
75 boolean failed = false;
76 try {
77 getWrappedProcessor().process(exchange);
78 Message result = resultMessage(exchange).getBody(Message.class);
79 enrichAuditDatasetFromResponse(auditDataset, result);
80 failed = !AuditUtils.isPositiveAck(result);
81 } catch (Exception e) {
82 failed = true;
83 if (auditDataset != null) {
84 auditDataset.setEventOutcomeDescription(e.getMessage());
85 }
86 throw e;
87 } finally {
88 if (auditDataset != null) {
89 auditDataset.setEventOutcomeIndicator(failed ?
90 EventOutcomeIndicator.MajorFailure :
91 EventOutcomeIndicator.Success);
92 getAuditStrategy().doAudit(auditContext, auditDataset);
93 } else {
94 LOG.warn("Audit dataset is not initialized, no auditing happens");
95 }
96 }
97 }
98
99 private void extractSslClientUser(Exchange exchange, AuditDatasetType auditDataset) {
100 IoSession ioSession = exchange.getIn().getHeader(Mina2Constants.MINA_IOSESSION, IoSession.class);
101 if (ioSession != null) {
102 SSLSession sslSession = (SSLSession) ioSession.getAttribute(SslFilter.SSL_SESSION);
103 if (sslSession != null) {
104 try {
105 Principal principal = sslSession.getPeerPrincipal();
106 if (principal != null) {
107 String dn = principal.getName();
108 LdapName ldapDN = new LdapName(dn);
109 for (Rdn rdn : ldapDN.getRdns()) {
110 if (rdn.getType().equalsIgnoreCase("CN")) {
111 auditDataset.setSourceUserName((String) rdn.getValue());
112 break;
113 }
114 }
115 }
116 } catch (Exception e) {
117 LOG.info("Could not extract CN from client certificate", e);
118 }
119 }
120 }
121 }
122
123
124
125
126
127
128
129
130 private AuditDatasetType createAndEnrichAuditDatasetFromRequest(Exchange exchange, Message msg) {
131 try {
132 AuditDatasetType auditDataset = getAuditStrategy().createAuditDataset();
133 AuditUtils.enrichGenericAuditDatasetFromRequest(auditDataset, msg);
134 return getAuditStrategy().enrichAuditDatasetFromRequest(auditDataset, msg, exchange.getIn().getHeaders());
135 } catch (Exception e) {
136 LOG.error("Exception when enriching audit dataset from request", e);
137 return null;
138 }
139 }
140
141
142
143
144
145 private void enrichAuditDatasetFromResponse(AuditDatasetType auditDataset, Message msg) {
146 try {
147 getAuditStrategy().enrichAuditDatasetFromResponse(auditDataset, msg, auditContext);
148 } catch (Exception e) {
149 LOG.error("Exception when enriching audit dataset from response", e);
150 }
151 }
152
153
154
155
156
157 private boolean isAuditable(Message message) {
158 try {
159
160 Terser terser = new Terser(message);
161 return (!ArrayUtils.contains(message.getNames(), "DSC") ||
162 !StringUtils.isNotEmpty(terser.get("DSC-1"))) &&
163 getEndpoint().getHl7v2TransactionConfiguration().isAuditable(MessageUtils.eventType(message));
164 } catch (Exception e) {
165 LOG.error("Exception when determining message auditability, no audit will be performed", e);
166 return false;
167 }
168 }
169 }