PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::security::atna_service_auditor Class Reference

High-level facade for emitting ATNA audit events from DICOM services. More...

#include <atna_service_auditor.h>

Collaboration diagram for kcenon::pacs::security::atna_service_auditor:
Collaboration graph

Public Types

enum class  xds_source_transaction : uint8_t { iti_41 , rad_68 }
 XDS-I.b source-side transaction identifier. More...
 
enum class  xds_consumer_transaction : uint8_t { iti_43 , rad_69 }
 XDS-I.b consumer-side transaction identifier. More...
 

Public Member Functions

 atna_service_auditor (const syslog_transport_config &config, std::string audit_source_id)
 Construct an auditor with syslog transport configuration.
 
virtual ~atna_service_auditor ()=default
 
 atna_service_auditor (const atna_service_auditor &)=delete
 
atna_service_auditor & operator= (const atna_service_auditor &)=delete
 
 atna_service_auditor (atna_service_auditor &&) noexcept
 
atna_service_auditor & operator= (atna_service_auditor &&) noexcept
 
void audit_instance_stored (const std::string &source_ae, const std::string &dest_ae, const std::string &study_uid, const std::string &patient_id, bool success)
 Audit a C-STORE (DICOM Instances Transferred) event.
 
void audit_query (const std::string &calling_ae, const std::string &called_ae, const std::string &query_level, bool success)
 Audit a C-FIND (Query) event.
 
void audit_authentication (const std::string &user_id, bool is_login, bool success)
 Audit a User Authentication event.
 
void audit_security_alert (const std::string &user_id, const std::string &alert_description)
 Audit a Security Alert event (e.g., access denied)
 
virtual void audit_xds_provide_and_register (xds_source_transaction transaction, const std::string &source_ae, const std::string &dest_ae, const std::string &study_uid, const std::string &patient_id, const std::vector< std::string > &sop_instance_uids, bool success)
 Audit an XDS-I.b Provide and Register Document Set transaction.
 
virtual void audit_xds_retrieve (xds_consumer_transaction transaction, const std::string &source_ae, const std::string &dest_ae, const std::string &study_uid, const std::string &patient_id, const std::vector< std::string > &sop_instance_uids, bool success)
 Audit an XDS-I.b Retrieve Document Set transaction.
 
void set_enabled (bool enabled) noexcept
 Enable or disable audit event emission.
 
bool is_enabled () const noexcept
 Check if audit event emission is enabled.
 
size_t events_sent () const noexcept
 Get the number of audit events successfully sent.
 
size_t events_failed () const noexcept
 Get the number of audit event send failures.
 
void reset_statistics () noexcept
 Reset statistics counters.
 
const std::string & audit_source_id () const noexcept
 Get the audit source identifier.
 
const atna_syslog_transport & transport () const noexcept
 Get the underlying transport (for advanced use)
 
void submit_audit_message (const atna_audit_message &message)
 Emit a pre-built RFC 3881 audit message.
 

Private Member Functions

void send_audit (const atna_audit_message &message)
 Send an audit message via syslog transport.
 

Private Attributes

std::string audit_source_id_
 Audit source identifier (e.g., "PACS_SYSTEM_01")
 
atna_syslog_transport transport_
 Syslog transport for sending audit messages.
 
std::atomic< bool > enabled_ {true}
 Whether audit is enabled.
 
std::atomic< size_t > events_sent_ {0}
 Statistics.
 
std::atomic< size_t > events_failed_ {0}
 

Detailed Description

High-level facade for emitting ATNA audit events from DICOM services.

Combines the audit message generator (atna_audit_logger) with the syslog transport (atna_syslog_transport) to provide simple, one-call audit methods for common DICOM operations.

Usage

config.host = "audit-server.hospital.local";
config.port = 6514;
atna_service_auditor auditor(config, "PACS_SYSTEM_01");
// After a C-STORE operation:
auditor.audit_instance_stored("MODALITY_01", "PACS_SCP",
"1.2.3.4.5", "PAT001", true);
// After a C-FIND operation:
auditor.audit_query("WORKSTATION_01", "PACS_SCP",
"STUDY", true);
High-level facade for emitting ATNA audit events from DICOM services.
@ tls
TLS over TCP (RFC 5425) — Secure.
Configuration for the Syslog transport.
syslog_transport_protocol protocol
Transport protocol (UDP or TLS)
uint16_t port
Port number (514 for UDP, 6514 for TLS per IANA)
std::string host
Audit Record Repository hostname or IP.

Definition at line 54 of file atna_service_auditor.h.

Member Enumeration Documentation

◆ xds_consumer_transaction

XDS-I.b consumer-side transaction identifier.

Used by audit_xds_retrieve() to distinguish between ITI-43 (general document retrieval) and RAD-69 (imaging document retrieval) transactions.

Enumerator
iti_43 

ITI-43 Retrieve Document Set.

rad_69 

RAD-69 Retrieve Imaging Document Set.

Definition at line 152 of file atna_service_auditor.h.

152 : uint8_t {
153 iti_43,
154 rad_69
155 };

◆ xds_source_transaction

XDS-I.b source-side transaction identifier.

Used by audit_xds_provide_and_register() to distinguish between ITI-41 (general document set) and RAD-68 (imaging document set) transactions.

Enumerator
iti_41 

ITI-41 Provide and Register Document Set-b.

rad_68 

RAD-68 Provide and Register Imaging Document Set.

Definition at line 140 of file atna_service_auditor.h.

140 : uint8_t {
141 iti_41,
142 rad_68
143 };
@ rad_68
RAD-68 Provide and Register Imaging Document Set.
@ iti_41
ITI-41 Provide and Register Document Set-b.

Constructor & Destructor Documentation

◆ atna_service_auditor() [1/3]

kcenon::pacs::security::atna_service_auditor::atna_service_auditor ( const syslog_transport_config & config,
std::string audit_source_id )

Construct an auditor with syslog transport configuration.

Parameters
configSyslog transport configuration
audit_source_idIdentifier for this audit source (e.g., "PACS_01")

Definition at line 14 of file atna_service_auditor.cpp.

18 transport_(config) {}
std::string audit_source_id_
Audit source identifier (e.g., "PACS_SYSTEM_01")
const std::string & audit_source_id() const noexcept
Get the audit source identifier.
atna_syslog_transport transport_
Syslog transport for sending audit messages.

◆ ~atna_service_auditor()

virtual kcenon::pacs::security::atna_service_auditor::~atna_service_auditor ( )
virtualdefault

◆ atna_service_auditor() [2/3]

kcenon::pacs::security::atna_service_auditor::atna_service_auditor ( const atna_service_auditor & )
delete

◆ atna_service_auditor() [3/3]

kcenon::pacs::security::atna_service_auditor::atna_service_auditor ( atna_service_auditor && other)
noexcept

Definition at line 20 of file atna_service_auditor.cpp.

22 : audit_source_id_(std::move(other.audit_source_id_)),
23 transport_(std::move(other.transport_)),
24 enabled_(other.enabled_.load(std::memory_order_relaxed)),
25 events_sent_(other.events_sent_.load(std::memory_order_relaxed)),
26 events_failed_(other.events_failed_.load(std::memory_order_relaxed)) {}
std::atomic< bool > enabled_
Whether audit is enabled.
std::atomic< size_t > events_sent_
Statistics.

Member Function Documentation

◆ audit_authentication()

void kcenon::pacs::security::atna_service_auditor::audit_authentication ( const std::string & user_id,
bool is_login,
bool success )

Audit a User Authentication event.

Parameters
user_idUser identifier (AE title or username)
is_logintrue for login, false for logout
successWhether the authentication succeeded

Definition at line 101 of file atna_service_auditor.cpp.

104 {
105
106 if (!enabled_.load(std::memory_order_relaxed)) {
107 return;
108 }
109
110 auto outcome = success ? atna_event_outcome::success
112
115 user_id,
116 "", // user IP (not available at this level)
117 is_login,
118 outcome);
119
120 send_audit(msg);
121}
static atna_audit_message build_user_authentication(const std::string &source_id, const std::string &user_id, const std::string &user_ip, bool is_login, atna_event_outcome outcome=atna_event_outcome::success)
Build User Authentication audit message (login/logout)
void send_audit(const atna_audit_message &message)
Send an audit message via syslog transport.

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_user_authentication(), enabled_, send_audit(), kcenon::pacs::security::serious_failure, and kcenon::pacs::security::success.

Here is the call graph for this function:

◆ audit_instance_stored()

void kcenon::pacs::security::atna_service_auditor::audit_instance_stored ( const std::string & source_ae,
const std::string & dest_ae,
const std::string & study_uid,
const std::string & patient_id,
bool success )

Audit a C-STORE (DICOM Instances Transferred) event.

Parameters
source_aeCalling AE title (sender)
dest_aeCalled AE title (receiver)
study_uidStudy Instance UID
patient_idPatient ID (optional, empty if unavailable)
successWhether the operation succeeded

Definition at line 48 of file atna_service_auditor.cpp.

53 {
54
55 if (!enabled_.load(std::memory_order_relaxed)) {
56 return;
57 }
58
61
64 source_ae, // source AE
65 "", // source IP (not available at service level)
66 dest_ae, // destination AE
67 "", // destination IP (not available at service level)
68 study_uid,
69 patient_id,
70 true, // is_import (SCP receiving)
71 outcome);
72
73 send_audit(msg);
74}
static atna_audit_message build_dicom_instances_transferred(const std::string &source_id, const std::string &source_ae, const std::string &source_ip, const std::string &dest_ae, const std::string &dest_ip, const std::string &study_uid, const std::string &patient_id="", bool is_import=true, atna_event_outcome outcome=atna_event_outcome::success)
Build DICOM Instances Transferred audit message (C-STORE, C-MOVE)

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_dicom_instances_transferred(), enabled_, send_audit(), kcenon::pacs::security::serious_failure, and kcenon::pacs::security::success.

Referenced by kcenon::pacs::services::storage_scp::handle_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ audit_query()

void kcenon::pacs::security::atna_service_auditor::audit_query ( const std::string & calling_ae,
const std::string & called_ae,
const std::string & query_level,
bool success )

Audit a C-FIND (Query) event.

Parameters
calling_aeCalling AE title (query requester)
called_aeCalled AE title (query responder)
query_levelQuery level string (PATIENT/STUDY/SERIES/IMAGE)
successWhether the operation succeeded

Definition at line 76 of file atna_service_auditor.cpp.

80 {
81
82 if (!enabled_.load(std::memory_order_relaxed)) {
83 return;
84 }
85
88
89 // Use query level as query_data for the audit trail
92 calling_ae,
93 "", // user IP (not available at service level)
94 "QueryLevel=" + query_level + " CalledAE=" + called_ae,
95 "", // patient_id (not available from query keys here)
96 outcome);
97
98 send_audit(msg);
99}
static atna_audit_message build_query(const std::string &source_id, const std::string &user_id, const std::string &user_ip, const std::string &query_data, const std::string &patient_id="", atna_event_outcome outcome=atna_event_outcome::success)
Build Query audit message (C-FIND, QIDO-RS)

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_query(), enabled_, send_audit(), kcenon::pacs::security::serious_failure, and kcenon::pacs::security::success.

Referenced by kcenon::pacs::services::query_scp::handle_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ audit_security_alert()

void kcenon::pacs::security::atna_service_auditor::audit_security_alert ( const std::string & user_id,
const std::string & alert_description )

Audit a Security Alert event (e.g., access denied)

Parameters
user_idUser identifier
alert_descriptionDescription of the security alert

Definition at line 123 of file atna_service_auditor.cpp.

125 {
126
127 if (!enabled_.load(std::memory_order_relaxed)) {
128 return;
129 }
130
133 user_id,
134 "", // user IP (not available at this level)
135 alert_description);
136
137 send_audit(msg);
138}
static atna_audit_message build_security_alert(const std::string &source_id, const std::string &user_id, const std::string &user_ip, const std::string &alert_description, atna_event_outcome outcome=atna_event_outcome::serious_failure)
Build Security Alert audit message.

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_security_alert(), enabled_, and send_audit().

Here is the call graph for this function:

◆ audit_source_id()

const std::string & kcenon::pacs::security::atna_service_auditor::audit_source_id ( ) const
nodiscardnoexcept

Get the audit source identifier.

Definition at line 295 of file atna_service_auditor.cpp.

295 {
296 return audit_source_id_;
297}

References audit_source_id_.

◆ audit_xds_provide_and_register()

void kcenon::pacs::security::atna_service_auditor::audit_xds_provide_and_register ( xds_source_transaction transaction,
const std::string & source_ae,
const std::string & dest_ae,
const std::string & study_uid,
const std::string & patient_id,
const std::vector< std::string > & sop_instance_uids,
bool success )
virtual

Audit an XDS-I.b Provide and Register Document Set transaction.

Emits an Export (DCM 110106) audit event for source-side XDS transactions (ITI-41 / RAD-68). The transaction identifier is recorded as an event type code to distinguish ITI-41 from RAD-68.

Parameters
transactionTransaction identifier (ITI-41 or RAD-68)
source_aeLocal source participant (AE / device identifier)
dest_aeRemote destination participant (XDS registry/repository)
study_uidStudy Instance UID (the study being provided)
patient_idPatient ID (optional, empty if unavailable)
sop_instance_uidsReferenced SOP Instance UIDs (may be empty for ITI-41)
successWhether the transaction succeeded

Definition at line 178 of file atna_service_auditor.cpp.

185 {
186
187 if (!enabled_.load(std::memory_order_relaxed)) {
188 return;
189 }
190
191 auto outcome = success ? atna_event_outcome::success
193
194 // XDS-I.b Provide and Register is an Export event (DCM 110106) —
195 // the local actor is sending a document set to a remote registry.
198 source_ae, // user (the local source actor)
199 "", // user IP (not available at service level)
200 dest_ae, // destination identifier (remote registry)
201 study_uid,
202 patient_id,
203 outcome);
204
205 // Tag the event with the specific IHE transaction code so consumers
206 // of the audit log can differentiate ITI-41 from RAD-68.
207 msg.event_type_codes.push_back(
208 transaction == xds_source_transaction::iti_41
209 ? xds_event_type_iti_41
210 : xds_event_type_rad_68);
211
212 // Add SOP Instance UID references if provided (required for RAD-68,
213 // optional but recommended for ITI-41).
214 append_sop_instance_objects(msg, sop_instance_uids);
215
216 send_audit(msg);
217}
static atna_audit_message build_export(const std::string &source_id, const std::string &user_id, const std::string &user_ip, const std::string &dest_id, const std::string &study_uid, const std::string &patient_id="", atna_event_outcome outcome=atna_event_outcome::success)
Build Export audit message (media/network export)

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_export(), enabled_, iti_41, send_audit(), kcenon::pacs::security::serious_failure, and kcenon::pacs::security::success.

Referenced by kcenon::pacs::services::xds::imaging_document_source::publish_document().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ audit_xds_retrieve()

void kcenon::pacs::security::atna_service_auditor::audit_xds_retrieve ( xds_consumer_transaction transaction,
const std::string & source_ae,
const std::string & dest_ae,
const std::string & study_uid,
const std::string & patient_id,
const std::vector< std::string > & sop_instance_uids,
bool success )
virtual

Audit an XDS-I.b Retrieve Document Set transaction.

Emits an Import (DCM 110107, via DICOM Instances Transferred) audit event for consumer-side XDS transactions (ITI-43 / RAD-69). The transaction identifier is recorded as an event type code.

Parameters
transactionTransaction identifier (ITI-43 or RAD-69)
source_aeRemote source participant (XDS repository)
dest_aeLocal destination participant (this consumer)
study_uidStudy Instance UID (the study being retrieved)
patient_idPatient ID (optional, empty if unavailable)
sop_instance_uidsReferenced SOP Instance UIDs (may be empty)
successWhether the transaction succeeded

Definition at line 219 of file atna_service_auditor.cpp.

226 {
227
228 if (!enabled_.load(std::memory_order_relaxed)) {
229 return;
230 }
231
232 auto outcome = success ? atna_event_outcome::success
234
235 // XDS-I.b Retrieve Document Set is an Import event
236 // (DICOM Instances Transferred with is_import=true) — the local
237 // actor is receiving a document set from a remote repository.
240 source_ae,
241 "", // source IP (not available at service level)
242 dest_ae,
243 "", // destination IP (not available at service level)
244 study_uid,
245 patient_id,
246 true, // is_import (consumer is receiving)
247 outcome);
248
249 // Tag the event with the specific IHE transaction code so consumers
250 // of the audit log can differentiate ITI-43 from RAD-69.
251 msg.event_type_codes.push_back(
253 ? xds_event_type_iti_43
254 : xds_event_type_rad_69);
255
256 // Add SOP Instance UID references if provided.
257 append_sop_instance_objects(msg, sop_instance_uids);
258
259 send_audit(msg);
260}

References audit_source_id_, kcenon::pacs::security::atna_audit_logger::build_dicom_instances_transferred(), enabled_, iti_43, send_audit(), kcenon::pacs::security::serious_failure, and kcenon::pacs::security::success.

Referenced by kcenon::pacs::services::xds::imaging_document_consumer::retrieve_document().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ events_failed()

size_t kcenon::pacs::security::atna_service_auditor::events_failed ( ) const
nodiscardnoexcept

Get the number of audit event send failures.

Definition at line 282 of file atna_service_auditor.cpp.

282 {
283 return events_failed_.load(std::memory_order_relaxed);
284}

References events_failed_.

◆ events_sent()

size_t kcenon::pacs::security::atna_service_auditor::events_sent ( ) const
nodiscardnoexcept

Get the number of audit events successfully sent.

Definition at line 278 of file atna_service_auditor.cpp.

278 {
279 return events_sent_.load(std::memory_order_relaxed);
280}

References events_sent_.

◆ is_enabled()

bool kcenon::pacs::security::atna_service_auditor::is_enabled ( ) const
nodiscardnoexcept

Check if audit event emission is enabled.

Returns
true if enabled

Definition at line 270 of file atna_service_auditor.cpp.

270 {
271 return enabled_.load(std::memory_order_relaxed);
272}

References enabled_.

◆ operator=() [1/2]

atna_service_auditor & kcenon::pacs::security::atna_service_auditor::operator= ( atna_service_auditor && other)
noexcept

Definition at line 28 of file atna_service_auditor.cpp.

29 {
30 if (this != &other) {
31 audit_source_id_ = std::move(other.audit_source_id_);
32 transport_ = std::move(other.transport_);
33 enabled_.store(other.enabled_.load(std::memory_order_relaxed),
34 std::memory_order_relaxed);
35 events_sent_.store(other.events_sent_.load(std::memory_order_relaxed),
36 std::memory_order_relaxed);
37 events_failed_.store(
38 other.events_failed_.load(std::memory_order_relaxed),
39 std::memory_order_relaxed);
40 }
41 return *this;
42}

References kcenon::pacs::security::other.

◆ operator=() [2/2]

atna_service_auditor & kcenon::pacs::security::atna_service_auditor::operator= ( const atna_service_auditor & )
delete

◆ reset_statistics()

void kcenon::pacs::security::atna_service_auditor::reset_statistics ( )
noexcept

Reset statistics counters.

Definition at line 286 of file atna_service_auditor.cpp.

286 {
287 events_sent_.store(0, std::memory_order_relaxed);
288 events_failed_.store(0, std::memory_order_relaxed);
289}

References events_failed_, and events_sent_.

◆ send_audit()

void kcenon::pacs::security::atna_service_auditor::send_audit ( const atna_audit_message & message)
private

Send an audit message via syslog transport.

Converts the message to XML and sends it. Updates statistics.

Parameters
messageThe audit message to send

Definition at line 315 of file atna_service_auditor.cpp.

315 {
316 auto xml = atna_audit_logger::to_xml(message);
317 auto result = transport_.send(xml);
318
319 if (result.is_ok()) {
320 events_sent_.fetch_add(1, std::memory_order_relaxed);
321 } else {
322 events_failed_.fetch_add(1, std::memory_order_relaxed);
323 }
324}
static std::string to_xml(const atna_audit_message &message)
Serialize an audit message to RFC 3881 XML.
kcenon::pacs::VoidResult send(const std::string &xml_message)
Send an RFC 3881 XML audit message via Syslog.

References events_failed_, events_sent_, kcenon::pacs::security::atna_syslog_transport::send(), kcenon::pacs::security::atna_audit_logger::to_xml(), and transport_.

Referenced by audit_authentication(), audit_instance_stored(), audit_query(), audit_security_alert(), audit_xds_provide_and_register(), audit_xds_retrieve(), and submit_audit_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_enabled()

void kcenon::pacs::security::atna_service_auditor::set_enabled ( bool enabled)
noexcept

Enable or disable audit event emission.

When disabled, audit methods return immediately without generating or sending any messages. Useful for testing or low-resource environments.

Parameters
enabledtrue to enable, false to disable

Definition at line 266 of file atna_service_auditor.cpp.

266 {
267 enabled_.store(enabled, std::memory_order_relaxed);
268}

◆ submit_audit_message()

void kcenon::pacs::security::atna_service_auditor::submit_audit_message ( const atna_audit_message & message)

Emit a pre-built RFC 3881 audit message.

Allows callers with their own transaction-specific event code and EventTypeCode vocabulary (e.g., the XDS.b actors under xds_audit_events.h) to route a fully-formed message through the auditor's serialized transport and statistics path instead of opening a second socket. Respects the enabled() flag and updates the same events_sent/events_failed counters as the high-level audit_* methods.

Thread safety: safe to call concurrently - delegation is through the same private send_audit() path as all other audit_* methods.

Parameters
messageThe audit message to serialize and send.
See also
Issue #1131

Definition at line 303 of file atna_service_auditor.cpp.

304 {
305 if (!enabled_.load(std::memory_order_relaxed)) {
306 return;
307 }
308 send_audit(message);
309}

References enabled_, and send_audit().

Here is the call graph for this function:

◆ transport()

const atna_syslog_transport & kcenon::pacs::security::atna_service_auditor::transport ( ) const
nodiscardnoexcept

Get the underlying transport (for advanced use)

Definition at line 299 of file atna_service_auditor.cpp.

299 {
300 return transport_;
301}

References transport_.

Member Data Documentation

◆ audit_source_id_

std::string kcenon::pacs::security::atna_service_auditor::audit_source_id_
private

◆ enabled_

std::atomic<bool> kcenon::pacs::security::atna_service_auditor::enabled_ {true}
private

◆ events_failed_

std::atomic<size_t> kcenon::pacs::security::atna_service_auditor::events_failed_ {0}
private

Definition at line 310 of file atna_service_auditor.h.

310{0};

Referenced by events_failed(), reset_statistics(), and send_audit().

◆ events_sent_

std::atomic<size_t> kcenon::pacs::security::atna_service_auditor::events_sent_ {0}
private

Statistics.

Definition at line 309 of file atna_service_auditor.h.

309{0};

Referenced by events_sent(), reset_statistics(), and send_audit().

◆ transport_

atna_syslog_transport kcenon::pacs::security::atna_service_auditor::transport_
private

Syslog transport for sending audit messages.

Definition at line 303 of file atna_service_auditor.h.

Referenced by send_audit(), and transport().


The documentation for this class was generated from the following files: