151 lines
4.3 KiB
PL/PgSQL
151 lines
4.3 KiB
PL/PgSQL
-- ============================================================================
|
|
-- SCHEMA: auth
|
|
-- TABLE: sessions
|
|
-- DESCRIPTION: Gestion de sesiones de usuario con tracking de dispositivos
|
|
-- VERSION: 1.0.0
|
|
-- CREATED: 2026-01-10
|
|
-- ============================================================================
|
|
|
|
-- Crear schema si no existe
|
|
CREATE SCHEMA IF NOT EXISTS auth;
|
|
|
|
-- Enum para estado de sesion
|
|
DO $$ BEGIN
|
|
CREATE TYPE auth.session_status AS ENUM (
|
|
'active', -- Sesion activa
|
|
'expired', -- Expirada por tiempo
|
|
'revoked' -- Revocada manualmente (logout)
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para tipo de dispositivo
|
|
DO $$ BEGIN
|
|
CREATE TYPE auth.device_type AS ENUM (
|
|
'desktop',
|
|
'mobile',
|
|
'tablet',
|
|
'unknown'
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Tabla de Sesiones
|
|
CREATE TABLE IF NOT EXISTS auth.sessions (
|
|
-- Identificadores
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Token hash (para validacion)
|
|
token_hash VARCHAR(255) NOT NULL,
|
|
|
|
-- Informacion del dispositivo
|
|
device_type auth.device_type DEFAULT 'unknown',
|
|
device_name VARCHAR(255),
|
|
browser VARCHAR(100),
|
|
browser_version VARCHAR(50),
|
|
os VARCHAR(100),
|
|
os_version VARCHAR(50),
|
|
|
|
-- Ubicacion
|
|
ip_address INET,
|
|
user_agent TEXT,
|
|
|
|
-- Estado
|
|
status auth.session_status NOT NULL DEFAULT 'active',
|
|
|
|
-- Timestamps
|
|
last_active_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
COMMENT ON TABLE auth.sessions IS
|
|
'Sesiones activas de usuarios con informacion de dispositivo para seguridad';
|
|
|
|
COMMENT ON COLUMN auth.sessions.token_hash IS
|
|
'Hash SHA256 del refresh token para validacion sin almacenar el token';
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_user
|
|
ON auth.sessions(user_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_tenant
|
|
ON auth.sessions(tenant_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_token_hash
|
|
ON auth.sessions(token_hash);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_status
|
|
ON auth.sessions(status) WHERE status = 'active';
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_expires
|
|
ON auth.sessions(expires_at) WHERE status = 'active';
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_last_active
|
|
ON auth.sessions(last_active_at DESC);
|
|
|
|
-- Funcion para limpiar sesiones expiradas
|
|
CREATE OR REPLACE FUNCTION auth.cleanup_expired_sessions()
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
deleted_count INTEGER;
|
|
BEGIN
|
|
UPDATE auth.sessions
|
|
SET status = 'expired'
|
|
WHERE status = 'active'
|
|
AND expires_at < NOW();
|
|
|
|
GET DIAGNOSTICS deleted_count = ROW_COUNT;
|
|
|
|
-- Eliminar sesiones muy antiguas (> 90 dias)
|
|
DELETE FROM auth.sessions
|
|
WHERE (status = 'expired' OR status = 'revoked')
|
|
AND created_at < NOW() - INTERVAL '90 days';
|
|
|
|
RETURN deleted_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION auth.cleanup_expired_sessions() IS
|
|
'Limpia sesiones expiradas y elimina registros muy antiguos. Ejecutar periodicamente.';
|
|
|
|
-- Funcion para revocar todas las sesiones de un usuario
|
|
CREATE OR REPLACE FUNCTION auth.revoke_all_user_sessions(
|
|
p_user_id UUID,
|
|
p_except_session_id UUID DEFAULT NULL
|
|
)
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
revoked_count INTEGER;
|
|
BEGIN
|
|
UPDATE auth.sessions
|
|
SET status = 'revoked',
|
|
revoked_at = NOW()
|
|
WHERE user_id = p_user_id
|
|
AND status = 'active'
|
|
AND (p_except_session_id IS NULL OR id != p_except_session_id);
|
|
|
|
GET DIAGNOSTICS revoked_count = ROW_COUNT;
|
|
RETURN revoked_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
COMMENT ON FUNCTION auth.revoke_all_user_sessions IS
|
|
'Revoca todas las sesiones activas de un usuario, opcionalmente excepto una sesion especifica';
|
|
|
|
-- RLS Policy para multi-tenancy
|
|
ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY sessions_tenant_isolation ON auth.sessions
|
|
FOR ALL
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
|
|
-- Grants
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.sessions TO trading_app;
|
|
GRANT SELECT ON auth.sessions TO trading_readonly;
|