@@ -322,3 +322,207 @@ def test_update_sso_settings(self, mock_proxy_config, mock_auth, monkeypatch):
322
322
323
323
# Verify save_config was called exactly once
324
324
assert mock_proxy_config ["save_call_count" ]() == 1
325
+
326
+ def test_update_sso_settings_with_null_values_clears_env_vars (
327
+ self , mock_proxy_config , mock_auth , monkeypatch
328
+ ):
329
+ """Test that updating SSO settings with null values clears environment variables"""
330
+ monkeypatch .setenv ("LITELLM_SALT_KEY" , "test_salt_key" )
331
+
332
+ # First, verify we have existing environment variables
333
+ initial_config = mock_proxy_config ["config" ]
334
+ assert "GOOGLE_CLIENT_ID" in initial_config ["environment_variables" ]
335
+ assert "MICROSOFT_CLIENT_ID" in initial_config ["environment_variables" ]
336
+
337
+ # Set some initial environment variables for runtime testing
338
+ monkeypatch .setenv ("GOOGLE_CLIENT_ID" , "test_existing_google_id" )
339
+ monkeypatch .setenv ("MICROSOFT_CLIENT_ID" , "test_existing_microsoft_id" )
340
+
341
+ # Send SSO settings with null values to clear them
342
+ clear_sso_settings = {
343
+ "google_client_id" : None ,
344
+ "google_client_secret" : None ,
345
+ "microsoft_client_id" : None ,
346
+ "microsoft_client_secret" : None ,
347
+ "microsoft_tenant" : None ,
348
+ "generic_client_id" : None ,
349
+ "generic_client_secret" : None ,
350
+ "generic_authorization_endpoint" : None ,
351
+ "generic_token_endpoint" : None ,
352
+ "generic_userinfo_endpoint" : None ,
353
+ "proxy_base_url" : None ,
354
+ "user_email" : None ,
355
+ "sso_provider" : None ,
356
+ }
357
+
358
+ response = client .patch ("/update/sso_settings" , json = clear_sso_settings )
359
+
360
+ assert response .status_code == 200
361
+ data = response .json ()
362
+ assert data ["status" ] == "success"
363
+
364
+ # Verify that environment variables were cleared from config
365
+ updated_config = mock_proxy_config ["config" ]
366
+
367
+ # These should be removed from environment_variables
368
+ assert "GOOGLE_CLIENT_ID" not in updated_config ["environment_variables" ]
369
+ assert "GOOGLE_CLIENT_SECRET" not in updated_config ["environment_variables" ]
370
+ assert "MICROSOFT_CLIENT_ID" not in updated_config ["environment_variables" ]
371
+ assert "MICROSOFT_CLIENT_SECRET" not in updated_config ["environment_variables" ]
372
+ assert "MICROSOFT_TENANT" not in updated_config ["environment_variables" ]
373
+ assert "PROXY_BASE_URL" not in updated_config ["environment_variables" ]
374
+
375
+ # Verify that runtime environment variables were cleared
376
+ assert "GOOGLE_CLIENT_ID" not in os .environ
377
+ assert "MICROSOFT_CLIENT_ID" not in os .environ
378
+
379
+ # Verify user_email was cleared from general_settings
380
+ assert updated_config ["general_settings" ].get ("proxy_admin_email" ) is None
381
+
382
+ # Verify save_config was called
383
+ assert mock_proxy_config ["save_call_count" ]() == 1
384
+
385
+ def test_update_sso_settings_with_empty_strings_clears_env_vars (
386
+ self , mock_proxy_config , mock_auth , monkeypatch
387
+ ):
388
+ """Test that updating SSO settings with empty strings also clears environment variables"""
389
+ monkeypatch .setenv ("LITELLM_SALT_KEY" , "test_salt_key" )
390
+
391
+ # Set some initial environment variables for runtime testing
392
+ monkeypatch .setenv ("GOOGLE_CLIENT_ID" , "test_existing_google_id" )
393
+ monkeypatch .setenv ("MICROSOFT_CLIENT_SECRET" , "test_existing_microsoft_secret" )
394
+
395
+ # Send SSO settings with empty strings to clear them
396
+ clear_sso_settings = {
397
+ "google_client_id" : "" ,
398
+ "google_client_secret" : "" ,
399
+ "microsoft_client_secret" : "" ,
400
+ "proxy_base_url" : "" ,
401
+ "user_email" : "" ,
402
+ }
403
+
404
+ response = client .patch ("/update/sso_settings" , json = clear_sso_settings )
405
+
406
+ assert response .status_code == 200
407
+ data = response .json ()
408
+ assert data ["status" ] == "success"
409
+
410
+ # Verify that environment variables with empty strings were cleared from config
411
+ updated_config = mock_proxy_config ["config" ]
412
+ assert "GOOGLE_CLIENT_ID" not in updated_config ["environment_variables" ]
413
+ assert "GOOGLE_CLIENT_SECRET" not in updated_config ["environment_variables" ]
414
+ assert "MICROSOFT_CLIENT_SECRET" not in updated_config ["environment_variables" ]
415
+ assert "PROXY_BASE_URL" not in updated_config ["environment_variables" ]
416
+
417
+ # Verify that runtime environment variables were cleared
418
+ assert "GOOGLE_CLIENT_ID" not in os .environ
419
+ assert "MICROSOFT_CLIENT_SECRET" not in os .environ
420
+
421
+ # Verify user_email was cleared from general_settings
422
+ assert updated_config ["general_settings" ].get ("proxy_admin_email" ) is None
423
+
424
+ # Verify save_config was called
425
+ assert mock_proxy_config ["save_call_count" ]() == 1
426
+
427
+ def test_update_sso_settings_mixed_null_and_valid_values (
428
+ self , mock_proxy_config , mock_auth , monkeypatch
429
+ ):
430
+ """Test updating SSO settings with mix of null and valid values"""
431
+ monkeypatch .setenv ("LITELLM_SALT_KEY" , "test_salt_key" )
432
+
433
+ # Set some initial environment variables
434
+ monkeypatch .setenv ("GOOGLE_CLIENT_ID" , "old_google_id" )
435
+ monkeypatch .setenv ("MICROSOFT_CLIENT_ID" , "old_microsoft_id" )
436
+ monkeypatch .setenv ("PROXY_BASE_URL" , "old_proxy_url" )
437
+
438
+ # Send mixed SSO settings - some null, some valid
439
+ mixed_sso_settings = {
440
+ "google_client_id" : "new_google_client_id" , # Valid value
441
+ "google_client_secret" : None , # Null to clear
442
+ "microsoft_client_id" : None , # Null to clear
443
+ "microsoft_client_secret" : "new_microsoft_secret" , # Valid value
444
+ "proxy_base_url" : "https://newproxy.com" , # Valid value
445
+ "user_email" : None , # Null to clear
446
+ }
447
+
448
+ response = client .patch ("/update/sso_settings" , json = mixed_sso_settings )
449
+
450
+ assert response .status_code == 200
451
+ data = response .json ()
452
+ assert data ["status" ] == "success"
453
+
454
+ # Verify the config was updated correctly
455
+ updated_config = mock_proxy_config ["config" ]
456
+
457
+ # Valid values should be set
458
+ assert (
459
+ updated_config ["environment_variables" ]["GOOGLE_CLIENT_ID" ]
460
+ != "new_google_client_id"
461
+ ) # Encrypted
462
+ assert (
463
+ updated_config ["environment_variables" ]["MICROSOFT_CLIENT_SECRET" ]
464
+ != "new_microsoft_secret"
465
+ ) # Encrypted
466
+ assert (
467
+ updated_config ["environment_variables" ]["PROXY_BASE_URL" ]
468
+ != "https://newproxy.com"
469
+ ) # Encrypted
470
+
471
+ # Null values should be cleared
472
+ assert "GOOGLE_CLIENT_SECRET" not in updated_config ["environment_variables" ]
473
+ assert "MICROSOFT_CLIENT_ID" not in updated_config ["environment_variables" ]
474
+
475
+ # Verify runtime environment variables
476
+ assert os .environ .get ("GOOGLE_CLIENT_ID" ) == "new_google_client_id"
477
+ assert os .environ .get ("MICROSOFT_CLIENT_SECRET" ) == "new_microsoft_secret"
478
+ assert "GOOGLE_CLIENT_SECRET" not in os .environ
479
+ assert "MICROSOFT_CLIENT_ID" not in os .environ
480
+
481
+ # Verify user_email was cleared from general_settings
482
+ assert updated_config ["general_settings" ].get ("proxy_admin_email" ) is None
483
+
484
+ # Verify save_config was called
485
+ assert mock_proxy_config ["save_call_count" ]() == 1
486
+
487
+ def test_update_sso_settings_ui_access_mode_handling (
488
+ self , mock_proxy_config , mock_auth , monkeypatch
489
+ ):
490
+ """Test that ui_access_mode is handled correctly in general_settings"""
491
+ monkeypatch .setenv ("LITELLM_SALT_KEY" , "test_salt_key" )
492
+
493
+ # Test setting ui_access_mode
494
+ sso_settings_with_ui_mode = {
495
+ "ui_access_mode" : "admin_only" ,
496
+ "user_email" :
"[email protected] " ,
497
+ }
498
+
499
+ response = client .patch ("/update/sso_settings" , json = sso_settings_with_ui_mode )
500
+
501
+ assert response .status_code == 200
502
+ data = response .json ()
503
+ assert data ["status" ] == "success"
504
+
505
+ # Verify ui_access_mode was set in general_settings (not environment_variables)
506
+ updated_config = mock_proxy_config ["config" ]
507
+ assert updated_config ["general_settings" ]["ui_access_mode" ] == "admin_only"
508
+ assert (
509
+ updated_config [
"general_settings" ][
"proxy_admin_email" ]
== "[email protected] "
510
+ )
511
+
512
+ # Verify ui_access_mode is NOT in environment_variables
513
+ assert "ui_access_mode" not in updated_config ["environment_variables" ]
514
+
515
+ # Test clearing ui_access_mode
516
+ clear_ui_mode = {"ui_access_mode" : None , "user_email" : None }
517
+
518
+ response = client .patch ("/update/sso_settings" , json = clear_ui_mode )
519
+
520
+ assert response .status_code == 200
521
+
522
+ # Verify ui_access_mode and user_email were cleared
523
+ updated_config = mock_proxy_config ["config" ]
524
+ assert updated_config ["general_settings" ].get ("ui_access_mode" ) is None
525
+ assert updated_config ["general_settings" ].get ("proxy_admin_email" ) is None
526
+
527
+ # Verify save_config was called twice (once for each update)
528
+ assert mock_proxy_config ["save_call_count" ]() == 2
0 commit comments