feat(auth): B-2.1 ConsentLog model (Loi 25) + User MFA/OAuth/Stripe fields

- New src/models/consent.py — ConsentLog with user_id FK, consent_type
  ('cgu' | 'confidentialite' | 'marketing' | 'analytics'), version, granted
  bool, granted_at/revoked_at timestamps, ip_address (45 chars for IPv6),
  user_agent (500 chars). User.consent_logs backref. Audit trail per
  LPRPSP art. 14 (consent tracé) + art. 3.5 (journal).
- src/models/user.py: add 7 new columns (totp_secret, totp_enabled DEFAULT 0,
  webauthn_credentials JSON, ordre_pro, cabinet, stripe_customer_id,
  subscription_status). Do NOT duplicate existing sso_provider/sso_subject/
  email_verified/etc. (per compatibility-audit C4).
- src/init_db.py: 7 add_column_if_not_exists() calls for the new User
  columns + 2 create_index_if_not_exists() for stripe_customer_id and
  subscription_status. NO Alembic — init_db.py pattern matches
  compatibility-audit C3.
- src/models/__init__.py: register ConsentLog import.
- tests/test_consent_log.py: 7 tests — grant flow, 4 consent types, revoke
  preserves audit trail, User backref, NOT NULL on ip/UA, User.B-2.1 fields
  round-trip, defaults safe.
This commit is contained in:
Allison
2026-04-27 21:44:37 -04:00
parent d45c9c9349
commit 48d2abfa74
5 changed files with 274 additions and 0 deletions

View File

@@ -284,6 +284,31 @@ def initialize_database(app):
app.logger.info("Added transcription_hotwords column to user table")
if add_column_if_not_exists(engine, 'user', 'transcription_initial_prompt', 'TEXT'):
app.logger.info("Added transcription_initial_prompt column to user table")
# === B-2.1: MFA / WebAuthn / Stripe / Loi 25 user fields ===
if add_column_if_not_exists(engine, 'user', 'totp_secret', 'VARCHAR(64)'):
app.logger.info("Added totp_secret column to user table")
if add_column_if_not_exists(engine, 'user', 'totp_enabled', 'BOOLEAN DEFAULT 0 NOT NULL'):
app.logger.info("Added totp_enabled column to user table")
if add_column_if_not_exists(engine, 'user', 'webauthn_credentials', 'JSON'):
app.logger.info("Added webauthn_credentials column to user table")
if add_column_if_not_exists(engine, 'user', 'ordre_pro', 'VARCHAR(50)'):
app.logger.info("Added ordre_pro column to user table")
if add_column_if_not_exists(engine, 'user', 'cabinet', 'VARCHAR(255)'):
app.logger.info("Added cabinet column to user table")
if add_column_if_not_exists(engine, 'user', 'stripe_customer_id', 'VARCHAR(120)'):
app.logger.info("Added stripe_customer_id column to user table")
if add_column_if_not_exists(engine, 'user', 'subscription_status', 'VARCHAR(20)'):
app.logger.info("Added subscription_status column to user table")
# === B-2.1: Indexes on stripe_customer_id and subscription_status ===
try:
if create_index_if_not_exists(engine, 'idx_user_stripe_customer', 'user', 'stripe_customer_id'):
app.logger.info("Created index idx_user_stripe_customer on user.stripe_customer_id")
if create_index_if_not_exists(engine, 'idx_user_subscription_status', 'user', 'subscription_status'):
app.logger.info("Created index idx_user_subscription_status on user.subscription_status")
except Exception as e:
app.logger.warning(f"Could not create B-2.1 user indexes: {e}")
if add_column_if_not_exists(engine, 'tag', 'default_hotwords', 'TEXT'):
app.logger.info("Added default_hotwords column to tag table")
if add_column_if_not_exists(engine, 'tag', 'default_initial_prompt', 'TEXT'):