Files
plainleaf/server/lockout.go
T
Zef HemelandGitHub a524c64884 Go backend (#1555)
* Replaces old Deno server with a Go implementation
* Restructured the code base accordingly
2025-09-19 13:23:20 +02:00

70 lines
1.6 KiB
Go

package server
import (
"math"
"sync"
"time"
)
// LockoutTimer implements a simple rate limiter to prevent brute force attacks
type LockoutTimer struct {
mutex sync.Mutex
bucketTime int64
bucketCount int
bucketSize int64 // duration in milliseconds
limit int
disabled bool
}
// NewLockoutTimer creates a new lockout timer
// countPeriodMs: time window in milliseconds
// limit: maximum attempts allowed in the time window
func NewLockoutTimer(countPeriodMs int, limit int) *LockoutTimer {
disabled := math.IsNaN(float64(countPeriodMs)) || math.IsNaN(float64(limit)) ||
countPeriodMs < 1 || limit < 1
return &LockoutTimer{
bucketSize: int64(countPeriodMs),
limit: limit,
disabled: disabled,
}
}
// updateBucketTime updates the current bucket time and resets count if needed
func (lt *LockoutTimer) updateBucketTime() {
currentBucketTime := time.Now().UnixMilli() / lt.bucketSize
if lt.bucketTime == currentBucketTime {
return
}
// the bucket is too old - empty it
lt.bucketTime = currentBucketTime
lt.bucketCount = 0
}
// IsLocked checks if the timer is currently locked due to too many attempts
func (lt *LockoutTimer) IsLocked() bool {
if lt.disabled {
return false
}
lt.mutex.Lock()
defer lt.mutex.Unlock()
lt.updateBucketTime()
return lt.bucketCount >= lt.limit
}
// AddCount increments the attempt counter
// IsLocked() should be called first to keep bucketTime current
func (lt *LockoutTimer) AddCount() {
if lt.disabled {
return
}
lt.mutex.Lock()
defer lt.mutex.Unlock()
// updateBucketTime should have been called by IsLocked first
lt.bucketCount++
}