19 std::ostringstream xml;
21 xml <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
22 xml <<
"<AuditMessage>\n";
25 xml <<
" <EventIdentification"
26 <<
" EventActionCode=\""
29 <<
" EventOutcomeIndicator=\""
39 xml <<
" </EventIdentification>\n";
43 xml <<
" <ActiveParticipant"
44 <<
" UserID=\"" <<
xml_escape(ap.user_id) <<
"\"";
46 if (!ap.alternative_user_id.empty()) {
47 xml <<
" AlternativeUserID=\""
51 if (!ap.user_name.empty()) {
52 xml <<
" UserName=\"" <<
xml_escape(ap.user_name) <<
"\"";
55 xml <<
" UserIsRequestor=\""
56 << (ap.user_is_requestor ?
"true" :
"false") <<
"\"";
58 if (!ap.network_access_point_id.empty()) {
59 xml <<
" NetworkAccessPointID=\""
60 <<
xml_escape(ap.network_access_point_id) <<
"\""
61 <<
" NetworkAccessPointTypeCode=\""
62 <<
static_cast<int>(ap.network_access_point_type) <<
"\"";
65 if (ap.role_id_codes.empty()) {
69 for (
const auto& role : ap.role_id_codes) {
70 xml <<
" <RoleIDCode "
73 xml <<
" </ActiveParticipant>\n";
78 xml <<
" <AuditSourceIdentification"
79 <<
" AuditSourceID=\""
83 xml <<
" AuditEnterpriseSiteID=\""
92 xml <<
" <AuditSourceTypeCode code=\""
93 <<
static_cast<int>(type_code) <<
"\"/>\n";
95 xml <<
" </AuditSourceIdentification>\n";
100 xml <<
" <ParticipantObjectIdentification"
101 <<
" ParticipantObjectTypeCode=\""
102 <<
static_cast<int>(po.object_type) <<
"\""
103 <<
" ParticipantObjectTypeCodeRole=\""
104 <<
static_cast<int>(po.object_role) <<
"\"";
106 if (!po.object_id.empty()) {
107 xml <<
" ParticipantObjectID=\""
111 if (!po.object_name.empty()) {
112 xml <<
" ParticipantObjectName=\""
116 bool has_children = !po.object_id_type_code.code.empty() ||
117 !po.object_query.empty() ||
118 !po.object_details.empty();
125 if (!po.object_id_type_code.code.empty()) {
126 xml <<
" <ParticipantObjectIDTypeCode "
131 if (!po.object_query.empty()) {
132 xml <<
" <ParticipantObjectQuery>"
134 <<
"</ParticipantObjectQuery>\n";
137 for (
const auto& detail : po.object_details) {
138 xml <<
" <ParticipantObjectDetail"
139 <<
" type=\"" <<
xml_escape(detail.type) <<
"\""
140 <<
" value=\"" <<
xml_escape(detail.value) <<
"\"/>\n";
143 xml <<
" </ParticipantObjectIdentification>\n";
147 xml <<
"</AuditMessage>\n";
157 const std::string& source_id,
158 const std::string& app_name,
172 ap.user_id = app_name;
173 ap.user_is_requestor =
false;
183 const std::string& source_id,
184 const std::string& user_id,
185 const std::string& user_ip,
186 const std::string& study_uid,
187 const std::string& patient_id,
197 ap.user_id = user_id;
198 ap.user_is_requestor =
true;
199 ap.network_access_point_id = user_ip;
206 if (!study_uid.empty()) {
216 if (!patient_id.empty()) {
229 const std::string& source_id,
230 const std::string& source_ae,
231 const std::string& source_ip,
232 const std::string& dest_ae,
233 const std::string& dest_ip,
234 const std::string& study_uid,
235 const std::string& patient_id,
267 if (!study_uid.empty()) {
277 if (!patient_id.empty()) {
290 const std::string& source_id,
291 const std::string& user_id,
292 const std::string& user_ip,
293 const std::string& study_uid,
294 const std::string& patient_id,
304 ap.user_id = user_id;
305 ap.user_is_requestor =
true;
306 ap.network_access_point_id = user_ip;
321 if (!patient_id.empty()) {
334 const std::string& source_id,
335 const std::string& user_id,
336 const std::string& user_ip,
337 const std::string& alert_description,
347 ap.user_id = user_id;
348 ap.user_is_requestor =
true;
349 ap.network_access_point_id = user_ip;
367 const std::string& source_id,
368 const std::string& user_id,
369 const std::string& user_ip,
382 ap.user_id = user_id;
383 ap.user_is_requestor =
true;
384 ap.network_access_point_id = user_ip;
394 const std::string& source_id,
395 const std::string& user_id,
396 const std::string& user_ip,
397 const std::string& query_data,
398 const std::string& patient_id,
408 ap.user_id = user_id;
409 ap.user_is_requestor =
true;
410 ap.network_access_point_id = user_ip;
426 if (!patient_id.empty()) {
439 const std::string& source_id,
440 const std::string& user_id,
441 const std::string& user_ip,
442 const std::string& dest_id,
443 const std::string& study_uid,
444 const std::string& patient_id,
472 if (!study_uid.empty()) {
482 if (!patient_id.empty()) {
500 result.reserve(str.size());
504 case '&': result +=
"&";
break;
505 case '<': result +=
"<";
break;
506 case '>': result +=
">";
break;
507 case '"': result +=
""";
break;
508 case '\'': result +=
"'";
break;
509 default: result += c;
break;
517 std::chrono::system_clock::time_point tp) {
519 auto time_t_val = std::chrono::system_clock::to_time_t(tp);
520 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
521 tp.time_since_epoch()) % 1000;
525 gmtime_s(&tm_val, &time_t_val);
527 gmtime_r(&time_t_val, &tm_val);
530 std::ostringstream oss;
531 oss << std::put_time(&tm_val,
"%Y-%m-%dT%H:%M:%S")
532 <<
'.' << std::setfill(
'0') << std::setw(3) << ms.count()
541 std::ostringstream oss;
542 oss <<
"csd-code=\"" <<
xml_escape(cv.code) <<
"\"";
544 if (!cv.code_system_name.empty()) {
545 oss <<
" codeSystemName=\"" <<
xml_escape(cv.code_system_name) <<
"\"";
548 if (!cv.display_name.empty()) {
549 oss <<
" originalText=\"" <<
xml_escape(cv.display_name) <<
"\"";
IHE ATNA-compliant audit message generator (RFC 3881 XML format)
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)
static std::string format_coded_value_attrs(const atna_coded_value &cv)
static std::string to_xml(const atna_audit_message &message)
Serialize an audit message to RFC 3881 XML.
static atna_audit_message build_study_deleted(const std::string &source_id, const std::string &user_id, const std::string &user_ip, const std::string &study_uid, const std::string &patient_id="", atna_event_outcome outcome=atna_event_outcome::success)
Build Study Deleted audit message.
static std::string xml_escape(const std::string &str)
static atna_audit_message build_application_activity(const std::string &source_id, const std::string &app_name, bool is_start, atna_event_outcome outcome=atna_event_outcome::success)
Build Application Activity audit message (start/stop)
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)
static std::string format_datetime(std::chrono::system_clock::time_point tp)
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.
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)
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)
static atna_audit_message build_dicom_instances_accessed(const std::string &source_id, const std::string &user_id, const std::string &user_ip, const std::string &study_uid, const std::string &patient_id="", atna_event_outcome outcome=atna_event_outcome::success)
Build DICOM Instances Accessed audit message (C-FIND, QIDO-RS)
const atna_coded_value dicom_instances_accessed
DICOM Instances Accessed (110103)
const atna_coded_value query
Query (110112)
const atna_coded_value user_authentication
User Authentication (110114)
const atna_coded_value security_alert
Security Alert (110113)
const atna_coded_value dicom_instances_transferred
DICOM Instances Transferred (110104)
const atna_coded_value dicom_study_deleted
DICOM Study Deleted (110105)
const atna_coded_value data_export
Export (110106)
const atna_coded_value application_activity
Application Activity (110100) — Start/Stop.
const atna_coded_value login
Login.
const atna_coded_value application_start
Application Start.
const atna_coded_value application_stop
Application Stop.
const atna_coded_value logout
Logout.
const atna_coded_value sop_class_uid
SOP Class UID.
const atna_coded_value study_instance_uid
Study Instance UID.
const atna_coded_value patient_number
Patient Number (RFC 3881 defined)
const atna_coded_value application
Application (110150)
const atna_coded_value source
Source Role ID (110153)
const atna_coded_value destination
Destination Role ID (110152)
atna_event_outcome
Outcome of the audited event.
An active participant in the audit event.
std::string network_access_point_id
Network access point (hostname or IP)
std::string user_id
User or process identifier.
bool user_is_requestor
Whether this participant initiated the event.
std::vector< atna_coded_value > role_id_codes
Role(s) of this participant.
atna_network_access_type network_access_point_type
Type of network access point.
Complete RFC 3881 audit message.
atna_event_action event_action
Action that triggered the event.
std::vector< atna_participant_object > participant_objects
Participant objects (patients/studies/queries)
std::vector< atna_coded_value > event_type_codes
Event type codes for sub-classification.
atna_event_outcome event_outcome
Outcome of the event.
atna_coded_value event_id
Event ID coded value (e.g., DCM 110114 = UserAuthentication)
atna_audit_source audit_source
Audit source identification.
std::vector< atna_active_participant > active_participants
Active participants (users/processes)
std::chrono::system_clock::time_point event_date_time
When the event occurred.
std::string audit_source_id
Unique identifier for the audit source.
std::vector< uint8_t > audit_source_type_codes
Audit source type codes (optional)
std::string audit_enterprise_site_id
Enterprise site identifier (optional)
A coded value with code, code system name, and display name.
An object (patient, study, query) involved in the event.
std::string object_id
Object identifier (e.g., Patient ID, Study UID)
std::string object_query
Query data (base64 encoded, for query events)
atna_object_role object_role
Role of the object in the event.
atna_object_type object_type
Object type.
atna_coded_value object_id_type_code
Object ID type code.
std::string object_name
Human-readable object name.