""" Authentication Manager for ID Agents ===================================== Handles user authentication, session management, and access control for the ID Agents application running on HF Spaces. """ import gradio as gr import time import secrets from typing import Dict, Optional, Tuple, Any from auth_config import authenticate_user, authenticate_with_code, get_user_capabilities class AuthManager: def __init__(self): self.active_sessions: Dict[str, Dict] = {} self.session_timeout = 3600 # 1 hour in seconds def create_session(self, user_info: Dict) -> str: """Create a new user session""" session_id = secrets.token_urlsafe(32) self.active_sessions[session_id] = { "user_info": user_info, "created_at": time.time(), "last_activity": time.time(), "capabilities": get_user_capabilities(user_info["access_level"]) } return session_id def validate_session(self, session_id: str) -> Tuple[bool, Optional[Dict]]: """Validate a session and return user info if valid""" if not session_id or session_id not in self.active_sessions: return False, None session = self.active_sessions[session_id] current_time = time.time() # Check if session has expired if current_time - session["last_activity"] > self.session_timeout: del self.active_sessions[session_id] return False, None # Update last activity session["last_activity"] = current_time return True, session def logout_session(self, session_id: str) -> bool: """Logout a user session""" if session_id in self.active_sessions: del self.active_sessions[session_id] return True return False def get_session_info(self, session_id: str) -> Optional[Dict]: """Get session information""" is_valid, session = self.validate_session(session_id) return session if is_valid else None # Global auth manager instance auth_manager = AuthManager() def create_auth_interface(): """Create the authentication interface""" with gr.Blocks(title="ID Agents - Beta Access") as auth_interface: # Add custom CSS for the auth interface auth_interface.css = """ :root { --auth-bg: #1a1f2e; --auth-panel: #23263a; --auth-accent: #00ffe7; --auth-accent2: #7f5cff; --auth-text: #ffffff; --auth-muted: #a0a5b8; --auth-success: #00ff88; --auth-error: #ff4757; } body, .gradio-container, .gr-block, .gr-app { background: var(--auth-bg) !important; color: var(--auth-text) !important; } .auth-container { max-width: 500px; margin: 2rem auto; padding: 2rem; background: linear-gradient(135deg, var(--auth-panel) 60%, #2a2f45 100%); border: 2px solid var(--auth-accent2); border-radius: 20px; box-shadow: 0 8px 40px rgba(127, 92, 255, 0.3); } .auth-title { text-align: center; font-size: 2rem; font-weight: 700; margin-bottom: 0.5rem; background: linear-gradient(90deg, var(--auth-accent), var(--auth-accent2)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .auth-subtitle { text-align: center; color: var(--auth-muted); margin-bottom: 2rem; font-size: 1.1rem; } .auth-tabs { background: transparent !important; border: none !important; } .auth-form { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 1.5rem; margin: 1rem 0; } .auth-input { background: rgba(255, 255, 255, 0.1) !important; border: 1px solid var(--auth-accent2) !important; border-radius: 8px !important; color: var(--auth-text) !important; margin-bottom: 1rem; } .auth-button { background: linear-gradient(90deg, var(--auth-accent2), var(--auth-accent)) !important; color: var(--auth-bg) !important; border: none !important; border-radius: 8px !important; font-weight: 600 !important; padding: 12px 24px !important; width: 100% !important; margin-top: 1rem; } .auth-success { color: var(--auth-success) !important; background: rgba(0, 255, 136, 0.1); border: 1px solid var(--auth-success); border-radius: 8px; padding: 1rem; margin: 1rem 0; } .auth-error { color: var(--auth-error) !important; background: rgba(255, 71, 87, 0.1); border: 1px solid var(--auth-error); border-radius: 8px; padding: 1rem; margin: 1rem 0; } .auth-info { background: rgba(0, 255, 231, 0.1); border: 1px solid var(--auth-accent); border-radius: 8px; padding: 1rem; margin: 1rem 0; color: var(--auth-accent); } .user-info { background: rgba(127, 92, 255, 0.1); border: 1px solid var(--auth-accent2); border-radius: 8px; padding: 1rem; margin: 1rem 0; } """ # Session state session_id = gr.State("") with gr.Column(elem_classes="auth-container"): gr.HTML('
🦠 ID Agents
') gr.HTML('
Beta Testing Access Portal
') # Authentication status auth_status = gr.Markdown("", visible=False) user_info_display = gr.Markdown("", visible=False) # Login tabs with gr.Tabs(elem_classes="auth-tabs") as auth_tabs: # Username/Password Login with gr.Tab("👤 User Login", elem_classes="auth-form"): gr.Markdown("### Sign in with your beta testing credentials") username_input = gr.Textbox( label="Username", placeholder="Enter your username...", elem_classes="auth-input" ) password_input = gr.Textbox( label="Password", type="password", placeholder="Enter your password...", elem_classes="auth-input" ) login_button = gr.Button("🔑 Sign In", elem_classes="auth-button") gr.HTML("""
Beta Testing Accounts:
• dr_smith (password: idweek2025)
• id_fellow (password: hello)
• pharmacist (password: stewardship)
• ipc_nurse (password: infection)
• researcher (password: research)
• admin (password: idagents2025)
""") # Invitation Code Login with gr.Tab("🎫 Invitation Code", elem_classes="auth-form"): gr.Markdown("### Enter your invitation code") invitation_input = gr.Textbox( label="Invitation Code", placeholder="Enter your invitation code...", elem_classes="auth-input" ) code_login_button = gr.Button("🎟️ Access with Code", elem_classes="auth-button") gr.HTML("""
Sample Invitation Codes:
• IDWEEK2025 (VIP Access)
• BETA-FELLOW (Fellowship Program)
• PHARM-STEW (Pharmacy Access)
• IPC-NURSE (Infection Prevention)
• RESEARCH-AI (Clinical Research)
""") # Main app placeholder (hidden until authenticated) app_container = gr.HTML("", visible=False) # Logout button (hidden until authenticated) logout_button = gr.Button("🚪 Logout", visible=False, elem_classes="auth-button") # Authentication functions def handle_login(username: str, password: str, current_session: str): """Handle username/password login""" if not username or not password: return ( gr.update(value="⚠️ Please enter both username and password.", visible=True, elem_classes="auth-error"), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), current_session ) success, user_info = authenticate_user(username, password) if success: session_id = auth_manager.create_session(user_info) user_display = f"""
✅ Welcome, {user_info['full_name']}!
Role: {user_info['role']}
Access Level: {user_info['access_level']}
Email: {user_info['email']}
""" return ( gr.update(value="🎉 Authentication successful! Loading ID Agents...", visible=True, elem_classes="auth-success"), gr.update(value=user_display, visible=True), gr.update(visible=False), # Hide auth tabs gr.update(visible=True), # Show app container gr.update(visible=True), # Show logout button session_id ) else: return ( gr.update(value="❌ Invalid username or password. Please try again.", visible=True, elem_classes="auth-error"), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), current_session ) def handle_invitation_login(invitation_code: str, current_session: str): """Handle invitation code login""" if not invitation_code: return ( gr.update(value="⚠️ Please enter an invitation code.", visible=True, elem_classes="auth-error"), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), current_session ) success, user_info = authenticate_with_code(invitation_code.upper()) if success: session_id = auth_manager.create_session(user_info) user_display = f"""
✅ Welcome, {user_info['full_name']}!
Role: {user_info['role']}
Access Level: {user_info['access_level']}
Email: {user_info['email']}
Authenticated via invitation code
""" return ( gr.update(value="🎉 Authentication successful! Loading ID Agents...", visible=True, elem_classes="auth-success"), gr.update(value=user_display, visible=True), gr.update(visible=False), # Hide auth tabs gr.update(visible=True), # Show app container gr.update(visible=True), # Show logout button session_id ) else: return ( gr.update(value="❌ Invalid or expired invitation code. Please check your code and try again.", visible=True, elem_classes="auth-error"), gr.update(visible=False), gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), current_session ) def handle_logout(current_session: str): """Handle user logout""" if current_session: auth_manager.logout_session(current_session) return ( gr.update(value="", visible=False), gr.update(visible=False), gr.update(visible=True), # Show auth tabs gr.update(visible=False), # Hide app container gr.update(visible=False), # Hide logout button "", # Clear session "", # Clear username "", # Clear password "" # Clear invitation code ) # Wire up the authentication handlers login_button.click( fn=handle_login, inputs=[username_input, password_input, session_id], outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id] ) code_login_button.click( fn=handle_invitation_login, inputs=[invitation_input, session_id], outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id] ) logout_button.click( fn=handle_logout, inputs=[session_id], outputs=[auth_status, user_info_display, auth_tabs, app_container, logout_button, session_id, username_input, password_input, invitation_input] ) return auth_interface, session_id def check_user_permission(session_id: str, required_capability: str) -> Tuple[bool, Optional[str]]: """ Check if user has permission for a specific capability Returns: (has_permission: bool, error_message: str or None) """ session_info = auth_manager.get_session_info(session_id) if not session_info: return False, "Please log in to access this feature." capabilities = session_info["capabilities"] if required_capability not in capabilities or not capabilities[required_capability]: user_level = session_info["user_info"]["access_level"] return False, f"Your access level ({user_level}) doesn't permit this action. Contact admin for upgrade." return True, None def get_user_limits(session_id: str) -> Dict[str, Any]: """Get user-specific limits based on their access level""" session_info = auth_manager.get_session_info(session_id) if not session_info: return { "max_agents": 0, "max_file_size_mb": 0, "can_upload_files": False } return { "max_agents": session_info["capabilities"]["max_agents"], "max_file_size_mb": session_info["capabilities"]["max_file_size_mb"], "can_upload_files": session_info["capabilities"]["can_upload_files"], "can_download_configs": session_info["capabilities"]["can_download_configs"], "can_see_debug_info": session_info["capabilities"]["can_see_debug_info"] }