// Copyright (C) 2022 Marius Schellenberger package server import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/hex" "io" weakrand "math/rand" "sync" "time" "git.giftfish.de/ston1th/goacc/pkg/db" ) type AuthValidator interface { Login(username, password string) (auth bool) } const cacheSize = 100 func init() { weakrand.Seed(time.Now().UnixNano()) } type DBLoginValidator struct { db *db.DB } func NewDBLoginValidator(db *db.DB) *DBLoginValidator { return &DBLoginValidator{db} } func (val *DBLoginValidator) Login(username, password string) bool { _, err := val.db.Login(username, password) return err == nil } type cache struct { mu sync.RWMutex m map[string]bool } func newCache() *cache { return &cache{m: make(map[string]bool)} } func (c *cache) get(k string) (v, ok bool) { c.mu.RLock() defer c.mu.RUnlock() v, ok = c.m[k] return } func (c *cache) set(k string, v bool) { c.mu.Lock() defer c.mu.Unlock() c.makeRoom() c.m[k] = v } func (c *cache) makeRoom() { n := len(c.m) if n < cacheSize { return } del := n / 10 if del < 1 { del = 1 } for i := 0; i <= del; i++ { rnd := weakrand.Intn(len(c.m)) j := 0 for k := range c.m { if j == rnd { delete(c.m, k) break } j++ } } } type BasicAuth struct { mu sync.Mutex auth AuthValidator cache *cache key []byte } func NewBasicAuth(auth AuthValidator) (*BasicAuth, error) { key := make([]byte, 32) _, err := io.ReadFull(rand.Reader, key) return &BasicAuth{ auth: auth, cache: newCache(), key: key, }, err } func (a *BasicAuth) genkey(username, password string) string { h := hmac.New(sha256.New, a.key) h.Write(append([]byte(username), []byte(password)...)) return hex.EncodeToString(h.Sum(nil)) } func (a *BasicAuth) Auth(username, password string) (auth bool) { k := a.genkey(username, password) auth, ok := a.cache.get(k) if !ok { a.mu.Lock() auth = a.auth.Login(username, password) a.mu.Unlock() a.cache.set(k, auth) } return }