Nobody has started this yet — be first.
Business impact
A fully-built extension point (SecurityEventLogger / NullSecurityEventLogger / RecordingSecurityEventLogger, wired into create_app() via AuditExtension.init_app(), built on the exact same pattern as MailerExtension in email.py) already proves itself at one call site: login() already calls audit.log("login_success", ...) and audit.log("login_failed", ...). But password_reset_confirm(), disable_2fa(), and revoke_all_sessions() -- each at least as security-relevant as a login attempt -- call it nowhere at all, leaving incident response and compliance with no record of these actions.
Problem
The audit.py extension point exists and works, but most of the security-relevant call sites in routes.py do not call audit.log(...).
Current behavior
A successful POST /auth/password-reset/confirm (and, separately, disable_2fa() and revoke_all_sessions()) produces zero recorded audit events, even though the extension point that would record them already exists and is proven working elsewhere in the same file.
Expected behavior
Every one of the following calls audit.log(...) with an event type and enough metadata (user_id, , , and anything else relevant) to reconstruct "what happened, to whom, from where" after the fact: -> e.g. , -> e.g. , -> e.g. .
Steps to reproduce
Why this matters
This is the "missing architectural extension point" case with a twist: the extension point itself is not missing -- it is fully built and already proven to work at one call site. The actual work is recognizing the pattern and applying the same three-line addition consistently across the parts of the codebase that need it, which requires reading and understanding several files (audit.py, email.py for the pattern it mirrors, and every relevant route in routes.py), not just one.
Suggested approach
Read audit.py's module docstring and compare login()'s two audit.log(...) calls against the shape of the calls you will need to add elsewhere. Decide what metadata (the free-form dict parameter) should carry for each new event type -- e.g. a password reset's audit event probably does not need the new password, but might reasonably note how many sessions were revoked as a result.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_12.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b feat/audit-log-extension-pointFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/audit-log-extension-pointSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inemailippassword_reset_confirm()"password_reset"disable_2fa()"2fa_disabled"revoke_all_sessions()"logout_everywhere"http://localhost:5000make seedpyotpmake docker-prod-upYou do not need Docker running at all to work the tickets -- the test suite uses an in-memory SQLite database (via StaticPool, an isolated DB per test function) and fakeredis (TestingConfig.USE_FAKE_REDIS), so everything runs against a plain virtualenv:
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pytest practice_tickets/tests/test_ticket_01.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
All 15 tests fail at the start; as you fix each ticket in app/, its test (and only its test) should flip to passing. make test (pytest -q) runs the project's own main suite -- as of writing it shows 9 pre-existing failures caused by ticket bugs 02, 03, 07, 11, and 15; fixing those tickets fixes those failures too, not a separate task. Do not look at git diff / version control history to find the bugs if this repo is ever put under source control -- the point is to find them the way you would find a real bug: from a failing test and a description of broken behavior, not a diff.
Level 2
Implement a feature
Extend the system within its own patterns.