webadmin.go 49 KB

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