Spaces:
Sleeping
Sleeping
| """OphthalmoCapture — Basic Authentication Service | |
| Provides a simple login gate using streamlit-authenticator. | |
| Doctors must authenticate before accessing the labeling interface. | |
| Their name is automatically set in the session for audit trails. | |
| If streamlit-authenticator is not installed, authentication is skipped | |
| and the app works in "anonymous" mode. | |
| """ | |
| import streamlit as st | |
| try: | |
| import streamlit_authenticator as stauth | |
| AUTH_AVAILABLE = True | |
| except ImportError: | |
| AUTH_AVAILABLE = False | |
| from i18n import t | |
| # ── Default credentials ────────────────────────────────────────────────────── | |
| # In production, load these from a secure YAML/env. For now, hardcoded demo. | |
| DEFAULT_CREDENTIALS = { | |
| "usernames": { | |
| "admin": { | |
| "name": "Administrador", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" — generate new hashes with stauth.Hasher | |
| }, | |
| "Saul": { | |
| "name": "Dr. Saul", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| "Marisse": { | |
| "name": "Dra. Marisse", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| "Angel": { | |
| "name": "Dr. Angel", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| "Enmanuel": { | |
| "name": "Dr. Enmanuel", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| "Micaela": { | |
| "name": "Dra. Micaela", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| "Miguel": { | |
| "name": "Dr. Miguel", | |
| "password": "$2b$12$dcvvIg0q/2hZ1pO9gBKqY./LfujFHvoJUvPDLx1qhLS0LtD2kzJoq", | |
| # plain: "admin123" | |
| }, | |
| } | |
| } | |
| COOKIE_NAME = "ophthalmocapture_auth" | |
| COOKIE_KEY = "ophthalmocapture_secret_key" | |
| COOKIE_EXPIRY_DAYS = 1 | |
| def _get_authenticator(): | |
| """Return a single shared Authenticate instance per session.""" | |
| if "authenticator" not in st.session_state: | |
| st.session_state["authenticator"] = stauth.Authenticate( | |
| credentials=DEFAULT_CREDENTIALS, | |
| cookie_name=COOKIE_NAME, | |
| cookie_key=COOKIE_KEY, | |
| cookie_expiry_days=COOKIE_EXPIRY_DAYS, | |
| ) | |
| return st.session_state["authenticator"] | |
| def require_auth() -> bool: | |
| """Show login form and return True if the user is authenticated. | |
| If streamlit-authenticator is not installed, returns True immediately | |
| (anonymous mode) and sets doctor_name to empty string. | |
| """ | |
| if not AUTH_AVAILABLE: | |
| # Graceful degradation: no auth library → anonymous mode | |
| return True | |
| authenticator = _get_authenticator() | |
| try: | |
| authenticator.login(location="main") | |
| except Exception: | |
| pass | |
| if st.session_state.get("authentication_status"): | |
| # Set doctor name from authenticated user | |
| username = st.session_state.get("username", "") | |
| user_info = DEFAULT_CREDENTIALS["usernames"].get(username, {}) | |
| st.session_state.doctor_name = user_info.get("name", username) | |
| return True | |
| elif st.session_state.get("authentication_status") is False: | |
| st.error(t("login_error")) | |
| return False | |
| else: | |
| st.info(t("login_prompt")) | |
| return False | |
| def render_logout_button(): | |
| """Show a logout button in the sidebar (only if auth is active).""" | |
| if not AUTH_AVAILABLE: | |
| return | |
| if st.session_state.get("authentication_status"): | |
| authenticator = _get_authenticator() | |
| authenticator.logout(t("logout"), location="sidebar") | |
| def do_logout(): | |
| """Programmatically log out the current user.""" | |
| if not AUTH_AVAILABLE: | |
| return | |
| try: | |
| authenticator = _get_authenticator() | |
| authenticator.logout(location="unrendered") | |
| except Exception: | |
| # Fallback: clear auth keys manually | |
| for key in ("authentication_status", "username", "name", "logout"): | |
| st.session_state.pop(key, None) | |
| st.session_state.pop("authenticator", None) | |