ch15.2
This commit is contained in:
91
internal/data/tokens.go
Normal file
91
internal/data/tokens.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/base32"
|
||||
"time"
|
||||
|
||||
"gitea.local.lab/Lbenedar/greenlight/internal/validator"
|
||||
)
|
||||
|
||||
const (
|
||||
ScopeActivation = "activation"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Plaintext string
|
||||
Hash []byte
|
||||
UserID int64
|
||||
Expiry time.Time
|
||||
Scope string
|
||||
}
|
||||
|
||||
func generateToken(userID int64, ttl time.Duration, scope string) (*Token, error) {
|
||||
token := &Token{
|
||||
UserID: userID,
|
||||
Expiry: time.Now().Add(ttl),
|
||||
Scope: scope,
|
||||
}
|
||||
|
||||
randomBytes := make([]byte, 16)
|
||||
|
||||
_, err := rand.Read(randomBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
token.Plaintext = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randomBytes)
|
||||
|
||||
hash := sha256.Sum256([]byte(token.Plaintext))
|
||||
token.Hash = hash[:]
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func ValidateTokenPlaintext(v *validator.Validator, tokenPlaintext string) {
|
||||
v.Check(tokenPlaintext != "", "token", "must be provided")
|
||||
v.Check(len(tokenPlaintext) == 26, "token", "must be 26 bytes long")
|
||||
}
|
||||
|
||||
type TokenModel struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
func (m TokenModel) New(userID int64, ttl time.Duration, scope string) (*Token, error) {
|
||||
token, err := generateToken(userID, ttl, scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = m.Insert(token)
|
||||
return token, err
|
||||
}
|
||||
|
||||
func (m TokenModel) Insert(token *Token) error {
|
||||
query := `
|
||||
INSERT INTO tokens (hash, user_id, expirt, scope)
|
||||
VALUES ($1, $2, $3, $4)`
|
||||
|
||||
args := []any{token.Hash, token.UserID, token.Expiry, token.Scope}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := m.DB.ExecContext(ctx, query, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m TokenModel) DeleteForUser(scope string, userID int64) error {
|
||||
query := `
|
||||
DELETE FROM tokens
|
||||
WHERE scope = $1 AND user_id = $2`
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
_, err := m.DB.ExecContext(ctx, query, scope, userID)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user