Git-Repository für bestehenden archivdms-Code initialisiert, Branch-/Commit-Konvention (feature/<ticket>-<slug>-Branches, Ticket-Prefix in Commit-Nachricht) etabliert.
71 lines
1.5 KiB
Go
71 lines
1.5 KiB
Go
package auth
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// keyedRateLimiter is a minimal in-memory token-bucket limiter keyed by an
|
|
// arbitrary string (used for per-(tenant,loginName) and per-IP LDAP login
|
|
// throttling). No external dependency, no Redis — buckets are created lazily
|
|
// and idle ones are swept periodically to bound memory.
|
|
type keyedRateLimiter struct {
|
|
mu sync.Mutex
|
|
buckets map[string]*bucket
|
|
burst float64
|
|
refillPerSec float64
|
|
lastSweep time.Time
|
|
}
|
|
|
|
type bucket struct {
|
|
tokens float64
|
|
last time.Time
|
|
}
|
|
|
|
func newKeyedRateLimiter(burst, refillPerSec float64) *keyedRateLimiter {
|
|
return &keyedRateLimiter{
|
|
buckets: make(map[string]*bucket),
|
|
burst: burst,
|
|
refillPerSec: refillPerSec,
|
|
lastSweep: time.Now(),
|
|
}
|
|
}
|
|
|
|
// allow consumes one token for key, returning false when the bucket is empty.
|
|
func (l *keyedRateLimiter) allow(key string) bool {
|
|
now := time.Now()
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
l.sweepLocked(now)
|
|
|
|
b, ok := l.buckets[key]
|
|
if !ok {
|
|
b = &bucket{tokens: l.burst, last: now}
|
|
l.buckets[key] = b
|
|
}
|
|
b.tokens += now.Sub(b.last).Seconds() * l.refillPerSec
|
|
if b.tokens > l.burst {
|
|
b.tokens = l.burst
|
|
}
|
|
b.last = now
|
|
|
|
if b.tokens < 1 {
|
|
return false
|
|
}
|
|
b.tokens--
|
|
return true
|
|
}
|
|
|
|
func (l *keyedRateLimiter) sweepLocked(now time.Time) {
|
|
if now.Sub(l.lastSweep) < time.Minute {
|
|
return
|
|
}
|
|
l.lastSweep = now
|
|
for k, b := range l.buckets {
|
|
if now.Sub(b.last) > 30*time.Minute {
|
|
delete(l.buckets, k)
|
|
}
|
|
}
|
|
}
|