From c23d7792801683b4799bcea5c718d4ddf9d2d65d Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Sun, 4 Feb 2024 14:33:51 +0100 Subject: [PATCH] WebClient: load shares using an async request instead of rendering them directly within the template Signed-off-by: Nicola Murino --- internal/httpd/httpd_test.go | 6 +-- internal/httpd/internal_test.go | 4 +- internal/httpd/server.go | 2 + internal/httpd/webclient.go | 28 +++++------- templates/webclient/shares.html | 81 +++++++++++++++++++++------------ 5 files changed, 70 insertions(+), 51 deletions(-) diff --git a/internal/httpd/httpd_test.go b/internal/httpd/httpd_test.go index 36bebfba..2f823c4a 100644 --- a/internal/httpd/httpd_test.go +++ b/internal/httpd/httpd_test.go @@ -7014,7 +7014,7 @@ func TestProviderErrors(t *testing.T) { assert.Equal(t, http.StatusOK, rr.Code) assert.Contains(t, rr.Body.String(), util.I18nErrorGetUser) - req, err = http.NewRequest(http.MethodGet, webClientSharesPath, nil) + req, err = http.NewRequest(http.MethodGet, webClientSharesPath+jsonAPISuffix, nil) assert.NoError(t, err) setJWTCookieForReq(req, userWebToken) rr = executeRequest(req) @@ -18518,14 +18518,14 @@ func TestWebUserShare(t *testing.T) { rr = executeRequest(req) checkResponseCode(t, http.StatusOK, rr) - req, err = http.NewRequest(http.MethodGet, webClientSharesPath+"?qlimit=aa", nil) + req, err = http.NewRequest(http.MethodGet, webClientSharesPath, nil) assert.NoError(t, err) req.RemoteAddr = defaultRemoteAddr setJWTCookieForReq(req, token) rr = executeRequest(req) checkResponseCode(t, http.StatusOK, rr) - req, err = http.NewRequest(http.MethodGet, webClientSharesPath+"?qlimit=1", nil) //nolint:goconst + req, err = http.NewRequest(http.MethodGet, webClientSharesPath+jsonAPISuffix, nil) //nolint:goconst assert.NoError(t, err) req.RemoteAddr = defaultRemoteAddr setJWTCookieForReq(req, token) diff --git a/internal/httpd/internal_test.go b/internal/httpd/internal_test.go index faac6250..9db01c4b 100644 --- a/internal/httpd/internal_test.go +++ b/internal/httpd/internal_test.go @@ -2645,9 +2645,9 @@ func TestWebUserInvalidClaims(t *testing.T) { assert.Contains(t, rr.Body.String(), util.I18nErrorInvalidToken) rr = httptest.NewRecorder() - req, _ = http.NewRequest(http.MethodGet, webClientSharesPath, nil) + req, _ = http.NewRequest(http.MethodGet, webClientSharesPath+jsonAPISuffix, nil) req.Header.Set("Cookie", fmt.Sprintf("jwt=%v", token["access_token"])) - server.handleClientGetShares(rr, req) + getAllShares(rr, req) assert.Equal(t, http.StatusForbidden, rr.Code) assert.Contains(t, rr.Body.String(), util.I18nErrorInvalidToken) diff --git a/internal/httpd/server.go b/internal/httpd/server.go index 637c8cc0..2df3f31d 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -1613,6 +1613,8 @@ func (s *httpdServer) setupWebClientRoutes() { Get(webClientRecoveryCodesPath, getRecoveryCodes) router.With(s.checkHTTPUserPerm(sdk.WebClientMFADisabled), verifyCSRFHeader). Post(webClientRecoveryCodesPath, generateRecoveryCodes) + router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientSharesDisabled), compressor.Handler, s.refreshCookie). + Get(webClientSharesPath+jsonAPISuffix, getAllShares) router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientSharesDisabled), s.refreshCookie). Get(webClientSharesPath, s.handleClientGetShares) router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientSharesDisabled), s.refreshCookie). diff --git a/internal/httpd/webclient.go b/internal/httpd/webclient.go index 2b540bd2..1d0fb3f7 100644 --- a/internal/httpd/webclient.go +++ b/internal/httpd/webclient.go @@ -197,7 +197,6 @@ type clientMFAPage struct { type clientSharesPage struct { baseClientPage - Shares []dataprovider.Share BasePublicSharesURL string } @@ -1515,36 +1514,33 @@ func (s *httpdServer) handleClientUpdateSharePost(w http.ResponseWriter, r *http } } -func (s *httpdServer) handleClientGetShares(w http.ResponseWriter, r *http.Request) { +func getAllShares(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) claims, err := getTokenClaims(r) if err != nil || claims.Username == "" { - s.renderClientForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken)) + sendAPIResponse(w, r, nil, util.I18nErrorInvalidToken, http.StatusForbidden) return } - limit := defaultQueryLimit - if _, ok := r.URL.Query()["qlimit"]; ok { - var err error - limit, err = strconv.Atoi(r.URL.Query().Get("qlimit")) - if err != nil { - limit = defaultQueryLimit - } - } - shares := make([]dataprovider.Share, 0, limit) + shares := make([]dataprovider.Share, 0, 10) for { - sh, err := dataprovider.GetShares(limit, len(shares), dataprovider.OrderASC, claims.Username) + sh, err := dataprovider.GetShares(defaultQueryLimit, len(shares), dataprovider.OrderASC, claims.Username) if err != nil { - s.renderClientInternalServerErrorPage(w, r, err) + sendAPIResponse(w, r, err, getI18NErrorString(err, util.I18nError500Message), http.StatusInternalServerError) return } shares = append(shares, sh...) - if len(sh) < limit { + if len(sh) < defaultQueryLimit { break } } + render.JSON(w, r, shares) +} + +func (s *httpdServer) handleClientGetShares(w http.ResponseWriter, r *http.Request) { + r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) + data := clientSharesPage{ baseClientPage: s.getBaseClientPageData(util.I18nSharesTitle, webClientSharesPath, r), - Shares: shares, BasePublicSharesURL: webClientPubSharesPath, } renderClientTemplate(w, templateClientShares, data) diff --git a/templates/webclient/shares.html b/templates/webclient/shares.html index d2720747..fc198487 100644 --- a/templates/webclient/shares.html +++ b/templates/webclient/shares.html @@ -20,6 +20,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). {{- end}} {{- define "page_body"}} +{{- template "errmsg" ""}}

View and manage shares

@@ -73,7 +74,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).