152 lines · 3.1 KB · Go
RawDownload
package auth import (	"sync"	"time") // sessionEntry stores auth binding with expiration.type sessionEntry struct {	authID    string	expiresAt time.Time} // SessionCache provides TTL-based session to auth mapping with automatic cleanup.type SessionCache struct {	mu      sync.RWMutex	entries map[string]sessionEntry	ttl     time.Duration	stopCh  chan struct{}} // NewSessionCache creates a cache with the specified TTL.// A background goroutine periodically cleans expired entries.func NewSessionCache(ttl time.Duration) *SessionCache {	if ttl <= 0 {		ttl = 30 * time.Minute	}	c := &SessionCache{		entries: make(map[string]sessionEntry),		ttl:     ttl,		stopCh:  make(chan struct{}),	}	go c.cleanupLoop()	return c} // Get retrieves the auth ID bound to a session, if still valid.// Does NOT refresh the TTL on access.func (c *SessionCache) Get(sessionID string) (string, bool) {	if sessionID == "" {		return "", false	}	c.mu.RLock()	entry, ok := c.entries[sessionID]	c.mu.RUnlock()	if !ok {		return "", false	}	if time.Now().After(entry.expiresAt) {		c.mu.Lock()		delete(c.entries, sessionID)		c.mu.Unlock()		return "", false	}	return entry.authID, true} // GetAndRefresh retrieves the auth ID bound to a session and refreshes TTL on hit.// This extends the binding lifetime for active sessions.func (c *SessionCache) GetAndRefresh(sessionID string) (string, bool) {	if sessionID == "" {		return "", false	}	now := time.Now()	c.mu.Lock()	entry, ok := c.entries[sessionID]	if !ok {		c.mu.Unlock()		return "", false	}	if now.After(entry.expiresAt) {		delete(c.entries, sessionID)		c.mu.Unlock()		return "", false	}	// Refresh TTL on successful access	entry.expiresAt = now.Add(c.ttl)	c.entries[sessionID] = entry	c.mu.Unlock()	return entry.authID, true} // Set binds a session to an auth ID with TTL refresh.func (c *SessionCache) Set(sessionID, authID string) {	if sessionID == "" || authID == "" {		return	}	c.mu.Lock()	c.entries[sessionID] = sessionEntry{		authID:    authID,		expiresAt: time.Now().Add(c.ttl),	}	c.mu.Unlock()} // Invalidate removes a specific session binding.func (c *SessionCache) Invalidate(sessionID string) {	if sessionID == "" {		return	}	c.mu.Lock()	delete(c.entries, sessionID)	c.mu.Unlock()} // InvalidateAuth removes all sessions bound to a specific auth ID.// Used when an auth becomes unavailable.func (c *SessionCache) InvalidateAuth(authID string) {	if authID == "" {		return	}	c.mu.Lock()	for sid, entry := range c.entries {		if entry.authID == authID {			delete(c.entries, sid)		}	}	c.mu.Unlock()} // Stop terminates the background cleanup goroutine.func (c *SessionCache) Stop() {	select {	case <-c.stopCh:	default:		close(c.stopCh)	}} func (c *SessionCache) cleanupLoop() {	ticker := time.NewTicker(c.ttl / 2)	defer ticker.Stop()	for {		select {		case <-c.stopCh:			return		case <-ticker.C:			c.cleanup()		}	}} func (c *SessionCache) cleanup() {	now := time.Now()	c.mu.Lock()	for sid, entry := range c.entries {		if now.After(entry.expiresAt) {			delete(c.entries, sid)		}	}	c.mu.Unlock()}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t