93 lines
2.1 KiB
Go
93 lines
2.1 KiB
Go
// Package store provides a session store for caching data
|
|
package store
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// RedirectTrack represents a single redirect attempt tracking entry
|
|
type RedirectTrack struct {
|
|
IP string // Client IP (X-Forwarded-For aware)
|
|
UserAgent string // Full User-Agent string for debugging
|
|
Path string // Request path (without query params)
|
|
Attempts int // Number of redirect attempts
|
|
FirstSeen time.Time // When first redirect was tracked
|
|
ExpiresAt time.Time // When to clean up this entry
|
|
}
|
|
|
|
type Store struct {
|
|
sessions sync.Map // key: string, value: *RegistrationSession
|
|
redirectTracks sync.Map // key: string, value: *RedirectTrack
|
|
cleanup *time.Ticker
|
|
tokenchecks sync.Map // key: int, value: *TokenCheck
|
|
}
|
|
|
|
func NewStore() *Store {
|
|
s := &Store{
|
|
cleanup: time.NewTicker(1 * time.Minute),
|
|
}
|
|
|
|
// Background cleanup of expired sessions
|
|
go func() {
|
|
for range s.cleanup.C {
|
|
s.cleanupExpired()
|
|
}
|
|
}()
|
|
|
|
return s
|
|
}
|
|
|
|
func (s *Store) Delete(id string) {
|
|
s.sessions.Delete(id)
|
|
}
|
|
|
|
func (s *Store) cleanupExpired() {
|
|
now := time.Now()
|
|
|
|
// Clean up expired registration sessions
|
|
s.sessions.Range(func(key, value any) bool {
|
|
session := value.(*RegistrationSession)
|
|
if now.After(session.ExpiresAt) {
|
|
s.sessions.Delete(key)
|
|
}
|
|
return true
|
|
})
|
|
|
|
// Clean up expired redirect tracks
|
|
s.redirectTracks.Range(func(key, value any) bool {
|
|
track := value.(*RedirectTrack)
|
|
if now.After(track.ExpiresAt) {
|
|
s.redirectTracks.Delete(key)
|
|
}
|
|
return true
|
|
})
|
|
|
|
s.tokenchecks.Range(func(key, value any) bool {
|
|
check := value.(*TokenCheck)
|
|
if now.After(check.ExpiresAt) {
|
|
s.tokenchecks.Delete(key)
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func generateID() string {
|
|
b := make([]byte, 32)
|
|
_, _ = rand.Read(b)
|
|
return base64.RawURLEncoding.EncodeToString(b)
|
|
}
|
|
|
|
// redirectKey generates a unique key for tracking redirects
|
|
// Uses IP + first 100 chars of UA + path as key (not hashed for debugging)
|
|
func redirectKey(ip, userAgent, path string) string {
|
|
ua := userAgent
|
|
if len(ua) > 100 {
|
|
ua = ua[:100]
|
|
}
|
|
return fmt.Sprintf("%s:%s:%s", ip, ua, path)
|
|
}
|