mud_server.api.permissions ========================== .. py:module:: mud_server.api.permissions .. autoapi-nested-parse:: Role-based permission system (RBAC). This module implements a Role-Based Access Control (RBAC) system for the MUD server. It defines: 1. User roles with different privilege levels 2. Specific permissions that can be checked 3. Mapping of which roles have which permissions 4. Helper functions to check permissions and role hierarchy Role Hierarchy (lowest to highest): Player → World Builder → Admin → Superuser Permission Design: - Each role has an explicit set of permissions - Roles DO NOT automatically inherit lower role permissions (must be explicit) - Superuser has a special FULL_ACCESS permission that grants everything - Permissions are granular to allow fine-grained access control Usage in API Routes: 1. Use validate_session_with_permission() to check specific permissions 2. Use require_permission() decorator for route-level protection 3. Use require_role() decorator for minimum role requirements 4. Use can_manage_role() to prevent privilege escalation Security Considerations: - Never allow users to elevate their own privileges - Always check role hierarchy for user management operations - Superusers can manage everyone, admins can manage lower roles - Players and World Builders cannot manage other users Attributes ---------- .. autoapisummary:: mud_server.api.permissions.ROLE_PERMISSIONS Classes ------- .. autoapisummary:: mud_server.api.permissions.Role mud_server.api.permissions.Permission Functions --------- .. autoapisummary:: mud_server.api.permissions.has_permission mud_server.api.permissions.get_role_hierarchy_level mud_server.api.permissions.can_manage_role mud_server.api.permissions.require_permission mud_server.api.permissions.require_role Module Contents --------------- .. py:class:: Role(*args, **kwds) Bases: :py:obj:`enum.Enum` User roles in the system, ordered by privilege level. Each role represents a different level of access and trust in the system. Roles are stored as lowercase strings in the database but represented as enum values in code for type safety. Roles (in order of privilege): PLAYER: Standard game player (lowest privilege) WORLDBUILDER: Player with world editing capabilities ADMIN: Administrator with user management powers SUPERUSER: Super administrator with full system access (highest privilege) .. py:attribute:: PLAYER :value: 'player' .. py:attribute:: WORLDBUILDER :value: 'worldbuilder' .. py:attribute:: ADMIN :value: 'admin' .. py:attribute:: SUPERUSER :value: 'superuser' .. py:class:: Permission(*args, **kwds) Bases: :py:obj:`enum.Enum` Specific permissions that can be granted to roles. Permissions are organized by category and represent specific actions that users can perform. Each permission should be as granular as possible to allow fine-grained access control. Permission Categories: - Player: Basic gameplay actions - World Builder: Content creation and editing - Admin: User management and system administration - Superuser: Full unrestricted access .. py:attribute:: PLAY_GAME :value: 'play_game' .. py:attribute:: CHAT :value: 'chat' .. py:attribute:: EDIT_WORLD :value: 'edit_world' .. py:attribute:: CREATE_ROOMS :value: 'create_rooms' .. py:attribute:: CREATE_ITEMS :value: 'create_items' .. py:attribute:: CREATE_USERS :value: 'create_users' .. py:attribute:: KICK_USERS :value: 'kick_users' .. py:attribute:: BAN_USERS :value: 'ban_users' .. py:attribute:: VIEW_LOGS :value: 'view_logs' .. py:attribute:: STOP_SERVER :value: 'stop_server' .. py:attribute:: MANAGE_USERS :value: 'manage_users' .. py:attribute:: CHANGE_ROLES :value: 'change_roles' .. py:attribute:: FULL_ACCESS :value: 'full_access' .. py:data:: ROLE_PERMISSIONS :type: dict[Role, set[Permission]] .. py:function:: has_permission(role, permission) Check if a role has a specific permission. This is the core permission checking function used throughout the application. It converts the role string to an enum, looks up the role's permissions, and checks if the requested permission is granted. Special Case: Superusers automatically have ALL permissions due to the FULL_ACCESS permission. This is checked first before looking up specific permissions. :param role: Role string (case-insensitive: "player", "worldbuilder", "admin", "superuser") :param permission: Permission enum value to check (e.g., Permission.MANAGE_USERS) :returns: True if the role has the permission, False if not or if role is invalid .. admonition:: Example >>> has_permission("admin", Permission.VIEW_LOGS) True >>> has_permission("player", Permission.MANAGE_USERS) False >>> has_permission("superuser", Permission.ANYTHING) True # Superuser has all permissions .. py:function:: get_role_hierarchy_level(role) Get the numeric hierarchy level of a role. The hierarchy level is used to determine which users can manage other users. Higher numbers indicate more privilege. This prevents lower-privileged users from managing higher-privileged users. Hierarchy Levels: 0 = Player (lowest privilege) 1 = World Builder 2 = Admin 3 = Superuser (highest privilege) :param role: Role string (case-insensitive) :returns: Integer hierarchy level (0-3) Returns 0 for invalid/unknown roles (treated as lowest privilege) .. admonition:: Example >>> get_role_hierarchy_level("admin") 2 >>> get_role_hierarchy_level("player") 0 >>> get_role_hierarchy_level("invalid") 0 .. py:function:: can_manage_role(manager_role, target_role) Check if a manager can manage (promote/demote/ban) a target user. This function enforces the rule that you can only manage users with a lower hierarchy level than yourself. This prevents privilege escalation and ensures proper administrative boundaries. Management Rules: - Superuser (3) can manage Admin (2), WorldBuilder (1), Player (0) - Admin (2) can manage WorldBuilder (1), Player (0) - WorldBuilder (1) can manage Player (0) - Player (0) cannot manage anyone - You CANNOT manage users at the same level or higher :param manager_role: Role of the user performing the management action :param target_role: Role of the user being managed :returns: True if manager can manage target, False otherwise Security Note: Always call this function before allowing role changes, bans, or other user management actions to prevent privilege escalation attacks. .. admonition:: Example >>> can_manage_role("admin", "player") True >>> can_manage_role("player", "admin") False >>> can_manage_role("admin", "admin") False # Cannot manage users at same level >>> can_manage_role("superuser", "admin") True .. py:function:: require_permission(permission) Decorator to require a specific permission for a route. This decorator wraps a route function and checks if the user has the required permission before allowing the function to execute. If the user lacks the permission, an HTTP 403 Forbidden error is raised. .. note:: This decorator is currently defined but not actively used in the codebase. Most routes use validate_session_with_permission() directly instead. This decorator could be useful for cleaner route definitions in the future. :param permission: The permission required to access the route :returns: Decorator function that wraps the route handler :raises HTTPException(403): If user lacks the required permission Usage: @app.post("/admin/action") @require_permission(Permission.MANAGE_USERS) async def admin_action(username: str, role: str): # This only executes if user has MANAGE_USERS permission ... Requirements: The wrapped function must have "role" in its kwargs, typically obtained from validate_session() before calling the route handler. .. py:function:: require_role(min_role) Decorator to require a minimum role level for a route. This decorator checks role hierarchy level rather than specific permissions. It ensures the user has at least the specified role level (or higher) before allowing access to the route. .. note:: This decorator is currently defined but not actively used in the codebase. Most routes use validate_session_with_permission() for permission-based checks instead of role-based checks. However, this could be useful for routes that need role-level restrictions regardless of specific permissions. :param min_role: Minimum role enum value required (e.g., Role.ADMIN) :returns: Decorator function that wraps the route handler :raises HTTPException(403): If user's role is below the minimum required level Usage: @app.post("/admin/dashboard") @require_role(Role.ADMIN) async def admin_dashboard(username: str, role: str): # This only executes if user is Admin or Superuser ... Requirements: The wrapped function must have "role" in its kwargs, typically obtained from validate_session() before calling the route handler. .. admonition:: Example If min_role=Role.ADMIN: - Superuser (level 3) ✓ Allowed (3 >= 2) - Admin (level 2) ✓ Allowed (2 >= 2) - WorldBuilder (level 1) ✗ Denied (1 < 2) - Player (level 0) ✗ Denied (0 < 2)