mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-02 04:47:02 -05:00
feat: Implement initial schema and add various API, service, and management command enhancements across the application.
This commit is contained in:
@@ -31,17 +31,13 @@ logger = logging.getLogger("security")
|
||||
# =============================================================================
|
||||
|
||||
# Enable secret rotation checking (set to True in production)
|
||||
SECRET_ROTATION_ENABLED = config(
|
||||
"SECRET_ROTATION_ENABLED", default=False, cast=bool
|
||||
)
|
||||
SECRET_ROTATION_ENABLED = config("SECRET_ROTATION_ENABLED", default=False, cast=bool)
|
||||
|
||||
# Secret version for tracking rotations
|
||||
SECRET_KEY_VERSION = config("SECRET_KEY_VERSION", default="1")
|
||||
|
||||
# Secret expiry warning threshold (days before expiry to start warning)
|
||||
SECRET_EXPIRY_WARNING_DAYS = config(
|
||||
"SECRET_EXPIRY_WARNING_DAYS", default=30, cast=int
|
||||
)
|
||||
SECRET_EXPIRY_WARNING_DAYS = config("SECRET_EXPIRY_WARNING_DAYS", default=30, cast=int)
|
||||
|
||||
# =============================================================================
|
||||
# Required Secrets Registry
|
||||
@@ -104,10 +100,7 @@ def validate_secret_strength(name: str, value: str, min_length: int = 10) -> boo
|
||||
return False
|
||||
|
||||
if len(value) < min_length:
|
||||
logger.error(
|
||||
f"Secret '{name}' is too short ({len(value)} chars, "
|
||||
f"minimum {min_length})"
|
||||
)
|
||||
logger.error(f"Secret '{name}' is too short ({len(value)} chars, " f"minimum {min_length})")
|
||||
return False
|
||||
|
||||
# Check for placeholder values
|
||||
@@ -123,9 +116,7 @@ def validate_secret_strength(name: str, value: str, min_length: int = 10) -> boo
|
||||
value_lower = value.lower()
|
||||
for pattern in placeholder_patterns:
|
||||
if pattern in value_lower:
|
||||
logger.warning(
|
||||
f"Secret '{name}' appears to contain a placeholder value"
|
||||
)
|
||||
logger.warning(f"Secret '{name}' appears to contain a placeholder value")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -148,9 +139,7 @@ def validate_secret_key(secret_key: str) -> bool:
|
||||
bool: True if valid, False otherwise
|
||||
"""
|
||||
if len(secret_key) < 50:
|
||||
logger.error(
|
||||
f"SECRET_KEY is too short ({len(secret_key)} chars, minimum 50)"
|
||||
)
|
||||
logger.error(f"SECRET_KEY is too short ({len(secret_key)} chars, minimum 50)")
|
||||
return False
|
||||
|
||||
has_upper = any(c.isupper() for c in secret_key)
|
||||
@@ -159,10 +148,7 @@ def validate_secret_key(secret_key: str) -> bool:
|
||||
has_special = any(not c.isalnum() for c in secret_key)
|
||||
|
||||
if not all([has_upper, has_lower, has_digit, has_special]):
|
||||
logger.warning(
|
||||
"SECRET_KEY should contain uppercase, lowercase, digits, "
|
||||
"and special characters"
|
||||
)
|
||||
logger.warning("SECRET_KEY should contain uppercase, lowercase, digits, " "and special characters")
|
||||
# Don't fail, just warn - some generated keys may not have all
|
||||
|
||||
return True
|
||||
@@ -193,7 +179,7 @@ def get_secret(
|
||||
value = config(name, default=default)
|
||||
except UndefinedValueError:
|
||||
if required:
|
||||
raise ValueError(f"Required secret '{name}' is not set")
|
||||
raise ValueError(f"Required secret '{name}' is not set") from None
|
||||
return default
|
||||
|
||||
if value and min_length > 0 and not validate_secret_strength(name, value, min_length):
|
||||
@@ -231,7 +217,7 @@ def validate_required_secrets(raise_on_error: bool = False) -> list[str]:
|
||||
msg = f"Required secret '{name}' is not set: {rules['description']}"
|
||||
errors.append(msg)
|
||||
if raise_on_error:
|
||||
raise ValueError(msg)
|
||||
raise ValueError(msg) from None
|
||||
|
||||
return errors
|
||||
|
||||
@@ -257,9 +243,7 @@ def check_secret_expiry() -> list[str]:
|
||||
version = int(SECRET_KEY_VERSION)
|
||||
# If version is very old, suggest rotation
|
||||
if version < 2:
|
||||
warnings_list.append(
|
||||
"SECRET_KEY version is old. Consider rotating secrets."
|
||||
)
|
||||
warnings_list.append("SECRET_KEY version is old. Consider rotating secrets.")
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -316,8 +300,7 @@ class EnvironmentSecretProvider(SecretProvider):
|
||||
def set_secret(self, name: str, value: str) -> bool:
|
||||
"""Environment variables are read-only at runtime."""
|
||||
logger.warning(
|
||||
f"Cannot set secret '{name}' in environment provider. "
|
||||
"Update your .env file or environment variables."
|
||||
f"Cannot set secret '{name}' in environment provider. " "Update your .env file or environment variables."
|
||||
)
|
||||
return False
|
||||
|
||||
@@ -385,4 +368,4 @@ def run_startup_validation() -> None:
|
||||
raise ValueError("SECRET_KEY does not meet security requirements")
|
||||
except UndefinedValueError:
|
||||
if not debug_mode:
|
||||
raise ValueError("SECRET_KEY is required in production")
|
||||
raise ValueError("SECRET_KEY is required in production") from None
|
||||
|
||||
Reference in New Issue
Block a user