|
""" |
|
Authentication module for Dynamic Highscores system. |
|
|
|
This module handles user authentication with HuggingFace, |
|
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 HuggingFaceAuth: |
|
"""Authentication manager for HuggingFace integration.""" |
|
|
|
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") |
|
|
|
def login_user(self, token): |
|
"""Log in a user with their HuggingFace token. |
|
|
|
Args: |
|
token: HuggingFace API token |
|
|
|
Returns: |
|
dict: User information if login successful, None otherwise |
|
""" |
|
try: |
|
|
|
login(token=token, add_to_git_credential=False) |
|
|
|
|
|
user_info = self.hf_api.whoami(token=token) |
|
|
|
if not user_info: |
|
return None |
|
|
|
|
|
username = user_info.get("name", user_info.get("fullname", "")) |
|
hf_user_id = user_info.get("id", "") |
|
|
|
if not hf_user_id: |
|
return None |
|
|
|
|
|
is_admin = (username == self.admin_username) |
|
|
|
|
|
user_id = self.db_manager.add_user(username, hf_user_id, is_admin) |
|
|
|
|
|
user = self.db_manager.get_user(hf_user_id) |
|
|
|
if user: |
|
|
|
user['token'] = 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 |
|
|
|
|
|
token = request.cookies.get("hf_token") |
|
|
|
if not token: |
|
return None |
|
|
|
try: |
|
|
|
user_info = self.hf_api.whoami(token=token) |
|
|
|
if not user_info: |
|
return None |
|
|
|
|
|
hf_user_id = user_info.get("id", "") |
|
user = self.db_manager.get_user(hf_user_id) |
|
|
|
if user: |
|
|
|
user['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): |
|
|
|
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 log in to access this feature." |
|
|
|
|
|
user = self.check_login(request) |
|
|
|
if not user: |
|
return "Please log in to access this feature." |
|
|
|
|
|
kwargs['user'] = user |
|
|
|
|
|
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): |
|
|
|
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." |
|
|
|
|
|
user = self.check_login(request) |
|
|
|
if not user: |
|
return "Admin access required." |
|
|
|
|
|
if not user.get('is_admin', False): |
|
return "Admin access required." |
|
|
|
|
|
kwargs['user'] = user |
|
|
|
|
|
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) |
|
|
|
|
|
def create_login_ui(): |
|
"""Create the login UI components. |
|
|
|
Returns: |
|
tuple: (login_button, logout_button, token_input, user_info) |
|
""" |
|
with gr.Row(): |
|
with gr.Column(scale=3): |
|
token_input = gr.Textbox( |
|
placeholder="Enter your HuggingFace token", |
|
label="HuggingFace Token", |
|
type="password", |
|
visible=True, |
|
info="Your token is only stored temporarily in browser session cookies and is never saved permanently" |
|
) |
|
login_button = gr.Button("Login") |
|
logout_button = gr.Button("Logout", visible=False) |
|
|
|
with gr.Column(scale=2): |
|
user_info = gr.Markdown("Not logged in") |
|
|
|
return login_button, logout_button, token_input, user_info |
|
|
|
def login_handler(token, auth_manager): |
|
"""Handle login button click. |
|
|
|
Args: |
|
token: HuggingFace token |
|
auth_manager: Authentication manager instance |
|
|
|
Returns: |
|
tuple: Updated UI components visibility and user info |
|
""" |
|
if not token: |
|
return gr.update(visible=True), gr.update(visible=False), "Please enter your HuggingFace token" |
|
|
|
user = auth_manager.login_user(token) |
|
|
|
if user: |
|
|
|
|
|
js = f""" |
|
document.cookie = "hf_token={token}; path=/; SameSite=Strict"; |
|
""" |
|
|
|
|
|
return ( |
|
gr.update(visible=False), |
|
gr.update(visible=True), |
|
f"Logged in as {user['username']}" |
|
) |
|
else: |
|
return ( |
|
gr.update(visible=True), |
|
gr.update(visible=False), |
|
"Login failed. Please check your token and try again." |
|
) |
|
|
|
def logout_handler(): |
|
"""Handle logout button click. |
|
|
|
Returns: |
|
tuple: Updated UI components visibility and user info |
|
""" |
|
|
|
js = """ |
|
document.cookie = "hf_token=; path=/; max-age=0; SameSite=Strict"; |
|
""" |
|
|
|
|
|
return ( |
|
gr.update(visible=True), |
|
gr.update(visible=False), |
|
"Logged out" |
|
) |
|
|
|
def setup_auth_handlers(login_button, logout_button, token_input, user_info, auth_manager): |
|
"""Set up event handlers for authentication UI components. |
|
|
|
Args: |
|
login_button: Login button component |
|
logout_button: Logout button component |
|
token_input: Token input component |
|
user_info: User info component |
|
auth_manager: Authentication manager instance |
|
""" |
|
login_button.click( |
|
fn=lambda token: login_handler(token, auth_manager), |
|
inputs=[token_input], |
|
outputs=[token_input, logout_button, user_info] |
|
) |
|
|
|
logout_button.click( |
|
fn=logout_handler, |
|
inputs=[], |
|
outputs=[token_input, logout_button, user_info] |
|
) |
|
|