wisski-cloud-distillery/internal/dis/component/auth/tokens/tokens.go
2023-11-02 12:05:13 +01:00

157 lines
3.4 KiB
Go

package tokens
import (
"context"
"crypto/rand"
"strings"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
"github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/tkw1536/pkglib/password"
"github.com/tkw1536/pkglib/reflectx"
"gorm.io/gorm"
)
// Tokens implements Tokens
type Tokens struct {
component.Base
dependencies struct {
SQL *sql.SQL
}
}
var (
_ component.UserDeleteHook = (*Tokens)(nil)
_ component.Table = (*Tokens)(nil)
)
func (tok *Tokens) TableInfo() component.TableInfo {
return component.TableInfo{
Name: models.TokensTable,
Model: reflectx.TypeFor[models.Token](),
}
}
func (tok *Tokens) table(ctx context.Context) (*gorm.DB, error) {
return tok.dependencies.SQL.QueryTable(ctx, tok)
}
func (tok *Tokens) OnUserDelete(ctx context.Context, user *models.User) error {
table, err := tok.table(ctx)
if err != nil {
return err
}
return table.Delete(&models.Token{}, &models.Token{User: user.User}).Error
}
// Tokens returns a list of tokens for the given user
func (tok *Tokens) Tokens(ctx context.Context, user string) ([]models.Token, error) {
// the empty user has no tokens
if user == "" {
return nil, nil
}
// get the table
table, err := tok.table(ctx)
if err != nil {
return nil, err
}
var tokens []models.Token
// make a query to find all keys (in the underlying model)
query := table.Find(&tokens, &models.Token{User: user})
if query.Error != nil {
return nil, query.Error
}
return tokens, nil
}
const (
tokenGroupLength = 8
tokenGroupCount = 8
tokenSeparator = "-"
tokenCharset password.Charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
)
// NewToken generates a new token
func NewToken() (string, error) {
// generate a new password
token, err := password.Generate(rand.Reader, tokenGroupCount*tokenGroupLength, tokenCharset)
if err != nil {
return "", err
}
// insert the token group separators
var result strings.Builder
result.Grow(len(token) + (tokenGroupCount-1)*len(tokenSeparator))
for i := 0; i < tokenGroupCount; i++ {
if i != 0 {
result.WriteString(tokenSeparator)
}
start := i * tokenGroupLength
result.WriteString(token[start : start+tokenGroupLength])
}
return result.String(), nil
}
// Add adds a new token, unless it already exists.
// The token is granted scopes with .SetScopes(scopes).
func (tok *Tokens) Add(ctx context.Context, user string, description string, scopes []string) (*models.Token, error) {
// create a new token and set the scopes
mk := models.Token{
User: user,
Description: description,
}
mk.SetScopes(scopes)
// generate a new id for the token
{
var err error
mk.TokenID, err = NewToken()
if err != nil {
return nil, err
}
}
// generate the actual token
var err error
mk.Token, err = NewToken()
if err != nil {
return nil, err
}
// get the table
table, err := tok.table(ctx)
if err != nil {
return nil, err
}
// create the token instance
if err := table.Create(&mk).Error; err != nil {
return nil, err
}
// and return
return &mk, nil
}
// Remove removes a token with the given token from the user
func (tok *Tokens) Remove(ctx context.Context, user, id string) error {
// get the table
table, err := tok.table(ctx)
if err != nil {
return err
}
// and do the delete
return table.Where("user = ? AND id = ?", user, id).Delete(&models.Token{}).Error
}