mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.6 KiB
128 lines
4.6 KiB
# frozen_string_literal: true
|
|
|
|
module Api
|
|
module V1
|
|
module Admin
|
|
# SecurityEventsController
|
|
# Handles security event monitoring and audit trail
|
|
# Implements export capability for compliance
|
|
class SecurityEventsController < ApiBaseController
|
|
# Layer 3: Authorization - only super admins can view security events
|
|
before_action :verify_super_admin_access
|
|
|
|
# GET /api/v1/admin/security_events
|
|
def index
|
|
# Layer 1: Scoped query
|
|
events = SecurityEvent.all
|
|
|
|
# Layer 4: Filtering
|
|
events = events.where(user_id: params[:user_id]) if params[:user_id].present?
|
|
events = events.where(event_type: params[:event_type]) if params[:event_type].present?
|
|
events = events.where('created_at >= ?', params[:from]) if params[:from].present?
|
|
events = events.where('created_at <= ?', params[:to]) if params[:to].present?
|
|
|
|
# Pagination
|
|
events = events.order(created_at: :desc)
|
|
@pagy, events = pagy(events, items: params[:limit] || 50)
|
|
|
|
render json: {
|
|
events: events.map do |event|
|
|
{
|
|
id: event.id,
|
|
event_type: event.event_type,
|
|
user_email: event.user&.email,
|
|
ip_address: event.ip_address,
|
|
details: event.details,
|
|
created_at: event.created_at,
|
|
description: event.description
|
|
}
|
|
end,
|
|
meta: pagy_meta(@pagy)
|
|
}
|
|
end
|
|
|
|
# GET /api/v1/admin/security_events/:id
|
|
def show
|
|
event = SecurityEvent.find_by(id: params[:id])
|
|
|
|
unless event
|
|
return render json: { error: 'Security event not found' }, status: :not_found
|
|
end
|
|
|
|
render json: {
|
|
id: event.id,
|
|
event_type: event.event_type,
|
|
user: {
|
|
id: event.user&.id,
|
|
email: event.user&.email,
|
|
name: event.user&.full_name
|
|
},
|
|
ip_address: event.ip_address,
|
|
details: event.details,
|
|
created_at: event.created_at,
|
|
description: event.description
|
|
}
|
|
end
|
|
|
|
# GET /api/v1/admin/security_events/export
|
|
def export
|
|
# Layer 4: Export capability
|
|
events = SecurityEvent.all
|
|
|
|
# Apply filters
|
|
events = events.where(event_type: params[:event_type]) if params[:event_type].present?
|
|
events = events.where('created_at >= ?', params[:from]) if params[:from].present?
|
|
events = events.where('created_at <= ?', params[:to]) if params[:to].present?
|
|
|
|
# Generate CSV
|
|
csv_data = events.export_csv(
|
|
start_date: params[:from],
|
|
end_date: params[:to],
|
|
event_types: params[:event_type]&.split(',')
|
|
)
|
|
|
|
# Log export event
|
|
log_security_event(:security_events_exported, {
|
|
event_count: events.count,
|
|
filters: params.slice(:event_type, :from, :to)
|
|
})
|
|
|
|
send_data csv_data,
|
|
filename: "security_events_#{Time.current.strftime('%Y%m%d_%H%M%S')}.csv",
|
|
type: 'text/csv'
|
|
end
|
|
|
|
# GET /api/v1/admin/security_events/alerts
|
|
def alerts
|
|
# Get recent critical events that might indicate attacks
|
|
critical_events = SecurityEvent.where(
|
|
event_type: ['unauthorized_institution_access', 'rate_limit_exceeded', 'token_validation_failure']
|
|
).where('created_at >= ?', 1.hour.ago)
|
|
|
|
# Group by type for summary
|
|
summary = critical_events.group(:event_type).count
|
|
|
|
# Check thresholds
|
|
alerts = []
|
|
alerts << { type: 'unauthorized_access', severity: 'high', count: summary['unauthorized_institution_access'] || 0 } if (summary['unauthorized_institution_access'] || 0) >= 5
|
|
alerts << { type: 'rate_limit', severity: 'medium', count: summary['rate_limit_exceeded'] || 0 } if (summary['rate_limit_exceeded'] || 0) >= 10
|
|
alerts << { type: 'token_failures', severity: 'high', count: summary['token_validation_failure'] || 0 } if (summary['token_validation_failure'] || 0) >= 20
|
|
|
|
render json: {
|
|
summary: summary,
|
|
alerts: alerts,
|
|
time_range: { from: 1.hour.ago, to: Time.current }
|
|
}
|
|
end
|
|
|
|
private
|
|
|
|
def verify_super_admin_access
|
|
unless current_user.cohort_super_admin?
|
|
render json: { error: 'Super admin access required' }, status: :forbidden
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end |