Source code for imednet_streamlit.credentials

"""Thread-safe credential management repository."""

import logging
import os
import sqlite3
import threading

_db_lock = threading.Lock()
logger = logging.getLogger(__name__)


[docs]class CredentialRepository: """Manages secure access to tenant credentials in a local SQLite database."""
[docs] def __init__(self, db_path: str): """Initialize the repository with a path to the database. Args: db_path: The filesystem path to the SQLite database. """ self.db_path = db_path self._initialize_db()
def _initialize_db(self) -> None: """Ensure the database and tables exist with the correct schema.""" with _db_lock: if not os.path.exists(self.db_path): os.makedirs(os.path.dirname(self.db_path), exist_ok=True) with sqlite3.connect(self.db_path) as conn: conn.execute( "CREATE TABLE IF NOT EXISTS tenants (study_key TEXT PRIMARY KEY, api_key TEXT, security_key TEXT)" ) cursor = conn.execute("PRAGMA table_info(tenants)") columns = [row[1] for row in cursor.fetchall()] if "env_url" not in columns: conn.execute("ALTER TABLE tenants ADD COLUMN env_url TEXT")
[docs] def get_credentials(self, study_key: str) -> tuple[str | None, str | None, str | None]: """Retrieve credentials for a given study key. Args: study_key: The study key to lookup. Returns: A tuple of (api_key, security_key, env_url), or (None, None, None) if not found. """ with _db_lock: if not os.path.exists(self.db_path): return None, None, None try: with sqlite3.connect(self.db_path) as conn: row = conn.execute( "SELECT api_key, security_key, env_url FROM tenants WHERE study_key=?", (study_key,), ).fetchone() if row: return row[0], row[1], row[2] except Exception as e: logger.error("Failed to read credentials: %s", e) return None, None, None
[docs] def get_provisioned_studies(self) -> list[str]: """Get a list of all provisioned study keys. Returns: A list of study keys. """ with _db_lock: if not os.path.exists(self.db_path): return [] try: with sqlite3.connect(self.db_path) as conn: return [row[0] for row in conn.execute("SELECT study_key FROM tenants")] except sqlite3.OperationalError: return []
[docs] def provision_tenant( self, study_key: str, api_key: str, security_key: str, env_url: str | None = None ) -> None: """Provision or update credentials for a tenant. Args: study_key: The study key identifier. api_key: The API key for the tenant. security_key: The security key for the tenant. env_url: Optional base URL for the tenant's environment. """ with _db_lock: if not os.path.exists(self.db_path): os.makedirs(os.path.dirname(self.db_path), exist_ok=True) with sqlite3.connect(self.db_path) as conn: conn.execute( "CREATE TABLE IF NOT EXISTS tenants (study_key TEXT PRIMARY KEY, api_key TEXT, security_key TEXT)" ) cursor = conn.execute("PRAGMA table_info(tenants)") columns = [row[1] for row in cursor.fetchall()] if "env_url" not in columns: conn.execute("ALTER TABLE tenants ADD COLUMN env_url TEXT") conn.execute( "INSERT OR REPLACE INTO tenants (study_key, api_key, security_key, env_url) VALUES (?, ?, ?, ?)", ( study_key.strip(), api_key.strip(), security_key.strip(), env_url.strip() if env_url and env_url.strip() else None, ), )