WebUI: extract a common struct for all pages

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino
2023-11-25 18:30:56 +01:00
parent ed828458ab
commit 74836af66e
4 changed files with 130 additions and 140 deletions

View File

@@ -162,14 +162,13 @@ func (s *httpdServer) refreshCookie(next http.Handler) http.Handler {
func (s *httpdServer) renderClientLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderClientLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := loginPage{ data := loginPage{
CurrentURL: webClientLoginPath, commonBasePage: getCommonBasePage(r),
Version: version.Get().Version, CurrentURL: webClientLoginPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient, FormDisabled: s.binding.isWebClientLoginFormDisabled(),
FormDisabled: s.binding.isWebClientLoginFormDisabled(),
} }
if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) { if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) {
data.CurrentURL += "?next=" + url.QueryEscape(next) data.CurrentURL += "?next=" + url.QueryEscape(next)
@@ -575,14 +574,13 @@ func (s *httpdServer) handleWebAdminLoginPost(w http.ResponseWriter, r *http.Req
func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderAdminLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := loginPage{ data := loginPage{
CurrentURL: webAdminLoginPath, commonBasePage: getCommonBasePage(r),
Version: version.Get().Version, CurrentURL: webAdminLoginPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebAdmin,
Branding: s.binding.Branding.WebAdmin, FormDisabled: s.binding.isWebAdminLoginFormDisabled(),
FormDisabled: s.binding.isWebAdminLoginFormDisabled(),
} }
if s.binding.showClientLoginURL() { if s.binding.showClientLoginURL() {
data.AltLoginURL = webClientLoginPath data.AltLoginURL = webClientLoginPath

View File

@@ -15,7 +15,10 @@
package httpd package httpd
import ( import (
"net/http"
"strings" "strings"
"github.com/unrolled/secure"
) )
const ( const (
@@ -41,13 +44,17 @@ const (
templateCommonBase = "base.html" templateCommonBase = "base.html"
) )
type commonBasePage struct {
CSPNonce string
StaticURL string
}
type loginPage struct { type loginPage struct {
commonBasePage
CurrentURL string CurrentURL string
Version string Version string
Error string Error string
CSRFToken string CSRFToken string
CSPNonce string
StaticURL string
AltLoginURL string AltLoginURL string
AltLoginName string AltLoginName string
ForgotPwdURL string ForgotPwdURL string
@@ -57,34 +64,31 @@ type loginPage struct {
} }
type twoFactorPage struct { type twoFactorPage struct {
commonBasePage
CurrentURL string CurrentURL string
Version string Version string
Error string Error string
CSRFToken string CSRFToken string
CSPNonce string
StaticURL string
RecoveryURL string RecoveryURL string
Title string Title string
Branding UIBranding Branding UIBranding
} }
type forgotPwdPage struct { type forgotPwdPage struct {
commonBasePage
CurrentURL string CurrentURL string
Error string Error string
CSRFToken string CSRFToken string
CSPNonce string
StaticURL string
LoginURL string LoginURL string
Title string Title string
Branding UIBranding Branding UIBranding
} }
type resetPwdPage struct { type resetPwdPage struct {
commonBasePage
CurrentURL string CurrentURL string
Error string Error string
CSRFToken string CSRFToken string
CSPNonce string
StaticURL string
LoginURL string LoginURL string
Title string Title string
Branding UIBranding Branding UIBranding
@@ -104,3 +108,10 @@ func getSliceFromDelimitedValues(values, delimiter string) []string {
func hasPrefixAndSuffix(key, prefix, suffix string) bool { func hasPrefixAndSuffix(key, prefix, suffix string) bool {
return strings.HasPrefix(key, prefix) && strings.HasSuffix(key, suffix) return strings.HasPrefix(key, prefix) && strings.HasSuffix(key, suffix)
} }
func getCommonBasePage(r *http.Request) commonBasePage {
return commonBasePage{
CSPNonce: secure.CSPNonce(r.Context()),
StaticURL: webStaticFilesPath,
}
}

View File

@@ -32,7 +32,6 @@ import (
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/sftpgo/sdk" "github.com/sftpgo/sdk"
sdkkms "github.com/sftpgo/sdk/kms" sdkkms "github.com/sftpgo/sdk/kms"
"github.com/unrolled/secure"
"github.com/drakkan/sftpgo/v2/internal/acme" "github.com/drakkan/sftpgo/v2/internal/acme"
"github.com/drakkan/sftpgo/v2/internal/common" "github.com/drakkan/sftpgo/v2/internal/common"
@@ -132,6 +131,7 @@ var (
) )
type basePage struct { type basePage struct {
commonBasePage
Title string Title string
CurrentURL string CurrentURL string
UsersURL string UsersURL string
@@ -164,7 +164,6 @@ type basePage struct {
FolderQuotaScanURL string FolderQuotaScanURL string
StatusURL string StatusURL string
MaintenanceURL string MaintenanceURL string
StaticURL string
UsersTitle string UsersTitle string
AdminsTitle string AdminsTitle string
ConnectionsTitle string ConnectionsTitle string
@@ -181,7 +180,6 @@ type basePage struct {
ConfigsTitle string ConfigsTitle string
Version string Version string
CSRFToken string CSRFToken string
CSPNonce string
IsEventManagerPage bool IsEventManagerPage bool
IsIPManagerPage bool IsIPManagerPage bool
IsServerManagerPage bool IsServerManagerPage bool
@@ -697,6 +695,7 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
csrfToken = createCSRFToken(util.GetIPFromRemoteAddress(r.RemoteAddr)) csrfToken = createCSRFToken(util.GetIPFromRemoteAddress(r.RemoteAddr))
} }
return basePage{ return basePage{
commonBasePage: getCommonBasePage(r),
Title: title, Title: title,
CurrentURL: currentURL, CurrentURL: currentURL,
UsersURL: webUsersPath, UsersURL: webUsersPath,
@@ -729,7 +728,6 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
StatusURL: webStatusPath, StatusURL: webStatusPath,
FolderQuotaScanURL: webScanVFolderPath, FolderQuotaScanURL: webScanVFolderPath,
MaintenanceURL: webMaintenancePath, MaintenanceURL: webMaintenancePath,
StaticURL: webStaticFilesPath,
UsersTitle: pageUsersTitle, UsersTitle: pageUsersTitle,
AdminsTitle: pageAdminsTitle, AdminsTitle: pageAdminsTitle,
ConnectionsTitle: pageConnectionsTitle, ConnectionsTitle: pageConnectionsTitle,
@@ -753,7 +751,6 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
HasSearcher: plugin.Handler.HasSearcher(), HasSearcher: plugin.Handler.HasSearcher(),
HasExternalLogin: isLoggedInWithOIDC(r), HasExternalLogin: isLoggedInWithOIDC(r),
CSRFToken: csrfToken, CSRFToken: csrfToken,
CSPNonce: secure.CSPNonce(r.Context()),
Branding: s.binding.Branding.WebAdmin, Branding: s.binding.Branding.WebAdmin,
} }
} }
@@ -802,55 +799,51 @@ func (s *httpdServer) renderNotFoundPage(w http.ResponseWriter, r *http.Request,
func (s *httpdServer) renderForgotPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderForgotPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := forgotPwdPage{ data := forgotPwdPage{
CurrentURL: webAdminForgotPwdPath, commonBasePage: getCommonBasePage(r),
Error: error, CurrentURL: webAdminForgotPwdPath,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Title: pageForgotPwdTitle,
Title: pageForgotPwdTitle, Branding: s.binding.Branding.WebAdmin,
Branding: s.binding.Branding.WebAdmin,
} }
renderAdminTemplate(w, templateForgotPassword, data) renderAdminTemplate(w, templateForgotPassword, data)
} }
func (s *httpdServer) renderResetPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderResetPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := resetPwdPage{ data := resetPwdPage{
CurrentURL: webAdminResetPwdPath, commonBasePage: getCommonBasePage(r),
Error: error, CurrentURL: webAdminResetPwdPath,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Title: pageResetPwdTitle,
Title: pageResetPwdTitle, Branding: s.binding.Branding.WebAdmin,
Branding: s.binding.Branding.WebAdmin,
} }
renderAdminTemplate(w, templateResetPassword, data) renderAdminTemplate(w, templateResetPassword, data)
} }
func (s *httpdServer) renderTwoFactorPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderTwoFactorPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := twoFactorPage{ data := twoFactorPage{
Title: pageTwoFactorTitle, commonBasePage: getCommonBasePage(r),
CurrentURL: webAdminTwoFactorPath, Title: pageTwoFactorTitle,
Version: version.Get().Version, CurrentURL: webAdminTwoFactorPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, RecoveryURL: webAdminTwoFactorRecoveryPath,
RecoveryURL: webAdminTwoFactorRecoveryPath, Branding: s.binding.Branding.WebAdmin,
Branding: s.binding.Branding.WebAdmin,
} }
renderAdminTemplate(w, templateTwoFactor, data) renderAdminTemplate(w, templateTwoFactor, data)
} }
func (s *httpdServer) renderTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := twoFactorPage{ data := twoFactorPage{
Title: pageTwoFactorRecoveryTitle, commonBasePage: getCommonBasePage(r),
CurrentURL: webAdminTwoFactorRecoveryPath, Title: pageTwoFactorRecoveryTitle,
Version: version.Get().Version, CurrentURL: webAdminTwoFactorRecoveryPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebAdmin,
Branding: s.binding.Branding.WebAdmin,
} }
renderAdminTemplate(w, templateTwoFactorRecovery, data) renderAdminTemplate(w, templateTwoFactorRecovery, data)
} }

View File

@@ -34,7 +34,6 @@ import (
"github.com/go-chi/render" "github.com/go-chi/render"
"github.com/rs/xid" "github.com/rs/xid"
"github.com/sftpgo/sdk" "github.com/sftpgo/sdk"
"github.com/unrolled/secure"
"github.com/drakkan/sftpgo/v2/internal/common" "github.com/drakkan/sftpgo/v2/internal/common"
"github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/dataprovider"
@@ -99,6 +98,7 @@ func isZeroTime(t time.Time) bool {
} }
type baseClientPage struct { type baseClientPage struct {
commonBasePage
Title string Title string
CurrentURL string CurrentURL string
FilesURL string FilesURL string
@@ -107,7 +107,6 @@ type baseClientPage struct {
ProfileURL string ProfileURL string
PingURL string PingURL string
ChangePwdURL string ChangePwdURL string
StaticURL string
LogoutURL string LogoutURL string
LoginURL string LoginURL string
EditURL string EditURL string
@@ -118,7 +117,6 @@ type baseClientPage struct {
ProfileTitle string ProfileTitle string
Version string Version string
CSRFToken string CSRFToken string
CSPNonce string
LoggedUser *dataprovider.User LoggedUser *dataprovider.User
Branding UIBranding Branding UIBranding
} }
@@ -129,11 +127,10 @@ type dirMapping struct {
} }
type viewPDFPage struct { type viewPDFPage struct {
Title string commonBasePage
URL string Title string
StaticURL string URL string
CSPNonce string Branding UIBranding
Branding UIBranding
} }
type editFilePage struct { type editFilePage struct {
@@ -167,12 +164,11 @@ type filesPage struct {
} }
type shareLoginPage struct { type shareLoginPage struct {
commonBasePage
CurrentURL string CurrentURL string
Version string Version string
Error string Error string
CSRFToken string CSRFToken string
CSPNonce string
StaticURL string
Branding UIBranding Branding UIBranding
} }
@@ -553,27 +549,26 @@ func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Re
v := version.Get() v := version.Get()
data := baseClientPage{ data := baseClientPage{
Title: title, commonBasePage: getCommonBasePage(r),
CurrentURL: currentURL, Title: title,
FilesURL: webClientFilesPath, CurrentURL: currentURL,
SharesURL: webClientSharesPath, FilesURL: webClientFilesPath,
ShareURL: webClientSharePath, SharesURL: webClientSharesPath,
ProfileURL: webClientProfilePath, ShareURL: webClientSharePath,
PingURL: webClientPingPath, ProfileURL: webClientProfilePath,
ChangePwdURL: webChangeClientPwdPath, PingURL: webClientPingPath,
StaticURL: webStaticFilesPath, ChangePwdURL: webChangeClientPwdPath,
LogoutURL: webClientLogoutPath, LogoutURL: webClientLogoutPath,
EditURL: webClientEditFilePath, EditURL: webClientEditFilePath,
MFAURL: webClientMFAPath, MFAURL: webClientMFAPath,
MFATitle: pageClient2FATitle, MFATitle: pageClient2FATitle,
FilesTitle: pageClientFilesTitle, FilesTitle: pageClientFilesTitle,
SharesTitle: pageClientSharesTitle, SharesTitle: pageClientSharesTitle,
ProfileTitle: pageClientProfileTitle, ProfileTitle: pageClientProfileTitle,
Version: fmt.Sprintf("%v-%v", v.Version, v.CommitHash), Version: fmt.Sprintf("%v-%v", v.Version, v.CommitHash),
CSRFToken: csrfToken, CSRFToken: csrfToken,
CSPNonce: secure.CSPNonce(r.Context()), LoggedUser: getUserFromToken(r),
LoggedUser: getUserFromToken(r), Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
if !strings.HasPrefix(r.RequestURI, webClientPubSharesPath) { if !strings.HasPrefix(r.RequestURI, webClientPubSharesPath) {
data.LoginURL = webClientLoginPath data.LoginURL = webClientLoginPath
@@ -583,41 +578,38 @@ func (s *httpdServer) getBaseClientPageData(title, currentURL string, r *http.Re
func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderClientForgotPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := forgotPwdPage{ data := forgotPwdPage{
CurrentURL: webClientForgotPwdPath, commonBasePage: getCommonBasePage(r),
Error: error, CurrentURL: webClientForgotPwdPath,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, LoginURL: webClientLoginPath,
LoginURL: webClientLoginPath, Title: pageClientForgotPwdTitle,
Title: pageClientForgotPwdTitle, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateForgotPassword, data) renderClientTemplate(w, templateForgotPassword, data)
} }
func (s *httpdServer) renderClientResetPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderClientResetPwdPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := resetPwdPage{ data := resetPwdPage{
CurrentURL: webClientResetPwdPath, commonBasePage: getCommonBasePage(r),
Error: error, CurrentURL: webClientResetPwdPath,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, LoginURL: webClientLoginPath,
LoginURL: webClientLoginPath, Title: pageClientResetPwdTitle,
Title: pageClientResetPwdTitle, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateResetPassword, data) renderClientTemplate(w, templateResetPassword, data)
} }
func (s *httpdServer) renderShareLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderShareLoginPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := shareLoginPage{ data := shareLoginPage{
CurrentURL: r.RequestURI, commonBasePage: getCommonBasePage(r),
Version: version.Get().Version, CurrentURL: r.RequestURI,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateShareLogin, data) renderClientTemplate(w, templateShareLogin, data)
} }
@@ -665,15 +657,14 @@ func (s *httpdServer) renderClientNotFoundPage(w http.ResponseWriter, r *http.Re
func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := twoFactorPage{ data := twoFactorPage{
Title: pageTwoFactorTitle, commonBasePage: getCommonBasePage(r),
CurrentURL: webClientTwoFactorPath, Title: pageTwoFactorTitle,
Version: version.Get().Version, CurrentURL: webClientTwoFactorPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, RecoveryURL: webClientTwoFactorRecoveryPath,
RecoveryURL: webClientTwoFactorRecoveryPath, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) { if next := r.URL.Query().Get("next"); strings.HasPrefix(next, webClientFilesPath) {
data.CurrentURL += "?next=" + url.QueryEscape(next) data.CurrentURL += "?next=" + url.QueryEscape(next)
@@ -683,14 +674,13 @@ func (s *httpdServer) renderClientTwoFactorPage(w http.ResponseWriter, r *http.R
func (s *httpdServer) renderClientTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, error, ip string) { func (s *httpdServer) renderClientTwoFactorRecoveryPage(w http.ResponseWriter, r *http.Request, error, ip string) {
data := twoFactorPage{ data := twoFactorPage{
Title: pageTwoFactorRecoveryTitle, commonBasePage: getCommonBasePage(r),
CurrentURL: webClientTwoFactorRecoveryPath, Title: pageTwoFactorRecoveryTitle,
Version: version.Get().Version, CurrentURL: webClientTwoFactorRecoveryPath,
Error: error, Version: version.Get().Version,
CSRFToken: createCSRFToken(ip), Error: error,
CSPNonce: secure.CSPNonce(r.Context()), CSRFToken: createCSRFToken(ip),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateTwoFactorRecovery, data) renderClientTemplate(w, templateTwoFactorRecovery, data)
} }
@@ -1088,12 +1078,11 @@ func (s *httpdServer) handleShareViewPDF(w http.ResponseWriter, r *http.Request)
} }
name := util.CleanPath(r.URL.Query().Get("path")) name := util.CleanPath(r.URL.Query().Get("path"))
data := viewPDFPage{ data := viewPDFPage{
Title: path.Base(name), commonBasePage: getCommonBasePage(r),
Title: path.Base(name),
URL: fmt.Sprintf("%s?path=%s&_=%d", path.Join(webClientPubSharesPath, share.ShareID, "getpdf"), URL: fmt.Sprintf("%s?path=%s&_=%d", path.Join(webClientPubSharesPath, share.ShareID, "getpdf"),
url.QueryEscape(name), time.Now().UTC().Unix()), url.QueryEscape(name), time.Now().UTC().Unix()),
StaticURL: webStaticFilesPath, Branding: s.binding.Branding.WebClient,
CSPNonce: secure.CSPNonce(r.Context()),
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateClientViewPDF, data) renderClientTemplate(w, templateClientViewPDF, data)
} }
@@ -1704,11 +1693,10 @@ func (s *httpdServer) handleClientViewPDF(w http.ResponseWriter, r *http.Request
} }
name = util.CleanPath(name) name = util.CleanPath(name)
data := viewPDFPage{ data := viewPDFPage{
Title: path.Base(name), commonBasePage: getCommonBasePage(r),
URL: fmt.Sprintf("%s?path=%s&_=%d", webClientGetPDFPath, url.QueryEscape(name), time.Now().UTC().Unix()), Title: path.Base(name),
StaticURL: webStaticFilesPath, URL: fmt.Sprintf("%s?path=%s&_=%d", webClientGetPDFPath, url.QueryEscape(name), time.Now().UTC().Unix()),
CSPNonce: secure.CSPNonce(r.Context()), Branding: s.binding.Branding.WebClient,
Branding: s.binding.Branding.WebClient,
} }
renderClientTemplate(w, templateClientViewPDF, data) renderClientTemplate(w, templateClientViewPDF, data)
} }