Azure: Use SSO settings in plugin context (#112058)

* Bump grafana-azure-sdk-go

* Set override values

* Add Azure settings helper covering SSO cases

* Ensure Azure settings are correctly created

- Add mock for sso settings service
- Add tests
- Update wire

* Minor improvements

* Test updates

* Move fake implementation

* add interface to limit leakage

* rename

* work sync

* Fix wire

* Add fake provider

* Update tests

* Actually fix the workspace

* More go dependency fixes

* Update tests

* Update workspace, again

* Add missing tests

* Fix dependencies

* These dependencies..

* More dependency things

* Okay now dependencies really are fixed

* Lint

* Update pkg/services/pluginsintegration/pluginconfig/request.go

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>

---------

Co-authored-by: Will Browne <wbrowne@tcd.ie>
Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>
This commit is contained in:
Andreas Christou
2025-10-10 11:46:26 +01:00
committed by GitHub
co-authored by Misi Will Browne
parent 59459560c2
commit fa9d6be255
15 changed files with 652 additions and 75 deletions
@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/plugins/auth"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsso"
"github.com/grafana/grafana/pkg/setting"
)
@@ -19,7 +20,7 @@ func TestRequestConfigProvider_PluginRequestConfig_Defaults(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Equal(t, map[string]string{
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "0",
"GF_SQL_MAX_IDLE_CONNS_DEFAULT": "0",
@@ -133,7 +134,7 @@ func TestRequestConfigProvider_PluginRequestConfig(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
p := NewRequestConfigProvider(tc.cfg)
p := NewRequestConfigProvider(tc.cfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), tc.expected)
})
}
@@ -168,7 +169,7 @@ func TestRequestConfigProvider_PluginRequestConfig_featureToggles(t *testing.T)
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), tc.features)
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), tc.expectedConfig)
}
})
@@ -182,7 +183,7 @@ func TestRequestConfigProvider_PluginRequestConfig_appURL(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), map[string]string{"GF_APP_URL": "https://myorg.com/"})
})
}
@@ -198,7 +199,7 @@ func TestRequestConfigProvider_PluginRequestConfig_SQL(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), map[string]string{
"GF_SQL_ROW_LIMIT": "23",
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "24",
@@ -216,7 +217,7 @@ func TestRequestConfigProvider_PluginRequestConfig_SQL(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Equal(t, map[string]string{
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "0",
"GF_SQL_MAX_IDLE_CONNS_DEFAULT": "0",
@@ -233,7 +234,7 @@ func TestRequestConfigProvider_PluginRequestConfig_concurrentQueryCount(t *testi
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), map[string]string{"GF_CONCURRENT_QUERY_COUNT": "42"})
})
@@ -242,7 +243,7 @@ func TestRequestConfigProvider_PluginRequestConfig_concurrentQueryCount(t *testi
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.NotContains(t, p.PluginRequestConfig(context.Background(), "", nil), "GF_CONCURRENT_QUERY_COUNT")
})
@@ -253,7 +254,7 @@ func TestRequestConfigProvider_PluginRequestConfig_concurrentQueryCount(t *testi
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.NotContains(t, p.PluginRequestConfig(context.Background(), "", nil), "GF_CONCURRENT_QUERY_COUNT")
})
}
@@ -265,7 +266,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azureAuthEnabled(t *testing.T
Features: featuremgmt.WithFeatures(),
}
p := NewRequestConfigProvider(cfg)
p := NewRequestConfigProvider(cfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", nil), map[string]string{"GFAZPL_AZURE_AUTH_ENABLED": "true"})
})
@@ -274,7 +275,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azureAuthEnabled(t *testing.T
Features: featuremgmt.WithFeatures(),
}
p := NewRequestConfigProvider(cfg)
p := NewRequestConfigProvider(cfg, &fakeSSOSettingsProvider{})
require.NotContains(t, p.PluginRequestConfig(context.Background(), "", nil), "GFAZPL_AZURE_AUTH_ENABLED")
})
@@ -284,7 +285,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azureAuthEnabled(t *testing.T
Features: featuremgmt.WithFeatures(),
}
p := NewRequestConfigProvider(cfg)
p := NewRequestConfigProvider(cfg, &fakeSSOSettingsProvider{})
require.NotContains(t, p.PluginRequestConfig(context.Background(), "", nil), "GFAZPL_AZURE_AUTH_ENABLED")
})
}
@@ -322,7 +323,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "grafana-azure-monitor-datasource", nil), map[string]string{
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
@@ -356,7 +357,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "grafana-azure-monitor-datasource", nil), map[string]string{
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
@@ -384,7 +385,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
m := p.PluginRequestConfig(context.Background(), "", nil)
require.NotContains(t, m, "GFAZPL_AZURE_CLOUD")
require.NotContains(t, m, "GFAZPL_MANAGED_IDENTITY_ENABLED")
@@ -413,7 +414,7 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "test-datasource", nil), map[string]string{
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
@@ -433,6 +434,108 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
"GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED": "true",
})
})
t.Run("sets user token endpoint settings from SSO settings for an Azure plugin", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.Azure = azSettings
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{
GetForProviderFunc: getAzureSSOSettings,
})
require.Subset(t, p.PluginRequestConfig(context.Background(), "grafana-azure-monitor-datasource", nil), map[string]string{
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
"GFAZPL_WORKLOAD_IDENTITY_ENABLED": "true",
"GFAZPL_WORKLOAD_IDENTITY_TENANT_ID": "mock_workload_identity_tenant_id",
"GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID": "mock_workload_identity_client_id",
"GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE": "mock_workload_identity_token_file",
"GFAZPL_USER_IDENTITY_ENABLED": "true",
"GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED": "true",
"GFAZPL_USER_IDENTITY_TOKEN_URL": "sso_user_identity_token_url",
"GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION": "sso_user_client_authentication",
"GFAZPL_USER_IDENTITY_CLIENT_ID": "sso_user_identity_client_id",
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "sso_user_identity_client_secret",
"GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID": "sso_user_identity_managed_identity_client_id",
"GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE": "sso_user_identity_federated_credential_audience",
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
"GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED": "true",
})
})
t.Run("does not use SSO settings if overrides have been set for an Azure plugin", func(t *testing.T) {
cfg := setting.NewCfg()
cfg.Azure = &azsettings.AzureSettings{
Cloud: azsettings.AzurePublic,
ManagedIdentityEnabled: true,
ManagedIdentityClientId: "mock_managed_identity_client_id",
WorkloadIdentityEnabled: true,
WorkloadIdentitySettings: &azsettings.WorkloadIdentitySettings{
TenantId: "mock_workload_identity_tenant_id",
ClientId: "mock_workload_identity_client_id",
TokenFile: "mock_workload_identity_token_file",
},
UserIdentityEnabled: true,
UserIdentityTokenEndpoint: &azsettings.TokenEndpointSettings{
TokenUrl: "override_user_identity_token_url",
TokenUrlOverride: true,
ClientAuthentication: "override_user_client_authentication",
ClientAuthenticationOverride: true,
ClientId: "override_user_identity_client_id",
ClientIdOverride: true,
ClientSecret: "override_user_identity_client_secret",
ClientSecretOverride: true,
ManagedIdentityClientId: "override_user_identity_managed_identity_client_id",
ManagedIdentityClientIdOverride: true,
FederatedCredentialAudience: "override_user_identity_federated_credential_audience",
FederatedCredentialAudienceOverride: true,
UsernameAssertion: true,
},
UserIdentityFallbackCredentialsEnabled: true,
ForwardSettingsPlugins: []string{"grafana-azure-monitor-datasource", "prometheus", "grafana-azure-data-explorer-datasource", "mssql", "grafana-azureprometheus-datasource"},
AzureEntraPasswordCredentialsEnabled: true,
}
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{
GetForProviderFunc: getAzureSSOSettings,
})
require.Subset(t, p.PluginRequestConfig(context.Background(), "grafana-azure-monitor-datasource", nil), map[string]string{
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
"GFAZPL_WORKLOAD_IDENTITY_ENABLED": "true",
"GFAZPL_WORKLOAD_IDENTITY_TENANT_ID": "mock_workload_identity_tenant_id",
"GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID": "mock_workload_identity_client_id",
"GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE": "mock_workload_identity_token_file",
"GFAZPL_USER_IDENTITY_ENABLED": "true",
"GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED": "true",
"GFAZPL_USER_IDENTITY_TOKEN_URL": "override_user_identity_token_url",
"GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION": "override_user_client_authentication",
"GFAZPL_USER_IDENTITY_CLIENT_ID": "override_user_identity_client_id",
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "override_user_identity_client_secret",
"GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID": "override_user_identity_managed_identity_client_id",
"GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE": "override_user_identity_federated_credential_audience",
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
"GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED": "true",
})
})
}
func getAzureSSOSettings(ctx context.Context, provider string) (*pluginsso.Settings, error) {
return &pluginsso.Settings{
Values: map[string]any{
"token_url": "sso_user_identity_token_url",
"client_authentication": "sso_user_client_authentication",
"client_id": "sso_user_identity_client_id",
"client_secret": "sso_user_identity_client_secret",
"managed_identity_client_id": "sso_user_identity_managed_identity_client_id",
"federated_credential_audience": "sso_user_identity_federated_credential_audience",
},
}, nil
}
func TestRequestConfigProvider_PluginRequestConfig_aws(t *testing.T) {
@@ -447,7 +550,7 @@ func TestRequestConfigProvider_PluginRequestConfig_aws(t *testing.T) {
cfg.AWSListMetricsPageLimit = "100"
cfg.AWSForwardSettingsPlugins = []string{"cloudwatch", "prometheus", "elasticsearch"}
p := NewRequestConfigProvider(cfg)
p := NewRequestConfigProvider(cfg, &fakeSSOSettingsProvider{})
t.Run("uses the aws settings for an AWS plugin", func(t *testing.T) {
require.Subset(t, p.PluginRequestConfig(context.Background(), "cloudwatch", nil), map[string]string{
@@ -471,7 +574,7 @@ func TestRequestConfigProvider_PluginRequestConfig_aws(t *testing.T) {
t.Run("uses the aws settings for a non-aws user-specified plugin", func(t *testing.T) {
cfg.AWSForwardSettingsPlugins = append(cfg.AWSForwardSettingsPlugins, "test-datasource")
p = NewRequestConfigProvider(cfg)
p = NewRequestConfigProvider(cfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "test-datasource", nil), map[string]string{
"AWS_AUTH_AssumeRoleEnabled": "false",
"AWS_AUTH_AllowedAuthProviders": "grafana_assume_role,keys",
@@ -489,9 +592,20 @@ func TestRequestConfigProvider_PluginRequestConfig_appClientSecret(t *testing.T)
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
require.NoError(t, err)
p := NewRequestConfigProvider(pCfg)
p := NewRequestConfigProvider(pCfg, &fakeSSOSettingsProvider{})
require.Subset(t, p.PluginRequestConfig(context.Background(), "", &auth.ExternalService{
ClientSecret: "mysecret",
}), map[string]string{backend.AppClientSecret: "mysecret"})
})
}
type fakeSSOSettingsProvider struct {
GetForProviderFunc func(ctx context.Context, provider string) (*pluginsso.Settings, error)
}
func (m *fakeSSOSettingsProvider) GetForProvider(ctx context.Context, provider string) (*pluginsso.Settings, error) {
if m.GetForProviderFunc != nil {
return m.GetForProviderFunc(ctx, provider)
}
return nil, nil
}