AzureMonitor: User authentication support (#81918)
* Stub out frontend user auth * Stub out backend user auth * Add context * Reorganise files * Refactor app registration form * Alert for user auth service principal credentials * AzureMonitor: Add flag for enabling/disabling fallback credentials for current user authentication (#82332) * Rename field * Add fallback setting * Update tests and mock * Remove duplicate setting line * Update name of property * Update frontend settings * Update docs and default config files * Update azure-sdk * Fix lint * Update test * Bump dependency * Update configuration * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Docs review * AzureMonitor: User authentication frontend updates (#83107) * Rename field * Add fallback setting * Update tests and mock * Remove duplicate setting line * Update name of property * Update frontend settings * Update docs and default config files * Add alerts to query editor - Add authenticatedBy property to grafana/data - Update mocks - Update query editor to disable it under certain circumstances - Update tests * Add separate FallbackCredentials component - Reset AppRegistrationCredentials component to only handle clientsecret credentials - Update AzureCredentialsForm - Update selectors - Update tests - Update credentials utility functions logic * Alert when fallback credentials disabled * Update condition * Update azure-sdk * Fix lint * Update test * Remove unneeded conditions * Set auth type correctly * Legacy cloud options * Fix client secret * Remove accidental import * Bump dependency * Add tests * Don't use VerticalGroup component * Remove unused import * Fix lint * Appropriately set oAuthPassThru and disableGrafanaCache properties * Clear azureCredentials on authType change * Correctly retrieve secret * Fix bug in authTypeOptions * Update public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> * Update public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.tsx Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> * Update public/app/plugins/datasource/azuremonitor/components/ConfigEditor/CurrentUserFallbackCredentials.tsx Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> * Add documentation links * Fix broken link --------- Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> * AzureMonitor: Update docs for current user authentication (#83440) * Rename field * Add fallback setting * Update tests and mock * Remove duplicate setting line * Update name of property * Update frontend settings * Update docs and default config files * Add alerts to query editor - Add authenticatedBy property to grafana/data - Update mocks - Update query editor to disable it under certain circumstances - Update tests * Add separate FallbackCredentials component - Reset AppRegistrationCredentials component to only handle clientsecret credentials - Update AzureCredentialsForm - Update selectors - Update tests - Update credentials utility functions logic * Alert when fallback credentials disabled * Update condition * Update azure-sdk * Fix lint * Update test * Remove unneeded conditions * Set auth type correctly * Legacy cloud options * Fix client secret * Remove accidental import * Bump dependency * Add tests * Don't use VerticalGroup component * Remove unused import * Update docs * Fix lint * Appropriately set oAuthPassThru and disableGrafanaCache properties * Clear azureCredentials on authType change * Correctly retrieve secret * Feedback * Spelling * Update docs/sources/datasources/azure-monitor/_index.md Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> * Update docs/sources/datasources/azure-monitor/_index.md Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> * Update docs/sources/datasources/azure-monitor/_index.md Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> * Update docs/sources/datasources/azure-monitor/_index.md Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> --------- Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com> * Docs review * Update docs with additional configuration information * Fix to appropriately hide the query editor * Typo * Update isCredentialsComplete * Update test --------- Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> Co-authored-by: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Co-authored-by: Larissa Wandzura <126723338+lwandz13@users.noreply.github.com>
This commit is contained in:
co-authored by
Larissa Wandzura
Christopher Moyer
Andrew Hackmann
parent
2a6a1fb3b3
commit
6bb7ab261a
@@ -62,6 +62,27 @@ func getFromLegacy(data map[string]interface{}, secureData map[string]string) (a
|
||||
credentials := &azcredentials.AzureWorkloadIdentityCredentials{}
|
||||
return credentials, nil
|
||||
|
||||
case azcredentials.AzureAuthCurrentUserIdentity:
|
||||
legacyCloud, err := maputil.GetStringOptional(data, "cloudName")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cloud, err := resolveLegacyCloudName(legacyCloud)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientSecret := secureData["clientSecret"]
|
||||
|
||||
credentials := &azcredentials.AadCurrentUserCredentials{
|
||||
ServiceCredentials: &azcredentials.AzureClientSecretCredentials{
|
||||
AzureCloud: cloud,
|
||||
TenantId: tenantId,
|
||||
ClientId: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
},
|
||||
}
|
||||
|
||||
return credentials, nil
|
||||
case azcredentials.AzureAuthClientSecret:
|
||||
legacyCloud, err := maputil.GetStringOptional(data, "cloudName")
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/v2/azsettings"
|
||||
"github.com/grafana/grafana-azure-sdk-go/v2/azusercontext"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
@@ -52,11 +53,11 @@ func ProvideService(httpClientProvider *httpclient.Provider) *Service {
|
||||
}
|
||||
|
||||
func (s *Service) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
return s.queryMux.QueryData(ctx, req)
|
||||
return s.queryMux.QueryData(azusercontext.WithUserFromQueryReq(ctx, req), req)
|
||||
}
|
||||
|
||||
func (s *Service) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
|
||||
return s.resourceHandler.CallResource(ctx, req, sender)
|
||||
return s.resourceHandler.CallResource(azusercontext.WithUserFromResourceReq(ctx, req), req, sender)
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
@@ -191,10 +192,10 @@ func (s *Service) getDSInfo(ctx context.Context, pluginCtx backend.PluginContext
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func queryMetricHealth(dsInfo types.DatasourceInfo) (*http.Response, error) {
|
||||
func queryMetricHealth(ctx context.Context, dsInfo types.DatasourceInfo) (*http.Response, error) {
|
||||
subscriptionsApiVersion := "2020-01-01"
|
||||
url := fmt.Sprintf("%v/subscriptions?api-version=%v", dsInfo.Routes["Azure Monitor"].URL, subscriptionsApiVersion)
|
||||
request, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -207,9 +208,9 @@ func queryMetricHealth(dsInfo types.DatasourceInfo) (*http.Response, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func checkAzureLogAnalyticsHealth(dsInfo types.DatasourceInfo, subscription string) (*http.Response, error) {
|
||||
func checkAzureLogAnalyticsHealth(ctx context.Context, dsInfo types.DatasourceInfo, subscription string) (*http.Response, error) {
|
||||
workspacesUrl := fmt.Sprintf("%v/subscriptions/%v/providers/Microsoft.OperationalInsights/workspaces?api-version=2017-04-26-preview", dsInfo.Routes["Azure Monitor"].URL, subscription)
|
||||
workspacesReq, err := http.NewRequest(http.MethodGet, workspacesUrl, nil)
|
||||
workspacesReq, err := http.NewRequestWithContext(ctx, http.MethodGet, workspacesUrl, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -238,7 +239,7 @@ func checkAzureLogAnalyticsHealth(dsInfo types.DatasourceInfo, subscription stri
|
||||
}
|
||||
|
||||
workspaceUrl := fmt.Sprintf("%v/v1/workspaces/%v/query", dsInfo.Routes["Azure Log Analytics"].URL, defaultWorkspaceId)
|
||||
workspaceReq, err := http.NewRequest(http.MethodPost, workspaceUrl, bytes.NewBuffer(body))
|
||||
workspaceReq, err := http.NewRequestWithContext(ctx, http.MethodPost, workspaceUrl, bytes.NewBuffer(body))
|
||||
workspaceReq.Header.Set("Content-Type", "application/json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -252,7 +253,7 @@ func checkAzureLogAnalyticsHealth(dsInfo types.DatasourceInfo, subscription stri
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func checkAzureMonitorResourceGraphHealth(dsInfo types.DatasourceInfo, subscription string) (*http.Response, error) {
|
||||
func checkAzureMonitorResourceGraphHealth(ctx context.Context, dsInfo types.DatasourceInfo, subscription string) (*http.Response, error) {
|
||||
body, err := json.Marshal(map[string]any{
|
||||
"query": "Resources | project id | limit 1",
|
||||
"subscriptions": []string{subscription},
|
||||
@@ -261,7 +262,7 @@ func checkAzureMonitorResourceGraphHealth(dsInfo types.DatasourceInfo, subscript
|
||||
return nil, err
|
||||
}
|
||||
url := fmt.Sprintf("%v/providers/Microsoft.ResourceGraph/resources?api-version=%v", dsInfo.Routes["Azure Resource Graph"].URL, resourcegraph.ArgAPIVersion)
|
||||
request, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(body))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -275,9 +276,9 @@ func checkAzureMonitorResourceGraphHealth(dsInfo types.DatasourceInfo, subscript
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func metricCheckHealth(dsInfo types.DatasourceInfo, logger log.Logger) (message string, defaultSubscription string, status backend.HealthStatus) {
|
||||
func metricCheckHealth(ctx context.Context, dsInfo types.DatasourceInfo, logger log.Logger) (message string, defaultSubscription string, status backend.HealthStatus) {
|
||||
defaultSubscription = dsInfo.Settings.SubscriptionId
|
||||
metricsRes, err := queryMetricHealth(dsInfo)
|
||||
metricsRes, err := queryMetricHealth(ctx, dsInfo)
|
||||
if err != nil {
|
||||
if ok := errors.Is(err, types.ErrorAzureHealthCheck); ok {
|
||||
return fmt.Sprintf("Error connecting to Azure Monitor endpoint: %s", err.Error()), defaultSubscription, backend.HealthStatusError
|
||||
@@ -309,8 +310,8 @@ func metricCheckHealth(dsInfo types.DatasourceInfo, logger log.Logger) (message
|
||||
return "Successfully connected to Azure Monitor endpoint.", defaultSubscription, backend.HealthStatusOk
|
||||
}
|
||||
|
||||
func logAnalyticsCheckHealth(dsInfo types.DatasourceInfo, defaultSubscription string) (message string, status backend.HealthStatus) {
|
||||
logsRes, err := checkAzureLogAnalyticsHealth(dsInfo, defaultSubscription)
|
||||
func logAnalyticsCheckHealth(ctx context.Context, dsInfo types.DatasourceInfo, defaultSubscription string) (message string, status backend.HealthStatus) {
|
||||
logsRes, err := checkAzureLogAnalyticsHealth(ctx, dsInfo, defaultSubscription)
|
||||
if err != nil {
|
||||
if err.Error() == "no default workspace found" {
|
||||
return "No Log Analytics workspaces found.", backend.HealthStatusUnknown
|
||||
@@ -337,8 +338,8 @@ func logAnalyticsCheckHealth(dsInfo types.DatasourceInfo, defaultSubscription st
|
||||
return "Successfully connected to Azure Log Analytics endpoint.", backend.HealthStatusOk
|
||||
}
|
||||
|
||||
func graphLogHealthCheck(dsInfo types.DatasourceInfo, defaultSubscription string) (message string, status backend.HealthStatus) {
|
||||
resourceGraphRes, err := checkAzureMonitorResourceGraphHealth(dsInfo, defaultSubscription)
|
||||
func graphLogHealthCheck(ctx context.Context, dsInfo types.DatasourceInfo, defaultSubscription string) (message string, status backend.HealthStatus) {
|
||||
resourceGraphRes, err := checkAzureMonitorResourceGraphHealth(ctx, dsInfo, defaultSubscription)
|
||||
if err != nil {
|
||||
if ok := errors.Is(err, types.ErrorAzureHealthCheck); ok {
|
||||
return fmt.Sprintf("Error connecting to Azure Resource Graph endpoint: %s", err.Error()), backend.HealthStatusError
|
||||
@@ -387,6 +388,7 @@ func parseSubscriptions(res *http.Response, logger log.Logger) ([]string, error)
|
||||
}
|
||||
|
||||
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
ctx = azusercontext.WithUserFromHealthCheckReq(ctx, req)
|
||||
dsInfo, err := s.getDSInfo(ctx, req.PluginContext)
|
||||
if err != nil {
|
||||
return &backend.CheckHealthResult{
|
||||
@@ -397,17 +399,17 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
|
||||
|
||||
status := backend.HealthStatusOk
|
||||
|
||||
metricsLog, defaultSubscription, metricsStatus := metricCheckHealth(dsInfo, s.logger)
|
||||
metricsLog, defaultSubscription, metricsStatus := metricCheckHealth(ctx, dsInfo, s.logger)
|
||||
if metricsStatus != backend.HealthStatusOk {
|
||||
status = metricsStatus
|
||||
}
|
||||
|
||||
logAnalyticsLog, logAnalyticsStatus := logAnalyticsCheckHealth(dsInfo, defaultSubscription)
|
||||
logAnalyticsLog, logAnalyticsStatus := logAnalyticsCheckHealth(ctx, dsInfo, defaultSubscription)
|
||||
if logAnalyticsStatus != backend.HealthStatusOk {
|
||||
status = logAnalyticsStatus
|
||||
}
|
||||
|
||||
graphLog, graphStatus := graphLogHealthCheck(dsInfo, defaultSubscription)
|
||||
graphLog, graphStatus := graphLogHealthCheck(ctx, dsInfo, defaultSubscription)
|
||||
if graphStatus != backend.HealthStatusOk {
|
||||
status = graphStatus
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ func newHTTPClient(ctx context.Context, route types.AzRoute, model types.Datasou
|
||||
}
|
||||
|
||||
authOpts := azhttpclient.NewAuthOptions(azureSettings)
|
||||
authOpts.AllowUserIdentity()
|
||||
authOpts.Scopes(route.Scopes)
|
||||
azhttpclient.AddAzureAuthentication(&clientOpts, authOpts, model.Credentials)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user