erp-core/docs/04-modelado/domain-models/auth-domain.md

268 lines
6.4 KiB
Markdown

# MODELO DE DOMINIO: Autenticación y Autorización
**Módulos:** MGN-001 (Fundamentos), MGN-002 (Empresas)
**Fecha:** 2025-11-24
**Referencia Odoo:** base, auth_signup
**Referencia Gamilit:** auth_management schema
---
## Diagrama de Entidades (Texto UML)
```
[Tenant]
- id: UUID (PK)
- name: String
- subdomain: String
- schema_name: String
- status: ENUM (active, suspended)
- settings: JSONB
1 <----> * [Company]
1 <----> * [User]
[Company]
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: String
- tax_id: String
- currency_id: UUID (FK)
- settings: JSONB
1 <----> * [User]
[User]
- id: UUID (PK)
- tenant_id: UUID (FK)
- email: String (UNIQUE per tenant)
- password_hash: String
- full_name: String
- status: ENUM (active, inactive, suspended)
- is_superuser: Boolean
* <----> * [Role] (through UserRole)
1 <----> * [Session]
[Role]
- id: UUID (PK)
- tenant_id: UUID (FK)
- name: String
- code: String
- description: Text
* <----> * [Permission] (through RolePermission)
[Permission]
- id: UUID (PK)
- resource: String (ej: 'purchase_orders')
- action: ENUM (create, read, update, delete, approve)
- description: Text
[Session]
- id: UUID (PK)
- user_id: UUID (FK)
- token: String
- expires_at: Timestamp
- ip_address: String
- user_agent: String
[UserRole] (many-to-many)
- user_id: UUID (FK)
- role_id: UUID (FK)
[RolePermission] (many-to-many)
- role_id: UUID (FK)
- permission_id: UUID (FK)
```
## Entidades Principales
### 1. Tenant (Multi-Tenancy)
**Descripción:** Representa un tenant (empresa matriz o grupo). Cada tenant tiene su propio schema PostgreSQL.
**Atributos:**
- `id`: Identificador único
- `name`: Nombre del tenant
- `subdomain`: Subdominio (ej: 'acme' → acme.erp.com)
- `schema_name`: Nombre del schema PostgreSQL (ej: 'tenant_acme')
- `status`: Estado (active, suspended)
- `settings`: Configuración JSON (logo, tema, etc.)
**Relaciones:**
- 1 Tenant → N Companies
- 1 Tenant → N Users
**Patrón Odoo:** Similar a res.company pero en un nivel superior
**Patrón Gamilit:** Implementado con schema-level isolation
**RLS Policy:**
```sql
-- Usuarios solo ven su tenant
CREATE POLICY tenant_isolation ON tenants
USING (id = get_current_tenant_id());
```
### 2. Company (Empresa)
**Descripción:** Empresa dentro de un tenant. Permite multi-empresa.
**Atributos:**
- `id`: UUID
- `tenant_id`: Tenant propietario
- `name`: Nombre empresa
- `tax_id`: RFC/Tax ID
- `currency_id`: Moneda por defecto
- `settings`: JSONB (dirección, contacto, etc.)
**Relaciones:**
- N Companies → 1 Tenant
- 1 Company → N Users (usuarios pueden estar en múltiples empresas)
**Patrón Odoo:** res.company
**Validaciones:**
- Un tenant debe tener al menos 1 company
- tax_id único por tenant
### 3. User (Usuario)
**Descripción:** Usuario del sistema con roles y permisos.
**Atributos:**
- `id`: UUID
- `tenant_id`: Tenant propietario
- `email`: Email (único por tenant)
- `password_hash`: bcrypt hash
- `full_name`: Nombre completo
- `status`: active, inactive, suspended
- `is_superuser`: Admin total del tenant
**Relaciones:**
- N Users → 1 Tenant
- N Users ←→ N Roles (many-to-many)
- 1 User → N Sessions
**Patrón Odoo:** res.users
**Patrón Gamilit:** auth.users con tenant_id
**Validaciones:**
- email único por tenant (no global)
- password mínimo 8 caracteres
- is_superuser requiere aprobación
**RLS Policy:**
```sql
CREATE POLICY users_own_tenant ON users
USING (tenant_id = get_current_tenant_id());
```
### 4. Role (Rol)
**Descripción:** Rol con permisos asignados (RBAC).
**Atributos:**
- `id`: UUID
- `tenant_id`: Tenant propietario
- `name`: Nombre del rol
- `code`: Código único (ej: 'admin', 'buyer')
- `description`: Descripción
**Relaciones:**
- N Roles → 1 Tenant
- N Roles ←→ N Permissions
**Patrón Odoo:** res.groups
**Roles predefinidos:**
- `admin`: Administrador del tenant
- `manager`: Gerente (full access transaccional)
- `user`: Usuario normal
- `readonly`: Solo lectura
### 5. Permission (Permiso)
**Descripción:** Permiso granular sobre recursos.
**Atributos:**
- `id`: UUID
- `resource`: Recurso (tabla/endpoint) (ej: 'purchase_orders')
- `action`: create, read, update, delete, approve, cancel
**Ejemplo de permisos:**
```
Resource: purchase_orders
Actions: create, read, update, delete, approve
```
**Patrón Odoo:** ir.model.access + ir.rule
**Implementación:** Validado en backend + RLS policies en BD
### 6. Session (Sesión)
**Descripción:** Sesión JWT de usuario.
**Atributos:**
- `id`: UUID
- `user_id`: Usuario propietario
- `token`: JWT token
- `expires_at`: Expiración
- `ip_address`: IP de origen
- `user_agent`: Navegador
**Patrón Gamilit:** auth.sessions
## Reglas de Negocio
### RN-AUTH-001: Multi-Tenancy Obligatorio
- TODO usuario pertenece a exactamente 1 tenant
- TODO dato transaccional tiene tenant_id
- Aislamiento completo entre tenants (schema-level)
### RN-AUTH-002: RBAC Granular
- Usuario puede tener múltiples roles
- Permisos se calculan como UNION de permisos de roles
- is_superuser bypassa todos los checks
### RN-AUTH-003: Sesiones JWT
- Expiración: 8 horas
- Refresh token: 30 días
- Invalidación: Logout o expiración
### RN-AUTH-004: Passwords Seguros
- Mínimo 8 caracteres
- Debe incluir: mayúscula, minúscula, número
- bcrypt hash (cost factor 12)
## Casos de Uso Principales
1. **UC-AUTH-001:** Login de usuario
2. **UC-AUTH-002:** Registro de nuevo tenant
3. **UC-AUTH-003:** Asignar rol a usuario
4. **UC-AUTH-004:** Validar permisos para acción
5. **UC-AUTH-005:** Reset password
6. **UC-AUTH-006:** Cambiar de empresa (multi-company)
## Validaciones y Constraints
```sql
-- Email único por tenant
UNIQUE (tenant_id, email)
-- Status válidos
CHECK (status IN ('active', 'inactive', 'suspended'))
-- Al menos 1 admin por tenant
-- (trigger custom)
-- Session expiration
CHECK (expires_at > created_at)
```
## Índices Requeridos
```sql
CREATE INDEX idx_users_tenant_id ON auth.users(tenant_id);
CREATE INDEX idx_users_email ON auth.users(email);
CREATE INDEX idx_sessions_user_id ON auth.sessions(user_id);
CREATE INDEX idx_sessions_token ON auth.sessions(token);
CREATE INDEX idx_user_roles_user_id ON auth.user_roles(user_id);
```
## Referencias
- [ALCANCE-POR-MODULO.md - MGN-001](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-001)
- [odoo-base-analysis.md](../../00-analisis-referencias/odoo/odoo-base-analysis.md)
- [ADR-006: RBAC](../../adr/ADR-006-rbac-sistema-permisos.md)