""" Centralized permission checks for ResolutionFlow. Role hierarchy: super_admin > owner > engineer > l1_tech > viewer - super_admin: is_super_admin=True, full system access - owner: account_role='owner', manage account resources - engineer: account_role='engineer' (default), CRUD own trees/steps - l1_tech: account_role='l1_tech', use /l1/* surface only — walk flows, resolve/escalate - viewer: account_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, "owner": 3, "engineer": 2, "l1_tech": 1, "viewer": 0, } def get_effective_role(user: User) -> str: """Get the effective role considering is_super_admin and account_role.""" if user.is_super_admin: return "super_admin" if user.account_role == "owner": return "owner" return user.account_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.account_role == "owner" and tree.account_id == user.account_id and user.account_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.account_role == "owner" and category.account_id == user.account_id and user.account_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.account_role == "owner" and tree.account_id == user.account_id and user.account_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.account_id == user.account_id and user.account_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.account_id == user.account_id and user.account_id is not None) or user.is_super_admin return False def can_create_tag(user: User, account_id: Optional[UUID]) -> bool: """Can the user create a tag for the given scope? - Super admins can create global tags (account_id=None) or any account's tags - Engineers can create account tags for their own account - Viewers cannot create tags """ if user.is_super_admin: return True if not can_create_content(user): return False if account_id is not None and account_id == user.account_id: return True return False def can_create_category(user: User, account_id: Optional[UUID]) -> bool: """Can the user create a category for the given account? - Super admins can create global or any account's categories - Account owners can create categories for their own account """ if user.is_super_admin: return True if user.account_role == "owner" and account_id == user.account_id and user.account_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.account_role == "owner" and category.account_id == user.account_id and user.account_id is not None: return True return False def can_create_step_category(user: User, account_id: Optional[UUID]) -> bool: """Can the user create a step category for the given account? - Super admins can create global or any account's step categories - Account owners can create step categories for their own account """ if user.is_super_admin: return True if user.account_role == "owner" and account_id == user.account_id and user.account_id is not None: return True return False def can_manage_script_template(user: User, template_created_by: Optional[UUID], template_account_id: Optional[UUID] = None) -> bool: """Can the user edit/delete this script template? - Super admins can manage any template - Account owners can manage any template in their account - Engineers can manage templates they created """ if user.is_super_admin: return True if user.account_role == "owner" and template_account_id == user.account_id and user.account_id is not None: return True if template_created_by == user.id: return True return False