web.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // Copyright (C) 2019-2023 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package httpd
  15. import (
  16. "errors"
  17. "fmt"
  18. "net/http"
  19. "strings"
  20. "github.com/unrolled/secure"
  21. "github.com/drakkan/sftpgo/v2/internal/util"
  22. "github.com/drakkan/sftpgo/v2/internal/version"
  23. )
  24. const (
  25. pageMFATitle = "Two-factor authentication"
  26. page400Title = "Bad request"
  27. page403Title = "Forbidden"
  28. page404Title = "Not found"
  29. page404Body = "The page you are looking for does not exist."
  30. page500Title = "Internal Server Error"
  31. page500Body = "The server is unable to fulfill your request."
  32. pageTwoFactorTitle = "Two-Factor authentication"
  33. pageTwoFactorRecoveryTitle = "Two-Factor recovery"
  34. webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
  35. redactedSecret = "[**redacted**]"
  36. csrfFormToken = "_form_token"
  37. csrfHeaderToken = "X-CSRF-TOKEN"
  38. templateCommonDir = "common"
  39. templateTwoFactor = "twofactor.html"
  40. templateTwoFactorRecovery = "twofactor-recovery.html"
  41. templateForgotPassword = "forgot-password.html"
  42. templateResetPassword = "reset-password.html"
  43. templateCommonCSS = "sftpgo.css"
  44. templateCommonBase = "base.html"
  45. )
  46. var (
  47. errInvalidTokenClaims = errors.New("invalid token claims")
  48. )
  49. type commonBasePage struct {
  50. CSPNonce string
  51. StaticURL string
  52. Version string
  53. }
  54. type loginPage struct {
  55. commonBasePage
  56. CurrentURL string
  57. Error string
  58. CSRFToken string
  59. AltLoginURL string
  60. AltLoginName string
  61. ForgotPwdURL string
  62. OpenIDLoginURL string
  63. Title string
  64. Branding UIBranding
  65. FormDisabled bool
  66. }
  67. type twoFactorPage struct {
  68. commonBasePage
  69. CurrentURL string
  70. Error string
  71. CSRFToken string
  72. RecoveryURL string
  73. Title string
  74. Branding UIBranding
  75. }
  76. type forgotPwdPage struct {
  77. commonBasePage
  78. CurrentURL string
  79. Error string
  80. CSRFToken string
  81. LoginURL string
  82. Title string
  83. Branding UIBranding
  84. }
  85. type resetPwdPage struct {
  86. commonBasePage
  87. CurrentURL string
  88. Error string
  89. CSRFToken string
  90. LoginURL string
  91. Title string
  92. Branding UIBranding
  93. }
  94. func getSliceFromDelimitedValues(values, delimiter string) []string {
  95. result := []string{}
  96. for _, v := range strings.Split(values, delimiter) {
  97. cleaned := strings.TrimSpace(v)
  98. if cleaned != "" {
  99. result = append(result, cleaned)
  100. }
  101. }
  102. return result
  103. }
  104. func hasPrefixAndSuffix(key, prefix, suffix string) bool {
  105. return strings.HasPrefix(key, prefix) && strings.HasSuffix(key, suffix)
  106. }
  107. func getCommonBasePage(r *http.Request) commonBasePage {
  108. v := version.Get()
  109. return commonBasePage{
  110. CSPNonce: secure.CSPNonce(r.Context()),
  111. StaticURL: webStaticFilesPath,
  112. Version: fmt.Sprintf("v%v-%v", v.Version, v.CommitHash),
  113. }
  114. }
  115. func i18nListDirMsg(status int) string {
  116. if status == http.StatusForbidden {
  117. return util.I18nErrorDirList403
  118. }
  119. return util.I18nErrorDirListGeneric
  120. }
  121. func i18nFsMsg(status int) string {
  122. if status == http.StatusForbidden {
  123. return util.I18nError403Message
  124. }
  125. return util.I18nErrorFsGeneric
  126. }
  127. func getI18NErrorString(err error, fallback string) string {
  128. var errI18n *util.I18nError
  129. if errors.As(err, &errI18n) {
  130. return errI18n.I18nMessage
  131. }
  132. return fallback
  133. }