1
0

api_user.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "strconv"
  7. "github.com/go-chi/chi"
  8. "github.com/go-chi/render"
  9. "github.com/drakkan/sftpgo/common"
  10. "github.com/drakkan/sftpgo/dataprovider"
  11. "github.com/drakkan/sftpgo/kms"
  12. "github.com/drakkan/sftpgo/vfs"
  13. )
  14. func getUsers(w http.ResponseWriter, r *http.Request) {
  15. limit := 100
  16. offset := 0
  17. order := dataprovider.OrderASC
  18. username := ""
  19. var err error
  20. if _, ok := r.URL.Query()["limit"]; ok {
  21. limit, err = strconv.Atoi(r.URL.Query().Get("limit"))
  22. if err != nil {
  23. err = errors.New("Invalid limit")
  24. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  25. return
  26. }
  27. if limit > 500 {
  28. limit = 500
  29. }
  30. }
  31. if _, ok := r.URL.Query()["offset"]; ok {
  32. offset, err = strconv.Atoi(r.URL.Query().Get("offset"))
  33. if err != nil {
  34. err = errors.New("Invalid offset")
  35. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  36. return
  37. }
  38. }
  39. if _, ok := r.URL.Query()["order"]; ok {
  40. order = r.URL.Query().Get("order")
  41. if order != dataprovider.OrderASC && order != dataprovider.OrderDESC {
  42. err = errors.New("Invalid order")
  43. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  44. return
  45. }
  46. }
  47. if _, ok := r.URL.Query()["username"]; ok {
  48. username = r.URL.Query().Get("username")
  49. }
  50. users, err := dataprovider.GetUsers(limit, offset, order, username)
  51. if err == nil {
  52. render.JSON(w, r, users)
  53. } else {
  54. sendAPIResponse(w, r, err, "", http.StatusInternalServerError)
  55. }
  56. }
  57. func getUserByID(w http.ResponseWriter, r *http.Request) {
  58. userID, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  59. if err != nil {
  60. err = errors.New("Invalid userID")
  61. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  62. return
  63. }
  64. user, err := dataprovider.GetUserByID(userID)
  65. if err == nil {
  66. user.HideConfidentialData()
  67. render.JSON(w, r, user)
  68. } else {
  69. sendAPIResponse(w, r, err, "", getRespStatus(err))
  70. }
  71. }
  72. func addUser(w http.ResponseWriter, r *http.Request) {
  73. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  74. var user dataprovider.User
  75. err := render.DecodeJSON(r.Body, &user)
  76. if err != nil {
  77. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  78. return
  79. }
  80. user.SetEmptySecretsIfNil()
  81. switch user.FsConfig.Provider {
  82. case dataprovider.S3FilesystemProvider:
  83. if user.FsConfig.S3Config.AccessSecret.IsRedacted() {
  84. sendAPIResponse(w, r, errors.New("invalid access_secret"), "", http.StatusBadRequest)
  85. return
  86. }
  87. case dataprovider.GCSFilesystemProvider:
  88. if user.FsConfig.GCSConfig.Credentials.IsRedacted() {
  89. sendAPIResponse(w, r, errors.New("invalid credentials"), "", http.StatusBadRequest)
  90. return
  91. }
  92. case dataprovider.AzureBlobFilesystemProvider:
  93. if user.FsConfig.AzBlobConfig.AccountKey.IsRedacted() {
  94. sendAPIResponse(w, r, errors.New("invalid account_key"), "", http.StatusBadRequest)
  95. return
  96. }
  97. case dataprovider.CryptedFilesystemProvider:
  98. if user.FsConfig.CryptConfig.Passphrase.IsRedacted() {
  99. sendAPIResponse(w, r, errors.New("invalid passphrase"), "", http.StatusBadRequest)
  100. return
  101. }
  102. case dataprovider.SFTPFilesystemProvider:
  103. if user.FsConfig.SFTPConfig.Password.IsRedacted() {
  104. sendAPIResponse(w, r, errors.New("invalid SFTP password"), "", http.StatusBadRequest)
  105. return
  106. }
  107. if user.FsConfig.SFTPConfig.PrivateKey.IsRedacted() {
  108. sendAPIResponse(w, r, errors.New("invalid SFTP private key"), "", http.StatusBadRequest)
  109. return
  110. }
  111. }
  112. err = dataprovider.AddUser(&user)
  113. if err == nil {
  114. user, err = dataprovider.UserExists(user.Username)
  115. if err == nil {
  116. user.HideConfidentialData()
  117. render.JSON(w, r, user)
  118. } else {
  119. sendAPIResponse(w, r, err, "", getRespStatus(err))
  120. }
  121. } else {
  122. sendAPIResponse(w, r, err, "", getRespStatus(err))
  123. }
  124. }
  125. func updateUser(w http.ResponseWriter, r *http.Request) {
  126. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  127. userID, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  128. if err != nil {
  129. err = errors.New("Invalid userID")
  130. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  131. return
  132. }
  133. disconnect := 0
  134. if _, ok := r.URL.Query()["disconnect"]; ok {
  135. disconnect, err = strconv.Atoi(r.URL.Query().Get("disconnect"))
  136. if err != nil {
  137. err = fmt.Errorf("invalid disconnect parameter: %v", err)
  138. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  139. return
  140. }
  141. }
  142. user, err := dataprovider.GetUserByID(userID)
  143. if err != nil {
  144. sendAPIResponse(w, r, err, "", getRespStatus(err))
  145. return
  146. }
  147. currentPermissions := user.Permissions
  148. currentS3AccessSecret := user.FsConfig.S3Config.AccessSecret
  149. currentAzAccountKey := user.FsConfig.AzBlobConfig.AccountKey
  150. currentGCSCredentials := user.FsConfig.GCSConfig.Credentials
  151. currentCryptoPassphrase := user.FsConfig.CryptConfig.Passphrase
  152. currentSFTPPassword := user.FsConfig.SFTPConfig.Password
  153. currentSFTPKey := user.FsConfig.SFTPConfig.PrivateKey
  154. user.Permissions = make(map[string][]string)
  155. user.FsConfig.S3Config = vfs.S3FsConfig{}
  156. user.FsConfig.AzBlobConfig = vfs.AzBlobFsConfig{}
  157. user.FsConfig.GCSConfig = vfs.GCSFsConfig{}
  158. user.FsConfig.CryptConfig = vfs.CryptFsConfig{}
  159. user.FsConfig.SFTPConfig = vfs.SFTPFsConfig{}
  160. err = render.DecodeJSON(r.Body, &user)
  161. if err != nil {
  162. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  163. return
  164. }
  165. user.SetEmptySecretsIfNil()
  166. // we use new Permissions if passed otherwise the old ones
  167. if len(user.Permissions) == 0 {
  168. user.Permissions = currentPermissions
  169. }
  170. updateEncryptedSecrets(&user, currentS3AccessSecret, currentAzAccountKey, currentGCSCredentials, currentCryptoPassphrase,
  171. currentSFTPPassword, currentSFTPKey)
  172. if user.ID != userID {
  173. sendAPIResponse(w, r, err, "user ID in request body does not match user ID in path parameter", http.StatusBadRequest)
  174. return
  175. }
  176. err = dataprovider.UpdateUser(&user)
  177. if err != nil {
  178. sendAPIResponse(w, r, err, "", getRespStatus(err))
  179. } else {
  180. sendAPIResponse(w, r, err, "User updated", http.StatusOK)
  181. if disconnect == 1 {
  182. disconnectUser(user.Username)
  183. }
  184. }
  185. }
  186. func deleteUser(w http.ResponseWriter, r *http.Request) {
  187. userID, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  188. if err != nil {
  189. err = errors.New("Invalid userID")
  190. sendAPIResponse(w, r, err, "", http.StatusBadRequest)
  191. return
  192. }
  193. user, err := dataprovider.GetUserByID(userID)
  194. if err != nil {
  195. sendAPIResponse(w, r, err, "", getRespStatus(err))
  196. return
  197. }
  198. err = dataprovider.DeleteUser(&user)
  199. if err != nil {
  200. sendAPIResponse(w, r, err, "", http.StatusInternalServerError)
  201. } else {
  202. sendAPIResponse(w, r, err, "User deleted", http.StatusOK)
  203. disconnectUser(user.Username)
  204. }
  205. }
  206. func disconnectUser(username string) {
  207. for _, stat := range common.Connections.GetStats() {
  208. if stat.Username == username {
  209. common.Connections.Close(stat.ConnectionID)
  210. }
  211. }
  212. }
  213. func updateEncryptedSecrets(user *dataprovider.User, currentS3AccessSecret, currentAzAccountKey,
  214. currentGCSCredentials, currentCryptoPassphrase, currentSFTPPassword, currentSFTPKey *kms.Secret) {
  215. // we use the new access secret if plain or empty, otherwise the old value
  216. switch user.FsConfig.Provider {
  217. case dataprovider.S3FilesystemProvider:
  218. if user.FsConfig.S3Config.AccessSecret.IsNotPlainAndNotEmpty() {
  219. user.FsConfig.S3Config.AccessSecret = currentS3AccessSecret
  220. }
  221. case dataprovider.AzureBlobFilesystemProvider:
  222. if user.FsConfig.AzBlobConfig.AccountKey.IsNotPlainAndNotEmpty() {
  223. user.FsConfig.AzBlobConfig.AccountKey = currentAzAccountKey
  224. }
  225. case dataprovider.GCSFilesystemProvider:
  226. if user.FsConfig.GCSConfig.Credentials.IsNotPlainAndNotEmpty() {
  227. user.FsConfig.GCSConfig.Credentials = currentGCSCredentials
  228. }
  229. case dataprovider.CryptedFilesystemProvider:
  230. if user.FsConfig.CryptConfig.Passphrase.IsNotPlainAndNotEmpty() {
  231. user.FsConfig.CryptConfig.Passphrase = currentCryptoPassphrase
  232. }
  233. case dataprovider.SFTPFilesystemProvider:
  234. if user.FsConfig.SFTPConfig.Password.IsNotPlainAndNotEmpty() {
  235. user.FsConfig.SFTPConfig.Password = currentSFTPPassword
  236. }
  237. if user.FsConfig.SFTPConfig.PrivateKey.IsNotPlainAndNotEmpty() {
  238. user.FsConfig.SFTPConfig.PrivateKey = currentSFTPKey
  239. }
  240. }
  241. }