""" Centralized permission checks for ResolutionFlow. Role hierarchy: super_admin > team_admin > engineer > viewer - super_admin: is_super_admin=True, full system access - team_admin: is_team_admin=True + valid team_id, manage team resources - engineer: role='engineer' (default), CRUD own trees/steps - viewer: role='viewer', read-only (can browse, run sessions, rate steps) """ from __future__ import annotations from typing import Optional, TYPE_CHECKING from uuid import UUID if TYPE_CHECKING: from app.models.user import User from app.models.tree import Tree from app.models.step_library import StepLibrary from app.models.category import TreeCategory from app.models.step_category import StepCategory ROLE_HIERARCHY = { "super_admin": 4, "team_admin": 3, "engineer": 2, "viewer": 1, } def get_effective_role(user: User) -> str: """Get the effective role considering is_super_admin and is_team_admin flags.""" if user.is_super_admin: return "super_admin" if user.is_team_admin and user.team_id is not None: return "team_admin" return user.role # "engineer" or "viewer" def has_minimum_role(user: User, minimum_role: str) -> bool: """Check if user has at least the specified role level.""" effective = get_effective_role(user) return ROLE_HIERARCHY.get(effective, 0) >= ROLE_HIERARCHY.get(minimum_role, 0) def can_create_content(user: User) -> bool: """Can the user create trees, steps, or other content? Viewers cannot.""" return has_minimum_role(user, "engineer") def can_edit_tree(user: User, tree: Tree) -> bool: """Can the user edit this specific tree?""" if user.is_super_admin: return True if not can_create_content(user): return False if tree.author_id == user.id: return True if user.is_team_admin and tree.team_id == user.team_id and user.team_id is not None: return True return False def can_delete_tree(user: User, tree: Tree) -> bool: """Can the user delete this tree? Super admin only.""" return user.is_super_admin def can_edit_step(user: User, step: StepLibrary) -> bool: """Can the user edit/delete this step?""" if user.is_super_admin: return True if not can_create_content(user): return False return step.created_by == user.id def can_manage_category(user: User, category: TreeCategory) -> bool: """Can the user edit/delete this category?""" if user.is_super_admin: return True if user.is_team_admin and category.team_id == user.team_id and user.team_id is not None: return True return False def can_manage_tree_tags(user: User, tree: Tree) -> bool: """Can the user add/remove tags on this tree?""" if user.is_super_admin: return True if not can_create_content(user): return False if tree.author_id == user.id: return True if user.is_team_admin and tree.team_id == user.team_id and user.team_id is not None: return True return False def can_access_tree(user: User, tree: Tree) -> bool: """Can the user access (view) this tree?""" if tree.is_default or tree.is_public: return True if tree.author_id == user.id: return True if tree.team_id == user.team_id and user.team_id is not None: return True if user.is_super_admin: return True return False def can_view_step(user: User, step: StepLibrary) -> bool: """Can the user view this step based on its visibility?""" if step.visibility == "public": return True if step.visibility == "private": return step.created_by == user.id if step.visibility == "team": return (step.team_id == user.team_id and user.team_id is not None) or user.is_super_admin return False def can_create_tag(user: User, team_id: Optional[UUID]) -> bool: """Can the user create a tag for the given scope? - Super admins can create global tags (team_id=None) or any team's tags - Engineers can create team tags for their own team - Viewers cannot create tags """ if user.is_super_admin: return True if not can_create_content(user): return False if team_id is not None and team_id == user.team_id: return True return False def can_create_category(user: User, team_id: Optional[UUID]) -> bool: """Can the user create a category for the given team? - Super admins can create global or any team's categories - Team admins can create categories for their own team """ if user.is_super_admin: return True if user.is_team_admin and team_id == user.team_id and user.team_id is not None: return True return False def can_manage_step_category(user: User, category: StepCategory) -> bool: """Can the user edit/delete this step category?""" if user.is_super_admin: return True if user.is_team_admin and category.team_id == user.team_id and user.team_id is not None: return True return False def can_create_step_category(user: User, team_id: Optional[UUID]) -> bool: """Can the user create a step category for the given team? - Super admins can create global or any team's step categories - Team admins can create step categories for their own team """ if user.is_super_admin: return True if user.is_team_admin and team_id == user.team_id and user.team_id is not None: return True return False