33from __future__ import annotations
44
55import logging
6+ import os
67from typing import TYPE_CHECKING
78
89import httpx
@@ -73,13 +74,28 @@ def __init__(
7374 self ._client_repository = client_repository or get_dcr_client_repository ()
7475 self ._settings = get_settings ()
7576
76- # Fernet cipher for encrypting client secrets
77+ # Fernet cipher for encrypting client secrets.
78+ # In production (Cloud Run), DCR_ENCRYPTION_KEY is required to prevent
79+ # silent data loss from missing encryption configuration.
7780 self ._fernet : Fernet | None = None
7881 if self ._settings .dcr_encryption_key :
7982 try :
8083 self ._fernet = Fernet (self ._settings .dcr_encryption_key .encode ())
8184 except Exception as e :
82- logger .error ("Invalid DCR encryption key: %s" , e )
85+ raise ValueError (
86+ f"Invalid DCR_ENCRYPTION_KEY: { e } . "
87+ "Generate a valid key with: "
88+ "python -c 'from cryptography.fernet import Fernet; "
89+ "print(Fernet.generate_key().decode())'"
90+ ) from e
91+ elif os .getenv ("K_SERVICE" ):
92+ raise ValueError (
93+ "DCR_ENCRYPTION_KEY is required in production "
94+ f"(K_SERVICE={ os .getenv ('K_SERVICE' )} ). "
95+ "Client secrets cannot be stored without an encryption key. "
96+ "Generate a key with: python -c 'from cryptography.fernet import Fernet; "
97+ "print(Fernet.generate_key().decode())'"
98+ )
8399
84100 def _get_gma_client (self ) -> GMAClient :
85101 """Get the GMA client (lazy initialization)."""
@@ -95,10 +111,15 @@ def _encrypt_secret(self, secret: str) -> str:
95111
96112 Returns:
97113 Encrypted secret as base64 string.
114+
115+ Raises:
116+ RuntimeError: If DCR_ENCRYPTION_KEY is not configured.
98117 """
99118 if not self ._fernet :
100- logger .warning ("DCR_ENCRYPTION_KEY not set, using ephemeral key" )
101- self ._fernet = Fernet (Fernet .generate_key ())
119+ raise RuntimeError (
120+ "Cannot encrypt client secret: DCR_ENCRYPTION_KEY is not configured. "
121+ "Set DCR_ENCRYPTION_KEY to a valid Fernet key before performing DCR operations."
122+ )
102123 return self ._fernet .encrypt (secret .encode ()).decode ()
103124
104125 def _decrypt_secret (self , encrypted_secret : str ) -> str | None :
0 commit comments