177 lines
4.9 KiB
Elixir
177 lines
4.9 KiB
Elixir
defmodule Towerops.Admin.AuditLogger do
|
|
@moduledoc """
|
|
Helper module for creating audit log entries with automatic extraction of
|
|
request metadata (IP address, user agent, request path).
|
|
|
|
This module provides convenience functions for logging various types of
|
|
audit events throughout the application.
|
|
"""
|
|
|
|
alias Towerops.Admin
|
|
|
|
@doc """
|
|
Logs an audit event with automatic extraction of conn metadata.
|
|
|
|
## Parameters
|
|
|
|
* `action` - String representing the action being logged
|
|
* `conn` - The Plug.Conn struct (can be nil for background jobs)
|
|
* `opts` - Keyword list of options:
|
|
* `:actor_id` - ID of the user performing the action (optional)
|
|
* `:target_user_id` - ID of the user being acted upon (optional)
|
|
* `:metadata` - Additional metadata map (optional)
|
|
* `:data_accessed` - Map of data fields that were accessed (optional)
|
|
|
|
## Examples
|
|
|
|
iex> log_event("user_data_viewed", conn, actor_id: admin_id, target_user_id: user_id)
|
|
{:ok, %AuditLog{}}
|
|
"""
|
|
def log_event(action, conn \\ nil, opts \\ []) do
|
|
attrs = build_attrs(action, conn, opts)
|
|
Admin.create_audit_log(attrs)
|
|
end
|
|
|
|
@doc """
|
|
Logs when a user's data is viewed by an admin or another user.
|
|
"""
|
|
def log_user_data_viewed(conn, viewer_id, target_user_id, data_accessed) do
|
|
log_event("user_data_viewed", conn,
|
|
actor_id: viewer_id,
|
|
target_user_id: target_user_id,
|
|
data_accessed: data_accessed
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs when a user exports their data.
|
|
"""
|
|
def log_user_data_exported(conn, user_id) do
|
|
log_event("user_data_exported", conn,
|
|
actor_id: user_id,
|
|
target_user_id: user_id,
|
|
metadata: %{export_format: "json"}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs when a user's profile is updated.
|
|
"""
|
|
def log_user_profile_updated(conn, user_id, fields_changed) do
|
|
log_event("user_profile_updated", conn,
|
|
actor_id: user_id,
|
|
target_user_id: user_id,
|
|
data_accessed: %{fields_changed: fields_changed}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs when organization data is accessed.
|
|
"""
|
|
def log_org_data_accessed(conn, user_id, org_id, action_detail) do
|
|
log_event("org_data_accessed", conn,
|
|
actor_id: user_id,
|
|
metadata: %{organization_id: org_id, action: action_detail}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs device creation.
|
|
"""
|
|
def log_device_created(conn, user_id, device_id, device_name) do
|
|
log_event("device_created", conn,
|
|
actor_id: user_id,
|
|
metadata: %{device_id: device_id, device_name: device_name}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs device update.
|
|
"""
|
|
def log_device_updated(conn, user_id, device_id, fields_changed) do
|
|
log_event("device_updated", conn,
|
|
actor_id: user_id,
|
|
metadata: %{device_id: device_id},
|
|
data_accessed: %{fields_changed: fields_changed}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs device deletion.
|
|
"""
|
|
def log_device_deleted(conn, user_id, device_id, device_name) do
|
|
log_event("device_deleted", conn,
|
|
actor_id: user_id,
|
|
metadata: %{device_id: device_id, device_name: device_name}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs when monitoring data is queried.
|
|
"""
|
|
def log_monitoring_data_queried(conn, user_id, device_id, time_range) do
|
|
log_event("monitoring_data_queried", conn,
|
|
actor_id: user_id,
|
|
metadata: %{device_id: device_id, time_range: time_range}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs failed access attempts.
|
|
"""
|
|
def log_failed_access_attempt(conn, user_id, resource, reason) do
|
|
log_event("failed_access_attempt", conn,
|
|
actor_id: user_id,
|
|
metadata: %{resource: resource, reason: reason}
|
|
)
|
|
end
|
|
|
|
@doc """
|
|
Logs privilege escalations (e.g., becoming org admin).
|
|
"""
|
|
def log_privilege_escalation(conn, user_id, from_role, to_role, context) do
|
|
log_event("privilege_escalation", conn,
|
|
actor_id: user_id,
|
|
metadata: %{from_role: from_role, to_role: to_role, context: context}
|
|
)
|
|
end
|
|
|
|
# Private helper functions
|
|
|
|
defp build_attrs(action, conn, opts) do
|
|
base_attrs = %{
|
|
action: action,
|
|
superuser_id: Keyword.get(opts, :actor_id),
|
|
target_user_id: Keyword.get(opts, :target_user_id),
|
|
metadata: Keyword.get(opts, :metadata, %{}),
|
|
data_accessed: Keyword.get(opts, :data_accessed)
|
|
}
|
|
|
|
if conn do
|
|
base_attrs
|
|
|> Map.put(:ip_address, get_ip_address(conn))
|
|
|> Map.put(:user_agent, get_user_agent(conn))
|
|
|> Map.put(:request_path, get_request_path(conn))
|
|
else
|
|
base_attrs
|
|
end
|
|
end
|
|
|
|
defp get_ip_address(%Plug.Conn{} = conn) do
|
|
case Plug.Conn.get_req_header(conn, "x-forwarded-for") do
|
|
[ip | _] -> ip |> String.split(",") |> List.first() |> String.trim()
|
|
[] -> to_string(:inet_parse.ntoa(conn.remote_ip))
|
|
end
|
|
end
|
|
|
|
defp get_user_agent(%Plug.Conn{} = conn) do
|
|
case Plug.Conn.get_req_header(conn, "user-agent") do
|
|
[ua | _] -> ua
|
|
[] -> nil
|
|
end
|
|
end
|
|
|
|
defp get_request_path(%Plug.Conn{} = conn) do
|
|
conn.request_path
|
|
end
|
|
end
|