Odoo · Seguridad · Permisos
La seguridad en Odoo tiene dos capas: ACL (¿puede este grupo crear/leer/escribir/borrar este modelo?) y record rules (¿qué registros concretos ve?). Configurarlas mal es la causa más frecuente de «el usuario no ve nada» o, peor, de fugas de datos entre clientes.
Panorama
Dos capas complementarias
| Capa | Archivo / modelo | Pregunta que responde |
|---|---|---|
| ACL | security/ir.model.access.csv | ¿El grupo tiene permiso CRUD sobre el modelo? |
| Record rules | security/*_rules.xml → ir.rule | ¿Qué filas del modelo puede ver/modificar? |
| Grupos | security/*_groups.xml → res.groups | ¿Quién es «Profesional», «Cliente», «Admin»? |
Sin ACL, el modelo no existe para el usuario. Con ACL pero sin record rules, ve todos los registros del modelo. En datos sensibles (menores, salud, contratos), siempre necesitas ambas capas.
Paso 1
Definir grupos de seguridad
<!-- security/diagnostic_groups.xml -->
<record id="group_diagnostic_user" model="res.groups">
<field name="name">Diagnostic User</field>
<field name="category_id" ref="base.module_category_services"/>
</record>
<record id="group_diagnostic_professional" model="res.groups">
<field name="name">Diagnostic Professional</field>
<field name="category_id" ref="base.module_category_services"/>
<field name="implied_ids" eval="[(4, ref('group_diagnostic_user'))]"/>
</record>
implied_ids hereda permisos: un profesional incluye automáticamente los del usuario base. Evita duplicar ACL para cada nivel.
Paso 2
ACL en ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_diagnostic_test_user,diagnostic.test.user,model_diagnostic_test,group_diagnostic_user,1,0,0,0
access_diagnostic_test_prof,diagnostic.test.professional,model_diagnostic_test,group_diagnostic_professional,1,1,1,0
access_diagnostic_test_manager,diagnostic.test.manager,model_diagnostic_test,base.group_system,1,1,1,1
Convención de id: access_<modelo>_<grupo>. El model_id:id usa el prefijo model_ + nombre del modelo con puntos sustituidos por guiones bajos.
Paso 3
Record rules por dominio
Ejemplo: un cliente solo ve sus diagnósticos; un profesional solo los asignados:
<record id="diagnostic_test_rule_client" model="ir.rule">
<field name="name">Diagnostic Test: client own records</field>
<field name="model_id" ref="model_diagnostic_test"/>
<field name="domain_force">[('partner_id', '=', user.partner_id.id)]</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="diagnostic_test_rule_professional" model="ir.rule">
<field name="name">Diagnostic Test: assigned professional</field>
<field name="model_id" ref="model_diagnostic_test"/>
<field name="domain_force">[('professional_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('group_diagnostic_professional'))]"/>
</record>
Orden
Orden en __manifest__.py
'data': [
'security/diagnostic_groups.xml', # 1. Grupos primero
'security/ir.model.access.csv', # 2. ACL
'security/diagnostic_rules.xml', # 3. Record rules (referencian grupos)
'views/diagnostic_test_views.xml',
],
Las record rules referencian group_id y model_id que deben existir. Cargar vistas antes que seguridad provoca errores de instalación o permisos temporales incorrectos.
Depuración
Checklist cuando «no ve registros»
| Síntoma | Causa probable | Acción |
|---|---|---|
| Access Error al abrir menú | Sin ACL para su grupo | Añadir línea en CSV |
| Lista vacía sin error | Record rule demasiado restrictiva | Revisar domain_force con ese usuario |
| Ve registros de otros clientes | Falta record rule o dominio incorrecto | Añadir regla para portal / multi-company |
| Admin ve todo, usuario no | base.group_system bypass rules | Probar con usuario de prueba real |
| Funciona en dev, falla en prod | Grupos no asignados al usuario | Verificar res.users → grupos |
# Shell de depuración (entorno de prueba)
user = env['res.users'].browse(UID)
env['diagnostic.test'].with_user(user).search([]) # ¿Qué ve realmente?
sudo() ignora record rules. Para probar permisos, usa with_user(usuario), nunca sudo() (lo veremos en la siguiente entrada de la serie).
Multi-company
Reglas globales vs por grupo
Odoo aplica record rules con operador AND entre grupos del usuario y OR entre reglas del mismo grupo. En multi-compañía, la regla estándar ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] suele añadirse automáticamente.
Si defines reglas propias, no olvides incluir la restricción de compañía o podrías filtrar registros de forma inconsistente entre módulos.
Resumen
Crea grupos con herencia (implied_ids), define ACL restrictivos en CSV y añade record rules con dominios que reflejen el negocio real. Carga seguridad antes que vistas, prueba con with_user() y documenta qué ve cada rol. La combinación ACL + rules es la base de cualquier módulo con datos sensibles.