Source code for scitex_browser.collaboration.credential_manager

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: "2025-10-19 05:50:00 (ywatanabe)"
# File: ./src/scitex/browser/collaboration/credential_manager.py
# ----------------------------------------
"""
Flexible credential management for browser automation.

Safe, clear communication with user.
Multiple input methods: env vars, terminal, browser.
"""

import getpass
import os
from typing import Dict, Optional

from playwright.async_api import Page


[docs] class CredentialManager: """ Flexible credential retrieval. Tries multiple sources in order: 1. Explicitly provided credentials 2. Environment variables 3. Terminal prompt (if terminal available) 4. Browser prompt (if browser available) Always clearly communicates what it's doing! """
[docs] def __init__(self, verbose: bool = True): self.verbose = verbose self.cache: Dict[str, str] = {} # Session cache (not persistent)
[docs] async def get_credential( self, name: str, env_var: Optional[str] = None, prompt_text: Optional[str] = None, page: Optional[Page] = None, mask: bool = False, # For passwords ) -> str: """ Get credential from best available source. Args: name: Credential name (for caching) env_var: Environment variable to check prompt_text: Text to show in prompt page: Playwright page (for browser prompts) mask: Whether to mask input (for passwords) Returns: Credential value Example: username = await creds.get_credential( name="username", env_var="SCITEX_CLOUD_USERNAME", prompt_text="Django username", page=page, ) """ # Check cache first if name in self.cache: if self.verbose: print(f"🔑 Using cached {name}") return self.cache[name] # Try environment variable if env_var: value = os.getenv(env_var) if value: if self.verbose: display_value = "***" if mask else value print(f"🔑 Using {name} from ${env_var}: {display_value}") self.cache[name] = value return value # Try terminal prompt if self._is_terminal_available(): value = await self._prompt_terminal(name, prompt_text, mask) if value: self.cache[name] = value return value # Try browser prompt if page: value = await self._prompt_browser(page, name, prompt_text) if value: self.cache[name] = value return value raise ValueError(f"Could not get credential: {name}")
[docs] def _is_terminal_available(self) -> bool: """Check if we can prompt in terminal.""" try: return os.isatty(0) # stdin is a terminal except: return False
[docs] async def _prompt_terminal( self, name: str, prompt_text: Optional[str], mask: bool, ) -> Optional[str]: """Prompt user in terminal.""" prompt_text = prompt_text or name print(f"\n🔑 Credential needed: {name}") print(f" (No environment variable found)") if mask: value = getpass.getpass(f" Enter {prompt_text}: ") else: value = input(f" Enter {prompt_text}: ") return value if value else None
[docs] async def _prompt_browser( self, page: Page, name: str, prompt_text: Optional[str], ) -> Optional[str]: """Prompt user in browser window.""" prompt_text = prompt_text or name print(f"\n🔑 Asking for {name} in browser...") # Wait for page to be ready try: await page.wait_for_load_state("domcontentloaded", timeout=2000) except: pass # Continue anyway value = await page.evaluate( f""" () => {{ const response = prompt('🔑 Credential needed: {prompt_text}\\n\\n(You can also set ${name.upper()} environment variable)'); return response; }} """ ) return value if value else None
[docs] async def get_login_credentials( self, page: Optional[Page] = None, username_env: str = "SCITEX_CLOUD_USERNAME", password_env: str = "SCITEX_CLOUD_PASSWORD", ) -> Dict[str, str]: """ Get both username and password. Convenient helper for login flows. Returns: {'username': '...', 'password': '...'} """ username = await self.get_credential( name="username", env_var=username_env, prompt_text="Username", page=page, mask=False, ) password = await self.get_credential( name="password", env_var=password_env, prompt_text="Password", page=page, mask=True, ) return {"username": username, "password": password}
[docs] def clear_cache(self): """Clear credential cache.""" self.cache = {} if self.verbose: print("🔑 Credential cache cleared")
# EOF