View Javadoc
1   /*
2    * Copyright 2018 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  
17  package org.openehealth.ipf.commons.ihe.fhir.audit.events;
18  
19  import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
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.ParticipantObjectIdTypeCode;
23  import org.openehealth.ipf.commons.audit.codes.ParticipantObjectTypeCode;
24  import org.openehealth.ipf.commons.audit.codes.ParticipantObjectTypeCodeRole;
25  import org.openehealth.ipf.commons.audit.event.CustomAuditMessageBuilder;
26  import org.openehealth.ipf.commons.ihe.core.atna.event.IHEAuditMessageBuilder;
27  import org.openehealth.ipf.commons.ihe.fhir.audit.GenericFhirAuditDataset;
28  import org.openehealth.ipf.commons.ihe.fhir.audit.codes.FhirEventIdCode;
29  import org.openehealth.ipf.commons.ihe.fhir.audit.codes.FhirEventTypeCode;
30  import org.openehealth.ipf.commons.ihe.fhir.audit.codes.FhirParticipantObjectIdTypeCode;
31  
32  import java.nio.charset.StandardCharsets;
33  import java.util.Base64;
34  
35  /**
36   * Builder for audit events related to generic FHIR transactions, using the resource type
37   * and operation type for the participants. The audit messages follow the pattern shared
38   * with audit messages of IHE transactions. Some exceptions:
39   * <ul>
40   * <li>the Event ID is always EV("rest", "http://hl7.org/fhir/audit-event-type", "RESTful Operation") (as described
41   * in https://www.hl7.org/fhir/valueset-audit-event-type.html)</li>
42   * <li>and the Event Action corresponds with the FHIR operation (see https://www.hl7.org/fhir/valueset-audit-event-action.html)</li>
43   * <li>the query/resource participant object's ParticipantObjectIdType is the resource type</li>
44   * </ul>
45   * This also allows an easy conversion into a FHIR AuditEvent.
46   *
47   * @author Christian Ohr
48   * @since 3.5
49   */
50  public class GenericFhirAuditMessageBuilder extends
51          IHEAuditMessageBuilder<GenericFhirAuditMessageBuilder, CustomAuditMessageBuilder> {
52  
53      public GenericFhirAuditMessageBuilder(AuditContext auditContext, GenericFhirAuditDataset auditDataset) {
54          super(auditContext, new CustomAuditMessageBuilder(
55                  auditDataset.getEventOutcomeIndicator(),
56                  auditDataset.getEventOutcomeDescription(),
57                  eventActionCode(auditDataset.getOperation()),
58                  FhirEventIdCode.RestfulOperation,
59                  FhirEventTypeCode.fromRestOperationType(auditDataset.getOperation())
60          ));
61  
62          // First the source, then the destination
63          if (auditDataset.isServerSide()) {
64              setRemoteParticipant(auditDataset);
65              addHumanRequestor(auditDataset);
66              setLocalParticipant(auditDataset);
67          } else {
68              setLocalParticipant(auditDataset);
69              addHumanRequestor(auditDataset);
70              setRemoteParticipant(auditDataset);
71          }
72      }
73  
74      public GenericFhirAuditMessageBuilder addPatients(GenericFhirAuditDataset auditDataset) {
75          auditDataset.getPatientIds().forEach(patientId ->
76                  delegate.addParticipantObjectIdentification(
77                          ParticipantObjectIdTypeCode.PatientNumber,
78                          null,
79                          null,
80                          null,
81                          patientId,
82                          ParticipantObjectTypeCode.Person,
83                          ParticipantObjectTypeCodeRole.Patient,
84                          null,
85                          null));
86          return self();
87      }
88  
89      /**
90       * @param auditDataset Audit Dataset
91       * @return this
92       */
93      public GenericFhirAuditMessageBuilder addQueryParticipantObject(GenericFhirAuditDataset auditDataset) {
94          delegate.addParticipantObjectIdentification(
95                  FhirParticipantObjectIdTypeCode.fromResourceType(auditDataset.getAffectedResourceType()),
96                  auditDataset.getAffectedResourceType(),
97                  Base64.getEncoder().encode(auditDataset.getQueryString().getBytes(StandardCharsets.UTF_8)),
98                  null,
99                  "FHIR Restful Query",
100                 ParticipantObjectTypeCode.System,
101                 ParticipantObjectTypeCodeRole.Query,
102                 null,
103                 null);
104 
105         return self();
106     }
107 
108     /**
109      * @param auditDataset Audit Dataset
110      * @return this
111      */
112     public GenericFhirAuditMessageBuilder addResourceParticipantObject(GenericFhirAuditDataset auditDataset) {
113         delegate.addParticipantObjectIdentification(
114                 FhirParticipantObjectIdTypeCode.fromResourceType(
115                         auditDataset.getResourceId().getResourceType()),
116                 null,
117                 null,
118                 null,
119                 auditDataset.getResourceId().getValue(),
120                 ParticipantObjectTypeCode.System,
121                 ParticipantObjectTypeCodeRole.Job,
122                 null,
123                 auditDataset.getSecurityLabel());
124         return self();
125     }
126 
127 
128     private static EventActionCode eventActionCode(RestOperationTypeEnum operation) {
129         switch (operation) {
130             case CREATE:
131                 return EventActionCode.Create;
132             case READ:
133             case VREAD:
134                 return EventActionCode.Read;
135             case UPDATE:
136             case PATCH:
137                 return EventActionCode.Update;
138             case DELETE:
139                 return EventActionCode.Delete;
140             default:
141                 return EventActionCode.Execute;
142         }
143     }
144 }