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

#include <audit_repository.h>

Collaboration diagram for kcenon::pacs::storage::audit_repository:
Collaboration graph

Public Member Functions

 audit_repository (sqlite3 *db)
 
 ~audit_repository ()
 
 audit_repository (const audit_repository &)=delete
 
auto operator= (const audit_repository &) -> audit_repository &=delete
 
 audit_repository (audit_repository &&) noexcept
 
auto operator= (audit_repository &&) noexcept -> audit_repository &
 
auto add_audit_log (const audit_record &record) -> Result< int64_t >
 
auto query_audit_log (const audit_query &query) const -> Result< std::vector< audit_record > >
 
auto find_audit_by_pk (int64_t pk) const -> std::optional< audit_record >
 
auto audit_count () const -> Result< size_t >
 
auto cleanup_old_audit_logs (std::chrono::hours age) -> Result< size_t >
 

Private Member Functions

auto parse_audit_row (void *stmt) const -> audit_record
 

Static Private Member Functions

static auto to_like_pattern (std::string_view pattern) -> std::string
 
static auto parse_timestamp (const std::string &str) -> std::chrono::system_clock::time_point
 

Private Attributes

sqlite3 * db_ {nullptr}
 

Detailed Description

Definition at line 83 of file audit_repository.h.

Constructor & Destructor Documentation

◆ audit_repository() [1/3]

kcenon::pacs::storage::audit_repository::audit_repository ( sqlite3 * db)
explicit

Definition at line 343 of file audit_repository.cpp.

◆ ~audit_repository()

kcenon::pacs::storage::audit_repository::~audit_repository ( )
default

◆ audit_repository() [2/3]

kcenon::pacs::storage::audit_repository::audit_repository ( const audit_repository & )
delete

◆ audit_repository() [3/3]

kcenon::pacs::storage::audit_repository::audit_repository ( audit_repository && other)
noexcept

Definition at line 347 of file audit_repository.cpp.

348 : db_(other.db_) {
349 other.db_ = nullptr;
350}

Member Function Documentation

◆ add_audit_log()

auto kcenon::pacs::storage::audit_repository::add_audit_log ( const audit_record & record) -> Result<int64_t>
nodiscard

Definition at line 406 of file audit_repository.cpp.

407 {
408 const char* sql = R"(
409 INSERT INTO audit_log (
410 event_type, outcome, user_id, source_ae, target_ae,
411 source_ip, patient_id, study_uid, message, details
412 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
413 )";
414
415 sqlite3_stmt* stmt = nullptr;
416 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
417 if (rc != SQLITE_OK) {
418 return make_error<int64_t>(
419 rc,
420 kcenon::pacs::compat::format("Failed to prepare audit insert: {}",
421 sqlite3_errmsg(db_)),
422 "storage");
423 }
424
425 sqlite3_bind_text(stmt, 1, record.event_type.c_str(), -1, SQLITE_TRANSIENT);
426 sqlite3_bind_text(stmt, 2, record.outcome.c_str(), -1, SQLITE_TRANSIENT);
427 sqlite3_bind_text(stmt, 3, record.user_id.c_str(), -1, SQLITE_TRANSIENT);
428 sqlite3_bind_text(stmt, 4, record.source_ae.c_str(), -1, SQLITE_TRANSIENT);
429 sqlite3_bind_text(stmt, 5, record.target_ae.c_str(), -1, SQLITE_TRANSIENT);
430 sqlite3_bind_text(stmt, 6, record.source_ip.c_str(), -1, SQLITE_TRANSIENT);
431 sqlite3_bind_text(stmt, 7, record.patient_id.c_str(), -1, SQLITE_TRANSIENT);
432 sqlite3_bind_text(stmt, 8, record.study_uid.c_str(), -1, SQLITE_TRANSIENT);
433 sqlite3_bind_text(stmt, 9, record.message.c_str(), -1, SQLITE_TRANSIENT);
434 sqlite3_bind_text(stmt, 10, record.details.c_str(), -1, SQLITE_TRANSIENT);
435
436 rc = sqlite3_step(stmt);
437 sqlite3_finalize(stmt);
438
439 if (rc != SQLITE_DONE) {
440 return make_error<int64_t>(
441 rc,
442 kcenon::pacs::compat::format("Failed to insert audit log: {}",
443 sqlite3_errmsg(db_)),
444 "storage");
445 }
446
447 return sqlite3_last_insert_rowid(db_);
448}

◆ audit_count()

auto kcenon::pacs::storage::audit_repository::audit_count ( ) const -> Result<size_t>
nodiscard

Definition at line 550 of file audit_repository.cpp.

550 {
551 const char* sql = "SELECT COUNT(*) FROM audit_log;";
552 sqlite3_stmt* stmt = nullptr;
553 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
554 if (rc != SQLITE_OK) {
555 return make_error<size_t>(
556 rc,
557 kcenon::pacs::compat::format("Failed to prepare query: {}",
558 sqlite3_errmsg(db_)),
559 "storage");
560 }
561
562 size_t count = 0;
563 if (sqlite3_step(stmt) == SQLITE_ROW) {
564 count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
565 }
566 sqlite3_finalize(stmt);
567 return ok(count);
568}

References db_.

◆ cleanup_old_audit_logs()

auto kcenon::pacs::storage::audit_repository::cleanup_old_audit_logs ( std::chrono::hours age) -> Result<size_t>
nodiscard

Definition at line 570 of file audit_repository.cpp.

571 {
572 auto cutoff = std::chrono::system_clock::now() - age;
573 auto cutoff_time = std::chrono::system_clock::to_time_t(cutoff);
574 std::ostringstream oss;
575 oss << std::put_time(std::gmtime(&cutoff_time), "%Y-%m-%d %H:%M:%S");
576 auto cutoff_str = oss.str();
577
578 const char* sql = "DELETE FROM audit_log WHERE timestamp < ?;";
579 sqlite3_stmt* stmt = nullptr;
580 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
581 if (rc != SQLITE_OK) {
582 return make_error<size_t>(
583 rc,
584 kcenon::pacs::compat::format("Failed to prepare audit cleanup: {}",
585 sqlite3_errmsg(db_)),
586 "storage");
587 }
588
589 sqlite3_bind_text(stmt, 1, cutoff_str.c_str(), -1, SQLITE_TRANSIENT);
590 rc = sqlite3_step(stmt);
591 sqlite3_finalize(stmt);
592
593 if (rc != SQLITE_DONE) {
594 return make_error<size_t>(
595 rc,
596 kcenon::pacs::compat::format("Failed to cleanup old audit logs: {}",
597 sqlite3_errmsg(db_)),
598 "storage");
599 }
600
601 return static_cast<size_t>(sqlite3_changes(db_));
602}

◆ find_audit_by_pk()

auto kcenon::pacs::storage::audit_repository::find_audit_by_pk ( int64_t pk) const -> std::optional<audit_record>
nodiscard

Definition at line 526 of file audit_repository.cpp.

527 {
528 const char* sql =
529 "SELECT audit_pk, event_type, outcome, timestamp, user_id, "
530 "source_ae, target_ae, source_ip, patient_id, study_uid, "
531 "message, details FROM audit_log WHERE audit_pk = ?;";
532
533 sqlite3_stmt* stmt = nullptr;
534 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
535 if (rc != SQLITE_OK) {
536 return std::nullopt;
537 }
538
539 sqlite3_bind_int64(stmt, 1, pk);
540
541 std::optional<audit_record> result;
542 if (sqlite3_step(stmt) == SQLITE_ROW) {
543 result = parse_audit_row(stmt);
544 }
545
546 sqlite3_finalize(stmt);
547 return result;
548}
auto parse_audit_row(void *stmt) const -> audit_record

◆ operator=() [1/2]

auto kcenon::pacs::storage::audit_repository::operator= ( audit_repository && other) -> audit_repository&
noexcept

Definition at line 352 of file audit_repository.cpp.

353 {
354 if (this != &other) {
355 db_ = other.db_;
356 other.db_ = nullptr;
357 }
358 return *this;
359}

◆ operator=() [2/2]

auto kcenon::pacs::storage::audit_repository::operator= ( const audit_repository & ) -> audit_repository &=delete
delete

◆ parse_audit_row()

auto kcenon::pacs::storage::audit_repository::parse_audit_row ( void * stmt) const -> audit_record
nodiscardprivate

Definition at line 387 of file audit_repository.cpp.

387 {
388 auto* stmt = static_cast<sqlite3_stmt*>(stmt_ptr);
389 audit_record record;
390
391 record.pk = sqlite3_column_int64(stmt, 0);
392 record.event_type = get_text(stmt, 1);
393 record.outcome = get_text(stmt, 2);
394 record.timestamp = parse_datetime(get_text(stmt, 3).c_str());
395 record.user_id = get_text(stmt, 4);
396 record.source_ae = get_text(stmt, 5);
397 record.target_ae = get_text(stmt, 6);
398 record.source_ip = get_text(stmt, 7);
399 record.patient_id = get_text(stmt, 8);
400 record.study_uid = get_text(stmt, 9);
401 record.message = get_text(stmt, 10);
402 record.details = get_text(stmt, 11);
403 return record;
404}

◆ parse_timestamp()

auto kcenon::pacs::storage::audit_repository::parse_timestamp ( const std::string & str) -> std::chrono::system_clock::time_point
staticnodiscardprivate

Definition at line 382 of file audit_repository.cpp.

383 {
384 return parse_datetime(str.c_str());
385}

◆ query_audit_log()

auto kcenon::pacs::storage::audit_repository::query_audit_log ( const audit_query & query) const -> Result<std::vector<audit_record>>
nodiscard

Definition at line 450 of file audit_repository.cpp.

451 {
452 std::vector<audit_record> results;
453 std::ostringstream sql;
454 sql << "SELECT audit_pk, event_type, outcome, timestamp, user_id, "
455 << "source_ae, target_ae, source_ip, patient_id, study_uid, "
456 << "message, details FROM audit_log WHERE 1=1";
457
458 std::vector<std::string> params;
459
460 if (query.event_type) {
461 sql << " AND event_type = ?";
462 params.push_back(*query.event_type);
463 }
464 if (query.outcome) {
465 sql << " AND outcome = ?";
466 params.push_back(*query.outcome);
467 }
468 if (query.user_id) {
469 sql << " AND user_id LIKE ?";
470 params.push_back(to_like_pattern(*query.user_id));
471 }
472 if (query.source_ae) {
473 sql << " AND source_ae = ?";
474 params.push_back(*query.source_ae);
475 }
476 if (query.patient_id) {
477 sql << " AND patient_id = ?";
478 params.push_back(*query.patient_id);
479 }
480 if (query.study_uid) {
481 sql << " AND study_uid = ?";
482 params.push_back(*query.study_uid);
483 }
484 if (query.date_from) {
485 sql << " AND date(timestamp) >= date(?)";
486 params.push_back(*query.date_from);
487 }
488 if (query.date_to) {
489 sql << " AND date(timestamp) <= date(?)";
490 params.push_back(*query.date_to);
491 }
492
493 sql << " ORDER BY timestamp DESC";
494
495 if (query.limit > 0) {
496 sql << " LIMIT " << query.limit;
497 }
498 if (query.offset > 0) {
499 sql << " OFFSET " << query.offset;
500 }
501 sql << ";";
502
503 sqlite3_stmt* stmt = nullptr;
504 auto rc = sqlite3_prepare_v2(db_, sql.str().c_str(), -1, &stmt, nullptr);
505 if (rc != SQLITE_OK) {
506 return make_error<std::vector<audit_record>>(
507 rc,
508 kcenon::pacs::compat::format("Failed to prepare query: {}",
509 sqlite3_errmsg(db_)),
510 "storage");
511 }
512
513 for (size_t i = 0; i < params.size(); ++i) {
514 sqlite3_bind_text(stmt, static_cast<int>(i + 1), params[i].c_str(), -1,
515 SQLITE_TRANSIENT);
516 }
517
518 while (sqlite3_step(stmt) == SQLITE_ROW) {
519 results.push_back(parse_audit_row(stmt));
520 }
521
522 sqlite3_finalize(stmt);
523 return ok(std::move(results));
524}
static auto to_like_pattern(std::string_view pattern) -> std::string
const atna_coded_value query
Query (110112)

◆ to_like_pattern()

auto kcenon::pacs::storage::audit_repository::to_like_pattern ( std::string_view pattern) -> std::string
staticnodiscardprivate

Definition at line 361 of file audit_repository.cpp.

362 {
363 std::string result;
364 result.reserve(pattern.size());
365
366 for (char c : pattern) {
367 if (c == '*') {
368 result += '%';
369 } else if (c == '?') {
370 result += '_';
371 } else if (c == '%' || c == '_') {
372 result += '\\';
373 result += c;
374 } else {
375 result += c;
376 }
377 }
378
379 return result;
380}

Member Data Documentation

◆ db_

sqlite3* kcenon::pacs::storage::audit_repository::db_ {nullptr}
private

Definition at line 110 of file audit_repository.h.

110{nullptr};

Referenced by audit_count().


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