From 2353378b23efa8337916fc33286c127329464185 Mon Sep 17 00:00:00 2001 From: mwiegand Date: Mon, 11 May 2026 21:44:39 +0200 Subject: [PATCH] alembic: add users.password_changed_at column Backfills existing rows from created_at, then enforces NOT NULL. Existing sessions without a pw_changed_at marker will be rejected on next request once the freshness check lands (one-time forced re-login post-deploy). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../versions/0009_user_password_changed_at.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 l4d2web/alembic/versions/0009_user_password_changed_at.py diff --git a/l4d2web/alembic/versions/0009_user_password_changed_at.py b/l4d2web/alembic/versions/0009_user_password_changed_at.py new file mode 100644 index 0000000..8b1a01c --- /dev/null +++ b/l4d2web/alembic/versions/0009_user_password_changed_at.py @@ -0,0 +1,32 @@ +"""users.password_changed_at + +Revision ID: 0009_user_password_changed_at +Revises: 0008_user_active +Create Date: 2026-05-11 +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + + +revision: str = "0009_user_password_changed_at" +down_revision: Union[str, Sequence[str], None] = "0008_user_active" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + with op.batch_alter_table("users") as batch_op: + batch_op.add_column(sa.Column("password_changed_at", sa.DateTime(), nullable=True)) + op.execute( + "UPDATE users SET password_changed_at = created_at " + "WHERE password_changed_at IS NULL" + ) + with op.batch_alter_table("users") as batch_op: + batch_op.alter_column("password_changed_at", nullable=False) + + +def downgrade() -> None: + with op.batch_alter_table("users") as batch_op: + batch_op.drop_column("password_changed_at")