nxtgauge-backend-rust/crates/cache/src/rate_limit.rs
Ashwin Kumar Sivakumar b9133018a5
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
fix(admin-auth): add rate limiting to admin login endpoint
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>
2026-07-14 16:12:52 +05:30

58 lines
2.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Generic sliding-window rate limiter.
//!
//! Key pattern: `rate:{namespace}:{identifier}`
//! Returns `Ok(true)` if the request is allowed, `Ok(false)` if rate-limited.
use redis::AsyncCommands;
use crate::RedisPool;
/// Check + increment a rate-limit counter.
///
/// * `namespace` e.g. `"login"`, `"register"`, `"lead"`
/// * `identifier` e.g. email, IP, user_id
/// * `max` maximum requests allowed in `window_secs`
/// * `window_secs` sliding window length in seconds
///
/// Returns `Ok(true)` = allowed, `Ok(false)` = blocked.
pub async fn check(
redis: &mut RedisPool,
namespace: &str,
identifier: &str,
max: i64,
window_secs: i64,
) -> Result<bool, redis::RedisError> {
let key = format!("rate:{namespace}:{identifier}");
let count: i64 = redis.incr(&key, 1i64).await?;
if count == 1 {
redis.expire::<_, ()>(&key, window_secs).await?;
}
Ok(count <= max)
}
/// Convenience wrappers ───────────────────────────────────────────────────────
/// Register: max 10 per hour per email
pub async fn check_register(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
check(redis, "register", email, 10, 3_600).await
}
/// Login: max 10 attempts per 15 min per email
pub async fn check_login(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
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
}
/// Job post: max 20 per hour per company
pub async fn check_job_post(redis: &mut RedisPool, company_id: &str) -> Result<bool, redis::RedisError> {
check(redis, "job_post", company_id, 20, 3_600).await
}