""" Authentication module for Dynamic Highscores system with token-based authentication. This module handles user authentication with HuggingFace using direct token input, user session management, and access control. """ import os import json import time import requests import gradio as gr from huggingface_hub import HfApi, login from functools import wraps class HuggingFaceTokenAuth: """Authentication manager for HuggingFace integration using tokens.""" def __init__(self, db_manager): """Initialize the authentication manager. Args: db_manager: Database manager instance for user storage """ self.db_manager = db_manager self.hf_api = HfApi() self.admin_username = os.environ.get("ADMIN_USERNAME", "Quazim0t0") self.running_in_space = 'SPACE_ID' in os.environ print(f"Running in Space: {self.running_in_space}") def validate_token(self, token): """Validate a HuggingFace token. Args: token: HuggingFace API token Returns: dict: User information if token is valid, None otherwise """ try: # Validate token with HuggingFace user_info = self.hf_api.whoami(token=token) return user_info except Exception as e: print(f"Token validation error: {e}") return None def login_user(self, read_token, write_token=None): """Log in a user with their HuggingFace tokens. Args: read_token: HuggingFace read API token write_token: HuggingFace write API token (optional) Returns: dict: User information if login successful, None otherwise """ try: # Validate read token with HuggingFace user_info = self.validate_token(read_token) if not user_info: return None # Check if user exists in our database, create if not username = user_info.get("name", user_info.get("fullname", "")) hf_user_id = user_info.get("id", "") if not hf_user_id: return None # Check if this is the admin account is_admin = (username == self.admin_username) # Add or get user from database user_id = self.db_manager.add_user(username, hf_user_id, is_admin) # Get complete user info from database user = self.db_manager.get_user(hf_user_id) if user: # Add tokens to user info for session only (not stored in database) user['read_token'] = read_token if write_token: user['write_token'] = write_token return user return None except Exception as e: print(f"Login error: {e}") return None def check_login(self, request: gr.Request): """Check if a user is logged in from a Gradio request. Args: request: Gradio request object Returns: dict: User information if logged in, None otherwise """ if not request: return None # Get token from cookies token = request.cookies.get("hf_token") if not token: # Try to get token from headers token = request.headers.get("HF-Token") if not token: return None try: # Validate token with HuggingFace user_info = self.hf_api.whoami(token=token) if not user_info: return None # Get user from database hf_user_id = user_info.get("id", "") user = self.db_manager.get_user(hf_user_id) if user: # Add token to user info for session only (not stored in database) user['read_token'] = token return user return None except Exception as e: print(f"Check login error: {e}") return None def require_login(self, func): """Decorator to require login for a function. Args: func: Function to decorate Returns: Function: Decorated function that requires login """ @wraps(func) def wrapper(*args, **kwargs): # Find the request argument request = None for arg in args: if isinstance(arg, gr.Request): request = arg break if not request and 'request' in kwargs: request = kwargs['request'] if not request: return "Please enter your HuggingFace tokens to access this feature." # Check if user is logged in user = self.check_login(request) if not user: return "Please enter your HuggingFace tokens to access this feature." # Add user to kwargs kwargs['user'] = user # Call the original function return func(*args, **kwargs) return wrapper def require_admin(self, func): """Decorator to require admin privileges for a function. Args: func: Function to decorate Returns: Function: Decorated function that requires admin privileges """ @wraps(func) def wrapper(*args, **kwargs): # Find the request argument request = None for arg in args: if isinstance(arg, gr.Request): request = arg break if not request and 'request' in kwargs: request = kwargs['request'] if not request: return "Admin access required." # Check if user is logged in user = self.check_login(request) if not user: return "Admin access required." # Check if user is admin if not user.get('is_admin', False): return "Admin access required." # Add user to kwargs kwargs['user'] = user # Call the original function return func(*args, **kwargs) return wrapper def can_submit_benchmark(self, user_id): """Check if a user can submit a benchmark today. Args: user_id: User ID to check Returns: bool: True if user can submit, False otherwise """ return self.db_manager.can_submit_today(user_id) def update_submission_date(self, user_id): """Update the last submission date for a user. Args: user_id: User ID to update """ self.db_manager.update_submission_date(user_id) # Token input UI components def create_token_input_ui(): """Create the token input UI components. Returns: tuple: (read_token, write_token, save_button, clear_button, token_status, token_state, token_js) """ with gr.Row(): with gr.Column(): gr.Markdown("### HuggingFace Token Authentication") gr.Markdown(""" Enter your HuggingFace tokens to use this application. You can find your tokens in your [HuggingFace settings](https://huggingface.co/settings/tokens). - **Read Token**: Required for accessing models and datasets - **Write Token**: Required for submitting evaluation results Your tokens are stored only in your browser's local storage and are not saved on the server. """) read_token = gr.Textbox( label="Read Token", placeholder="Enter your HuggingFace read token", type="password" ) write_token = gr.Textbox( label="Write Token", placeholder="Enter your HuggingFace write token", type="password" ) save_button = gr.Button("Save Tokens") clear_button = gr.Button("Clear Tokens") token_status = gr.Markdown("Not authenticated") # Hidden field to store the token status token_state = gr.State(None) # JavaScript to handle token storage token_js = """ """ return read_token, write_token, save_button, clear_button, token_status, token_state, token_js def validate_tokens(auth_manager, read_token, write_token): """Validate HuggingFace tokens. Args: auth_manager: Authentication manager instance read_token: HuggingFace read token write_token: HuggingFace write token Returns: str: Status message """ if not read_token or not write_token: return "Please enter both read and write tokens" # Validate read token read_user_info = auth_manager.validate_token(read_token) if not read_user_info: return "Invalid read token" # Validate write token write_user_info = auth_manager.validate_token(write_token) if not write_user_info: return "Invalid write token" # Check if tokens belong to the same user if read_user_info.get("id") != write_user_info.get("id"): return "Tokens must belong to the same user" # Login user with tokens user = auth_manager.login_user(read_token, write_token) if not user: return "Failed to authenticate user" return f"Authenticated as {user.get('username')}"