web.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // Copyright (C) 2019 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. "net/http"
  18. "strings"
  19. "github.com/go-chi/render"
  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. pageTwoFactorTitle = "Two-Factor authentication"
  27. pageTwoFactorRecoveryTitle = "Two-Factor recovery"
  28. webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
  29. redactedSecret = "[**redacted**]"
  30. csrfFormToken = "_form_token"
  31. csrfHeaderToken = "X-CSRF-TOKEN"
  32. templateCommonDir = "common"
  33. templateTwoFactor = "twofactor.html"
  34. templateTwoFactorRecovery = "twofactor-recovery.html"
  35. templateForgotPassword = "forgot-password.html"
  36. templateResetPassword = "reset-password.html"
  37. templateChangePwd = "changepassword.html"
  38. templateMessage = "message.html"
  39. templateCommonBase = "base.html"
  40. templateCommonBaseLogin = "baselogin.html"
  41. templateCommonLogin = "login.html"
  42. )
  43. var (
  44. errInvalidTokenClaims = errors.New("invalid token claims")
  45. )
  46. type commonBasePage struct {
  47. CSPNonce string
  48. StaticURL string
  49. Version string
  50. }
  51. type loginPage struct {
  52. commonBasePage
  53. CurrentURL string
  54. Error *util.I18nError
  55. CSRFToken string
  56. AltLoginURL string
  57. AltLoginName string
  58. ForgotPwdURL string
  59. OpenIDLoginURL string
  60. Title string
  61. Branding UIBranding
  62. FormDisabled bool
  63. CheckRedirect bool
  64. }
  65. type twoFactorPage struct {
  66. commonBasePage
  67. CurrentURL string
  68. Error *util.I18nError
  69. CSRFToken string
  70. RecoveryURL string
  71. Title string
  72. Branding UIBranding
  73. }
  74. type forgotPwdPage struct {
  75. commonBasePage
  76. CurrentURL string
  77. Error *util.I18nError
  78. CSRFToken string
  79. LoginURL string
  80. Title string
  81. Branding UIBranding
  82. }
  83. type resetPwdPage struct {
  84. commonBasePage
  85. CurrentURL string
  86. Error *util.I18nError
  87. CSRFToken string
  88. LoginURL string
  89. Title string
  90. Branding UIBranding
  91. }
  92. func getSliceFromDelimitedValues(values, delimiter string) []string {
  93. result := []string{}
  94. for _, v := range strings.Split(values, delimiter) {
  95. cleaned := strings.TrimSpace(v)
  96. if cleaned != "" {
  97. result = append(result, cleaned)
  98. }
  99. }
  100. return result
  101. }
  102. func hasPrefixAndSuffix(key, prefix, suffix string) bool {
  103. return strings.HasPrefix(key, prefix) && strings.HasSuffix(key, suffix)
  104. }
  105. func getCommonBasePage(r *http.Request) commonBasePage {
  106. return commonBasePage{
  107. CSPNonce: secure.CSPNonce(r.Context()),
  108. StaticURL: webStaticFilesPath,
  109. Version: version.GetServerVersion(" ", true),
  110. }
  111. }
  112. func i18nListDirMsg(status int) string {
  113. if status == http.StatusForbidden {
  114. return util.I18nErrorDirList403
  115. }
  116. return util.I18nErrorDirListGeneric
  117. }
  118. func i18nFsMsg(status int) string {
  119. if status == http.StatusForbidden {
  120. return util.I18nError403Message
  121. }
  122. return util.I18nErrorFsGeneric
  123. }
  124. func getI18NErrorString(err error, fallback string) string {
  125. var errI18n *util.I18nError
  126. if errors.As(err, &errI18n) {
  127. return errI18n.Message
  128. }
  129. return fallback
  130. }
  131. func getI18nError(err error) *util.I18nError {
  132. var errI18n *util.I18nError
  133. if err != nil {
  134. errI18n = util.NewI18nError(err, util.I18nError500Message)
  135. }
  136. return errI18n
  137. }
  138. func handlePingRequest(w http.ResponseWriter, r *http.Request) {
  139. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  140. render.PlainText(w, r, "PONG")
  141. }