webadmin.go 49 KB

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