mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
e822c8a24d
* 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>
163 lines
4.5 KiB
Go
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
|
|
}
|