mirror of https://github.com/grafana/grafana
parent
5f80bf5297
commit
e6857bf17d
@ -1,79 +0,0 @@ |
||||
package api |
||||
|
||||
import ( |
||||
"context" |
||||
"crypto/tls" |
||||
"net" |
||||
"net/http" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy" |
||||
"github.com/grafana/grafana/pkg/infra/log" |
||||
"github.com/grafana/grafana/pkg/middleware" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/plugins" |
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol" |
||||
"github.com/grafana/grafana/pkg/services/org" |
||||
"github.com/grafana/grafana/pkg/util" |
||||
"github.com/grafana/grafana/pkg/web" |
||||
) |
||||
|
||||
var pluginProxyTransport *http.Transport |
||||
var applog = log.New("app.routes") |
||||
|
||||
func (hs *HTTPServer) initAppPluginRoutes(r *web.Mux) { |
||||
pluginProxyTransport = &http.Transport{ |
||||
TLSClientConfig: &tls.Config{ |
||||
InsecureSkipVerify: hs.Cfg.PluginsAppsSkipVerifyTLS, |
||||
Renegotiation: tls.RenegotiateFreelyAsClient, |
||||
}, |
||||
Proxy: http.ProxyFromEnvironment, |
||||
DialContext: (&net.Dialer{ |
||||
Timeout: 30 * time.Second, |
||||
KeepAlive: 30 * time.Second, |
||||
}).DialContext, |
||||
TLSHandshakeTimeout: 10 * time.Second, |
||||
} |
||||
|
||||
for _, plugin := range hs.pluginStore.Plugins(context.Background(), plugins.App) { |
||||
for _, route := range plugin.Routes { |
||||
url := util.JoinURLFragments("/api/plugin-proxy/"+plugin.ID, route.Path) |
||||
handlers := make([]web.Handler, 0) |
||||
handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ |
||||
ReqSignedIn: true, |
||||
})) |
||||
|
||||
// Preventing access to plugin routes if the user has no right to access the plugin
|
||||
authorize := ac.Middleware(hs.AccessControl) |
||||
handlers = append(handlers, authorize(middleware.ReqSignedIn, |
||||
ac.EvalPermission(plugins.ActionAppAccess, plugins.ScopeProvider.GetResourceScope(plugin.ID)))) |
||||
|
||||
if route.ReqRole != "" { |
||||
if route.ReqRole == org.RoleAdmin { |
||||
handlers = append(handlers, middleware.RoleAuth(org.RoleAdmin)) |
||||
} else if route.ReqRole == org.RoleEditor { |
||||
handlers = append(handlers, middleware.RoleAuth(org.RoleEditor, org.RoleAdmin)) |
||||
} |
||||
} |
||||
|
||||
handlers = append(handlers, AppPluginRoute(route, plugin.ID, hs)) |
||||
for _, method := range strings.Split(route.Method, ",") { |
||||
r.Handle(strings.TrimSpace(method), url, handlers) |
||||
} |
||||
|
||||
applog.Debug("Plugins: Adding proxy route", "url", url) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func AppPluginRoute(route *plugins.Route, appID string, hs *HTTPServer) web.Handler { |
||||
return func(c *models.ReqContext) { |
||||
path := web.Params(c.Req)["*"] |
||||
|
||||
proxy := pluginproxy.NewApiPluginProxy(c, path, route, appID, hs.Cfg, hs.PluginSettings, hs.SecretsService) |
||||
proxy.Transport = pluginProxyTransport |
||||
|
||||
proxy.ServeHTTP(c.Resp, c.Req) |
||||
} |
||||
} |
@ -0,0 +1,68 @@ |
||||
package api |
||||
|
||||
import ( |
||||
"crypto/tls" |
||||
"net" |
||||
"net/http" |
||||
"regexp" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/grafana/grafana/pkg/api/pluginproxy" |
||||
"github.com/grafana/grafana/pkg/models" |
||||
"github.com/grafana/grafana/pkg/services/pluginsettings" |
||||
"github.com/grafana/grafana/pkg/web" |
||||
) |
||||
|
||||
func (hs *HTTPServer) ProxyPluginRequest(c *models.ReqContext) { |
||||
var once sync.Once |
||||
var pluginProxyTransport *http.Transport |
||||
once.Do(func() { |
||||
pluginProxyTransport = &http.Transport{ |
||||
TLSClientConfig: &tls.Config{ |
||||
InsecureSkipVerify: hs.Cfg.PluginsAppsSkipVerifyTLS, |
||||
Renegotiation: tls.RenegotiateFreelyAsClient, |
||||
}, |
||||
Proxy: http.ProxyFromEnvironment, |
||||
DialContext: (&net.Dialer{ |
||||
Timeout: 30 * time.Second, |
||||
KeepAlive: 30 * time.Second, |
||||
}).DialContext, |
||||
TLSHandshakeTimeout: 10 * time.Second, |
||||
} |
||||
}) |
||||
|
||||
pluginID := web.Params(c.Req)[":pluginId"] |
||||
|
||||
plugin, exists := hs.pluginStore.Plugin(c.Req.Context(), pluginID) |
||||
if !exists { |
||||
c.JsonApiErr(http.StatusNotFound, "Plugin not found, no installed plugin with that id", nil) |
||||
return |
||||
} |
||||
|
||||
query := pluginsettings.GetByPluginIDArgs{OrgID: c.OrgID, PluginID: plugin.ID} |
||||
ps, err := hs.PluginSettings.GetPluginSettingByPluginID(c.Req.Context(), &query) |
||||
if err != nil { |
||||
c.JsonApiErr(http.StatusInternalServerError, "Failed to fetch plugin settings", err) |
||||
return |
||||
} |
||||
|
||||
proxyPath := getProxyPath(c) |
||||
p, err := pluginproxy.NewPluginProxy(ps, plugin.Routes, c, proxyPath, hs.Cfg, hs.SecretsService, hs.tracer, pluginProxyTransport) |
||||
if err != nil { |
||||
c.JsonApiErr(http.StatusInternalServerError, "Failed to create plugin proxy", err) |
||||
return |
||||
} |
||||
|
||||
p.HandleRequest() |
||||
} |
||||
|
||||
var pluginProxyPathRegexp = regexp.MustCompile(`^\/api\/plugin-proxy\/([\w\-]+)\/?`) |
||||
|
||||
func extractProxyPath(originalRawPath string) string { |
||||
return pluginProxyPathRegexp.ReplaceAllString(originalRawPath, "") |
||||
} |
||||
|
||||
func getProxyPath(c *models.ReqContext) string { |
||||
return extractProxyPath(c.Req.URL.EscapedPath()) |
||||
} |
@ -0,0 +1,36 @@ |
||||
package api |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
) |
||||
|
||||
func TestExtractPluginProxyPath(t *testing.T) { |
||||
testCases := []struct { |
||||
originalRawPath string |
||||
exp string |
||||
}{ |
||||
{ |
||||
"/api/plugin-proxy/test", |
||||
"", |
||||
}, |
||||
{ |
||||
"/api/plugin-proxy/test/some/thing", |
||||
"some/thing", |
||||
}, |
||||
{ |
||||
"/api/plugin-proxy/test2/api/services/afsd%2Fafsd/operations", |
||||
"api/services/afsd%2Fafsd/operations", |
||||
}, |
||||
{ |
||||
"/api/plugin-proxy/cloudflare-app/with-token/api/v4/accounts", |
||||
"with-token/api/v4/accounts", |
||||
}, |
||||
} |
||||
for _, tc := range testCases { |
||||
t.Run("Given raw path, should extract expected proxy path", func(t *testing.T) { |
||||
assert.Equal(t, tc.exp, extractProxyPath(tc.originalRawPath)) |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue