2222 logger .error ("Please run 'api-admin custom init' to regenerate defaults." )
2323 sys .exit (1 )
2424
25+ # Security validation constants
26+ MIN_SECRET_KEY_LENGTH = 32
27+
2528
2629class Settings (BaseSettings ):
2730 """Main Settings class.
2831
2932 This allows to set some defaults, that can be overwritten from the .env
3033 file if it exists.
31- Do NOT put passwords and similar in here, use the .env file instead, it will
32- not be stored in the Git repository.
34+
35+ SECURITY WARNING: Critical settings (SECRET_KEY, DB_PASSWORD, DB_USER)
36+ MUST be set in your .env file. The application will fail to start if
37+ these are not properly configured with secure values.
38+
39+ Do NOT put real passwords in this file - use the .env file instead
40+ (it's in .gitignore and won't be stored in Git).
41+
42+ To get started, copy .env.example to .env and update the values.
3343 """
3444
3545 project_root : Path = get_project_root ()
@@ -45,23 +55,28 @@ class Settings(BaseSettings):
4555 cors_origins : str = "*"
4656
4757 # Setup the Postgresql database.
48- db_user : str = "my_db_username"
49- db_password : str = "Sup3rS3cr3tP455w0rd" # noqa: S105
58+ # IMPORTANT: Set DB_USER and DB_PASSWORD in your .env file!
59+ db_user : str = "CHANGE_ME_IN_ENV_FILE"
60+ db_password : str = "CHANGE_ME_IN_ENV_FILE" # noqa: S105
5061 db_address : str = "localhost"
5162 db_port : str = "5432"
5263 db_name : str = "api-template"
5364
5465 test_with_postgres : bool = False
5566
5667 # Setup the TEST Postgresql database.
57- test_db_user : str = "my_db_username"
58- test_db_password : str = "Sup3rS3cr3tP455w0rd" # noqa: S105
68+ # Note: Safe defaults for local/CI testing only
69+ test_db_user : str = "test_user"
70+ test_db_password : str = "test_password_local_only" # noqa: S105
5971 test_db_address : str = "localhost"
6072 test_db_port : str = "5432"
6173 test_db_name : str = "api-template-test"
6274
63- # JTW secret Key
64- secret_key : str = "32DigitsofSecretNumbers" # noqa: S105
75+ # JWT secret Key - CRITICAL SECURITY SETTING
76+ # Generate with: openssl rand -hex 32
77+ # Or Python: import secrets; secrets.token_hex(32)
78+ # Set SECRET_KEY in your .env file!
79+ secret_key : str = "CHANGE_ME_IN_ENV_FILE" # noqa: S105
6580 access_token_expire_minutes : int = 120
6681
6782 # Custom Metadata
@@ -73,9 +88,11 @@ class Settings(BaseSettings):
7388 year : str = custom_metadata .year
7489
7590 # email settings
76- mail_username : str = "test_username"
77- mail_password : str = "s3cr3tma1lp@ssw0rd" # noqa: S105
78- mail_from : str = "test@email.com"
91+ # Note: Set these in .env for production email functionality
92+ # Email features will fail gracefully if not configured
93+ mail_username : str = ""
94+ mail_password : str = ""
95+ mail_from : str = ""
7996 mail_port : int = 587
8097 mail_server : str = "mail.server.com"
8198 mail_from_name : str = "FASTAPI Template"
@@ -106,6 +123,80 @@ def check_api_root(cls: type[Settings], value: str) -> str:
106123 return value [:- 1 ]
107124 return value
108125
126+ @field_validator ("secret_key" )
127+ @classmethod
128+ def validate_secret_key (cls : type [Settings ], value : str ) -> str :
129+ """Ensure secret key is not a weak or default value."""
130+ weak_keys = [
131+ "CHANGE_ME_IN_ENV_FILE" ,
132+ "32DigitsofSecretNumbers" ,
133+ "CHANGE_ME" ,
134+ "secret" ,
135+ "secretkey" ,
136+ ]
137+ if value .lower () in [k .lower () for k in weak_keys ]:
138+ msg = (
139+ "\n "
140+ "=" * 70 + "\n "
141+ "SECURITY ERROR: SECRET_KEY is using a weak/default value!\n "
142+ "=" * 70 + "\n "
143+ "Generate a strong key with one of these commands:\n "
144+ " openssl rand -hex 32\n "
145+ " python -c 'import secrets; print(secrets.token_hex(32))'\n \n "
146+ "Then add it to your .env file:\n "
147+ " SECRET_KEY=your_generated_key_here\n "
148+ "=" * 70
149+ )
150+ raise ValueError (msg )
151+ if len (value ) < MIN_SECRET_KEY_LENGTH :
152+ msg = (
153+ f"SECRET_KEY must be at least { MIN_SECRET_KEY_LENGTH } "
154+ f"characters for security. Current length: { len (value )} "
155+ )
156+ raise ValueError (msg )
157+ return value
158+
159+ @field_validator ("db_password" )
160+ @classmethod
161+ def validate_db_password (cls : type [Settings ], value : str ) -> str :
162+ """Ensure database password is not a weak or default value."""
163+ weak_passwords = [
164+ "CHANGE_ME_IN_ENV_FILE" ,
165+ "Sup3rS3cr3tP455w0rd" ,
166+ "CHANGE_ME" ,
167+ "password" ,
168+ "admin" ,
169+ ]
170+ if value in weak_passwords :
171+ msg = (
172+ "\n "
173+ "=" * 70 + "\n "
174+ "SECURITY ERROR: DB_PASSWORD is using a weak/default value!\n "
175+ "=" * 70 + "\n "
176+ "Set a strong database password in your .env file:\n "
177+ " DB_PASSWORD=your_secure_password_here\n "
178+ "=" * 70
179+ )
180+ raise ValueError (msg )
181+ return value
182+
183+ @field_validator ("db_user" )
184+ @classmethod
185+ def validate_db_user (cls : type [Settings ], value : str ) -> str :
186+ """Ensure database user is not a default value."""
187+ if value == "CHANGE_ME_IN_ENV_FILE" :
188+ msg = (
189+ "\n "
190+ "=" * 70 + "\n "
191+ "CONFIGURATION ERROR: DB_USER is not set!\n "
192+ "=" * 70 + "\n "
193+ "Set your database username in your .env file:\n "
194+ " DB_USER=your_database_username\n "
195+ "=" * 70
196+ )
197+ raise ValueError (msg )
198+ return value
199+
109200
110201@lru_cache
111202def get_settings () -> Settings :
0 commit comments