package auth import ( "fmt" "time" "sync" "crypto/rand" "encoding/base64" ) type Session struct { Created time.Time Modified time.Time } type Sessions struct { s map[string]Session lock sync.Mutex } func NewSessionContainer() *Sessions { return &Sessions{ s: make(map[string]Session), } } func createSessionId() (string, error) { bytes := make([]byte, 32) _, err := rand.Read(bytes) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(bytes), nil } func (s *Sessions) NewSession() (string, error) { s.lock.Lock() defer s.lock.Unlock() id, err := createSessionId() if err != nil { return "", err } session := Session{ Created: time.Now(), Modified: time.Now(), } s.s[id] = session return id, nil } func (s *Sessions) IsSessionValid(id string) bool { s.lock.Lock() defer s.lock.Unlock() _, ok := s.s[id] return ok } func (s *Sessions) GetSession(id string) (Session, error) { s.lock.Lock() defer s.lock.Unlock() session, ok := s.s[id] if !ok { return Session{}, fmt.Errorf("invalid session id: %v", id) } return session, nil } func (s *Sessions) TouchSession(id string) { s.lock.Lock() defer s.lock.Unlock() session := s.s[id] session.Modified = time.Now() s.s[id] = session } func (s *Sessions) DeleteSession(id string) { s.lock.Lock() defer s.lock.Unlock() delete(s.s, id) } func (s *Sessions) CleanSessions(maxIdle time.Duration) { s.lock.Lock() defer s.lock.Unlock() expire := time.Now().Add(-maxIdle) for id, session := range s.s { if session.Modified.Before(expire) { // last modified before the expiration time // so this session is expired delete(s.s, id) } } }