OAuth: Add support for new user identity flags for grafana-azure-sdk-go (#98703)

* added changes for rebase

* ran go mod tidy and ran a build

* ran a build

* Update docs/sources/setup-grafana/configure-grafana/_index.md

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>

* Update docs/sources/setup-grafana/configure-grafana/_index.md

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>

* Update docs/sources/setup-grafana/configure-grafana/_index.md

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>

* updated go.work.sum to upstream

* added newline to match upstream

* added more specificity in documentation

---------

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
This commit is contained in:
John Naizer 2025-01-24 04:48:27 -08:00 committed by GitHub
parent 33d82c43df
commit eb2d276a42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 117 additions and 6 deletions

View File

@ -1013,6 +1013,10 @@ user_identity_fallback_credentials_enabled = true
# By default is the same as token URL configured for AAD authentication settings
user_identity_token_url =
# Override client authentication method for Azure Active Directory
# By default is the same as client authentication method configured for AAD authentication settings
user_identity_client_authentication =
# Override ADD application ID which would be used to exchange users token to an access token for the datasource
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
user_identity_client_id =
@ -1021,6 +1025,14 @@ user_identity_client_id =
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
user_identity_client_secret =
# Override the AAD managed identity client ID
# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow)
user_identity_managed_identity_client_id =
# Override the AAD federated credential audience
# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow)
user_identity_federated_credential_audience =
# Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy
# In most cases this will not need to be used. To enable this set the value to "username"
# The default is empty and any other value will not enable this functionality

View File

@ -998,6 +998,10 @@
# By default is the same as token URL configured for AAD authentication settings
;user_identity_token_url =
# Override client authentication method for Azure Active Directory
# By default is the same as client authentication method configured for AAD authentication settings
;user_identity_client_authentication =
# Override ADD application ID which would be used to exchange users token to an access token for the datasource
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
;user_identity_client_id =
@ -1006,6 +1010,14 @@
# By default is the same as used in AAD authentication or can be set to another application (for OBO flow)
;user_identity_client_secret =
# Override the AAD managed identity client ID
# By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow)
;user_identity_managed_identity_client_id =
# Override the AAD federated credential audience
# By default is the same as used in AAD authentication or can be set to another audience (for OBO flow)
;user_identity_federated_credential_audience =
# Allows the usage of a custom token request assertion when Grafana is behind an authentication proxy
# In most cases this will not need to be used. To enable this set the value to "username"
# The default is empty and any other value will not enable this functionality

View File

@ -1361,7 +1361,13 @@ Override token URL for Azure Active Directory.
By default is the same as token URL configured for AAD authentication settings.
#### `user_identity_client_id`
### `user_identity_client_authentication`
Override client authentication method for Azure Active Directory. Currently supported values are `client_secret_post` and `managed_identity`.
By default is the same as client authentication method configured for AAD authentication settings.
### `user_identity_client_id`
Override ADD application ID which would be used to exchange users token to an access token for the data source.
@ -1373,7 +1379,19 @@ Override the AAD application client secret.
By default is the same as used in AAD authentication or can be set to another application (for OBO flow).
#### `forward_settings_to_plugins`
### `user_identity_managed_identity_client_id`
Override the AAD application managed identity client ID of the federated credential configured as a user-assigned managed identity.
By default is the same as used in AAD authentication or can be set to another managed identity (for OBO flow).
### `user_identity_federated_credential_audience`
Override the AAD federated credential audience of the federated credential configured as a user-assigned managed identity.
By default is the same as used in AAD authentication or can be set to another audience (for OBO flow).
### `forward_settings_to_plugins`
Set plugins to receive Azure settings via plugin context.

View File

@ -115,12 +115,21 @@ func (s *RequestConfigProvider) PluginRequestConfig(ctx context.Context, pluginI
if azureSettings.UserIdentityTokenEndpoint.TokenUrl != "" {
m[azsettings.UserIdentityTokenURL] = azureSettings.UserIdentityTokenEndpoint.TokenUrl
}
if azureSettings.UserIdentityTokenEndpoint.ClientAuthentication != "" {
m[azsettings.UserIdentityClientAuthentication] = azureSettings.UserIdentityTokenEndpoint.ClientAuthentication
}
if azureSettings.UserIdentityTokenEndpoint.ClientId != "" {
m[azsettings.UserIdentityClientID] = azureSettings.UserIdentityTokenEndpoint.ClientId
}
if azureSettings.UserIdentityTokenEndpoint.ClientSecret != "" {
m[azsettings.UserIdentityClientSecret] = azureSettings.UserIdentityTokenEndpoint.ClientSecret
}
if azureSettings.UserIdentityTokenEndpoint.ManagedIdentityClientId != "" {
m[azsettings.UserIdentityManagedIdentityClientID] = azureSettings.UserIdentityTokenEndpoint.ManagedIdentityClientId
}
if azureSettings.UserIdentityTokenEndpoint.FederatedCredentialAudience != "" {
m[azsettings.UserIdentityFederatedCredentialAudience] = azureSettings.UserIdentityTokenEndpoint.FederatedCredentialAudience
}
if azureSettings.UserIdentityTokenEndpoint.UsernameAssertion {
m[azsettings.UserIdentityAssertion] = "username"
}

View File

@ -302,10 +302,13 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
},
UserIdentityEnabled: true,
UserIdentityTokenEndpoint: &azsettings.TokenEndpointSettings{
TokenUrl: "mock_user_identity_token_url",
ClientId: "mock_user_identity_client_id",
ClientSecret: "mock_user_identity_client_secret",
UsernameAssertion: true,
TokenUrl: "mock_user_identity_token_url",
ClientAuthentication: "mock_user_client_authentication",
ClientId: "mock_user_identity_client_id",
ClientSecret: "mock_user_identity_client_secret",
ManagedIdentityClientId: "mock_user_identity_managed_identity_client_id",
FederatedCredentialAudience: "mock_user_identity_federated_credential_audience",
UsernameAssertion: true,
},
UserIdentityFallbackCredentialsEnabled: true,
ForwardSettingsPlugins: []string{"grafana-azure-monitor-datasource", "prometheus", "grafana-azure-data-explorer-datasource", "mssql"},
@ -330,8 +333,11 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
"GFAZPL_USER_IDENTITY_ENABLED": "true",
"GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED": "true",
"GFAZPL_USER_IDENTITY_TOKEN_URL": "mock_user_identity_token_url",
"GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION": "mock_user_client_authentication",
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
"GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID": "mock_user_identity_managed_identity_client_id",
"GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE": "mock_user_identity_federated_credential_audience",
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
})
})
@ -362,8 +368,11 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
"GFAZPL_USER_IDENTITY_ENABLED": "true",
"GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED": "true",
"GFAZPL_USER_IDENTITY_TOKEN_URL": "mock_user_identity_token_url",
"GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION": "mock_user_client_authentication",
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
"GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID": "mock_user_identity_managed_identity_client_id",
"GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE": "mock_user_identity_federated_credential_audience",
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
})
})
@ -387,8 +396,11 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_ENABLED")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_TOKEN_URL")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_ID")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_SECRET")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE")
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_ASSERTION")
require.NotContains(t, m, "GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED")
})
@ -412,8 +424,11 @@ func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
"GFAZPL_USER_IDENTITY_ENABLED": "true",
"GFAZPL_USER_IDENTITY_FALLBACK_SERVICE_CREDENTIALS_ENABLED": "true",
"GFAZPL_USER_IDENTITY_TOKEN_URL": "mock_user_identity_token_url",
"GFAZPL_USER_IDENTITY_CLIENT_AUTHENTICATION": "mock_user_client_authentication",
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
"GFAZPL_USER_IDENTITY_MANAGED_IDENTITY_CLIENT_ID": "mock_user_identity_managed_identity_client_id",
"GFAZPL_USER_IDENTITY_FEDERATED_CREDENTIAL_AUDIENCE": "mock_user_identity_federated_credential_audience",
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
"GFAZPL_AZURE_ENTRA_PASSWORD_CREDENTIALS_ENABLED": "true",
})

View File

@ -49,14 +49,20 @@ func (cfg *Cfg) readAzureSettings() {
azureAdSection := cfg.Raw.Section("auth.azuread")
if azureAdSection.Key("enabled").MustBool(false) {
tokenEndpointSettings.TokenUrl = azureAdSection.Key("token_url").String()
tokenEndpointSettings.ClientAuthentication = azureAdSection.Key("client_authentication").String()
tokenEndpointSettings.ClientId = azureAdSection.Key("client_id").String()
tokenEndpointSettings.ClientSecret = azureAdSection.Key("client_secret").String()
tokenEndpointSettings.ManagedIdentityClientId = azureAdSection.Key("managed_identity_client_id").String()
tokenEndpointSettings.FederatedCredentialAudience = azureAdSection.Key("federated_credential_audience").String()
}
// Override individual settings
if val := azureSection.Key("user_identity_token_url").String(); val != "" {
tokenEndpointSettings.TokenUrl = val
}
if val := azureSection.Key("user_identity_client_authentication").String(); val != "" {
tokenEndpointSettings.ClientAuthentication = val
}
if val := azureSection.Key("user_identity_client_id").String(); val != "" {
tokenEndpointSettings.ClientId = val
tokenEndpointSettings.ClientSecret = ""
@ -64,6 +70,12 @@ func (cfg *Cfg) readAzureSettings() {
if val := azureSection.Key("user_identity_client_secret").String(); val != "" {
tokenEndpointSettings.ClientSecret = val
}
if val := azureSection.Key("user_identity_managed_identity_client_id").String(); val != "" {
tokenEndpointSettings.ManagedIdentityClientId = val
}
if val := azureSection.Key("user_identity_federated_credential_audience").String(); val != "" {
tokenEndpointSettings.FederatedCredentialAudience = val
}
if val := azureSection.Key("username_assertion").String(); val != "" && val == "username" {
tokenEndpointSettings.UsernameAssertion = true
}

View File

@ -145,10 +145,16 @@ func TestAzureSettings(t *testing.T) {
require.NoError(t, err)
_, err = azureAdSection.NewKey("token_url", "URL_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_authentication", "METHOD_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_id", "ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_secret", "SECRET_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("managed_identity_client_id", "MANAGED_ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("federated_credential_audience", "AUDIENCE_1")
require.NoError(t, err)
azureSection, err := cfg.Raw.NewSection("azure")
require.NoError(t, err)
@ -161,8 +167,11 @@ func TestAzureSettings(t *testing.T) {
assert.True(t, cfg.Azure.UserIdentityEnabled)
assert.Equal(t, "URL_1", cfg.Azure.UserIdentityTokenEndpoint.TokenUrl)
assert.Equal(t, "METHOD_1", cfg.Azure.UserIdentityTokenEndpoint.ClientAuthentication)
assert.Equal(t, "ID_1", cfg.Azure.UserIdentityTokenEndpoint.ClientId)
assert.Equal(t, "SECRET_1", cfg.Azure.UserIdentityTokenEndpoint.ClientSecret)
assert.Equal(t, "MANAGED_ID_1", cfg.Azure.UserIdentityTokenEndpoint.ManagedIdentityClientId)
assert.Equal(t, "AUDIENCE_1", cfg.Azure.UserIdentityTokenEndpoint.FederatedCredentialAudience)
})
t.Run("should not use token endpoint from Azure AD if not enabled", func(t *testing.T) {
@ -174,10 +183,16 @@ func TestAzureSettings(t *testing.T) {
require.NoError(t, err)
_, err = azureAdSection.NewKey("token_url", "URL_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_authentication", "METHOD_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_id", "ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_secret", "SECRET_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("managed_identity_client_id", "MANAGED_ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("federated_credential_audience", "AUDIENCE_1")
require.NoError(t, err)
azureSection, err := cfg.Raw.NewSection("azure")
require.NoError(t, err)
@ -190,8 +205,11 @@ func TestAzureSettings(t *testing.T) {
assert.True(t, cfg.Azure.UserIdentityEnabled)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.TokenUrl)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.ClientAuthentication)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.ClientId)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.ClientSecret)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.ManagedIdentityClientId)
assert.Empty(t, cfg.Azure.UserIdentityTokenEndpoint.FederatedCredentialAudience)
})
t.Run("should override Azure AD settings", func(t *testing.T) {
@ -203,10 +221,16 @@ func TestAzureSettings(t *testing.T) {
require.NoError(t, err)
_, err = azureAdSection.NewKey("token_url", "URL_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_authentication", "METHOD_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_id", "ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("client_secret", "SECRET_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("managed_identity_client_id", "MANAGED_ID_1")
require.NoError(t, err)
_, err = azureAdSection.NewKey("federated_credential_audience", "AUDIENCE_1")
require.NoError(t, err)
azureSection, err := cfg.Raw.NewSection("azure")
require.NoError(t, err)
@ -214,10 +238,16 @@ func TestAzureSettings(t *testing.T) {
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_token_url", "URL_2")
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_client_authentication", "METHOD_2")
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_client_id", "ID_2")
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_client_secret", "SECRET_2")
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_managed_identity_client_id", "MANAGED_ID_2")
require.NoError(t, err)
_, err = azureSection.NewKey("user_identity_federated_credential_audience", "AUDIENCE_2")
require.NoError(t, err)
cfg.readAzureSettings()
require.NotNil(t, cfg.Azure)
@ -225,8 +255,11 @@ func TestAzureSettings(t *testing.T) {
assert.True(t, cfg.Azure.UserIdentityEnabled)
assert.Equal(t, "URL_2", cfg.Azure.UserIdentityTokenEndpoint.TokenUrl)
assert.Equal(t, "METHOD_2", cfg.Azure.UserIdentityTokenEndpoint.ClientAuthentication)
assert.Equal(t, "ID_2", cfg.Azure.UserIdentityTokenEndpoint.ClientId)
assert.Equal(t, "SECRET_2", cfg.Azure.UserIdentityTokenEndpoint.ClientSecret)
assert.Equal(t, "MANAGED_ID_2", cfg.Azure.UserIdentityTokenEndpoint.ManagedIdentityClientId)
assert.Equal(t, "AUDIENCE_2", cfg.Azure.UserIdentityTokenEndpoint.FederatedCredentialAudience)
})
t.Run("should not use secret from Azure AD if client ID overridden", func(t *testing.T) {