Implemented simple authentication for testing and admin panel for user management: Backend: - Add simple email/password login for test users (admin@test.local, user@test.local) - Implement RBAC (Role-Based Access Control) with Permission enum - Create admin endpoints for user management and system analytics - Add bcrypt password hashing for test users - Create script to generate test users in database Frontend: - Add SimpleLogin component for test authentication - Create AdminPanel with user management and system analytics - Add role-based navigation (Admin tab visible only for admins) - Update AuthContext to support both MSAL and simple login - Add API methods for admin operations Features: - Admins can view all users, manage roles, activate/deactivate accounts - Admins can view system-wide analytics (users, conversations, tokens, costs) - Regular users only see their own chats and usage - Role badges in UI show user role (user/admin/superadmin) Note: Simple authentication is for testing only. Production uses Azure AD MSAL. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
146 lines
4 KiB
Python
146 lines
4 KiB
Python
"""
|
|
Role-Based Access Control (RBAC) utilities
|
|
"""
|
|
|
|
from enum import Enum
|
|
from typing import List
|
|
from fastapi import HTTPException, status
|
|
|
|
|
|
class UserRole(str, Enum):
|
|
"""User roles for RBAC"""
|
|
USER = "user"
|
|
ADMIN = "admin"
|
|
SUPERADMIN = "superadmin"
|
|
|
|
|
|
class Permission(str, Enum):
|
|
"""System permissions"""
|
|
# User permissions
|
|
READ_OWN_PROFILE = "read_own_profile"
|
|
UPDATE_OWN_PROFILE = "update_own_profile"
|
|
|
|
# Chat permissions
|
|
READ_OWN_CHATS = "read_own_chats"
|
|
CREATE_CHAT = "create_chat"
|
|
UPDATE_OWN_CHAT = "update_own_chat"
|
|
DELETE_OWN_CHAT = "delete_own_chat"
|
|
|
|
# Token usage permissions
|
|
READ_OWN_USAGE = "read_own_usage"
|
|
|
|
# Admin permissions
|
|
READ_ALL_USERS = "read_all_users"
|
|
UPDATE_USER_ROLE = "update_user_role"
|
|
READ_ALL_CHATS = "read_all_chats"
|
|
READ_ALL_USAGE = "read_all_usage"
|
|
VIEW_ANALYTICS = "view_analytics"
|
|
MANAGE_SYSTEM = "manage_system"
|
|
|
|
|
|
# Role to permissions mapping
|
|
ROLE_PERMISSIONS = {
|
|
UserRole.USER: [
|
|
Permission.READ_OWN_PROFILE,
|
|
Permission.UPDATE_OWN_PROFILE,
|
|
Permission.READ_OWN_CHATS,
|
|
Permission.CREATE_CHAT,
|
|
Permission.UPDATE_OWN_CHAT,
|
|
Permission.DELETE_OWN_CHAT,
|
|
Permission.READ_OWN_USAGE,
|
|
],
|
|
UserRole.ADMIN: [
|
|
# All user permissions
|
|
Permission.READ_OWN_PROFILE,
|
|
Permission.UPDATE_OWN_PROFILE,
|
|
Permission.READ_OWN_CHATS,
|
|
Permission.CREATE_CHAT,
|
|
Permission.UPDATE_OWN_CHAT,
|
|
Permission.DELETE_OWN_CHAT,
|
|
Permission.READ_OWN_USAGE,
|
|
# Admin permissions
|
|
Permission.READ_ALL_USERS,
|
|
Permission.UPDATE_USER_ROLE,
|
|
Permission.READ_ALL_CHATS,
|
|
Permission.READ_ALL_USAGE,
|
|
Permission.VIEW_ANALYTICS,
|
|
],
|
|
UserRole.SUPERADMIN: [
|
|
# All permissions
|
|
Permission.READ_OWN_PROFILE,
|
|
Permission.UPDATE_OWN_PROFILE,
|
|
Permission.READ_OWN_CHATS,
|
|
Permission.CREATE_CHAT,
|
|
Permission.UPDATE_OWN_CHAT,
|
|
Permission.DELETE_OWN_CHAT,
|
|
Permission.READ_OWN_USAGE,
|
|
Permission.READ_ALL_USERS,
|
|
Permission.UPDATE_USER_ROLE,
|
|
Permission.READ_ALL_CHATS,
|
|
Permission.READ_ALL_USAGE,
|
|
Permission.VIEW_ANALYTICS,
|
|
Permission.MANAGE_SYSTEM,
|
|
]
|
|
}
|
|
|
|
|
|
def check_permission(user_role: str, required_permission: Permission) -> bool:
|
|
"""
|
|
Check if a user role has a specific permission
|
|
|
|
Args:
|
|
user_role: User's role
|
|
required_permission: Permission to check
|
|
|
|
Returns:
|
|
True if user has permission, False otherwise
|
|
"""
|
|
try:
|
|
role = UserRole(user_role)
|
|
permissions = ROLE_PERMISSIONS.get(role, [])
|
|
return required_permission in permissions
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def require_permission(user_role: str, required_permission: Permission) -> None:
|
|
"""
|
|
Raise exception if user doesn't have required permission
|
|
|
|
Args:
|
|
user_role: User's role
|
|
required_permission: Permission to check
|
|
|
|
Raises:
|
|
HTTPException: If user doesn't have permission
|
|
"""
|
|
if not check_permission(user_role, required_permission):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail=f"Permission denied. Required permission: {required_permission.value}"
|
|
)
|
|
|
|
|
|
def require_role(user_role: str, required_roles: List[UserRole]) -> None:
|
|
"""
|
|
Raise exception if user doesn't have one of the required roles
|
|
|
|
Args:
|
|
user_role: User's role
|
|
required_roles: List of acceptable roles
|
|
|
|
Raises:
|
|
HTTPException: If user doesn't have required role
|
|
"""
|
|
try:
|
|
role = UserRole(user_role)
|
|
if role not in required_roles:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail=f"Access denied. Required roles: {[r.value for r in required_roles]}"
|
|
)
|
|
except ValueError:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail="Invalid user role"
|
|
)
|