diff --git a/backend/backend/utils/secrets.py b/backend/backend/utils/secrets.py index f4ccf87a..135f87df 100644 --- a/backend/backend/utils/secrets.py +++ b/backend/backend/utils/secrets.py @@ -4,37 +4,38 @@ logger = logging.getLogger(__name__) -def get_secret(key: str) -> str: + +def get_secret(key: str) -> str | None: """ - Retrieve secrets from either files or environment variables. Implements a "secrets provider" pattern + Retrieve secrets from either files or environment variables. Implements a "secrets provider" pattern commonly used in containerized applications. - + 1. Check if {key}_FILE exists as an environment variable (e.g. PGSQL_PASSWORD_FILE) - If it exists, read the secret from that file location - This supports Docker/Kubernetes secrets (https://docs.docker.com/reference/compose-file/secrets) 2. Fall back to checking if {key} exists as a regular environment variable - + Example: # Using file-based secret: DATABASE_PASSWORD_FILE=/run/secrets/db_password get_secret('DATABASE_PASSWORD') # reads from /run/secrets/db_password - + # Using environment variable: DATABASE_PASSWORD=ebeefa2b4634ab18b0280c96fce1adc5969dcad133cce440353b5ed1a7387f0a get_secret('DATABASE_PASSWORD') # returns 'ebeefa2b4634ab18b0280c96fce1adc5969dcad133cce440353b5ed1a7387f0a' - + Args: key: Name of the secret to retrieve (e.g. 'DATABASE_PASSWORD') - + Returns: - str: The secret value or empty string if not found + str: The secret value or None if not found """ - debug_mode = os.getenv('DEBUG', 'False').lower() == 'true' - + debug_mode = os.getenv("DEBUG", "False").lower() == "true" + file_env_key = f"{key}_FILE" file_path = os.getenv(file_env_key) - + if file_path: path = Path(file_path) if path.exists(): @@ -47,13 +48,15 @@ def get_secret(key: str) -> str: if debug_mode: logger.debug(f"Failed to read secret file for '{key}': {e}") elif debug_mode: - logger.debug(f"File path specified for '{key}' but file not found: {file_path}") - - secret = os.getenv(key, '') + logger.debug( + f"File path specified for '{key}' but file not found: {file_path}" + ) + + secret = os.getenv(key, None) if debug_mode: if secret: logger.debug(f"Loaded secret '{key}' from environment variable") else: logger.debug(f"Secret '{key}' not found in environment or file") - + return secret diff --git a/backend/tests/utils/test_secret.py b/backend/tests/utils/test_secret.py index 8aba2226..20c1c65f 100644 --- a/backend/tests/utils/test_secret.py +++ b/backend/tests/utils/test_secret.py @@ -63,7 +63,7 @@ def test_get_secret_missing_file(): def test_get_secret_neither_exists(): - """Test empty string returned when neither file nor env var exists""" + """Test None returned when neither file nor env var exists""" with patch.dict(os.environ, {}, clear=True): assert get_secret("TEST_SECRET") == None diff --git a/frontend/utils/secretConfig.ts b/frontend/utils/secretConfig.ts index 3faf3bd2..d81ce946 100644 --- a/frontend/utils/secretConfig.ts +++ b/frontend/utils/secretConfig.ts @@ -21,7 +21,7 @@ import { readFileSync } from 'fs' * @param key - Name of the secret to retrieve (e.g. 'DATABASE_PASSWORD') * @returns The secret value or empty string if not found */ -export function getSecret(key: string): string { +export function getSecret(key: string): string | undefined { const debug = process.env.DEBUG ? process.env.DEBUG === 'True' : false const fileEnvKey = `${key}_FILE` @@ -45,7 +45,7 @@ export function getSecret(key: string): string { console.debug(`[secrets] File path specified for '${key}' but file not found: ${filePath}`) } - const secret = process.env[key] || '' + const secret = process.env[key] || undefined if (debug) { if (secret) { console.debug(`[secrets] Loaded secret '${key}' from environment variable`)