cloudsmith_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cloudsmith
  14. import (
  15. "context"
  16. "crypto/tls"
  17. "encoding/json"
  18. "net/http"
  19. "net/http/httptest"
  20. "testing"
  21. apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  22. "sigs.k8s.io/yaml"
  23. genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
  24. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  25. "github.com/external-secrets/external-secrets/pkg/esutils"
  26. )
  27. const mockJWTToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxNzAwMDAwMDAwfQ.signature"
  28. func TestCloudsmithGenerator_Generate(t *testing.T) {
  29. // Test server that mimics Cloudsmith OIDC endpoint
  30. server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  31. if r.Method != "POST" {
  32. http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
  33. return
  34. }
  35. var req OIDCRequest
  36. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  37. http.Error(w, "Bad request", http.StatusBadRequest)
  38. return
  39. }
  40. // Mock response with a JWT-like token (simplified for testing)
  41. mockToken := mockJWTToken
  42. response := OIDCResponse{
  43. Token: mockToken,
  44. }
  45. w.Header().Set("Content-Type", "application/json")
  46. w.WriteHeader(http.StatusCreated)
  47. json.NewEncoder(w).Encode(response)
  48. }))
  49. defer server.Close()
  50. // Create test spec
  51. spec := &genv1alpha1.CloudsmithAccessToken{
  52. Spec: genv1alpha1.CloudsmithAccessTokenSpec{
  53. APIURL: server.URL,
  54. OrgSlug: "test-org",
  55. ServiceSlug: "test-service",
  56. ServiceAccountRef: esmeta.ServiceAccountSelector{
  57. Name: "test-sa",
  58. Audiences: []string{"https://api.cloudsmith.io"},
  59. },
  60. },
  61. }
  62. specBytes, err := yaml.Marshal(spec)
  63. if err != nil {
  64. t.Fatalf("Failed to marshal spec: %v", err)
  65. }
  66. _ = &apiextensions.JSON{
  67. Raw: specBytes,
  68. }
  69. // Create generator with custom HTTP client that accepts self-signed certificates
  70. httpClient := &http.Client{
  71. Transport: &http.Transport{
  72. TLSClientConfig: &tls.Config{
  73. InsecureSkipVerify: true,
  74. },
  75. },
  76. }
  77. generator := &Generator{
  78. httpClient: httpClient,
  79. }
  80. // Note: This test will fail because we don't have a real service account token
  81. // In a real test environment, you would mock the fetchServiceAccountToken function
  82. // or set up proper test fixtures
  83. t.Run("parseSpec", func(t *testing.T) {
  84. parsed, err := parseSpec(specBytes)
  85. if err != nil {
  86. t.Fatalf("Failed to parse spec: %v", err)
  87. }
  88. if parsed.Spec.OrgSlug != "test-org" {
  89. t.Errorf("Expected OrgSlug to be 'test-org', got %s", parsed.Spec.OrgSlug)
  90. }
  91. if parsed.Spec.ServiceSlug != "test-service" {
  92. t.Errorf("Expected ServiceSlug to be 'test-service', got %s", parsed.Spec.ServiceSlug)
  93. }
  94. })
  95. t.Run("exchangeTokenWithCloudsmith", func(t *testing.T) {
  96. ctx := context.Background()
  97. oidcToken := "mock-oidc-token"
  98. token, err := generator.exchangeTokenWithCloudsmith(
  99. ctx,
  100. oidcToken,
  101. "test-org",
  102. "test-service",
  103. server.URL,
  104. )
  105. if err != nil {
  106. t.Fatalf("Failed to exchange token: %v", err)
  107. }
  108. if token == "" {
  109. t.Error("Expected non-empty token")
  110. }
  111. })
  112. t.Run("ParseJWTClaims", func(t *testing.T) {
  113. // Mock JWT token with known payload
  114. mockToken := mockJWTToken
  115. claims, err := esutils.ParseJWTClaims(mockToken)
  116. if err != nil {
  117. t.Fatalf("Failed to get claims: %v", err)
  118. }
  119. if claims["sub"] != "1234567890" {
  120. t.Errorf("Expected sub claim to be '1234567890', got %v", claims["sub"])
  121. }
  122. if claims["name"] != "John Doe" {
  123. t.Errorf("Expected name claim to be 'John Doe', got %v", claims["name"])
  124. }
  125. })
  126. t.Run("ExtractJWTExpiration", func(t *testing.T) {
  127. // Mock JWT token with known exp claim
  128. mockToken := mockJWTToken
  129. exp, err := esutils.ExtractJWTExpiration(mockToken)
  130. if err != nil {
  131. t.Fatalf("Failed to get token expiration: %v", err)
  132. }
  133. if exp != "1700000000" {
  134. t.Errorf("Expected expiration to be '1700000000', got %s", exp)
  135. }
  136. })
  137. }