diff --git a/app/services/role_service.py b/app/services/role_service.py index be48cfc..a832a0e 100644 --- a/app/services/role_service.py +++ b/app/services/role_service.py @@ -3,6 +3,7 @@ from __future__ import annotations import uuid +from datetime import UTC, datetime from typing import Any from sqlalchemy import select @@ -20,7 +21,7 @@ class RoleService: tenant_id: uuid.UUID, ) -> list[dict[str, Any]]: """List all roles in a tenant.""" - q = select(Role).where(Role.tenant_id == tenant_id) + q = select(Role).where(Role.tenant_id == tenant_id, Role.deleted_at.is_(None)) result = await db.execute(q) roles = result.scalars().all() return [self._role_to_dict(r) for r in roles] @@ -31,6 +32,7 @@ class RoleService: tenant_id: uuid.UUID, name: str, permissions: dict[str, Any], + denied_permissions: list[str] | None = None, field_permissions: dict[str, Any] | None = None, ) -> Role: """Create a new custom role.""" @@ -38,6 +40,7 @@ class RoleService: tenant_id=tenant_id, name=name, permissions=permissions, + denied_permissions=denied_permissions or [], field_permissions=field_permissions or {}, ) db.add(role) @@ -51,21 +54,35 @@ class RoleService: role_id: uuid.UUID, name: str | None = None, permissions: dict[str, Any] | None = None, + denied_permissions: list[str] | None = None, field_permissions: dict[str, Any] | None = None, ) -> Role | None: - """Update a role.""" - q = select(Role).where(Role.id == role_id, Role.tenant_id == tenant_id) + """Update a role. Increments permission_version on permission changes.""" + q = select(Role).where( + Role.id == role_id, + Role.tenant_id == tenant_id, + Role.deleted_at.is_(None), + ) result = await db.execute(q) role = result.scalar_one_or_none() if role is None: return None + version_bump = False if name is not None: role.name = name if permissions is not None: role.permissions = permissions + version_bump = True + if denied_permissions is not None: + role.denied_permissions = denied_permissions + version_bump = True if field_permissions is not None: role.field_permissions = field_permissions + version_bump = True + + if version_bump: + role.permission_version += 1 await db.flush() return role @@ -76,14 +93,18 @@ class RoleService: tenant_id: uuid.UUID, role_id: uuid.UUID, ) -> bool: - """Delete a role.""" - q = select(Role).where(Role.id == role_id, Role.tenant_id == tenant_id) + """Soft-delete a role.""" + q = select(Role).where( + Role.id == role_id, + Role.tenant_id == tenant_id, + Role.deleted_at.is_(None), + ) result = await db.execute(q) role = result.scalar_one_or_none() if role is None: return False - await db.delete(role) + role.deleted_at = datetime.now(UTC) await db.flush() return True @@ -93,7 +114,9 @@ class RoleService: "id": str(role.id), "name": role.name, "permissions": role.permissions, + "denied_permissions": role.denied_permissions, "field_permissions": role.field_permissions, + "permission_version": role.permission_version, }