mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 11:21:08 +00:00
[AZ-536] [AZ-537] [AZ-538] Argon2id, login rate limit + lockout, CORS https-only
AZ-536 — replace unsalted SHA-384 password hashing with Argon2id (RFC 9106). Stored as PHC string with 64 MiB / 3 iter / 1 lane defaults; legacy SHA-384 hashes detected by prefix and lazily re-hashed on next successful login. Verify uses CryptographicOperations.FixedTimeEquals on both formats. AZ-537 — add per-IP sliding window rate limit on /login (ASP.NET Core RateLimiter, 10/60s default — production-tight) plus DB-backed per-account limit (5/300s) and consecutive-failure lockout (10 / 15 min) on the users row. Adds a generic audit_events table with INSERT/SELECT-only grants for the app role so the per-account count is queryable and admins cannot erase their own forensic trail. BusinessExceptionHandler maps AccountLocked to 423 and LoginRateLimited to 429, both with Retry-After. AZ-538 — drop the http://admin.azaion.com origin from CORS, gate UseHsts() + UseHttpsRedirection() to non-Development envs (1y / preload). Test infra: Npgsql in the e2e project + a DbHelper for direct DB inspection used by the AZ-536/537 ACs. appsettings.Development.json raises PerIpPermitLimit to 1000 so the suite (~270 logins from one container IP) doesn't false-trip the limiter. Tests: 53 pass + 3 documented skips (per-IP rate limit needs distinct client IPs; HSTS/HTTPS redirect need ASPNETCORE_ENVIRONMENT=Production). Code review: PASS_WITH_WARNINGS — 0 Critical, 0 High, 1 Medium, 3 Low. See _docs/03_implementation/reviews/batch_01_cycle2_review.md. Closes AZ-530 epic batch 1 of 4. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
-- AZ-537 (Epic AZ-530, CMMC AC.L2-3.1.8): account lockout + audit events.
|
||||
-- Adds the per-row state used by UserService.ValidateUser to enforce a 10-failure
|
||||
-- consecutive-attempt lockout, plus a generic audit_events table that the per-account
|
||||
-- sliding-window rate-limit reads. The audit table is also reused by future security
|
||||
-- events (login_success, lockout_release, etc.).
|
||||
|
||||
ALTER TABLE public.users
|
||||
ADD COLUMN IF NOT EXISTS failed_login_count int NOT NULL DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS lockout_until timestamp NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.audit_events
|
||||
(
|
||||
id bigserial PRIMARY KEY,
|
||||
event_type varchar(64) NOT NULL,
|
||||
occurred_at timestamp NOT NULL DEFAULT now(),
|
||||
email varchar(160) NULL,
|
||||
ip varchar(64) NULL,
|
||||
metadata text NULL
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS audit_events_event_type_email_idx
|
||||
ON public.audit_events (event_type, email, occurred_at DESC);
|
||||
|
||||
GRANT INSERT, SELECT ON public.audit_events TO azaion_admin;
|
||||
GRANT SELECT ON public.audit_events TO azaion_reader;
|
||||
GRANT USAGE, SELECT ON SEQUENCE public.audit_events_id_seq TO azaion_admin;
|
||||
Reference in New Issue
Block a user