9c3d3ef7ed
- cantaloupe/ package with click CLI (python -m cantaloupe export) - export command: --output, --headless/--visible, --scheduled, --email, --password - configure command: saves credentials to ~/.cantaloupe.env (600 perms) - cantaloupe/config.py: credential loading from env vars + env file - Updated src/auth.py _get_credentials() to also check ~/.cantaloupe.env - File size, row count (openpyxl), and elapsed time reporting - 39 new CLI/config tests + 4 updated auth tests; 86/86 pass
94 lines
2.8 KiB
Python
94 lines
2.8 KiB
Python
"""
|
|
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 "", ""
|