From 57701fd2f28f95f545569e6173b90050dc0e2913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 24 Apr 2023 09:55:55 +0200 Subject: [PATCH] ServeFromSubPath: Redirect to URL with subpath when subpath missing (#66724) * ServeFromSubPath: Redirect to URL with subpath when subpath missing * Review fixes * Added tests * Use constant * change to useMiddleware * Update pkg/middleware/subpath_redirect.go --------- Co-authored-by: Carl Bergquist --- pkg/api/http_server.go | 2 ++ pkg/middleware/subpath_redirect.go | 26 +++++++++++++++ pkg/middleware/subpath_redirect_test.go | 43 +++++++++++++++++++++++++ pkg/middleware/testing.go | 3 ++ 4 files changed, 74 insertions(+) create mode 100644 pkg/middleware/subpath_redirect.go create mode 100644 pkg/middleware/subpath_redirect_test.go diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 093eac80840..b8dcc68310c 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -602,6 +602,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { if hs.Cfg.ServeFromSubPath && hs.Cfg.AppSubURL != "" { m.SetURLPrefix(hs.Cfg.AppSubURL) + m.UseMiddleware(middleware.SubPathRedirect(hs.Cfg)) } m.UseMiddleware(web.Renderer(filepath.Join(hs.Cfg.StaticRootPath, "views"), "[[", "]]")) @@ -616,6 +617,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { m.UseMiddleware(hs.ContextHandler.Middleware) m.Use(middleware.OrgRedirect(hs.Cfg, hs.userService)) + if !hs.Features.IsEnabled(featuremgmt.FlagAuthnService) { m.Use(accesscontrol.LoadPermissionsMiddleware(hs.accesscontrolService)) } diff --git a/pkg/middleware/subpath_redirect.go b/pkg/middleware/subpath_redirect.go new file mode 100644 index 00000000000..2395399ad42 --- /dev/null +++ b/pkg/middleware/subpath_redirect.go @@ -0,0 +1,26 @@ +package middleware + +import ( + "fmt" + "net/http" + "strings" + + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/web" +) + +// SubPathRedirect Redirects URLs that are missing the configured subpath to an URL that contains the subpath. +func SubPathRedirect(cfg *setting.Cfg) web.Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + // Direct to url with subpath if the request is missing the subpath and is not an API request. + if !strings.HasPrefix(req.RequestURI, cfg.AppSubURL) && !strings.HasPrefix(req.RequestURI, "/api") { + newURL := fmt.Sprintf("%s%s", cfg.AppURL, strings.TrimPrefix(req.RequestURI, "/")) + http.Redirect(rw, req, newURL, http.StatusMovedPermanently) + return + } + + next.ServeHTTP(rw, req) + }) + } +} diff --git a/pkg/middleware/subpath_redirect_test.go b/pkg/middleware/subpath_redirect_test.go new file mode 100644 index 00000000000..ef2fd2c40f6 --- /dev/null +++ b/pkg/middleware/subpath_redirect_test.go @@ -0,0 +1,43 @@ +package middleware + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSubPathRedirect(t *testing.T) { + testSubPathRedirect(t, "/", "http://localhost:3000/subpath/") + testSubPathRedirect(t, "/admin/my/page", "http://localhost:3000/subpath/admin/my/page") + testSubPathRedirect(t, "/api/users", "") +} + +func testSubPathRedirect(t *testing.T, url string, expectedRedirect string) { + middlewareScenario(t, "GET url without subpath", func(t *testing.T, sc *scenarioContext) { + sc.cfg.AppSubURL = "/subpath" + sc.cfg.AppURL = "http://localhost:3000/subpath/" + + sc.m.UseMiddleware(SubPathRedirect(sc.cfg)) + sc.m.Get("/api/users", sc.defaultHandler) + + sc.fakeReqWithParams("GET", url, map[string]string{}).exec() + + if expectedRedirect != "" { + assert.Equal(t, 301, sc.resp.Code) + + // nolint:bodyclose + resp := sc.resp.Result() + t.Cleanup(func() { + err := resp.Body.Close() + assert.NoError(t, err) + }) + redirectURL, err := resp.Location() + require.NoError(t, err) + + assert.Equal(t, expectedRedirect, redirectURL.String()) + } else { + assert.Equal(t, 200, sc.resp.Code) + } + }) +} diff --git a/pkg/middleware/testing.go b/pkg/middleware/testing.go index 555d4d4f727..9c928d624c0 100644 --- a/pkg/middleware/testing.go +++ b/pkg/middleware/testing.go @@ -99,7 +99,10 @@ func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map for k, v := range queryParams { q.Add(k, v) } + req.URL.RawQuery = q.Encode() + req.RequestURI = req.URL.RequestURI() + require.NoError(sc.t, err) reqCtx := &contextmodel.ReqContext{