383 lines
11 KiB
Go
383 lines
11 KiB
Go
package test
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/kms/api-key-service/internal/auth"
|
|
"github.com/kms/api-key-service/internal/config"
|
|
"github.com/kms/api-key-service/internal/domain"
|
|
)
|
|
|
|
func TestJWTManager_GenerateToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
Claims: map[string]string{
|
|
"department": "engineering",
|
|
"role": "developer",
|
|
},
|
|
}
|
|
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, tokenString)
|
|
|
|
// Verify token structure (should have 3 parts separated by dots)
|
|
parts := len(tokenString)
|
|
assert.Greater(t, parts, 100) // JWT tokens are typically longer than 100 chars
|
|
}
|
|
|
|
func TestJWTManager_ValidateToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
Claims: map[string]string{
|
|
"department": "engineering",
|
|
},
|
|
}
|
|
|
|
// Generate token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Validate token
|
|
claims, err := jwtManager.ValidateToken(tokenString)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userToken.UserID, claims.UserID)
|
|
assert.Equal(t, userToken.AppID, claims.AppID)
|
|
assert.Equal(t, userToken.Permissions, claims.Permissions)
|
|
assert.Equal(t, userToken.TokenType, claims.TokenType)
|
|
assert.Equal(t, userToken.Claims, claims.Claims)
|
|
}
|
|
|
|
func TestJWTManager_ValidateToken_InvalidToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
// Test with invalid token
|
|
_, err := jwtManager.ValidateToken("invalid.token.here")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Invalid token")
|
|
}
|
|
|
|
func TestJWTManager_ValidateToken_ExpiredToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read"},
|
|
IssuedAt: time.Now().Add(-2 * time.Hour),
|
|
ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired 1 hour ago
|
|
MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid also expired
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate token (this should work even with past dates)
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Validate token (this should fail due to expiration)
|
|
_, err = jwtManager.ValidateToken(tokenString)
|
|
assert.Error(t, err)
|
|
// The error could be either JWT expiration or our custom max valid check
|
|
assert.True(t,
|
|
strings.Contains(err.Error(), "expired beyond maximum validity") ||
|
|
strings.Contains(err.Error(), "token is expired"),
|
|
"Expected expiration error, got: %s", err.Error())
|
|
}
|
|
|
|
func TestJWTManager_RefreshToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate original token
|
|
originalToken, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Refresh token with new expiration
|
|
newExpiration := time.Now().Add(2 * time.Hour)
|
|
refreshedToken, err := jwtManager.RefreshToken(originalToken, newExpiration)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, refreshedToken)
|
|
assert.NotEqual(t, originalToken, refreshedToken)
|
|
|
|
// Validate refreshed token
|
|
claims, err := jwtManager.ValidateToken(refreshedToken)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userToken.UserID, claims.UserID)
|
|
assert.Equal(t, userToken.AppID, claims.AppID)
|
|
}
|
|
|
|
func TestJWTManager_RefreshToken_ExpiredMaxValid(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read"},
|
|
IssuedAt: time.Now().Add(-2 * time.Hour),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(-30 * time.Minute), // Max valid expired
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Try to refresh (should fail due to max valid expiration)
|
|
newExpiration := time.Now().Add(2 * time.Hour)
|
|
_, err = jwtManager.RefreshToken(tokenString, newExpiration)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "expired beyond maximum validity")
|
|
}
|
|
|
|
func TestJWTManager_ExtractClaims(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(-1 * time.Hour), // Expired token
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate expired token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Extract claims (should work even for expired tokens)
|
|
claims, err := jwtManager.ExtractClaims(tokenString)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userToken.UserID, claims.UserID)
|
|
assert.Equal(t, userToken.AppID, claims.AppID)
|
|
assert.Equal(t, userToken.Permissions, claims.Permissions)
|
|
}
|
|
|
|
func TestJWTManager_RevokeToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Revoke token
|
|
err = jwtManager.RevokeToken(tokenString)
|
|
assert.NoError(t, err)
|
|
|
|
// Check if token is revoked
|
|
revoked, err := jwtManager.IsTokenRevoked(tokenString)
|
|
assert.NoError(t, err)
|
|
assert.True(t, revoked)
|
|
}
|
|
|
|
func TestJWTManager_RevokeToken_AlreadyExpired(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read"},
|
|
IssuedAt: time.Now().Add(-2 * time.Hour),
|
|
ExpiresAt: time.Now().Add(-1 * time.Hour), // Already expired
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate expired token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Revoke expired token (should succeed but not add to blacklist)
|
|
err = jwtManager.RevokeToken(tokenString)
|
|
assert.NoError(t, err)
|
|
|
|
// Check if token is revoked (should be false since it was already expired)
|
|
revoked, err := jwtManager.IsTokenRevoked(tokenString)
|
|
assert.NoError(t, err)
|
|
assert.False(t, revoked)
|
|
}
|
|
|
|
func TestJWTManager_IsTokenRevoked_NotRevoked(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
// Generate token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Check if token is revoked (should be false)
|
|
revoked, err := jwtManager.IsTokenRevoked(tokenString)
|
|
assert.NoError(t, err)
|
|
assert.False(t, revoked)
|
|
}
|
|
|
|
func TestJWTManager_GetTokenInfo(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
Claims: map[string]string{
|
|
"department": "engineering",
|
|
},
|
|
}
|
|
|
|
// Generate token
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
require.NoError(t, err)
|
|
|
|
// Get token info
|
|
info := jwtManager.GetTokenInfo(tokenString)
|
|
assert.Equal(t, userToken.UserID, info["user_id"])
|
|
assert.Equal(t, userToken.AppID, info["app_id"])
|
|
assert.Equal(t, userToken.Permissions, info["permissions"])
|
|
assert.Equal(t, userToken.TokenType, info["token_type"])
|
|
assert.NotNil(t, info["issued_at"])
|
|
assert.NotNil(t, info["expires_at"])
|
|
assert.NotNil(t, info["max_valid_at"])
|
|
assert.NotNil(t, info["jti"])
|
|
}
|
|
|
|
func TestJWTManager_GetTokenInfo_InvalidToken(t *testing.T) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
// Get info for invalid token
|
|
info := jwtManager.GetTokenInfo("invalid.token.here")
|
|
assert.Contains(t, info["error"], "Invalid token format")
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkJWTManager_GenerateToken(b *testing.B) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := jwtManager.GenerateToken(userToken)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkJWTManager_ValidateToken(b *testing.B) {
|
|
cfg := config.NewConfig()
|
|
logger := zap.NewNop()
|
|
jwtManager := auth.NewJWTManager(cfg, logger)
|
|
|
|
userToken := &domain.UserToken{
|
|
UserID: "test-user-123",
|
|
AppID: "test-app-456",
|
|
Permissions: []string{"read", "write"},
|
|
IssuedAt: time.Now(),
|
|
ExpiresAt: time.Now().Add(1 * time.Hour),
|
|
MaxValidAt: time.Now().Add(24 * time.Hour),
|
|
TokenType: domain.TokenTypeUser,
|
|
}
|
|
|
|
tokenString, err := jwtManager.GenerateToken(userToken)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := jwtManager.ValidateToken(tokenString)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|