web.go 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
  1. package httpd
  2. import (
  3. "errors"
  4. "fmt"
  5. "html/template"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "path"
  10. "path/filepath"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "github.com/go-chi/render"
  15. "github.com/drakkan/sftpgo/common"
  16. "github.com/drakkan/sftpgo/dataprovider"
  17. "github.com/drakkan/sftpgo/kms"
  18. "github.com/drakkan/sftpgo/utils"
  19. "github.com/drakkan/sftpgo/version"
  20. "github.com/drakkan/sftpgo/vfs"
  21. )
  22. type userPageMode int
  23. const (
  24. userPageModeAdd userPageMode = iota + 1
  25. userPageModeUpdate
  26. userPageModeTemplate
  27. )
  28. type folderPageMode int
  29. const (
  30. folderPageModeAdd folderPageMode = iota + 1
  31. folderPageModeUpdate
  32. folderPageModeTemplate
  33. )
  34. const (
  35. templateBase = "base.html"
  36. templateUsers = "users.html"
  37. templateUser = "user.html"
  38. templateAdmins = "admins.html"
  39. templateAdmin = "admin.html"
  40. templateConnections = "connections.html"
  41. templateFolders = "folders.html"
  42. templateFolder = "folder.html"
  43. templateMessage = "message.html"
  44. templateStatus = "status.html"
  45. templateLogin = "login.html"
  46. templateChangePwd = "changepwd.html"
  47. templateMaintenance = "maintenance.html"
  48. pageUsersTitle = "Users"
  49. pageAdminsTitle = "Admins"
  50. pageConnectionsTitle = "Connections"
  51. pageStatusTitle = "Status"
  52. pageFoldersTitle = "Folders"
  53. pageChangePwdTitle = "Change password"
  54. pageMaintenanceTitle = "Maintenance"
  55. page400Title = "Bad request"
  56. page403Title = "Forbidden"
  57. page404Title = "Not found"
  58. page404Body = "The page you are looking for does not exist."
  59. page500Title = "Internal Server Error"
  60. page500Body = "The server is unable to fulfill your request."
  61. defaultQueryLimit = 500
  62. webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
  63. redactedSecret = "[**redacted**]"
  64. csrfFormToken = "_form_token"
  65. csrfHeaderToken = "X-CSRF-TOKEN"
  66. )
  67. var (
  68. templates = make(map[string]*template.Template)
  69. )
  70. type basePage struct {
  71. Title string
  72. CurrentURL string
  73. UsersURL string
  74. UserURL string
  75. UserTemplateURL string
  76. AdminsURL string
  77. AdminURL string
  78. QuotaScanURL string
  79. ConnectionsURL string
  80. FoldersURL string
  81. FolderURL string
  82. FolderTemplateURL string
  83. LogoutURL string
  84. ChangeAdminPwdURL string
  85. FolderQuotaScanURL string
  86. StatusURL string
  87. MaintenanceURL string
  88. UsersTitle string
  89. AdminsTitle string
  90. ConnectionsTitle string
  91. FoldersTitle string
  92. StatusTitle string
  93. MaintenanceTitle string
  94. Version string
  95. CSRFToken string
  96. LoggedAdmin *dataprovider.Admin
  97. }
  98. type usersPage struct {
  99. basePage
  100. Users []dataprovider.User
  101. }
  102. type adminsPage struct {
  103. basePage
  104. Admins []dataprovider.Admin
  105. }
  106. type foldersPage struct {
  107. basePage
  108. Folders []vfs.BaseVirtualFolder
  109. }
  110. type connectionsPage struct {
  111. basePage
  112. Connections []*common.ConnectionStatus
  113. }
  114. type statusPage struct {
  115. basePage
  116. Status ServicesStatus
  117. }
  118. type userPage struct {
  119. basePage
  120. User *dataprovider.User
  121. RootPerms []string
  122. Error string
  123. ValidPerms []string
  124. ValidSSHLoginMethods []string
  125. ValidProtocols []string
  126. RootDirPerms []string
  127. RedactedSecret string
  128. Mode userPageMode
  129. }
  130. type adminPage struct {
  131. basePage
  132. Admin *dataprovider.Admin
  133. Error string
  134. IsAdd bool
  135. }
  136. type changePwdPage struct {
  137. basePage
  138. Error string
  139. }
  140. type maintenancePage struct {
  141. basePage
  142. BackupPath string
  143. RestorePath string
  144. Error string
  145. }
  146. type folderPage struct {
  147. basePage
  148. Folder vfs.BaseVirtualFolder
  149. Error string
  150. Mode folderPageMode
  151. }
  152. type messagePage struct {
  153. basePage
  154. Error string
  155. Success string
  156. }
  157. type loginPage struct {
  158. CurrentURL string
  159. Version string
  160. Error string
  161. CSRFToken string
  162. }
  163. type userTemplateFields struct {
  164. Username string
  165. Password string
  166. PublicKey string
  167. }
  168. func loadTemplates(templatesPath string) {
  169. usersPaths := []string{
  170. filepath.Join(templatesPath, templateBase),
  171. filepath.Join(templatesPath, templateUsers),
  172. }
  173. userPaths := []string{
  174. filepath.Join(templatesPath, templateBase),
  175. filepath.Join(templatesPath, templateUser),
  176. }
  177. adminsPaths := []string{
  178. filepath.Join(templatesPath, templateBase),
  179. filepath.Join(templatesPath, templateAdmins),
  180. }
  181. adminPaths := []string{
  182. filepath.Join(templatesPath, templateBase),
  183. filepath.Join(templatesPath, templateAdmin),
  184. }
  185. changePwdPaths := []string{
  186. filepath.Join(templatesPath, templateBase),
  187. filepath.Join(templatesPath, templateChangePwd),
  188. }
  189. connectionsPaths := []string{
  190. filepath.Join(templatesPath, templateBase),
  191. filepath.Join(templatesPath, templateConnections),
  192. }
  193. messagePath := []string{
  194. filepath.Join(templatesPath, templateBase),
  195. filepath.Join(templatesPath, templateMessage),
  196. }
  197. foldersPath := []string{
  198. filepath.Join(templatesPath, templateBase),
  199. filepath.Join(templatesPath, templateFolders),
  200. }
  201. folderPath := []string{
  202. filepath.Join(templatesPath, templateBase),
  203. filepath.Join(templatesPath, templateFolder),
  204. }
  205. statusPath := []string{
  206. filepath.Join(templatesPath, templateBase),
  207. filepath.Join(templatesPath, templateStatus),
  208. }
  209. loginPath := []string{
  210. filepath.Join(templatesPath, templateLogin),
  211. }
  212. maintenancePath := []string{
  213. filepath.Join(templatesPath, templateBase),
  214. filepath.Join(templatesPath, templateMaintenance),
  215. }
  216. usersTmpl := utils.LoadTemplate(template.ParseFiles(usersPaths...))
  217. userTmpl := utils.LoadTemplate(template.ParseFiles(userPaths...))
  218. adminsTmpl := utils.LoadTemplate(template.ParseFiles(adminsPaths...))
  219. adminTmpl := utils.LoadTemplate(template.ParseFiles(adminPaths...))
  220. connectionsTmpl := utils.LoadTemplate(template.ParseFiles(connectionsPaths...))
  221. messageTmpl := utils.LoadTemplate(template.ParseFiles(messagePath...))
  222. foldersTmpl := utils.LoadTemplate(template.ParseFiles(foldersPath...))
  223. folderTmpl := utils.LoadTemplate(template.ParseFiles(folderPath...))
  224. statusTmpl := utils.LoadTemplate(template.ParseFiles(statusPath...))
  225. loginTmpl := utils.LoadTemplate(template.ParseFiles(loginPath...))
  226. changePwdTmpl := utils.LoadTemplate(template.ParseFiles(changePwdPaths...))
  227. maintenanceTmpl := utils.LoadTemplate(template.ParseFiles(maintenancePath...))
  228. templates[templateUsers] = usersTmpl
  229. templates[templateUser] = userTmpl
  230. templates[templateAdmins] = adminsTmpl
  231. templates[templateAdmin] = adminTmpl
  232. templates[templateConnections] = connectionsTmpl
  233. templates[templateMessage] = messageTmpl
  234. templates[templateFolders] = foldersTmpl
  235. templates[templateFolder] = folderTmpl
  236. templates[templateStatus] = statusTmpl
  237. templates[templateLogin] = loginTmpl
  238. templates[templateChangePwd] = changePwdTmpl
  239. templates[templateMaintenance] = maintenanceTmpl
  240. }
  241. func getBasePageData(title, currentURL string, r *http.Request) basePage {
  242. var csrfToken string
  243. if currentURL != "" {
  244. csrfToken = createCSRFToken()
  245. }
  246. return basePage{
  247. Title: title,
  248. CurrentURL: currentURL,
  249. UsersURL: webUsersPath,
  250. UserURL: webUserPath,
  251. UserTemplateURL: webTemplateUser,
  252. AdminsURL: webAdminsPath,
  253. AdminURL: webAdminPath,
  254. FoldersURL: webFoldersPath,
  255. FolderURL: webFolderPath,
  256. FolderTemplateURL: webTemplateFolder,
  257. LogoutURL: webLogoutPath,
  258. ChangeAdminPwdURL: webChangeAdminPwdPath,
  259. QuotaScanURL: webQuotaScanPath,
  260. ConnectionsURL: webConnectionsPath,
  261. StatusURL: webStatusPath,
  262. FolderQuotaScanURL: webScanVFolderPath,
  263. MaintenanceURL: webMaintenancePath,
  264. UsersTitle: pageUsersTitle,
  265. AdminsTitle: pageAdminsTitle,
  266. ConnectionsTitle: pageConnectionsTitle,
  267. FoldersTitle: pageFoldersTitle,
  268. StatusTitle: pageStatusTitle,
  269. MaintenanceTitle: pageMaintenanceTitle,
  270. Version: version.GetAsString(),
  271. LoggedAdmin: getAdminFromToken(r),
  272. CSRFToken: csrfToken,
  273. }
  274. }
  275. func renderTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
  276. err := templates[tmplName].ExecuteTemplate(w, tmplName, data)
  277. if err != nil {
  278. http.Error(w, err.Error(), http.StatusInternalServerError)
  279. }
  280. }
  281. func renderMessagePage(w http.ResponseWriter, r *http.Request, title, body string, statusCode int, err error, message string) {
  282. var errorString string
  283. if body != "" {
  284. errorString = body + " "
  285. }
  286. if err != nil {
  287. errorString += err.Error()
  288. }
  289. data := messagePage{
  290. basePage: getBasePageData(title, "", r),
  291. Error: errorString,
  292. Success: message,
  293. }
  294. w.WriteHeader(statusCode)
  295. renderTemplate(w, templateMessage, data)
  296. }
  297. func renderInternalServerErrorPage(w http.ResponseWriter, r *http.Request, err error) {
  298. renderMessagePage(w, r, page500Title, page500Body, http.StatusInternalServerError, err, "")
  299. }
  300. func renderBadRequestPage(w http.ResponseWriter, r *http.Request, err error) {
  301. renderMessagePage(w, r, page400Title, "", http.StatusBadRequest, err, "")
  302. }
  303. func renderForbiddenPage(w http.ResponseWriter, r *http.Request, body string) {
  304. renderMessagePage(w, r, page403Title, "", http.StatusForbidden, nil, body)
  305. }
  306. func renderNotFoundPage(w http.ResponseWriter, r *http.Request, err error) {
  307. renderMessagePage(w, r, page404Title, page404Body, http.StatusNotFound, err, "")
  308. }
  309. func renderChangePwdPage(w http.ResponseWriter, r *http.Request, error string) {
  310. data := changePwdPage{
  311. basePage: getBasePageData(pageChangePwdTitle, webChangeAdminPwdPath, r),
  312. Error: error,
  313. }
  314. renderTemplate(w, templateChangePwd, data)
  315. }
  316. func renderMaintenancePage(w http.ResponseWriter, r *http.Request, error string) {
  317. data := maintenancePage{
  318. basePage: getBasePageData(pageMaintenanceTitle, webMaintenancePath, r),
  319. BackupPath: webBackupPath,
  320. RestorePath: webRestorePath,
  321. Error: error,
  322. }
  323. renderTemplate(w, templateMaintenance, data)
  324. }
  325. func renderAddUpdateAdminPage(w http.ResponseWriter, r *http.Request, admin *dataprovider.Admin,
  326. error string, isAdd bool) {
  327. currentURL := webAdminPath
  328. if !isAdd {
  329. currentURL = fmt.Sprintf("%v/%v", webAdminPath, url.PathEscape(admin.Username))
  330. }
  331. data := adminPage{
  332. basePage: getBasePageData("Add a new user", currentURL, r),
  333. Admin: admin,
  334. Error: error,
  335. IsAdd: isAdd,
  336. }
  337. renderTemplate(w, templateAdmin, data)
  338. }
  339. func renderUserPage(w http.ResponseWriter, r *http.Request, user *dataprovider.User, mode userPageMode, error string) {
  340. user.SetEmptySecretsIfNil()
  341. var title, currentURL string
  342. switch mode {
  343. case userPageModeAdd:
  344. title = "Add a new user"
  345. currentURL = webUserPath
  346. case userPageModeUpdate:
  347. title = "Update user"
  348. currentURL = fmt.Sprintf("%v/%v", webUserPath, url.PathEscape(user.Username))
  349. case userPageModeTemplate:
  350. title = "User template"
  351. currentURL = webTemplateUser
  352. }
  353. if user.Password != "" && user.IsPasswordHashed() && mode == userPageModeUpdate {
  354. user.Password = redactedSecret
  355. }
  356. data := userPage{
  357. basePage: getBasePageData(title, currentURL, r),
  358. Mode: mode,
  359. Error: error,
  360. User: user,
  361. ValidPerms: dataprovider.ValidPerms,
  362. ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
  363. ValidProtocols: dataprovider.ValidProtocols,
  364. RootDirPerms: user.GetPermissionsForPath("/"),
  365. RedactedSecret: redactedSecret,
  366. }
  367. renderTemplate(w, templateUser, data)
  368. }
  369. func renderFolderPage(w http.ResponseWriter, r *http.Request, folder vfs.BaseVirtualFolder, mode folderPageMode, error string) {
  370. var title, currentURL string
  371. switch mode {
  372. case folderPageModeAdd:
  373. title = "Add a new folder"
  374. currentURL = webFolderPath
  375. case folderPageModeUpdate:
  376. title = "Update folder"
  377. currentURL = fmt.Sprintf("%v/%v", webFolderPath, url.PathEscape(folder.Name))
  378. case folderPageModeTemplate:
  379. title = "Folder template"
  380. currentURL = webTemplateFolder
  381. }
  382. data := folderPage{
  383. basePage: getBasePageData(title, currentURL, r),
  384. Error: error,
  385. Folder: folder,
  386. Mode: mode,
  387. }
  388. renderTemplate(w, templateFolder, data)
  389. }
  390. func getFoldersForTemplate(r *http.Request) []string {
  391. var res []string
  392. formValue := r.Form.Get("folders")
  393. folders := make(map[string]bool)
  394. for _, name := range getSliceFromDelimitedValues(formValue, "\n") {
  395. if _, ok := folders[name]; ok {
  396. continue
  397. }
  398. folders[name] = true
  399. res = append(res, name)
  400. }
  401. return res
  402. }
  403. func getUsersForTemplate(r *http.Request) []userTemplateFields {
  404. var res []userTemplateFields
  405. formValue := r.Form.Get("users")
  406. users := make(map[string]bool)
  407. for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
  408. if strings.Contains(cleaned, "::") {
  409. mapping := strings.Split(cleaned, "::")
  410. if len(mapping) > 1 {
  411. username := strings.TrimSpace(mapping[0])
  412. password := strings.TrimSpace(mapping[1])
  413. var publicKey string
  414. if len(mapping) > 2 {
  415. publicKey = strings.TrimSpace(mapping[2])
  416. }
  417. if username == "" || (password == "" && publicKey == "") {
  418. continue
  419. }
  420. if _, ok := users[username]; ok {
  421. continue
  422. }
  423. users[username] = true
  424. res = append(res, userTemplateFields{
  425. Username: username,
  426. Password: password,
  427. PublicKey: publicKey,
  428. })
  429. }
  430. }
  431. }
  432. return res
  433. }
  434. func getVirtualFoldersFromPostFields(r *http.Request) []vfs.VirtualFolder {
  435. var virtualFolders []vfs.VirtualFolder
  436. formValue := r.Form.Get("virtual_folders")
  437. for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
  438. if strings.Contains(cleaned, "::") {
  439. mapping := strings.Split(cleaned, "::")
  440. if len(mapping) > 1 {
  441. vfolder := vfs.VirtualFolder{
  442. BaseVirtualFolder: vfs.BaseVirtualFolder{
  443. Name: strings.TrimSpace(mapping[1]),
  444. },
  445. VirtualPath: strings.TrimSpace(mapping[0]),
  446. QuotaFiles: -1,
  447. QuotaSize: -1,
  448. }
  449. if len(mapping) > 2 {
  450. quotaFiles, err := strconv.Atoi(strings.TrimSpace(mapping[2]))
  451. if err == nil {
  452. vfolder.QuotaFiles = quotaFiles
  453. }
  454. }
  455. if len(mapping) > 3 {
  456. quotaSize, err := strconv.ParseInt(strings.TrimSpace(mapping[3]), 10, 64)
  457. if err == nil {
  458. vfolder.QuotaSize = quotaSize
  459. }
  460. }
  461. virtualFolders = append(virtualFolders, vfolder)
  462. }
  463. }
  464. }
  465. return virtualFolders
  466. }
  467. func getUserPermissionsFromPostFields(r *http.Request) map[string][]string {
  468. permissions := make(map[string][]string)
  469. permissions["/"] = r.Form["permissions"]
  470. subDirsPermsValue := r.Form.Get("sub_dirs_permissions")
  471. for _, cleaned := range getSliceFromDelimitedValues(subDirsPermsValue, "\n") {
  472. if strings.Contains(cleaned, "::") {
  473. dirPerms := strings.Split(cleaned, "::")
  474. if len(dirPerms) > 1 {
  475. dir := dirPerms[0]
  476. dir = strings.TrimSpace(dir)
  477. perms := []string{}
  478. for _, p := range strings.Split(dirPerms[1], ",") {
  479. cleanedPerm := strings.TrimSpace(p)
  480. if cleanedPerm != "" {
  481. perms = append(perms, cleanedPerm)
  482. }
  483. }
  484. if dir != "" {
  485. permissions[dir] = perms
  486. }
  487. }
  488. }
  489. }
  490. return permissions
  491. }
  492. func getSliceFromDelimitedValues(values, delimiter string) []string {
  493. result := []string{}
  494. for _, v := range strings.Split(values, delimiter) {
  495. cleaned := strings.TrimSpace(v)
  496. if cleaned != "" {
  497. result = append(result, cleaned)
  498. }
  499. }
  500. return result
  501. }
  502. func getListFromPostFields(value string) map[string][]string {
  503. result := make(map[string][]string)
  504. for _, cleaned := range getSliceFromDelimitedValues(value, "\n") {
  505. if strings.Contains(cleaned, "::") {
  506. dirExts := strings.Split(cleaned, "::")
  507. if len(dirExts) > 1 {
  508. dir := dirExts[0]
  509. dir = path.Clean(strings.TrimSpace(dir))
  510. exts := []string{}
  511. for _, e := range strings.Split(dirExts[1], ",") {
  512. cleanedExt := strings.TrimSpace(e)
  513. if cleanedExt != "" {
  514. exts = append(exts, cleanedExt)
  515. }
  516. }
  517. if dir != "" {
  518. if _, ok := result[dir]; ok {
  519. result[dir] = append(result[dir], exts...)
  520. } else {
  521. result[dir] = exts
  522. }
  523. result[dir] = utils.RemoveDuplicates(result[dir])
  524. }
  525. }
  526. }
  527. }
  528. return result
  529. }
  530. func getFilePatternsFromPostField(valueAllowed, valuesDenied string) []dataprovider.PatternsFilter {
  531. var result []dataprovider.PatternsFilter
  532. allowedPatterns := getListFromPostFields(valueAllowed)
  533. deniedPatterns := getListFromPostFields(valuesDenied)
  534. for dirAllowed, allowPatterns := range allowedPatterns {
  535. filter := dataprovider.PatternsFilter{
  536. Path: dirAllowed,
  537. AllowedPatterns: allowPatterns,
  538. }
  539. for dirDenied, denPatterns := range deniedPatterns {
  540. if dirAllowed == dirDenied {
  541. filter.DeniedPatterns = denPatterns
  542. break
  543. }
  544. }
  545. result = append(result, filter)
  546. }
  547. for dirDenied, denPatterns := range deniedPatterns {
  548. found := false
  549. for _, res := range result {
  550. if res.Path == dirDenied {
  551. found = true
  552. break
  553. }
  554. }
  555. if !found {
  556. result = append(result, dataprovider.PatternsFilter{
  557. Path: dirDenied,
  558. DeniedPatterns: denPatterns,
  559. })
  560. }
  561. }
  562. return result
  563. }
  564. func getFileExtensionsFromPostField(valueAllowed, valuesDenied string) []dataprovider.ExtensionsFilter {
  565. var result []dataprovider.ExtensionsFilter
  566. allowedExtensions := getListFromPostFields(valueAllowed)
  567. deniedExtensions := getListFromPostFields(valuesDenied)
  568. for dirAllowed, allowedExts := range allowedExtensions {
  569. filter := dataprovider.ExtensionsFilter{
  570. Path: dirAllowed,
  571. AllowedExtensions: allowedExts,
  572. }
  573. for dirDenied, deniedExts := range deniedExtensions {
  574. if dirAllowed == dirDenied {
  575. filter.DeniedExtensions = deniedExts
  576. break
  577. }
  578. }
  579. result = append(result, filter)
  580. }
  581. for dirDenied, deniedExts := range deniedExtensions {
  582. found := false
  583. for _, res := range result {
  584. if res.Path == dirDenied {
  585. found = true
  586. break
  587. }
  588. }
  589. if !found {
  590. result = append(result, dataprovider.ExtensionsFilter{
  591. Path: dirDenied,
  592. DeniedExtensions: deniedExts,
  593. })
  594. }
  595. }
  596. return result
  597. }
  598. func getFiltersFromUserPostFields(r *http.Request) dataprovider.UserFilters {
  599. var filters dataprovider.UserFilters
  600. filters.AllowedIP = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  601. filters.DeniedIP = getSliceFromDelimitedValues(r.Form.Get("denied_ip"), ",")
  602. filters.DeniedLoginMethods = r.Form["ssh_login_methods"]
  603. filters.DeniedProtocols = r.Form["denied_protocols"]
  604. filters.FileExtensions = getFileExtensionsFromPostField(r.Form.Get("allowed_extensions"), r.Form.Get("denied_extensions"))
  605. filters.FilePatterns = getFilePatternsFromPostField(r.Form.Get("allowed_patterns"), r.Form.Get("denied_patterns"))
  606. return filters
  607. }
  608. func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
  609. secret := kms.NewPlainSecret(r.Form.Get(field))
  610. if strings.TrimSpace(secret.GetPayload()) == redactedSecret {
  611. secret.SetStatus(kms.SecretStatusRedacted)
  612. }
  613. if strings.TrimSpace(secret.GetPayload()) == "" {
  614. secret.SetStatus("")
  615. }
  616. return secret
  617. }
  618. func getS3Config(r *http.Request) (vfs.S3FsConfig, error) {
  619. var err error
  620. config := vfs.S3FsConfig{}
  621. config.Bucket = r.Form.Get("s3_bucket")
  622. config.Region = r.Form.Get("s3_region")
  623. config.AccessKey = r.Form.Get("s3_access_key")
  624. config.AccessSecret = getSecretFromFormField(r, "s3_access_secret")
  625. config.Endpoint = r.Form.Get("s3_endpoint")
  626. config.StorageClass = r.Form.Get("s3_storage_class")
  627. config.KeyPrefix = r.Form.Get("s3_key_prefix")
  628. config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
  629. if err != nil {
  630. return config, err
  631. }
  632. config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
  633. return config, err
  634. }
  635. func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
  636. var err error
  637. config := vfs.GCSFsConfig{}
  638. config.Bucket = r.Form.Get("gcs_bucket")
  639. config.StorageClass = r.Form.Get("gcs_storage_class")
  640. config.KeyPrefix = r.Form.Get("gcs_key_prefix")
  641. autoCredentials := r.Form.Get("gcs_auto_credentials")
  642. if autoCredentials != "" {
  643. config.AutomaticCredentials = 1
  644. } else {
  645. config.AutomaticCredentials = 0
  646. }
  647. credentials, _, err := r.FormFile("gcs_credential_file")
  648. if err == http.ErrMissingFile {
  649. return config, nil
  650. }
  651. if err != nil {
  652. return config, err
  653. }
  654. defer credentials.Close()
  655. fileBytes, err := io.ReadAll(credentials)
  656. if err != nil || len(fileBytes) == 0 {
  657. if len(fileBytes) == 0 {
  658. err = errors.New("credentials file size must be greater than 0")
  659. }
  660. return config, err
  661. }
  662. config.Credentials = kms.NewPlainSecret(string(fileBytes))
  663. config.AutomaticCredentials = 0
  664. return config, err
  665. }
  666. func getSFTPConfig(r *http.Request) vfs.SFTPFsConfig {
  667. config := vfs.SFTPFsConfig{}
  668. config.Endpoint = r.Form.Get("sftp_endpoint")
  669. config.Username = r.Form.Get("sftp_username")
  670. config.Password = getSecretFromFormField(r, "sftp_password")
  671. config.PrivateKey = getSecretFromFormField(r, "sftp_private_key")
  672. fingerprintsFormValue := r.Form.Get("sftp_fingerprints")
  673. config.Fingerprints = getSliceFromDelimitedValues(fingerprintsFormValue, "\n")
  674. config.Prefix = r.Form.Get("sftp_prefix")
  675. return config
  676. }
  677. func getAzureConfig(r *http.Request) (vfs.AzBlobFsConfig, error) {
  678. var err error
  679. config := vfs.AzBlobFsConfig{}
  680. config.Container = r.Form.Get("az_container")
  681. config.AccountName = r.Form.Get("az_account_name")
  682. config.AccountKey = getSecretFromFormField(r, "az_account_key")
  683. config.SASURL = r.Form.Get("az_sas_url")
  684. config.Endpoint = r.Form.Get("az_endpoint")
  685. config.KeyPrefix = r.Form.Get("az_key_prefix")
  686. config.AccessTier = r.Form.Get("az_access_tier")
  687. config.UseEmulator = len(r.Form.Get("az_use_emulator")) > 0
  688. config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("az_upload_part_size"), 10, 64)
  689. if err != nil {
  690. return config, err
  691. }
  692. config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("az_upload_concurrency"))
  693. return config, err
  694. }
  695. func getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, error) {
  696. var fs dataprovider.Filesystem
  697. provider, err := strconv.Atoi(r.Form.Get("fs_provider"))
  698. if err != nil {
  699. provider = int(dataprovider.LocalFilesystemProvider)
  700. }
  701. fs.Provider = dataprovider.FilesystemProvider(provider)
  702. switch fs.Provider {
  703. case dataprovider.S3FilesystemProvider:
  704. config, err := getS3Config(r)
  705. if err != nil {
  706. return fs, err
  707. }
  708. fs.S3Config = config
  709. case dataprovider.AzureBlobFilesystemProvider:
  710. config, err := getAzureConfig(r)
  711. if err != nil {
  712. return fs, err
  713. }
  714. fs.AzBlobConfig = config
  715. case dataprovider.GCSFilesystemProvider:
  716. config, err := getGCSConfig(r)
  717. if err != nil {
  718. return fs, err
  719. }
  720. fs.GCSConfig = config
  721. case dataprovider.CryptedFilesystemProvider:
  722. fs.CryptConfig.Passphrase = getSecretFromFormField(r, "crypt_passphrase")
  723. case dataprovider.SFTPFilesystemProvider:
  724. fs.SFTPConfig = getSFTPConfig(r)
  725. }
  726. return fs, nil
  727. }
  728. func getAdminFromPostFields(r *http.Request) (dataprovider.Admin, error) {
  729. var admin dataprovider.Admin
  730. err := r.ParseForm()
  731. if err != nil {
  732. return admin, err
  733. }
  734. status, err := strconv.Atoi(r.Form.Get("status"))
  735. if err != nil {
  736. return admin, err
  737. }
  738. admin.Username = r.Form.Get("username")
  739. admin.Password = r.Form.Get("password")
  740. admin.Permissions = r.Form["permissions"]
  741. admin.Email = r.Form.Get("email")
  742. admin.Status = status
  743. admin.Filters.AllowList = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
  744. admin.AdditionalInfo = r.Form.Get("additional_info")
  745. admin.Description = r.Form.Get("description")
  746. return admin, nil
  747. }
  748. func replacePlaceholders(field string, replacements map[string]string) string {
  749. for k, v := range replacements {
  750. field = strings.ReplaceAll(field, k, v)
  751. }
  752. return field
  753. }
  754. func getFolderFromTemplate(folder vfs.BaseVirtualFolder, name string) vfs.BaseVirtualFolder {
  755. folder.Name = name
  756. replacements := make(map[string]string)
  757. replacements["%name%"] = folder.Name
  758. folder.MappedPath = replacePlaceholders(folder.MappedPath, replacements)
  759. folder.Description = replacePlaceholders(folder.Description, replacements)
  760. return folder
  761. }
  762. func getCryptFsFromTemplate(fsConfig vfs.CryptFsConfig, replacements map[string]string) vfs.CryptFsConfig {
  763. if fsConfig.Passphrase != nil {
  764. if fsConfig.Passphrase.IsPlain() {
  765. payload := replacePlaceholders(fsConfig.Passphrase.GetPayload(), replacements)
  766. fsConfig.Passphrase = kms.NewPlainSecret(payload)
  767. }
  768. }
  769. return fsConfig
  770. }
  771. func getS3FsFromTemplate(fsConfig vfs.S3FsConfig, replacements map[string]string) vfs.S3FsConfig {
  772. fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
  773. fsConfig.AccessKey = replacePlaceholders(fsConfig.AccessKey, replacements)
  774. if fsConfig.AccessSecret != nil && fsConfig.AccessSecret.IsPlain() {
  775. payload := replacePlaceholders(fsConfig.AccessSecret.GetPayload(), replacements)
  776. fsConfig.AccessSecret = kms.NewPlainSecret(payload)
  777. }
  778. return fsConfig
  779. }
  780. func getGCSFsFromTemplate(fsConfig vfs.GCSFsConfig, replacements map[string]string) vfs.GCSFsConfig {
  781. fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
  782. return fsConfig
  783. }
  784. func getAzBlobFsFromTemplate(fsConfig vfs.AzBlobFsConfig, replacements map[string]string) vfs.AzBlobFsConfig {
  785. fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
  786. fsConfig.AccountName = replacePlaceholders(fsConfig.AccountName, replacements)
  787. if fsConfig.AccountKey != nil && fsConfig.AccountKey.IsPlain() {
  788. payload := replacePlaceholders(fsConfig.AccountKey.GetPayload(), replacements)
  789. fsConfig.AccountKey = kms.NewPlainSecret(payload)
  790. }
  791. return fsConfig
  792. }
  793. func getSFTPFsFromTemplate(fsConfig vfs.SFTPFsConfig, replacements map[string]string) vfs.SFTPFsConfig {
  794. fsConfig.Prefix = replacePlaceholders(fsConfig.Prefix, replacements)
  795. fsConfig.Username = replacePlaceholders(fsConfig.Username, replacements)
  796. if fsConfig.Password != nil && fsConfig.Password.IsPlain() {
  797. payload := replacePlaceholders(fsConfig.Password.GetPayload(), replacements)
  798. fsConfig.Password = kms.NewPlainSecret(payload)
  799. }
  800. return fsConfig
  801. }
  802. func getUserFromTemplate(user dataprovider.User, template userTemplateFields) dataprovider.User {
  803. user.Username = template.Username
  804. user.Password = template.Password
  805. user.PublicKeys = nil
  806. if template.PublicKey != "" {
  807. user.PublicKeys = append(user.PublicKeys, template.PublicKey)
  808. }
  809. replacements := make(map[string]string)
  810. replacements["%username%"] = user.Username
  811. user.Password = replacePlaceholders(user.Password, replacements)
  812. replacements["%password%"] = user.Password
  813. user.HomeDir = replacePlaceholders(user.HomeDir, replacements)
  814. var vfolders []vfs.VirtualFolder
  815. for _, vfolder := range user.VirtualFolders {
  816. vfolder.Name = replacePlaceholders(vfolder.Name, replacements)
  817. vfolder.VirtualPath = replacePlaceholders(vfolder.VirtualPath, replacements)
  818. vfolders = append(vfolders, vfolder)
  819. }
  820. user.VirtualFolders = vfolders
  821. user.Description = replacePlaceholders(user.Description, replacements)
  822. user.AdditionalInfo = replacePlaceholders(user.AdditionalInfo, replacements)
  823. switch user.FsConfig.Provider {
  824. case dataprovider.CryptedFilesystemProvider:
  825. user.FsConfig.CryptConfig = getCryptFsFromTemplate(user.FsConfig.CryptConfig, replacements)
  826. case dataprovider.S3FilesystemProvider:
  827. user.FsConfig.S3Config = getS3FsFromTemplate(user.FsConfig.S3Config, replacements)
  828. case dataprovider.GCSFilesystemProvider:
  829. user.FsConfig.GCSConfig = getGCSFsFromTemplate(user.FsConfig.GCSConfig, replacements)
  830. case dataprovider.AzureBlobFilesystemProvider:
  831. user.FsConfig.AzBlobConfig = getAzBlobFsFromTemplate(user.FsConfig.AzBlobConfig, replacements)
  832. case dataprovider.SFTPFilesystemProvider:
  833. user.FsConfig.SFTPConfig = getSFTPFsFromTemplate(user.FsConfig.SFTPConfig, replacements)
  834. }
  835. return user
  836. }
  837. func getUserFromPostFields(r *http.Request) (dataprovider.User, error) {
  838. var user dataprovider.User
  839. err := r.ParseMultipartForm(maxRequestSize)
  840. if err != nil {
  841. return user, err
  842. }
  843. publicKeysFormValue := r.Form.Get("public_keys")
  844. publicKeys := getSliceFromDelimitedValues(publicKeysFormValue, "\n")
  845. uid, err := strconv.Atoi(r.Form.Get("uid"))
  846. if err != nil {
  847. return user, err
  848. }
  849. gid, err := strconv.Atoi(r.Form.Get("gid"))
  850. if err != nil {
  851. return user, err
  852. }
  853. maxSessions, err := strconv.Atoi(r.Form.Get("max_sessions"))
  854. if err != nil {
  855. return user, err
  856. }
  857. quotaSize, err := strconv.ParseInt(r.Form.Get("quota_size"), 10, 64)
  858. if err != nil {
  859. return user, err
  860. }
  861. quotaFiles, err := strconv.Atoi(r.Form.Get("quota_files"))
  862. if err != nil {
  863. return user, err
  864. }
  865. bandwidthUL, err := strconv.ParseInt(r.Form.Get("upload_bandwidth"), 10, 64)
  866. if err != nil {
  867. return user, err
  868. }
  869. bandwidthDL, err := strconv.ParseInt(r.Form.Get("download_bandwidth"), 10, 64)
  870. if err != nil {
  871. return user, err
  872. }
  873. status, err := strconv.Atoi(r.Form.Get("status"))
  874. if err != nil {
  875. return user, err
  876. }
  877. expirationDateMillis := int64(0)
  878. expirationDateString := r.Form.Get("expiration_date")
  879. if len(strings.TrimSpace(expirationDateString)) > 0 {
  880. expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
  881. if err != nil {
  882. return user, err
  883. }
  884. expirationDateMillis = utils.GetTimeAsMsSinceEpoch(expirationDate)
  885. }
  886. fsConfig, err := getFsConfigFromUserPostFields(r)
  887. if err != nil {
  888. return user, err
  889. }
  890. user = dataprovider.User{
  891. Username: r.Form.Get("username"),
  892. Password: r.Form.Get("password"),
  893. PublicKeys: publicKeys,
  894. HomeDir: r.Form.Get("home_dir"),
  895. VirtualFolders: getVirtualFoldersFromPostFields(r),
  896. UID: uid,
  897. GID: gid,
  898. Permissions: getUserPermissionsFromPostFields(r),
  899. MaxSessions: maxSessions,
  900. QuotaSize: quotaSize,
  901. QuotaFiles: quotaFiles,
  902. UploadBandwidth: bandwidthUL,
  903. DownloadBandwidth: bandwidthDL,
  904. Status: status,
  905. ExpirationDate: expirationDateMillis,
  906. Filters: getFiltersFromUserPostFields(r),
  907. FsConfig: fsConfig,
  908. AdditionalInfo: r.Form.Get("additional_info"),
  909. Description: r.Form.Get("description"),
  910. }
  911. maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
  912. user.Filters.MaxUploadFileSize = maxFileSize
  913. return user, err
  914. }
  915. func renderLoginPage(w http.ResponseWriter, error string) {
  916. data := loginPage{
  917. CurrentURL: webLoginPath,
  918. Version: version.Get().Version,
  919. Error: error,
  920. CSRFToken: createCSRFToken(),
  921. }
  922. renderTemplate(w, templateLogin, data)
  923. }
  924. func handleWebAdminChangePwd(w http.ResponseWriter, r *http.Request) {
  925. renderChangePwdPage(w, r, "")
  926. }
  927. func handleWebAdminChangePwdPost(w http.ResponseWriter, r *http.Request) {
  928. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  929. err := r.ParseForm()
  930. if err != nil {
  931. renderChangePwdPage(w, r, err.Error())
  932. return
  933. }
  934. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  935. renderForbiddenPage(w, r, err.Error())
  936. return
  937. }
  938. err = doChangeAdminPassword(r, r.Form.Get("current_password"), r.Form.Get("new_password1"),
  939. r.Form.Get("new_password2"))
  940. if err != nil {
  941. renderChangePwdPage(w, r, err.Error())
  942. return
  943. }
  944. handleWebLogout(w, r)
  945. }
  946. func handleWebLogout(w http.ResponseWriter, r *http.Request) {
  947. c := jwtTokenClaims{}
  948. c.removeCookie(w, r)
  949. http.Redirect(w, r, webLoginPath, http.StatusFound)
  950. }
  951. func handleWebLogin(w http.ResponseWriter, r *http.Request) {
  952. renderLoginPage(w, "")
  953. }
  954. func handleWebMaintenance(w http.ResponseWriter, r *http.Request) {
  955. renderMaintenancePage(w, r, "")
  956. }
  957. func handleWebRestore(w http.ResponseWriter, r *http.Request) {
  958. err := r.ParseMultipartForm(MaxRestoreSize)
  959. if err != nil {
  960. renderMaintenancePage(w, r, err.Error())
  961. return
  962. }
  963. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  964. renderForbiddenPage(w, r, err.Error())
  965. return
  966. }
  967. restoreMode, err := strconv.Atoi(r.Form.Get("mode"))
  968. if err != nil {
  969. renderMaintenancePage(w, r, err.Error())
  970. return
  971. }
  972. scanQuota, err := strconv.Atoi(r.Form.Get("quota"))
  973. if err != nil {
  974. renderMaintenancePage(w, r, err.Error())
  975. return
  976. }
  977. backupFile, _, err := r.FormFile("backup_file")
  978. if err != nil {
  979. renderMaintenancePage(w, r, err.Error())
  980. return
  981. }
  982. defer backupFile.Close()
  983. backupContent, err := io.ReadAll(backupFile)
  984. if err != nil || len(backupContent) == 0 {
  985. if len(backupContent) == 0 {
  986. err = errors.New("backup file size must be greater than 0")
  987. }
  988. renderMaintenancePage(w, r, err.Error())
  989. return
  990. }
  991. if err := restoreBackup(backupContent, "", scanQuota, restoreMode); err != nil {
  992. renderMaintenancePage(w, r, err.Error())
  993. return
  994. }
  995. renderMessagePage(w, r, "Data restored", "", http.StatusOK, nil, "Your backup was successfully restored")
  996. }
  997. func handleGetWebAdmins(w http.ResponseWriter, r *http.Request) {
  998. limit := defaultQueryLimit
  999. if _, ok := r.URL.Query()["qlimit"]; ok {
  1000. var err error
  1001. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  1002. if err != nil {
  1003. limit = defaultQueryLimit
  1004. }
  1005. }
  1006. admins := make([]dataprovider.Admin, 0, limit)
  1007. for {
  1008. a, err := dataprovider.GetAdmins(limit, len(admins), dataprovider.OrderASC)
  1009. if err != nil {
  1010. renderInternalServerErrorPage(w, r, err)
  1011. return
  1012. }
  1013. admins = append(admins, a...)
  1014. if len(a) < limit {
  1015. break
  1016. }
  1017. }
  1018. data := adminsPage{
  1019. basePage: getBasePageData(pageAdminsTitle, webAdminsPath, r),
  1020. Admins: admins,
  1021. }
  1022. renderTemplate(w, templateAdmins, data)
  1023. }
  1024. func handleWebAddAdminGet(w http.ResponseWriter, r *http.Request) {
  1025. admin := &dataprovider.Admin{Status: 1}
  1026. renderAddUpdateAdminPage(w, r, admin, "", true)
  1027. }
  1028. func handleWebUpdateAdminGet(w http.ResponseWriter, r *http.Request) {
  1029. username := getURLParam(r, "username")
  1030. admin, err := dataprovider.AdminExists(username)
  1031. if err == nil {
  1032. renderAddUpdateAdminPage(w, r, &admin, "", false)
  1033. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1034. renderNotFoundPage(w, r, err)
  1035. } else {
  1036. renderInternalServerErrorPage(w, r, err)
  1037. }
  1038. }
  1039. func handleWebAddAdminPost(w http.ResponseWriter, r *http.Request) {
  1040. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1041. admin, err := getAdminFromPostFields(r)
  1042. if err != nil {
  1043. renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
  1044. return
  1045. }
  1046. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1047. renderForbiddenPage(w, r, err.Error())
  1048. return
  1049. }
  1050. err = dataprovider.AddAdmin(&admin)
  1051. if err != nil {
  1052. renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
  1053. return
  1054. }
  1055. http.Redirect(w, r, webAdminsPath, http.StatusSeeOther)
  1056. }
  1057. func handleWebUpdateAdminPost(w http.ResponseWriter, r *http.Request) {
  1058. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1059. username := getURLParam(r, "username")
  1060. admin, err := dataprovider.AdminExists(username)
  1061. if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1062. renderNotFoundPage(w, r, err)
  1063. return
  1064. } else if err != nil {
  1065. renderInternalServerErrorPage(w, r, err)
  1066. return
  1067. }
  1068. updatedAdmin, err := getAdminFromPostFields(r)
  1069. if err != nil {
  1070. renderAddUpdateAdminPage(w, r, &updatedAdmin, err.Error(), false)
  1071. return
  1072. }
  1073. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1074. renderForbiddenPage(w, r, err.Error())
  1075. return
  1076. }
  1077. updatedAdmin.ID = admin.ID
  1078. updatedAdmin.Username = admin.Username
  1079. if updatedAdmin.Password == "" {
  1080. updatedAdmin.Password = admin.Password
  1081. }
  1082. claims, err := getTokenClaims(r)
  1083. if err != nil || claims.Username == "" {
  1084. renderAddUpdateAdminPage(w, r, &updatedAdmin, fmt.Sprintf("Invalid token claims: %v", err), false)
  1085. return
  1086. }
  1087. if username == claims.Username {
  1088. if claims.isCriticalPermRemoved(updatedAdmin.Permissions) {
  1089. renderAddUpdateAdminPage(w, r, &updatedAdmin, "You cannot remove these permissions to yourself", false)
  1090. return
  1091. }
  1092. if updatedAdmin.Status == 0 {
  1093. renderAddUpdateAdminPage(w, r, &updatedAdmin, "You cannot disable yourself", false)
  1094. return
  1095. }
  1096. }
  1097. err = dataprovider.UpdateAdmin(&updatedAdmin)
  1098. if err != nil {
  1099. renderAddUpdateAdminPage(w, r, &admin, err.Error(), false)
  1100. return
  1101. }
  1102. http.Redirect(w, r, webAdminsPath, http.StatusSeeOther)
  1103. }
  1104. func handleGetWebUsers(w http.ResponseWriter, r *http.Request) {
  1105. limit := defaultQueryLimit
  1106. if _, ok := r.URL.Query()["qlimit"]; ok {
  1107. var err error
  1108. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  1109. if err != nil {
  1110. limit = defaultQueryLimit
  1111. }
  1112. }
  1113. users := make([]dataprovider.User, 0, limit)
  1114. for {
  1115. u, err := dataprovider.GetUsers(limit, len(users), dataprovider.OrderASC)
  1116. if err != nil {
  1117. renderInternalServerErrorPage(w, r, err)
  1118. return
  1119. }
  1120. users = append(users, u...)
  1121. if len(u) < limit {
  1122. break
  1123. }
  1124. }
  1125. data := usersPage{
  1126. basePage: getBasePageData(pageUsersTitle, webUsersPath, r),
  1127. Users: users,
  1128. }
  1129. renderTemplate(w, templateUsers, data)
  1130. }
  1131. func handleWebTemplateFolderGet(w http.ResponseWriter, r *http.Request) {
  1132. if r.URL.Query().Get("from") != "" {
  1133. name := r.URL.Query().Get("from")
  1134. folder, err := dataprovider.GetFolderByName(name)
  1135. if err == nil {
  1136. renderFolderPage(w, r, folder, folderPageModeTemplate, "")
  1137. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1138. renderNotFoundPage(w, r, err)
  1139. } else {
  1140. renderInternalServerErrorPage(w, r, err)
  1141. }
  1142. } else {
  1143. folder := vfs.BaseVirtualFolder{}
  1144. renderFolderPage(w, r, folder, folderPageModeTemplate, "")
  1145. }
  1146. }
  1147. func handleWebTemplateFolderPost(w http.ResponseWriter, r *http.Request) {
  1148. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1149. templateFolder := vfs.BaseVirtualFolder{}
  1150. err := r.ParseForm()
  1151. if err != nil {
  1152. renderMessagePage(w, r, "Error parsing folders fields", "", http.StatusBadRequest, err, "")
  1153. return
  1154. }
  1155. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1156. renderForbiddenPage(w, r, err.Error())
  1157. return
  1158. }
  1159. templateFolder.MappedPath = r.Form.Get("mapped_path")
  1160. templateFolder.Description = r.Form.Get("description")
  1161. var dump dataprovider.BackupData
  1162. dump.Version = dataprovider.DumpVersion
  1163. foldersFields := getFoldersForTemplate(r)
  1164. for _, tmpl := range foldersFields {
  1165. f := getFolderFromTemplate(templateFolder, tmpl)
  1166. if err := dataprovider.ValidateFolder(&f); err != nil {
  1167. renderMessagePage(w, r, fmt.Sprintf("Error validating folder %#v", f.Name), "", http.StatusBadRequest, err, "")
  1168. return
  1169. }
  1170. dump.Folders = append(dump.Folders, f)
  1171. }
  1172. if len(dump.Folders) == 0 {
  1173. renderMessagePage(w, r, "No folders to export", "No valid folders found, export is not possible", http.StatusBadRequest, nil, "")
  1174. return
  1175. }
  1176. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-folders-from-template.json\"", len(dump.Folders)))
  1177. render.JSON(w, r, dump)
  1178. }
  1179. func handleWebTemplateUserGet(w http.ResponseWriter, r *http.Request) {
  1180. if r.URL.Query().Get("from") != "" {
  1181. username := r.URL.Query().Get("from")
  1182. user, err := dataprovider.UserExists(username)
  1183. if err == nil {
  1184. user.SetEmptySecrets()
  1185. renderUserPage(w, r, &user, userPageModeTemplate, "")
  1186. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1187. renderNotFoundPage(w, r, err)
  1188. } else {
  1189. renderInternalServerErrorPage(w, r, err)
  1190. }
  1191. } else {
  1192. user := dataprovider.User{Status: 1}
  1193. renderUserPage(w, r, &user, userPageModeTemplate, "")
  1194. }
  1195. }
  1196. func handleWebTemplateUserPost(w http.ResponseWriter, r *http.Request) {
  1197. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1198. templateUser, err := getUserFromPostFields(r)
  1199. if err != nil {
  1200. renderMessagePage(w, r, "Error parsing user fields", "", http.StatusBadRequest, err, "")
  1201. return
  1202. }
  1203. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1204. renderForbiddenPage(w, r, err.Error())
  1205. return
  1206. }
  1207. var dump dataprovider.BackupData
  1208. dump.Version = dataprovider.DumpVersion
  1209. userTmplFields := getUsersForTemplate(r)
  1210. for _, tmpl := range userTmplFields {
  1211. u := getUserFromTemplate(templateUser, tmpl)
  1212. if err := dataprovider.ValidateUser(&u); err != nil {
  1213. renderMessagePage(w, r, fmt.Sprintf("Error validating user %#v", u.Username), "", http.StatusBadRequest, err, "")
  1214. return
  1215. }
  1216. dump.Users = append(dump.Users, u)
  1217. for _, folder := range u.VirtualFolders {
  1218. if !dump.HasFolder(folder.Name) {
  1219. dump.Folders = append(dump.Folders, folder.BaseVirtualFolder)
  1220. }
  1221. }
  1222. }
  1223. if len(dump.Users) == 0 {
  1224. renderMessagePage(w, r, "No users to export", "No valid users found, export is not possible", http.StatusBadRequest, nil, "")
  1225. return
  1226. }
  1227. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-users-from-template.json\"", len(dump.Users)))
  1228. render.JSON(w, r, dump)
  1229. }
  1230. func handleWebAddUserGet(w http.ResponseWriter, r *http.Request) {
  1231. if r.URL.Query().Get("clone-from") != "" {
  1232. username := r.URL.Query().Get("clone-from")
  1233. user, err := dataprovider.UserExists(username)
  1234. if err == nil {
  1235. user.ID = 0
  1236. user.Username = ""
  1237. user.Password = ""
  1238. user.SetEmptySecrets()
  1239. renderUserPage(w, r, &user, userPageModeAdd, "")
  1240. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1241. renderNotFoundPage(w, r, err)
  1242. } else {
  1243. renderInternalServerErrorPage(w, r, err)
  1244. }
  1245. } else {
  1246. user := dataprovider.User{Status: 1}
  1247. renderUserPage(w, r, &user, userPageModeAdd, "")
  1248. }
  1249. }
  1250. func handleWebUpdateUserGet(w http.ResponseWriter, r *http.Request) {
  1251. username := getURLParam(r, "username")
  1252. user, err := dataprovider.UserExists(username)
  1253. if err == nil {
  1254. renderUserPage(w, r, &user, userPageModeUpdate, "")
  1255. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1256. renderNotFoundPage(w, r, err)
  1257. } else {
  1258. renderInternalServerErrorPage(w, r, err)
  1259. }
  1260. }
  1261. func handleWebAddUserPost(w http.ResponseWriter, r *http.Request) {
  1262. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1263. user, err := getUserFromPostFields(r)
  1264. if err != nil {
  1265. renderUserPage(w, r, &user, userPageModeAdd, err.Error())
  1266. return
  1267. }
  1268. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1269. renderForbiddenPage(w, r, err.Error())
  1270. return
  1271. }
  1272. err = dataprovider.AddUser(&user)
  1273. if err == nil {
  1274. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  1275. } else {
  1276. renderUserPage(w, r, &user, userPageModeAdd, err.Error())
  1277. }
  1278. }
  1279. func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
  1280. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1281. username := getURLParam(r, "username")
  1282. user, err := dataprovider.UserExists(username)
  1283. if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1284. renderNotFoundPage(w, r, err)
  1285. return
  1286. } else if err != nil {
  1287. renderInternalServerErrorPage(w, r, err)
  1288. return
  1289. }
  1290. updatedUser, err := getUserFromPostFields(r)
  1291. if err != nil {
  1292. renderUserPage(w, r, &user, userPageModeUpdate, err.Error())
  1293. return
  1294. }
  1295. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1296. renderForbiddenPage(w, r, err.Error())
  1297. return
  1298. }
  1299. updatedUser.ID = user.ID
  1300. updatedUser.Username = user.Username
  1301. updatedUser.SetEmptySecretsIfNil()
  1302. if updatedUser.Password == redactedSecret {
  1303. updatedUser.Password = user.Password
  1304. }
  1305. updateEncryptedSecrets(&updatedUser, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
  1306. user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase, user.FsConfig.SFTPConfig.Password,
  1307. user.FsConfig.SFTPConfig.PrivateKey)
  1308. err = dataprovider.UpdateUser(&updatedUser)
  1309. if err == nil {
  1310. if len(r.Form.Get("disconnect")) > 0 {
  1311. disconnectUser(user.Username)
  1312. }
  1313. http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
  1314. } else {
  1315. renderUserPage(w, r, &user, userPageModeUpdate, err.Error())
  1316. }
  1317. }
  1318. func handleWebGetStatus(w http.ResponseWriter, r *http.Request) {
  1319. data := statusPage{
  1320. basePage: getBasePageData(pageStatusTitle, webStatusPath, r),
  1321. Status: getServicesStatus(),
  1322. }
  1323. renderTemplate(w, templateStatus, data)
  1324. }
  1325. func handleWebGetConnections(w http.ResponseWriter, r *http.Request) {
  1326. connectionStats := common.Connections.GetStats()
  1327. data := connectionsPage{
  1328. basePage: getBasePageData(pageConnectionsTitle, webConnectionsPath, r),
  1329. Connections: connectionStats,
  1330. }
  1331. renderTemplate(w, templateConnections, data)
  1332. }
  1333. func handleWebAddFolderGet(w http.ResponseWriter, r *http.Request) {
  1334. renderFolderPage(w, r, vfs.BaseVirtualFolder{}, folderPageModeAdd, "")
  1335. }
  1336. func handleWebAddFolderPost(w http.ResponseWriter, r *http.Request) {
  1337. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1338. folder := vfs.BaseVirtualFolder{}
  1339. err := r.ParseForm()
  1340. if err != nil {
  1341. renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
  1342. return
  1343. }
  1344. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1345. renderForbiddenPage(w, r, err.Error())
  1346. return
  1347. }
  1348. folder.MappedPath = r.Form.Get("mapped_path")
  1349. folder.Name = r.Form.Get("name")
  1350. folder.Description = r.Form.Get("description")
  1351. err = dataprovider.AddFolder(&folder)
  1352. if err == nil {
  1353. http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
  1354. } else {
  1355. renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
  1356. }
  1357. }
  1358. func handleWebUpdateFolderGet(w http.ResponseWriter, r *http.Request) {
  1359. name := getURLParam(r, "name")
  1360. folder, err := dataprovider.GetFolderByName(name)
  1361. if err == nil {
  1362. renderFolderPage(w, r, folder, folderPageModeUpdate, "")
  1363. } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1364. renderNotFoundPage(w, r, err)
  1365. } else {
  1366. renderInternalServerErrorPage(w, r, err)
  1367. }
  1368. }
  1369. func handleWebUpdateFolderPost(w http.ResponseWriter, r *http.Request) {
  1370. r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
  1371. name := getURLParam(r, "name")
  1372. folder, err := dataprovider.GetFolderByName(name)
  1373. if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
  1374. renderNotFoundPage(w, r, err)
  1375. return
  1376. } else if err != nil {
  1377. renderInternalServerErrorPage(w, r, err)
  1378. return
  1379. }
  1380. err = r.ParseForm()
  1381. if err != nil {
  1382. renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
  1383. return
  1384. }
  1385. if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
  1386. renderForbiddenPage(w, r, err.Error())
  1387. return
  1388. }
  1389. folder.MappedPath = r.Form.Get("mapped_path")
  1390. folder.Description = r.Form.Get("description")
  1391. err = dataprovider.UpdateFolder(&folder)
  1392. if err != nil {
  1393. renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
  1394. return
  1395. }
  1396. http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
  1397. }
  1398. func handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
  1399. limit := defaultQueryLimit
  1400. if _, ok := r.URL.Query()["qlimit"]; ok {
  1401. var err error
  1402. limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
  1403. if err != nil {
  1404. limit = defaultQueryLimit
  1405. }
  1406. }
  1407. folders := make([]vfs.BaseVirtualFolder, 0, limit)
  1408. for {
  1409. f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC)
  1410. if err != nil {
  1411. renderInternalServerErrorPage(w, r, err)
  1412. return
  1413. }
  1414. folders = append(folders, f...)
  1415. if len(f) < limit {
  1416. break
  1417. }
  1418. }
  1419. data := foldersPage{
  1420. basePage: getBasePageData(pageFoldersTitle, webFoldersPath, r),
  1421. Folders: folders,
  1422. }
  1423. renderTemplate(w, templateFolders, data)
  1424. }