@@ -206,75 +206,64 @@ def _thing_collections_block(
206206
207207
208208def _pygeoapi_db_settings () -> tuple [str , str , str , str , str ]:
209- from dotenv import dotenv_values
210-
211- # Read .env directly so stale shell vars (e.g. from conda) can't override
212- # PYGEOAPI_POSTGRES_* values. Shell env takes precedence only for the
213- # PYGEOAPI_-prefixed keys (explicit per-service override), while the
214- # generic POSTGRES_* fallback always comes from the file.
215- env_file = Path (__file__ ).resolve ().parents [1 ] / ".env"
216- dotenv = dotenv_values (env_file ) if env_file .exists () else {}
217-
218- def _resolve (pygeoapi_key : str , fallback_key : str , default : str = "" ) -> str :
219- # Priority: .env PYGEOAPI_* > shell PYGEOAPI_* > .env POSTGRES_* >
220- # shell POSTGRES_* > hard default.
221- # Shell PYGEOAPI_* comes before .env POSTGRES_* so that explicit
222- # per-service overrides in Docker (e.g. PYGEOAPI_POSTGRES_HOST=db)
223- # beat the generic localhost values in .env.
224- return (
225- (dotenv .get (pygeoapi_key ) or "" ).strip ()
226- or (os .environ .get (pygeoapi_key ) or "" ).strip ()
227- or (dotenv .get (fallback_key ) or "" ).strip ()
228- or (os .environ .get (fallback_key ) or "" ).strip ()
229- or default
230- )
231-
232- host = _resolve ("PYGEOAPI_POSTGRES_HOST" , "POSTGRES_HOST" , "127.0.0.1" )
233- port = _resolve ("PYGEOAPI_POSTGRES_PORT" , "POSTGRES_PORT" , "5432" )
234- dbname = _resolve ("PYGEOAPI_POSTGRES_DB" , "POSTGRES_DB" , "postgres" )
235- user = _resolve ("PYGEOAPI_POSTGRES_USER" , "POSTGRES_USER" )
209+ host = (
210+ (os .environ .get ("PYGEOAPI_POSTGRES_HOST" ) or "" ).strip ()
211+ or (os .environ .get ("POSTGRES_HOST" ) or "" ).strip ()
212+ or "127.0.0.1"
213+ )
214+ port = (
215+ (os .environ .get ("PYGEOAPI_POSTGRES_PORT" ) or "" ).strip ()
216+ or (os .environ .get ("POSTGRES_PORT" ) or "" ).strip ()
217+ or "5432"
218+ )
219+ dbname = (
220+ (os .environ .get ("PYGEOAPI_POSTGRES_DB" ) or "" ).strip ()
221+ or (os .environ .get ("POSTGRES_DB" ) or "" ).strip ()
222+ or "postgres"
223+ )
224+ user = (os .environ .get ("PYGEOAPI_POSTGRES_USER" ) or "" ).strip () or (
225+ os .environ .get ("POSTGRES_USER" ) or ""
226+ ).strip ()
236227 if not user :
237228 raise RuntimeError (
238229 "PYGEOAPI_POSTGRES_USER or POSTGRES_USER must be set and "
239- "non-empty in the environment or .env file ."
230+ "non-empty to generate the pygeoapi configuration ."
240231 )
241- # Resolve the actual password at config-write time and embed it directly
242- # in the generated config file (which is already chmod 0600). This avoids
243- # stale shell env vars corrupting the ${VAR} expansion that pygeoapi's
244- # yaml_load would otherwise perform at request time.
245- password = _resolve ("PYGEOAPI_POSTGRES_PASSWORD" , "POSTGRES_PASSWORD" )
246- if not password :
232+ if os .environ .get ("PYGEOAPI_POSTGRES_PASSWORD" ) is None :
247233 raise RuntimeError (
248- "PYGEOAPI_POSTGRES_PASSWORD or POSTGRES_PASSWORD must be set "
249- "and non-empty in the environment or .env file ."
234+ "PYGEOAPI_POSTGRES_PASSWORD must be set to "
235+ "generate the pygeoapi configuration ."
250236 )
251- return host , port , dbname , user , password
237+ return host , port , dbname , user , "${PYGEOAPI_POSTGRES_PASSWORD}"
252238
253239
254240def _write_config (path : Path ) -> None :
255- host , port , dbname , user , password = _pygeoapi_db_settings ()
256- # Escape braces so str.format() doesn't misinterpret them in the password.
257- password_for_format = password .replace ("{" , "{{" ).replace ("}" , "}}" )
241+ host , port , dbname , user , password_placeholder = _pygeoapi_db_settings ()
258242 template = _template_path ().read_text (encoding = "utf-8" )
259243 config = template .format (
260244 server_url = _server_url (),
261245 postgres_host = host ,
262246 postgres_port = port ,
263247 postgres_db = dbname ,
264248 postgres_user = user ,
265- postgres_password_env = password_for_format ,
249+ postgres_password_env = password_placeholder ,
266250 thing_collections_block = _thing_collections_block (
267251 host = host ,
268252 port = port ,
269253 dbname = dbname ,
270254 user = user ,
271- password_placeholder = password ,
255+ password_placeholder = password_placeholder ,
272256 ),
273257 )
274- # NOTE: The generated runtime config file contains database credentials
275- # including the plaintext password. It is protected by chmod 0600.
258+ # NOTE: The generated runtime config file at
259+ # `${PYGEOAPI_RUNTIME_DIR}/pygeoapi-config.yml` (default:
260+ # `/tmp/pygeoapi/pygeoapi-config.yml`) contains database connection details
261+ # (host, port, dbname, user). Although the password is expected to be
262+ # provided via environment variables at runtime by pygeoapi, this file
263+ # should still be treated as sensitive configuration:
276264 # * Do not commit it to version control.
277265 # * Do not expose it in logs, error messages, or diagnostics.
266+ # * Ensure filesystem permissions restrict access appropriately.
278267 path .write_text (config , encoding = "utf-8" )
279268 path .chmod (0o600 )
280269
0 commit comments