Kulcare Docs
DeveloperAuthentication Documentation04 featuresAudit_trail

Audit Trail System

Overview

The audit trail system provides comprehensive logging and tracking of all user activities within the KulCare platform. It captures detailed information about user actions, system events, and data modifications to ensure compliance, security, and operational transparency.

Table of Contents

Architecture

The audit trail system follows a modular architecture integrated with KulCare's parts-based system:

Data Models

SystemLog

The primary model for storing audit trail events:

class SystemLog < ApplicationRecord
  # Core audit event associations
  belongs_to :owner, polymorphic: true, optional: true      # User who performed action
  belongs_to :trackable, polymorphic: true, optional: true  # Object being tracked
  belongs_to :clinic, optional: true                        # Associated clinic
  belongs_to :clinic_patient, optional: true               # Associated clinic patient
  belongs_to :patient, optional: true                      # Associated patient

  # Key scopes for filtering
  scope :audit_events, -> { where(key: audit_event_keys) }
  scope :for_clinic, ->(clinic_id) { where(clinic_id: clinic_id) }
  scope :for_patient_any, ->(patient_id) { ... }
  scope :date_range, ->(start_date, end_date) { ... }
  scope :by_key, ->(key) { where(key: key) }
  scope :successful, -> { where(event_status: SUCCESS) }
  scope :failed, -> { where(event_status: [FAILURE, UNAUTHORIZED]) }
end

Database Schema

CREATE TABLE system_logs (
  id BIGINT PRIMARY KEY,
  key VARCHAR NOT NULL,                    -- Event type identifier
  owner_id BIGINT,                        -- User who performed action
  owner_type VARCHAR,                     -- User model type
  trackable_id BIGINT,                    -- Associated object ID
  trackable_type VARCHAR,                 -- Associated object type
  clinic_id BIGINT,                       -- Clinic context
  clinic_patient_id BIGINT,               -- Clinic patient context
  patient_id BIGINT,                      -- Patient context
  parameters JSONB,                       -- Additional event data
  description TEXT,                       -- Human-readable description
  event_status VARCHAR DEFAULT 'SUCCESS', -- SUCCESS/FAILURE/UNAUTHORIZED
  user_role VARCHAR,                      -- User role at time of action
  ip_address VARCHAR,                     -- User IP address
  user_agent TEXT,                        -- Browser/device information
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL,
  
  -- Indexes for performance
  INDEX idx_system_logs_audit (key, clinic_id, created_at),
  INDEX idx_system_logs_patient (patient_id, clinic_patient_id),
  INDEX idx_system_logs_owner (owner_type, owner_id)
);

ClinicExportFile

Manages audit trail export requests and file generation:

class ClinicExportFile < ApplicationRecord
  belongs_to :clinic
  belongs_to :user
  
  # Export types
  EXPORT_TYPES = %w[AUDIT_TRAIL_REPORT PATIENT_RECORDS_EXPORT]
  
  # Status values
  STATUS_VALUES = %w[PROCESSING FILE_GENERATED FAILED FILE_REMOVED]
end

Key Components

Controllers

AuditTrailsController

Handles clinic-specific audit trail requests with multi-tenancy support:

class AuditTrailsController < ApiController
  # GET /clinics/:clinic_id/audit_trails
  def index
    result = AuditTrails::Fetch.call(params)
    render_result(result, result.success? ? AuditTrailSerializer : nil)
  end

  # POST /clinics/:clinic_id/audit_trails/export
  def export
    result = AuditTrails::Export.call(params)
    render_result result
  end
end

AdminAuditTrailsController

Provides admin-level access across all clinics:

class AdminAuditTrailsController < ApiController
  # GET /admin/audit_trails
  def index
    result = AdminAuditTrailActions::Fetch.call(params)
    render_result(result, result.success? ? AuditTrailSerializer : nil)
  end
end

Interactors

AuditTrails::Fetch

Core business logic for fetching and filtering audit trails:

Key features:

  • Multi-tenant security: Always scopes to authorized clinic
  • Flexible filtering: Date range, user, event type, status, patient
  • Performance optimization: Efficient pagination and indexing
  • Data validation: Input sanitization and error handling

AuditTrails::Export

Manages asynchronous export of audit data:

module AuditTrails
  class Export < BaseInteractor
    def call
      validate_clinic
      validate_export_request    # Date validation, 90-day limit
      save_log                  # Create export record
      queue_export_job          # Async processing
    end
  end
end

Background Jobs

GenerateAuditTrail

Handles the heavy lifting of CSV generation and file management:

Key features:

  • Memory efficiency: Streaming for large datasets (>50k records)
  • Security: Secure file paths, sanitized filenames, restricted permissions
  • Monitoring: Progress logging and error handling
  • Cleanup: Automatic file deletion after 7 days

API Endpoints

Clinic-Level Audit Trails

GET /clinics/:clinic_id/audit_trails

Fetch audit trails for a specific clinic with filtering and pagination.

Parameters:

{
  "start_date": "2024-01-01",      // Optional: Filter by date range
  "end_date": "2024-01-31",        // Optional: Filter by date range
  "user_id": "12345",              // Optional: Filter by user
  "event_key": "patient.create",   // Optional: Filter by event type
  "status": "success",             // Optional: success/failure/failed
  "patient_id": "67890",           // Optional: Filter by patient
  "page": 1,                       // Optional: Page number (default: 1)
  "per_page": 50                   // Optional: Records per page (max: 200)
}

Response:

{
  "success": true,
  "message": "Audit trails fetched successfully",
  "data": {
    "audit_trails": [
      {
        "id": 12345,
        "timestamp": "2024-01-15T10:30:00Z",
        "key": "patient.create",
        "event_status": "SUCCESS",
        "description": "Patient record created",
        "user": {
          "id": 789,
          "name": "Dr. John Smith",
          "type": "Doctor"
        },
        "patient": {
          "id": 456,
          "name": "Jane Doe"
        },
        "resource": {
          "type": "Patient",
          "id": 456
        },
        "ip_address": "192.168.1.100",
        "user_agent": "Mozilla/5.0...",
        "device_type": "desktop",
        "parameters": {
          "patient_age": 35,
          "patient_gender": "female"
        }
      }
    ],
    "total_pages": 10,
    "total_records": 500
  }
}

POST /clinics/:clinic_id/audit_trails/export

Generate and export audit trail report as CSV.

Parameters:

{
  "start_date": "2024-01-01",      // Optional: Export date range
  "end_date": "2024-01-31",        // Optional: Export date range  
  "event_key": "patient.create"    // Optional: Filter by event type
}

Response:

{
  "success": true,
  "message": "Audit trail export enqueued",
  "data": {
    "export_id": 123,
    "estimated_completion": "2024-01-15T10:35:00Z"
  }
}

Patient-Specific Audit Trails

GET /clinics/:clinic_id/patients/:patient_id/audit_trails

Fetch audit trails specific to a patient.

Admin-Level Audit Trails

GET /admin/audit_trails

Admin access to audit trails across all clinics (requires admin role).

Event Types

Patient Management Events

PATIENT_VIEW = 'patient.view'                    # Patient record accessed
PATIENT_UPDATE = 'patient.update'                # Patient information modified
PATIENT_CREATE = 'patient.create'                # New patient registered
PATIENT_DELETE = 'patient.delete'                # Patient record deleted
PATIENT_MEDICAL_RECORD_CREATE = 'patient.medical_record.create'
PATIENT_VITAL_CREATE = 'patient.vital.create'
PATIENT_MEDICATION_CREATE = 'patient.medication.create'

Appointment & Booking Events

APPOINTMENT_BOOK = 'appointment.book'             # New appointment scheduled
APPOINTMENT_CANCEL = 'appointment.cancel'        # Appointment cancelled
APPOINTMENT_UPDATE = 'appointment.update'        # Appointment modified

Careplan & Prescription Events

CAREPLAN_UPDATE = 'careplan.update'              # Treatment plan modified
CAREPLAN_SEND = 'careplan.send'                  # Prescription sent to patient
CAREPLAN_EMAIL_SEND = 'careplan.email_send'      # Prescription emailed
CAREPLAN_PRINT = 'careplan.print'                # Prescription printed

Billing & Payment Events

BILL_CREATE = 'bill.create'                      # Invoice generated
BILL_DELETE = 'bill.delete'                      # Invoice deleted
PAYMENT_CREATE = 'payment.create'                # Payment recorded
PAYMENT_UPDATE = 'payment.update'                # Payment modified
PAYMENT_REFUND = 'payment.refund'                # Payment refunded

Authentication Events

AUTH_LOGIN = 'auth.login'                        # Successful login
AUTH_LOGOUT = 'auth.logout'                      # User logout
AUTH_FAILED_LOGIN = 'auth.failed_login'          # Failed login attempt
AUTH_REGISTRATION = 'auth.registration'          # New user registration

Administrative Events

AUDIT_EXPORT = 'audit.export'                    # Audit trail exported

Security Features

Access Control

  • Role-based authorization: Pundit policies enforce clinic-level access
  • Multi-tenant isolation: Clinic data is strictly isolated
  • Admin oversight: Separate admin endpoints for system-wide monitoring

Data Protection

  • IP address tracking: All actions logged with source IP
  • User agent logging: Device/browser information captured
  • Parameter filtering: Sensitive data (passwords, tokens) excluded from logs
  • Secure export: Generated files have restrictive permissions (0o600)

Compliance Features

  • Immutable logs: Audit records are append-only
  • Comprehensive tracking: All significant user actions logged
  • Data retention: Configurable retention policies
  • Export capabilities: Support for compliance reporting

Export Functionality

Export Process Flow

CSV Export Format

The exported CSV includes these columns:

ColumnDescriptionExample
Date/TimeISO 8601 timestamp2024-01-15T10:30:00Z
UserUser who performed actionDr. John Smith
User RoleRole at time of actiondoctor
Event TypeAction performedpatient.create
PatientAssociated patientJane Doe
ResourceObject being trackedPatient #456
IP AddressSource IP address192.168.1.100
User AgentBrowser/device infoMozilla/5.0...
StatusSuccess/failure statusSUCCESS
DescriptionHuman-readable descriptionPatient record created

Export Security Features

  • File size limits: Maximum 100MB per export
  • Access control: Only authorized clinic users can export their data
  • Secure storage: Files stored with restrictive permissions
  • Automatic cleanup: Files automatically deleted after 7 days
  • Email notification: Users notified when export is ready

Usage Examples

Basic Audit Trail Retrieval

# Get recent audit trails for a clinic
curl -X GET \
  "https://api.kulcare.com/clinics/123/audit_trails?page=1&per_page=50" \
  -H "Authorization: Bearer <token>"

Filtered Query Examples

# Get patient creation events from last 30 days
curl -X GET \
  "https://api.kulcare.com/clinics/123/audit_trails" \
  -G \
  -d "event_key=patient.create" \
  -d "start_date=2024-01-01" \
  -d "end_date=2024-01-31" \
  -H "Authorization: Bearer <token>"

# Get all failed authentication attempts
curl -X GET \
  "https://api.kulcare.com/clinics/123/audit_trails" \
  -G \
  -d "event_key=auth.failed_login" \
  -d "status=failure" \
  -H "Authorization: Bearer <token>"

# Get audit trails for specific patient
curl -X GET \
  "https://api.kulcare.com/clinics/123/patients/456/audit_trails" \
  -H "Authorization: Bearer <token>"

Export Request

# Request audit trail export for date range
curl -X POST \
  "https://api.kulcare.com/clinics/123/audit_trails/export" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{
    "start_date": "2024-01-01",
    "end_date": "2024-01-31",
    "event_key": "patient.create"
  }'

Programming Examples

Creating Audit Logs

# In a controller or interactor
SystemLog.create!(
  key: SystemLog::PATIENT_CREATE,
  owner: current_user,
  trackable: patient,
  clinic: current_clinic,
  patient: patient,
  event_status: SystemLog::SUCCESS,
  description: "Patient record created successfully",
  parameters: {
    patient_age: patient.age,
    patient_gender: patient.gender,
    registration_source: 'web_portal'
  },
  user_role: current_user.role,
  ip_address: request_info[:ip_address],
  user_agent: request_info[:user_agent]
)

Querying Audit Trails

# Get recent login attempts for a clinic
recent_logins = SystemLog.audit_events
                        .for_clinic(clinic.id)
                        .by_key(SystemLog::AUTH_LOGIN)
                        .recent_first
                        .limit(100)

# Get failed actions in date range
failed_actions = SystemLog.audit_events
                         .for_clinic(clinic.id)
                         .failed
                         .date_range(1.week.ago, Time.current)

# Get all patient-related activities
patient_activities = SystemLog.audit_events
                             .for_patient_any(patient.id)
                             .where(key: [
                               SystemLog::PATIENT_VIEW,
                               SystemLog::PATIENT_UPDATE,
                               SystemLog::PATIENT_MEDICAL_RECORD_CREATE
                             ])

Configuration

Application Settings

# config/settings.yml
audit_trail:
  default_per_page: 50          # Default pagination size
  max_per_page: 200             # Maximum records per page
  export_retention_days: 7      # Days to keep export files
  max_export_days: 90          # Maximum date range for exports
  large_dataset_threshold: 50000 # Threshold for streaming exports

Database Configuration

Ensure proper indexing for performance:

-- Essential indexes for audit trail queries
CREATE INDEX CONCURRENTLY idx_system_logs_clinic_date 
  ON system_logs (clinic_id, created_at DESC);

CREATE INDEX CONCURRENTLY idx_system_logs_patient_activities 
  ON system_logs (patient_id, clinic_patient_id, created_at DESC);

CREATE INDEX CONCURRENTLY idx_system_logs_audit_keys 
  ON system_logs (key) WHERE key IN ('patient.view', 'patient.create', ...);

Background Job Configuration

# config/application.rb
config.active_job.queue_adapter = :sidekiq

# Audit export job configuration
config.active_job.queue_name_prefix = Rails.env

Email Configuration

# Configure audit trail mailer
class AuditTrailMailer < ActionMailer::Base
  default from: Settings.mailer.default_from

  def export_ready(export_file)
    @export_file = export_file
    @clinic = export_file.clinic
    
    mail(
      to: export_file.user.email,
      subject: "Audit Trail Export Ready - #{@clinic.name}"
    )
  end
end

Monitoring and Alerting

Set up monitoring for audit trail system health:

# Monitor export job failures
class GenerateAuditTrail < ActiveJob::Base
  rescue_from StandardError do |exception|
    # Log to monitoring system
    Rails.logger.error "Audit export failed: #{exception.message}"
    
    # Alert administrators
    AdminNotificationService.audit_export_failed(exception, export_file)
    
    # Update export record
    export_file.update!(
      status: 'FAILED',
      params: export_file.params.merge('error_message' => exception.message)
    )
  end
end


Last updated: January 2024

On this page