2022-05-02 09:35:49 +02:00

112 lines
2.6 KiB
Go

package photosapi
import (
"errors"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"gorm.io/gorm"
)
// Errors
var (
ErrSessionNotFound = errors.New("session not found")
ErrSessionInvalid = errors.New("session invalid")
ErrTokenMissing = errors.New("token missing")
)
// Model
type Session struct {
ID uint32 `gorm:"primary_key"`
Token string `gorm:"size:36"`
Account *Account `gorm:"constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
AccountId uint32 `gorm:"not null"`
CreatedAt time.Time
UpdatedAt time.Time
}
func (s *Session) BeforeCreate(tx *gorm.DB) error {
uuid, err := uuid.NewRandom()
if err != nil {
return err
}
s.Token = uuid.String()
return nil
}
func NewSession(tx *gorm.DB, login string, password string) (*Session, error) {
account := NewAccount(login, password)
if err := tx.Where(
"login = ? and encrypted_password = ?",
account.Login,
account.EncryptedPassword,
).First(account).Error; err != nil {
return nil, err
}
session := &Session{Account: account}
if err := tx.Create(session).Error; err != nil {
return nil, err
}
return session, nil
}
// Service
func (s *Service) RequireAuthToken(c *gin.Context) {
tokenAuth := c.GetHeader("Authorization")
tokenCookie, _ := c.Cookie("photoapitoken")
if tokenAuth != "" {
if !strings.HasPrefix(tokenAuth, "Private ") {
s.Error(c, http.StatusForbidden, ErrTokenMissing)
} else {
c.Set("token", tokenAuth[8:])
}
} else if tokenCookie != "" {
c.Set("token", tokenCookie)
} else {
s.Error(c, http.StatusForbidden, ErrTokenMissing)
}
}
func (s *Service) RequireSession(c *gin.Context) {
s.RequireAuthToken(c)
if c.IsAborted() {
return
}
sess := &Session{}
if err := s.DB.Preload("Account").Where("token = ?", c.GetString("token")).First(sess).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
s.Error(c, http.StatusForbidden, ErrSessionNotFound)
} else {
s.Error(c, http.StatusForbidden, err)
}
return
}
if sess.Account == nil {
s.Error(c, http.StatusInternalServerError, ErrSessionInvalid)
return
}
s.DB.Select("updated_at").Save(sess)
s.LogOk.Printf("Session", "User: %s", sess.Account.Login)
c.Set("session", sess)
}
func (s *Service) CurrentSession(c *gin.Context) *Session {
return c.MustGet("session").(*Session)
}
func (s *Service) SessionCleaner() {
for range time.Tick(time.Minute) {
t := time.Now().UTC().Add(-3 * time.Hour).Truncate(time.Minute)
s.LogOk.Printf("Session", "Cleaning old session < %s", t)
if err := s.DB.Where("updated_at < ?", t).Delete(&Session{}).Error; err != nil {
s.LogErr.Printf("Session", "Cleaning failed: %s", err)
}
}
}