github.com/argoproj/argo-cd/v3@v3.2.1/util/session/sessionmanager_test.go (about) 1 package session 2 3 import ( 4 "context" 5 "encoding/pem" 6 stderrors "errors" 7 "fmt" 8 "io" 9 "math" 10 "net/http" 11 "net/http/httptest" 12 "strconv" 13 "strings" 14 "testing" 15 "time" 16 17 "github.com/golang-jwt/jwt/v5" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 "google.golang.org/grpc/codes" 21 "google.golang.org/grpc/status" 22 corev1 "k8s.io/api/core/v1" 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/client-go/kubernetes/fake" 26 27 "github.com/argoproj/argo-cd/v3/common" 28 appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1" 29 apps "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake" 30 "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1" 31 "github.com/argoproj/argo-cd/v3/test" 32 jwtutil "github.com/argoproj/argo-cd/v3/util/jwt" 33 "github.com/argoproj/argo-cd/v3/util/password" 34 "github.com/argoproj/argo-cd/v3/util/settings" 35 utiltest "github.com/argoproj/argo-cd/v3/util/test" 36 ) 37 38 func getProjLister(objects ...runtime.Object) v1alpha1.AppProjectNamespaceLister { 39 return test.NewFakeProjListerFromInterface(apps.NewSimpleClientset(objects...).ArgoprojV1alpha1().AppProjects("argocd")) 40 } 41 42 func getKubeClient(t *testing.T, pass string, enabled bool, capabilities ...settings.AccountCapability) *fake.Clientset { 43 t.Helper() 44 const defaultSecretKey = "Hello, world!" 45 46 bcrypt, err := password.HashPassword(pass) 47 require.NoError(t, err) 48 if len(capabilities) == 0 { 49 capabilities = []settings.AccountCapability{settings.AccountCapabilityLogin, settings.AccountCapabilityApiKey} 50 } 51 var capabilitiesStr []string 52 for i := range capabilities { 53 capabilitiesStr = append(capabilitiesStr, string(capabilities[i])) 54 } 55 56 return fake.NewClientset(&corev1.ConfigMap{ 57 ObjectMeta: metav1.ObjectMeta{ 58 Name: "argocd-cm", 59 Namespace: "argocd", 60 Labels: map[string]string{ 61 "app.kubernetes.io/part-of": "argocd", 62 }, 63 }, 64 Data: map[string]string{ 65 "admin": strings.Join(capabilitiesStr, ","), 66 "admin.enabled": strconv.FormatBool(enabled), 67 }, 68 }, &corev1.Secret{ 69 ObjectMeta: metav1.ObjectMeta{ 70 Name: "argocd-secret", 71 Namespace: "argocd", 72 }, 73 Data: map[string][]byte{ 74 "admin.password": []byte(bcrypt), 75 "server.secretkey": []byte(defaultSecretKey), 76 }, 77 }) 78 } 79 80 func newSessionManager(settingsMgr *settings.SettingsManager, projectLister v1alpha1.AppProjectNamespaceLister, storage UserStateStorage) *SessionManager { 81 mgr := NewSessionManager(settingsMgr, projectLister, "", nil, storage) 82 mgr.verificationDelayNoiseEnabled = false 83 return mgr 84 } 85 86 func TestSessionManager_AdminToken(t *testing.T) { 87 redisClient, closer := test.NewInMemoryRedis() 88 defer closer() 89 90 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", true), "argocd") 91 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(redisClient)) 92 93 token, err := mgr.Create("admin:login", 0, "123") 94 require.NoError(t, err, "Could not create token") 95 96 claims, newToken, err := mgr.Parse(token) 97 require.NoError(t, err) 98 assert.Empty(t, newToken) 99 100 mapClaims := *(claims.(*jwt.MapClaims)) 101 subject := mapClaims["sub"].(string) 102 if subject != "admin" { 103 t.Errorf("Token claim subject %q does not match expected subject %q.", subject, "admin") 104 } 105 } 106 107 func TestSessionManager_AdminToken_ExpiringSoon(t *testing.T) { 108 redisClient, closer := test.NewInMemoryRedis() 109 defer closer() 110 111 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", true), "argocd") 112 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(redisClient)) 113 114 token, err := mgr.Create("admin:login", int64(autoRegenerateTokenDuration.Seconds()-1), "123") 115 require.NoError(t, err) 116 117 // verify new token is generated is login token is expiring soon 118 _, newToken, err := mgr.Parse(token) 119 require.NoError(t, err) 120 assert.NotEmpty(t, newToken) 121 122 // verify that new token is valid and for the same user 123 claims, _, err := mgr.Parse(newToken) 124 require.NoError(t, err) 125 126 mapClaims := *(claims.(*jwt.MapClaims)) 127 subject := mapClaims["sub"].(string) 128 assert.Equal(t, "admin", subject) 129 } 130 131 func TestSessionManager_AdminToken_Revoked(t *testing.T) { 132 redisClient, closer := test.NewInMemoryRedis() 133 defer closer() 134 135 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", true), "argocd") 136 storage := NewUserStateStorage(redisClient) 137 138 mgr := newSessionManager(settingsMgr, getProjLister(), storage) 139 140 token, err := mgr.Create("admin:login", 0, "123") 141 require.NoError(t, err) 142 143 err = storage.RevokeToken(t.Context(), "123", time.Hour) 144 require.NoError(t, err) 145 146 _, _, err = mgr.Parse(token) 147 assert.EqualError(t, err, "token is revoked, please re-login") 148 } 149 150 func TestSessionManager_AdminToken_Deactivated(t *testing.T) { 151 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", false), "argocd") 152 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 153 154 token, err := mgr.Create("admin:login", 0, "abc") 155 require.NoError(t, err, "Could not create token") 156 157 _, _, err = mgr.Parse(token) 158 assert.ErrorContains(t, err, "account admin is disabled") 159 } 160 161 func TestSessionManager_AdminToken_LoginCapabilityDisabled(t *testing.T) { 162 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", true, settings.AccountCapabilityLogin), "argocd") 163 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 164 165 token, err := mgr.Create("admin", 0, "abc") 166 require.NoError(t, err, "Could not create token") 167 168 _, _, err = mgr.Parse(token) 169 assert.ErrorContains(t, err, "account admin does not have 'apiKey' capability") 170 } 171 172 func TestSessionManager_ProjectToken(t *testing.T) { 173 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "pass", true), "argocd") 174 175 t.Run("Valid Token", func(t *testing.T) { 176 proj := appv1.AppProject{ 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: "default", 179 Namespace: "argocd", 180 }, 181 Spec: appv1.AppProjectSpec{Roles: []appv1.ProjectRole{{Name: "test"}}}, 182 Status: appv1.AppProjectStatus{JWTTokensByRole: map[string]appv1.JWTTokens{ 183 "test": { 184 Items: []appv1.JWTToken{{ID: "abc", IssuedAt: time.Now().Unix(), ExpiresAt: 0}}, 185 }, 186 }}, 187 } 188 mgr := newSessionManager(settingsMgr, getProjLister(&proj), NewUserStateStorage(nil)) 189 190 jwtToken, err := mgr.Create("proj:default:test", 100, "abc") 191 require.NoError(t, err) 192 193 claims, _, err := mgr.Parse(jwtToken) 194 require.NoError(t, err) 195 196 mapClaims, err := jwtutil.MapClaims(claims) 197 require.NoError(t, err) 198 199 assert.Equal(t, "proj:default:test", mapClaims["sub"]) 200 }) 201 202 t.Run("Token Revoked", func(t *testing.T) { 203 proj := appv1.AppProject{ 204 ObjectMeta: metav1.ObjectMeta{ 205 Name: "default", 206 Namespace: "argocd", 207 }, 208 Spec: appv1.AppProjectSpec{Roles: []appv1.ProjectRole{{Name: "test"}}}, 209 } 210 211 mgr := newSessionManager(settingsMgr, getProjLister(&proj), NewUserStateStorage(nil)) 212 213 jwtToken, err := mgr.Create("proj:default:test", 10, "") 214 require.NoError(t, err) 215 216 _, _, err = mgr.Parse(jwtToken) 217 assert.ErrorContains(t, err, "does not exist in project 'default'") 218 }) 219 } 220 221 type tokenVerifierMock struct { 222 claims jwt.Claims 223 err error 224 } 225 226 func (tm *tokenVerifierMock) VerifyToken(_ string) (jwt.Claims, string, error) { 227 if tm.claims == nil { 228 return nil, "", tm.err 229 } 230 return tm.claims, "", tm.err 231 } 232 233 func strPointer(str string) *string { 234 return &str 235 } 236 237 func TestSessionManager_WithAuthMiddleware(t *testing.T) { 238 handlerFunc := func() func(http.ResponseWriter, *http.Request) { 239 return func(w http.ResponseWriter, _ *http.Request) { 240 t.Helper() 241 w.WriteHeader(http.StatusOK) 242 w.Header().Set("Content-Type", "application/text") 243 _, err := w.Write([]byte("Ok")) 244 require.NoError(t, err, "error writing response: %s", err) 245 } 246 } 247 type testCase struct { 248 name string 249 authDisabled bool 250 cookieHeader bool 251 verifiedClaims *jwt.RegisteredClaims 252 verifyTokenErr error 253 expectedStatusCode int 254 expectedResponseBody *string 255 } 256 257 cases := []testCase{ 258 { 259 name: "will authenticate successfully", 260 authDisabled: false, 261 cookieHeader: true, 262 verifiedClaims: &jwt.RegisteredClaims{}, 263 verifyTokenErr: nil, 264 expectedStatusCode: http.StatusOK, 265 expectedResponseBody: strPointer("Ok"), 266 }, 267 { 268 name: "will be noop if auth is disabled", 269 authDisabled: true, 270 cookieHeader: false, 271 verifiedClaims: nil, 272 verifyTokenErr: nil, 273 expectedStatusCode: http.StatusOK, 274 expectedResponseBody: strPointer("Ok"), 275 }, 276 { 277 name: "will return 400 if no cookie header", 278 authDisabled: false, 279 cookieHeader: false, 280 verifiedClaims: &jwt.RegisteredClaims{}, 281 verifyTokenErr: nil, 282 expectedStatusCode: http.StatusBadRequest, 283 expectedResponseBody: nil, 284 }, 285 { 286 name: "will return 401 verify token fails", 287 authDisabled: false, 288 cookieHeader: true, 289 verifiedClaims: &jwt.RegisteredClaims{}, 290 verifyTokenErr: stderrors.New("token error"), 291 expectedStatusCode: http.StatusUnauthorized, 292 expectedResponseBody: nil, 293 }, 294 { 295 name: "will return 200 if claims are nil", 296 authDisabled: false, 297 cookieHeader: true, 298 verifiedClaims: nil, 299 verifyTokenErr: nil, 300 expectedStatusCode: http.StatusOK, 301 expectedResponseBody: strPointer("Ok"), 302 }, 303 } 304 for _, tc := range cases { 305 tc := tc 306 t.Run(tc.name, func(t *testing.T) { 307 // given 308 mux := http.NewServeMux() 309 mux.HandleFunc("/", handlerFunc()) 310 tm := &tokenVerifierMock{ 311 claims: tc.verifiedClaims, 312 err: tc.verifyTokenErr, 313 } 314 ts := httptest.NewServer(WithAuthMiddleware(tc.authDisabled, tm, mux)) 315 defer ts.Close() 316 req, err := http.NewRequest(http.MethodGet, ts.URL, http.NoBody) 317 require.NoErrorf(t, err, "error creating request: %s", err) 318 if tc.cookieHeader { 319 req.Header.Add("Cookie", "argocd.token=123456") 320 } 321 322 // when 323 resp, err := http.DefaultClient.Do(req) 324 325 // then 326 require.NoError(t, err) 327 assert.NotNil(t, resp) 328 assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) 329 if tc.expectedResponseBody != nil { 330 body, err := io.ReadAll(resp.Body) 331 require.NoError(t, err) 332 actual := strings.TrimSuffix(string(body), "\n") 333 assert.Contains(t, actual, *tc.expectedResponseBody) 334 } 335 }) 336 } 337 } 338 339 var ( 340 loggedOutContext = context.Background() 341 //nolint:staticcheck 342 loggedInContext = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"iss": "qux", "sub": "foo", "email": "bar", "groups": []string{"baz"}}) 343 //nolint:staticcheck 344 loggedInContextFederated = context.WithValue(context.Background(), "claims", &jwt.MapClaims{"iss": "qux", "sub": "not-foo", "email": "bar", "groups": []string{"baz"}, "federated_claims": map[string]any{"user_id": "foo"}}) 345 ) 346 347 func TestIss(t *testing.T) { 348 assert.Empty(t, Iss(loggedOutContext)) 349 assert.Equal(t, "qux", Iss(loggedInContext)) 350 assert.Equal(t, "qux", Iss(loggedInContextFederated)) 351 } 352 353 func TestLoggedIn(t *testing.T) { 354 assert.False(t, LoggedIn(loggedOutContext)) 355 assert.True(t, LoggedIn(loggedInContext)) 356 assert.True(t, LoggedIn(loggedInContextFederated)) 357 } 358 359 func TestUsername(t *testing.T) { 360 assert.Empty(t, Username(loggedOutContext)) 361 assert.Equal(t, "bar", Username(loggedInContext)) 362 assert.Equal(t, "bar", Username(loggedInContextFederated)) 363 } 364 365 func TestGetUserIdentifier(t *testing.T) { 366 assert.Empty(t, GetUserIdentifier(loggedOutContext)) 367 assert.Equal(t, "foo", GetUserIdentifier(loggedInContext)) 368 assert.Equal(t, "foo", GetUserIdentifier(loggedInContextFederated)) 369 } 370 371 func TestGroups(t *testing.T) { 372 assert.Empty(t, Groups(loggedOutContext, []string{"groups"})) 373 assert.Equal(t, []string{"baz"}, Groups(loggedInContext, []string{"groups"})) 374 } 375 376 func TestVerifyUsernamePassword(t *testing.T) { 377 const password = "password" 378 379 for _, tc := range []struct { 380 name string 381 disabled bool 382 userName string 383 password string 384 expected error 385 }{ 386 { 387 name: "Success if userName and password is correct", 388 disabled: false, 389 userName: common.ArgoCDAdminUsername, 390 password: password, 391 expected: nil, 392 }, 393 { 394 name: "Return error if password is empty", 395 disabled: false, 396 userName: common.ArgoCDAdminUsername, 397 password: "", 398 expected: status.Errorf(codes.Unauthenticated, blankPasswordError), 399 }, 400 { 401 name: "Return error if password is not correct", 402 disabled: false, 403 userName: common.ArgoCDAdminUsername, 404 password: "foo", 405 expected: status.Errorf(codes.Unauthenticated, invalidLoginError), 406 }, 407 { 408 name: "Return error if disableAdmin is true", 409 disabled: true, 410 userName: common.ArgoCDAdminUsername, 411 password: password, 412 expected: status.Errorf(codes.Unauthenticated, accountDisabled, "admin"), 413 }, 414 } { 415 t.Run(tc.name, func(t *testing.T) { 416 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, password, !tc.disabled), "argocd") 417 418 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 419 420 err := mgr.VerifyUsernamePassword(tc.userName, tc.password) 421 422 if tc.expected == nil { 423 require.NoError(t, err) 424 } else { 425 assert.EqualError(t, err, tc.expected.Error()) 426 } 427 }) 428 } 429 } 430 431 func TestCacheValueGetters(t *testing.T) { 432 t.Run("Default values", func(t *testing.T) { 433 mlf := getMaxLoginFailures() 434 assert.Equal(t, defaultMaxLoginFailures, mlf) 435 436 mcs := getMaximumCacheSize() 437 assert.Equal(t, defaultMaxCacheSize, mcs) 438 }) 439 440 t.Run("Valid environment overrides", func(t *testing.T) { 441 t.Setenv(envLoginMaxFailCount, "5") 442 t.Setenv(envLoginMaxCacheSize, "5") 443 444 mlf := getMaxLoginFailures() 445 assert.Equal(t, 5, mlf) 446 447 mcs := getMaximumCacheSize() 448 assert.Equal(t, 5, mcs) 449 }) 450 451 t.Run("Invalid environment overrides", func(t *testing.T) { 452 t.Setenv(envLoginMaxFailCount, "invalid") 453 t.Setenv(envLoginMaxCacheSize, "invalid") 454 455 mlf := getMaxLoginFailures() 456 assert.Equal(t, defaultMaxLoginFailures, mlf) 457 458 mcs := getMaximumCacheSize() 459 assert.Equal(t, defaultMaxCacheSize, mcs) 460 }) 461 462 t.Run("Less than allowed in environment overrides", func(t *testing.T) { 463 t.Setenv(envLoginMaxFailCount, "-1") 464 t.Setenv(envLoginMaxCacheSize, "-1") 465 466 mlf := getMaxLoginFailures() 467 assert.Equal(t, defaultMaxLoginFailures, mlf) 468 469 mcs := getMaximumCacheSize() 470 assert.Equal(t, defaultMaxCacheSize, mcs) 471 }) 472 473 t.Run("Greater than allowed in environment overrides", func(t *testing.T) { 474 t.Setenv(envLoginMaxFailCount, strconv.Itoa(math.MaxInt32+1)) 475 t.Setenv(envLoginMaxCacheSize, strconv.Itoa(math.MaxInt32+1)) 476 477 mlf := getMaxLoginFailures() 478 assert.Equal(t, defaultMaxLoginFailures, mlf) 479 480 mcs := getMaximumCacheSize() 481 assert.Equal(t, defaultMaxCacheSize, mcs) 482 }) 483 } 484 485 func TestLoginRateLimiter(t *testing.T) { 486 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "password", true), "argocd") 487 storage := NewUserStateStorage(nil) 488 489 mgr := newSessionManager(settingsMgr, getProjLister(), storage) 490 491 t.Run("Test login delay valid user", func(t *testing.T) { 492 for i := 0; i < getMaxLoginFailures(); i++ { 493 err := mgr.VerifyUsernamePassword("admin", "wrong") 494 require.Error(t, err) 495 } 496 497 // The 11th time should fail even if password is right 498 { 499 err := mgr.VerifyUsernamePassword("admin", "password") 500 require.Error(t, err) 501 } 502 503 storage.attempts = map[string]LoginAttempts{} 504 // Failed counter should have been reset, should validate immediately 505 { 506 err := mgr.VerifyUsernamePassword("admin", "password") 507 require.NoError(t, err) 508 } 509 }) 510 511 t.Run("Test login delay invalid user", func(t *testing.T) { 512 for i := 0; i < getMaxLoginFailures(); i++ { 513 err := mgr.VerifyUsernamePassword("invalid", "wrong") 514 require.Error(t, err) 515 } 516 517 err := mgr.VerifyUsernamePassword("invalid", "wrong") 518 require.Error(t, err) 519 }) 520 } 521 522 func TestMaxUsernameLength(t *testing.T) { 523 username := "" 524 for i := 0; i < maxUsernameLength+1; i++ { 525 username += "a" 526 } 527 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "password", true), "argocd") 528 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 529 err := mgr.VerifyUsernamePassword(username, "password") 530 assert.ErrorContains(t, err, fmt.Sprintf(usernameTooLongError, maxUsernameLength)) 531 } 532 533 func TestMaxCacheSize(t *testing.T) { 534 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "password", true), "argocd") 535 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 536 537 invalidUsers := []string{"invalid1", "invalid2", "invalid3", "invalid4", "invalid5", "invalid6", "invalid7"} 538 // Temporarily decrease max cache size 539 t.Setenv(envLoginMaxCacheSize, "5") 540 541 for _, user := range invalidUsers { 542 err := mgr.VerifyUsernamePassword(user, "password") 543 require.Error(t, err) 544 } 545 546 assert.Len(t, mgr.GetLoginFailures(), 5) 547 } 548 549 func TestFailedAttemptsExpiry(t *testing.T) { 550 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClient(t, "password", true), "argocd") 551 mgr := newSessionManager(settingsMgr, getProjLister(), NewUserStateStorage(nil)) 552 553 invalidUsers := []string{"invalid1", "invalid2", "invalid3", "invalid4", "invalid5", "invalid6", "invalid7"} 554 555 t.Setenv(envLoginFailureWindowSeconds, "1") 556 557 for _, user := range invalidUsers { 558 err := mgr.VerifyUsernamePassword(user, "password") 559 require.Error(t, err) 560 } 561 562 time.Sleep(2 * time.Second) 563 564 err := mgr.VerifyUsernamePassword("invalid8", "password") 565 require.Error(t, err) 566 assert.Len(t, mgr.GetLoginFailures(), 1) 567 } 568 569 func getKubeClientWithConfig(config map[string]string, secretConfig map[string][]byte) *fake.Clientset { 570 mergedSecretConfig := map[string][]byte{ 571 "server.secretkey": []byte("Hello, world!"), 572 } 573 for key, value := range secretConfig { 574 mergedSecretConfig[key] = value 575 } 576 577 return fake.NewClientset(&corev1.ConfigMap{ 578 ObjectMeta: metav1.ObjectMeta{ 579 Name: "argocd-cm", 580 Namespace: "argocd", 581 Labels: map[string]string{ 582 "app.kubernetes.io/part-of": "argocd", 583 }, 584 }, 585 Data: config, 586 }, &corev1.Secret{ 587 ObjectMeta: metav1.ObjectMeta{ 588 Name: "argocd-secret", 589 Namespace: "argocd", 590 }, 591 Data: mergedSecretConfig, 592 }) 593 } 594 595 func TestSessionManager_VerifyToken(t *testing.T) { 596 oidcTestServer := utiltest.GetOIDCTestServer(t, nil) 597 t.Cleanup(oidcTestServer.Close) 598 599 dexTestServer := utiltest.GetDexTestServer(t) 600 t.Cleanup(dexTestServer.Close) 601 602 t.Run("RS512 is supported", func(t *testing.T) { 603 dexConfig := map[string]string{ 604 "url": "", 605 "oidc.config": fmt.Sprintf(` 606 name: Test 607 issuer: %s 608 clientID: xxx 609 clientSecret: yyy 610 requestedScopes: ["oidc"]`, oidcTestServer.URL), 611 } 612 613 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, nil), "argocd") 614 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 615 mgr.verificationDelayNoiseEnabled = false 616 // Use test server's client to avoid TLS issues. 617 mgr.client = oidcTestServer.Client() 618 619 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 620 claims.Issuer = oidcTestServer.URL 621 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 622 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 623 require.NoError(t, err) 624 tokenString, err := token.SignedString(key) 625 require.NoError(t, err) 626 627 _, _, err = mgr.VerifyToken(tokenString) 628 assert.NotContains(t, err.Error(), "oidc: id token signed with unsupported algorithm") 629 }) 630 631 t.Run("oidcConfig.rootCA is respected", func(t *testing.T) { 632 cert := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: oidcTestServer.TLS.Certificates[0].Certificate[0]}) 633 634 dexConfig := map[string]string{ 635 "url": "", 636 "oidc.config": fmt.Sprintf(` 637 name: Test 638 issuer: %s 639 clientID: xxx 640 clientSecret: yyy 641 requestedScopes: ["oidc"] 642 rootCA: | 643 %s 644 `, oidcTestServer.URL, strings.ReplaceAll(string(cert), "\n", "\n ")), 645 } 646 647 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, nil), "argocd") 648 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 649 mgr.verificationDelayNoiseEnabled = false 650 651 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 652 claims.Issuer = oidcTestServer.URL 653 token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) 654 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 655 require.NoError(t, err) 656 tokenString, err := token.SignedString(key) 657 require.NoError(t, err) 658 659 _, _, err = mgr.VerifyToken(tokenString) 660 // If the root CA is being respected, we won't get this error. The error message is environment-dependent, so 661 // we check for either of the error messages associated with a failed cert check. 662 assert.NotContains(t, err.Error(), "certificate is not trusted") 663 assert.NotContains(t, err.Error(), "certificate signed by unknown authority") 664 }) 665 666 t.Run("OIDC provider is Dex, TLS is configured", func(t *testing.T) { 667 dexConfig := map[string]string{ 668 "url": dexTestServer.URL, 669 "dex.config": `connectors: 670 - type: github 671 name: GitHub 672 config: 673 clientID: aabbccddeeff00112233 674 clientSecret: aabbccddeeff00112233`, 675 } 676 677 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 678 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 679 secretConfig := map[string][]byte{ 680 "tls.crt": utiltest.Cert, 681 "tls.key": utiltest.PrivateKey, 682 } 683 684 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd") 685 mgr := NewSessionManager(settingsMgr, getProjLister(), dexTestServer.URL, nil, NewUserStateStorage(nil)) 686 mgr.verificationDelayNoiseEnabled = false 687 688 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 689 claims.Issuer = dexTestServer.URL + "/api/dex" 690 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 691 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 692 require.NoError(t, err) 693 tokenString, err := token.SignedString(key) 694 require.NoError(t, err) 695 696 _, _, err = mgr.VerifyToken(tokenString) 697 require.Error(t, err) 698 assert.ErrorIs(t, err, common.ErrTokenVerification) 699 }) 700 701 t.Run("OIDC provider is external, TLS is configured", func(t *testing.T) { 702 dexConfig := map[string]string{ 703 "url": "", 704 "oidc.config": fmt.Sprintf(` 705 name: Test 706 issuer: %s 707 clientID: xxx 708 clientSecret: yyy 709 requestedScopes: ["oidc"]`, oidcTestServer.URL), 710 } 711 712 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 713 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 714 secretConfig := map[string][]byte{ 715 "tls.crt": utiltest.Cert, 716 "tls.key": utiltest.PrivateKey, 717 } 718 719 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd") 720 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 721 mgr.verificationDelayNoiseEnabled = false 722 723 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 724 claims.Issuer = oidcTestServer.URL 725 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 726 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 727 require.NoError(t, err) 728 tokenString, err := token.SignedString(key) 729 require.NoError(t, err) 730 731 _, _, err = mgr.VerifyToken(tokenString) 732 require.Error(t, err) 733 assert.ErrorIs(t, err, common.ErrTokenVerification) 734 }) 735 736 t.Run("OIDC provider is Dex, TLS is configured", func(t *testing.T) { 737 dexConfig := map[string]string{ 738 "url": dexTestServer.URL, 739 "dex.config": `connectors: 740 - type: github 741 name: GitHub 742 config: 743 clientID: aabbccddeeff00112233 744 clientSecret: aabbccddeeff00112233`, 745 } 746 747 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 748 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 749 secretConfig := map[string][]byte{ 750 "tls.crt": utiltest.Cert, 751 "tls.key": utiltest.PrivateKey, 752 } 753 754 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd") 755 mgr := NewSessionManager(settingsMgr, getProjLister(), dexTestServer.URL, nil, NewUserStateStorage(nil)) 756 mgr.verificationDelayNoiseEnabled = false 757 758 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 759 claims.Issuer = dexTestServer.URL + "/api/dex" 760 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 761 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 762 require.NoError(t, err) 763 tokenString, err := token.SignedString(key) 764 require.NoError(t, err) 765 766 _, _, err = mgr.VerifyToken(tokenString) 767 require.Error(t, err) 768 assert.ErrorIs(t, err, common.ErrTokenVerification) 769 }) 770 771 t.Run("OIDC provider is external, TLS is configured, OIDCTLSInsecureSkipVerify is true", func(t *testing.T) { 772 dexConfig := map[string]string{ 773 "url": "", 774 "oidc.config": fmt.Sprintf(` 775 name: Test 776 issuer: %s 777 clientID: xxx 778 clientSecret: yyy 779 requestedScopes: ["oidc"]`, oidcTestServer.URL), 780 "oidc.tls.insecure.skip.verify": "true", 781 } 782 783 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 784 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 785 secretConfig := map[string][]byte{ 786 "tls.crt": utiltest.Cert, 787 "tls.key": utiltest.PrivateKey, 788 } 789 790 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, secretConfig), "argocd") 791 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 792 mgr.verificationDelayNoiseEnabled = false 793 794 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 795 claims.Issuer = oidcTestServer.URL 796 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 797 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 798 require.NoError(t, err) 799 tokenString, err := token.SignedString(key) 800 require.NoError(t, err) 801 802 _, _, err = mgr.VerifyToken(tokenString) 803 assert.NotContains(t, err.Error(), "certificate is not trusted") 804 assert.NotContains(t, err.Error(), "certificate signed by unknown authority") 805 }) 806 807 t.Run("OIDC provider is external, TLS is not configured, OIDCTLSInsecureSkipVerify is true", func(t *testing.T) { 808 dexConfig := map[string]string{ 809 "url": "", 810 "oidc.config": fmt.Sprintf(` 811 name: Test 812 issuer: %s 813 clientID: xxx 814 clientSecret: yyy 815 requestedScopes: ["oidc"]`, oidcTestServer.URL), 816 "oidc.tls.insecure.skip.verify": "true", 817 } 818 819 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(dexConfig, nil), "argocd") 820 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 821 mgr.verificationDelayNoiseEnabled = false 822 823 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"test-client"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 824 claims.Issuer = oidcTestServer.URL 825 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 826 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 827 require.NoError(t, err) 828 tokenString, err := token.SignedString(key) 829 require.NoError(t, err) 830 831 _, _, err = mgr.VerifyToken(tokenString) 832 // This is the error thrown when the test server's certificate _is_ being verified. 833 assert.NotContains(t, err.Error(), "certificate is not trusted") 834 assert.NotContains(t, err.Error(), "certificate signed by unknown authority") 835 }) 836 837 t.Run("OIDC provider is external, audience is not specified", func(t *testing.T) { 838 config := map[string]string{ 839 "url": "", 840 "oidc.config": fmt.Sprintf(` 841 name: Test 842 issuer: %s 843 clientID: xxx 844 clientSecret: yyy 845 requestedScopes: ["oidc"]`, oidcTestServer.URL), 846 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 847 } 848 849 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 850 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 851 secretConfig := map[string][]byte{ 852 "tls.crt": utiltest.Cert, 853 "tls.key": utiltest.PrivateKey, 854 } 855 856 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 857 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 858 mgr.verificationDelayNoiseEnabled = false 859 860 claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 861 claims.Issuer = oidcTestServer.URL 862 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 863 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 864 require.NoError(t, err) 865 tokenString, err := token.SignedString(key) 866 require.NoError(t, err) 867 868 _, _, err = mgr.VerifyToken(tokenString) 869 require.Error(t, err) 870 }) 871 872 t.Run("OIDC provider is external, audience is not specified, absent audience is allowed", func(t *testing.T) { 873 config := map[string]string{ 874 "url": "", 875 "oidc.config": fmt.Sprintf(` 876 name: Test 877 issuer: %s 878 clientID: xxx 879 clientSecret: yyy 880 requestedScopes: ["oidc"] 881 skipAudienceCheckWhenTokenHasNoAudience: true`, oidcTestServer.URL), 882 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 883 } 884 885 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 886 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 887 secretConfig := map[string][]byte{ 888 "tls.crt": utiltest.Cert, 889 "tls.key": utiltest.PrivateKey, 890 } 891 892 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 893 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 894 mgr.verificationDelayNoiseEnabled = false 895 896 claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 897 claims.Issuer = oidcTestServer.URL 898 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 899 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 900 require.NoError(t, err) 901 tokenString, err := token.SignedString(key) 902 require.NoError(t, err) 903 904 _, _, err = mgr.VerifyToken(tokenString) 905 require.NoError(t, err) 906 }) 907 908 t.Run("OIDC provider is external, audience is not specified but is required", func(t *testing.T) { 909 config := map[string]string{ 910 "url": "", 911 "oidc.config": fmt.Sprintf(` 912 name: Test 913 issuer: %s 914 clientID: xxx 915 clientSecret: yyy 916 requestedScopes: ["oidc"] 917 skipAudienceCheckWhenTokenHasNoAudience: false`, oidcTestServer.URL), 918 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 919 } 920 921 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 922 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 923 secretConfig := map[string][]byte{ 924 "tls.crt": utiltest.Cert, 925 "tls.key": utiltest.PrivateKey, 926 } 927 928 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 929 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 930 mgr.verificationDelayNoiseEnabled = false 931 932 claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 933 claims.Issuer = oidcTestServer.URL 934 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 935 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 936 require.NoError(t, err) 937 tokenString, err := token.SignedString(key) 938 require.NoError(t, err) 939 940 _, _, err = mgr.VerifyToken(tokenString) 941 require.Error(t, err) 942 assert.ErrorIs(t, err, common.ErrTokenVerification) 943 }) 944 945 t.Run("OIDC provider is external, audience is client ID, no allowed list specified", func(t *testing.T) { 946 config := map[string]string{ 947 "url": "", 948 "oidc.config": fmt.Sprintf(` 949 name: Test 950 issuer: %s 951 clientID: xxx 952 clientSecret: yyy 953 requestedScopes: ["oidc"]`, oidcTestServer.URL), 954 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 955 } 956 957 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 958 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 959 secretConfig := map[string][]byte{ 960 "tls.crt": utiltest.Cert, 961 "tls.key": utiltest.PrivateKey, 962 } 963 964 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 965 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 966 mgr.verificationDelayNoiseEnabled = false 967 968 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"xxx"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 969 claims.Issuer = oidcTestServer.URL 970 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 971 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 972 require.NoError(t, err) 973 tokenString, err := token.SignedString(key) 974 require.NoError(t, err) 975 976 _, _, err = mgr.VerifyToken(tokenString) 977 require.NoError(t, err) 978 }) 979 980 t.Run("OIDC provider is external, audience is in allowed list", func(t *testing.T) { 981 config := map[string]string{ 982 "url": "", 983 "oidc.config": fmt.Sprintf(` 984 name: Test 985 issuer: %s 986 clientID: xxx 987 clientSecret: yyy 988 requestedScopes: ["oidc"] 989 allowedAudiences: 990 - something`, oidcTestServer.URL), 991 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 992 } 993 994 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 995 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 996 secretConfig := map[string][]byte{ 997 "tls.crt": utiltest.Cert, 998 "tls.key": utiltest.PrivateKey, 999 } 1000 1001 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1002 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1003 mgr.verificationDelayNoiseEnabled = false 1004 1005 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1006 claims.Issuer = oidcTestServer.URL 1007 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1008 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 1009 require.NoError(t, err) 1010 tokenString, err := token.SignedString(key) 1011 require.NoError(t, err) 1012 1013 _, _, err = mgr.VerifyToken(tokenString) 1014 require.NoError(t, err) 1015 }) 1016 1017 t.Run("OIDC provider is external, audience is not in allowed list", func(t *testing.T) { 1018 config := map[string]string{ 1019 "url": "", 1020 "oidc.config": fmt.Sprintf(` 1021 name: Test 1022 issuer: %s 1023 clientID: xxx 1024 clientSecret: yyy 1025 requestedScopes: ["oidc"] 1026 allowedAudiences: 1027 - something-else`, oidcTestServer.URL), 1028 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 1029 } 1030 1031 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 1032 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 1033 secretConfig := map[string][]byte{ 1034 "tls.crt": utiltest.Cert, 1035 "tls.key": utiltest.PrivateKey, 1036 } 1037 1038 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1039 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1040 mgr.verificationDelayNoiseEnabled = false 1041 1042 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1043 claims.Issuer = oidcTestServer.URL 1044 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1045 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 1046 require.NoError(t, err) 1047 tokenString, err := token.SignedString(key) 1048 require.NoError(t, err) 1049 1050 _, _, err = mgr.VerifyToken(tokenString) 1051 require.Error(t, err) 1052 assert.ErrorIs(t, err, common.ErrTokenVerification) 1053 }) 1054 1055 t.Run("OIDC provider is external, audience is not client ID, and there is no allow list", func(t *testing.T) { 1056 config := map[string]string{ 1057 "url": "", 1058 "oidc.config": fmt.Sprintf(` 1059 name: Test 1060 issuer: %s 1061 clientID: xxx 1062 clientSecret: yyy 1063 requestedScopes: ["oidc"]`, oidcTestServer.URL), 1064 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 1065 } 1066 1067 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 1068 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 1069 secretConfig := map[string][]byte{ 1070 "tls.crt": utiltest.Cert, 1071 "tls.key": utiltest.PrivateKey, 1072 } 1073 1074 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1075 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1076 mgr.verificationDelayNoiseEnabled = false 1077 1078 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1079 claims.Issuer = oidcTestServer.URL 1080 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1081 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 1082 require.NoError(t, err) 1083 tokenString, err := token.SignedString(key) 1084 require.NoError(t, err) 1085 1086 _, _, err = mgr.VerifyToken(tokenString) 1087 require.Error(t, err) 1088 assert.ErrorIs(t, err, common.ErrTokenVerification) 1089 }) 1090 1091 t.Run("OIDC provider is external, audience is specified, but allow list is empty", func(t *testing.T) { 1092 config := map[string]string{ 1093 "url": "", 1094 "oidc.config": fmt.Sprintf(` 1095 name: Test 1096 issuer: %s 1097 clientID: xxx 1098 clientSecret: yyy 1099 requestedScopes: ["oidc"] 1100 allowedAudiences: []`, oidcTestServer.URL), 1101 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 1102 } 1103 1104 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 1105 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 1106 secretConfig := map[string][]byte{ 1107 "tls.crt": utiltest.Cert, 1108 "tls.key": utiltest.PrivateKey, 1109 } 1110 1111 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1112 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1113 mgr.verificationDelayNoiseEnabled = false 1114 1115 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"something"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1116 claims.Issuer = oidcTestServer.URL 1117 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1118 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 1119 require.NoError(t, err) 1120 tokenString, err := token.SignedString(key) 1121 require.NoError(t, err) 1122 1123 _, _, err = mgr.VerifyToken(tokenString) 1124 require.Error(t, err) 1125 assert.ErrorIs(t, err, common.ErrTokenVerification) 1126 }) 1127 1128 // Make sure the logic works to allow any of the allowed audiences, not just the first one. 1129 t.Run("OIDC provider is external, audience is specified, actual audience isn't the first allowed audience", func(t *testing.T) { 1130 config := map[string]string{ 1131 "url": "", 1132 "oidc.config": fmt.Sprintf(` 1133 name: Test 1134 issuer: %s 1135 clientID: xxx 1136 clientSecret: yyy 1137 requestedScopes: ["oidc"] 1138 allowedAudiences: ["aud-a", "aud-b"]`, oidcTestServer.URL), 1139 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 1140 } 1141 1142 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 1143 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 1144 secretConfig := map[string][]byte{ 1145 "tls.crt": utiltest.Cert, 1146 "tls.key": utiltest.PrivateKey, 1147 } 1148 1149 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1150 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1151 mgr.verificationDelayNoiseEnabled = false 1152 1153 claims := jwt.RegisteredClaims{Audience: jwt.ClaimStrings{"aud-b"}, Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1154 claims.Issuer = oidcTestServer.URL 1155 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1156 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey) 1157 require.NoError(t, err) 1158 tokenString, err := token.SignedString(key) 1159 require.NoError(t, err) 1160 1161 _, _, err = mgr.VerifyToken(tokenString) 1162 require.NoError(t, err) 1163 }) 1164 1165 t.Run("OIDC provider is external, audience is not specified, token is signed with the wrong key", func(t *testing.T) { 1166 config := map[string]string{ 1167 "url": "", 1168 "oidc.config": fmt.Sprintf(` 1169 name: Test 1170 issuer: %s 1171 clientID: xxx 1172 clientSecret: yyy 1173 requestedScopes: ["oidc"]`, oidcTestServer.URL), 1174 "oidc.tls.insecure.skip.verify": "true", // This isn't what we're testing. 1175 } 1176 1177 // This is not actually used in the test. The test only calls the OIDC test server. But a valid cert/key pair 1178 // must be set to test VerifyToken's behavior when Argo CD is configured with TLS enabled. 1179 secretConfig := map[string][]byte{ 1180 "tls.crt": utiltest.Cert, 1181 "tls.key": utiltest.PrivateKey, 1182 } 1183 1184 settingsMgr := settings.NewSettingsManager(t.Context(), getKubeClientWithConfig(config, secretConfig), "argocd") 1185 mgr := NewSessionManager(settingsMgr, getProjLister(), "", nil, NewUserStateStorage(nil)) 1186 mgr.verificationDelayNoiseEnabled = false 1187 1188 claims := jwt.RegisteredClaims{Subject: "admin", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24))} 1189 claims.Issuer = oidcTestServer.URL 1190 token := jwt.NewWithClaims(jwt.SigningMethodRS512, claims) 1191 key, err := jwt.ParseRSAPrivateKeyFromPEM(utiltest.PrivateKey2) 1192 require.NoError(t, err) 1193 tokenString, err := token.SignedString(key) 1194 require.NoError(t, err) 1195 1196 _, _, err = mgr.VerifyToken(tokenString) 1197 require.Error(t, err) 1198 assert.ErrorIs(t, err, common.ErrTokenVerification) 1199 }) 1200 } 1201 1202 func Test_PickFailureAttemptWhenOverflowed(t *testing.T) { 1203 t.Run("Not pick admin user from the queue", func(t *testing.T) { 1204 failures := map[string]LoginAttempts{ 1205 "admin": { 1206 FailCount: 1, 1207 }, 1208 "test2": { 1209 FailCount: 1, 1210 }, 1211 } 1212 1213 // inside pickRandomNonAdminLoginFailure, it uses random, so we need to test it multiple times 1214 for i := 0; i < 1000; i++ { 1215 user := pickRandomNonAdminLoginFailure(failures, "test") 1216 assert.Equal(t, "test2", *user) 1217 } 1218 }) 1219 1220 t.Run("Not pick admin user and current user from the queue", func(t *testing.T) { 1221 failures := map[string]LoginAttempts{ 1222 "test": { 1223 FailCount: 1, 1224 }, 1225 "admin": { 1226 FailCount: 1, 1227 }, 1228 "test2": { 1229 FailCount: 1, 1230 }, 1231 } 1232 1233 // inside pickRandomNonAdminLoginFailure, it uses random, so we need to test it multiple times 1234 for i := 0; i < 1000; i++ { 1235 user := pickRandomNonAdminLoginFailure(failures, "test") 1236 assert.Equal(t, "test2", *user) 1237 } 1238 }) 1239 }