13.5.1Basic Audit Log Patterns (BALP) Interceptor

 

The IHE Basic Audit Log Patterns implementation guide describes a set of workflows and data models for the creation of AuditEvent resources based on user/client actions.

HAPI FHIR provides an interceptor that can be registered against a server, and will observe events on that server and automatically generate AuditEvent resources which are conformant to the appropriate profiles within the BALP specification.

This interceptor implements the following profiles:

BALP Profile Trigger Triggering Pointcut
Create Performed when a resource has been created that is not a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_CREATED
PatientCreate Performed when a resource has been created that is a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_CREATED
Read Performed when a resource has been read that is not a member of the Patient compartment. Note that other extended operations which expose individual resource data may also trigger the creation of an AuditEvent with this profile. For example, the $diff operation exposes data within a resource, so it will also trigger this event. STORAGE_PRESHOW_RESOURCES
PatientRead Performed when a resource has been read that is a member of the Patient compartment. Note that other extended operations which expose individual resource data may also trigger the creation of an AuditEvent with this profile. For example, the $diff operation exposes data within a resource, so it will also trigger this event. STORAGE_PRESHOW_RESOURCES
Update Performed when a resource has been updated that is not a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_UPDATED
PatientUpdate Performed when a resource has been updated that is a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_UPDATED
Delete Performed when a resource has been deleted that is not a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_DELETED
PatientDelete Performed when a resource has been deleted that is a member of the Patient compartment. STORAGE_PRECOMMIT_RESOURCE_DELETED
Query Performed when a non-patient-oriented search is performed. This refers to a search that is returning data that is not in a specific patient compartment. STORAGE_PRESHOW_RESOURCES
PatientQuery Performed when a patient-oriented search is performed. This refers to a search that returns data in a specific patient compartment. STORAGE_PRESHOW_RESOURCES

13.5.2Architecture

 

The HAPI FHIR BALP infrastructure consists of the following components:

  • The BalpAuditCaptureInterceptor is the primary interceptor, which you register against a HAPI FHIR Plain Server.
  • The IBalpAuditEventSink is an interface which receives generated AuditEvents and processes them. Appropriate processing will depend on your use case, but could be storing them locally, transmitting them to a remote server, logging them to a syslog, or even selectively dropping them. See Audit Event Sink below.
  • The IBalpAuditContextServices is an interface which supplies context information for a given client action. When generating a BALP conformant AuditEvent resource, the BalpAuditCaptureInterceptor will automatically populate most of the AuditEvent with details such as the entity (ie. the resource being accessed or modified) and the server (the FHIR server being used to transmit or store the information). However, other information such as the agent and the user (ie. the FHIR client and the physical user) are not known to HAPI FHIR and must be supplied for each request. This interface supplies these details.

13.5.3Audit Event Sink

 

The BALP IBalpAuditEventSink receives and handles generated audit events.

This interface is designed to support custom implementations, so you can absolutely create your own. HAPI FHIR ships with the following implementation:

  • AsyncMemoryQueueBackedFhirClientBalpSink uses an HTTP/REST FHIR client to transmit AuditEvents to a FHIR server endpoint. This can be a local or a remote endpoint, and can be a server with any version of FHIR. Messages are transmitted asynchronously using an in-memory queue.

If you create an implementation of this interface that you think would be useful to others, we would welcome community contributions!

13.5.4Audit Context Services

 

In order to use this interceptor, you must suply an instance of IBalpAuditContextServices. This interface supplies the information about each request that the interceptor cannot determine on its own, such as the identity of the requesting user and the requesting client.

The implementation of this interface for the public HAPI FHIR server is available here.

13.5.5Example

 

The following example shows a simple implementation of the Context Services:

public class ExampleBalpAuditContextServices implements IBalpAuditContextServices {

   /**
    * Here we are just hard-coding a simple display name. In a real implementation
    * we should use the actual identity of the requesting client.
    */
   @Nonnull
   @Override
   public Reference getAgentClientWho(RequestDetails theRequestDetails) {
      Reference client = new Reference();
      client.setDisplay("Growth Chart Application");
      client.getIdentifier().setSystem("http://example.org/clients").setValue("growth_chart");
      return client;
   }

   /**
    * Here we are just hard-coding a simple display name. In a real implementation
    * we should use the actual identity of the requesting user.
    */
   @Nonnull
   @Override
   public Reference getAgentUserWho(RequestDetails theRequestDetails) {
      Reference user = new Reference();
      user.getIdentifier().setSystem("http://example.org/users").setValue("my_username");
      return user;
   }
}

And the following example shows a HAPI FHIR Basic Server with the BALP interceptor wired in:

public class MyServer extends RestfulServer {

   /**
    * Constructor
    */
   public MyServer() {
      super(FhirContext.forR4Cached());
   }

   @Override
   protected void initialize() throws ServletException {
      // Register your resource providers and other interceptors here...

      /*
       * Create our context sservices object
       */
      IBalpAuditContextServices contextServices = new ExampleBalpAuditContextServices();

      /*
       * Create our event sink
       */
      FhirContext fhirContext = FhirContext.forR4Cached();
      String targetUrl = "http://my.fhir.server/baseR4";
      List<Object> clientInterceptors = List.of(
            // We'll register an auth interceptor against the sink FHIR client so that
            // credentials get passed to the target server. Of course in a real implementation
            // you should never hard code credentials like this.
            new BasicAuthInterceptor("username", "password"));
      IBalpAuditEventSink eventSink =
            new AsyncMemoryQueueBackedFhirClientBalpSink(fhirContext, targetUrl, clientInterceptors);
   }
}