From cdde811e72e870e5b4229767a21d1bd77b894b39 Mon Sep 17 00:00:00 2001 From: Alexey Date: Thu, 18 Dec 2025 09:46:57 +0200 Subject: [PATCH] add token service, sqlite driver and test --- internal/token/service.go | 30 +++++++++++++ internal/token/store_sqlite.go | 42 +++++++++++++++++ internal/token/store_sqlite_test.go | 67 ++++++++++++++++++++++++++++ internal/token/testdata/tokens.db | Bin 0 -> 16384 bytes 4 files changed, 139 insertions(+) create mode 100644 internal/token/service.go create mode 100644 internal/token/store_sqlite.go create mode 100644 internal/token/store_sqlite_test.go create mode 100644 internal/token/testdata/tokens.db diff --git a/internal/token/service.go b/internal/token/service.go new file mode 100644 index 0000000..dd15d79 --- /dev/null +++ b/internal/token/service.go @@ -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) +} diff --git a/internal/token/store_sqlite.go b/internal/token/store_sqlite.go new file mode 100644 index 0000000..b84057e --- /dev/null +++ b/internal/token/store_sqlite.go @@ -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 +} diff --git a/internal/token/store_sqlite_test.go b/internal/token/store_sqlite_test.go new file mode 100644 index 0000000..a710320 --- /dev/null +++ b/internal/token/store_sqlite_test.go @@ -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") + } +} diff --git a/internal/token/testdata/tokens.db b/internal/token/testdata/tokens.db new file mode 100644 index 0000000000000000000000000000000000000000..60a6757d1f5bbaf72eb8c12184d085b7364e64a7 GIT binary patch literal 16384 zcmeI%Jx{_w7zgk>N<>VsIuW*;3=J=73yQEPY6l{s0)x2F8jhq9sKRNwI{MZ06YwP* zoE$x`@8H4}JU9Z>PUHkmDr)`{8UBOw`8}=Q+a%7EAN_H6|gebCwWt)|dJeeQU z*^O^{g=BBe8px7eZ8nqQ)6 zsnAk