feat: rate limiting connexion (5 essais / 15 min)
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
checkRateLimit,
|
||||||
|
recordFailedAttempt,
|
||||||
|
clearAttempts,
|
||||||
|
_resetForTests,
|
||||||
|
} from '@/lib/rate-limit'
|
||||||
|
|
||||||
|
beforeEach(() => _resetForTests())
|
||||||
|
|
||||||
|
describe('checkRateLimit', () => {
|
||||||
|
it('autorise une nouvelle clé', () => {
|
||||||
|
expect(checkRateLimit('ip:user@test.com')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('autorise après moins de 5 tentatives', () => {
|
||||||
|
const key = 'ip:user@test.com'
|
||||||
|
recordFailedAttempt(key)
|
||||||
|
recordFailedAttempt(key)
|
||||||
|
recordFailedAttempt(key)
|
||||||
|
recordFailedAttempt(key)
|
||||||
|
expect(checkRateLimit(key)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bloque après 5 tentatives', () => {
|
||||||
|
const key = 'ip:user@test.com'
|
||||||
|
for (let i = 0; i < 5; i++) recordFailedAttempt(key)
|
||||||
|
expect(checkRateLimit(key)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('débloque après clearAttempts', () => {
|
||||||
|
const key = 'ip:user@test.com'
|
||||||
|
for (let i = 0; i < 5; i++) recordFailedAttempt(key)
|
||||||
|
clearAttempts(key)
|
||||||
|
expect(checkRateLimit(key)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const MAX_ATTEMPTS = 5
|
||||||
|
const BLOCK_DURATION_MS = 15 * 60 * 1000 // 15 minutes
|
||||||
|
|
||||||
|
type Entry = { count: number; blockedAt: number | null }
|
||||||
|
let attempts = new Map<string, Entry>()
|
||||||
|
|
||||||
|
export function checkRateLimit(key: string): boolean {
|
||||||
|
const now = Date.now()
|
||||||
|
const entry = attempts.get(key)
|
||||||
|
if (!entry) return true
|
||||||
|
if (entry.blockedAt !== null) {
|
||||||
|
if (now - entry.blockedAt < BLOCK_DURATION_MS) return false
|
||||||
|
attempts.delete(key)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordFailedAttempt(key: string): void {
|
||||||
|
const entry = attempts.get(key) ?? { count: 0, blockedAt: null }
|
||||||
|
entry.count += 1
|
||||||
|
if (entry.count >= MAX_ATTEMPTS) entry.blockedAt = Date.now()
|
||||||
|
attempts.set(key, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearAttempts(key: string): void {
|
||||||
|
attempts.delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Réinitialisation pour les tests uniquement */
|
||||||
|
export function _resetForTests(): void {
|
||||||
|
attempts = new Map()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user