20std::string escape_json(std::string_view s) {
22 result.reserve(s.size() + 10);
25 case '"': result +=
"\\\"";
break;
26 case '\\': result +=
"\\\\";
break;
27 case '\n': result +=
"\\n";
break;
28 case '\r': result +=
"\\r";
break;
29 case '\t': result +=
"\\t";
break;
30 default: result += c;
break;
37size_t skip_ws(std::string_view s,
size_t pos) {
38 while (pos < s.size() && (s[pos] ==
' ' || s[pos] ==
'\t' ||
39 s[pos] ==
'\n' || s[pos] ==
'\r')) {
48 while (pos < s.size() && s[pos] !=
'"') {
49 if (s[pos] ==
'\\' && pos + 1 < s.size()) {
52 case '"': result +=
'"';
break;
53 case '\\': result +=
'\\';
break;
54 case 'n': result +=
'\n';
break;
55 case 'r': result +=
'\r';
break;
56 case 't': result +=
'\t';
break;
57 default: result += s[pos];
break;
64 if (pos < s.size()) ++pos;
70std::string find_json_value(std::string_view json, std::string_view key) {
72 std::string needle =
"\"" + std::string(key) +
"\"";
73 auto key_pos = json.find(needle);
74 if (key_pos == std::string_view::npos)
return "";
76 size_t pos = key_pos + needle.size();
77 pos = skip_ws(json, pos);
78 if (pos >= json.size() || json[pos] !=
':')
return "";
80 pos = skip_ws(json, pos);
81 if (pos >= json.size())
return "";
83 if (json[pos] ==
'"') {
91 while (pos < json.size() && json[pos] !=
',' && json[pos] !=
'}' &&
92 json[pos] !=
' ' && json[pos] !=
'\n' && json[pos] !=
'\r') {
95 return std::string(json.substr(start, pos - start));
99std::string find_nested_value(std::string_view json,
100 std::string_view section,
101 std::string_view key) {
103 std::string needle =
"\"" + std::string(section) +
"\"";
104 auto sec_pos = json.find(needle);
105 if (sec_pos == std::string_view::npos)
return "";
108 auto brace_pos = json.find(
'{', sec_pos + needle.size());
109 if (brace_pos == std::string_view::npos)
return "";
113 size_t end_pos = brace_pos + 1;
114 while (end_pos < json.size() && depth > 0) {
115 if (json[end_pos] ==
'{') ++depth;
116 else if (json[end_pos] ==
'}') --depth;
120 auto sub_json = json.substr(brace_pos, end_pos - brace_pos);
121 return find_json_value(sub_json, key);
124bool parse_bool(
const std::string& val,
bool default_val) {
125 if (val ==
"true")
return true;
126 if (val ==
"false")
return false;
130uint16_t parse_uint16(
const std::string& val, uint16_t default_val) {
131 if (val.empty())
return default_val;
133 auto v = std::stoul(val);
134 if (v > 65535)
return default_val;
135 return static_cast<uint16_t
>(v);
165 std::ostringstream oss;
166 oss << R
"({"enabled":)" << (config.enabled ? "true" :
"false")
168 << R"(","transport":{"protocol":")"
174 << R"(","client_cert_path":")"
176 << R"(","client_key_path":")"
178 << R"(","verify_server":)"
180 << R
"(},"audit_storage":)"
182 << R
"(,"audit_query":)"
184 << R
"(,"audit_authentication":)"
186 << R
"(,"audit_security_alerts":)"
200 auto enabled_val = find_json_value(json_str,
"enabled");
201 if (!enabled_val.empty()) {
205 auto source_id = find_json_value(json_str,
"audit_source_id");
206 if (!source_id.empty()) {
211 auto storage_val = find_json_value(json_str,
"audit_storage");
212 if (!storage_val.empty()) {
216 auto query_val = find_json_value(json_str,
"audit_query");
217 if (!query_val.empty()) {
221 auto auth_val = find_json_value(json_str,
"audit_authentication");
222 if (!auth_val.empty()) {
227 auto alerts_val = find_json_value(json_str,
"audit_security_alerts");
228 if (!alerts_val.empty()) {
234 auto proto = find_nested_value(json_str,
"transport",
"protocol");
235 if (!proto.empty()) {
239 auto host = find_nested_value(json_str,
"transport",
"host");
244 auto port = find_nested_value(json_str,
"transport",
"port");
250 auto app = find_nested_value(json_str,
"transport",
"app_name");
255 auto ca = find_nested_value(json_str,
"transport",
"ca_cert_path");
260 auto cert = find_nested_value(json_str,
"transport",
"client_cert_path");
265 auto key = find_nested_value(json_str,
"transport",
"client_key_path");
270 auto verify = find_nested_value(json_str,
"transport",
"verify_server");
271 if (!verify.empty()) {
287 result.
valid =
false;
288 result.
errors.emplace_back(
"audit_source_id must not be empty");
292 result.
valid =
false;
293 result.
errors.emplace_back(
"transport.host must not be empty");
297 result.
valid =
false;
298 result.
errors.emplace_back(
299 "transport.port must be in range 1-65535");
304 result.
valid =
false;
305 result.
errors.emplace_back(
306 "transport.ca_cert_path is required for TLS protocol");
307 }
else if (!std::filesystem::exists(
309 result.
valid =
false;
310 result.
errors.emplace_back(
311 "transport.ca_cert_path file does not exist: " +
317 result.
valid =
false;
318 result.
errors.emplace_back(
319 "transport.client_cert_path file does not exist: " +
325 result.
valid =
false;
326 result.
errors.emplace_back(
327 "transport.client_key_path file does not exist: " +
Configuration management for ATNA audit logging.
std::optional< std::string > extract_string(const std::string &json, const std::string &key)
Simple JSON value extractor (for basic parsing)
syslog_transport_protocol
Syslog transport protocol.
@ udp
UDP (RFC 5426) — Fire-and-forget.
@ tls
TLS over TCP (RFC 5425) — Secure.
std::string to_json(const atna_config &config)
Serialize an atna_config to a JSON string.
atna_config make_default_atna_config()
Create a default ATNA configuration.
atna_config parse_atna_config(std::string_view json_str)
Parse an atna_config from a JSON string.
atna_config_validation validate(const atna_config &config)
Validate an ATNA configuration.
Validation result for ATNA configuration.
std::vector< std::string > errors
Configuration for ATNA audit logging.
bool audit_security_alerts
Audit security alert events (access denied, etc.)
syslog_transport_config transport
Syslog transport configuration.
bool enabled
Master enable/disable for ATNA audit logging.
bool audit_storage
Audit C-STORE events (DICOM Instances Transferred)
bool audit_query
Audit C-FIND events (Query)
bool audit_authentication
Audit login/logout events (User Authentication)
std::string audit_source_id
Audit source identifier (e.g., "PACS_SYSTEM_01")
std::string client_key_path
Path to client private key file (mutual TLS)
std::string ca_cert_path
Path to CA certificate file for server verification.
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.
std::string app_name
Application name in Syslog header.
std::string client_cert_path
Path to client certificate file (mutual TLS)
bool verify_server
Whether to verify server certificate (disable only for testing)