- Replace old DDL structure with new numbered files (01-24) - Update migrations and seeds for new schema - Clean up deprecated files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
566 lines
21 KiB
PL/PgSQL
566 lines
21 KiB
PL/PgSQL
-- =============================================================
|
|
-- ARCHIVO: 07-users-rbac.sql
|
|
-- DESCRIPCION: Sistema RBAC (Roles, Permisos, Invitaciones)
|
|
-- VERSION: 1.0.0
|
|
-- PROYECTO: ERP-Core V2
|
|
-- FECHA: 2026-01-10
|
|
-- EPIC: SAAS-CORE-AUTH (EPIC-SAAS-001)
|
|
-- HISTORIAS: US-004, US-005, US-006, US-030
|
|
-- =============================================================
|
|
|
|
-- =====================
|
|
-- SCHEMA: users
|
|
-- =====================
|
|
CREATE SCHEMA IF NOT EXISTS users;
|
|
|
|
-- =====================
|
|
-- TABLA: users.roles
|
|
-- Roles del sistema con herencia (US-004)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Info basica
|
|
name VARCHAR(100) NOT NULL,
|
|
display_name VARCHAR(255),
|
|
description TEXT,
|
|
color VARCHAR(20),
|
|
icon VARCHAR(50),
|
|
|
|
-- Jerarquia
|
|
parent_role_id UUID REFERENCES users.roles(id) ON DELETE SET NULL,
|
|
hierarchy_level INTEGER DEFAULT 0,
|
|
|
|
-- Flags
|
|
is_system BOOLEAN DEFAULT FALSE, -- No editable por usuarios
|
|
is_default BOOLEAN DEFAULT FALSE, -- Asignado a nuevos usuarios
|
|
is_superadmin BOOLEAN DEFAULT FALSE, -- Acceso total
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
deleted_at TIMESTAMPTZ,
|
|
|
|
-- Constraint: nombre unico por tenant (o global si tenant_id es NULL)
|
|
UNIQUE NULLS NOT DISTINCT (tenant_id, name)
|
|
);
|
|
|
|
-- Indices para roles
|
|
CREATE INDEX IF NOT EXISTS idx_roles_tenant ON users.roles(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_roles_parent ON users.roles(parent_role_id);
|
|
CREATE INDEX IF NOT EXISTS idx_roles_system ON users.roles(is_system) WHERE is_system = TRUE;
|
|
CREATE INDEX IF NOT EXISTS idx_roles_default ON users.roles(tenant_id, is_default) WHERE is_default = TRUE;
|
|
|
|
-- =====================
|
|
-- TABLA: users.permissions
|
|
-- Permisos granulares del sistema (US-004)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.permissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
-- Identificacion
|
|
resource VARCHAR(100) NOT NULL, -- users, tenants, branches, invoices, etc.
|
|
action VARCHAR(50) NOT NULL, -- create, read, update, delete, export, etc.
|
|
scope VARCHAR(50) DEFAULT 'own', -- own, tenant, global
|
|
|
|
-- Info
|
|
display_name VARCHAR(255),
|
|
description TEXT,
|
|
category VARCHAR(100), -- auth, billing, inventory, sales, etc.
|
|
|
|
-- Flags
|
|
is_dangerous BOOLEAN DEFAULT FALSE, -- Requiere confirmacion adicional
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE(resource, action, scope)
|
|
);
|
|
|
|
-- Indices para permissions
|
|
CREATE INDEX IF NOT EXISTS idx_permissions_resource ON users.permissions(resource);
|
|
CREATE INDEX IF NOT EXISTS idx_permissions_category ON users.permissions(category);
|
|
|
|
-- =====================
|
|
-- TABLA: users.role_permissions
|
|
-- Asignacion de permisos a roles (US-004)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.role_permissions (
|
|
role_id UUID NOT NULL REFERENCES users.roles(id) ON DELETE CASCADE,
|
|
permission_id UUID NOT NULL REFERENCES users.permissions(id) ON DELETE CASCADE,
|
|
|
|
-- Condiciones opcionales
|
|
conditions JSONB DEFAULT '{}', -- Condiciones adicionales (ej: solo ciertos estados)
|
|
|
|
-- Metadata
|
|
granted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
granted_by UUID REFERENCES auth.users(id),
|
|
|
|
PRIMARY KEY (role_id, permission_id)
|
|
);
|
|
|
|
-- Indices para role_permissions
|
|
CREATE INDEX IF NOT EXISTS idx_role_permissions_role ON users.role_permissions(role_id);
|
|
CREATE INDEX IF NOT EXISTS idx_role_permissions_permission ON users.role_permissions(permission_id);
|
|
|
|
-- =====================
|
|
-- TABLA: users.user_roles
|
|
-- Asignacion de roles a usuarios (US-004)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.user_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
role_id UUID NOT NULL REFERENCES users.roles(id) ON DELETE CASCADE,
|
|
|
|
-- Contexto
|
|
tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
branch_id UUID REFERENCES core.branches(id) ON DELETE CASCADE, -- Opcional: rol por sucursal
|
|
|
|
-- Flags
|
|
is_primary BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Vigencia
|
|
valid_from TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
valid_until TIMESTAMPTZ,
|
|
|
|
-- Metadata
|
|
assigned_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
assigned_by UUID REFERENCES auth.users(id),
|
|
revoked_at TIMESTAMPTZ,
|
|
revoked_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Constraint: un usuario solo puede tener un rol una vez por branch (o global si branch es NULL)
|
|
UNIQUE (user_id, role_id, branch_id)
|
|
);
|
|
|
|
-- Indices para user_roles
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_user ON users.user_roles(user_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_role ON users.user_roles(role_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_tenant ON users.user_roles(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_branch ON users.user_roles(branch_id) WHERE branch_id IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_user_roles_active ON users.user_roles(user_id, revoked_at) WHERE revoked_at IS NULL;
|
|
|
|
-- =====================
|
|
-- TABLA: users.invitations
|
|
-- Invitaciones de usuario (US-006)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.invitations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Destinatario
|
|
email VARCHAR(255) NOT NULL,
|
|
first_name VARCHAR(100),
|
|
last_name VARCHAR(100),
|
|
|
|
-- Token de invitacion
|
|
token_hash VARCHAR(255) NOT NULL UNIQUE,
|
|
token_expires_at TIMESTAMPTZ NOT NULL,
|
|
|
|
-- Rol a asignar
|
|
role_id UUID REFERENCES users.roles(id) ON DELETE SET NULL,
|
|
branch_id UUID REFERENCES core.branches(id) ON DELETE SET NULL,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'expired', 'revoked')),
|
|
|
|
-- Mensaje personalizado
|
|
message TEXT,
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
invited_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
invited_by UUID NOT NULL REFERENCES auth.users(id),
|
|
accepted_at TIMESTAMPTZ,
|
|
accepted_user_id UUID REFERENCES auth.users(id),
|
|
resent_at TIMESTAMPTZ,
|
|
resent_count INTEGER DEFAULT 0,
|
|
|
|
-- Constraint: email unico por tenant mientras este pendiente
|
|
CONSTRAINT unique_pending_invitation UNIQUE (tenant_id, email, status)
|
|
);
|
|
|
|
-- Indices para invitations
|
|
CREATE INDEX IF NOT EXISTS idx_invitations_tenant ON users.invitations(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_invitations_email ON users.invitations(email);
|
|
CREATE INDEX IF NOT EXISTS idx_invitations_token ON users.invitations(token_hash);
|
|
CREATE INDEX IF NOT EXISTS idx_invitations_status ON users.invitations(status) WHERE status = 'pending';
|
|
CREATE INDEX IF NOT EXISTS idx_invitations_expires ON users.invitations(token_expires_at) WHERE status = 'pending';
|
|
|
|
-- =====================
|
|
-- TABLA: users.tenant_settings
|
|
-- Configuraciones por tenant (US-005)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.tenant_settings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE UNIQUE,
|
|
|
|
-- Limites
|
|
max_users INTEGER DEFAULT 10,
|
|
max_branches INTEGER DEFAULT 5,
|
|
max_storage_mb INTEGER DEFAULT 1024,
|
|
|
|
-- Features habilitadas
|
|
features_enabled TEXT[] DEFAULT '{}',
|
|
|
|
-- Configuracion de branding
|
|
branding JSONB DEFAULT '{
|
|
"logo_url": null,
|
|
"primary_color": "#2563eb",
|
|
"secondary_color": "#64748b"
|
|
}',
|
|
|
|
-- Configuracion regional
|
|
locale VARCHAR(10) DEFAULT 'es-MX',
|
|
timezone VARCHAR(50) DEFAULT 'America/Mexico_City',
|
|
currency VARCHAR(3) DEFAULT 'MXN',
|
|
date_format VARCHAR(20) DEFAULT 'DD/MM/YYYY',
|
|
|
|
-- Configuracion de seguridad
|
|
security_settings JSONB DEFAULT '{
|
|
"require_mfa": false,
|
|
"session_timeout_minutes": 480,
|
|
"password_min_length": 8,
|
|
"password_require_special": true
|
|
}',
|
|
|
|
-- Configuracion de notificaciones
|
|
notification_settings JSONB DEFAULT '{
|
|
"email_enabled": true,
|
|
"push_enabled": true,
|
|
"sms_enabled": false
|
|
}',
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}',
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- =====================
|
|
-- TABLA: users.profile_role_mapping
|
|
-- Mapeo de perfiles ERP a roles RBAC (US-030)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS users.profile_role_mapping (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
profile_code VARCHAR(10) NOT NULL, -- ADM, CNT, VNT, ALM, etc.
|
|
role_id UUID NOT NULL REFERENCES users.roles(id) ON DELETE CASCADE,
|
|
|
|
-- Flags
|
|
is_default BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE(profile_code, role_id)
|
|
);
|
|
|
|
-- Indice para profile_role_mapping
|
|
CREATE INDEX IF NOT EXISTS idx_profile_role_mapping_profile ON users.profile_role_mapping(profile_code);
|
|
|
|
-- =====================
|
|
-- RLS POLICIES
|
|
-- =====================
|
|
ALTER TABLE users.roles ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation_roles ON users.roles
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid OR tenant_id IS NULL);
|
|
|
|
ALTER TABLE users.role_permissions ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation_role_permissions ON users.role_permissions
|
|
USING (role_id IN (
|
|
SELECT id FROM users.roles
|
|
WHERE tenant_id = current_setting('app.current_tenant_id', true)::uuid OR tenant_id IS NULL
|
|
));
|
|
|
|
ALTER TABLE users.user_roles ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation_user_roles ON users.user_roles
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid OR tenant_id IS NULL);
|
|
|
|
ALTER TABLE users.invitations ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation_invitations ON users.invitations
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
ALTER TABLE users.tenant_settings ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY tenant_isolation_settings ON users.tenant_settings
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- =====================
|
|
-- FUNCIONES
|
|
-- =====================
|
|
|
|
-- Funcion para verificar si un usuario tiene un permiso especifico
|
|
CREATE OR REPLACE FUNCTION users.has_permission(
|
|
p_user_id UUID,
|
|
p_resource VARCHAR(100),
|
|
p_action VARCHAR(50),
|
|
p_scope VARCHAR(50) DEFAULT 'own'
|
|
)
|
|
RETURNS BOOLEAN AS $$
|
|
DECLARE
|
|
has_perm BOOLEAN;
|
|
BEGIN
|
|
-- Verificar si es superadmin
|
|
IF EXISTS (
|
|
SELECT 1 FROM auth.users
|
|
WHERE id = p_user_id AND is_superadmin = TRUE
|
|
) THEN
|
|
RETURN TRUE;
|
|
END IF;
|
|
|
|
-- Verificar permisos via roles
|
|
SELECT EXISTS (
|
|
SELECT 1
|
|
FROM users.user_roles ur
|
|
JOIN users.role_permissions rp ON rp.role_id = ur.role_id
|
|
JOIN users.permissions p ON p.id = rp.permission_id
|
|
WHERE ur.user_id = p_user_id
|
|
AND ur.revoked_at IS NULL
|
|
AND (ur.valid_until IS NULL OR ur.valid_until > CURRENT_TIMESTAMP)
|
|
AND p.resource = p_resource
|
|
AND p.action = p_action
|
|
AND (p.scope = p_scope OR p.scope = 'global')
|
|
) INTO has_perm;
|
|
|
|
RETURN has_perm;
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
|
|
-- Funcion para obtener todos los permisos de un usuario
|
|
CREATE OR REPLACE FUNCTION users.get_user_permissions(p_user_id UUID)
|
|
RETURNS TABLE (
|
|
resource VARCHAR(100),
|
|
action VARCHAR(50),
|
|
scope VARCHAR(50),
|
|
conditions JSONB
|
|
) AS $$
|
|
BEGIN
|
|
-- Si es superadmin, devolver wildcard
|
|
IF EXISTS (
|
|
SELECT 1 FROM auth.users
|
|
WHERE id = p_user_id AND is_superadmin = TRUE
|
|
) THEN
|
|
RETURN QUERY
|
|
SELECT '*'::VARCHAR(100), '*'::VARCHAR(50), 'global'::VARCHAR(50), '{}'::JSONB;
|
|
RETURN;
|
|
END IF;
|
|
|
|
RETURN QUERY
|
|
SELECT DISTINCT
|
|
p.resource,
|
|
p.action,
|
|
p.scope,
|
|
rp.conditions
|
|
FROM users.user_roles ur
|
|
JOIN users.role_permissions rp ON rp.role_id = ur.role_id
|
|
JOIN users.permissions p ON p.id = rp.permission_id
|
|
WHERE ur.user_id = p_user_id
|
|
AND ur.revoked_at IS NULL
|
|
AND (ur.valid_until IS NULL OR ur.valid_until > CURRENT_TIMESTAMP);
|
|
END;
|
|
$$ LANGUAGE plpgsql STABLE;
|
|
|
|
-- Funcion para obtener permisos heredados de un rol
|
|
CREATE OR REPLACE FUNCTION users.get_role_permissions_with_inheritance(p_role_id UUID)
|
|
RETURNS TABLE (
|
|
permission_id UUID,
|
|
resource VARCHAR(100),
|
|
action VARCHAR(50),
|
|
scope VARCHAR(50),
|
|
inherited_from UUID
|
|
) AS $$
|
|
WITH RECURSIVE role_hierarchy AS (
|
|
-- Rol base
|
|
SELECT id, parent_role_id, 0 as level
|
|
FROM users.roles
|
|
WHERE id = p_role_id
|
|
|
|
UNION ALL
|
|
|
|
-- Roles padre (herencia)
|
|
SELECT r.id, r.parent_role_id, rh.level + 1
|
|
FROM users.roles r
|
|
JOIN role_hierarchy rh ON r.id = rh.parent_role_id
|
|
WHERE rh.level < 10 -- Limite de profundidad
|
|
)
|
|
SELECT
|
|
p.id as permission_id,
|
|
p.resource,
|
|
p.action,
|
|
p.scope,
|
|
rh.id as inherited_from
|
|
FROM role_hierarchy rh
|
|
JOIN users.role_permissions rp ON rp.role_id = rh.id
|
|
JOIN users.permissions p ON p.id = rp.permission_id;
|
|
$$ LANGUAGE sql STABLE;
|
|
|
|
-- Funcion para limpiar invitaciones expiradas
|
|
CREATE OR REPLACE FUNCTION users.cleanup_expired_invitations()
|
|
RETURNS INTEGER AS $$
|
|
DECLARE
|
|
updated_count INTEGER;
|
|
BEGIN
|
|
UPDATE users.invitations
|
|
SET status = 'expired'
|
|
WHERE status = 'pending'
|
|
AND token_expires_at < CURRENT_TIMESTAMP;
|
|
|
|
GET DIAGNOSTICS updated_count = ROW_COUNT;
|
|
RETURN updated_count;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- =====================
|
|
-- TRIGGERS
|
|
-- =====================
|
|
|
|
-- Trigger para actualizar updated_at en roles
|
|
CREATE OR REPLACE FUNCTION users.update_role_timestamp()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_roles_updated_at
|
|
BEFORE UPDATE ON users.roles
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION users.update_role_timestamp();
|
|
|
|
-- Trigger para actualizar updated_at en tenant_settings
|
|
CREATE TRIGGER trg_tenant_settings_updated_at
|
|
BEFORE UPDATE ON users.tenant_settings
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION users.update_role_timestamp();
|
|
|
|
-- =====================
|
|
-- SEED DATA: Permisos Base
|
|
-- =====================
|
|
INSERT INTO users.permissions (resource, action, scope, display_name, description, category) VALUES
|
|
-- Auth
|
|
('users', 'create', 'tenant', 'Crear usuarios', 'Crear nuevos usuarios en el tenant', 'auth'),
|
|
('users', 'read', 'tenant', 'Ver usuarios', 'Ver lista de usuarios del tenant', 'auth'),
|
|
('users', 'read', 'own', 'Ver perfil propio', 'Ver su propio perfil', 'auth'),
|
|
('users', 'update', 'tenant', 'Editar usuarios', 'Editar cualquier usuario del tenant', 'auth'),
|
|
('users', 'update', 'own', 'Editar perfil propio', 'Editar su propio perfil', 'auth'),
|
|
('users', 'delete', 'tenant', 'Eliminar usuarios', 'Eliminar usuarios del tenant', 'auth'),
|
|
('roles', 'create', 'tenant', 'Crear roles', 'Crear nuevos roles', 'auth'),
|
|
('roles', 'read', 'tenant', 'Ver roles', 'Ver roles del tenant', 'auth'),
|
|
('roles', 'update', 'tenant', 'Editar roles', 'Editar roles existentes', 'auth'),
|
|
('roles', 'delete', 'tenant', 'Eliminar roles', 'Eliminar roles', 'auth'),
|
|
('invitations', 'create', 'tenant', 'Invitar usuarios', 'Enviar invitaciones', 'auth'),
|
|
('invitations', 'read', 'tenant', 'Ver invitaciones', 'Ver invitaciones pendientes', 'auth'),
|
|
('invitations', 'delete', 'tenant', 'Cancelar invitaciones', 'Revocar invitaciones', 'auth'),
|
|
|
|
-- Tenants
|
|
('tenants', 'read', 'own', 'Ver configuracion', 'Ver configuracion del tenant', 'tenants'),
|
|
('tenants', 'update', 'own', 'Editar configuracion', 'Editar configuracion del tenant', 'tenants'),
|
|
('tenant_settings', 'read', 'own', 'Ver ajustes', 'Ver ajustes del tenant', 'tenants'),
|
|
('tenant_settings', 'update', 'own', 'Editar ajustes', 'Editar ajustes del tenant', 'tenants'),
|
|
|
|
-- Branches
|
|
('branches', 'create', 'tenant', 'Crear sucursales', 'Crear nuevas sucursales', 'branches'),
|
|
('branches', 'read', 'tenant', 'Ver sucursales', 'Ver todas las sucursales', 'branches'),
|
|
('branches', 'read', 'own', 'Ver sucursal asignada', 'Ver solo su sucursal', 'branches'),
|
|
('branches', 'update', 'tenant', 'Editar sucursales', 'Editar cualquier sucursal', 'branches'),
|
|
('branches', 'delete', 'tenant', 'Eliminar sucursales', 'Eliminar sucursales', 'branches'),
|
|
|
|
-- Billing
|
|
('billing', 'read', 'tenant', 'Ver facturacion', 'Ver informacion de facturacion', 'billing'),
|
|
('billing', 'update', 'tenant', 'Gestionar facturacion', 'Cambiar plan, metodo de pago', 'billing'),
|
|
('invoices', 'read', 'tenant', 'Ver facturas', 'Ver historial de facturas', 'billing'),
|
|
('invoices', 'export', 'tenant', 'Exportar facturas', 'Descargar facturas', 'billing'),
|
|
|
|
-- Audit
|
|
('audit_logs', 'read', 'tenant', 'Ver auditoria', 'Ver logs de auditoria', 'audit'),
|
|
('audit_logs', 'export', 'tenant', 'Exportar auditoria', 'Exportar logs', 'audit'),
|
|
('activity', 'read', 'own', 'Ver mi actividad', 'Ver actividad propia', 'audit'),
|
|
|
|
-- Notifications
|
|
('notifications', 'read', 'own', 'Ver notificaciones', 'Ver notificaciones propias', 'notifications'),
|
|
('notifications', 'update', 'own', 'Gestionar notificaciones', 'Marcar como leidas', 'notifications'),
|
|
('notification_settings', 'read', 'own', 'Ver preferencias', 'Ver preferencias de notificacion', 'notifications'),
|
|
('notification_settings', 'update', 'own', 'Editar preferencias', 'Editar preferencias', 'notifications')
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- =====================
|
|
-- SEED DATA: Roles Base del Sistema
|
|
-- =====================
|
|
INSERT INTO users.roles (id, tenant_id, name, display_name, description, is_system, is_superadmin, hierarchy_level, icon, color) VALUES
|
|
('10000000-0000-0000-0000-000000000001', NULL, 'superadmin', 'Super Administrador', 'Acceso total a la plataforma', TRUE, TRUE, 0, 'shield-check', '#dc2626'),
|
|
('10000000-0000-0000-0000-000000000002', NULL, 'admin', 'Administrador', 'Administrador del tenant', TRUE, FALSE, 1, 'shield', '#ea580c'),
|
|
('10000000-0000-0000-0000-000000000003', NULL, 'manager', 'Gerente', 'Gerente con acceso a reportes', TRUE, FALSE, 2, 'briefcase', '#0891b2'),
|
|
('10000000-0000-0000-0000-000000000004', NULL, 'user', 'Usuario', 'Usuario estandar', TRUE, FALSE, 3, 'user', '#64748b'),
|
|
('10000000-0000-0000-0000-000000000005', NULL, 'viewer', 'Visor', 'Solo lectura', TRUE, FALSE, 4, 'eye', '#94a3b8')
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Asignar permisos al rol Admin
|
|
INSERT INTO users.role_permissions (role_id, permission_id)
|
|
SELECT '10000000-0000-0000-0000-000000000002', id
|
|
FROM users.permissions
|
|
WHERE resource NOT IN ('audit_logs') -- Admin no tiene acceso a audit
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Asignar permisos al rol Manager
|
|
INSERT INTO users.role_permissions (role_id, permission_id)
|
|
SELECT '10000000-0000-0000-0000-000000000003', id
|
|
FROM users.permissions
|
|
WHERE scope = 'own'
|
|
OR (resource IN ('branches', 'users', 'invoices') AND action = 'read')
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Asignar permisos al rol User
|
|
INSERT INTO users.role_permissions (role_id, permission_id)
|
|
SELECT '10000000-0000-0000-0000-000000000004', id
|
|
FROM users.permissions
|
|
WHERE scope = 'own'
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- Asignar permisos al rol Viewer
|
|
INSERT INTO users.role_permissions (role_id, permission_id)
|
|
SELECT '10000000-0000-0000-0000-000000000005', id
|
|
FROM users.permissions
|
|
WHERE action = 'read' AND scope = 'own'
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- =====================
|
|
-- SEED DATA: Mapeo de Perfiles ERP a Roles (US-030)
|
|
-- =====================
|
|
INSERT INTO users.profile_role_mapping (profile_code, role_id) VALUES
|
|
('ADM', '10000000-0000-0000-0000-000000000002'), -- Admin
|
|
('CNT', '10000000-0000-0000-0000-000000000003'), -- Manager
|
|
('VNT', '10000000-0000-0000-0000-000000000004'), -- User
|
|
('CMP', '10000000-0000-0000-0000-000000000004'), -- User
|
|
('ALM', '10000000-0000-0000-0000-000000000004'), -- User
|
|
('HRH', '10000000-0000-0000-0000-000000000003'), -- Manager
|
|
('PRD', '10000000-0000-0000-0000-000000000004'), -- User
|
|
('EMP', '10000000-0000-0000-0000-000000000005'), -- Viewer
|
|
('GER', '10000000-0000-0000-0000-000000000003'), -- Manager
|
|
('AUD', '10000000-0000-0000-0000-000000000005') -- Viewer (read-only)
|
|
ON CONFLICT DO NOTHING;
|
|
|
|
-- =====================
|
|
-- COMENTARIOS
|
|
-- =====================
|
|
COMMENT ON TABLE users.roles IS 'Roles del sistema con soporte para herencia';
|
|
COMMENT ON TABLE users.permissions IS 'Permisos granulares (resource.action.scope)';
|
|
COMMENT ON TABLE users.role_permissions IS 'Asignacion de permisos a roles';
|
|
COMMENT ON TABLE users.user_roles IS 'Asignacion de roles a usuarios';
|
|
COMMENT ON TABLE users.invitations IS 'Invitaciones para nuevos usuarios';
|
|
COMMENT ON TABLE users.tenant_settings IS 'Configuraciones personalizadas por tenant';
|
|
COMMENT ON TABLE users.profile_role_mapping IS 'Mapeo de perfiles ERP a roles RBAC';
|
|
|
|
COMMENT ON FUNCTION users.has_permission IS 'Verifica si un usuario tiene un permiso especifico';
|
|
COMMENT ON FUNCTION users.get_user_permissions IS 'Obtiene todos los permisos de un usuario';
|
|
COMMENT ON FUNCTION users.get_role_permissions_with_inheritance IS 'Obtiene permisos de un rol incluyendo herencia';
|