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
pull/108132/merge
Misi 4 days ago committed by GitHub
parent 032ea5d5b8
commit a94647d5cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      docs/sources/developers/http_api/sso-settings.md
  2. 1
      docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
  3. 4
      docs/sources/setup-grafana/configure-security/configure-authentication/saml/_index.md
  4. 4
      docs/sources/setup-grafana/configure-security/configure-authentication/saml/saml-configuration-options/_index.md
  5. 2
      go.mod
  6. 4
      go.sum
  7. 5
      packages/grafana-data/src/types/featureToggles.gen.ts
  8. 8
      pkg/services/featuremgmt/registry.go
  9. 1
      pkg/services/featuremgmt/toggles_gen.csv
  10. 4
      pkg/services/featuremgmt/toggles_gen.go
  11. 3
      pkg/services/featuremgmt/toggles_gen.json
  12. 7
      pkg/services/ssosettings/ssosettingsimpl/service.go
  13. 75
      pkg/services/ssosettings/ssosettingsimpl/service_test.go

@ -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

@ -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. | |

@ -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/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml/configure-saml-with-azuread/)
- [Configure SAML with Okta](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/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" >}}

@ -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"

@ -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

@ -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=

@ -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;

@ -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.",

@ -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

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
112 expressionParser experimental @grafana/grafana-app-platform-squad false true false
113 groupByVariable experimental @grafana/dashboards-squad false false false
114 scopeFilters experimental @grafana/dashboards-squad false false false
ssoSettingsSAML GA @grafana/identity-access-team false false false
115 oauthRequireSubClaim experimental @grafana/identity-access-team false false false
116 newDashboardWithFiltersAndGroupBy experimental @grafana/dashboards-squad false false false
117 cloudWatchNewLabelParsing GA @grafana/aws-datasources false false false

@ -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"

@ -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",

@ -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)

@ -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)
}

Loading…
Cancel
Save