186 lines
6.2 KiB
PL/PgSQL
186 lines
6.2 KiB
PL/PgSQL
-- Chattyness Audit Schema Tables
|
|
-- PostgreSQL 18
|
|
--
|
|
-- Audit trails and activity logging
|
|
-- Load via: psql -f schema/tables/080_audit.sql
|
|
|
|
\set ON_ERROR_STOP on
|
|
|
|
BEGIN;
|
|
|
|
-- =============================================================================
|
|
-- Audit Event Types
|
|
-- =============================================================================
|
|
|
|
CREATE TYPE audit.event_category AS ENUM (
|
|
'auth', -- Login, logout, password changes
|
|
'account', -- Profile updates, settings changes
|
|
'realm', -- Realm creation, modification, deletion
|
|
'scene', -- Scene creation, modification
|
|
'moderation', -- Moderation actions
|
|
'prop', -- Prop transfers, creation
|
|
'admin' -- Administrative actions
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- Audit Log
|
|
-- =============================================================================
|
|
-- Immutable log of significant events for compliance and debugging.
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE audit.events (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Event classification
|
|
category audit.event_category NOT NULL,
|
|
action TEXT NOT NULL, -- Specific action (e.g., 'login', 'create_realm', 'ban_user')
|
|
|
|
-- Actor
|
|
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
|
|
session_id UUID, -- Auth session ID (not FK to allow historical queries)
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
|
|
-- Target (what was affected)
|
|
target_type TEXT, -- 'user', 'realm', 'scene', 'prop', etc.
|
|
target_id UUID,
|
|
|
|
-- Context
|
|
realm_id UUID REFERENCES realm.realms(id) ON DELETE SET NULL,
|
|
|
|
-- Event data
|
|
details JSONB NOT NULL DEFAULT '{}',
|
|
|
|
-- Outcome
|
|
success BOOLEAN NOT NULL DEFAULT true,
|
|
error_message TEXT,
|
|
|
|
-- Immutable timestamp
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
COMMENT ON TABLE audit.events IS 'Immutable audit log of significant system events';
|
|
COMMENT ON COLUMN audit.events.details IS 'JSON payload with event-specific data';
|
|
|
|
CREATE INDEX idx_audit_events_time ON audit.events (created_at DESC);
|
|
CREATE INDEX idx_audit_events_user ON audit.events (user_id, created_at DESC)
|
|
WHERE user_id IS NOT NULL;
|
|
CREATE INDEX idx_audit_events_category ON audit.events (category, created_at DESC);
|
|
CREATE INDEX idx_audit_events_target ON audit.events (target_type, target_id, created_at DESC)
|
|
WHERE target_id IS NOT NULL;
|
|
CREATE INDEX idx_audit_events_realm ON audit.events (realm_id, created_at DESC)
|
|
WHERE realm_id IS NOT NULL;
|
|
CREATE INDEX idx_audit_events_ip ON audit.events (ip_address, created_at DESC)
|
|
WHERE ip_address IS NOT NULL;
|
|
|
|
-- =============================================================================
|
|
-- Login History
|
|
-- =============================================================================
|
|
-- Dedicated table for login attempts (success and failure).
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE audit.login_history (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- User (may be NULL for failed attempts with unknown username)
|
|
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
|
|
attempted_username TEXT,
|
|
|
|
-- Attempt details
|
|
success BOOLEAN NOT NULL,
|
|
failure_reason TEXT,
|
|
auth_provider auth.auth_provider,
|
|
|
|
-- Client info
|
|
ip_address INET NOT NULL,
|
|
user_agent TEXT,
|
|
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
COMMENT ON TABLE audit.login_history IS 'History of login attempts for security monitoring';
|
|
|
|
CREATE INDEX idx_audit_login_history_user ON audit.login_history (user_id, created_at DESC)
|
|
WHERE user_id IS NOT NULL;
|
|
CREATE INDEX idx_audit_login_history_ip ON audit.login_history (ip_address, created_at DESC);
|
|
CREATE INDEX idx_audit_login_history_failed ON audit.login_history (ip_address, created_at DESC)
|
|
WHERE success = false;
|
|
|
|
-- =============================================================================
|
|
-- Data Export Requests (GDPR compliance)
|
|
-- =============================================================================
|
|
|
|
CREATE TYPE audit.export_status AS ENUM (
|
|
'pending',
|
|
'processing',
|
|
'completed',
|
|
'failed',
|
|
'expired'
|
|
);
|
|
|
|
CREATE TABLE audit.data_exports (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
-- Request details
|
|
status audit.export_status NOT NULL DEFAULT 'pending',
|
|
requested_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
-- Processing
|
|
started_at TIMESTAMPTZ,
|
|
completed_at TIMESTAMPTZ,
|
|
error_message TEXT,
|
|
|
|
-- Result
|
|
download_path public.asset_path,
|
|
download_expires_at TIMESTAMPTZ,
|
|
downloaded_at TIMESTAMPTZ
|
|
);
|
|
|
|
COMMENT ON TABLE audit.data_exports IS 'User data export requests (GDPR compliance)';
|
|
|
|
CREATE INDEX idx_audit_data_exports_user ON audit.data_exports (user_id);
|
|
CREATE INDEX idx_audit_data_exports_status ON audit.data_exports (status)
|
|
WHERE status IN ('pending', 'processing');
|
|
|
|
-- =============================================================================
|
|
-- Account Deletion Requests (GDPR compliance)
|
|
-- =============================================================================
|
|
|
|
CREATE TYPE audit.deletion_status AS ENUM (
|
|
'pending',
|
|
'processing',
|
|
'completed',
|
|
'cancelled'
|
|
);
|
|
|
|
CREATE TABLE audit.deletion_requests (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
|
|
-- Request details
|
|
status audit.deletion_status NOT NULL DEFAULT 'pending',
|
|
reason TEXT,
|
|
requested_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
|
|
-- Grace period (user can cancel during this time)
|
|
scheduled_for TIMESTAMPTZ NOT NULL,
|
|
cancelled_at TIMESTAMPTZ,
|
|
|
|
-- Processing
|
|
completed_at TIMESTAMPTZ,
|
|
|
|
-- Confirmation
|
|
confirmed_at TIMESTAMPTZ,
|
|
confirmation_token_hash TEXT
|
|
);
|
|
|
|
COMMENT ON TABLE audit.deletion_requests IS 'Account deletion requests with grace period';
|
|
|
|
CREATE INDEX idx_audit_deletion_requests_user ON audit.deletion_requests (user_id);
|
|
CREATE INDEX idx_audit_deletion_requests_pending ON audit.deletion_requests (scheduled_for)
|
|
WHERE status = 'pending';
|
|
|
|
COMMIT;
|