web.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "html/template"
  6. "io/ioutil"
  7. "net/http"
  8. "path"
  9. "path/filepath"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "github.com/go-chi/chi"
  14. "github.com/drakkan/sftpgo/common"
  15. "github.com/drakkan/sftpgo/dataprovider"
  16. "github.com/drakkan/sftpgo/kms"
  17. "github.com/drakkan/sftpgo/utils"
  18. "github.com/drakkan/sftpgo/version"
  19. "github.com/drakkan/sftpgo/vfs"
  20. )
  21. const (
  22. templateBase = "base.html"
  23. templateUsers = "users.html"
  24. templateUser = "user.html"
  25. templateConnections = "connections.html"
  26. templateFolders = "folders.html"
  27. templateFolder = "folder.html"
  28. templateMessage = "message.html"
  29. templateStatus = "status.html"
  30. pageUsersTitle = "Users"
  31. pageConnectionsTitle = "Connections"
  32. pageStatusTitle = "Status"
  33. pageFoldersTitle = "Folders"
  34. page400Title = "Bad request"
  35. page404Title = "Not found"
  36. page404Body = "The page you are looking for does not exist."
  37. page500Title = "Internal Server Error"
  38. page500Body = "The server is unable to fulfill your request."
  39. defaultQueryLimit = 500
  40. webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
  41. redactedSecret = "[**redacted**]"
  42. )
  43. var (
  44. templates = make(map[string]*template.Template)
  45. )
  46. type basePage struct {
  47. Title string
  48. CurrentURL string
  49. UsersURL string
  50. UserURL string
  51. APIUserURL string
  52. APIConnectionsURL string
  53. APIQuotaScanURL string
  54. ConnectionsURL string
  55. FoldersURL string
  56. FolderURL string
  57. APIFoldersURL string
  58. APIFolderQuotaScanURL string
  59. StatusURL string
  60. UsersTitle string
  61. ConnectionsTitle string
  62. FoldersTitle string
  63. StatusTitle string
  64. Version string
  65. }
  66. type usersPage struct {
  67. basePage
  68. Users []dataprovider.User
  69. }
  70. type foldersPage struct {
  71. basePage
  72. Folders []vfs.BaseVirtualFolder
  73. }
  74. type connectionsPage struct {
  75. basePage
  76. Connections []common.ConnectionStatus
  77. }
  78. type statusPage struct {
  79. basePage
  80. Status ServicesStatus
  81. }
  82. type userPage struct {
  83. basePage
  84. User dataprovider.User
  85. RootPerms []string
  86. Error string
  87. ValidPerms []string
  88. ValidSSHLoginMethods []string
  89. ValidProtocols []string
  90. RootDirPerms []string
  91. RedactedSecret string
  92. IsAdd bool
  93. }
  94. type folderPage struct {
  95. basePage
  96. Folder vfs.BaseVirtualFolder
  97. Error string
  98. }
  99. type messagePage struct {
  100. basePage
  101. Error string
  102. Success string
  103. }
  104. func loadTemplates(templatesPath string) {
  105. usersPaths := []string{
  106. filepath.Join(templatesPath, templateBase),
  107. filepath.Join(templatesPath, templateUsers),
  108. }
  109. userPaths := []string{
  110. filepath.Join(templatesPath, templateBase),
  111. filepath.Join(templatesPath, templateUser),
  112. }
  113. connectionsPaths := []string{
  114. filepath.Join(templatesPath, templateBase),
  115. filepath.Join(templatesPath, templateConnections),
  116. }
  117. messagePath := []string{
  118. filepath.Join(templatesPath, templateBase),
  119. filepath.Join(templatesPath, templateMessage),
  120. }
  121. foldersPath := []string{
  122. filepath.Join(templatesPath, templateBase),
  123. filepath.Join(templatesPath, templateFolders),
  124. }
  125. folderPath := []string{
  126. filepath.Join(templatesPath, templateBase),
  127. filepath.Join(templatesPath, templateFolder),
  128. }
  129. statusPath := []string{
  130. filepath.Join(templatesPath, templateBase),
  131. filepath.Join(templatesPath, templateStatus),
  132. }
  133. usersTmpl := utils.LoadTemplate(template.ParseFiles(usersPaths...))
  134. userTmpl := utils.LoadTemplate(template.ParseFiles(userPaths...))
  135. connectionsTmpl := utils.LoadTemplate(template.ParseFiles(connectionsPaths...))
  136. messageTmpl := utils.LoadTemplate(template.ParseFiles(messagePath...))
  137. foldersTmpl := utils.LoadTemplate(template.ParseFiles(foldersPath...))
  138. folderTmpl := utils.LoadTemplate(template.ParseFiles(folderPath...))
  139. statusTmpl := utils.LoadTemplate(template.ParseFiles(statusPath...))
  140. templates[templateUsers] = usersTmpl
  141. templates[templateUser] = userTmpl
  142. templates[templateConnections] = connectionsTmpl
  143. templates[templateMessage] = messageTmpl
  144. templates[templateFolders] = foldersTmpl
  145. templates[templateFolder] = folderTmpl
  146. templates[templateStatus] = statusTmpl
  147. }
  148. func getBasePageData(title, currentURL string) basePage {
  149. return basePage{
  150. Title: title,
  151. CurrentURL: currentURL,
  152. UsersURL: webUsersPath,
  153. UserURL: webUserPath,
  154. FoldersURL: webFoldersPath,
  155. FolderURL: webFolderPath,
  156. APIUserURL: userPath,
  157. APIConnectionsURL: activeConnectionsPath,
  158. APIQuotaScanURL: quotaScanPath,
  159. APIFoldersURL: folderPath,
  160. APIFolderQuotaScanURL: quotaScanVFolderPath,
  161. ConnectionsURL: webConnectionsPath,
  162. StatusURL: webStatusPath,
  163. UsersTitle: pageUsersTitle,
  164. ConnectionsTitle: pageConnectionsTitle,
  165. FoldersTitle: pageFoldersTitle,
  166. StatusTitle: pageStatusTitle,
  167. Version: version.GetAsString(),
  168. }
  169. }
  170. func renderTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
  171. err := templates[tmplName].ExecuteTemplate(w, tmplName, data)
  172. if err != nil {
  173. http.Error(w, err.Error(), http.StatusInternalServerError)
  174. }
  175. }
  176. func renderMessagePage(w http.ResponseWriter, title, body string, statusCode int, err error, message string) {
  177. var errorString string
  178. if len(body) > 0 {
  179. errorString = body + " "
  180. }
  181. if err != nil {
  182. errorString += err.Error()
  183. }
  184. data := messagePage{
  185. basePage: getBasePageData(title, ""),
  186. Error: errorString,
  187. Success: message,
  188. }
  189. w.WriteHeader(statusCode)
  190. renderTemplate(w, templateMessage, data)
  191. }
  192. func renderInternalServerErrorPage(w http.ResponseWriter, err error) {
  193. renderMessagePage(w, page500Title, page500Body, http.StatusInternalServerError, err, "")
  194. }
  195. func renderBadRequestPage(w http.ResponseWriter, err error) {
  196. renderMessagePage(w, page400Title, "", http.StatusBadRequest, err, "")
  197. }
  198. func renderNotFoundPage(w http.ResponseWriter, err error) {
  199. renderMessagePage(w, page404Title, page404Body, http.StatusNotFound, err, "")
  200. }
  201. func renderAddUserPage(w http.ResponseWriter, user dataprovider.User, error string) {
  202. user.SetEmptySecretsIfNil()
  203. data := userPage{
  204. basePage: getBasePageData("Add a new user", webUserPath),
  205. IsAdd: true,
  206. Error: error,
  207. User: user,
  208. ValidPerms: dataprovider.ValidPerms,
  209. ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
  210. ValidProtocols: dataprovider.ValidProtocols,
  211. RootDirPerms: user.GetPermissionsForPath("/"),
  212. RedactedSecret: redactedSecret,
  213. }
  214. renderTemplate(w, templateUser, data)
  215. }
  216. func renderUpdateUserPage(w http.ResponseWriter, user dataprovider.User, error string) {
  217. user.SetEmptySecretsIfNil()
  218. data := userPage{
  219. basePage: getBasePageData("Update user", fmt.Sprintf("%v/%v", webUserPath, user.ID)),
  220. IsAdd: false,
  221. Error: error,
  222. User: user,
  223. ValidPerms: dataprovider.ValidPerms,
  224. ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
  225. ValidProtocols: dataprovider.ValidProtocols,
  226. RootDirPerms: user.GetPermissionsForPath("/"),
  227. RedactedSecret: redactedSecret,
  228. }
  229. renderTemplate(w, templateUser, data)
  230. }
  231. func renderAddFolderPage(w http.ResponseWriter, folder vfs.BaseVirtualFolder, error string) {
  232. data := folderPage{
  233. basePage: getBasePageData("Add a new folder", webFolderPath),
  234. Error: error,
  235. Folder: folder,
  236. }
  237. renderTemplate(w, templateFolder, data)
  238. }
  239. func getVirtualFoldersFromPostFields(r *http.Request) []vfs.VirtualFolder {
  240. var virtualFolders []vfs.VirtualFolder
  241. formValue := r.Form.Get("virtual_folders")
  242. for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
  243. if strings.Contains(cleaned, "::") {
  244. mapping := strings.Split(cleaned, "::")
  245. if len(mapping) > 1 {
  246. vfolder := vfs.VirtualFolder{
  247. BaseVirtualFolder: vfs.BaseVirtualFolder{
  248. MappedPath: strings.TrimSpace(mapping[1]),
  249. },
  250. VirtualPath: strings.TrimSpace(mapping[0]),
  251. QuotaFiles: -1,
  252. QuotaSize: -1,
  253. }
  254. if len(mapping) > 2 {
  255. quotaFiles, err := strconv.Atoi(strings.TrimSpace(mapping[2]))
  256. if err == nil {
  257. vfolder.QuotaFiles = quotaFiles
  258. }
  259. }
  260. if len(mapping) > 3 {
  261. quotaSize, err := strconv.ParseInt(strings.TrimSpace(mapping[3]), 10, 64)
  262. if err == nil {
  263. vfolder.QuotaSize = quotaSize
  264. }
  265. }
  266. virtualFolders = append(virtualFolders, vfolder)
  267. }
  268. }
  269. }
  270. return virtualFolders
  271. }
  272. func getUserPermissionsFromPostFields(r *http.Request) map[string][]string {
  273. permissions := make(map[string][]string)
  274. permissions["/"] = r.Form["permissions"]
  275. subDirsPermsValue := r.Form.Get("sub_dirs_permissions")
  276. for _, cleaned := range getSliceFromDelimitedValues(subDirsPermsValue, "\n") {
  277. if strings.Contains(cleaned, "::") {
  278. dirPerms := strings.Split(cleaned, "::")
  279. if len(dirPerms) > 1 {
  280. dir := dirPerms[0]
  281. dir = strings.TrimSpace(dir)
  282. perms := []string{}
  283. for _, p := range strings.Split(dirPerms[1], ",") {
  284. cleanedPerm := strings.TrimSpace(p)
  285. if len(cleanedPerm) > 0 {
  286. perms = append(perms, cleanedPerm)
  287. }
  288. }
  289. if len(dir) > 0 {
  290. permissions[dir] = perms
  291. }
  292. }
  293. }
  294. }
  295. return permissions
  296. }
  297. func getSliceFromDelimitedValues(values, delimiter string) []string {
  298. result := []string{}
  299. for _, v := range strings.Split(values, delimiter) {
  300. cleaned := strings.TrimSpace(v)
  301. if len(cleaned) > 0 {
  302. result = append(result, cleaned)
  303. }
  304. }
  305. return result
  306. }
  307. func getListFromPostFields(value string) map[string][]string {
  308. result := make(map[string][]string)
  309. for _, cleaned := range getSliceFromDelimitedValues(value, "\n") {
  310. if strings.Contains(cleaned, "::") {
  311. dirExts := strings.Split(cleaned, "::")
  312. if len(dirExts) > 1 {
  313. dir := dirExts[0]
  314. dir = path.Clean(strings.TrimSpace(dir))
  315. exts := []string{}
  316. for _, e := range strings.Split(dirExts[1], ",") {
  317. cleanedExt := strings.TrimSpace(e)
  318. if cleanedExt != "" {
  319. exts = append(exts, cleanedExt)
  320. }
  321. }
  322. if dir != "" {
  323. if _, ok := result[dir]; ok {
  324. result[dir] = append(result[dir], exts...)
  325. } else {
  326. result[dir] = exts
  327. }
  328. result[dir] = utils.RemoveDuplicates(result[dir])
  329. }
  330. }
  331. }
  332. }
  333. return result
  334. }
  335. func getFilePatternsFromPostField(valueAllowed, valuesDenied string) []dataprovider.PatternsFilter {
  336. var result []dataprovider.PatternsFilter
  337. allowedPatterns := getListFromPostFields(valueAllowed)
  338. deniedPatterns := getListFromPostFields(valuesDenied)
  339. for dirAllowed, allowPatterns := range allowedPatterns {
  340. filter := dataprovider.PatternsFilter{
  341. Path: dirAllowed,
  342. AllowedPatterns: allowPatterns,
  343. }
  344. for dirDenied, denPatterns := range deniedPatterns {
  345. if dirAllowed == dirDenied {
  346. filter.DeniedPatterns = denPatterns
  347. break
  348. }
  349. }
  350. result = append(result, filter)
  351. }
  352. for dirDenied, denPatterns := range deniedPatterns {
  353. found := false
  354. for _, res := range result {
  355. if res.Path == dirDenied {
  356. found = true
  357. break
  358. }
  359. }
  360. if !found {
  361. result = append(result, dataprovider.PatternsFilter{
  362. Path: dirDenied,
  363. DeniedPatterns: denPatterns,
  364. })
  365. }
  366. }
  367. return result
  368. }
  369. func getFileExtensionsFromPostField(valueAllowed, valuesDenied string) []dataprovider.ExtensionsFilter {
  370. var result []dataprovider.ExtensionsFilter
  371. allowedExtensions := getListFromPostFields(valueAllowed)
  372. deniedExtensions := getListFromPostFields(valuesDenied)
  373. for dirAllowed, allowedExts := range allowedExtensions {
  374. filter := dataprovider.ExtensionsFilter{
  375. Path: dirAllowed,
  376. AllowedExtensions: allowedExts,
  377. }
  378. for dirDenied, deniedExts := range deniedExtensions {
  379. if dirAllowed == dirDenied {
  380. filter.DeniedExtensions = deniedExts
  381. break
  382. }
  383. }
  384. result = append(result, filter)
  385. }
  386. for dirDenied, deniedExts := range deniedExtensions {
  387. found := false
  388. for _, res := range result {
  389. if res.Path == dirDenied {
  390. found = true
  391. break
  392. }
  393. }
  394. if !found {
  395. result = append(result, dataprovider.ExtensionsFilter{
  396. Path: dirDenied,
  397. DeniedExtensions: deniedExts,
  398. })
  399. }
  400. }
  401. return result
  402. }
  403. func getFiltersFromUserPostFields(r *http.Request) dataprovider.UserFilters {
  404. var filters dataprovider.UserFilters
  405. filters.AllowedIP = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  406. filters.DeniedIP = getSliceFromDelimitedValues(r.Form.Get("denied_ip"), ",")
  407. filters.DeniedLoginMethods = r.Form["ssh_login_methods"]
  408. filters.DeniedProtocols = r.Form["denied_protocols"]
  409. filters.FileExtensions = getFileExtensionsFromPostField(r.Form.Get("allowed_extensions"), r.Form.Get("denied_extensions"))
  410. filters.FilePatterns = getFilePatternsFromPostField(r.Form.Get("allowed_patterns"), r.Form.Get("denied_patterns"))
  411. return filters
  412. }
  413. func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
  414. secret := kms.NewPlainSecret(r.Form.Get(field))
  415. if strings.TrimSpace(secret.GetPayload()) == redactedSecret {
  416. secret.SetStatus(kms.SecretStatusRedacted)
  417. }
  418. if strings.TrimSpace(secret.GetPayload()) == "" {
  419. secret.SetStatus("")
  420. }
  421. return secret
  422. }
  423. func getS3Config(r *http.Request) (vfs.S3FsConfig, error) {
  424. var err error
  425. config := vfs.S3FsConfig{}
  426. config.Bucket = r.Form.Get("s3_bucket")
  427. config.Region = r.Form.Get("s3_region")
  428. config.AccessKey = r.Form.Get("s3_access_key")
  429. config.AccessSecret = getSecretFromFormField(r, "s3_access_secret")
  430. config.Endpoint = r.Form.Get("s3_endpoint")
  431. config.StorageClass = r.Form.Get("s3_storage_class")
  432. config.KeyPrefix = r.Form.Get("s3_key_prefix")
  433. config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
  434. if err != nil {
  435. return config, err
  436. }
  437. config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
  438. return config, err
  439. }
  440. func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
  441. var err error
  442. config := vfs.GCSFsConfig{}
  443. config.Bucket = r.Form.Get("gcs_bucket")
  444. config.StorageClass = r.Form.Get("gcs_storage_class")
  445. config.KeyPrefix = r.Form.Get("gcs_key_prefix")
  446. autoCredentials := r.Form.Get("gcs_auto_credentials")
  447. if autoCredentials != "" {
  448. config.AutomaticCredentials = 1
  449. } else {
  450. config.AutomaticCredentials = 0
  451. }
  452. credentials, _, err := r.FormFile("gcs_credential_file")
  453. if err == http.ErrMissingFile {
  454. return config, nil
  455. }
  456. if err != nil {
  457. return config, err
  458. }
  459. defer credentials.Close()
  460. fileBytes, err := ioutil.ReadAll(credentials)
  461. if err != nil || len(fileBytes) == 0 {
  462. if len(fileBytes) == 0 {
  463. err = errors.New("credentials file size must be greater than 0")
  464. }
  465. return config, err
  466. }
  467. config.Credentials = kms.NewPlainSecret(string(fileBytes))
  468. config.AutomaticCredentials = 0
  469. return config, err
  470. }
  471. func getSFTPConfig(r *http.Request) vfs.SFTPFsConfig {
  472. config := vfs.SFTPFsConfig{}
  473. config.Endpoint = r.Form.Get("sftp_endpoint")
  474. config.Username = r.Form.Get("sftp_username")
  475. config.Password = getSecretFromFormField(r, "sftp_password")
  476. config.PrivateKey = getSecretFromFormField(r, "sftp_private_key")
  477. fingerprintsFormValue := r.Form.Get("sftp_fingerprints")
  478. config.Fingerprints = getSliceFromDelimitedValues(fingerprintsFormValue, "\n")
  479. config.Prefix = r.Form.Get("sftp_prefix")
  480. return config
  481. }
  482. func getAzureConfig(r *http.Request) (vfs.AzBlobFsConfig, error) {
  483. var err error
  484. config := vfs.AzBlobFsConfig{}
  485. config.Container = r.Form.Get("az_container")
  486. config.AccountName = r.Form.Get("az_account_name")
  487. config.AccountKey = getSecretFromFormField(r, "az_account_key")
  488. config.SASURL = r.Form.Get("az_sas_url")
  489. config.Endpoint = r.Form.Get("az_endpoint")
  490. config.KeyPrefix = r.Form.Get("az_key_prefix")
  491. config.AccessTier = r.Form.Get("az_access_tier")
  492. config.UseEmulator = len(r.Form.Get("az_use_emulator")) > 0
  493. config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("az_upload_part_size"), 10, 64)
  494. if err != nil {
  495. return config, err
  496. }
  497. config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("az_upload_concurrency"))
  498. return config, err
  499. }
  500. func getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, error) {
  501. var fs dataprovider.Filesystem
  502. provider, err := strconv.Atoi(r.Form.Get("fs_provider"))
  503. if err != nil {
  504. provider = int(dataprovider.LocalFilesystemProvider)
  505. }
  506. fs.Provider = dataprovider.FilesystemProvider(provider)
  507. switch fs.Provider {
  508. case dataprovider.S3FilesystemProvider:
  509. config, err := getS3Config(r)
  510. if err != nil {
  511. return fs, err
  512. }
  513. fs.S3Config = config
  514. case dataprovider.AzureBlobFilesystemProvider:
  515. config, err := getAzureConfig(r)
  516. if err != nil {
  517. return fs, err
  518. }
  519. fs.AzBlobConfig = config
  520. case dataprovider.GCSFilesystemProvider:
  521. config, err := getGCSConfig(r)
  522. if err != nil {
  523. return fs, err
  524. }
  525. fs.GCSConfig = config
  526. case dataprovider.CryptedFilesystemProvider:
  527. fs.CryptConfig.Passphrase = getSecretFromFormField(r, "crypt_passphrase")
  528. case dataprovider.SFTPFilesystemProvider:
  529. fs.SFTPConfig = getSFTPConfig(r)
  530. }
  531. return fs, nil
  532. }
  533. func getUserFromPostFields(r *http.Request) (dataprovider.User, error) {
  534. var user dataprovider.User
  535. err := r.ParseMultipartForm(maxRequestSize)
  536. if err != nil {
  537. return user, err
  538. }
  539. publicKeysFormValue := r.Form.Get("public_keys")
  540. publicKeys := getSliceFromDelimitedValues(publicKeysFormValue, "\n")
  541. uid, err := strconv.Atoi(r.Form.Get("uid"))
  542. if err != nil {
  543. return user, err
  544. }
  545. gid, err := strconv.Atoi(r.Form.Get("gid"))
  546. if err != nil {
  547. return user, err
  548. }
  549. maxSessions, err := strconv.Atoi(r.Form.Get("max_sessions"))
  550. if err != nil {
  551. return user, err
  552. }
  553. quotaSize, err := strconv.ParseInt(r.Form.Get("quota_size"), 10, 64)
  554. if err != nil {
  555. return user, err
  556. }
  557. quotaFiles, err := strconv.Atoi(r.Form.Get("quota_files"))
  558. if err != nil {
  559. return user, err
  560. }
  561. bandwidthUL, err := strconv.ParseInt(r.Form.Get("upload_bandwidth"), 10, 64)
  562. if err != nil {
  563. return user, err
  564. }
  565. bandwidthDL, err := strconv.ParseInt(r.Form.Get("download_bandwidth"), 10, 64)
  566. if err != nil {
  567. return user, err
  568. }
  569. status, err := strconv.Atoi(r.Form.Get("status"))
  570. if err != nil {
  571. return user, err
  572. }
  573. expirationDateMillis := int64(0)
  574. expirationDateString := r.Form.Get("expiration_date")
  575. if len(strings.TrimSpace(expirationDateString)) > 0 {
  576. expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
  577. if err != nil {
  578. return user, err
  579. }
  580. expirationDateMillis = utils.GetTimeAsMsSinceEpoch(expirationDate)
  581. }
  582. fsConfig, err := getFsConfigFromUserPostFields(r)
  583. if err != nil {
  584. return user, err
  585. }
  586. user = dataprovider.User{
  587. Username: r.Form.Get("username"),
  588. Password: r.Form.Get("password"),
  589. PublicKeys: publicKeys,
  590. HomeDir: r.Form.Get("home_dir"),
  591. VirtualFolders: getVirtualFoldersFromPostFields(r),
  592. UID: uid,
  593. GID: gid,
  594. Permissions: getUserPermissionsFromPostFields(r),
  595. MaxSessions: maxSessions,
  596. QuotaSize: quotaSize,
  597. QuotaFiles: quotaFiles,
  598. UploadBandwidth: bandwidthUL,
  599. DownloadBandwidth: bandwidthDL,
  600. Status: status,
  601. ExpirationDate: expirationDateMillis,
  602. Filters: getFiltersFromUserPostFields(r),
  603. FsConfig: fsConfig,
  604. AdditionalInfo: r.Form.Get("additional_info"),
  605. }
  606. maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
  607. user.Filters.MaxUploadFileSize = maxFileSize
  608. return user, err
  609. }
  610. func handleGetWebUsers(w http.ResponseWriter, r *http.Request) {
  611. limit := defaultQueryLimit
  612. if _, ok := r.URL.Query()["qlimit"]; ok {
  613. var err error
  614. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  615. if err != nil {
  616. limit = defaultQueryLimit
  617. }
  618. }
  619. users := make([]dataprovider.User, 0, limit)
  620. for {
  621. u, err := dataprovider.GetUsers(limit, len(users), dataprovider.OrderASC, "")
  622. if err != nil {
  623. renderInternalServerErrorPage(w, err)
  624. return
  625. }
  626. users = append(users, u...)
  627. if len(u) < limit {
  628. break
  629. }
  630. }
  631. data := usersPage{
  632. basePage: getBasePageData(pageUsersTitle, webUsersPath),
  633. Users: users,
  634. }
  635. renderTemplate(w, templateUsers, data)
  636. }
  637. func handleWebAddUserGet(w http.ResponseWriter, r *http.Request) {
  638. renderAddUserPage(w, dataprovider.User{Status: 1}, "")
  639. }
  640. func handleWebUpdateUserGet(w http.ResponseWriter, r *http.Request) {
  641. id, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  642. if err != nil {
  643. renderBadRequestPage(w, err)
  644. return
  645. }
  646. user, err := dataprovider.GetUserByID(id)
  647. if err == nil {
  648. renderUpdateUserPage(w, user, "")
  649. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  650. renderNotFoundPage(w, err)
  651. } else {
  652. renderInternalServerErrorPage(w, err)
  653. }
  654. }
  655. func handleWebAddUserPost(w http.ResponseWriter, r *http.Request) {
  656. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  657. user, err := getUserFromPostFields(r)
  658. if err != nil {
  659. renderAddUserPage(w, user, err.Error())
  660. return
  661. }
  662. err = dataprovider.AddUser(user)
  663. if err == nil {
  664. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  665. } else {
  666. renderAddUserPage(w, user, err.Error())
  667. }
  668. }
  669. func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
  670. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  671. id, err := strconv.ParseInt(chi.URLParam(r, "userID"), 10, 64)
  672. if err != nil {
  673. renderBadRequestPage(w, err)
  674. return
  675. }
  676. user, err := dataprovider.GetUserByID(id)
  677. if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  678. renderNotFoundPage(w, err)
  679. return
  680. } else if err != nil {
  681. renderInternalServerErrorPage(w, err)
  682. return
  683. }
  684. updatedUser, err := getUserFromPostFields(r)
  685. if err != nil {
  686. renderUpdateUserPage(w, user, err.Error())
  687. return
  688. }
  689. updatedUser.ID = user.ID
  690. updatedUser.SetEmptySecretsIfNil()
  691. if len(updatedUser.Password) == 0 {
  692. updatedUser.Password = user.Password
  693. }
  694. updateEncryptedSecrets(&updatedUser, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
  695. user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase, user.FsConfig.SFTPConfig.Password,
  696. user.FsConfig.SFTPConfig.PrivateKey)
  697. err = dataprovider.UpdateUser(updatedUser)
  698. if err == nil {
  699. if len(r.Form.Get("disconnect")) > 0 {
  700. disconnectUser(user.Username)
  701. }
  702. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  703. } else {
  704. renderUpdateUserPage(w, user, err.Error())
  705. }
  706. }
  707. func handleWebGetStatus(w http.ResponseWriter, r *http.Request) {
  708. data := statusPage{
  709. basePage: getBasePageData(pageStatusTitle, webStatusPath),
  710. Status: getServicesStatus(),
  711. }
  712. renderTemplate(w, templateStatus, data)
  713. }
  714. func handleWebGetConnections(w http.ResponseWriter, r *http.Request) {
  715. connectionStats := common.Connections.GetStats()
  716. data := connectionsPage{
  717. basePage: getBasePageData(pageConnectionsTitle, webConnectionsPath),
  718. Connections: connectionStats,
  719. }
  720. renderTemplate(w, templateConnections, data)
  721. }
  722. func handleWebAddFolderGet(w http.ResponseWriter, r *http.Request) {
  723. renderAddFolderPage(w, vfs.BaseVirtualFolder{}, "")
  724. }
  725. func handleWebAddFolderPost(w http.ResponseWriter, r *http.Request) {
  726. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  727. folder := vfs.BaseVirtualFolder{}
  728. err := r.ParseForm()
  729. if err != nil {
  730. renderAddFolderPage(w, folder, err.Error())
  731. return
  732. }
  733. folder.MappedPath = r.Form.Get("mapped_path")
  734. err = dataprovider.AddFolder(folder)
  735. if err == nil {
  736. http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
  737. } else {
  738. renderAddFolderPage(w, folder, err.Error())
  739. }
  740. }
  741. func handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
  742. limit := defaultQueryLimit
  743. if _, ok := r.URL.Query()["qlimit"]; ok {
  744. var err error
  745. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  746. if err != nil {
  747. limit = defaultQueryLimit
  748. }
  749. }
  750. folders := make([]vfs.BaseVirtualFolder, 0, limit)
  751. for {
  752. f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC, "")
  753. if err != nil {
  754. renderInternalServerErrorPage(w, err)
  755. return
  756. }
  757. folders = append(folders, f...)
  758. if len(f) < limit {
  759. break
  760. }
  761. }
  762. data := foldersPage{
  763. basePage: getBasePageData(pageFoldersTitle, webFoldersPath),
  764. Folders: folders,
  765. }
  766. renderTemplate(w, templateFolders, data)
  767. }