webadmin.go 48 KB

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