ATNA Plugin

EHRbase provides capabilities to log all accesses to its services based on the ATNA (Audit Trail and Node Authentication) specification.

../../../_images/atna_diagram.png

The ATNA logging functionality is implemented using the EHRbase Plugin mechanism. The following services are covered by the ATNA Logging Plugin:

  • Definition
    • Stored query
      • List stored queries

      • Store a query

      • Get stored query and info/metadata

      • Delete stored query

  • EHR
    • EHR
      • Create EHR

      • Create EHR with ID

      • Get EHR summary by ID

      • Get EHR summary by Subject ID

    • EHR Status
      • Get EHR Status version by time

      • Get EHR Status by version ID

      • Update EHR Status

    • Versioned EHR Status
      • Get Versioned EHR Status

      • Get Versioned EHR Status revision history

      • Get Versioned EHR Status version by time

      • Get Versioned EHR Status version by ID

    • Composition
      • Create Composition

      • Update Composition

      • Delete Composition

      • Get Composition by version ID

      • Get Composition at time

      • Delete Composition

    • Versioned Composition
      • Get Versioned Composition

      • Get Versioned Composition revision history

      • Get Versioned Composition version by ID

      • Get Versioned Composition version at time

    • Directory
      • Create Directory

      • Update Directory

      • Delete Directory

      • Get Folder in Directory version

      • Get Folder in Directory version at time

    • Contribution
      • Create Contribution

      • Get Contribution by ID

  • Query
    • Execute ad-hoc (non-stored) AQL Query

    • Execute Store Query

  • Admin
    • EHR
      • Update EHR

      • Delete EHR

    • Composition
      • Delete Composition

    • Directory
      • Delete Directory

    • Contribution
      • Delete Contribution

Parameters / Environment Variables

Parameter

Env Variable

Use

Example

ipf.atna.audit-enabled

IPF_ATNA_AUDITENABLED

Whether to enable ATNA audit feature

“true”

ipf.atna.audit-enterprise-site-id

IPF_ATNA_AUDITENTERPRISESITEID

Enterprise Site ID (CDR Base Tenant ID is used)

1f332a66-0e57-11ed-861d-0242ac120002

ipf.atna.audit-repository-host

IPF_ATNA_AUDITREPOSITORYHOST

Audit repository host

localhost

ipf.atna.audit-repository-port

IPF_ATNA_AUDITREPOSITORYPORT

Audit repository port

514

ipf.atna.audit-source-id

IPF_ATNA_AUDITSOURCEID

Audit source ID

${spring.application.name}

ipf.atna.audit-value-if-missing

IPF_ATNA_AUDITVALUEIFMISSING

Value used for mandatory elements that are empty.

UNKNOWN

ATNA Message Example and Mapping

The code listing shows an example ATNA message that is created whenever a user creates an electronic health record object (EHR) in EHRbase. The user information is either extracted from the Basic Auth credentials or from the OAuth2 JWT Token.

Listing 1: Example ATNA message
<!-- EHR creation successful -->
<?xml version="1.0" encoding="UTF-8"?>
<AuditMessage>
    <EventIdentification EventActionCode="C" EventDateTime="2023-09-21T10:13:50.289269153Z" EventOutcomeIndicator="0">
        <EventID csd-code="110110" codeSystemName="DCM" originalText="Patient Record" />
        <EventOutcomeDescription>Operation performed successfully</EventOutcomeDescription>
    </EventIdentification>
    <ActiveParticipant UserID="john doe " UserIsRequestor="true" NetworkAccessPointID="10.216.24.150"
        NetworkAccessPointTypeCode="2">
        <RoleIDCode csd-code="110153" codeSystemName="DCM" originalText="Source Role ID" />
    </ActiveParticipant>
    <ActiveParticipant UserID="ehrbase" UserIsRequestor="false" NetworkAccessPointID="10.42.23.77"
        NetworkAccessPointTypeCode="2">
        <RoleIDCode csd-code="110152" codeSystemName="DCM" originalText="Destination Role ID" />
    </ActiveParticipant>
    <AuditSourceIdentification AuditEnterpriseSiteID="1f332a66-0e57-11ed-861d-0242ac120002" AuditSourceID="ehrbase">
        <AuditSourceTypeCode csd-code="4" codeSystemName="DCM" originalText="Application Server Process or Thread" />
    </AuditSourceIdentification>
    <ParticipantObjectIdentification ParticipantObjectID="ae1d91f9-43c4-4ed9-bea0-51e2f1494e0b"
        ParticipantObjectTypeCode="1" ParticipantObjectTypeCodeRole="1" ParticipantObjectDataLifeCycle="1">
        <ParticipantObjectIDTypeCode csd-code="2" codeSystemName="RFC-3881" originalText="Patient Number" />
    </ParticipantObjectIdentification>
</AuditMessage>
Table 6: openEHR to ATNA Mapping

Field

Explanation

Mapping

EventActionCode

Represents the action performed

“Create(“”C””), Read(“”R””), Update(“”U””), Delete(“”D””), Execute(“”E””)”

EventDateTime

The Data of the Message

Use current datetime

EventOutcomeIndicator

status of the operation

“Success(0), MinorFailure(4), SeriousFailure(8), MajorFailure(12)”

EventID.csd-code and EventID.originalText

type of the entity

“a) In case of an EHR: csd-code=””110110”” and originalText=””Patient Record””; b) for the rest: always the string composition, contribution, query, or directory”

AuditEnterpriseSiteID

ID of the audit producing system

Tenant-ID of EHRbase

ActiveParticipant.UserID

The user identifier

“a) Basic Auth: username; b) OAuth2: sub claim from the token”

ParticipantObjectID [EHR]

The EHR was affected by the transaction

EHR and EHR Status: 1 x ParticipantObjectIdentification with ParticipantObjectTypeCode=1, ParticipantObjectIDTypeCode.originalText=URI, and ParticipantObjectID as the Patient Subject ID of the HER

ParticipantObjectID [Composition, Contribution, Directory]

Compositions, Contributions, or the Directory was affected by the transaction

“a) ParticipantObjectTypeCode=1, ParticipantObjectIDTypeCode.originalText=Patient Number, and ParticipantObjectID is the Patient Subject ID of the parent EHR; b) ParticipantObjectTypeCode=2, ParticipantObjectIDTypeCode.originalText=URI, and ParticipantObjectID in the form /ehr/{ehrId}/{object}/{?objectId}”

ParticipantObjectID [Ad-hoc query execution]

Ad-hoc queries are part of the transaction

Ad-hoc query execution: 1 x ParticipantObjectIdentification with ParticipantObjectTypeCode=2, ParticipantObjectIDTypeCode.originalText=Search Criteria, and ParticipantObjectID set to UNKNOWN

ParticipantObjectID [Stored queries]

Creation and execution of stored queries are part of the transaction

“a) ParticipantObjectTypeCode=2, ParticipantObjectIDTypeCode.originalText=URI, and ParticipantObjectID in the form /definition/query/{queryQualifiedName}/{?version}; b) ParticipantObjectTypeCode=2, ParticipantObjectIDTypeCode.originalText=Search Criteria, and ParticipantObjectID as the qualified name of the query”

NetworkAccessPointID

Identification of the user device that creates the audit record

The IP address of the requesting client

Setup an Audit Repository

The easiest way to install and configure your own Audit Record Repository instance is to used the Elastic Stack together with Docker.

Step 1: Create the Docker Compose file

The following Docker Compose file installs and configures Elasticsearch, Kibana and Logstash according to the default configuration used in CDR Base.

services:
  elasticsearch:
    image: elasticsearch:7.7.0
    hostname: elasticsearch
    environment:
      - "discovery.type=single-node"
    ports:
      - 9200:9200
      - 9300:9300
    networks:
      - elastic-network
  kibana:
    image: kibana:7.7.0
    hostname: kibana
    ports:
      - 5601:5601
    links:
      - elasticsearch:elasticsearch
    depends_on:
      - elasticsearch
    networks:
      - elastic-network
  logstash:
    image: logstash:7.7.0
    hostname: logstash
    ports:
      - 9600:9600
      - 8089:8089
      - 514:514/udp
    volumes:
      - ./logstash:/usr/share/logstash/pipeline/
    links:
      - elasticsearch:elasticsearch
    depends_on:
      - elasticsearch
    networks:
      - elastic-network
networks:
  elastic-network: { }

Step 2: Create the Docker Compose file

The second step is the configuration of Logstash to enable the support of the Syslog Protocol and DICOM Audit Trail Message Format Profile. The following configuration file has to be stored in the directory referenced in the volume mounted in the logstash service declared in the Docker Compose file above (i.e. ./logstash/logstash.conf).

input {
  udp {
    port => 514

  }
}
filter {
  grok {
    match => {
      "message" => "<%{NONNEGINT:syslog_header_pri}>%{NONNEGINT:syslog_header_version}%{SPACE}(?:-|%{TIMESTAMP_ISO8601})%{SPACE}(?:-|%{IPORHOST:syslog_header_hostname})%{SPACE}(?:%{SYSLOG5424PRINTASCII:syslog_header_app-name}|-)%{SPACE}(?:-|%{SYSLOG5424PRINTASCII:syslog_header_procid})%{SPACE}(?:-|%{SYSLOG5424PRINTASCII:syslog_header_msgid})%{SPACE}(?:-|(?<syslog_structured_data>(\[.*?[^\\]\])+))(?:%{SPACE}%{GREEDYDATA:syslog_message}|)"
    }
  }
  xml {
    store_xml => true
    target => "AuditMessage"
    force_array => false
    source => "syslog_message"
  }
  mutate {
    remove_field => [ "@timestamp", "@version", "message", "host" ]
  }
}
output {
  elasticsearch { hosts => ["elasticsearch:9200"] }
}

Step 3: Start your Audit Repository instance

docker-compose -f docker-compose-audit.yml up