feat(admin): list currently rate-limited entities, resolved to users #331

Merged
Claude-Run merged 1 commit from feat/BUNYIP-315-admin-rate-limits-list into main 2026-07-04 01:37:12 +02:00
Member

Add a read-only GET /v1/admin/rate-limits (AdminUser-guarded) that returns every currently-active throttle across both rate-limit sources, each resolved to a user where possible.

Unifies the two places rate-limit state lives:

  • The rate_limits table (login, registration, the OCI/OAuth presets, the per-account 2FA failure counter). Each row's opaque key is interpreted per its action's new KeyKind: email-keyed and user-id-keyed rows resolve to a user (id + email) via UserRepository, the 2fa_verify_user: prefix is stripped to recover the user id, and IP-keyed rows expose the IP with no user. A row is surfaced only when its window is still open and its count is at or over the cap; cap and window come from the RateLimitConfig presets via the new by_action lookup, never re-hardcoded, and retry_after is computed from window_start via RateLimit::active_retry_after.
  • The email-verify / email-change resend limiters (BUNYIP-313), which count rows outside the rate_limits table. Users at or over the shared RESEND_LIMIT_MAX within the rolling window are synthesized into entries under the pseudo-actions email_verification / email_change, reusing the shared constants and resend_retry_after_secs so retry_after matches the enforcement path exactly.

Supporting changes, all single-source-of-truth: add a key_kind field to every RateLimitConfig preset plus a KeyKind/KeySubject taxonomy and by_action/subject helpers; promote the feedback_submit config from a handler-local literal to a preset so its cap/window resolve from one place; extract the 2FA key prefix into TWO_FACTOR_KEY_PREFIX shared by the totp handler and the read path; add RateLimitRepository::list_active and TokenRepository::list_email_{verification,change}_over_limit; make resend_retry_after_secs pub.

The unified list is paginated in memory like list_users. Unit-tested at the pure-logic level for an email-keyed action, an IP-keyed action, the 2fa_verify_user: prefix case, the active-window/cap gate, and an email-verification synthesized entry.

#BUNYIP-315

Add a read-only GET /v1/admin/rate-limits (AdminUser-guarded) that returns every currently-active throttle across both rate-limit sources, each resolved to a user where possible. Unifies the two places rate-limit state lives: - The rate_limits table (login, registration, the OCI/OAuth presets, the per-account 2FA failure counter). Each row's opaque key is interpreted per its action's new KeyKind: email-keyed and user-id-keyed rows resolve to a user (id + email) via UserRepository, the 2fa_verify_user: prefix is stripped to recover the user id, and IP-keyed rows expose the IP with no user. A row is surfaced only when its window is still open and its count is at or over the cap; cap and window come from the RateLimitConfig presets via the new by_action lookup, never re-hardcoded, and retry_after is computed from window_start via RateLimit::active_retry_after. - The email-verify / email-change resend limiters (BUNYIP-313), which count rows outside the rate_limits table. Users at or over the shared RESEND_LIMIT_MAX within the rolling window are synthesized into entries under the pseudo-actions email_verification / email_change, reusing the shared constants and resend_retry_after_secs so retry_after matches the enforcement path exactly. Supporting changes, all single-source-of-truth: add a key_kind field to every RateLimitConfig preset plus a KeyKind/KeySubject taxonomy and by_action/subject helpers; promote the feedback_submit config from a handler-local literal to a preset so its cap/window resolve from one place; extract the 2FA key prefix into TWO_FACTOR_KEY_PREFIX shared by the totp handler and the read path; add RateLimitRepository::list_active and TokenRepository::list_email_{verification,change}_over_limit; make resend_retry_after_secs pub. The unified list is paginated in memory like list_users. Unit-tested at the pure-logic level for an email-keyed action, an IP-keyed action, the 2fa_verify_user: prefix case, the active-window/cap gate, and an email-verification synthesized entry. #BUNYIP-315
feat(admin): list currently rate-limited entities, resolved to users
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 35s
Check / fmt + clippy + build + tests (pull_request) Successful in 16m23s
Create release / Create release from merged PR (pull_request) Has been skipped
519789e9f6
Add a read-only GET /v1/admin/rate-limits (AdminUser-guarded) that returns every currently-active throttle across both rate-limit sources, each resolved to a user where possible.

Unifies the two places rate-limit state lives:

- The rate_limits table (login, registration, the OCI/OAuth presets, the per-account 2FA failure counter). Each row's opaque key is interpreted per its action's new KeyKind: email-keyed and user-id-keyed rows resolve to a user (id + email) via UserRepository, the 2fa_verify_user: prefix is stripped to recover the user id, and IP-keyed rows expose the IP with no user. A row is surfaced only when its window is still open and its count is at or over the cap; cap and window come from the RateLimitConfig presets via the new by_action lookup, never re-hardcoded, and retry_after is computed from window_start via RateLimit::active_retry_after.
- The email-verify / email-change resend limiters (BUNYIP-313), which count rows outside the rate_limits table. Users at or over the shared RESEND_LIMIT_MAX within the rolling window are synthesized into entries under the pseudo-actions email_verification / email_change, reusing the shared constants and resend_retry_after_secs so retry_after matches the enforcement path exactly.

Supporting changes, all single-source-of-truth: add a key_kind field to every RateLimitConfig preset plus a KeyKind/KeySubject taxonomy and by_action/subject helpers; promote the feedback_submit config from a handler-local literal to a preset so its cap/window resolve from one place; extract the 2FA key prefix into TWO_FACTOR_KEY_PREFIX shared by the totp handler and the read path; add RateLimitRepository::list_active and TokenRepository::list_email_{verification,change}_over_limit; make resend_retry_after_secs pub.

The unified list is paginated in memory like list_users. Unit-tested at the pure-logic level for an email-keyed action, an IP-keyed action, the 2fa_verify_user: prefix case, the active-window/cap gate, and an email-verification synthesized entry.

#BUNYIP-315
Claude-Run deleted branch feat/BUNYIP-315-admin-rate-limits-list 2026-07-04 01:37:12 +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!331
No description provided.