puerta/internal/auth/session.go

82 lines
1.7 KiB
Go

// SPDX-License-Identifier: Apache-2.0
// Copyright © 2022 Roberto Hidalgo <nidito@un.rob.mx>
package auth
import (
"math/rand"
"strings"
"time"
"git.rob.mx/nidito/puerta/internal/user"
"github.com/upper/db/v4"
)
var letterSrc = rand.NewSource(time.Now().UnixNano())
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
// 6 bits to represent a letter index
letterIdxBits = 6
// All 1-bits, as many as letterIdxBits
letterIdxMask = 1<<letterIdxBits - 1
// # of letter indices fitting in 63 bits
letterIdxMax = 63 / letterIdxBits
)
func NewToken() string {
sb := strings.Builder{}
n := 32
sb.Grow(n)
for i, cache, remain := n-1, letterSrc.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = letterSrc.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
sb.WriteByte(letterBytes[idx])
i--
}
cache >>= letterIdxBits
remain--
}
return sb.String()
}
type Session struct {
Token string `db:"token"`
UserID int `db:"user"`
Expires time.Time `db:"expires"`
}
func (s *Session) Store(sess db.Session) db.Store {
return sess.Collection("session")
}
type SessionUser struct {
Token string `db:"token"`
UserID int `db:"user"`
Expires time.Time `db:"expires"`
user.User `db:",inline"`
}
func (s *SessionUser) Expired() bool {
return s.Expires.After(time.Now())
}
func NewSession(user *user.User, table db.Collection) (*Session, error) {
sess := &Session{
Token: NewToken(),
UserID: user.ID,
Expires: user.TTL.FromNow(),
}
if !user.IsAdmin {
// delete previous sessions
table.Find(db.Cond{"user": user.ID}).Delete()
}
// insert new one
_, err := table.Insert(sess)
return sess, err
}