web.go 23 KB

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