From 9ca9c53baff36782a113b8985943e7bbeeca9793 Mon Sep 17 00:00:00 2001 From: Ryan Copley Date: Sat, 23 Aug 2025 17:57:39 -0400 Subject: [PATCH] - --- internal/domain/duration.go | 10 ++++++++++ internal/domain/models.go | 8 ++++---- internal/services/application_service.go | 16 ++++++++++++++++ test/auth_test.go | 9 ++++++--- test/integration_test.go | 13 +++++++++++-- test/mock_repositories.go | 12 ++++++++++++ 6 files changed, 59 insertions(+), 9 deletions(-) diff --git a/internal/domain/duration.go b/internal/domain/duration.go index 453510a..bdc2643 100644 --- a/internal/domain/duration.go +++ b/internal/domain/duration.go @@ -45,3 +45,13 @@ func (d Duration) MarshalJSON() ([]byte, error) { func (d Duration) String() string { return d.Duration.String() } + +// Int64 returns the duration in nanoseconds for validator compatibility +func (d Duration) Int64() int64 { + return int64(d.Duration) +} + +// IsZero returns true if the duration is zero +func (d Duration) IsZero() bool { + return d.Duration == 0 +} diff --git a/internal/domain/models.go b/internal/domain/models.go index 8073704..dfa42f2 100644 --- a/internal/domain/models.go +++ b/internal/domain/models.go @@ -159,8 +159,8 @@ type CreateApplicationRequest struct { Type []ApplicationType `json:"type" validate:"required,min=1,dive,oneof=static user"` CallbackURL string `json:"callback_url" validate:"required,url,max=500"` TokenPrefix string `json:"token_prefix" validate:"omitempty,min=2,max=4,uppercase"` - TokenRenewalDuration Duration `json:"token_renewal_duration" validate:"required,min=1"` - MaxTokenDuration Duration `json:"max_token_duration" validate:"required,min=1"` + TokenRenewalDuration Duration `json:"token_renewal_duration" validate:"required"` + MaxTokenDuration Duration `json:"max_token_duration" validate:"required"` Owner Owner `json:"owner" validate:"required"` } @@ -171,8 +171,8 @@ type UpdateApplicationRequest struct { CallbackURL *string `json:"callback_url,omitempty" validate:"omitempty,url,max=500"` HMACKey *string `json:"hmac_key,omitempty" validate:"omitempty,min=1,max=255"` TokenPrefix *string `json:"token_prefix,omitempty" validate:"omitempty,min=2,max=4,uppercase"` - TokenRenewalDuration *Duration `json:"token_renewal_duration,omitempty" validate:"omitempty,min=1"` - MaxTokenDuration *Duration `json:"max_token_duration,omitempty" validate:"omitempty,min=1"` + TokenRenewalDuration *Duration `json:"token_renewal_duration,omitempty"` + MaxTokenDuration *Duration `json:"max_token_duration,omitempty"` Owner *Owner `json:"owner,omitempty" validate:"omitempty"` } diff --git a/internal/services/application_service.go b/internal/services/application_service.go index 2caf866..355dd1f 100644 --- a/internal/services/application_service.go +++ b/internal/services/application_service.go @@ -42,6 +42,14 @@ func (s *applicationService) Create(ctx context.Context, req *domain.CreateAppli return nil, fmt.Errorf("validation failed: %w", err) } + // Manual validation for Duration fields + if req.TokenRenewalDuration.Duration <= 0 { + return nil, fmt.Errorf("token_renewal_duration must be greater than 0") + } + if req.MaxTokenDuration.Duration <= 0 { + return nil, fmt.Errorf("max_token_duration must be greater than 0") + } + // Basic permission validation - check if user can create applications // In a real system, this would check against user roles/permissions if userID == "" { @@ -127,6 +135,14 @@ func (s *applicationService) Update(ctx context.Context, appID string, updates * return nil, fmt.Errorf("user authentication required") } + // Manual validation for Duration fields + if updates.TokenRenewalDuration != nil && updates.TokenRenewalDuration.Duration <= 0 { + return nil, fmt.Errorf("token_renewal_duration must be greater than 0") + } + if updates.MaxTokenDuration != nil && updates.MaxTokenDuration.Duration <= 0 { + return nil, fmt.Errorf("max_token_duration must be greater than 0") + } + // Additional business logic validation if updates.TokenRenewalDuration != nil && updates.MaxTokenDuration != nil { if updates.TokenRenewalDuration.Duration > updates.MaxTokenDuration.Duration { diff --git a/test/auth_test.go b/test/auth_test.go index c9f93a4..c327145 100644 --- a/test/auth_test.go +++ b/test/auth_test.go @@ -19,7 +19,8 @@ import ( func TestAuthenticationService_ValidateJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() - authService := services.NewAuthenticationService(config, logger) + permRepo := NewMockPermissionRepository() + authService := services.NewAuthenticationService(config, logger, permRepo) userToken := &domain.UserToken{ AppID: "test-app", @@ -51,7 +52,8 @@ func TestAuthenticationService_ValidateJWTToken(t *testing.T) { func TestAuthenticationService_GenerateJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() - authService := services.NewAuthenticationService(config, logger) + permRepo := NewMockPermissionRepository() + authService := services.NewAuthenticationService(config, logger, permRepo) userToken := &domain.UserToken{ AppID: "test-app", @@ -76,7 +78,8 @@ func TestAuthenticationService_GenerateJWTToken(t *testing.T) { func TestAuthenticationService_RefreshJWTToken(t *testing.T) { config := NewMockConfig() logger := zap.NewNop() - authService := services.NewAuthenticationService(config, logger) + permRepo := NewMockPermissionRepository() + authService := services.NewAuthenticationService(config, logger, permRepo) userToken := &domain.UserToken{ AppID: "test-app", diff --git a/test/integration_test.go b/test/integration_test.go index 4b26aba..1cf53e1 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "io" "net/http" "net/http/httptest" "testing" @@ -54,6 +55,7 @@ func (suite *IntegrationTestSuite) SetupSuite() { "INTERNAL_HMAC_KEY": "test-hmac-key-for-integration-tests", "AUTH_PROVIDER": "header", "AUTH_HEADER_USER_EMAIL": "X-User-Email", + "JWT_SECRET": "test-jwt-secret-for-integration-tests", "RATE_LIMIT_ENABLED": "false", // Disable for tests "METRICS_ENABLED": "false", }, @@ -97,7 +99,7 @@ func (suite *IntegrationTestSuite) setupServer() { // Initialize services appService := services.NewApplicationService(appRepo, logger) tokenService := services.NewTokenService(tokenRepo, appRepo, permRepo, grantRepo, suite.cfg.GetString("INTERNAL_HMAC_KEY"), suite.cfg, logger) - authService := services.NewAuthenticationService(suite.cfg, logger) + authService := services.NewAuthenticationService(suite.cfg, logger, permRepo) // Initialize handlers healthHandler := handlers.NewHealthHandler(suite.db, logger) @@ -396,7 +398,6 @@ func (suite *IntegrationTestSuite) TestStaticTokenWorkflow() { suite.T().Run("VerifyStaticToken", func(t *testing.T) { verifyReq := domain.VerifyRequest{ AppID: testApp.AppID, - Type: domain.TokenTypeStatic, Token: createdToken.Token, Permissions: []string{"repo.read"}, } @@ -483,6 +484,14 @@ func (suite *IntegrationTestSuite) TestUserTokenWorkflow() { require.NoError(t, err) defer resp.Body.Close() + // Debug: Print response body if not 200 + if resp.StatusCode != http.StatusOK { + bodyBytes, _ := io.ReadAll(resp.Body) + resp.Body.Close() + resp.Body = io.NopCloser(bytes.NewReader(bodyBytes)) + t.Logf("Login failed with status %d, body: %s", resp.StatusCode, string(bodyBytes)) + } + assert.Equal(t, http.StatusOK, resp.StatusCode) // The response should contain either a token directly or a redirect URL diff --git a/test/mock_repositories.go b/test/mock_repositories.go index 058859c..feef2ba 100644 --- a/test/mock_repositories.go +++ b/test/mock_repositories.go @@ -337,6 +337,18 @@ func NewMockPermissionRepository() repository.PermissionRepository { UpdatedAt: time.Now(), UpdatedBy: "system", }, + { + ID: uuid.New(), + Scope: "app.read", + Name: "Application Read", + Description: "Read application data", + Category: "application", + IsSystem: false, + CreatedAt: time.Now(), + CreatedBy: "system", + UpdatedAt: time.Now(), + UpdatedBy: "system", + }, } for _, perm := range defaultPerms {