79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
package db
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/hhhapz/codequest/models"
|
|
"github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
func (db *DB) CreateToken(ctx context.Context, user *models.User) (*models.Token, error) {
|
|
tokenString := createToken(48)
|
|
now := time.Now()
|
|
|
|
token := &models.Token{
|
|
Token: tokenString,
|
|
UserID: user.ID,
|
|
CreatedAt: models.NewTime(now),
|
|
}
|
|
|
|
for {
|
|
err := token.Insert(ctx, db.DB)
|
|
if err == nil {
|
|
return token, nil
|
|
}
|
|
|
|
e := new(sqlite3.Error)
|
|
if errors.As(err, &e) && e.ExtendedCode == sqlite3.ErrConstraintUnique {
|
|
token.Token = createToken(48)
|
|
continue
|
|
}
|
|
return nil, fmt.Errorf("could not create token: %v", err)
|
|
}
|
|
}
|
|
|
|
var ErrInvalidToken = errors.New("no results found")
|
|
|
|
func (db *DB) UserByToken(ctx context.Context, token string) (*models.User, error) {
|
|
user, err := models.UserByToken(ctx, db.DB, token)
|
|
if err == sql.ErrNoRows {
|
|
return nil, models.NewUserError("invalid token: session expired")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (db *DB) RevokeToken(ctx context.Context, token string) error {
|
|
_, err := models.DeleteToken(ctx, db.DB, token)
|
|
return err
|
|
}
|
|
|
|
func (db *DB) RevokeUserTokens(ctx context.Context, user *models.User) (int64, error) {
|
|
res, err := models.DeleteTokens(ctx, db.DB, user.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return res.RowsAffected()
|
|
}
|
|
|
|
func createToken(size int) string {
|
|
token := make([]byte, size*3/4)
|
|
_, err := rand.Read(token)
|
|
if err != nil {
|
|
// Not being able to generate random bytes means something far
|
|
// worse is wrong.
|
|
panic(err)
|
|
}
|
|
|
|
return base64.URLEncoding.EncodeToString(token)
|
|
}
|