permisos de agenda: selector de acciones por usuario (ver, agendar, editar, eliminar, visitas)
- roles.py: AGENDA_PERMS, AGENDA_PERM_KEYS, WRITE_RANK_MIN, agenda_defaults_for, resolve_agenda_perms, AGENDA_STATUS_PERM, AGENDA_STATE_PERM, _agenda_status_perm - frontend_user.py: campo agenda_perms, _agenda_perms_list(), to_public_dict() expone agenda_perms y agenda_perms_custom - auth.py: require_agenda(perm) decorador + agenda_perm_error() - frontend.py: @require_agenda aplicado a 22 endpoints + branch en update_appointment_status por accion - auth_users_update: whitelist acepta agenda_perms - Odoo upgrade: columna skeen_frontend_user.agenda_perms - Frontend: types AgendaPerm, AGENDA_PERM_ITEMS, canAgenda() en auth.tsx - UsuariosPanel: checkboxes de permisos de agenda en modal de permisos - Agenda.tsx: gates por permiso en crear/editar/eliminar/visitas/dragdrop/bloqueos - EstadoChecklist/BloqueosModal/ListaEspera: hide actions without permission Verificacion API: 18/18 tests OK (admin hereda, recorte=403, lect=403, restore=null)
This commit is contained in:
@@ -15,7 +15,7 @@ import time
|
||||
|
||||
from odoo.http import request, Response
|
||||
|
||||
from ..roles import ROLE_RANKS, get_menu_roles
|
||||
from ..roles import ROLE_RANKS, get_menu_roles, AGENDA_PERMS, resolve_agenda_perms
|
||||
|
||||
TTL_SECONDS = 60 * 60 * 12 # 12 horas
|
||||
SECRET_KEY = 'skeen.frontend.secret'
|
||||
@@ -182,3 +182,46 @@ def require_role(min_role='lectura'):
|
||||
return func(self, *args, **kw)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Permisos de ACCION dentro de la Agenda (selector por usuario).
|
||||
#
|
||||
# require_role sigue siendo el guard de rango: el token no lleva permisos,
|
||||
# asi que no hay listas cacheadas que se puedan desincronizar. Este decorador
|
||||
# REFINA dentro de la agenda y solo puede QUITAR acceso, nunca darlo por
|
||||
# encima del rango del rol.
|
||||
# ---------------------------------------------------------------------------
|
||||
AGENDA_PERM_LABELS = dict(AGENDA_PERMS)
|
||||
|
||||
|
||||
def agenda_perm_error(user, perm):
|
||||
"""Response 403 si la accion no esta permitida; None si si lo esta."""
|
||||
if user is None or perm not in resolve_agenda_perms(user):
|
||||
label = AGENDA_PERM_LABELS.get(perm, perm)
|
||||
return _json({'status': 'error', 'message': 'Sin permiso: %s' % label}, 403)
|
||||
return None
|
||||
|
||||
|
||||
def require_agenda(perm):
|
||||
"""Decorator: exige token valido y la accion de agenda indicada.
|
||||
|
||||
Se apila por DEBAJO de require_role (decorador interno), de modo que el
|
||||
control de rango del rol ya corrió y solo falta refinar la acción concreta.
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(self, *args, **kw):
|
||||
if request.httprequest.method == 'OPTIONS':
|
||||
return _json({}, 200)
|
||||
user = current_user(request.env)
|
||||
if not user:
|
||||
return _json({'status': 'error', 'message': 'No autenticado'}, 401)
|
||||
denied = agenda_perm_error(user, perm)
|
||||
if denied is not None:
|
||||
return denied
|
||||
request.frontend_user = user
|
||||
return func(self, *args, **kw)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
Reference in New Issue
Block a user