| """ |
| Admin API Key Resolution |
| |
| Shared utility for resolving the admin API key from config, environment, |
| or auto-generated key file. Used by both routes.py and admin.py to ensure |
| consistent authentication across all admin endpoints. |
| """ |
|
|
| import os |
| import hmac |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| _generated_admin_api_key = None |
|
|
|
|
| def get_admin_api_key(config): |
| """Get the admin API key from config, environment variable, or auto-generate one. |
| |
| Priority order: |
| 1. Config file: admin_api_key setting |
| 2. Environment variable: POTATO_ADMIN_API_KEY |
| 3. Auto-generated: Creates a random key and saves it to {task_dir}/admin_api_key.txt |
| |
| Args: |
| config: The application config dict. |
| |
| Returns: |
| str or None: The admin API key, or None if generation fails. |
| """ |
| global _generated_admin_api_key |
|
|
| |
| configured_key = config.get("admin_api_key") |
| if configured_key: |
| return configured_key |
|
|
| |
| env_key = os.environ.get("POTATO_ADMIN_API_KEY") |
| if env_key: |
| return env_key |
|
|
| |
| if _generated_admin_api_key: |
| return _generated_admin_api_key |
|
|
| |
| task_dir = config.get("task_dir", ".") |
| if not task_dir: |
| task_dir = "." |
|
|
| key_file_path = os.path.join(task_dir, "admin_api_key.txt") |
|
|
| |
| if os.path.exists(key_file_path): |
| try: |
| with open(key_file_path, 'r', encoding='utf-8') as f: |
| existing_key = f.read().strip() |
| if existing_key: |
| _generated_admin_api_key = existing_key |
| logger.info(f"Loaded existing admin API key from {key_file_path}") |
| return _generated_admin_api_key |
| except Exception as e: |
| logger.warning(f"Could not read existing admin API key file: {e}") |
|
|
| |
| import secrets |
| _generated_admin_api_key = secrets.token_urlsafe(32) |
|
|
| |
| try: |
| with open(key_file_path, 'w', encoding='utf-8') as f: |
| f.write(_generated_admin_api_key) |
| logger.info(f"Generated admin API key and saved to {key_file_path}") |
| logger.info(f"Use this key to access the admin dashboard at /admin") |
| except Exception as e: |
| logger.warning(f"Could not save admin API key to file: {e}") |
| logger.info(f"Auto-generated admin API key (not persisted): {_generated_admin_api_key}") |
|
|
| return _generated_admin_api_key |
|
|
|
|
| def validate_admin_api_key(provided_key, config): |
| """Validate an admin API key against the configured or auto-generated key. |
| |
| Args: |
| provided_key: The API key provided in the request. |
| config: The application config dict. |
| |
| Returns: |
| bool: True if the key is valid or debug mode is enabled. |
| """ |
| if config.get("debug", False): |
| return True |
|
|
| expected_key = get_admin_api_key(config) |
| if not expected_key: |
| logger.warning("Could not obtain admin API key") |
| return False |
|
|
| |
| return hmac.compare_digest(str(provided_key or ""), expected_key) |
|
|