api_http_user.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package httpd
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "net/http"
  7. "time"
  8. "github.com/go-chi/render"
  9. "github.com/rs/xid"
  10. "github.com/drakkan/sftpgo/v2/common"
  11. "github.com/drakkan/sftpgo/v2/dataprovider"
  12. "github.com/drakkan/sftpgo/v2/util"
  13. )
  14. func readUserFolder(w http.ResponseWriter, r *http.Request) {
  15. claims, err := getTokenClaims(r)
  16. if err != nil || claims.Username == "" {
  17. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  18. return
  19. }
  20. user, err := dataprovider.UserExists(claims.Username)
  21. if err != nil {
  22. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  23. return
  24. }
  25. connID := xid.New().String()
  26. connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, connID)
  27. if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
  28. sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  29. return
  30. }
  31. connection := &Connection{
  32. BaseConnection: common.NewBaseConnection(connID, common.ProtocolHTTP, r.RemoteAddr, user),
  33. request: r,
  34. }
  35. common.Connections.Add(connection)
  36. defer common.Connections.Remove(connection.GetID())
  37. name := util.CleanPath(r.URL.Query().Get("path"))
  38. contents, err := connection.ReadDir(name)
  39. if err != nil {
  40. sendAPIResponse(w, r, err, "Unable to get directory contents", getMappedStatusCode(err))
  41. return
  42. }
  43. results := make([]map[string]interface{}, 0, len(contents))
  44. for _, info := range contents {
  45. res := make(map[string]interface{})
  46. res["name"] = info.Name()
  47. if info.Mode().IsRegular() {
  48. res["size"] = info.Size()
  49. }
  50. res["mode"] = info.Mode()
  51. res["last_modified"] = info.ModTime().UTC().Format(time.RFC3339)
  52. results = append(results, res)
  53. }
  54. render.JSON(w, r, results)
  55. }
  56. func getUserFile(w http.ResponseWriter, r *http.Request) {
  57. claims, err := getTokenClaims(r)
  58. if err != nil || claims.Username == "" {
  59. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  60. return
  61. }
  62. user, err := dataprovider.UserExists(claims.Username)
  63. if err != nil {
  64. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  65. return
  66. }
  67. connID := xid.New().String()
  68. connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, connID)
  69. if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
  70. sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  71. return
  72. }
  73. connection := &Connection{
  74. BaseConnection: common.NewBaseConnection(connID, common.ProtocolHTTP, r.RemoteAddr, user),
  75. request: r,
  76. }
  77. common.Connections.Add(connection)
  78. defer common.Connections.Remove(connection.GetID())
  79. name := util.CleanPath(r.URL.Query().Get("path"))
  80. if name == "/" {
  81. sendAPIResponse(w, r, nil, "Please set the path to a valid file", http.StatusBadRequest)
  82. return
  83. }
  84. info, err := connection.Stat(name, 0)
  85. if err != nil {
  86. sendAPIResponse(w, r, err, "Unable to stat the requested file", getMappedStatusCode(err))
  87. return
  88. }
  89. if info.IsDir() {
  90. sendAPIResponse(w, r, nil, fmt.Sprintf("Please set the path to a valid file, %#v is a directory", name), http.StatusBadRequest)
  91. return
  92. }
  93. if status, err := downloadFile(w, r, connection, name, info); err != nil {
  94. resp := apiResponse{
  95. Error: err.Error(),
  96. Message: http.StatusText(status),
  97. }
  98. ctx := r.Context()
  99. if status != 0 {
  100. ctx = context.WithValue(ctx, render.StatusCtxKey, status)
  101. }
  102. render.JSON(w, r.WithContext(ctx), resp)
  103. }
  104. }
  105. func getUserFilesAsZipStream(w http.ResponseWriter, r *http.Request) {
  106. claims, err := getTokenClaims(r)
  107. if err != nil || claims.Username == "" {
  108. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  109. return
  110. }
  111. user, err := dataprovider.UserExists(claims.Username)
  112. if err != nil {
  113. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  114. return
  115. }
  116. connID := xid.New().String()
  117. connectionID := fmt.Sprintf("%v_%v", common.ProtocolHTTP, connID)
  118. if err := checkHTTPClientUser(&user, r, connectionID); err != nil {
  119. sendAPIResponse(w, r, err, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  120. return
  121. }
  122. connection := &Connection{
  123. BaseConnection: common.NewBaseConnection(connID, common.ProtocolHTTP, r.RemoteAddr, user),
  124. request: r,
  125. }
  126. common.Connections.Add(connection)
  127. defer common.Connections.Remove(connection.GetID())
  128. var filesList []string
  129. err = render.DecodeJSON(r.Body, &filesList)
  130. if err != nil {
  131. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  132. return
  133. }
  134. baseDir := "/"
  135. for idx := range filesList {
  136. filesList[idx] = util.CleanPath(filesList[idx])
  137. }
  138. w.Header().Set("Content-Disposition", "attachment; filename=\"sftpgo-download.zip\"")
  139. renderCompressedFiles(w, connection, baseDir, filesList)
  140. }
  141. func getUserPublicKeys(w http.ResponseWriter, r *http.Request) {
  142. claims, err := getTokenClaims(r)
  143. if err != nil || claims.Username == "" {
  144. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  145. return
  146. }
  147. user, err := dataprovider.UserExists(claims.Username)
  148. if err != nil {
  149. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  150. return
  151. }
  152. render.JSON(w, r, user.PublicKeys)
  153. }
  154. func setUserPublicKeys(w http.ResponseWriter, r *http.Request) {
  155. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  156. claims, err := getTokenClaims(r)
  157. if err != nil || claims.Username == "" {
  158. sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
  159. return
  160. }
  161. user, err := dataprovider.UserExists(claims.Username)
  162. if err != nil {
  163. sendAPIResponse(w, r, nil, "Unable to retrieve your user", getRespStatus(err))
  164. return
  165. }
  166. var publicKeys []string
  167. err = render.DecodeJSON(r.Body, &publicKeys)
  168. if err != nil {
  169. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  170. return
  171. }
  172. user.PublicKeys = publicKeys
  173. err = dataprovider.UpdateUser(&user)
  174. if err != nil {
  175. sendAPIResponse(w, r, err, "", getRespStatus(err))
  176. return
  177. }
  178. sendAPIResponse(w, r, err, "Public keys updated", http.StatusOK)
  179. }
  180. func changeUserPassword(w http.ResponseWriter, r *http.Request) {
  181. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  182. var pwd pwdChange
  183. err := render.DecodeJSON(r.Body, &pwd)
  184. if err != nil {
  185. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  186. return
  187. }
  188. err = doChangeUserPassword(r, pwd.CurrentPassword, pwd.NewPassword, pwd.NewPassword)
  189. if err != nil {
  190. sendAPIResponse(w, r, err, "", getRespStatus(err))
  191. return
  192. }
  193. sendAPIResponse(w, r, err, "Password updated", http.StatusOK)
  194. }
  195. func doChangeUserPassword(r *http.Request, currentPassword, newPassword, confirmNewPassword string) error {
  196. if currentPassword == "" || newPassword == "" || confirmNewPassword == "" {
  197. return util.NewValidationError("please provide the current password and the new one two times")
  198. }
  199. if newPassword != confirmNewPassword {
  200. return util.NewValidationError("the two password fields do not match")
  201. }
  202. if currentPassword == newPassword {
  203. return util.NewValidationError("the new password must be different from the current one")
  204. }
  205. claims, err := getTokenClaims(r)
  206. if err != nil || claims.Username == "" {
  207. return errors.New("invalid token claims")
  208. }
  209. user, err := dataprovider.CheckUserAndPass(claims.Username, currentPassword, util.GetIPFromRemoteAddress(r.RemoteAddr),
  210. common.ProtocolHTTP)
  211. if err != nil {
  212. return util.NewValidationError("current password does not match")
  213. }
  214. user.Password = newPassword
  215. return dataprovider.UpdateUser(&user)
  216. }