Spaces:
Runtime error
Runtime error
| """ | |
| Admin Authentication Module | |
| Simple authentication for protecting Settings pages. | |
| Admin credentials are stored in SQLite database. | |
| Initial setup requires ADMIN_USERNAME and ADMIN_PASSWORD in .streamlit/secrets.toml or environment variables. | |
| Author: Wong Xin Ping | |
| Date: 26 January 2026 | |
| """ | |
| import streamlit as st | |
| import hashlib | |
| import os | |
| from settings.api_database import ( | |
| verify_admin_password, | |
| admin_credentials_exist, | |
| set_admin_credentials, | |
| get_admin_credentials_from_db, | |
| ) | |
| def hash_password(password: str) -> str: | |
| """Hash password using SHA-256""" | |
| return hashlib.sha256(password.encode()).hexdigest() | |
| def is_authenticated() -> bool: | |
| """Check if user is authenticated""" | |
| return st.session_state.get('admin_authenticated', False) | |
| def logout(): | |
| """Log out the admin user""" | |
| st.session_state['admin_authenticated'] = False | |
| st.session_state['admin_username'] = None | |
| def _get_initial_credentials(): | |
| """Get initial credentials from environment variables or Streamlit secrets.""" | |
| username = os.environ.get("ADMIN_USERNAME") or st.secrets.get("ADMIN_USERNAME", "admin") | |
| password = os.environ.get("ADMIN_PASSWORD") or st.secrets.get("ADMIN_PASSWORD", "") | |
| return username, password | |
| def login_page(): | |
| """Display login page and handle authentication""" | |
| # Check if admin credentials exist in database | |
| if not admin_credentials_exist(): | |
| # First-time setup - try to get from secrets/env | |
| username, password = _get_initial_credentials() | |
| if username and password: | |
| set_admin_credentials(username, password) | |
| else: | |
| # Show setup form | |
| st.markdown("### Initial Admin Setup") | |
| st.warning("No admin credentials found. Please set up admin account.") | |
| st.caption("You can also set ADMIN_USERNAME and ADMIN_PASSWORD in .streamlit/secrets.toml or environment variables.") | |
| with st.form("admin_setup_form"): | |
| new_username = st.text_input("Username", placeholder="Enter admin username") | |
| new_password = st.text_input("Password", type="password", placeholder="Enter admin password") | |
| confirm_password = st.text_input("Confirm Password", type="password", placeholder="Confirm password") | |
| submitted = st.form_submit_button("Create Admin Account", type="primary") | |
| if submitted: | |
| if not new_username or not new_password: | |
| st.error("Username and password are required") | |
| elif new_password != confirm_password: | |
| st.error("Passwords do not match") | |
| elif len(new_password) < 8: | |
| st.error("Password must be at least 8 characters") | |
| else: | |
| if set_admin_credentials(new_username, new_password): | |
| st.success("Admin account created successfully!") | |
| st.rerun() | |
| else: | |
| st.error("Failed to create admin account") | |
| return | |
| # Normal login flow | |
| st.markdown("### Admin Login") | |
| st.caption("Enter admin credentials to access settings") | |
| with st.form("admin_login_form"): | |
| username = st.text_input("Username", value="admin", placeholder="Enter username") | |
| password = st.text_input("Password", type="password", placeholder="Enter password") | |
| col1, col2 = st.columns([1, 3]) | |
| with col1: | |
| submitted = st.form_submit_button("Login", type="primary", use_container_width=True) | |
| if submitted: | |
| if verify_admin_password(username, password): | |
| st.session_state['admin_authenticated'] = True | |
| st.session_state['admin_username'] = username | |
| st.success("Login successful!") | |
| st.rerun() | |
| else: | |
| st.error("Invalid username or password") | |
| def require_auth(page_function): | |
| """Decorator/wrapper to require authentication for a page""" | |
| def wrapper(*args, **kwargs): | |
| if not is_authenticated(): | |
| login_page() | |
| return None | |
| return page_function(*args, **kwargs) | |
| return wrapper | |
| def show_logout_button(): | |
| """Show logout button in sidebar or page""" | |
| if is_authenticated(): | |
| username = st.session_state.get('admin_username', 'Admin') | |
| col1, col2 = st.columns([3, 1]) | |
| with col1: | |
| st.caption(f"Logged in as: **{username}**") | |
| with col2: | |
| if st.button("Logout", key="logout_btn", use_container_width=True): | |
| logout() | |
| st.rerun() | |
| def change_password_form(): | |
| """Display form to change admin password""" | |
| if not is_authenticated(): | |
| return | |
| st.markdown("### Change Password") | |
| with st.form("change_password_form"): | |
| current_password = st.text_input("Current Password", type="password") | |
| new_password = st.text_input("New Password", type="password") | |
| confirm_password = st.text_input("Confirm New Password", type="password") | |
| submitted = st.form_submit_button("Change Password", type="primary") | |
| if submitted: | |
| username = st.session_state.get('admin_username', '') | |
| if not verify_admin_password(username, current_password): | |
| st.error("Current password is incorrect") | |
| elif new_password != confirm_password: | |
| st.error("New passwords do not match") | |
| elif len(new_password) < 8: | |
| st.error("Password must be at least 8 characters") | |
| else: | |
| if set_admin_credentials(username, new_password): | |
| st.success("Password changed successfully!") | |
| else: | |
| st.error("Failed to change password") | |