package authentication import ( "crypto/rand" "fmt" "time" "somehole.com/common/security/identity" ) const ( RetryCreateSessionToken = 3 MaxSessionAge = 1 * time.Hour ) type SessionToken [8]byte func NewSessionToken() (st SessionToken) { var stBytes = make([]byte, 8) rand.Read(stBytes) st = SessionToken(stBytes) return } type NextToken [4]byte func NewNextToken() (nt NextToken) { var ntBytes = make([]byte, 4) rand.Read(ntBytes) nt = NextToken(ntBytes) return } type Session struct { identity.Identity nextTokens []NextToken expiration time.Time } func NewSession(identity identity.Identity) (s *Session) { s = &Session{ Identity: identity, nextTokens: make([]NextToken, 0), expiration: time.Now().Add(MaxSessionAge), } return } func (s *Session) Active() (ok bool) { ok = time.Now().Before(s.expiration) return } func (s *Session) NewNextToken() (nt NextToken) { nt = NewNextToken() s.nextTokens = append(s.nextTokens, nt) return } func (s *Session) NextTokenIsCurrent(nt NextToken) (ok bool) { ok = s.nextTokens[len(s.nextTokens)-1] == nt return } type SessionService struct { sessions map[SessionToken]*Session } func NewSessionService() (srv *SessionService) { srv = &SessionService{ sessions: make(map[SessionToken]*Session), } return } func (srv *SessionService) NewSessionToken() (st SessionToken, err error) { for i := 0; i < RetryCreateSessionToken; i++ { st = NewSessionToken() if _, exists := srv.sessions[st]; exists { err = fmt.Errorf("could only creat colliding session tokens in %d tries", RetryCreateSessionToken) continue } err = nil break } return } func (srv *SessionService) AddSession(st SessionToken, session *Session) (err error) { if _, exists := srv.sessions[st]; exists { err = fmt.Errorf("session already exists") return } srv.sessions[st] = session return } func (srv *SessionService) GetSession(sessionToken SessionToken) (session *Session, err error) { var exists bool session, exists = srv.sessions[sessionToken] if !exists { err = fmt.Errorf("session not found") return } return }