grafana/pkg/api/pluginproxy/ds_auth_provider.go
idafurjes e822c8a24d
CloudMonitoring: Migrate to use backend plugin SDK contracts (#38650)
* Use SDK contracts for cloudmonitoring

* Get build running, tests passing and do some refactoring (#38754)

* fix build+tests and refactor

* remove alerting stuff

* remove unused field

* fix plugin fetch

* end to end

* resp rename

* tidy annotations

* reformatting

* update refID

* reformat imports

* fix styling

* clean up unmarshalling

* uncomment + fix tests

* appease linter

* remove spaces

* remove old cruft

* add check for empty queries

* update tests

* remove pm as dep

* adjust proxy route contract

* fix service loading

* use UNIX val

* fix endpoint + resp

* h@ckz for frontend

* fix resp

* fix interval

* always set custom meta

* remove unused param

* fix labels fetch

* fix linter

* fix test + remove unused field

* apply pr feedback

* fix grafana-auto intervals

* fix tests

* resolve conflicts

* fix bad merge

* fix conflicts

* remove bad logger import

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Will Browne <will.browne@grafana.com>
2021-10-08 14:46:35 +02:00

163 lines
4.5 KiB
Go

package pluginproxy
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
type DSInfo struct {
ID int64
Updated time.Time
JSONData map[string]interface{}
DecryptedSecureJSONData map[string]string
}
// ApplyRoute should use the plugin route data to set auth headers and custom headers.
func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route *plugins.AppPluginRoute,
ds DSInfo, cfg *setting.Cfg) {
proxyPath = strings.TrimPrefix(proxyPath, route.Path)
data := templateData{
JsonData: ds.JSONData,
SecureJsonData: ds.DecryptedSecureJSONData,
}
if len(route.URL) > 0 {
interpolatedURL, err := interpolateString(route.URL, data)
if err != nil {
logger.Error("Error interpolating proxy url", "error", err)
return
}
routeURL, err := url.Parse(interpolatedURL)
if err != nil {
logger.Error("Error parsing plugin route url", "error", err)
return
}
req.URL.Scheme = routeURL.Scheme
req.URL.Host = routeURL.Host
req.Host = routeURL.Host
req.URL.Path = util.JoinURLFragments(routeURL.Path, proxyPath)
}
if err := addQueryString(req, route, data); err != nil {
logger.Error("Failed to render plugin URL query string", "error", err)
}
if err := addHeaders(&req.Header, route, data); err != nil {
logger.Error("Failed to render plugin headers", "error", err)
}
if err := setBodyContent(req, route, data); err != nil {
logger.Error("Failed to set plugin route body content", "error", err)
}
if tokenProvider, err := getTokenProvider(ctx, cfg, ds, route, data); err != nil {
logger.Error("Failed to resolve auth token provider", "error", err)
} else if tokenProvider != nil {
if token, err := tokenProvider.GetAccessToken(); err != nil {
logger.Error("Failed to get access token", "error", err)
} else {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
}
}
if cfg.DataProxyLogging {
logger.Debug("Requesting", "url", req.URL.String())
}
}
func getTokenProvider(ctx context.Context, cfg *setting.Cfg, ds DSInfo, pluginRoute *plugins.AppPluginRoute,
data templateData) (accessTokenProvider, error) {
authType := pluginRoute.AuthType
// Plugin can override authentication type specified in route configuration
if authTypeOverride, ok := ds.JSONData["authenticationType"].(string); ok && authTypeOverride != "" {
authType = authTypeOverride
}
tokenAuth, err := interpolateAuthParams(pluginRoute.TokenAuth, data)
if err != nil {
return nil, err
}
jwtTokenAuth, err := interpolateAuthParams(pluginRoute.JwtTokenAuth, data)
if err != nil {
return nil, err
}
switch authType {
case "azure":
if tokenAuth == nil {
return nil, fmt.Errorf("'tokenAuth' not configured for authentication type '%s'", authType)
}
return newAzureAccessTokenProvider(ctx, cfg, tokenAuth)
case "gce":
if jwtTokenAuth == nil {
return nil, fmt.Errorf("'jwtTokenAuth' not configured for authentication type '%s'", authType)
}
provider := newGceAccessTokenProvider(ctx, ds, pluginRoute, jwtTokenAuth)
return provider, nil
case "jwt":
if jwtTokenAuth == nil {
return nil, fmt.Errorf("'jwtTokenAuth' not configured for authentication type '%s'", authType)
}
provider := newJwtAccessTokenProvider(ctx, ds, pluginRoute, jwtTokenAuth)
return provider, nil
case "":
// Fallback to authentication methods when authentication type isn't explicitly configured
if tokenAuth != nil {
provider := newGenericAccessTokenProvider(ds, pluginRoute, tokenAuth)
return provider, nil
}
if jwtTokenAuth != nil {
provider := newJwtAccessTokenProvider(ctx, ds, pluginRoute, jwtTokenAuth)
return provider, nil
}
// No authentication
return nil, nil
default:
return nil, fmt.Errorf("authentication type '%s' not supported", authType)
}
}
func interpolateAuthParams(tokenAuth *plugins.JwtTokenAuth, data templateData) (*plugins.JwtTokenAuth, error) {
if tokenAuth == nil {
// Nothing to interpolate
return nil, nil
}
interpolatedUrl, err := interpolateString(tokenAuth.Url, data)
if err != nil {
return nil, err
}
interpolatedParams := make(map[string]string)
for key, value := range tokenAuth.Params {
interpolatedParam, err := interpolateString(value, data)
if err != nil {
return nil, err
}
interpolatedParams[key] = interpolatedParam
}
return &plugins.JwtTokenAuth{
Url: interpolatedUrl,
Scopes: tokenAuth.Scopes,
Params: interpolatedParams,
}, nil
}