fix(admin-auth): add rate limiting to admin login endpoint
All checks were successful
build-and-release / build (makeup-artists) (push) Successful in 8m13s
build-and-release / build (cron) (push) Successful in 4m39s
build-and-release / build (gateway) (push) Successful in 2m47s
build-and-release / build (employees) (push) Successful in 8m49s
build-and-release / build (fitness-trainers) (push) Successful in 7m42s
build-and-release / build (payments) (push) Successful in 8m6s
build-and-release / build (jobs) (push) Successful in 4m19s
build-and-release / build (catering-services) (push) Successful in 9m29s
build-and-release / build (photographers) (push) Successful in 8m44s
build-and-release / build (customers) (push) Successful in 9m30s
build-and-release / build (tutors) (push) Successful in 7m8s
build-and-release / build (developers) (push) Successful in 9m35s
build-and-release / build (graphic-designers) (push) Successful in 9m33s
build-and-release / build (social-media-managers) (push) Successful in 8m57s
build-and-release / build (leads) (push) Successful in 9m9s
build-and-release / build (ugc-content-creators) (push) Successful in 8m52s
build-and-release / build (video-editors) (push) Successful in 8m14s
build-and-release / build (companies) (push) Successful in 9m58s
build-and-release / build (job-seekers) (push) Successful in 9m55s
build-and-release / build (users) (push) Successful in 10m21s

The admin/employee login handler had no brute-force protection, unlike
the regular user login path. Given these accounts hold internal/
super-admin privileges, add a tighter limit (5 attempts/15min vs 10
for regular users) using the existing sliding-window Redis limiter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Ashwin Kumar Sivakumar 2026-07-14 16:12:52 +05:30
parent e53098728a
commit b9133018a5
2 changed files with 12 additions and 1 deletions

View file

@ -54,7 +54,12 @@ async fn login(
Json(payload): Json<LoginPayload>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let email = payload.email.to_lowercase();
let mut redis = state.redis.clone();
if !cache::rate_limit::check_admin_login(&mut redis, &email).await.unwrap_or(true) {
return Err(err(StatusCode::TOO_MANY_REQUESTS, "Too many login attempts. Try again in 15 minutes.", "RATE_LIMITED"));
}
let employee = EmployeeRepository::get_by_email(&state.pool, &email)
.await
.map_err(|_| err(StatusCode::INTERNAL_SERVER_ERROR, "DB error", "DB_ERROR"))?

View file

@ -41,6 +41,12 @@ pub async fn check_login(redis: &mut RedisPool, email: &str) -> Result<bool, red
check(redis, "login", email, 10, 900).await
}
/// Admin/employee login: max 5 attempts per 15 min per email (tighter than regular
/// user login because these accounts hold internal/super-admin privileges).
pub async fn check_admin_login(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
check(redis, "admin_login", email, 5, 900).await
}
/// Lead request: max 5 per hour per professional
pub async fn check_lead(redis: &mut RedisPool, professional_id: &str) -> Result<bool, redis::RedisError> {
check(redis, "lead", professional_id, 5, 3_600).await