Ruolo supervisor

This commit is contained in:
2026-03-27 14:43:42 +01:00
parent ab6db28449
commit d7ae840ac6
9 changed files with 166 additions and 81 deletions
+6 -4
View File
@@ -77,15 +77,15 @@ async def list_mailboxes(
"""
svc = _svc(db)
if current_user.is_admin:
# Admin: tutte le caselle del tenant
if current_user.is_supervisor_or_admin:
# Admin e supervisor: tutte le caselle del tenant
items, total = await svc.list_mailboxes(
tenant_id=current_user.tenant_id,
page=page,
page_size=page_size,
)
else:
# Operatori: caselle con permesso
# Operator e readonly: caselle con permesso esplicito
from app.services.permission_service import PermissionService
perm_svc = PermissionService(db)
visible_ids = await perm_svc.get_visible_mailboxes(current_user)
@@ -140,7 +140,9 @@ async def get_unread_counts(
from app.models.message import Message
# Determina le caselle visibili
if current_user.is_admin:
# Admin e supervisor: nessun filtro (accesso a tutto il tenant)
# Operator e readonly: solo caselle con permesso esplicito can_read
if current_user.is_supervisor_or_admin:
visible_ids = None # nessun filtro
else:
from app.services.permission_service import PermissionService
+7 -4
View File
@@ -96,11 +96,14 @@ async def _get_visible_mailbox_ids(
user, db: AsyncSession
) -> Optional[list[uuid.UUID]]:
"""
Per utenti non-admin restituisce la lista di mailbox_id accessibili.
Restituisce None se l'utente e admin (accesso illimitato al tenant).
Per utenti non-admin/supervisor restituisce la lista di mailbox_id accessibili.
Restituisce None se l'utente e' admin o supervisor (accesso illimitato al tenant).
Admin e supervisor: None (nessun filtro, query diretta sull'intero tenant).
Operator e readonly: lista esplicita di caselle con can_read=True.
"""
if user.is_admin:
return None # nessun filtro per admin
if user.is_supervisor_or_admin:
return None # nessun filtro per admin e supervisor
from app.services.permission_service import PermissionService
perm_svc = PermissionService(db)
+15
View File
@@ -153,6 +153,20 @@ async def require_super_admin(
return current_user
async def require_supervisor_or_admin(
current_user: Annotated[User, Depends(get_current_user)],
) -> User:
"""
Richiede ruolo supervisor, admin o super_admin.
Il supervisor ha accesso in lettura implicito a tutte le caselle del tenant
ma non puo' gestire la configurazione (caselle, utenti, permessi, impostazioni).
"""
if not current_user.is_supervisor_or_admin:
raise ForbiddenError("Richiesto ruolo supervisore o amministratore")
return current_user
# ─── Protezione endpoint admin con X-Admin-Key header ─────────────────────────
async def verify_admin_key(
@@ -176,4 +190,5 @@ CurrentUser = Annotated[User, Depends(get_current_user)]
CurrentTenant = Annotated[Tenant, Depends(get_current_tenant)]
AdminUser = Annotated[User, Depends(require_admin)]
SuperAdminUser = Annotated[User, Depends(require_super_admin)]
SupervisorOrAdminUser = Annotated[User, Depends(require_supervisor_or_admin)]
DB = Annotated[AsyncSession, Depends(get_db)]
+10
View File
@@ -86,6 +86,16 @@ class User(Base):
def is_super_admin(self) -> bool:
return self.role == "super_admin"
@property
def is_supervisor(self) -> bool:
"""Ruolo supervisor: lettura implicita su tutte le caselle, senza poteri di gestione."""
return self.role == "supervisor"
@property
def is_supervisor_or_admin(self) -> bool:
"""True per super_admin, admin e supervisor (accesso in lettura a tutto il tenant)."""
return self.role in ("super_admin", "admin", "supervisor")
def __repr__(self) -> str:
return f"<User {self.email!r} role={self.role!r}>"
+22 -11
View File
@@ -27,9 +27,13 @@ class PermissionService:
async def get_visible_mailboxes(
self, user: User
) -> list[uuid.UUID]:
"""Restituisce gli UUID delle caselle visibili all'utente."""
if user.role in ("super_admin", "admin"):
# Admin vede tutte le caselle del tenant
"""Restituisce gli UUID delle caselle visibili all'utente.
Admin e supervisor vedono tutte le caselle del tenant.
Operator e readonly vedono solo le caselle con can_read=True esplicito.
"""
if user.role in ("super_admin", "admin", "supervisor"):
# Admin e supervisor vedono tutte le caselle del tenant
result = await self.db.execute(
select(Mailbox.id).where(
Mailbox.tenant_id == user.tenant_id,
@@ -38,7 +42,7 @@ class PermissionService:
)
return [row[0] for row in result.all()]
# Operatori: solo caselle con can_read=True
# Operator e readonly: solo caselle con can_read=True esplicito
result = await self.db.execute(
select(MailboxPermission.mailbox_id).where(
MailboxPermission.user_id == user.id,
@@ -50,9 +54,13 @@ class PermissionService:
async def check_can_read(
self, user: User, mailbox_id: uuid.UUID
) -> bool:
"""Verifica se l'utente può leggere i messaggi della casella."""
if user.role in ("super_admin", "admin"):
# Verifica solo che la casella appartenga al tenant
"""Verifica se l'utente puo' leggere i messaggi della casella.
Admin e supervisor hanno accesso implicito a tutte le caselle del tenant.
Operator e readonly richiedono permesso esplicito can_read.
"""
if user.role in ("super_admin", "admin", "supervisor"):
# Admin e supervisor: verifica solo che la casella appartenga al tenant
return await self._mailbox_belongs_to_tenant(mailbox_id, user.tenant_id)
perm = await self._get_permission(user.id, mailbox_id)
@@ -62,12 +70,15 @@ class PermissionService:
self, user: User, mailbox_id: uuid.UUID
) -> bool:
"""
Verifica se l'utente può inviare dalla casella.
Verifica se l'utente puo' inviare dalla casella.
L'accesso in invio è concesso se:
1. L'utente è admin del tenant, oppure
L'accesso in invio e' concesso se:
1. L'utente e' admin del tenant, oppure
2. L'utente ha un permesso diretto can_send sulla casella, oppure
3. L'utente è assegnato a una Virtual Box attiva che include la casella.
3. L'utente e' assegnato a una Virtual Box attiva che include la casella.
Nota: il supervisor NON ha invio implicito richiede can_send esplicito
come operator, ma diversamente da operator vede tutte le caselle.
"""
if user.role in ("super_admin", "admin"):
return await self._mailbox_belongs_to_tenant(mailbox_id, user.tenant_id)