CRITICAL (CVE-class): - C-4: Remove DEV_MODE fallback with hardcoded postgres:postgres credentials - C-3: WebSocket endpoints now require JWT or API key token (?token=) auth - C-1: Superset environment passwords encrypted via Fernet at rest in DB - H-4: SESSION_SECRET_KEY separated from JWT AUTH_SECRET_KEY - H-1: Log injection via %s-formatting instead of f-strings - H-5: X-Trace-ID header validated as UUID4 to prevent trace poisoning INFRASTRUCTURE: - New src/core/encryption.py — EncryptionManager extracted from llm_provider - Test DB: per-module sqlite:///:memory: with PRAGMA foreign_keys=ON - testcontainers PostgreSQL via TEST_DB=postgres env var - conftest temp-file global engine (no 10GB shared-cache leak) TEST FIXES (44 pre-existing → 0): - test_smoke_plugins: module-level sys.modules mock isolated to per-test fixture - test_migration_engine: EXT:Python:uuid → uuid syntax fix - test_dashboards_api: mock env attributes + correct patch target - test_constants_audit_fixes: sync expected constant names with actual - test_defensive_guards: patch.object instead of module-level Repo mock - test_clean_release_cli: removed empty config.json directory - FK violations: TaskRecord parents in log_persistence, Environment in mapping_service, ReleaseCandidate in candidate_manifest_services ORTHOGONAL TESTS (18 new): - test_security_orthogonal.py: bcrypt 72-byte limit, unicode, API key format invariants, Fernet robustness, encryption key lifecycle, log injection protection, JWT edge cases MODEL FIXES: - MetricSnapshot.job_id: nullable with SET NULL (was broken FK for aggregate prune snapshots, hidden by SQLite) CLEANUP: - Removed stale :memory:test_main/test_auth/test_tasks file databases - Removed duplicate #endregion in encryption_key.py
72 lines
2.9 KiB
Python
72 lines
2.9 KiB
Python
# #region AuthConfigModule [C:2] [TYPE Module] [SEMANTICS pydantic, auth, auth-config, config]
|
|
#
|
|
# @BRIEF Centralized configuration for authentication and authorization.
|
|
# @LAYER Core
|
|
# @RELATION DEPENDS_ON -> [EXT:Library:pydantic]
|
|
#
|
|
# @INVARIANT All sensitive configuration must be loaded from environment; no hardcoded secrets.
|
|
# @RATIONALE SECRET_KEY and AUTH_DATABASE_URL crash-early if env vars are missing.
|
|
# Dev fallback for AUTH_DATABASE_URL removed — Class 1 violation restored.
|
|
# @REJECTED Default secrets in source code rejected — Class 1 security violation:
|
|
# "super-secret-key-change-in-production" and "postgres:postgres" exposed
|
|
# secrets in version control. DEV_MODE fallback for AUTH_DATABASE_URL removed
|
|
# in [SEC:C-4] — hardcoded postgres:postgres is a clear-text credential leak.
|
|
|
|
from pydantic import Field, field_validator
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
# #region AuthConfig [TYPE Class]
|
|
# @BRIEF Holds authentication-related settings.
|
|
# @PRE Environment variables may be provided via .env file.
|
|
# @POST Returns a configuration object with validated settings.
|
|
# @RELATION INHERITS -> [EXT:Library:pydantic_settings.BaseSettings]
|
|
class AuthConfig(BaseSettings):
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
# JWT Settings
|
|
SECRET_KEY: str = Field(default="", validation_alias="AUTH_SECRET_KEY")
|
|
ALGORITHM: str = "HS256"
|
|
ACCESS_TOKEN_EXPIRE_MINUTES: int = 480
|
|
REFRESH_TOKEN_EXPIRE_DAYS: int = 7
|
|
|
|
# Database Settings
|
|
AUTH_DATABASE_URL: str = Field(default="", validation_alias="AUTH_DATABASE_URL")
|
|
|
|
# ADFS Settings
|
|
ADFS_CLIENT_ID: str = Field(default="", validation_alias="ADFS_CLIENT_ID")
|
|
ADFS_CLIENT_SECRET: str = Field(default="", validation_alias="ADFS_CLIENT_SECRET")
|
|
ADFS_METADATA_URL: str = Field(default="", validation_alias="ADFS_METADATA_URL")
|
|
|
|
@field_validator("SECRET_KEY", mode="after")
|
|
@classmethod
|
|
def validate_secret_key(cls, v: str) -> str:
|
|
if v:
|
|
return v
|
|
raise ValueError(
|
|
"AUTH_SECRET_KEY environment variable is required. "
|
|
"Set it in .env or export it before starting the server."
|
|
)
|
|
|
|
@field_validator("AUTH_DATABASE_URL", mode="after")
|
|
@classmethod
|
|
def validate_auth_db_url(cls, v: str) -> str:
|
|
if v:
|
|
return v
|
|
raise ValueError(
|
|
"AUTH_DATABASE_URL environment variable is required. "
|
|
"Set it in .env or export it before starting the server. "
|
|
"For local development, create a .env file with AUTH_DATABASE_URL=postgresql+psycopg2://... "
|
|
"or use docker-compose.yml with pre-configured PostgreSQL."
|
|
)
|
|
|
|
# #endregion AuthConfig
|
|
|
|
# #region auth_config [TYPE Variable]
|
|
# @BRIEF Singleton instance of AuthConfig.
|
|
# @RELATION DEPENDS_ON -> AuthConfig
|
|
auth_config = AuthConfig()
|
|
# #endregion auth_config
|
|
|
|
# #endregion AuthConfigModule
|