""" Credential management for cantaloupe CLI. Reads credentials from: 1. CANTALOUPE_EMAIL / CANTALOUPE_PASSWORD environment variables 2. ~/.cantaloupe.env file (key=value format) Saves credentials to ~/.cantaloupe.env with restrictive permissions (600). """ import os from pathlib import Path ENV_FILE = Path.home() / ".cantaloupe.env" ENV_EMAIL_KEY = "CANTALOUPE_EMAIL" ENV_PASSWORD_KEY = "CANTALOUPE_PASSWORD" def load_credentials() -> tuple[str, str]: """Load credentials from env vars or ~/.cantaloupe.env. Priority: environment variables override the env file. Returns (email, password) — either or both may be empty strings. """ email = os.environ.get(ENV_EMAIL_KEY, "") password = os.environ.get(ENV_PASSWORD_KEY, "") # If both are set via env, use them if email and password: return email, password # Check the env file if ENV_FILE.exists(): for line in ENV_FILE.read_text().splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue key, _, value = line.partition("=") key = key.strip() value = value.strip().strip('"').strip("'") if key == ENV_EMAIL_KEY and not email: email = value elif key == ENV_PASSWORD_KEY and not password: password = value return email, password def save_credentials(email: str, password: str) -> Path: """Save credentials to ~/.cantaloupe.env. Updates the email/password lines in-place, preserving any other configuration lines that may be present. Sets file permissions to 600. Returns the path to the env file. """ ENV_FILE.parent.mkdir(parents=True, exist_ok=True) # Read existing lines, filtering out old credential entries lines: list[str] = [] if ENV_FILE.exists(): for line in ENV_FILE.read_text().splitlines(): stripped = line.strip() if stripped.startswith(f"{ENV_EMAIL_KEY}=") or stripped.startswith( f"{ENV_PASSWORD_KEY}=" ): continue lines.append(line) # Append new credentials lines.append(f"{ENV_EMAIL_KEY}={email}") lines.append(f"{ENV_PASSWORD_KEY}={password}") ENV_FILE.write_text("\n".join(lines) + "\n") ENV_FILE.chmod(0o600) return ENV_FILE def prompt_credentials() -> tuple[str, str]: """Prompt interactively for credentials. Uses input() for email and getpass() for password. Returns (email, password) — empty strings if cancelled. """ try: import getpass email = input("Cantaloupe email: ").strip() password = getpass.getpass("Cantaloupe password: ") return email, password except (EOFError, KeyboardInterrupt): return "", ""