1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.openehealth.ipf.commons.ihe.hpd.iti59;
17
18 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils;
20 import org.openehealth.ipf.commons.audit.AuditContext;
21 import org.openehealth.ipf.commons.audit.codes.EventActionCode;
22 import org.openehealth.ipf.commons.audit.codes.EventOutcomeIndicator;
23 import org.openehealth.ipf.commons.audit.codes.ParticipantObjectTypeCode;
24 import org.openehealth.ipf.commons.audit.model.AuditMessage;
25 import org.openehealth.ipf.commons.ihe.core.atna.AuditStrategySupport;
26 import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.*;
27
28 import javax.naming.InvalidNameException;
29 import javax.naming.ldap.LdapName;
30 import javax.naming.ldap.Rdn;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.stream.Stream;
34
35 import static org.apache.commons.lang3.ClassUtils.getShortCanonicalName;
36 import static org.apache.commons.lang3.StringUtils.isEmpty;
37 import static org.apache.commons.lang3.StringUtils.trimToNull;
38
39
40
41
42
43
44 @Slf4j
45 abstract class Iti59AuditStrategy extends AuditStrategySupport<Iti59AuditDataset> {
46
47 private static final Map<String, ParticipantObjectTypeCode> PARTICIPANT_OBJECT_CODE_MAP;
48 static {
49 PARTICIPANT_OBJECT_CODE_MAP = new HashMap<>();
50 PARTICIPANT_OBJECT_CODE_MAP.put("HCProfessional".toLowerCase(), ParticipantObjectTypeCode.Person);
51 PARTICIPANT_OBJECT_CODE_MAP.put("HCRegulatedOrganization".toLowerCase(), ParticipantObjectTypeCode.Organization);
52 }
53
54 protected Iti59AuditStrategy(boolean serverSide) {
55 super(serverSide);
56 }
57
58 @Override
59 public Iti59AuditDataset createAuditDataset() {
60 return new Iti59AuditDataset(isServerSide());
61 }
62
63
64
65
66
67
68 private static void enrichAuditItem(Iti59AuditDataset.RequestItem item, String dn) {
69 try {
70 for (Rdn rdn : new LdapName(dn).getRdns()) {
71 String value = (String) rdn.getValue();
72 switch (rdn.getType().toLowerCase()) {
73 case "uid":
74 item.setUid(value);
75 break;
76 case "ou":
77 item.setParticipantObjectTypeCode(PARTICIPANT_OBJECT_CODE_MAP.get(value.toLowerCase()));
78 break;
79 }
80 }
81 } catch (InvalidNameException e) {
82 log.debug("Cannot parse DN", e);
83 }
84 }
85
86 @Override
87 public Iti59AuditDataset enrichAuditDatasetFromRequest(Iti59AuditDataset auditDataset, Object requestObject, Map<String, Object> parameters) {
88 BatchRequest batchRequest = (BatchRequest) requestObject;
89 if ((batchRequest == null) ||
90 (batchRequest.getBatchRequests() == null) ||
91 batchRequest.getBatchRequests().isEmpty()) {
92 log.debug("Empty batch request");
93 return auditDataset;
94 }
95
96 Iti59AuditDataset.RequestItem[] requestItems = new Iti59AuditDataset.RequestItem[batchRequest.getBatchRequests().size()];
97
98 for (int i = 0; i < batchRequest.getBatchRequests().size(); ++i) {
99 DsmlMessage dsmlMessage = batchRequest.getBatchRequests().get(i);
100
101 if (dsmlMessage instanceof AddRequest) {
102 AddRequest addRequest = (AddRequest) dsmlMessage;
103 requestItems[i] = new Iti59AuditDataset.RequestItem(trimToNull(addRequest.getRequestID()), EventActionCode.Create);
104 enrichAuditItem(requestItems[i], addRequest.getDn());
105
106 } else if (dsmlMessage instanceof ModifyRequest) {
107 ModifyRequest modifyRequest = (ModifyRequest) dsmlMessage;
108 requestItems[i] = new Iti59AuditDataset.RequestItem(trimToNull(modifyRequest.getRequestID()), EventActionCode.Update);
109 enrichAuditItem(requestItems[i], modifyRequest.getDn());
110
111 } else if (dsmlMessage instanceof ModifyDNRequest) {
112 ModifyDNRequest modifyDNRequest = (ModifyDNRequest) dsmlMessage;
113 requestItems[i] = new Iti59AuditDataset.RequestItem(trimToNull(modifyDNRequest.getRequestID()), EventActionCode.Execute);
114 enrichAuditItem(requestItems[i], modifyDNRequest.getDn());
115 try {
116 requestItems[i].setNewUid(new LdapName(modifyDNRequest.getNewrdn()).getRdns().stream()
117 .filter(rdn -> "uid".equalsIgnoreCase(rdn.getType()))
118 .map(rdn -> (String) rdn.getValue())
119 .findAny()
120 .orElse(null));
121 } catch (Exception e) {
122 log.debug("Cannot parse new Rdn", e);
123 }
124
125 } else if (dsmlMessage instanceof DelRequest) {
126 DelRequest delRequest = (DelRequest) dsmlMessage;
127 requestItems[i] = new Iti59AuditDataset.RequestItem(trimToNull(delRequest.getRequestID()), EventActionCode.Delete);
128 enrichAuditItem(requestItems[i], delRequest.getDn());
129
130 } else {
131 log.debug("Cannot handle ITI-59 request of type {}", getShortCanonicalName(dsmlMessage, "<null>"));
132 }
133 }
134
135 auditDataset.setRequestItems(requestItems);
136 return auditDataset;
137 }
138
139 @Override
140 public boolean enrichAuditDatasetFromResponse(Iti59AuditDataset auditDataset, Object responseObject, AuditContext auditContext) {
141
142 if (auditDataset.getRequestItems() == null) {
143 log.debug("The request was empty, nothing to audit");
144 return true;
145 }
146
147 BatchResponse batchResponse = (BatchResponse) responseObject;
148
149
150 if ((batchResponse == null) || (batchResponse.getBatchResponses() == null)) {
151 for (Iti59AuditDataset.RequestItem requestItem : auditDataset.getRequestItems()) {
152 if (requestItem != null) {
153 requestItem.setOutcomeCode(EventOutcomeIndicator.SeriousFailure);
154 }
155 }
156 return false;
157 }
158
159
160 Map<String, Object> byRequestId = new HashMap<>();
161 Object[] byNumber = new Object[batchResponse.getBatchResponses().size()];
162
163 for (int i = 0; i < batchResponse.getBatchResponses().size(); ++i) {
164 Object value = batchResponse.getBatchResponses().get(i).getValue();
165 if (value instanceof LDAPResult) {
166 LDAPResult ldapResult = (LDAPResult) value;
167 if (isEmpty(ldapResult.getRequestID())) {
168 byNumber[i] = ldapResult;
169 } else {
170 byRequestId.put(ldapResult.getRequestID(), ldapResult);
171 }
172 } else if (value instanceof ErrorResponse) {
173 ErrorResponse errorResponse = (ErrorResponse) value;
174 if (isEmpty(errorResponse.getRequestID())) {
175 byNumber[i] = errorResponse;
176 } else {
177 byRequestId.put(errorResponse.getRequestID(), errorResponse);
178 }
179 }
180 }
181
182
183 for (int i = 0; i < auditDataset.getRequestItems().length; ++i) {
184 Iti59AuditDataset.RequestItem requestItem = auditDataset.getRequestItems()[i];
185
186 if (requestItem != null) {
187 if (isEmpty(requestItem.getRequestId())) {
188 setOutcomeCode(
189 requestItem,
190 (i < byNumber.length) ? byNumber[i] : null,
191 "Could not find response for the ID-less ITI-59 request number {}: either too few responses, or wrong type, or has a request ID",
192 i);
193 } else {
194 setOutcomeCode(
195 requestItem,
196 byRequestId.get(requestItem.getRequestId()),
197 "Could not find response for the ITI-59 sub-request with ID '{}': either no ID match, or wrong type",
198 requestItem.getRequestId());
199 }
200 }
201 }
202
203 return true;
204 }
205
206 private static void setOutcomeCode(Iti59AuditDataset.RequestItem requestItem, Object value, String failureLogMessage, Object... failureLogArgs) {
207 if (value instanceof LDAPResult) {
208 LDAPResult ldapResult = (LDAPResult) value;
209 requestItem.setOutcomeCode((ldapResult.getResultCode() != null) && (ldapResult.getResultCode().getCode() == 0)
210 ? EventOutcomeIndicator.Success
211 : EventOutcomeIndicator.SeriousFailure);
212 requestItem.setOutcomeDescription(ldapResult.getErrorMessage());
213 } else if (value instanceof ErrorResponse) {
214 requestItem.setOutcomeCode(EventOutcomeIndicator.SeriousFailure);
215 requestItem.setOutcomeDescription(((ErrorResponse)value).getMessage());
216 } else {
217 requestItem.setOutcomeCode(EventOutcomeIndicator.MajorFailure);
218 log.debug(failureLogMessage, failureLogArgs);
219 }
220 }
221
222 @Override
223 public EventOutcomeIndicator getEventOutcomeIndicator(Object response) {
224
225 return null;
226 }
227
228 @Override
229 public AuditMessage[] makeAuditMessage(AuditContext auditContext, Iti59AuditDataset auditDataset) {
230
231 return Stream.of(auditDataset.getRequestItems())
232 .filter(requestItem -> StringUtils.isNotBlank(requestItem.getUid()))
233 .map(requestItem -> makeAuditMessage(auditContext, auditDataset, requestItem))
234 .toArray(AuditMessage[]::new);
235 }
236
237 protected abstract AuditMessage makeAuditMessage(AuditContext auditContext, Iti59AuditDataset auditDataset, Iti59AuditDataset.RequestItem requestItem);
238
239 }