from __future__ import annotations from apps.workspaces.models import Workspace, WorkspaceMembership WORKSPACE_VIEW = "workspace.view" WORKSPACE_EDIT = "workspace.edit" WORKSPACE_DELETE = "workspace.delete" WORKSPACE_LOGS_VIEW = "workspace.logs.view" WORKSPACE_MEMBERS_VIEW = "workspace.members.view" WORKSPACE_MEMBERS_ADD = "workspace.members.add" WORKSPACE_MEMBERS_REMOVE = "workspace.members.remove" WORKSPACE_MEMBERS_CHANGE_ROLE = "workspace.members.change_role" CLIENTS_VIEW = "clients.view" CLIENTS_CREATE = "clients.create" CLIENTS_EDIT = "clients.edit" CLIENTS_DELETE = "clients.delete" TAGS_VIEW = "tags.view" TAGS_CREATE = "tags.create" TAGS_EDIT = "tags.edit" TAGS_DELETE = "tags.delete" PROJECTS_VIEW = "projects.view" PROJECTS_CREATE = "projects.create" PROJECTS_EDIT = "projects.edit" PROJECTS_DELETE = "projects.delete" PROJECTS_ARCHIVE = "projects.archive" TIME_ENTRIES_VIEW_OWN = "time_entries.view_own" TIME_ENTRIES_MANAGE_OWN = "time_entries.manage_own" WORKSPACE_ROLE_CAPABILITIES = { WorkspaceMembership.Role.OWNER: { WORKSPACE_VIEW, WORKSPACE_EDIT, WORKSPACE_DELETE, WORKSPACE_LOGS_VIEW, WORKSPACE_MEMBERS_VIEW, WORKSPACE_MEMBERS_ADD, WORKSPACE_MEMBERS_REMOVE, WORKSPACE_MEMBERS_CHANGE_ROLE, CLIENTS_VIEW, CLIENTS_CREATE, CLIENTS_EDIT, CLIENTS_DELETE, TAGS_VIEW, TAGS_CREATE, TAGS_EDIT, TAGS_DELETE, PROJECTS_VIEW, PROJECTS_CREATE, PROJECTS_EDIT, PROJECTS_DELETE, PROJECTS_ARCHIVE, TIME_ENTRIES_VIEW_OWN, TIME_ENTRIES_MANAGE_OWN, }, WorkspaceMembership.Role.ADMIN: { WORKSPACE_VIEW, WORKSPACE_EDIT, WORKSPACE_LOGS_VIEW, WORKSPACE_MEMBERS_VIEW, WORKSPACE_MEMBERS_ADD, WORKSPACE_MEMBERS_REMOVE, WORKSPACE_MEMBERS_CHANGE_ROLE, CLIENTS_VIEW, CLIENTS_CREATE, CLIENTS_EDIT, CLIENTS_DELETE, TAGS_VIEW, TAGS_CREATE, TAGS_EDIT, TAGS_DELETE, PROJECTS_VIEW, PROJECTS_CREATE, PROJECTS_EDIT, PROJECTS_DELETE, PROJECTS_ARCHIVE, TIME_ENTRIES_VIEW_OWN, TIME_ENTRIES_MANAGE_OWN, }, WorkspaceMembership.Role.MEMBER: { WORKSPACE_VIEW, CLIENTS_VIEW, TAGS_VIEW, TAGS_CREATE, PROJECTS_VIEW, TIME_ENTRIES_VIEW_OWN, TIME_ENTRIES_MANAGE_OWN, }, WorkspaceMembership.Role.GUEST: { WORKSPACE_VIEW, CLIENTS_VIEW, TAGS_VIEW, PROJECTS_VIEW, TIME_ENTRIES_VIEW_OWN, TIME_ENTRIES_MANAGE_OWN, }, } def get_workspace_membership(user, workspace: Workspace) -> WorkspaceMembership | None: if not user or not user.is_authenticated: return None return WorkspaceMembership.objects.filter( workspace=workspace, user=user, is_active=True, is_deleted=False, ).first() def get_workspace_role(user, workspace: Workspace) -> str | None: if not user or not user.is_authenticated: return None if workspace.owner_id == user.id: return WorkspaceMembership.Role.OWNER membership = get_workspace_membership(user, workspace) return getattr(membership, "role", None) def has_workspace_capability(user, workspace: Workspace, capability: str) -> bool: role = get_workspace_role(user, workspace) if not role: return False return capability in WORKSPACE_ROLE_CAPABILITIES.get(role, set()) def has_project_capability(user, project, capability: str) -> bool: return has_workspace_capability(user, project.workspace, capability) def can_delete_workspace_object(user, obj, capability: str) -> bool: workspace = getattr(obj, "workspace", None) if workspace is None: return False if not has_workspace_capability(user, workspace, capability): return False actor_role = get_workspace_role(user, workspace) if actor_role == WorkspaceMembership.Role.OWNER: return True return getattr(obj, "created_by_id", None) == getattr(user, "id", None) def can_manage_workspace_members(user, workspace: Workspace) -> bool: return has_workspace_capability(user, workspace, WORKSPACE_MEMBERS_CHANGE_ROLE) def can_assign_workspace_role(user, workspace: Workspace, role: str) -> bool: actor_role = get_workspace_role(user, workspace) if actor_role == WorkspaceMembership.Role.OWNER: return True if actor_role == WorkspaceMembership.Role.ADMIN: return role not in { WorkspaceMembership.Role.OWNER, WorkspaceMembership.Role.ADMIN, } return False def can_change_workspace_membership(user, membership: WorkspaceMembership, *, new_role: str | None = None) -> bool: workspace = membership.workspace actor_role = get_workspace_role(user, workspace) if actor_role not in { WorkspaceMembership.Role.OWNER, WorkspaceMembership.Role.ADMIN, }: return False if membership.user_id == user.id: return False target_is_canonical_owner = workspace.owner_id == membership.user_id target_is_owner_role = membership.role == WorkspaceMembership.Role.OWNER target_is_admin_role = membership.role == WorkspaceMembership.Role.ADMIN if actor_role == WorkspaceMembership.Role.ADMIN: if target_is_owner_role or target_is_admin_role or target_is_canonical_owner: return False if new_role in { WorkspaceMembership.Role.OWNER, WorkspaceMembership.Role.ADMIN, }: return False return True if target_is_canonical_owner: return False if new_role == WorkspaceMembership.Role.OWNER and workspace.owner_id != user.id: return False return True