mirror of
https://github.com/thomiceli/opengist.git
synced 2026-08-07 07:14:49 +00:00
8e462397f4
Go CI / Lint (push) Has been cancelled
Go CI / Check (push) Has been cancelled
Go CI / Test (mysql, 1.25, mysql:8, ubuntu-latest, 3306:3306) (push) Has been cancelled
Go CI / Test (postgres, 1.25, postgres:16, ubuntu-latest, 5432:5432) (push) Has been cancelled
Go CI / Test (sqlite, 1.25, macOS-latest) (push) Has been cancelled
Go CI / Test (sqlite, 1.25, ubuntu-latest) (push) Has been cancelled
Go CI / Build (1.25, macOS-latest) (push) Has been cancelled
Go CI / Build (1.25, ubuntu-latest) (push) Has been cancelled
Go CI / Build (1.25, windows-latest) (push) Has been cancelled
Signed-off-by: Thomas Miceli <tho.miceli@gmail.com>
157 lines
3.7 KiB
Go
157 lines
3.7 KiB
Go
package db
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
ScopeGist = iota
|
|
ScopeUser
|
|
)
|
|
|
|
const (
|
|
NoPermission = iota
|
|
ReadPermission
|
|
ReadWritePermission
|
|
)
|
|
|
|
type AccessToken struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
Name string
|
|
TokenHash string `gorm:"uniqueIndex,size:64"` // SHA-256 hash of the token
|
|
CreatedAt int64
|
|
ExpiresAt int64 // 0 means no expiration
|
|
LastUsedAt int64
|
|
UserID uint
|
|
User User `validate:"-"`
|
|
|
|
ScopeGist uint // 0 = none, 1 = read, 2 = read+write
|
|
ScopeUser uint // 0 = none, 1 = read, 2 = read+write
|
|
}
|
|
|
|
// GenerateToken creates a new random token and returns the plain text token.
|
|
// The token hash is stored in the AccessToken struct.
|
|
// The plain text token should be shown to the user once and never stored.
|
|
func (t *AccessToken) GenerateToken() (string, error) {
|
|
bytes := make([]byte, 32)
|
|
if _, err := rand.Read(bytes); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
plainToken := "og_" + hex.EncodeToString(bytes)
|
|
|
|
hash := sha256.Sum256([]byte(plainToken))
|
|
t.TokenHash = hex.EncodeToString(hash[:])
|
|
|
|
return plainToken, nil
|
|
}
|
|
|
|
func GetAccessTokenByID(tokenID uint) (*AccessToken, error) {
|
|
token := new(AccessToken)
|
|
err := db.
|
|
Where("id = ?", tokenID).
|
|
First(&token).Error
|
|
return token, err
|
|
}
|
|
|
|
func GetAccessTokenByToken(plainToken string) (*AccessToken, error) {
|
|
hash := sha256.Sum256([]byte(plainToken))
|
|
tokenHash := hex.EncodeToString(hash[:])
|
|
|
|
token := new(AccessToken)
|
|
err := db.
|
|
Preload("User").
|
|
Where("token_hash = ?", tokenHash).
|
|
First(&token).Error
|
|
return token, err
|
|
}
|
|
|
|
func GetAccessTokensByUserID(userID uint) ([]*AccessToken, error) {
|
|
var tokens []*AccessToken
|
|
err := db.
|
|
Where("user_id = ?", userID).
|
|
Order("created_at desc").
|
|
Find(&tokens).Error
|
|
return tokens, err
|
|
}
|
|
|
|
func (t *AccessToken) Create() error {
|
|
t.CreatedAt = time.Now().Unix()
|
|
return db.Create(t).Error
|
|
}
|
|
|
|
func (t *AccessToken) Delete() error {
|
|
return db.Delete(t).Error
|
|
}
|
|
|
|
func (t *AccessToken) UpdateLastUsed() error {
|
|
return db.Model(t).Update("last_used_at", time.Now().Unix()).Error
|
|
}
|
|
|
|
func (t *AccessToken) IsExpired() bool {
|
|
if t.ExpiresAt == 0 {
|
|
return false
|
|
}
|
|
return time.Now().Unix() > t.ExpiresAt
|
|
}
|
|
|
|
func (t *AccessToken) HasGistReadPermission() bool {
|
|
return t.ScopeGist >= ReadPermission
|
|
}
|
|
|
|
func (t *AccessToken) HasGistWritePermission() bool {
|
|
return t.ScopeGist >= ReadWritePermission
|
|
}
|
|
|
|
func (t *AccessToken) HasUserReadPermission() bool {
|
|
return t.ScopeUser >= ReadPermission
|
|
}
|
|
|
|
func (t *AccessToken) HasUserWritePermission() bool {
|
|
return t.ScopeUser >= ReadWritePermission
|
|
}
|
|
|
|
func (t *AccessToken) CheckForPermission(scope, permission uint) bool {
|
|
if scope == ScopeGist {
|
|
return t.ScopeGist >= permission
|
|
}
|
|
if scope == ScopeUser {
|
|
return t.ScopeUser >= permission
|
|
}
|
|
return false
|
|
}
|
|
|
|
// -- DTO -- //
|
|
|
|
type AccessTokenDTO struct {
|
|
Name string `form:"name" validate:"required,max=255"`
|
|
ScopeGist uint `form:"scope_gist" validate:"min=0,max=2"`
|
|
ScopeUser uint `form:"scope_user" validate:"min=0,max=2"`
|
|
ExpiresAt string `form:"expires_at"` // empty means no expiration, otherwise date format (YYYY-MM-DD)
|
|
}
|
|
|
|
func (dto *AccessTokenDTO) ToAccessToken() *AccessToken {
|
|
var expiresAt int64
|
|
if dto.ExpiresAt != "" {
|
|
// date input format: 2006-01-02, expires at end of day
|
|
if t, err := time.ParseInLocation("2006-01-02", dto.ExpiresAt, time.Local); err == nil {
|
|
expiresAt = t.Add(24*time.Hour - time.Second).Unix()
|
|
}
|
|
}
|
|
|
|
return &AccessToken{
|
|
Name: dto.Name,
|
|
ScopeGist: dto.ScopeGist,
|
|
ScopeUser: dto.ScopeUser,
|
|
ExpiresAt: expiresAt,
|
|
}
|
|
}
|
|
|
|
// SaveAccessTokenForTest is exported for tests only; saves the entire AccessToken row.
|
|
func SaveAccessTokenForTest(t *AccessToken) error {
|
|
return db.Save(t).Error
|
|
}
|