diff --git a/pkg/api/grafana_com_proxy.go b/pkg/api/grafana_com_proxy.go index 088b745b82f..9c2c0a23547 100644 --- a/pkg/api/grafana_com_proxy.go +++ b/pkg/api/grafana_com_proxy.go @@ -23,7 +23,7 @@ var grafanaComProxyTransport = &http.Transport{ TLSHandshakeTimeout: 10 * time.Second, } -func ReverseProxyGnetReq(logger log.Logger, proxyPath string, version string, grafanaComAPIUrl string) *httputil.ReverseProxy { +func ReverseProxyGnetReq(logger log.Logger, proxyPath, version, grafanaComAPIUrl, grafanaComAPIToken string) *httputil.ReverseProxy { url, _ := url.Parse(grafanaComAPIUrl) director := func(req *http.Request) { @@ -40,6 +40,10 @@ func ReverseProxyGnetReq(logger log.Logger, proxyPath string, version string, gr // send the current Grafana version for each request proxied to GCOM req.Header.Add("grafana-version", version) + + if grafanaComAPIToken != "" { + req.Header.Set("Authorization", "Bearer "+grafanaComAPIToken) + } } return proxyutil.NewReverseProxy(logger, director) @@ -47,7 +51,7 @@ func ReverseProxyGnetReq(logger log.Logger, proxyPath string, version string, gr func (hs *HTTPServer) ProxyGnetRequest(c *contextmodel.ReqContext) { proxyPath := web.Params(c.Req)["*"] - proxy := ReverseProxyGnetReq(c.Logger, proxyPath, hs.Cfg.BuildVersion, hs.Cfg.GrafanaComAPIURL) + proxy := ReverseProxyGnetReq(c.Logger, proxyPath, hs.Cfg.BuildVersion, hs.Cfg.GrafanaComAPIURL, hs.Cfg.PluginInstallToken) proxy.Transport = grafanaComProxyTransport proxy.ServeHTTP(c.Resp, c.Req) } diff --git a/pkg/plugins/config/config.go b/pkg/plugins/config/config.go index 37ba863c86f..a26c66176a1 100644 --- a/pkg/plugins/config/config.go +++ b/pkg/plugins/config/config.go @@ -18,7 +18,8 @@ type PluginManagementCfg struct { PluginsCDNURLTemplate string - GrafanaComAPIURL string + GrafanaComAPIURL string + GrafanaComAPIToken string GrafanaAppURL string @@ -38,7 +39,7 @@ type Features struct { // NewPluginManagementCfg returns a new PluginManagementCfg. func NewPluginManagementCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string, pluginsCDNURLTemplate string, appURL string, features Features, angularSupportEnabled bool, - grafanaComAPIURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string, + grafanaComAPIURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string, grafanaComAPIToken string, ) *PluginManagementCfg { return &PluginManagementCfg{ PluginsPath: pluginsPath, @@ -53,5 +54,6 @@ func NewPluginManagementCfg(devMode bool, pluginsPath string, pluginSettings set AngularSupportEnabled: angularSupportEnabled, HideAngularDeprecation: hideAngularDeprecation, ForwardHostEnvVars: forwardHostEnvVars, + GrafanaComAPIToken: grafanaComAPIToken, } } diff --git a/pkg/plugins/repo/client.go b/pkg/plugins/repo/client.go index 59140584f0a..29293537a66 100644 --- a/pkg/plugins/repo/client.go +++ b/pkg/plugins/repo/client.go @@ -23,15 +23,17 @@ type Client struct { httpClient http.Client httpClientNoTimeout http.Client retryCount int + grafanaComAPIToken string log log.PrettyLogger } -func NewClient(skipTLSVerify bool, logger log.PrettyLogger) *Client { +func NewClient(skipTLSVerify bool, grafanaComAPIToken string, logger log.PrettyLogger) *Client { return &Client{ httpClient: MakeHttpClient(skipTLSVerify, 10*time.Second), httpClientNoTimeout: MakeHttpClient(skipTLSVerify, 0), log: logger, + grafanaComAPIToken: grafanaComAPIToken, } } @@ -153,6 +155,14 @@ func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL, // Note: This is also used as part of the grafana plugin install CLI operation bodyReader, err := c.sendReqNoTimeout(ctx, u, compatOpts) if err != nil { + var errResp ErrResponse4xx + if errors.As(err, &errResp) { + if errResp.StatusCode() == 401 { + c.log.Error("Unauthorized download plugin", "error", err) + return err + } + } + if c.retryCount < 3 { c.retryCount++ c.log.Debug("Failed downloading. Will retry.") @@ -223,6 +233,10 @@ func (c *Client) createReq(ctx context.Context, url *url.URL, compatOpts CompatO req.Header.Set("grafana-origin", orig.(string)) } + if c.grafanaComAPIToken != "" { + req.Header.Set("Authorization", "Bearer "+c.grafanaComAPIToken) + } + return req, err } diff --git a/pkg/plugins/repo/client_test.go b/pkg/plugins/repo/client_test.go index 400944da0d1..6ffdc76c2a7 100644 --- a/pkg/plugins/repo/client_test.go +++ b/pkg/plugins/repo/client_test.go @@ -69,4 +69,22 @@ func Test_Download(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, count, "should retry on error") }) + + t.Run("it should use gcom token when it's available", func(t *testing.T) { + expectedToken := "token-test" + var gcomCalled bool + fakeServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.Header.Get("Authorization") + require.Equal(t, "Bearer "+expectedToken, token, "gcom token should be set") + err := writeFakeZip(w) + require.NoError(t, err) + gcomCalled = true + })) + defer fakeServer.Close() + cli := fakeServer.Client() + repo := Client{httpClient: *cli, httpClientNoTimeout: *cli, log: log.NewPrettyLogger("test"), grafanaComAPIToken: expectedToken} + _, err := repo.Download(context.Background(), fakeServer.URL, "", CompatOpts{}) + require.NoError(t, err) + require.True(t, gcomCalled) + }) } diff --git a/pkg/plugins/repo/service.go b/pkg/plugins/repo/service.go index f9d2d5e5bbc..8007a03d609 100644 --- a/pkg/plugins/repo/service.go +++ b/pkg/plugins/repo/service.go @@ -27,22 +27,24 @@ func ProvideService(cfg *config.PluginManagementCfg) (*Manager, error) { } return NewManager(ManagerCfg{ - SkipTLSVerify: false, - BaseURL: baseURL, - Logger: log.NewPrettyLogger("plugin.repository"), + SkipTLSVerify: false, + BaseURL: baseURL, + Logger: log.NewPrettyLogger("plugin.repository"), + GrafanaComAPIToken: cfg.GrafanaComAPIToken, }), nil } type ManagerCfg struct { - SkipTLSVerify bool - BaseURL string - Logger log.PrettyLogger + SkipTLSVerify bool + BaseURL string + GrafanaComAPIToken string + Logger log.PrettyLogger } func NewManager(cfg ManagerCfg) *Manager { return &Manager{ baseURL: cfg.BaseURL, - client: NewClient(cfg.SkipTLSVerify, cfg.Logger), + client: NewClient(cfg.SkipTLSVerify, cfg.GrafanaComAPIToken, cfg.Logger), log: cfg.Logger, } } diff --git a/pkg/services/pluginsintegration/pluginconfig/config.go b/pkg/services/pluginsintegration/pluginconfig/config.go index 56b20aa6e1a..d2ae8849fd6 100644 --- a/pkg/services/pluginsintegration/pluginconfig/config.go +++ b/pkg/services/pluginsintegration/pluginconfig/config.go @@ -39,6 +39,7 @@ func ProvidePluginManagementConfig(cfg *setting.Cfg, settingProvider setting.Pro cfg.DisablePlugins, cfg.HideAngularDeprecation, cfg.ForwardHostEnvVars, + cfg.PluginInstallToken, ), nil }