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