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