feat(#28): Gate B [08/10]: AES-GCM settings round-trip (AES-1, AES-2)
- AES-GCM encrypt/decrypt module (app/security/aes.py):
- get_aes_key: decode base64 AES master key to 32 bytes
- encrypt_setting: AES-256-GCM with fresh 12-byte nonce per call, returns nonce||ciphertext
- decrypt_setting: decrypts ciphertext, raises AuthenticationError on tamper/wrong key
- Settings endpoint (app/api/settings.py):
- GET /api/v1/settings: returns SettingsView with decrypted values, secrets masked
(woo_app_password never returned, only woo_credentials_configured boolean)
- PUT /api/v1/settings: encrypts all setting values AES-GCM, stores in settings table
- Rejects woo_consumer_key/woo_consumer_secret fields
- Validates woo_create_status (draft|publish)
- App factory (app/main.py): stores settings on app.state for dependency injection
- Unit tests (tests/unit/test_aes.py): 18 tests covering AES-1 (round-trip), AES-2
(missing key refusal), FUND-17 (fresh nonce, tamper rejection), FUND-19
(no plaintext fallback)
Test results: 23/23 passed (18 new AES + 5 existing health tests)
This commit is contained in:
+12
-7
@@ -5,7 +5,8 @@ from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from app.api import health
|
||||
from app.api import health as health_router
|
||||
from app.api import settings as settings_router
|
||||
from app.config import Settings
|
||||
from app.db.engine import close_db_connection, init_db
|
||||
from app.logging_config import configure_logging
|
||||
@@ -26,24 +27,28 @@ async def lifespan(app: FastAPI):
|
||||
await close_db_connection()
|
||||
|
||||
|
||||
def create_app(settings: Settings | None = None) -> FastAPI:
|
||||
def create_app(app_settings: Settings | None = None) -> FastAPI:
|
||||
"""Create a fresh FastAPI app instance.
|
||||
|
||||
Each call produces an independent app with its own database connection
|
||||
and cleared dependency overrides. This is the factory used by tests.
|
||||
|
||||
Args:
|
||||
settings: Application settings. Uses env vars if None.
|
||||
app_settings: Application settings. Uses env vars if None.
|
||||
|
||||
Returns:
|
||||
Configured FastAPI application.
|
||||
"""
|
||||
if settings is None:
|
||||
settings = Settings()
|
||||
settings.validate()
|
||||
if app_settings is None:
|
||||
app_settings = Settings()
|
||||
app_settings.validate()
|
||||
|
||||
app = FastAPI(title="SlabUploader", version="0.1.0", lifespan=lifespan)
|
||||
|
||||
app.include_router(health.router)
|
||||
# Store settings on app state for dependency injection
|
||||
app.state.settings = app_settings
|
||||
|
||||
app.include_router(health_router.router)
|
||||
app.include_router(settings_router.router)
|
||||
|
||||
return app
|
||||
|
||||
Reference in New Issue
Block a user