feat(admin): reset an active rate limit for a user or key, audited #332

Merged
Claude-Run merged 1 commit from feat/BUNYIP-316-admin-rate-limit-reset into main 2026-07-04 02:08:53 +02:00
Member

Add POST /v1/admin/rate-limits/reset (AdminUser-guarded) taking { action, key }, the exact identifiers the BUNYIP-315 list surfaces, and clear that one active throttle so the affected user can act again immediately.

Dispatch on action: the email_verification / email_change pseudo-actions are backed by row counts rather than the rate_limits table, so key is the user id and the reset deletes that user's in-window email_verification_tokens / email_change_requests rows (new TokenRepository::delete_recent_* methods), dropping the count below the shared resend threshold; every other action is a rate_limits table row cleared via RateLimitRepository::reset and must resolve to a known RateLimitConfig preset (a typo'd action is a clean 400, not a silent no-op).

Record a new AuditAction::AdminRateLimitReset (as_str mapping + admin-severity classification) carrying the acting admin, the reset action + key, and the resolved target user where the key maps to one. The reset dispatch lives in a thin apply_rate_limit_reset helper so the HTTP handler stays an audit wrapper and the behaviour is integration-testable against a real pool. DB-backed tests prove a throttled user is un-throttled after reset for both a rate_limits action (login) and the email-verification limiter, and that bad input is rejected; they skip when DATABASE_URL is unset, matching the existing admin handler test pattern.

#BUNYIP-316

Add POST /v1/admin/rate-limits/reset (AdminUser-guarded) taking { action, key }, the exact identifiers the BUNYIP-315 list surfaces, and clear that one active throttle so the affected user can act again immediately. Dispatch on action: the email_verification / email_change pseudo-actions are backed by row counts rather than the rate_limits table, so key is the user id and the reset deletes that user's in-window email_verification_tokens / email_change_requests rows (new TokenRepository::delete_recent_* methods), dropping the count below the shared resend threshold; every other action is a rate_limits table row cleared via RateLimitRepository::reset and must resolve to a known RateLimitConfig preset (a typo'd action is a clean 400, not a silent no-op). Record a new AuditAction::AdminRateLimitReset (as_str mapping + admin-severity classification) carrying the acting admin, the reset action + key, and the resolved target user where the key maps to one. The reset dispatch lives in a thin apply_rate_limit_reset helper so the HTTP handler stays an audit wrapper and the behaviour is integration-testable against a real pool. DB-backed tests prove a throttled user is un-throttled after reset for both a rate_limits action (login) and the email-verification limiter, and that bad input is rejected; they skip when DATABASE_URL is unset, matching the existing admin handler test pattern. #BUNYIP-316
feat(admin): reset an active rate limit for a user or key, audited
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 37s
Check / fmt + clippy + build + tests (pull_request) Successful in 17m12s
Create release / Create release from merged PR (pull_request) Has been skipped
a3a505021f
Add POST /v1/admin/rate-limits/reset (AdminUser-guarded) taking { action, key }, the exact identifiers the BUNYIP-315 list surfaces, and clear that one active throttle so the affected user can act again immediately.

Dispatch on action: the email_verification / email_change pseudo-actions are backed by row counts rather than the rate_limits table, so key is the user id and the reset deletes that user's in-window email_verification_tokens / email_change_requests rows (new TokenRepository::delete_recent_* methods), dropping the count below the shared resend threshold; every other action is a rate_limits table row cleared via RateLimitRepository::reset and must resolve to a known RateLimitConfig preset (a typo'd action is a clean 400, not a silent no-op).

Record a new AuditAction::AdminRateLimitReset (as_str mapping + admin-severity classification) carrying the acting admin, the reset action + key, and the resolved target user where the key maps to one. The reset dispatch lives in a thin apply_rate_limit_reset helper so the HTTP handler stays an audit wrapper and the behaviour is integration-testable against a real pool. DB-backed tests prove a throttled user is un-throttled after reset for both a rate_limits action (login) and the email-verification limiter, and that bad input is rejected; they skip when DATABASE_URL is unset, matching the existing admin handler test pattern.

#BUNYIP-316
Claude-Run deleted branch feat/BUNYIP-316-admin-rate-limit-reset 2026-07-04 02:08:53 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!332
No description provided.