mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 21:53:41 +00:00
Adds "Passwort vergessen?" to login page with email-based password reset flow. Backend generates secure token (SHA-256 hashed, 1h expiry), sends reset link via SMTP, and validates on submission. Includes rate limiting (3 requests/hour/email), audit logging, and account unlock on successful reset. New ResetPasswordPage with password confirmation. New DB table: password_reset_tokens (migration 008). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Add password_reset_tokens table.
|
|
|
|
Revision ID: 008_password_reset_tokens
|
|
Revises: 007_add_report_type
|
|
"""
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "008_password_reset_tokens"
|
|
down_revision = "007_add_report_type"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"password_reset_tokens",
|
|
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
|
sa.Column(
|
|
"user_id",
|
|
sa.Integer,
|
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column("token_hash", sa.String(255), nullable=False),
|
|
sa.Column("expires_at", sa.DateTime, nullable=False),
|
|
sa.Column("used_at", sa.DateTime, nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime,
|
|
nullable=False,
|
|
server_default=sa.func.now(),
|
|
),
|
|
)
|
|
op.create_index("idx_prt_token", "password_reset_tokens", ["token_hash"])
|
|
op.create_index("idx_prt_user", "password_reset_tokens", ["user_id"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index("idx_prt_user", table_name="password_reset_tokens")
|
|
op.drop_index("idx_prt_token", table_name="password_reset_tokens")
|
|
op.drop_table("password_reset_tokens")
|