2018-09-10 09:59:29 -05:00
|
|
|
package pluginproxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
2021-10-08 07:46:35 -05:00
|
|
|
"time"
|
2018-09-10 09:59:29 -05:00
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/plugins"
|
2021-05-14 06:59:07 -05:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2018-09-10 09:59:29 -05:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
|
|
|
)
|
|
|
|
|
2021-10-08 07:46:35 -05:00
|
|
|
type DSInfo struct {
|
|
|
|
ID int64
|
|
|
|
Updated time.Time
|
|
|
|
JSONData map[string]interface{}
|
|
|
|
DecryptedSecureJSONData map[string]string
|
|
|
|
}
|
|
|
|
|
2020-11-13 02:52:38 -06:00
|
|
|
// ApplyRoute should use the plugin route data to set auth headers and custom headers.
|
2021-11-01 04:53:33 -05:00
|
|
|
func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route *plugins.Route,
|
2021-10-08 07:46:35 -05:00
|
|
|
ds DSInfo, cfg *setting.Cfg) {
|
2018-09-10 09:59:29 -05:00
|
|
|
proxyPath = strings.TrimPrefix(proxyPath, route.Path)
|
|
|
|
|
|
|
|
data := templateData{
|
2021-10-08 07:46:35 -05:00
|
|
|
JsonData: ds.JSONData,
|
|
|
|
SecureJsonData: ds.DecryptedSecureJSONData,
|
2018-09-10 09:59:29 -05:00
|
|
|
}
|
|
|
|
|
2020-09-18 06:22:07 -05:00
|
|
|
if len(route.URL) > 0 {
|
2020-11-17 03:56:42 -06:00
|
|
|
interpolatedURL, err := interpolateString(route.URL, data)
|
2020-09-18 06:22:07 -05:00
|
|
|
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)
|
2018-09-10 09:59:29 -05:00
|
|
|
}
|
|
|
|
|
2020-04-24 03:32:13 -05:00
|
|
|
if err := addQueryString(req, route, data); err != nil {
|
|
|
|
logger.Error("Failed to render plugin URL query string", "error", err)
|
|
|
|
}
|
|
|
|
|
2018-09-10 09:59:29 -05:00
|
|
|
if err := addHeaders(&req.Header, route, data); err != nil {
|
|
|
|
logger.Error("Failed to render plugin headers", "error", err)
|
|
|
|
}
|
|
|
|
|
2021-03-31 09:38:35 -05:00
|
|
|
if err := setBodyContent(req, route, data); err != nil {
|
|
|
|
logger.Error("Failed to set plugin route body content", "error", err)
|
|
|
|
}
|
|
|
|
|
2021-05-14 06:59:07 -05:00
|
|
|
if tokenProvider, err := getTokenProvider(ctx, cfg, ds, route, data); err != nil {
|
2021-05-06 15:05:23 -05:00
|
|
|
logger.Error("Failed to resolve auth token provider", "error", err)
|
|
|
|
} else if tokenProvider != nil {
|
2021-06-11 10:02:24 -05:00
|
|
|
if token, err := tokenProvider.GetAccessToken(); err != nil {
|
2018-09-10 09:59:29 -05:00
|
|
|
logger.Error("Failed to get access token", "error", err)
|
|
|
|
} else {
|
2018-11-30 13:12:55 -06:00
|
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
2018-09-10 09:59:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-15 07:30:06 -05:00
|
|
|
if cfg.DataProxyLogging {
|
2021-06-15 08:03:24 -05:00
|
|
|
logger.Debug("Requesting", "url", req.URL.String())
|
|
|
|
}
|
2021-05-03 07:46:32 -05:00
|
|
|
}
|
2018-10-08 06:49:27 -05:00
|
|
|
|
2021-11-01 04:53:33 -05:00
|
|
|
func getTokenProvider(ctx context.Context, cfg *setting.Cfg, ds DSInfo, pluginRoute *plugins.Route,
|
2021-05-06 15:05:23 -05:00
|
|
|
data templateData) (accessTokenProvider, error) {
|
|
|
|
authType := pluginRoute.AuthType
|
2021-05-03 07:46:32 -05:00
|
|
|
|
2021-05-06 15:05:23 -05:00
|
|
|
// Plugin can override authentication type specified in route configuration
|
2021-10-08 07:46:35 -05:00
|
|
|
if authTypeOverride, ok := ds.JSONData["authenticationType"].(string); ok && authTypeOverride != "" {
|
2021-05-06 15:05:23 -05:00
|
|
|
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 {
|
2021-05-14 06:59:07 -05:00
|
|
|
case "azure":
|
|
|
|
if tokenAuth == nil {
|
|
|
|
return nil, fmt.Errorf("'tokenAuth' not configured for authentication type '%s'", authType)
|
|
|
|
}
|
2021-07-05 05:20:12 -05:00
|
|
|
return newAzureAccessTokenProvider(ctx, cfg, tokenAuth)
|
2021-05-14 06:59:07 -05:00
|
|
|
|
2021-05-03 07:46:32 -05:00
|
|
|
case "gce":
|
2021-05-06 15:05:23 -05:00
|
|
|
if jwtTokenAuth == nil {
|
|
|
|
return nil, fmt.Errorf("'jwtTokenAuth' not configured for authentication type '%s'", authType)
|
|
|
|
}
|
2021-10-21 15:29:56 -05:00
|
|
|
return newGceAccessTokenProvider(ctx, ds, pluginRoute, jwtTokenAuth), nil
|
2021-05-06 15:05:23 -05:00
|
|
|
|
2021-05-03 07:46:32 -05:00
|
|
|
case "jwt":
|
2021-05-06 15:05:23 -05:00
|
|
|
if jwtTokenAuth == nil {
|
|
|
|
return nil, fmt.Errorf("'jwtTokenAuth' not configured for authentication type '%s'", authType)
|
2021-05-03 07:46:32 -05:00
|
|
|
}
|
2021-05-06 15:05:23 -05:00
|
|
|
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
|
2021-05-03 07:46:32 -05:00
|
|
|
}
|
2021-05-06 15:05:23 -05:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-01 04:53:33 -05:00
|
|
|
func interpolateAuthParams(tokenAuth *plugins.JWTTokenAuth, data templateData) (*plugins.JWTTokenAuth, error) {
|
2021-05-06 15:05:23 -05:00
|
|
|
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
|
2018-10-03 10:08:13 -05:00
|
|
|
}
|
2021-05-06 15:05:23 -05:00
|
|
|
interpolatedParams[key] = interpolatedParam
|
2018-10-03 10:08:13 -05:00
|
|
|
}
|
|
|
|
|
2021-11-01 04:53:33 -05:00
|
|
|
return &plugins.JWTTokenAuth{
|
2021-05-06 15:05:23 -05:00
|
|
|
Url: interpolatedUrl,
|
|
|
|
Scopes: tokenAuth.Scopes,
|
|
|
|
Params: interpolatedParams,
|
|
|
|
}, nil
|
2018-09-10 09:59:29 -05:00
|
|
|
}
|