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
- Data Models
- Key Components
- API Endpoints
- Event Types
- Security Features
- Export Functionality
- Usage Examples
- Configuration
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]) }
endDatabase 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]
endKey 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
endAdminAuditTrailsController
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
endInteractors
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
endBackground 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 modifiedCareplan & 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 printedBilling & 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 refundedAuthentication 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 registrationAdministrative Events
AUDIT_EXPORT = 'audit.export' # Audit trail exportedSecurity 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:
| Column | Description | Example |
|---|---|---|
| Date/Time | ISO 8601 timestamp | 2024-01-15T10:30:00Z |
| User | User who performed action | Dr. John Smith |
| User Role | Role at time of action | doctor |
| Event Type | Action performed | patient.create |
| Patient | Associated patient | Jane Doe |
| Resource | Object being tracked | Patient #456 |
| IP Address | Source IP address | 192.168.1.100 |
| User Agent | Browser/device info | Mozilla/5.0... |
| Status | Success/failure status | SUCCESS |
| Description | Human-readable description | Patient 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 exportsDatabase 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.envEmail 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
endMonitoring 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
endRelated Documentation
- Authentication System - User authentication and authorization
- Database Schema - Complete database design
- Multi-Profile System - Multi-tenancy architecture
- Development Guide - Testing procedures
Last updated: January 2024