View Javadoc
1   /*
2    * Copyright 2017 the original author or authors.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.openehealth.ipf.commons.audit.event;
17  
18  import org.openehealth.ipf.commons.audit.AuditContext;
19  import org.openehealth.ipf.commons.audit.codes.*;
20  import org.openehealth.ipf.commons.audit.model.*;
21  import org.openehealth.ipf.commons.audit.types.*;
22  
23  import java.time.Instant;
24  import java.util.*;
25  import java.util.regex.Pattern;
26  
27  import static java.util.Objects.requireNonNull;
28  
29  /**
30   * AuditMessage builder with some protected helper methods that are called by subclasses in order to add
31   * e.g. participants and participating objects of the audit event.
32   *
33   * @param <T>
34   *
35   * @author Christian Ohr
36   * @since 3.5
37   */
38  public abstract class BaseAuditMessageBuilder<T extends BaseAuditMessageBuilder<T>> implements AuditMessageBuilder<T> {
39  
40      private static final Pattern IPV4 = Pattern.compile("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$");
41      private static final Pattern IPV6 = Pattern.compile("^(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}$", Pattern.CASE_INSENSITIVE);
42      private AuditMessage auditMessage;
43  
44      public BaseAuditMessageBuilder() {
45          this.auditMessage = new AuditMessage();
46      }
47  
48      @Override
49      public void validate() {
50          auditMessage.validate();
51      }
52  
53      /**
54       * @return the AuditMessage built
55       */
56      public AuditMessage getMessage() {
57          return auditMessage;
58      }
59  
60      /**
61       * Sets a {@link org.openehealth.ipf.commons.audit.model.AuditSourceIdentificationType} for a given Audit Source ID
62       *
63       * @param sourceId The Audit Source ID to use
64       * @return this
65       */
66      public T setAuditSource(String sourceId) {
67          return setAuditSource(sourceId, null);
68      }
69  
70      /**
71       * Sets a {@link org.openehealth.ipf.commons.audit.model.AuditSourceIdentificationType} for a given Audit Source ID
72       * and Audit Source Enterprise Site ID
73       *
74       * @param sourceId         The Audit Source ID to use
75       * @param enterpriseSiteId The Audit Enterprise Site ID to use
76       * @return this
77       */
78      public T setAuditSource(String sourceId, String enterpriseSiteId) {
79          return setAuditSource(sourceId, enterpriseSiteId, (AuditSource[]) null);
80      }
81  
82      /**
83       * Sets a {@link org.openehealth.ipf.commons.audit.model.AuditSourceIdentificationType} for a given Audit Source ID,
84       * Audit Source Enterprise Site ID, and a list of audit source type codes
85       *
86       * @param sourceId         The Audit Source ID to use
87       * @param enterpriseSiteId The Audit Enterprise Site ID to use
88       * @param typeCodes        The RFC 3881 Audit Source Type codes to use
89       * @return this
90       */
91      public T setAuditSource(String sourceId,
92                              String enterpriseSiteId,
93                              AuditSource... typeCodes) {
94          return setAuditSourceIdentification(sourceId, enterpriseSiteId, typeCodes);
95      }
96  
97      /**
98       * Sets the audit source from the audit context
99       *
100      * @param auditContext audit context
101      * @return this
102      */
103     public T setAuditSource(AuditContext auditContext) {
104         return setAuditSourceIdentification(
105                 auditContext.getAuditSourceId(),
106                 auditContext.getAuditEnterpriseSiteId(),
107                 auditContext.getAuditSource());
108     }
109 
110 
111     /**
112      * Create and set an Event Identification block for this audit event message
113      *
114      * @param outcome The Event Outcome Indicator
115      * @param action  The Event Action Code
116      * @param id      The Event ID
117      * @param type    The Event Type Code
118      * @return this
119      */
120     public T setEventIdentification(EventOutcomeIndicator outcome,
121                                     String eventOutcomeDescription,
122                                     EventActionCode action,
123                                     EventId id,
124                                     EventType type,
125                                     PurposeOfUse... purposesOfUse) {
126         return setEventIdentification(outcome, eventOutcomeDescription, action, id, type,
127                 purposesOfUse != null ? Arrays.asList(purposesOfUse) : Collections.emptyList());
128     }
129 
130     /**
131      * Create and set an Event Identification block for this audit event message
132      *
133      * @param outcome The Event Outcome Indicator
134      * @param action  The Event Action Code
135      * @param id      The Event ID
136      * @param type    The Event Type Code
137      * @return this
138      */
139     public T setEventIdentification(EventOutcomeIndicator outcome,
140                                     String eventOutcomeDescription,
141                                     EventActionCode action,
142                                     EventId id,
143                                     EventType type,
144                                     Collection<PurposeOfUse> purposesOfUse) {
145         EventIdentificationType eventIdentification = new EventIdentificationType(id, Instant.now(), outcome);
146         eventIdentification.setEventActionCode(action);
147         eventIdentification.setEventOutcomeDescription(eventOutcomeDescription);
148         if (type != null) {
149             eventIdentification.getEventTypeCode().add(type);
150         }
151         if (purposesOfUse != null) {
152             purposesOfUse.stream()
153                     .filter(Objects::nonNull)
154                     .forEach(pou -> eventIdentification.getPurposesOfUse().add(pou));
155         }
156         auditMessage.setEventIdentification(eventIdentification);
157         return self();
158     }
159 
160     /**
161      * Create and add an Audit Source Identification to this audit event message
162      *
163      * @param sourceID         The Audit Source ID
164      * @param enterpriseSiteID The Audit Enterprise Site ID
165      * @param typeCodes        The Audit Source Type Codes
166      * @return this
167      */
168     public T setAuditSourceIdentification(String sourceID,
169                                           String enterpriseSiteID,
170                                           AuditSource... typeCodes) {
171         return setAuditSourceIdentification(sourceID, enterpriseSiteID,
172                 typeCodes != null ? Arrays.asList(typeCodes) : Collections.emptyList());
173     }
174 
175     /**
176      * Create and add an Audit Source Identification to this audit event message
177      *
178      * @param sourceID         The Audit Source ID
179      * @param enterpriseSiteID The Audit Enterprise Site ID
180      * @param typeCodes        The Audit Source Type Codes
181      * @return this
182      */
183     public T setAuditSourceIdentification(String sourceID,
184                                           String enterpriseSiteID,
185                                           Collection<AuditSource> typeCodes) {
186         AuditSourceIdentificationType asi = new AuditSourceIdentificationType(sourceID);
187         if (typeCodes != null) {
188             typeCodes.stream()
189                     .filter(Objects::nonNull)
190                     .forEach(typeCode -> asi.getAuditSourceType().add(typeCode));
191         }
192         asi.setAuditEnterpriseSiteID(enterpriseSiteID);
193         return setAuditSourceIdentification(asi);
194     }
195 
196     public T setAuditSourceIdentification(AuditSourceIdentificationType auditSourceIdentificationType) {
197         auditMessage.setAuditSourceIdentification(auditSourceIdentificationType);
198         return self();
199     }
200 
201     /**
202      * Adds an Active Participant representing the source participant
203      *
204      * @param userId      The identity of the local user or process exporting the data. If both are known, then two active participants shall be included (both the person and the process).
205      * @param altUserId   The Active Participant's Alternate UserID
206      * @param userName    The Active Participant's UserName
207      * @param networkId   The Active Participant's Network Access Point ID
208      * @param isRequestor Whether the participant represents the requestor (i.e. push request)
209      * @return this
210      */
211     public T addSourceActiveParticipant(String userId,
212                                         String altUserId,
213                                         String userName,
214                                         String networkId,
215                                         boolean isRequestor) {
216         return addActiveParticipant(
217                 userId,
218                 altUserId,
219                 userName,
220                 isRequestor,
221                 Collections.singletonList(ActiveParticipantRoleIdCode.Source),
222                 networkId);
223     }
224 
225     /**
226      * Adds an Active Participant representing destination participant
227      *
228      * @param userId               The identity of the remote user or process receiving the data
229      * @param altUserId            The Active Participant's Alternate UserID
230      * @param userName             The Active Participant's UserName
231      * @param networkAccessPointId The Active Participant's Network Access Point ID
232      * @param userIsRequestor      Whether the destination participant represents the requestor (i.e. pull request)
233      * @return this
234      */
235     public T addDestinationActiveParticipant(String userId,
236                                              String altUserId,
237                                              String userName,
238                                              String networkAccessPointId,
239                                              boolean userIsRequestor) {
240         return addActiveParticipant(
241                 userId,
242                 altUserId,
243                 userName,
244                 userIsRequestor,
245                 Collections.singletonList(ActiveParticipantRoleIdCode.Destination),
246                 networkAccessPointId);
247     }
248 
249 
250     /**
251      * Create and add an Active Participant to this audit event message but automatically
252      * determine the Network Access Point ID Type Code
253      *
254      * @param userID               The Active Participant's UserID
255      * @param altUserID            The Active Participant's Alternate UserID
256      * @param userName             The Active Participant's UserName
257      * @param userIsRequestor      Whether this Active Participant is a requestor
258      * @param roleIdCodes          The Active Participant's Role Codes
259      * @param networkAccessPointID The Active Participant's Network Access Point ID (IP / Hostname)
260      * @return this
261      */
262     public T addActiveParticipant(String userID,
263                                   String altUserID,
264                                   String userName,
265                                   Boolean userIsRequestor,
266                                   List<ActiveParticipantRoleId> roleIdCodes,
267                                   String networkAccessPointID) {
268         // Does lookup to see if using IP Address or hostname in Network Access Point ID
269         return addActiveParticipant(
270                 userID,
271                 altUserID,
272                 userName,
273                 userIsRequestor,
274                 roleIdCodes,
275                 networkAccessPointID,
276                 getNetworkAccessPointCodeFromAddress(networkAccessPointID),
277                 null,
278                 null);
279     }
280 
281     /**
282      * Create and add an Active Participant block to this audit event message
283      *
284      * @param userID                     The Active Participant's UserID
285      * @param altUserID                  The Active Participant's Alternate UserID
286      * @param userName                   The Active Participant's UserName
287      * @param userIsRequestor            Whether this Active Participant is a requestor
288      * @param roleIdCodes                The Active Participant's Role Codes
289      * @param networkAccessPointID       The Active Participant's Network Access Point ID (IP / Hostname)
290      * @param networkAccessPointTypeCode The type code for the Network Access Point ID
291      * @return this
292      */
293     public T addActiveParticipant(String userID,
294                                   String altUserID,
295                                   String userName,
296                                   Boolean userIsRequestor,
297                                   List<ActiveParticipantRoleId> roleIdCodes,
298                                   String networkAccessPointID,
299                                   NetworkAccessPointTypeCode networkAccessPointTypeCode,
300                                   String mediaIdentifier,
301                                   MediaType mediaType) {
302         ActiveParticipantType ap = new ActiveParticipantType(userID, userIsRequestor);
303         ap.setAlternativeUserID(altUserID);
304         ap.setUserName(userName);
305         if (roleIdCodes != null) {
306             roleIdCodes.stream()
307                     .filter(Objects::nonNull)
308                     .forEach(roleId -> ap.getRoleIDCodes().add(roleId));
309         }
310         ap.setNetworkAccessPointID(networkAccessPointID);
311         ap.setNetworkAccessPointTypeCode(networkAccessPointTypeCode);
312         ap.setMediaIdentifier(mediaIdentifier);
313         ap.setMediaType(mediaType);
314         return addActiveParticipant(ap);
315     }
316 
317     public T addActiveParticipant(ActiveParticipantType activeParticipantType) {
318         auditMessage.getActiveParticipants().add(activeParticipantType);
319         return self();
320     }
321 
322     /**
323      * Adds a Participant Object representing a patient involved in the event
324      *
325      * @param patientId   Identifier of the involved patient
326      * @param patientName name of the involved patient
327      * @return this
328      */
329     public T addPatientParticipantObject(String patientId, String patientName,
330                                          List<TypeValuePairType> details,
331                                          ParticipantObjectDataLifeCycle lifecycle) {
332         return addParticipantObjectIdentification(
333                 ParticipantObjectIdTypeCode.PatientNumber,
334                 patientName,
335                 null,
336                 details,
337                 requireNonNull(patientId),
338                 ParticipantObjectTypeCode.Person,
339                 ParticipantObjectTypeCodeRole.Patient,
340                 lifecycle,
341                 null);
342     }
343 
344     /**
345      * Adds a Participant Object representing a studies involved in the event
346      *
347      * @param studyId       Identifier of the involved study
348      * @param objectDetails objectDetails
349      * @return this
350      */
351     public T addStudyParticipantObject(String studyId, List<TypeValuePairType> objectDetails) {
352         return addParticipantObjectIdentification(
353                 ParticipantObjectIdTypeCode.StudyInstanceUID,
354                 studyId,
355                 null,
356                 objectDetails,
357                 requireNonNull(studyId),
358                 ParticipantObjectTypeCode.System,
359                 ParticipantObjectTypeCodeRole.Report,
360                 null,
361                 null);
362     }
363 
364 
365     /**
366      * Create and add an Participant Object Identification block to this audit event message
367      *
368      * @param objectIDTypeCode    The Participant Object ID Type code
369      * @param objectName          The Participant Object Name
370      * @param objectQuery         The Participant Object Query data
371      * @param objectDetails       The Participant Object detail
372      * @param objectID            The Participant Object ID
373      * @param objectTypeCode      The Participant Object Type Code
374      * @param objectTypeCodeRole  The Participant Object Type Code's ROle
375      * @param objectDataLifeCycle The Participant Object Data Life Cycle
376      * @param objectSensitivity   The Participant Object sensitivity
377      * @return this
378      */
379     public T addParticipantObjectIdentification(ParticipantObjectIdType objectIDTypeCode,
380                                                 String objectName,
381                                                 byte[] objectQuery,
382                                                 List<TypeValuePairType> objectDetails,
383                                                 String objectID,
384                                                 ParticipantObjectTypeCode objectTypeCode,
385                                                 ParticipantObjectTypeCodeRole objectTypeCodeRole,
386                                                 ParticipantObjectDataLifeCycle objectDataLifeCycle,
387                                                 String objectSensitivity) {
388         ParticipantObjectIdentificationType poit = new ParticipantObjectIdentificationType(objectID, objectIDTypeCode);
389 
390         poit.setParticipantObjectName(objectName);
391         poit.setParticipantObjectQuery(objectQuery);
392         if (objectDetails != null) {
393             objectDetails.stream()
394                     .filter(Objects::nonNull)
395                     .forEach(objectDetail -> poit.getParticipantObjectDetails().add(objectDetail));
396         }
397         poit.setParticipantObjectTypeCode(objectTypeCode);
398         poit.setParticipantObjectTypeCodeRole(objectTypeCodeRole);
399         poit.setParticipantObjectDataLifeCycle(objectDataLifeCycle);
400         poit.setParticipantObjectSensitivity(objectSensitivity);
401         return addParticipantObjectIdentification(poit);
402     }
403 
404     public T addParticipantObjectIdentification(ParticipantObjectIdentificationType poit) {
405         auditMessage.getParticipantObjectIdentifications().add(poit);
406         return self();
407     }
408 
409     protected NetworkAccessPointTypeCode getNetworkAccessPointCodeFromAddress(String address) {
410         if (address == null) {
411             return null;
412         }
413         return IPV4.matcher(address).matches() || IPV6.matcher(address).matches() ?
414                 NetworkAccessPointTypeCode.IPAddress :
415                 NetworkAccessPointTypeCode.MachineName;
416     }
417 
418     /**
419      * Create and set a Type Value Pair instance for a given type and value
420      *
421      * @param type  The type to set
422      * @param value The value to set
423      * @return The Type Value Pair instance
424      */
425     public TypeValuePairType getTypeValuePair(String type, Object value) {
426         return new TypeValuePairType(requireNonNull(type), requireNonNull(value).toString());
427     }
428 
429 
430 }