add token service, sqlite driver and test
This commit is contained in:
30
internal/token/service.go
Normal file
30
internal/token/service.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TokenStore interface {
|
||||
revoke(tokenID string, expiresAt time.Time) error
|
||||
isRevoked(tokenID string) (bool, error)
|
||||
}
|
||||
|
||||
type TokenService struct {
|
||||
store TokenStore
|
||||
}
|
||||
|
||||
func NewTokenService(store TokenStore) (*TokenService, error) {
|
||||
if store == nil {
|
||||
return nil, fmt.Errorf("store is nil")
|
||||
}
|
||||
return &TokenService{store: store}, nil
|
||||
}
|
||||
|
||||
func (s *TokenService) Revoke(jti string, exp time.Time) error {
|
||||
return s.store.revoke(jti, exp)
|
||||
}
|
||||
|
||||
func (s *TokenService) IsRevoked(jti string) (bool, error) {
|
||||
return s.store.isRevoked(jti)
|
||||
}
|
||||
42
internal/token/store_sqlite.go
Normal file
42
internal/token/store_sqlite.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type SQLiteTokenStore struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
TokenID string `gorm:"primaryKey"`
|
||||
Expiration time.Time `gorm:"index"`
|
||||
}
|
||||
|
||||
func NewSQLiteTokenStore(db *gorm.DB) (*SQLiteTokenStore, error) {
|
||||
if db == nil {
|
||||
return nil, fmt.Errorf("db is nil")
|
||||
}
|
||||
return &SQLiteTokenStore{
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteTokenStore) revoke(tokenID string, expiresAt time.Time) error {
|
||||
return s.db.Create(&Token{
|
||||
TokenID: tokenID,
|
||||
Expiration: expiresAt,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func (s *SQLiteTokenStore) isRevoked(tokenID string) (bool, error) {
|
||||
var count int64
|
||||
err := s.db.Model(&Token{}).Where("token_id = ?", tokenID).Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
67
internal/token/store_sqlite_test.go
Normal file
67
internal/token/store_sqlite_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package token
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func setupTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
|
||||
dbPath := filepath.Join("testdata", "tokens.db")
|
||||
|
||||
_ = os.Remove(dbPath)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open db: %v", err)
|
||||
}
|
||||
|
||||
if err := db.AutoMigrate(&Token{}); err != nil {
|
||||
t.Fatalf("failed to migrate: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func TestSQLiteTokenStore_RevokeAndCheck(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
|
||||
store, err := NewSQLiteTokenStore(db)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create store: %v", err)
|
||||
}
|
||||
|
||||
service, err := NewTokenService(store)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create service: %v", err)
|
||||
}
|
||||
|
||||
jti := "test-token-123"
|
||||
exp := time.Now().Add(time.Hour)
|
||||
|
||||
revoked, err := service.IsRevoked(jti)
|
||||
if err != nil {
|
||||
t.Fatalf("isRevoked failed: %v", err)
|
||||
}
|
||||
if revoked {
|
||||
t.Fatalf("token should NOT be revoked initially")
|
||||
}
|
||||
|
||||
if err := service.Revoke(jti, exp); err != nil {
|
||||
t.Fatalf("revoke failed: %v", err)
|
||||
}
|
||||
|
||||
revoked, err = service.IsRevoked(jti)
|
||||
if err != nil {
|
||||
t.Fatalf("isRevoked failed: %v", err)
|
||||
}
|
||||
if !revoked {
|
||||
t.Fatalf("token should be revoked")
|
||||
}
|
||||
}
|
||||
BIN
internal/token/testdata/tokens.db
vendored
Normal file
BIN
internal/token/testdata/tokens.db
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user