f12d4f0b12
The original auth commit shipped the in-memory session store with just Issue and Valid. The Slice-2 /api/logout handler and the audit-trail (user column on each deployment) need: - User(tok): look up the username for a valid session. - Revoke(tok): drop a session; used by /api/logout. Tiny follow-up — kept as its own commit because the rest of the auth work had already shipped in the parent commit by the time the dashboard's logout button and the deployment-audit-trail surfaced the need for these methods.
68 lines
1.3 KiB
Go
68 lines
1.3 KiB
Go
package api
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Sessions is an in-memory token store. Replace with a signed JWT or
|
|
// a Redis-backed store when we need multi-replica CP. For MVP one process
|
|
// is enough.
|
|
type Sessions struct {
|
|
mu sync.RWMutex
|
|
store map[string]session
|
|
}
|
|
|
|
type session struct {
|
|
user string
|
|
expires time.Time
|
|
}
|
|
|
|
func NewSessions() *Sessions {
|
|
return &Sessions{store: make(map[string]session)}
|
|
}
|
|
|
|
func (s *Sessions) Issue(user string) string {
|
|
b := make([]byte, 16)
|
|
_, _ = rand.Read(b)
|
|
tok := hex.EncodeToString(b)
|
|
s.mu.Lock()
|
|
s.store[tok] = session{user: user, expires: time.Now().Add(12 * time.Hour)}
|
|
s.mu.Unlock()
|
|
return tok
|
|
}
|
|
|
|
func (s *Sessions) Valid(tok string) bool {
|
|
s.mu.RLock()
|
|
sess, ok := s.store[tok]
|
|
s.mu.RUnlock()
|
|
if !ok {
|
|
return false
|
|
}
|
|
return time.Now().Before(sess.expires)
|
|
}
|
|
|
|
// User returns the username for a valid token, or "" if the token is
|
|
// unknown or expired.
|
|
func (s *Sessions) User(tok string) (string, bool) {
|
|
s.mu.RLock()
|
|
sess, ok := s.store[tok]
|
|
s.mu.RUnlock()
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
if !time.Now().Before(sess.expires) {
|
|
return "", false
|
|
}
|
|
return sess.user, true
|
|
}
|
|
|
|
// Revoke drops a session. Used by /api/logout.
|
|
func (s *Sessions) Revoke(tok string) {
|
|
s.mu.Lock()
|
|
delete(s.store, tok)
|
|
s.mu.Unlock()
|
|
}
|