From a94647d5ccdc06f37610ed4722adc81b9f533ee8 Mon Sep 17 00:00:00 2001 From: Misi Date: Wed, 16 Jul 2025 21:13:31 +0200 Subject: [PATCH] Auth: Remove ssoSettingsSAML feature toggle (#108109) * Remove ssoSettingsSAML feature toggle * Remove from docs + align tests * Update workspace * revert go.mod go.sum change * make update-workspace without enterprise linked --- .../developers/http_api/sso-settings.md | 4 - .../feature-toggles/index.md | 1 - .../configure-authentication/saml/_index.md | 4 - .../saml/saml-configuration-options/_index.md | 4 - go.mod | 2 +- go.sum | 4 +- .../src/types/featureToggles.gen.ts | 5 -- pkg/services/featuremgmt/registry.go | 8 -- pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../ssosettings/ssosettingsimpl/service.go | 7 +- .../ssosettingsimpl/service_test.go | 75 +++++++------------ 13 files changed, 35 insertions(+), 87 deletions(-) diff --git a/docs/sources/developers/http_api/sso-settings.md b/docs/sources/developers/http_api/sso-settings.md index ce1aaf3756e..a99a4d6f0fc 100644 --- a/docs/sources/developers/http_api/sso-settings.md +++ b/docs/sources/developers/http_api/sso-settings.md @@ -22,10 +22,6 @@ title: SSO Settings API > If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information. -{{< admonition type="note" >}} -Available since Grafana 11. SAML support is in public preview behind the `ssoSettingsSAML` feature flag. -{{< /admonition >}} - The API can be used to create, update, delete, get, and list SSO Settings for OAuth2 and SAML. The settings managed by this API are stored in the database and override diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 303cd8e4839..a471a92f08c 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -58,7 +58,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `groupToNestedTableTransformation` | Enables the group to nested table transformation | Yes | | `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | Yes | | `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes | -| `ssoSettingsSAML` | Use the new SSO Settings API to configure the SAML connector | Yes | | `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes | | `newDashboardSharingComponent` | Enables the new sharing drawer design | Yes | | `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | | diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/_index.md index 4a846a622e2..6dd7f45042d 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/_index.md @@ -39,10 +39,6 @@ If you are using Okta or Azure AD as Identity Provider, see the following docume - [Configure SAML with Azure AD](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-azuread/) - [Configure SAML with Okta](https://grafana.com/docs/grafana//setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-okta/) -{{< admonition type="note" >}} -The API and Terraform support are available in Public Preview in Grafana v11.1 behind the `ssoSettingsSAML` feature toggle. -{{< /admonition >}} - All methods offer the same configuration options. However, if you want to keep all of Grafana authentication settings in one place, use the Grafana configuration file or the Terraform provider. If you are a Grafana Cloud user, you do not have access to Grafana configuration file. Instead, configure SAML through the other methods. {{< admonition type="note" >}} diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/_index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/_index.md index 752282d441d..1a9a5fc80d5 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/_index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/_index.md @@ -79,10 +79,6 @@ allowed_organizations = Engineering, Sales ## Example SAML configuration in Terraform -{{< admonition type="note" >}} -Available in Public Preview in Grafana v11.1 behind the `ssoSettingsSAML` feature toggle. Supported in the Terraform provider since v2.17.0. -{{< /admonition >}} - ```terraform resource "grafana_sso_settings" "saml_sso_settings" { provider_name = "saml" diff --git a/go.mod b/go.mod index b94a1f12ceb..8bcc5a4a9c9 100644 --- a/go.mod +++ b/go.mod @@ -230,7 +230,7 @@ require ( require ( github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b // @grafana/plugins-platform-backend github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b // @grafana/alerting-backend - github.com/grafana/grafana/apps/dashboard v0.0.0-20250716152245-2202c99d7030 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad + github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b // @grafana/grafana-search-and-storage github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b // @grafana/identity-access-team github.com/grafana/grafana/apps/investigations v0.0.0-20250627191313-2f1a6ae1712b // @fcjack @matryer diff --git a/go.sum b/go.sum index 950bd732d5f..d35aba9b5a4 100644 --- a/go.sum +++ b/go.sum @@ -1611,8 +1611,8 @@ github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b h1:8o github.com/grafana/grafana/apps/advisor v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:q+h3HbmqU/PposW6lq8cMle1v8vuyX1LCMrGzbabHxc= github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b h1:jr+C3epmjhd5Yyob4P1Z/dPaW4LRTkU5UJLXsI4eaeM= github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:WpI7TCck4P2wKTO2WJLBRcfOWvUGvTdxYu3QqS3z7jM= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250716152245-2202c99d7030 h1:ravSwl2XJacO/o2u3IVaMacd0NpL1Msf9ENLcLbLwO0= -github.com/grafana/grafana/apps/dashboard v0.0.0-20250716152245-2202c99d7030/go.mod h1:1XWiRSVuDQiayapHhQiDc4S4e9GzEZgg/3GeNCuDgn4= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20 h1:2aeZOXOdJID5BbrUtBWG+yGUU4wDXkqwsq9drSalrDE= +github.com/grafana/grafana/apps/dashboard v0.0.0-20250716154214-974103c6fa20/go.mod h1:1XWiRSVuDQiayapHhQiDc4S4e9GzEZgg/3GeNCuDgn4= github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b h1:31MwoIKKT9Ay0ZjbT4lkfcPijiWogUWzXs2EjrCgodI= github.com/grafana/grafana/apps/folder v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:dLtYBp1pza5HYalezNvzlP8JDeKrZ5BKTonDgEOE0NY= github.com/grafana/grafana/apps/iam v0.0.0-20250627191313-2f1a6ae1712b h1:NV8v9xdM/pzjjy+1cLqUseia3bYcvQGh88vZdMW/jA0= diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 42ffa7f1491..da95cb93db2 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -509,11 +509,6 @@ export interface FeatureToggles { */ scopeFilters?: boolean; /** - * Use the new SSO Settings API to configure the SAML connector - * @default true - */ - ssoSettingsSAML?: boolean; - /** * Require that sub claims is present in oauth tokens. */ oauthRequireSubClaim?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 172161a9cf8..ed2d1ad9c8b 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -859,14 +859,6 @@ var ( HideFromDocs: true, HideFromAdminPage: true, }, - { - Name: "ssoSettingsSAML", - Description: "Use the new SSO Settings API to configure the SAML connector", - Stage: FeatureStageGeneralAvailability, - Owner: identityAccessTeam, - Expression: "true", - AllowSelfServe: true, - }, { Name: "oauthRequireSubClaim", Description: "Require that sub claims is present in oauth tokens.", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index db43824df5f..df7f2b9dd58 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -112,7 +112,6 @@ kubernetesAggregatorCapTokenAuth,experimental,@grafana/grafana-app-platform-squa expressionParser,experimental,@grafana/grafana-app-platform-squad,false,true,false groupByVariable,experimental,@grafana/dashboards-squad,false,false,false scopeFilters,experimental,@grafana/dashboards-squad,false,false,false -ssoSettingsSAML,GA,@grafana/identity-access-team,false,false,false oauthRequireSubClaim,experimental,@grafana/identity-access-team,false,false,false newDashboardWithFiltersAndGroupBy,experimental,@grafana/dashboards-squad,false,false,false cloudWatchNewLabelParsing,GA,@grafana/aws-datasources,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 4e5aaaf058c..67f5c3e0669 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -459,10 +459,6 @@ const ( // Enables the use of scope filters in Grafana FlagScopeFilters = "scopeFilters" - // FlagSsoSettingsSAML - // Use the new SSO Settings API to configure the SAML connector - FlagSsoSettingsSAML = "ssoSettingsSAML" - // FlagOauthRequireSubClaim // Require that sub claims is present in oauth tokens. FlagOauthRequireSubClaim = "oauthRequireSubClaim" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 362761a9ee1..c545d85e6ee 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -3066,7 +3066,8 @@ "metadata": { "name": "ssoSettingsSAML", "resourceVersion": "1750434297879", - "creationTimestamp": "2024-03-14T11:04:45Z" + "creationTimestamp": "2024-03-14T11:04:45Z", + "deletionTimestamp": "2025-07-15T09:43:39Z" }, "spec": { "description": "Use the new SSO Settings API to configure the SAML connector", diff --git a/pkg/services/ssosettings/ssosettingsimpl/service.go b/pkg/services/ssosettings/ssosettingsimpl/service.go index 458c0a21b6a..fd09a1481ff 100644 --- a/pkg/services/ssosettings/ssosettingsimpl/service.go +++ b/pkg/services/ssosettings/ssosettingsimpl/service.go @@ -68,11 +68,8 @@ func ProvideService(cfg *setting.Cfg, sqlStore db.DB, ac ac.AccessControl, if licensing.FeatureEnabled(social.SAMLProviderName) { fbStrategies = append(fbStrategies, strategies.NewSAMLStrategy(settingsProvider)) - - if features.IsEnabledGlobally(featuremgmt.FlagSsoSettingsSAML) { - providersList = append(providersList, social.SAMLProviderName) - configurableProviders[social.SAMLProviderName] = true - } + providersList = append(providersList, social.SAMLProviderName) + configurableProviders[social.SAMLProviderName] = true } store := database.ProvideStore(sqlStore) diff --git a/pkg/services/ssosettings/ssosettingsimpl/service_test.go b/pkg/services/ssosettings/ssosettingsimpl/service_test.go index 4882f22674d..781ba148ea0 100644 --- a/pkg/services/ssosettings/ssosettingsimpl/service_test.go +++ b/pkg/services/ssosettings/ssosettingsimpl/service_test.go @@ -347,7 +347,7 @@ func TestService_GetForProvider(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, true, false, true, true) + env := setupTestEnv(t, true, false, true) if tc.setup != nil { tc.setup(env) } @@ -514,7 +514,7 @@ func TestService_GetForProviderWithRedactedSecrets(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, true) + env := setupTestEnv(t, false, false, true) if tc.setup != nil { tc.setup(env) } @@ -665,7 +665,7 @@ func TestService_List(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) if tc.setup != nil { tc.setup(env) } @@ -967,7 +967,7 @@ func TestService_ListWithRedactedSecrets(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) if tc.setup != nil { tc.setup(env) } @@ -991,7 +991,7 @@ func TestService_Upsert(t *testing.T) { t.Run("successfully upsert SSO settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1054,7 +1054,7 @@ func TestService_Upsert(t *testing.T) { t.Run("successfully upsert SSO settings for LDAP", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, true) + env := setupTestEnv(t, false, false, true) provider := social.LDAPProviderName settings := models.SSOSettings{ @@ -1124,7 +1124,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if provider is not configurable", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.GrafanaComProviderName settings := &models.SSOSettings{ @@ -1147,7 +1147,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if provider was not found in reloadables", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := &models.SSOSettings{ @@ -1171,7 +1171,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if validation fails", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1195,7 +1195,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if a fallback strategy is not available for the provider", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) settings := &models.SSOSettings{ Provider: social.AzureADProviderName, @@ -1216,7 +1216,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if a secret does not have the type string", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.OktaProviderName settings := models.SSOSettings{ @@ -1239,7 +1239,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if secrets encryption failed", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.OktaProviderName settings := models.SSOSettings{ @@ -1264,7 +1264,7 @@ func TestService_Upsert(t *testing.T) { t.Run("should not update the current secret if the secret has not been updated", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1308,7 +1308,7 @@ func TestService_Upsert(t *testing.T) { t.Run("run validation with all new and current secrets available in settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1361,7 +1361,7 @@ func TestService_Upsert(t *testing.T) { t.Run("returns error if store failed to upsert settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1393,7 +1393,7 @@ func TestService_Upsert(t *testing.T) { t.Run("successfully upsert SSO settings if reload fails", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName settings := models.SSOSettings{ @@ -1426,7 +1426,7 @@ func TestService_Delete(t *testing.T) { t.Run("successfully delete SSO settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) var wg sync.WaitGroup wg.Add(1) @@ -1464,7 +1464,7 @@ func TestService_Delete(t *testing.T) { t.Run("return error if SSO setting was not found for the specified provider", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName reloadable := ssosettingstests.NewMockReloadable(t) @@ -1480,7 +1480,7 @@ func TestService_Delete(t *testing.T) { t.Run("should not delete the SSO settings if the provider is not configurable", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) env.cfg.SSOSettingsConfigurableProviders = map[string]bool{social.AzureADProviderName: true} provider := social.GrafanaComProviderName @@ -1493,7 +1493,7 @@ func TestService_Delete(t *testing.T) { t.Run("return error when store fails to delete the SSO settings for the specified provider", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName env.store.ExpectedError = errors.New("delete sso settings failed") @@ -1506,7 +1506,7 @@ func TestService_Delete(t *testing.T) { t.Run("return successfully when the deletion was successful but reloading the settings fail", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := social.AzureADProviderName reloadable := ssosettingstests.NewMockReloadable(t) @@ -1523,7 +1523,7 @@ func TestService_Delete(t *testing.T) { t.Run("should delete SAML SettingsProvider while deleting SAML SSO Settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, true, true, true, false) + env := setupTestEnv(t, true, true, false) mockProvider := &settingtest.MockProvider{} mockProvider.On("Current", mock.Anything).Return(setting.SettingsBag{ @@ -1598,7 +1598,7 @@ func TestService_DoReload(t *testing.T) { t.Run("successfully reload settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) settingsList := []*models.SSOSettings{ { @@ -1638,7 +1638,7 @@ func TestService_DoReload(t *testing.T) { t.Run("successfully reload settings when some providers have empty settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) settingsList := []*models.SSOSettings{ { @@ -1668,7 +1668,7 @@ func TestService_DoReload(t *testing.T) { t.Run("failed fetching the SSO settings", func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) provider := "github" @@ -1798,7 +1798,7 @@ func TestService_decryptSecrets(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, false, false, false, false) + env := setupTestEnv(t, false, false, false) if tc.setup != nil { tc.setup(env) @@ -1823,7 +1823,6 @@ func Test_ProviderService(t *testing.T) { tests := []struct { name string isLicenseEnabled bool - samlEnabled bool expectedProvidersList []string strategiesLength int }{ @@ -1841,24 +1840,9 @@ func Test_ProviderService(t *testing.T) { }, strategiesLength: 2, }, - { - name: "should return all fallback strategies and it should return all OAuth providers but not SAML because the licensing feature is enabled but the configurable provider is not setup", - isLicenseEnabled: true, - expectedProvidersList: []string{ - "github", - "gitlab", - "google", - "generic_oauth", - "grafana_com", - "azuread", - "okta", - }, - strategiesLength: 3, - }, { name: "should return all fallback strategies and it should return all OAuth providers and SAML because the licensing feature is enabled and the provider is setup", isLicenseEnabled: true, - samlEnabled: true, expectedProvidersList: []string{ "github", "gitlab", @@ -1877,7 +1861,7 @@ func Test_ProviderService(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - env := setupTestEnv(t, tc.isLicenseEnabled, true, tc.samlEnabled, false) + env := setupTestEnv(t, tc.isLicenseEnabled, true, false) require.Equal(t, tc.expectedProvidersList, env.service.providersList) require.Equal(t, tc.strategiesLength, len(env.service.fbStrategies)) @@ -1885,7 +1869,7 @@ func Test_ProviderService(t *testing.T) { } } -func setupTestEnv(t *testing.T, isLicensingEnabled, keepFallbackStratergies, samlEnabled bool, ldapEnabled bool) testEnv { +func setupTestEnv(t *testing.T, isLicensingEnabled, keepFallbackStratergies bool, ldapEnabled bool) testEnv { t.Helper() store := ssosettingstests.NewFakeStore() @@ -1916,9 +1900,6 @@ func setupTestEnv(t *testing.T, isLicensingEnabled, keepFallbackStratergies, sam licensing.On("FeatureEnabled", "saml").Return(isLicensingEnabled) features := make([]any, 0) - if samlEnabled { - features = append(features, featuremgmt.FlagSsoSettingsSAML) - } if ldapEnabled { features = append(features, featuremgmt.FlagSsoSettingsLDAP) }