webadmin.go 47 KB

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