|
""" |
|
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: |
|
|
|
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: |
|
|
|
user_info = self.validate_token(read_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['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 |
|
|
|
|
|
token = request.cookies.get("hf_token") |
|
|
|
if not token: |
|
|
|
token = request.headers.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['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): |
|
|
|
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." |
|
|
|
|
|
user = self.check_login(request) |
|
|
|
if not user: |
|
return "Please enter your HuggingFace tokens 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_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") |
|
|
|
|
|
token_state = gr.State(None) |
|
|
|
|
|
token_js = """ |
|
<script> |
|
// Function to save tokens to localStorage |
|
function saveTokens() { |
|
const readToken = document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value; |
|
const writeToken = document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value; |
|
|
|
if (readToken && writeToken) { |
|
localStorage.setItem("hf_read_token", readToken); |
|
localStorage.setItem("hf_write_token", writeToken); |
|
|
|
// Set token in cookie for server-side access |
|
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict"; |
|
|
|
// Update status |
|
const statusElement = document.querySelector('div[data-testid="markdown"] p'); |
|
if (statusElement) { |
|
statusElement.textContent = "Authenticated with tokens"; |
|
statusElement.style.color = "green"; |
|
} |
|
|
|
// Reload page to apply tokens |
|
setTimeout(() => window.location.reload(), 1000); |
|
} else { |
|
alert("Please enter both read and write tokens"); |
|
} |
|
} |
|
|
|
// Function to clear tokens from localStorage |
|
function clearTokens() { |
|
localStorage.removeItem("hf_read_token"); |
|
localStorage.removeItem("hf_write_token"); |
|
|
|
// Clear token cookie |
|
document.cookie = "hf_token=; path=/; max-age=0; SameSite=Strict"; |
|
|
|
// Update status |
|
const statusElement = document.querySelector('div[data-testid="markdown"] p'); |
|
if (statusElement) { |
|
statusElement.textContent = "Not authenticated"; |
|
statusElement.style.color = "red"; |
|
} |
|
|
|
// Clear input fields |
|
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = ""; |
|
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = ""; |
|
|
|
// Reload page to apply changes |
|
setTimeout(() => window.location.reload(), 1000); |
|
} |
|
|
|
// Function to load tokens from localStorage |
|
function loadTokens() { |
|
const readToken = localStorage.getItem("hf_read_token"); |
|
const writeToken = localStorage.getItem("hf_write_token"); |
|
|
|
if (readToken && writeToken) { |
|
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = readToken; |
|
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = writeToken; |
|
|
|
// Update status |
|
const statusElement = document.querySelector('div[data-testid="markdown"] p'); |
|
if (statusElement) { |
|
statusElement.textContent = "Authenticated with tokens"; |
|
statusElement.style.color = "green"; |
|
} |
|
|
|
// Set token in cookie for server-side access if not already set |
|
if (!document.cookie.includes("hf_token=")) { |
|
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict"; |
|
} |
|
} |
|
} |
|
|
|
// Add event listeners once DOM is loaded |
|
document.addEventListener("DOMContentLoaded", function() { |
|
// Load tokens from localStorage |
|
loadTokens(); |
|
|
|
// Add event listeners to buttons |
|
const saveButton = document.querySelector('button:nth-of-type(1)'); |
|
const clearButton = document.querySelector('button:nth-of-type(2)'); |
|
|
|
if (saveButton) { |
|
saveButton.addEventListener("click", saveTokens); |
|
} |
|
|
|
if (clearButton) { |
|
clearButton.addEventListener("click", clearTokens); |
|
} |
|
}); |
|
</script> |
|
""" |
|
|
|
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" |
|
|
|
|
|
read_user_info = auth_manager.validate_token(read_token) |
|
if not read_user_info: |
|
return "Invalid read token" |
|
|
|
|
|
write_user_info = auth_manager.validate_token(write_token) |
|
if not write_user_info: |
|
return "Invalid write token" |
|
|
|
|
|
if read_user_info.get("id") != write_user_info.get("id"): |
|
return "Tokens must belong to the same user" |
|
|
|
|
|
user = auth_manager.login_user(read_token, write_token) |
|
if not user: |
|
return "Failed to authenticate user" |
|
|
|
return f"Authenticated as {user.get('username')}" |
|
|