## New Tables Created (Sprint 1 - DDL Roadmap Q1-2026) ### users schema (4 tables): - profiles: Extended user profile information - user_settings: User preferences and configurations - kyc_verifications: KYC/AML verification records - risk_profiles: Trading risk assessment profiles ### admin schema (3 tables): - admin_roles: Platform administrative roles - platform_analytics: Aggregated platform metrics - api_keys: Programmatic API access keys ### notifications schema (1 table): - notifications: Multi-channel notification system ### market_data schema (4 tables): - tickers: Financial instruments catalog - ohlcv_5m: 5-minute OHLCV price data - technical_indicators: Pre-calculated TA indicators - ohlcv_5m_staging: Staging table for data ingestion ## Features: - Multi-tenancy with RLS policies - Comprehensive indexes for query optimization - Triggers for computed fields and timestamps - Helper functions for common operations - Views for dashboard and reporting - Full GRANTS configuration Roadmap: orchestration/planes/ROADMAP-IMPLEMENTACION-DDL-2026-Q1.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
244 lines
7.8 KiB
PL/PgSQL
244 lines
7.8 KiB
PL/PgSQL
-- ============================================================================
|
|
-- SCHEMA: admin
|
|
-- TABLE: admin_roles
|
|
-- DESCRIPTION: Roles administrativos de la plataforma
|
|
-- VERSION: 1.0.0
|
|
-- CREATED: 2026-01-16
|
|
-- SPRINT: Sprint 1 - DDL Implementation Roadmap Q1-2026
|
|
-- ============================================================================
|
|
|
|
-- Crear schema si no existe
|
|
CREATE SCHEMA IF NOT EXISTS admin;
|
|
|
|
-- Grant usage
|
|
GRANT USAGE ON SCHEMA admin TO trading_app;
|
|
GRANT USAGE ON SCHEMA admin TO trading_readonly;
|
|
|
|
-- Enum para nivel de acceso administrativo
|
|
DO $$ BEGIN
|
|
CREATE TYPE admin.admin_level AS ENUM (
|
|
'super_admin', -- Acceso total a la plataforma
|
|
'platform_admin', -- Administracion de plataforma (sin acceso a codigo)
|
|
'tenant_admin', -- Administrador de tenant
|
|
'support', -- Soporte al cliente
|
|
'analyst', -- Solo lectura para analisis
|
|
'auditor' -- Solo lectura para auditorias
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Tabla de Roles Administrativos
|
|
CREATE TABLE IF NOT EXISTS admin.admin_roles (
|
|
-- Identificadores
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,
|
|
|
|
-- Nivel administrativo
|
|
level admin.admin_level NOT NULL,
|
|
|
|
-- Scope del rol
|
|
is_global BOOLEAN NOT NULL DEFAULT FALSE, -- Aplica a todos los tenants
|
|
tenant_id UUID REFERENCES tenants.tenants(id), -- Tenant especifico (NULL si global)
|
|
|
|
-- Permisos especificos (override del nivel)
|
|
permissions JSONB DEFAULT '{}'::JSONB,
|
|
denied_permissions JSONB DEFAULT '[]'::JSONB, -- Permisos explicitamente denegados
|
|
|
|
-- Configuracion
|
|
can_impersonate BOOLEAN NOT NULL DEFAULT FALSE, -- Puede actuar como otro usuario
|
|
can_view_pii BOOLEAN NOT NULL DEFAULT FALSE, -- Puede ver datos personales
|
|
can_export_data BOOLEAN NOT NULL DEFAULT FALSE, -- Puede exportar datos masivos
|
|
can_modify_config BOOLEAN NOT NULL DEFAULT FALSE, -- Puede modificar configuracion
|
|
can_manage_admins BOOLEAN NOT NULL DEFAULT FALSE, -- Puede gestionar otros admins
|
|
|
|
-- Restricciones de IP
|
|
allowed_ips INET[], -- IPs permitidas (NULL = todas)
|
|
|
|
-- Restricciones de horario
|
|
allowed_hours_start TIME, -- Hora inicio permitida
|
|
allowed_hours_end TIME, -- Hora fin permitida
|
|
allowed_days INTEGER[], -- Dias permitidos (0=Dom, 6=Sab)
|
|
|
|
-- Activacion
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
activated_at TIMESTAMPTZ,
|
|
deactivated_at TIMESTAMPTZ,
|
|
deactivation_reason TEXT,
|
|
|
|
-- Expiracion
|
|
expires_at TIMESTAMPTZ, -- Rol temporal
|
|
|
|
-- Auditoria de asignacion
|
|
assigned_by UUID REFERENCES users.users(id),
|
|
assigned_at TIMESTAMPTZ DEFAULT NOW(),
|
|
assignment_reason TEXT,
|
|
|
|
-- Ultima actividad administrativa
|
|
last_admin_action_at TIMESTAMPTZ,
|
|
total_admin_actions INTEGER NOT NULL DEFAULT 0,
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}'::JSONB,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT admin_roles_unique_user_tenant UNIQUE (user_id, tenant_id),
|
|
CONSTRAINT admin_roles_tenant_check CHECK (
|
|
(is_global = TRUE AND tenant_id IS NULL) OR
|
|
(is_global = FALSE AND tenant_id IS NOT NULL)
|
|
)
|
|
);
|
|
|
|
COMMENT ON TABLE admin.admin_roles IS
|
|
'Roles administrativos asignados a usuarios de la plataforma';
|
|
|
|
COMMENT ON COLUMN admin.admin_roles.level IS
|
|
'Nivel de acceso: super_admin, platform_admin, tenant_admin, support, analyst, auditor';
|
|
|
|
COMMENT ON COLUMN admin.admin_roles.is_global IS
|
|
'TRUE si el rol aplica a todos los tenants (solo para super_admin/platform_admin)';
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_user_id
|
|
ON admin.admin_roles(user_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_tenant_id
|
|
ON admin.admin_roles(tenant_id)
|
|
WHERE tenant_id IS NOT NULL;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_level
|
|
ON admin.admin_roles(level);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_active
|
|
ON admin.admin_roles(is_active)
|
|
WHERE is_active = TRUE;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_global
|
|
ON admin.admin_roles(is_global)
|
|
WHERE is_global = TRUE;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_admin_roles_expires
|
|
ON admin.admin_roles(expires_at)
|
|
WHERE expires_at IS NOT NULL;
|
|
|
|
-- Trigger para updated_at
|
|
CREATE OR REPLACE FUNCTION admin.update_admin_timestamp()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS admin_role_updated_at ON admin.admin_roles;
|
|
CREATE TRIGGER admin_role_updated_at
|
|
BEFORE UPDATE ON admin.admin_roles
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION admin.update_admin_timestamp();
|
|
|
|
-- Funcion para verificar acceso admin
|
|
CREATE OR REPLACE FUNCTION admin.check_admin_access(
|
|
p_user_id UUID,
|
|
p_tenant_id UUID DEFAULT NULL,
|
|
p_required_level admin.admin_level DEFAULT 'support'
|
|
)
|
|
RETURNS BOOLEAN AS $$
|
|
DECLARE
|
|
v_role RECORD;
|
|
v_level_order INTEGER;
|
|
v_required_order INTEGER;
|
|
v_current_time TIME;
|
|
v_current_day INTEGER;
|
|
BEGIN
|
|
-- Orden de niveles (mayor = mas permisos)
|
|
SELECT CASE p_required_level
|
|
WHEN 'super_admin' THEN 6
|
|
WHEN 'platform_admin' THEN 5
|
|
WHEN 'tenant_admin' THEN 4
|
|
WHEN 'support' THEN 3
|
|
WHEN 'analyst' THEN 2
|
|
WHEN 'auditor' THEN 1
|
|
END INTO v_required_order;
|
|
|
|
-- Buscar rol activo
|
|
SELECT * INTO v_role
|
|
FROM admin.admin_roles
|
|
WHERE user_id = p_user_id
|
|
AND is_active = TRUE
|
|
AND (expires_at IS NULL OR expires_at > NOW())
|
|
AND (is_global = TRUE OR tenant_id = p_tenant_id)
|
|
ORDER BY is_global DESC -- Preferir roles globales
|
|
LIMIT 1;
|
|
|
|
IF v_role IS NULL THEN
|
|
RETURN FALSE;
|
|
END IF;
|
|
|
|
-- Verificar nivel
|
|
SELECT CASE v_role.level
|
|
WHEN 'super_admin' THEN 6
|
|
WHEN 'platform_admin' THEN 5
|
|
WHEN 'tenant_admin' THEN 4
|
|
WHEN 'support' THEN 3
|
|
WHEN 'analyst' THEN 2
|
|
WHEN 'auditor' THEN 1
|
|
END INTO v_level_order;
|
|
|
|
IF v_level_order < v_required_order THEN
|
|
RETURN FALSE;
|
|
END IF;
|
|
|
|
-- Verificar restricciones de horario si existen
|
|
IF v_role.allowed_hours_start IS NOT NULL AND v_role.allowed_hours_end IS NOT NULL THEN
|
|
v_current_time := CURRENT_TIME;
|
|
IF v_current_time < v_role.allowed_hours_start OR v_current_time > v_role.allowed_hours_end THEN
|
|
RETURN FALSE;
|
|
END IF;
|
|
END IF;
|
|
|
|
-- Verificar restricciones de dias si existen
|
|
IF v_role.allowed_days IS NOT NULL AND array_length(v_role.allowed_days, 1) > 0 THEN
|
|
v_current_day := EXTRACT(DOW FROM CURRENT_DATE)::INTEGER;
|
|
IF NOT (v_current_day = ANY(v_role.allowed_days)) THEN
|
|
RETURN FALSE;
|
|
END IF;
|
|
END IF;
|
|
|
|
RETURN TRUE;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Vista de administradores activos
|
|
CREATE OR REPLACE VIEW admin.v_active_admins AS
|
|
SELECT
|
|
ar.id,
|
|
ar.user_id,
|
|
u.email,
|
|
u.first_name,
|
|
u.last_name,
|
|
ar.level,
|
|
ar.is_global,
|
|
ar.tenant_id,
|
|
t.name AS tenant_name,
|
|
ar.can_impersonate,
|
|
ar.can_view_pii,
|
|
ar.expires_at,
|
|
ar.last_admin_action_at,
|
|
ar.total_admin_actions
|
|
FROM admin.admin_roles ar
|
|
JOIN users.users u ON ar.user_id = u.id
|
|
LEFT JOIN tenants.tenants t ON ar.tenant_id = t.id
|
|
WHERE ar.is_active = TRUE
|
|
AND (ar.expires_at IS NULL OR ar.expires_at > NOW())
|
|
ORDER BY ar.level, ar.created_at;
|
|
|
|
-- Grants
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON admin.admin_roles TO trading_app;
|
|
GRANT SELECT ON admin.admin_roles TO trading_readonly;
|
|
GRANT SELECT ON admin.v_active_admins TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION admin.check_admin_access TO trading_app;
|