# DDL Specification: core_rbac Schema ## Identificacion | Campo | Valor | |-------|-------| | **Schema** | core_rbac | | **Modulo** | MGN-003 Roles/RBAC | | **Version** | 1.0 | | **Fecha** | 2025-12-05 | | **Estado** | Ready | --- ## Diagrama ER ```mermaid erDiagram roles ||--o{ role_permissions : has roles ||--o{ user_roles : assigned_to permissions ||--o{ role_permissions : granted_to users ||--o{ user_roles : has tenants ||--o{ roles : owns roles { uuid id PK uuid tenant_id FK string name string slug string description boolean is_built_in boolean is_active timestamp created_at timestamp updated_at uuid created_by FK uuid updated_by FK timestamp deleted_at uuid deleted_by FK } permissions { uuid id PK string code UK string name string description string module string parent_code FK boolean is_deprecated int sort_order timestamp created_at } role_permissions { uuid id PK uuid role_id FK uuid permission_id FK timestamp created_at uuid created_by FK } user_roles { uuid id PK uuid user_id FK uuid role_id FK timestamp assigned_at uuid assigned_by FK timestamp expires_at } ``` --- ## Tablas ### 1. roles Almacena los roles del sistema, tanto built-in como personalizados por tenant. ```sql CREATE TABLE core_rbac.roles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id), name VARCHAR(50) NOT NULL, slug VARCHAR(50) NOT NULL, description VARCHAR(500), is_built_in BOOLEAN NOT NULL DEFAULT false, is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by UUID REFERENCES core_users.users(id), updated_by UUID REFERENCES core_users.users(id), deleted_at TIMESTAMPTZ, deleted_by UUID REFERENCES core_users.users(id), CONSTRAINT uq_roles_tenant_name UNIQUE (tenant_id, name) WHERE deleted_at IS NULL, CONSTRAINT uq_roles_tenant_slug UNIQUE (tenant_id, slug) WHERE deleted_at IS NULL, CONSTRAINT chk_roles_name_length CHECK (char_length(name) >= 3), CONSTRAINT chk_roles_slug_format CHECK (slug ~ '^[a-z0-9_-]+$') ); -- Indices CREATE INDEX idx_roles_tenant_id ON core_rbac.roles(tenant_id) WHERE deleted_at IS NULL; CREATE INDEX idx_roles_slug ON core_rbac.roles(slug) WHERE deleted_at IS NULL; CREATE INDEX idx_roles_is_built_in ON core_rbac.roles(is_built_in); CREATE INDEX idx_roles_created_at ON core_rbac.roles(created_at DESC); -- Trigger para updated_at CREATE TRIGGER trg_roles_updated_at BEFORE UPDATE ON core_rbac.roles FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); -- Comentarios COMMENT ON TABLE core_rbac.roles IS 'Roles del sistema para RBAC'; COMMENT ON COLUMN core_rbac.roles.is_built_in IS 'true para roles del sistema (admin, user, etc.)'; COMMENT ON COLUMN core_rbac.roles.slug IS 'Identificador URL-friendly, unico por tenant'; ``` --- ### 2. permissions Catalogo maestro de permisos del sistema. Son globales (no por tenant). ```sql CREATE TABLE core_rbac.permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code VARCHAR(100) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, description VARCHAR(500), module VARCHAR(50) NOT NULL, parent_code VARCHAR(100) REFERENCES core_rbac.permissions(code), is_deprecated BOOLEAN NOT NULL DEFAULT false, sort_order INT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT chk_permissions_code_format CHECK (code ~ '^[a-z]+:[a-z_]+(:?:[a-z_]+)?$|^[a-z]+:\*$') ); -- Indices CREATE INDEX idx_permissions_module ON core_rbac.permissions(module); CREATE INDEX idx_permissions_parent ON core_rbac.permissions(parent_code); CREATE INDEX idx_permissions_deprecated ON core_rbac.permissions(is_deprecated); -- Comentarios COMMENT ON TABLE core_rbac.permissions IS 'Catalogo de permisos del sistema'; COMMENT ON COLUMN core_rbac.permissions.code IS 'Formato: modulo:accion o modulo:recurso:accion'; COMMENT ON COLUMN core_rbac.permissions.parent_code IS 'Para permisos wildcard (users:* -> users:read)'; ``` --- ### 3. role_permissions Tabla de union entre roles y permisos (M:N). ```sql CREATE TABLE core_rbac.role_permissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), role_id UUID NOT NULL REFERENCES core_rbac.roles(id) ON DELETE CASCADE, permission_id UUID NOT NULL REFERENCES core_rbac.permissions(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by UUID REFERENCES core_users.users(id), CONSTRAINT uq_role_permissions UNIQUE (role_id, permission_id) ); -- Indices CREATE INDEX idx_role_permissions_role ON core_rbac.role_permissions(role_id); CREATE INDEX idx_role_permissions_permission ON core_rbac.role_permissions(permission_id); -- Comentarios COMMENT ON TABLE core_rbac.role_permissions IS 'Asignacion de permisos a roles'; ``` --- ### 4. user_roles Tabla de union entre usuarios y roles (M:N). ```sql CREATE TABLE core_rbac.user_roles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES core_users.users(id) ON DELETE CASCADE, role_id UUID NOT NULL REFERENCES core_rbac.roles(id) ON DELETE CASCADE, assigned_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, assigned_by UUID REFERENCES core_users.users(id), expires_at TIMESTAMPTZ, CONSTRAINT uq_user_roles UNIQUE (user_id, role_id) ); -- Indices CREATE INDEX idx_user_roles_user ON core_rbac.user_roles(user_id); CREATE INDEX idx_user_roles_role ON core_rbac.user_roles(role_id); CREATE INDEX idx_user_roles_expires ON core_rbac.user_roles(expires_at) WHERE expires_at IS NOT NULL; -- Comentarios COMMENT ON TABLE core_rbac.user_roles IS 'Asignacion de roles a usuarios'; COMMENT ON COLUMN core_rbac.user_roles.expires_at IS 'Roles temporales con fecha de expiracion'; ``` --- ## Data Seed: Roles Built-in ```sql -- Roles del sistema (se crean para cada tenant nuevo) -- Ref: Odoo base_groups.xml - group_user, group_portal, group_public, group_system, group_erp_manager INSERT INTO core_rbac.roles (tenant_id, name, slug, description, is_built_in) VALUES (:tenant_id, 'Super Administrador', 'super_admin', 'Acceso total al sistema (Ref: Odoo group_system)', true), (:tenant_id, 'Propietario', 'tenant_owner', 'Propietario de la cuenta, gestiona billing y usuarios (MGN-015)', true), (:tenant_id, 'Administrador', 'admin', 'Gestion completa del tenant (Ref: Odoo group_erp_manager)', true), (:tenant_id, 'Gerente', 'manager', 'Supervision operativa y reportes', true), (:tenant_id, 'Usuario', 'user', 'Acceso basico al sistema (Ref: Odoo group_user)', true), (:tenant_id, 'Agente WhatsApp', 'whatsapp_agent', 'Atiende conversaciones de WhatsApp (MGN-017)', true), (:tenant_id, 'Usuario Portal', 'portal_user', 'Usuario externo con acceso limitado (Ref: Odoo group_portal)', true), (:tenant_id, 'Invitado', 'guest', 'Acceso de solo lectura limitado (Ref: Odoo group_public)', true); ``` --- ## Data Seed: Permisos del Sistema ```sql -- Permisos de Auth (MGN-001) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('auth:sessions:read', 'Ver sesiones', 'Ver sesiones activas del usuario', 'auth', 10), ('auth:sessions:revoke', 'Revocar sesiones', 'Cerrar sesiones activas', 'auth', 20); -- Permisos de Users (MGN-002) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('users:*', 'Todos los permisos de usuarios', 'Acceso completo a gestion de usuarios', 'users', 0), ('users:read', 'Leer usuarios', 'Ver listado y detalle de usuarios', 'users', 10), ('users:create', 'Crear usuarios', 'Crear nuevos usuarios', 'users', 20), ('users:update', 'Actualizar usuarios', 'Modificar datos de usuarios', 'users', 30), ('users:delete', 'Eliminar usuarios', 'Eliminar usuarios (soft delete)', 'users', 40), ('users:activate', 'Activar/Desactivar', 'Cambiar estado de usuarios', 'users', 50), ('users:export', 'Exportar usuarios', 'Exportar lista de usuarios a CSV', 'users', 60), ('users:import', 'Importar usuarios', 'Importar usuarios desde CSV', 'users', 70); -- Establecer parent para wildcards UPDATE core_rbac.permissions SET parent_code = 'users:*' WHERE module = 'users' AND code != 'users:*'; -- Permisos de Roles (MGN-003) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('roles:*', 'Todos los permisos de roles', 'Acceso completo a gestion de roles', 'roles', 0), ('roles:read', 'Leer roles', 'Ver listado y detalle de roles', 'roles', 10), ('roles:create', 'Crear roles', 'Crear nuevos roles personalizados', 'roles', 20), ('roles:update', 'Actualizar roles', 'Modificar roles existentes', 'roles', 30), ('roles:delete', 'Eliminar roles', 'Eliminar roles personalizados', 'roles', 40), ('roles:assign', 'Asignar roles', 'Asignar roles a usuarios', 'roles', 50), ('permissions:read', 'Ver permisos', 'Ver catalogo de permisos', 'roles', 60); UPDATE core_rbac.permissions SET parent_code = 'roles:*' WHERE module = 'roles' AND code NOT IN ('roles:*', 'permissions:read'); -- Permisos de Tenants (MGN-004) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('tenants:read', 'Ver tenant', 'Ver configuracion del tenant', 'tenants', 10), ('tenants:update', 'Actualizar tenant', 'Modificar configuracion del tenant', 'tenants', 20), ('tenants:billing', 'Facturacion', 'Gestionar facturacion del tenant', 'tenants', 30); -- Permisos de Settings (MGN-006) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('settings:read', 'Ver configuracion', 'Ver configuracion del sistema', 'settings', 10), ('settings:update', 'Modificar configuracion', 'Cambiar configuracion del sistema', 'settings', 20); -- Permisos de Audit (MGN-007) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('audit:read', 'Ver auditoria', 'Ver logs de auditoria', 'audit', 10), ('audit:export', 'Exportar auditoria', 'Exportar logs de auditoria', 'audit', 20); -- Permisos de Reports (MGN-009) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('reports:*', 'Todos los permisos de reportes', 'Acceso completo a reportes', 'reports', 0), ('reports:read', 'Ver reportes', 'Ver reportes del sistema', 'reports', 10), ('reports:create', 'Crear reportes', 'Crear reportes personalizados', 'reports', 20), ('reports:export', 'Exportar reportes', 'Exportar reportes a PDF/Excel', 'reports', 30), ('reports:schedule', 'Programar reportes', 'Programar envio de reportes', 'reports', 40); UPDATE core_rbac.permissions SET parent_code = 'reports:*' WHERE module = 'reports' AND code != 'reports:*'; -- Permisos de Financial (MGN-010) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('financial:*', 'Todos los permisos financieros', 'Acceso completo a modulo financiero', 'financial', 0), ('financial:accounts:read', 'Ver cuentas', 'Ver plan de cuentas', 'financial', 10), ('financial:accounts:manage', 'Gestionar cuentas', 'Administrar plan de cuentas', 'financial', 20), ('financial:transactions:read', 'Ver transacciones', 'Ver movimientos contables', 'financial', 30), ('financial:transactions:create', 'Crear transacciones', 'Registrar movimientos', 'financial', 40), ('financial:transactions:approve', 'Aprobar transacciones', 'Aprobar movimientos pendientes', 'financial', 50), ('financial:reports:read', 'Reportes financieros', 'Ver reportes financieros', 'financial', 60); UPDATE core_rbac.permissions SET parent_code = 'financial:*' WHERE module = 'financial' AND code != 'financial:*'; -- Permisos de Inventory (MGN-011) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('inventory:*', 'Todos los permisos de inventario', 'Acceso completo a inventario', 'inventory', 0), ('inventory:products:read', 'Ver productos', 'Ver catalogo de productos', 'inventory', 10), ('inventory:products:create', 'Crear productos', 'Agregar productos al catalogo', 'inventory', 20), ('inventory:products:update', 'Actualizar productos', 'Modificar productos', 'inventory', 30), ('inventory:products:delete', 'Eliminar productos', 'Eliminar productos del catalogo', 'inventory', 40), ('inventory:stock:read', 'Ver stock', 'Ver niveles de inventario', 'inventory', 50), ('inventory:stock:adjust', 'Ajustar stock', 'Realizar ajustes de inventario', 'inventory', 60), ('inventory:movements:read', 'Ver movimientos', 'Ver historial de movimientos', 'inventory', 70), ('inventory:movements:create', 'Crear movimientos', 'Registrar entradas/salidas', 'inventory', 80); UPDATE core_rbac.permissions SET parent_code = 'inventory:*' WHERE module = 'inventory' AND code != 'inventory:*'; -- Permisos de Portal (para usuarios externos) - Ref: Odoo portal module INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('portal:profile:read', 'Ver perfil propio', 'Ver datos del propio perfil', 'portal', 10), ('portal:profile:update', 'Actualizar perfil propio', 'Modificar datos del propio perfil', 'portal', 20), ('portal:documents:read', 'Ver documentos propios', 'Ver documentos asociados al usuario', 'portal', 30), ('portal:orders:read', 'Ver ordenes propias', 'Ver historial de ordenes', 'portal', 40), ('portal:invoices:read', 'Ver facturas propias', 'Ver facturas emitidas', 'portal', 50), ('portal:tickets:read', 'Ver tickets propios', 'Ver tickets de soporte', 'portal', 60), ('portal:tickets:create', 'Crear tickets', 'Crear nuevos tickets de soporte', 'portal', 70); -- Permisos de Billing (MGN-015) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('billing:*', 'Todos los permisos de billing', 'Acceso completo a facturacion SaaS', 'billing', 0), ('billing:subscription:read', 'Ver suscripcion', 'Ver plan y suscripcion actual', 'billing', 10), ('billing:subscription:manage', 'Gestionar suscripcion', 'Cambiar plan, cancelar, reactivar', 'billing', 20), ('billing:seats:manage', 'Gestionar asientos', 'Agregar/remover usuarios del plan', 'billing', 30), ('billing:payment_methods:read', 'Ver metodos de pago', 'Ver tarjetas y metodos guardados', 'billing', 40), ('billing:payment_methods:manage', 'Gestionar metodos de pago', 'Agregar/eliminar metodos de pago', 'billing', 50), ('billing:invoices:read', 'Ver facturas SaaS', 'Ver historial de facturas de la plataforma', 'billing', 60), ('billing:invoices:download', 'Descargar facturas', 'Descargar PDF/CFDI de facturas', 'billing', 70), ('billing:usage:read', 'Ver uso', 'Ver metricas de consumo del plan', 'billing', 80); UPDATE core_rbac.permissions SET parent_code = 'billing:*' WHERE module = 'billing' AND code != 'billing:*'; -- Permisos de Payment Integrations (MGN-016) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('payments:*', 'Todos los permisos de pagos', 'Acceso completo a integraciones de pago', 'payments', 0), ('payments:providers:read', 'Ver proveedores', 'Ver integraciones de pago configuradas', 'payments', 10), ('payments:providers:configure', 'Configurar proveedores', 'Conectar MercadoPago, Clip, Stripe', 'payments', 20), ('payments:terminals:read', 'Ver terminales', 'Ver terminales registradas', 'payments', 30), ('payments:terminals:manage', 'Gestionar terminales', 'Agregar/configurar terminales', 'payments', 40), ('payments:transactions:read', 'Ver transacciones', 'Ver historial de cobros', 'payments', 50), ('payments:transactions:process', 'Procesar pagos', 'Cobrar en terminal/gateway', 'payments', 60), ('payments:transactions:refund', 'Reembolsar', 'Procesar devoluciones', 'payments', 70), ('payments:reconciliation:read', 'Ver conciliacion', 'Ver estado de conciliacion', 'payments', 80), ('payments:reconciliation:manage', 'Gestionar conciliacion', 'Conciliar transacciones', 'payments', 90); UPDATE core_rbac.permissions SET parent_code = 'payments:*' WHERE module = 'payments' AND code != 'payments:*'; -- Permisos de WhatsApp (MGN-017) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('whatsapp:*', 'Todos los permisos de WhatsApp', 'Acceso completo a WhatsApp Business', 'whatsapp', 0), ('whatsapp:accounts:read', 'Ver cuentas', 'Ver cuentas de WhatsApp conectadas', 'whatsapp', 10), ('whatsapp:accounts:configure', 'Configurar cuentas', 'Conectar/desconectar cuentas WA', 'whatsapp', 20), ('whatsapp:templates:read', 'Ver templates', 'Ver plantillas de mensajes', 'whatsapp', 30), ('whatsapp:templates:manage', 'Gestionar templates', 'Crear/editar plantillas', 'whatsapp', 40), ('whatsapp:conversations:read', 'Ver conversaciones', 'Ver inbox de mensajes', 'whatsapp', 50), ('whatsapp:conversations:reply', 'Responder mensajes', 'Enviar mensajes en conversaciones', 'whatsapp', 60), ('whatsapp:conversations:assign', 'Asignar conversaciones', 'Asignar a otros agentes', 'whatsapp', 70), ('whatsapp:bulk:send', 'Envio masivo', 'Enviar campanas de marketing', 'whatsapp', 80), ('whatsapp:chatbot:read', 'Ver chatbot', 'Ver flujos de chatbot', 'whatsapp', 90), ('whatsapp:chatbot:manage', 'Gestionar chatbot', 'Crear/editar flujos', 'whatsapp', 100), ('whatsapp:analytics:read', 'Ver analiticas', 'Ver metricas de WhatsApp', 'whatsapp', 110); UPDATE core_rbac.permissions SET parent_code = 'whatsapp:*' WHERE module = 'whatsapp' AND code != 'whatsapp:*'; -- Permisos de AI Agents (MGN-018) INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES ('ai_agents:*', 'Todos los permisos de IA', 'Acceso completo a agentes de IA', 'ai_agents', 0), ('ai_agents:agents:read', 'Ver agentes', 'Ver agentes configurados', 'ai_agents', 10), ('ai_agents:agents:manage', 'Gestionar agentes', 'Crear/editar/eliminar agentes', 'ai_agents', 20), ('ai_agents:kb:read', 'Ver bases de conocimiento', 'Ver KBs del tenant', 'ai_agents', 30), ('ai_agents:kb:manage', 'Gestionar KB', 'Crear/editar KBs y documentos', 'ai_agents', 40), ('ai_agents:kb:upload', 'Subir documentos', 'Agregar documentos a KB', 'ai_agents', 50), ('ai_agents:tools:read', 'Ver herramientas', 'Ver tools disponibles', 'ai_agents', 60), ('ai_agents:tools:manage', 'Gestionar herramientas', 'Configurar tools para agentes', 'ai_agents', 70), ('ai_agents:conversations:read', 'Ver conversaciones IA', 'Ver historial de chats con IA', 'ai_agents', 80), ('ai_agents:feedback:read', 'Ver feedback', 'Ver calificaciones de respuestas', 'ai_agents', 90), ('ai_agents:feedback:manage', 'Gestionar feedback', 'Corregir/entrenar agente', 'ai_agents', 100), ('ai_agents:analytics:read', 'Ver analiticas IA', 'Ver metricas de agentes', 'ai_agents', 110), ('ai_agents:usage:read', 'Ver consumo tokens', 'Ver uso de tokens de IA', 'ai_agents', 120); UPDATE core_rbac.permissions SET parent_code = 'ai_agents:*' WHERE module = 'ai_agents' AND code != 'ai_agents:*'; ``` --- ## Data Seed: Permisos por Rol Built-in ```sql -- Funcion helper para asignar permisos CREATE OR REPLACE FUNCTION core_rbac.assign_permissions_to_role( p_tenant_id UUID, p_role_slug VARCHAR, p_permission_codes TEXT[] ) RETURNS void AS $$ DECLARE v_role_id UUID; v_permission_id UUID; v_code TEXT; BEGIN SELECT id INTO v_role_id FROM core_rbac.roles WHERE tenant_id = p_tenant_id AND slug = p_role_slug; FOREACH v_code IN ARRAY p_permission_codes LOOP SELECT id INTO v_permission_id FROM core_rbac.permissions WHERE code = v_code; IF v_permission_id IS NOT NULL THEN INSERT INTO core_rbac.role_permissions (role_id, permission_id) VALUES (v_role_id, v_permission_id) ON CONFLICT DO NOTHING; END IF; END LOOP; END; $$ LANGUAGE plpgsql; -- Super Admin: Todos los permisos (wildcard) SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'super_admin', ARRAY[ 'users:*', 'roles:*', 'permissions:read', 'tenants:read', 'tenants:update', 'tenants:billing', 'settings:read', 'settings:update', 'audit:read', 'audit:export', 'reports:*', 'financial:*', 'inventory:*', 'billing:*', 'payments:*', 'whatsapp:*', 'ai_agents:*' ]); -- Tenant Owner: Propietario de cuenta (MGN-015) SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'tenant_owner', ARRAY[ 'users:*', 'roles:read', 'roles:assign', 'tenants:read', 'tenants:update', 'tenants:billing', 'billing:*', 'payments:providers:read', 'payments:providers:configure', 'whatsapp:accounts:read', 'whatsapp:accounts:configure', 'whatsapp:analytics:read', 'ai_agents:agents:read', 'ai_agents:usage:read', 'ai_agents:analytics:read' ]); -- Admin: Gestion del tenant SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'admin', ARRAY[ 'users:*', 'roles:read', 'roles:create', 'roles:update', 'roles:delete', 'roles:assign', 'permissions:read', 'tenants:read', 'tenants:update', 'settings:read', 'settings:update', 'audit:read', 'reports:read', 'reports:export', 'payments:*', 'whatsapp:*', 'ai_agents:*' ]); -- Manager: Supervision SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'manager', ARRAY[ 'users:read', 'roles:read', 'permissions:read', 'audit:read', 'reports:read', 'reports:export', 'financial:accounts:read', 'financial:transactions:read', 'financial:reports:read', 'inventory:products:read', 'inventory:stock:read', 'inventory:movements:read', 'payments:transactions:read', 'payments:reconciliation:read', 'whatsapp:conversations:read', 'whatsapp:analytics:read', 'ai_agents:conversations:read', 'ai_agents:feedback:read', 'ai_agents:analytics:read' ]); -- User: Acceso basico SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'user', ARRAY[ 'reports:read', 'payments:transactions:read' ]); -- WhatsApp Agent: Atiende conversaciones (MGN-017) SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'whatsapp_agent', ARRAY[ 'whatsapp:conversations:read', 'whatsapp:conversations:reply', 'whatsapp:templates:read' ]); -- Portal User: Acceso limitado para usuarios externos (Ref: Odoo group_portal) SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'portal_user', ARRAY[ 'portal:profile:read', 'portal:profile:update', 'portal:documents:read', 'portal:orders:read', 'portal:invoices:read', 'portal:tickets:read', 'portal:tickets:create' ]); -- Guest: Solo lectura minima (Ref: Odoo group_public) SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'guest', ARRAY[ -- Sin permisos especificos, solo acceso a dashboard publico ]); ``` --- ## Row Level Security (RLS) ```sql -- Habilitar RLS ALTER TABLE core_rbac.roles ENABLE ROW LEVEL SECURITY; ALTER TABLE core_rbac.role_permissions ENABLE ROW LEVEL SECURITY; ALTER TABLE core_rbac.user_roles ENABLE ROW LEVEL SECURITY; -- Politicas para roles CREATE POLICY roles_tenant_isolation ON core_rbac.roles FOR ALL USING (tenant_id = current_setting('app.tenant_id')::uuid); CREATE POLICY roles_select_built_in ON core_rbac.roles FOR SELECT USING (is_built_in = true); -- Politicas para role_permissions (via rol) CREATE POLICY role_permissions_tenant_isolation ON core_rbac.role_permissions FOR ALL USING ( role_id IN ( SELECT id FROM core_rbac.roles WHERE tenant_id = current_setting('app.tenant_id')::uuid ) ); -- Politicas para user_roles (via usuario) CREATE POLICY user_roles_tenant_isolation ON core_rbac.user_roles FOR ALL USING ( user_id IN ( SELECT id FROM core_users.users WHERE tenant_id = current_setting('app.tenant_id')::uuid ) ); ``` --- ## Funciones de Utilidad ### Verificar Permiso de Usuario ```sql CREATE OR REPLACE FUNCTION core_rbac.user_has_permission( p_user_id UUID, p_permission_code VARCHAR ) RETURNS BOOLEAN AS $$ DECLARE v_has_permission BOOLEAN; v_module VARCHAR; BEGIN -- Verificar permiso directo SELECT EXISTS ( SELECT 1 FROM core_rbac.user_roles ur JOIN core_rbac.role_permissions rp ON rp.role_id = ur.role_id JOIN core_rbac.permissions p ON p.id = rp.permission_id WHERE ur.user_id = p_user_id AND (ur.expires_at IS NULL OR ur.expires_at > NOW()) AND p.code = p_permission_code ) INTO v_has_permission; IF v_has_permission THEN RETURN true; END IF; -- Verificar wildcard del modulo v_module := split_part(p_permission_code, ':', 1); SELECT EXISTS ( SELECT 1 FROM core_rbac.user_roles ur JOIN core_rbac.role_permissions rp ON rp.role_id = ur.role_id JOIN core_rbac.permissions p ON p.id = rp.permission_id WHERE ur.user_id = p_user_id AND (ur.expires_at IS NULL OR ur.expires_at > NOW()) AND p.code = v_module || ':*' ) INTO v_has_permission; RETURN v_has_permission; END; $$ LANGUAGE plpgsql STABLE; ``` ### Obtener Permisos Efectivos de Usuario ```sql CREATE OR REPLACE FUNCTION core_rbac.get_user_permissions( p_user_id UUID ) RETURNS TABLE (permission_code VARCHAR, source_role VARCHAR) AS $$ BEGIN RETURN QUERY SELECT DISTINCT p.code, r.name FROM core_rbac.user_roles ur JOIN core_rbac.roles r ON r.id = ur.role_id JOIN core_rbac.role_permissions rp ON rp.role_id = r.id JOIN core_rbac.permissions p ON p.id = rp.permission_id WHERE ur.user_id = p_user_id AND (ur.expires_at IS NULL OR ur.expires_at > NOW()) AND r.is_active = true AND p.is_deprecated = false ORDER BY p.code; END; $$ LANGUAGE plpgsql STABLE; ``` --- ## Vistas ### Vista: Roles con Conteo de Usuarios ```sql CREATE VIEW core_rbac.vw_roles_summary AS SELECT r.id, r.tenant_id, r.name, r.slug, r.description, r.is_built_in, r.is_active, COUNT(DISTINCT ur.user_id) AS users_count, COUNT(DISTINCT rp.permission_id) AS permissions_count, r.created_at FROM core_rbac.roles r LEFT JOIN core_rbac.user_roles ur ON ur.role_id = r.id LEFT JOIN core_rbac.role_permissions rp ON rp.role_id = r.id WHERE r.deleted_at IS NULL GROUP BY r.id; ``` ### Vista: Permisos Agrupados por Modulo ```sql CREATE VIEW core_rbac.vw_permissions_by_module AS SELECT module, jsonb_agg( jsonb_build_object( 'id', id, 'code', code, 'name', name, 'description', description, 'isDeprecated', is_deprecated ) ORDER BY sort_order ) AS permissions FROM core_rbac.permissions WHERE is_deprecated = false GROUP BY module ORDER BY module; ``` --- ## Resumen de Tablas | Tabla | Columnas | Descripcion | |-------|----------|-------------| | roles | 12 | Roles del sistema (built-in y custom) | | permissions | 9 | Catalogo maestro de permisos | | role_permissions | 5 | Union M:N roles-permisos | | user_roles | 6 | Union M:N usuarios-roles | **Total: 4 tablas, 32 columnas** --- ## Resumen de Roles Built-in | Rol | Slug | Descripcion | |-----|------|-------------| | Super Administrador | super_admin | Acceso total al sistema | | Propietario | tenant_owner | Gestiona billing y usuarios (MGN-015) | | Administrador | admin | Gestion completa del tenant | | Gerente | manager | Supervision operativa y reportes | | Usuario | user | Acceso basico al sistema | | Agente WhatsApp | whatsapp_agent | Atiende conversaciones (MGN-017) | | Usuario Portal | portal_user | Usuario externo con acceso limitado | | Invitado | guest | Solo lectura limitada | --- ## Resumen de Permisos por Modulo | Modulo | Permisos | Descripcion | |--------|----------|-------------| | auth | 2 | Sesiones y tokens | | users | 8 | Gestion de usuarios | | roles | 7 | Roles y permisos | | tenants | 3 | Configuracion tenant | | settings | 2 | Configuracion sistema | | audit | 2 | Logs de auditoria | | reports | 5 | Reportes | | financial | 7 | Modulo financiero | | inventory | 9 | Inventario | | portal | 7 | Usuarios externos | | billing | 9 | Billing SaaS (MGN-015) | | payments | 10 | Pagos POS (MGN-016) | | whatsapp | 12 | WhatsApp Business (MGN-017) | | ai_agents | 13 | AI Agents (MGN-018) | **Total: 96 permisos** --- ## Historial | Version | Fecha | Autor | Cambios | |---------|-------|-------|---------| | 1.0 | 2025-12-05 | System | Creacion inicial | | 1.1 | 2025-12-05 | System | Agregar rol portal_user y permisos portal:* (Ref: Odoo group_portal) | | 1.2 | 2025-12-05 | System | Roles: tenant_owner, whatsapp_agent. Permisos: billing:*, payments:*, whatsapp:*, ai_agents:* (MGN-015/016/017/018) |