12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039 |
- package httpd
- import (
- "errors"
- "fmt"
- "html/template"
- "io"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "time"
- "github.com/go-chi/render"
- "github.com/sftpgo/sdk"
- sdkkms "github.com/sftpgo/sdk/kms"
- "github.com/drakkan/sftpgo/v2/common"
- "github.com/drakkan/sftpgo/v2/dataprovider"
- "github.com/drakkan/sftpgo/v2/kms"
- "github.com/drakkan/sftpgo/v2/mfa"
- "github.com/drakkan/sftpgo/v2/smtp"
- "github.com/drakkan/sftpgo/v2/util"
- "github.com/drakkan/sftpgo/v2/version"
- "github.com/drakkan/sftpgo/v2/vfs"
- )
- type userPageMode int
- const (
- userPageModeAdd userPageMode = iota + 1
- userPageModeUpdate
- userPageModeTemplate
- )
- type folderPageMode int
- const (
- folderPageModeAdd folderPageMode = iota + 1
- folderPageModeUpdate
- folderPageModeTemplate
- )
- const (
- templateAdminDir = "webadmin"
- templateBase = "base.html"
- templateBaseLogin = "baselogin.html"
- templateFsConfig = "fsconfig.html"
- templateUsers = "users.html"
- templateUser = "user.html"
- templateAdmins = "admins.html"
- templateAdmin = "admin.html"
- templateConnections = "connections.html"
- templateFolders = "folders.html"
- templateFolder = "folder.html"
- templateMessage = "message.html"
- templateStatus = "status.html"
- templateLogin = "login.html"
- templateDefender = "defender.html"
- templateProfile = "profile.html"
- templateChangePwd = "changepassword.html"
- templateMaintenance = "maintenance.html"
- templateMFA = "mfa.html"
- templateSetup = "adminsetup.html"
- pageUsersTitle = "Users"
- pageAdminsTitle = "Admins"
- pageConnectionsTitle = "Connections"
- pageStatusTitle = "Status"
- pageFoldersTitle = "Folders"
- pageProfileTitle = "My profile"
- pageChangePwdTitle = "Change password"
- pageMaintenanceTitle = "Maintenance"
- pageDefenderTitle = "Defender"
- pageForgotPwdTitle = "SFTPGo Admin - Forgot password"
- pageResetPwdTitle = "SFTPGo Admin - Reset password"
- pageSetupTitle = "Create first admin user"
- defaultQueryLimit = 500
- )
- var (
- adminTemplates = make(map[string]*template.Template)
- )
- type basePage struct {
- Title string
- CurrentURL string
- UsersURL string
- UserURL string
- UserTemplateURL string
- AdminsURL string
- AdminURL string
- QuotaScanURL string
- ConnectionsURL string
- FoldersURL string
- FolderURL string
- FolderTemplateURL string
- DefenderURL string
- LogoutURL string
- ProfileURL string
- ChangePwdURL string
- MFAURL string
- FolderQuotaScanURL string
- StatusURL string
- MaintenanceURL string
- StaticURL string
- UsersTitle string
- AdminsTitle string
- ConnectionsTitle string
- FoldersTitle string
- StatusTitle string
- MaintenanceTitle string
- DefenderTitle string
- Version string
- CSRFToken string
- HasDefender bool
- LoggedAdmin *dataprovider.Admin
- }
- type usersPage struct {
- basePage
- Users []dataprovider.User
- }
- type adminsPage struct {
- basePage
- Admins []dataprovider.Admin
- }
- type foldersPage struct {
- basePage
- Folders []vfs.BaseVirtualFolder
- }
- type connectionsPage struct {
- basePage
- Connections []*common.ConnectionStatus
- }
- type statusPage struct {
- basePage
- Status ServicesStatus
- }
- type fsWrapper struct {
- vfs.Filesystem
- IsUserPage bool
- HasUsersBaseDir bool
- DirPath string
- }
- type userPage struct {
- basePage
- User *dataprovider.User
- RootPerms []string
- Error string
- ValidPerms []string
- ValidLoginMethods []string
- ValidProtocols []string
- WebClientOptions []string
- RootDirPerms []string
- RedactedSecret string
- Mode userPageMode
- VirtualFolders []vfs.BaseVirtualFolder
- CanImpersonate bool
- FsWrapper fsWrapper
- }
- type adminPage struct {
- basePage
- Admin *dataprovider.Admin
- Error string
- IsAdd bool
- }
- type profilePage struct {
- basePage
- Error string
- AllowAPIKeyAuth bool
- Email string
- Description string
- }
- type changePasswordPage struct {
- basePage
- Error string
- }
- type mfaPage struct {
- basePage
- TOTPConfigs []string
- TOTPConfig dataprovider.AdminTOTPConfig
- GenerateTOTPURL string
- ValidateTOTPURL string
- SaveTOTPURL string
- RecCodesURL string
- }
- type maintenancePage struct {
- basePage
- BackupPath string
- RestorePath string
- Error string
- }
- type defenderHostsPage struct {
- basePage
- DefenderHostsURL string
- }
- type setupPage struct {
- basePage
- Username string
- Error string
- }
- type folderPage struct {
- basePage
- Folder vfs.BaseVirtualFolder
- Error string
- Mode folderPageMode
- FsWrapper fsWrapper
- }
- type messagePage struct {
- basePage
- Error string
- Success string
- }
- type userTemplateFields struct {
- Username string
- Password string
- PublicKey string
- }
- func loadAdminTemplates(templatesPath string) {
- usersPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateUsers),
- }
- userPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateFsConfig),
- filepath.Join(templatesPath, templateAdminDir, templateUser),
- }
- adminsPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateAdmins),
- }
- adminPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateAdmin),
- }
- profilePaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateProfile),
- }
- changePwdPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateChangePwd),
- }
- connectionsPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateConnections),
- }
- messagePaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateMessage),
- }
- foldersPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateFolders),
- }
- folderPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateFsConfig),
- filepath.Join(templatesPath, templateAdminDir, templateFolder),
- }
- statusPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateStatus),
- }
- loginPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBaseLogin),
- filepath.Join(templatesPath, templateAdminDir, templateLogin),
- }
- maintenancePaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateMaintenance),
- }
- defenderPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateDefender),
- }
- mfaPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBase),
- filepath.Join(templatesPath, templateAdminDir, templateMFA),
- }
- twoFactorPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBaseLogin),
- filepath.Join(templatesPath, templateAdminDir, templateTwoFactor),
- }
- twoFactorRecoveryPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBaseLogin),
- filepath.Join(templatesPath, templateAdminDir, templateTwoFactorRecovery),
- }
- setupPaths := []string{
- filepath.Join(templatesPath, templateAdminDir, templateBaseLogin),
- filepath.Join(templatesPath, templateAdminDir, templateSetup),
- }
- forgotPwdPaths := []string{
- filepath.Join(templatesPath, templateCommonDir, templateForgotPassword),
- }
- resetPwdPaths := []string{
- filepath.Join(templatesPath, templateCommonDir, templateResetPassword),
- }
- fsBaseTpl := template.New("fsBaseTemplate").Funcs(template.FuncMap{
- "ListFSProviders": func() []sdk.FilesystemProvider {
- return []sdk.FilesystemProvider{sdk.LocalFilesystemProvider, sdk.CryptedFilesystemProvider,
- sdk.S3FilesystemProvider, sdk.GCSFilesystemProvider,
- sdk.AzureBlobFilesystemProvider, sdk.SFTPFilesystemProvider,
- }
- },
- })
- usersTmpl := util.LoadTemplate(nil, usersPaths...)
- userTmpl := util.LoadTemplate(fsBaseTpl, userPaths...)
- adminsTmpl := util.LoadTemplate(nil, adminsPaths...)
- adminTmpl := util.LoadTemplate(nil, adminPaths...)
- connectionsTmpl := util.LoadTemplate(nil, connectionsPaths...)
- messageTmpl := util.LoadTemplate(nil, messagePaths...)
- foldersTmpl := util.LoadTemplate(nil, foldersPaths...)
- folderTmpl := util.LoadTemplate(fsBaseTpl, folderPaths...)
- statusTmpl := util.LoadTemplate(nil, statusPaths...)
- loginTmpl := util.LoadTemplate(nil, loginPaths...)
- profileTmpl := util.LoadTemplate(nil, profilePaths...)
- changePwdTmpl := util.LoadTemplate(nil, changePwdPaths...)
- maintenanceTmpl := util.LoadTemplate(nil, maintenancePaths...)
- defenderTmpl := util.LoadTemplate(nil, defenderPaths...)
- mfaTmpl := util.LoadTemplate(nil, mfaPaths...)
- twoFactorTmpl := util.LoadTemplate(nil, twoFactorPaths...)
- twoFactorRecoveryTmpl := util.LoadTemplate(nil, twoFactorRecoveryPaths...)
- setupTmpl := util.LoadTemplate(nil, setupPaths...)
- forgotPwdTmpl := util.LoadTemplate(nil, forgotPwdPaths...)
- resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
- adminTemplates[templateUsers] = usersTmpl
- adminTemplates[templateUser] = userTmpl
- adminTemplates[templateAdmins] = adminsTmpl
- adminTemplates[templateAdmin] = adminTmpl
- adminTemplates[templateConnections] = connectionsTmpl
- adminTemplates[templateMessage] = messageTmpl
- adminTemplates[templateFolders] = foldersTmpl
- adminTemplates[templateFolder] = folderTmpl
- adminTemplates[templateStatus] = statusTmpl
- adminTemplates[templateLogin] = loginTmpl
- adminTemplates[templateProfile] = profileTmpl
- adminTemplates[templateChangePwd] = changePwdTmpl
- adminTemplates[templateMaintenance] = maintenanceTmpl
- adminTemplates[templateDefender] = defenderTmpl
- adminTemplates[templateMFA] = mfaTmpl
- adminTemplates[templateTwoFactor] = twoFactorTmpl
- adminTemplates[templateTwoFactorRecovery] = twoFactorRecoveryTmpl
- adminTemplates[templateSetup] = setupTmpl
- adminTemplates[templateForgotPassword] = forgotPwdTmpl
- adminTemplates[templateResetPassword] = resetPwdTmpl
- }
- func getBasePageData(title, currentURL string, r *http.Request) basePage {
- var csrfToken string
- if currentURL != "" {
- csrfToken = createCSRFToken()
- }
- return basePage{
- Title: title,
- CurrentURL: currentURL,
- UsersURL: webUsersPath,
- UserURL: webUserPath,
- UserTemplateURL: webTemplateUser,
- AdminsURL: webAdminsPath,
- AdminURL: webAdminPath,
- FoldersURL: webFoldersPath,
- FolderURL: webFolderPath,
- FolderTemplateURL: webTemplateFolder,
- DefenderURL: webDefenderPath,
- LogoutURL: webLogoutPath,
- ProfileURL: webAdminProfilePath,
- ChangePwdURL: webChangeAdminPwdPath,
- MFAURL: webAdminMFAPath,
- QuotaScanURL: webQuotaScanPath,
- ConnectionsURL: webConnectionsPath,
- StatusURL: webStatusPath,
- FolderQuotaScanURL: webScanVFolderPath,
- MaintenanceURL: webMaintenancePath,
- StaticURL: webStaticFilesPath,
- UsersTitle: pageUsersTitle,
- AdminsTitle: pageAdminsTitle,
- ConnectionsTitle: pageConnectionsTitle,
- FoldersTitle: pageFoldersTitle,
- StatusTitle: pageStatusTitle,
- MaintenanceTitle: pageMaintenanceTitle,
- DefenderTitle: pageDefenderTitle,
- Version: version.GetAsString(),
- LoggedAdmin: getAdminFromToken(r),
- HasDefender: common.Config.DefenderConfig.Enabled,
- CSRFToken: csrfToken,
- }
- }
- func renderAdminTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
- err := adminTemplates[tmplName].ExecuteTemplate(w, tmplName, data)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- }
- }
- func renderMessagePage(w http.ResponseWriter, r *http.Request, title, body string, statusCode int, err error, message string) {
- var errorString string
- if body != "" {
- errorString = body + " "
- }
- if err != nil {
- errorString += err.Error()
- }
- data := messagePage{
- basePage: getBasePageData(title, "", r),
- Error: errorString,
- Success: message,
- }
- w.WriteHeader(statusCode)
- renderAdminTemplate(w, templateMessage, data)
- }
- func renderInternalServerErrorPage(w http.ResponseWriter, r *http.Request, err error) {
- renderMessagePage(w, r, page500Title, page500Body, http.StatusInternalServerError, err, "")
- }
- func renderBadRequestPage(w http.ResponseWriter, r *http.Request, err error) {
- renderMessagePage(w, r, page400Title, "", http.StatusBadRequest, err, "")
- }
- func renderForbiddenPage(w http.ResponseWriter, r *http.Request, body string) {
- renderMessagePage(w, r, page403Title, "", http.StatusForbidden, nil, body)
- }
- func renderNotFoundPage(w http.ResponseWriter, r *http.Request, err error) {
- renderMessagePage(w, r, page404Title, page404Body, http.StatusNotFound, err, "")
- }
- func renderForgotPwdPage(w http.ResponseWriter, error string) {
- data := forgotPwdPage{
- CurrentURL: webAdminForgotPwdPath,
- Error: error,
- CSRFToken: createCSRFToken(),
- StaticURL: webStaticFilesPath,
- Title: pageForgotPwdTitle,
- }
- renderAdminTemplate(w, templateForgotPassword, data)
- }
- func renderResetPwdPage(w http.ResponseWriter, error string) {
- data := resetPwdPage{
- CurrentURL: webAdminResetPwdPath,
- Error: error,
- CSRFToken: createCSRFToken(),
- StaticURL: webStaticFilesPath,
- Title: pageResetPwdTitle,
- }
- renderAdminTemplate(w, templateResetPassword, data)
- }
- func renderTwoFactorPage(w http.ResponseWriter, error string) {
- data := twoFactorPage{
- CurrentURL: webAdminTwoFactorPath,
- Version: version.Get().Version,
- Error: error,
- CSRFToken: createCSRFToken(),
- StaticURL: webStaticFilesPath,
- RecoveryURL: webAdminTwoFactorRecoveryPath,
- }
- renderAdminTemplate(w, templateTwoFactor, data)
- }
- func renderTwoFactorRecoveryPage(w http.ResponseWriter, error string) {
- data := twoFactorPage{
- CurrentURL: webAdminTwoFactorRecoveryPath,
- Version: version.Get().Version,
- Error: error,
- CSRFToken: createCSRFToken(),
- StaticURL: webStaticFilesPath,
- }
- renderAdminTemplate(w, templateTwoFactorRecovery, data)
- }
- func renderMFAPage(w http.ResponseWriter, r *http.Request) {
- data := mfaPage{
- basePage: getBasePageData(pageMFATitle, webAdminMFAPath, r),
- TOTPConfigs: mfa.GetAvailableTOTPConfigNames(),
- GenerateTOTPURL: webAdminTOTPGeneratePath,
- ValidateTOTPURL: webAdminTOTPValidatePath,
- SaveTOTPURL: webAdminTOTPSavePath,
- RecCodesURL: webAdminRecoveryCodesPath,
- }
- admin, err := dataprovider.AdminExists(data.LoggedAdmin.Username)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- data.TOTPConfig = admin.Filters.TOTPConfig
- renderAdminTemplate(w, templateMFA, data)
- }
- func renderProfilePage(w http.ResponseWriter, r *http.Request, error string) {
- data := profilePage{
- basePage: getBasePageData(pageProfileTitle, webAdminProfilePath, r),
- Error: error,
- }
- admin, err := dataprovider.AdminExists(data.LoggedAdmin.Username)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- data.AllowAPIKeyAuth = admin.Filters.AllowAPIKeyAuth
- data.Email = admin.Email
- data.Description = admin.Description
- renderAdminTemplate(w, templateProfile, data)
- }
- func renderChangePasswordPage(w http.ResponseWriter, r *http.Request, error string) {
- data := changePasswordPage{
- basePage: getBasePageData(pageChangePwdTitle, webChangeAdminPwdPath, r),
- Error: error,
- }
- renderAdminTemplate(w, templateChangePwd, data)
- }
- func renderMaintenancePage(w http.ResponseWriter, r *http.Request, error string) {
- data := maintenancePage{
- basePage: getBasePageData(pageMaintenanceTitle, webMaintenancePath, r),
- BackupPath: webBackupPath,
- RestorePath: webRestorePath,
- Error: error,
- }
- renderAdminTemplate(w, templateMaintenance, data)
- }
- func renderAdminSetupPage(w http.ResponseWriter, r *http.Request, username, error string) {
- data := setupPage{
- basePage: getBasePageData(pageSetupTitle, webAdminSetupPath, r),
- Username: username,
- Error: error,
- }
- renderAdminTemplate(w, templateSetup, data)
- }
- func renderAddUpdateAdminPage(w http.ResponseWriter, r *http.Request, admin *dataprovider.Admin,
- error string, isAdd bool) {
- currentURL := webAdminPath
- title := "Add a new admin"
- if !isAdd {
- currentURL = fmt.Sprintf("%v/%v", webAdminPath, url.PathEscape(admin.Username))
- title = "Update admin"
- }
- data := adminPage{
- basePage: getBasePageData(title, currentURL, r),
- Admin: admin,
- Error: error,
- IsAdd: isAdd,
- }
- renderAdminTemplate(w, templateAdmin, data)
- }
- func renderUserPage(w http.ResponseWriter, r *http.Request, user *dataprovider.User, mode userPageMode, error string) {
- folders, err := getWebVirtualFolders(w, r, defaultQueryLimit)
- if err != nil {
- return
- }
- user.SetEmptySecretsIfNil()
- var title, currentURL string
- switch mode {
- case userPageModeAdd:
- title = "Add a new user"
- currentURL = webUserPath
- case userPageModeUpdate:
- title = "Update user"
- currentURL = fmt.Sprintf("%v/%v", webUserPath, url.PathEscape(user.Username))
- case userPageModeTemplate:
- title = "User template"
- currentURL = webTemplateUser
- }
- if user.Password != "" && user.IsPasswordHashed() && mode == userPageModeUpdate {
- user.Password = redactedSecret
- }
- user.FsConfig.RedactedSecret = redactedSecret
- data := userPage{
- basePage: getBasePageData(title, currentURL, r),
- Mode: mode,
- Error: error,
- User: user,
- ValidPerms: dataprovider.ValidPerms,
- ValidLoginMethods: dataprovider.ValidLoginMethods,
- ValidProtocols: dataprovider.ValidProtocols,
- WebClientOptions: sdk.WebClientOptions,
- RootDirPerms: user.GetPermissionsForPath("/"),
- VirtualFolders: folders,
- CanImpersonate: os.Getuid() == 0,
- FsWrapper: fsWrapper{
- Filesystem: user.FsConfig,
- IsUserPage: true,
- HasUsersBaseDir: dataprovider.HasUsersBaseDir(),
- DirPath: user.HomeDir,
- },
- }
- renderAdminTemplate(w, templateUser, data)
- }
- func renderFolderPage(w http.ResponseWriter, r *http.Request, folder vfs.BaseVirtualFolder, mode folderPageMode, error string) {
- var title, currentURL string
- switch mode {
- case folderPageModeAdd:
- title = "Add a new folder"
- currentURL = webFolderPath
- case folderPageModeUpdate:
- title = "Update folder"
- currentURL = fmt.Sprintf("%v/%v", webFolderPath, url.PathEscape(folder.Name))
- case folderPageModeTemplate:
- title = "Folder template"
- currentURL = webTemplateFolder
- }
- folder.FsConfig.RedactedSecret = redactedSecret
- folder.FsConfig.SetEmptySecretsIfNil()
- data := folderPage{
- basePage: getBasePageData(title, currentURL, r),
- Error: error,
- Folder: folder,
- Mode: mode,
- FsWrapper: fsWrapper{
- Filesystem: folder.FsConfig,
- IsUserPage: false,
- HasUsersBaseDir: false,
- DirPath: folder.MappedPath,
- },
- }
- renderAdminTemplate(w, templateFolder, data)
- }
- func getFoldersForTemplate(r *http.Request) []string {
- var res []string
- folderNames := r.Form["tpl_foldername"]
- folders := make(map[string]bool)
- for _, name := range folderNames {
- name = strings.TrimSpace(name)
- if name == "" {
- continue
- }
- if _, ok := folders[name]; ok {
- continue
- }
- folders[name] = true
- res = append(res, name)
- }
- return res
- }
- func getUsersForTemplate(r *http.Request) []userTemplateFields {
- var res []userTemplateFields
- tplUsernames := r.Form["tpl_username"]
- tplPasswords := r.Form["tpl_password"]
- tplPublicKeys := r.Form["tpl_public_keys"]
- users := make(map[string]bool)
- for idx, username := range tplUsernames {
- username = strings.TrimSpace(username)
- password := ""
- publicKey := ""
- if len(tplPasswords) > idx {
- password = strings.TrimSpace(tplPasswords[idx])
- }
- if len(tplPublicKeys) > idx {
- publicKey = strings.TrimSpace(tplPublicKeys[idx])
- }
- if username == "" || (password == "" && publicKey == "") {
- continue
- }
- if _, ok := users[username]; ok {
- continue
- }
- users[username] = true
- res = append(res, userTemplateFields{
- Username: username,
- Password: password,
- PublicKey: publicKey,
- })
- }
- return res
- }
- func getVirtualFoldersFromPostFields(r *http.Request) []vfs.VirtualFolder {
- var virtualFolders []vfs.VirtualFolder
- folderPaths := r.Form["vfolder_path"]
- folderNames := r.Form["vfolder_name"]
- folderQuotaSizes := r.Form["vfolder_quota_size"]
- folderQuotaFiles := r.Form["vfolder_quota_files"]
- for idx, p := range folderPaths {
- p = strings.TrimSpace(p)
- name := ""
- if len(folderNames) > idx {
- name = folderNames[idx]
- }
- if p != "" && name != "" {
- vfolder := vfs.VirtualFolder{
- BaseVirtualFolder: vfs.BaseVirtualFolder{
- Name: name,
- },
- VirtualPath: p,
- QuotaFiles: -1,
- QuotaSize: -1,
- }
- if len(folderQuotaSizes) > idx {
- quotaSize, err := strconv.ParseInt(strings.TrimSpace(folderQuotaSizes[idx]), 10, 64)
- if err == nil {
- vfolder.QuotaSize = quotaSize
- }
- }
- if len(folderQuotaFiles) > idx {
- quotaFiles, err := strconv.Atoi(strings.TrimSpace(folderQuotaFiles[idx]))
- if err == nil {
- vfolder.QuotaFiles = quotaFiles
- }
- }
- virtualFolders = append(virtualFolders, vfolder)
- }
- }
- return virtualFolders
- }
- func getUserPermissionsFromPostFields(r *http.Request) map[string][]string {
- permissions := make(map[string][]string)
- permissions["/"] = r.Form["permissions"]
- for k := range r.Form {
- if strings.HasPrefix(k, "sub_perm_path") {
- p := strings.TrimSpace(r.Form.Get(k))
- if p != "" {
- idx := strings.TrimPrefix(k, "sub_perm_path")
- permissions[p] = r.Form[fmt.Sprintf("sub_perm_permissions%v", idx)]
- }
- }
- }
- return permissions
- }
- func getBandwidthLimitsFromPostFields(r *http.Request) ([]sdk.BandwidthLimit, error) {
- var result []sdk.BandwidthLimit
- for k := range r.Form {
- if strings.HasPrefix(k, "bandwidth_limit_sources") {
- sources := getSliceFromDelimitedValues(r.Form.Get(k), ",")
- if len(sources) > 0 {
- bwLimit := sdk.BandwidthLimit{
- Sources: sources,
- }
- idx := strings.TrimPrefix(k, "bandwidth_limit_sources")
- ul := r.Form.Get(fmt.Sprintf("upload_bandwidth_source%v", idx))
- dl := r.Form.Get(fmt.Sprintf("download_bandwidth_source%v", idx))
- if ul != "" {
- bandwidthUL, err := strconv.ParseInt(ul, 10, 64)
- if err != nil {
- return result, fmt.Errorf("invalid upload_bandwidth_source%v %#v: %w", idx, ul, err)
- }
- bwLimit.UploadBandwidth = bandwidthUL
- }
- if dl != "" {
- bandwidthDL, err := strconv.ParseInt(dl, 10, 64)
- if err != nil {
- return result, fmt.Errorf("invalid download_bandwidth_source%v %#v: %w", idx, ul, err)
- }
- bwLimit.DownloadBandwidth = bandwidthDL
- }
- result = append(result, bwLimit)
- }
- }
- }
- return result, nil
- }
- func getPatterDenyPolicyFromString(policy string) int {
- denyPolicy := sdk.DenyPolicyDefault
- if policy == "1" {
- denyPolicy = sdk.DenyPolicyHide
- }
- return denyPolicy
- }
- func getFilePatternsFromPostField(r *http.Request) []sdk.PatternsFilter {
- var result []sdk.PatternsFilter
- allowedPatterns := make(map[string][]string)
- deniedPatterns := make(map[string][]string)
- patternPolicies := make(map[string]string)
- for k := range r.Form {
- if strings.HasPrefix(k, "pattern_path") {
- p := strings.TrimSpace(r.Form.Get(k))
- idx := strings.TrimPrefix(k, "pattern_path")
- filters := strings.TrimSpace(r.Form.Get(fmt.Sprintf("patterns%v", idx)))
- filters = strings.ReplaceAll(filters, " ", "")
- patternType := r.Form.Get(fmt.Sprintf("pattern_type%v", idx))
- patternPolicy := r.Form.Get(fmt.Sprintf("pattern_policy%v", idx))
- if p != "" && filters != "" {
- if patternType == "allowed" {
- allowedPatterns[p] = append(allowedPatterns[p], strings.Split(filters, ",")...)
- } else {
- deniedPatterns[p] = append(deniedPatterns[p], strings.Split(filters, ",")...)
- }
- if patternPolicy != "" && patternPolicy != "0" {
- patternPolicies[p] = patternPolicy
- }
- }
- }
- }
- for dirAllowed, allowPatterns := range allowedPatterns {
- filter := sdk.PatternsFilter{
- Path: dirAllowed,
- AllowedPatterns: allowPatterns,
- DenyPolicy: getPatterDenyPolicyFromString(patternPolicies[dirAllowed]),
- }
- for dirDenied, denPatterns := range deniedPatterns {
- if dirAllowed == dirDenied {
- filter.DeniedPatterns = denPatterns
- break
- }
- }
- result = append(result, filter)
- }
- for dirDenied, denPatterns := range deniedPatterns {
- found := false
- for _, res := range result {
- if res.Path == dirDenied {
- found = true
- break
- }
- }
- if !found {
- result = append(result, sdk.PatternsFilter{
- Path: dirDenied,
- DeniedPatterns: denPatterns,
- DenyPolicy: getPatterDenyPolicyFromString(patternPolicies[dirDenied]),
- })
- }
- }
- return result
- }
- func getFiltersFromUserPostFields(r *http.Request) (sdk.BaseUserFilters, error) {
- var filters sdk.BaseUserFilters
- bwLimits, err := getBandwidthLimitsFromPostFields(r)
- if err != nil {
- return filters, err
- }
- filters.BandwidthLimits = bwLimits
- filters.AllowedIP = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
- filters.DeniedIP = getSliceFromDelimitedValues(r.Form.Get("denied_ip"), ",")
- filters.DeniedLoginMethods = r.Form["ssh_login_methods"]
- filters.DeniedProtocols = r.Form["denied_protocols"]
- filters.FilePatterns = getFilePatternsFromPostField(r)
- filters.TLSUsername = sdk.TLSUsername(r.Form.Get("tls_username"))
- filters.WebClient = r.Form["web_client_options"]
- hooks := r.Form["hooks"]
- if util.IsStringInSlice("external_auth_disabled", hooks) {
- filters.Hooks.ExternalAuthDisabled = true
- }
- if util.IsStringInSlice("pre_login_disabled", hooks) {
- filters.Hooks.PreLoginDisabled = true
- }
- if util.IsStringInSlice("check_password_disabled", hooks) {
- filters.Hooks.CheckPasswordDisabled = true
- }
- filters.DisableFsChecks = len(r.Form.Get("disable_fs_checks")) > 0
- filters.AllowAPIKeyAuth = len(r.Form.Get("allow_api_key_auth")) > 0
- return filters, nil
- }
- func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
- secret := kms.NewPlainSecret(r.Form.Get(field))
- if strings.TrimSpace(secret.GetPayload()) == redactedSecret {
- secret.SetStatus(sdkkms.SecretStatusRedacted)
- }
- if strings.TrimSpace(secret.GetPayload()) == "" {
- secret.SetStatus("")
- }
- return secret
- }
- func getS3Config(r *http.Request) (vfs.S3FsConfig, error) {
- var err error
- config := vfs.S3FsConfig{}
- config.Bucket = r.Form.Get("s3_bucket")
- config.Region = r.Form.Get("s3_region")
- config.AccessKey = r.Form.Get("s3_access_key")
- config.AccessSecret = getSecretFromFormField(r, "s3_access_secret")
- config.Endpoint = r.Form.Get("s3_endpoint")
- config.StorageClass = r.Form.Get("s3_storage_class")
- config.ACL = r.Form.Get("s3_acl")
- config.KeyPrefix = r.Form.Get("s3_key_prefix")
- config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("s3_upload_part_size"), 10, 64)
- if err != nil {
- return config, err
- }
- config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("s3_upload_concurrency"))
- if err != nil {
- return config, err
- }
- config.DownloadPartSize, err = strconv.ParseInt(r.Form.Get("s3_download_part_size"), 10, 64)
- if err != nil {
- return config, err
- }
- config.DownloadConcurrency, err = strconv.Atoi(r.Form.Get("s3_download_concurrency"))
- if err != nil {
- return config, err
- }
- config.ForcePathStyle = r.Form.Get("s3_force_path_style") != ""
- config.DownloadPartMaxTime, err = strconv.Atoi(r.Form.Get("s3_download_part_max_time"))
- return config, err
- }
- func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
- var err error
- config := vfs.GCSFsConfig{}
- config.Bucket = r.Form.Get("gcs_bucket")
- config.StorageClass = r.Form.Get("gcs_storage_class")
- config.ACL = r.Form.Get("gcs_acl")
- config.KeyPrefix = r.Form.Get("gcs_key_prefix")
- autoCredentials := r.Form.Get("gcs_auto_credentials")
- if autoCredentials != "" {
- config.AutomaticCredentials = 1
- } else {
- config.AutomaticCredentials = 0
- }
- credentials, _, err := r.FormFile("gcs_credential_file")
- if err == http.ErrMissingFile {
- return config, nil
- }
- if err != nil {
- return config, err
- }
- defer credentials.Close()
- fileBytes, err := io.ReadAll(credentials)
- if err != nil || len(fileBytes) == 0 {
- if len(fileBytes) == 0 {
- err = errors.New("credentials file size must be greater than 0")
- }
- return config, err
- }
- config.Credentials = kms.NewPlainSecret(string(fileBytes))
- config.AutomaticCredentials = 0
- return config, err
- }
- func getSFTPConfig(r *http.Request) (vfs.SFTPFsConfig, error) {
- var err error
- config := vfs.SFTPFsConfig{}
- config.Endpoint = r.Form.Get("sftp_endpoint")
- config.Username = r.Form.Get("sftp_username")
- config.Password = getSecretFromFormField(r, "sftp_password")
- config.PrivateKey = getSecretFromFormField(r, "sftp_private_key")
- fingerprintsFormValue := r.Form.Get("sftp_fingerprints")
- config.Fingerprints = getSliceFromDelimitedValues(fingerprintsFormValue, "\n")
- config.Prefix = r.Form.Get("sftp_prefix")
- config.DisableCouncurrentReads = len(r.Form.Get("sftp_disable_concurrent_reads")) > 0
- config.BufferSize, err = strconv.ParseInt(r.Form.Get("sftp_buffer_size"), 10, 64)
- return config, err
- }
- func getAzureConfig(r *http.Request) (vfs.AzBlobFsConfig, error) {
- var err error
- config := vfs.AzBlobFsConfig{}
- config.Container = r.Form.Get("az_container")
- config.AccountName = r.Form.Get("az_account_name")
- config.AccountKey = getSecretFromFormField(r, "az_account_key")
- config.SASURL = getSecretFromFormField(r, "az_sas_url")
- config.Endpoint = r.Form.Get("az_endpoint")
- config.KeyPrefix = r.Form.Get("az_key_prefix")
- config.AccessTier = r.Form.Get("az_access_tier")
- config.UseEmulator = len(r.Form.Get("az_use_emulator")) > 0
- config.UploadPartSize, err = strconv.ParseInt(r.Form.Get("az_upload_part_size"), 10, 64)
- if err != nil {
- return config, err
- }
- config.UploadConcurrency, err = strconv.Atoi(r.Form.Get("az_upload_concurrency"))
- return config, err
- }
- func getFsConfigFromPostFields(r *http.Request) (vfs.Filesystem, error) {
- var fs vfs.Filesystem
- fs.Provider = sdk.GetProviderByName(r.Form.Get("fs_provider"))
- switch fs.Provider {
- case sdk.S3FilesystemProvider:
- config, err := getS3Config(r)
- if err != nil {
- return fs, err
- }
- fs.S3Config = config
- case sdk.AzureBlobFilesystemProvider:
- config, err := getAzureConfig(r)
- if err != nil {
- return fs, err
- }
- fs.AzBlobConfig = config
- case sdk.GCSFilesystemProvider:
- config, err := getGCSConfig(r)
- if err != nil {
- return fs, err
- }
- fs.GCSConfig = config
- case sdk.CryptedFilesystemProvider:
- fs.CryptConfig.Passphrase = getSecretFromFormField(r, "crypt_passphrase")
- case sdk.SFTPFilesystemProvider:
- config, err := getSFTPConfig(r)
- if err != nil {
- return fs, err
- }
- fs.SFTPConfig = config
- }
- return fs, nil
- }
- func getAdminFromPostFields(r *http.Request) (dataprovider.Admin, error) {
- var admin dataprovider.Admin
- err := r.ParseForm()
- if err != nil {
- return admin, err
- }
- status, err := strconv.Atoi(r.Form.Get("status"))
- if err != nil {
- return admin, err
- }
- admin.Username = r.Form.Get("username")
- admin.Password = r.Form.Get("password")
- admin.Permissions = r.Form["permissions"]
- admin.Email = r.Form.Get("email")
- admin.Status = status
- admin.Filters.AllowList = getSliceFromDelimitedValues(r.Form.Get("allowed_ip"), ",")
- admin.Filters.AllowAPIKeyAuth = len(r.Form.Get("allow_api_key_auth")) > 0
- admin.AdditionalInfo = r.Form.Get("additional_info")
- admin.Description = r.Form.Get("description")
- return admin, nil
- }
- func replacePlaceholders(field string, replacements map[string]string) string {
- for k, v := range replacements {
- field = strings.ReplaceAll(field, k, v)
- }
- return field
- }
- func getFolderFromTemplate(folder vfs.BaseVirtualFolder, name string) vfs.BaseVirtualFolder {
- folder.Name = name
- replacements := make(map[string]string)
- replacements["%name%"] = folder.Name
- folder.MappedPath = replacePlaceholders(folder.MappedPath, replacements)
- folder.Description = replacePlaceholders(folder.Description, replacements)
- switch folder.FsConfig.Provider {
- case sdk.CryptedFilesystemProvider:
- folder.FsConfig.CryptConfig = getCryptFsFromTemplate(folder.FsConfig.CryptConfig, replacements)
- case sdk.S3FilesystemProvider:
- folder.FsConfig.S3Config = getS3FsFromTemplate(folder.FsConfig.S3Config, replacements)
- case sdk.GCSFilesystemProvider:
- folder.FsConfig.GCSConfig = getGCSFsFromTemplate(folder.FsConfig.GCSConfig, replacements)
- case sdk.AzureBlobFilesystemProvider:
- folder.FsConfig.AzBlobConfig = getAzBlobFsFromTemplate(folder.FsConfig.AzBlobConfig, replacements)
- case sdk.SFTPFilesystemProvider:
- folder.FsConfig.SFTPConfig = getSFTPFsFromTemplate(folder.FsConfig.SFTPConfig, replacements)
- }
- return folder
- }
- func getCryptFsFromTemplate(fsConfig vfs.CryptFsConfig, replacements map[string]string) vfs.CryptFsConfig {
- if fsConfig.Passphrase != nil {
- if fsConfig.Passphrase.IsPlain() {
- payload := replacePlaceholders(fsConfig.Passphrase.GetPayload(), replacements)
- fsConfig.Passphrase = kms.NewPlainSecret(payload)
- }
- }
- return fsConfig
- }
- func getS3FsFromTemplate(fsConfig vfs.S3FsConfig, replacements map[string]string) vfs.S3FsConfig {
- fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
- fsConfig.AccessKey = replacePlaceholders(fsConfig.AccessKey, replacements)
- if fsConfig.AccessSecret != nil && fsConfig.AccessSecret.IsPlain() {
- payload := replacePlaceholders(fsConfig.AccessSecret.GetPayload(), replacements)
- fsConfig.AccessSecret = kms.NewPlainSecret(payload)
- }
- return fsConfig
- }
- func getGCSFsFromTemplate(fsConfig vfs.GCSFsConfig, replacements map[string]string) vfs.GCSFsConfig {
- fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
- return fsConfig
- }
- func getAzBlobFsFromTemplate(fsConfig vfs.AzBlobFsConfig, replacements map[string]string) vfs.AzBlobFsConfig {
- fsConfig.KeyPrefix = replacePlaceholders(fsConfig.KeyPrefix, replacements)
- fsConfig.AccountName = replacePlaceholders(fsConfig.AccountName, replacements)
- if fsConfig.AccountKey != nil && fsConfig.AccountKey.IsPlain() {
- payload := replacePlaceholders(fsConfig.AccountKey.GetPayload(), replacements)
- fsConfig.AccountKey = kms.NewPlainSecret(payload)
- }
- return fsConfig
- }
- func getSFTPFsFromTemplate(fsConfig vfs.SFTPFsConfig, replacements map[string]string) vfs.SFTPFsConfig {
- fsConfig.Prefix = replacePlaceholders(fsConfig.Prefix, replacements)
- fsConfig.Username = replacePlaceholders(fsConfig.Username, replacements)
- if fsConfig.Password != nil && fsConfig.Password.IsPlain() {
- payload := replacePlaceholders(fsConfig.Password.GetPayload(), replacements)
- fsConfig.Password = kms.NewPlainSecret(payload)
- }
- return fsConfig
- }
- func getUserFromTemplate(user dataprovider.User, template userTemplateFields) dataprovider.User {
- user.Username = template.Username
- user.Password = template.Password
- user.PublicKeys = nil
- if template.PublicKey != "" {
- user.PublicKeys = append(user.PublicKeys, template.PublicKey)
- }
- replacements := make(map[string]string)
- replacements["%username%"] = user.Username
- user.Password = replacePlaceholders(user.Password, replacements)
- replacements["%password%"] = user.Password
- user.HomeDir = replacePlaceholders(user.HomeDir, replacements)
- var vfolders []vfs.VirtualFolder
- for _, vfolder := range user.VirtualFolders {
- vfolder.Name = replacePlaceholders(vfolder.Name, replacements)
- vfolder.VirtualPath = replacePlaceholders(vfolder.VirtualPath, replacements)
- vfolders = append(vfolders, vfolder)
- }
- user.VirtualFolders = vfolders
- user.Description = replacePlaceholders(user.Description, replacements)
- user.AdditionalInfo = replacePlaceholders(user.AdditionalInfo, replacements)
- switch user.FsConfig.Provider {
- case sdk.CryptedFilesystemProvider:
- user.FsConfig.CryptConfig = getCryptFsFromTemplate(user.FsConfig.CryptConfig, replacements)
- case sdk.S3FilesystemProvider:
- user.FsConfig.S3Config = getS3FsFromTemplate(user.FsConfig.S3Config, replacements)
- case sdk.GCSFilesystemProvider:
- user.FsConfig.GCSConfig = getGCSFsFromTemplate(user.FsConfig.GCSConfig, replacements)
- case sdk.AzureBlobFilesystemProvider:
- user.FsConfig.AzBlobConfig = getAzBlobFsFromTemplate(user.FsConfig.AzBlobConfig, replacements)
- case sdk.SFTPFilesystemProvider:
- user.FsConfig.SFTPConfig = getSFTPFsFromTemplate(user.FsConfig.SFTPConfig, replacements)
- }
- return user
- }
- func getUserFromPostFields(r *http.Request) (dataprovider.User, error) {
- var user dataprovider.User
- err := r.ParseMultipartForm(maxRequestSize)
- if err != nil {
- return user, err
- }
- defer r.MultipartForm.RemoveAll() //nolint:errcheck
- uid, err := strconv.Atoi(r.Form.Get("uid"))
- if err != nil {
- return user, err
- }
- gid, err := strconv.Atoi(r.Form.Get("gid"))
- if err != nil {
- return user, err
- }
- maxSessions, err := strconv.Atoi(r.Form.Get("max_sessions"))
- if err != nil {
- return user, err
- }
- quotaSize, err := strconv.ParseInt(r.Form.Get("quota_size"), 10, 64)
- if err != nil {
- return user, err
- }
- quotaFiles, err := strconv.Atoi(r.Form.Get("quota_files"))
- if err != nil {
- return user, err
- }
- bandwidthUL, err := strconv.ParseInt(r.Form.Get("upload_bandwidth"), 10, 64)
- if err != nil {
- return user, err
- }
- bandwidthDL, err := strconv.ParseInt(r.Form.Get("download_bandwidth"), 10, 64)
- if err != nil {
- return user, err
- }
- status, err := strconv.Atoi(r.Form.Get("status"))
- if err != nil {
- return user, err
- }
- expirationDateMillis := int64(0)
- expirationDateString := r.Form.Get("expiration_date")
- if strings.TrimSpace(expirationDateString) != "" {
- expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
- if err != nil {
- return user, err
- }
- expirationDateMillis = util.GetTimeAsMsSinceEpoch(expirationDate)
- }
- fsConfig, err := getFsConfigFromPostFields(r)
- if err != nil {
- return user, err
- }
- filters, err := getFiltersFromUserPostFields(r)
- if err != nil {
- return user, err
- }
- user = dataprovider.User{
- BaseUser: sdk.BaseUser{
- Username: r.Form.Get("username"),
- Email: r.Form.Get("email"),
- Password: r.Form.Get("password"),
- PublicKeys: r.Form["public_keys"],
- HomeDir: r.Form.Get("home_dir"),
- UID: uid,
- GID: gid,
- Permissions: getUserPermissionsFromPostFields(r),
- MaxSessions: maxSessions,
- QuotaSize: quotaSize,
- QuotaFiles: quotaFiles,
- UploadBandwidth: bandwidthUL,
- DownloadBandwidth: bandwidthDL,
- Status: status,
- ExpirationDate: expirationDateMillis,
- AdditionalInfo: r.Form.Get("additional_info"),
- Description: r.Form.Get("description"),
- },
- Filters: dataprovider.UserFilters{
- BaseUserFilters: filters,
- },
- VirtualFolders: getVirtualFoldersFromPostFields(r),
- FsConfig: fsConfig,
- }
- maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
- user.Filters.MaxUploadFileSize = maxFileSize
- return user, err
- }
- func handleWebAdminForgotPwd(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- if !smtp.IsEnabled() {
- renderNotFoundPage(w, r, errors.New("this page does not exist"))
- return
- }
- renderForgotPwdPage(w, "")
- }
- func handleWebAdminForgotPwdPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- err := r.ParseForm()
- if err != nil {
- renderForgotPwdPage(w, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- username := r.Form.Get("username")
- err = handleForgotPassword(r, username, true)
- if err != nil {
- if e, ok := err.(*util.ValidationError); ok {
- renderForgotPwdPage(w, e.GetErrorString())
- return
- }
- renderForgotPwdPage(w, err.Error())
- return
- }
- http.Redirect(w, r, webAdminResetPwdPath, http.StatusFound)
- }
- func handleWebAdminPasswordReset(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
- if !smtp.IsEnabled() {
- renderNotFoundPage(w, r, errors.New("this page does not exist"))
- return
- }
- renderResetPwdPage(w, "")
- }
- func handleWebAdminTwoFactor(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderTwoFactorPage(w, "")
- }
- func handleWebAdminTwoFactorRecovery(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderTwoFactorRecoveryPage(w, "")
- }
- func handleWebAdminMFA(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderMFAPage(w, r)
- }
- func handleWebAdminProfile(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderProfilePage(w, r, "")
- }
- func handleWebAdminChangePwd(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderChangePasswordPage(w, r, "")
- }
- func handleWebAdminChangePwdPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- err := r.ParseForm()
- if err != nil {
- renderChangePasswordPage(w, r, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- err = doChangeAdminPassword(r, r.Form.Get("current_password"), r.Form.Get("new_password1"),
- r.Form.Get("new_password2"))
- if err != nil {
- renderChangePasswordPage(w, r, err.Error())
- return
- }
- handleWebLogout(w, r)
- }
- func handleWebAdminProfilePost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- err := r.ParseForm()
- if err != nil {
- renderProfilePage(w, r, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderProfilePage(w, r, "Invalid token claims")
- return
- }
- admin, err := dataprovider.AdminExists(claims.Username)
- if err != nil {
- renderProfilePage(w, r, err.Error())
- return
- }
- admin.Filters.AllowAPIKeyAuth = len(r.Form.Get("allow_api_key_auth")) > 0
- admin.Email = r.Form.Get("email")
- admin.Description = r.Form.Get("description")
- err = dataprovider.UpdateAdmin(&admin, dataprovider.ActionExecutorSelf, util.GetIPFromRemoteAddress(r.RemoteAddr))
- if err != nil {
- renderProfilePage(w, r, err.Error())
- return
- }
- renderMessagePage(w, r, "Profile updated", "", http.StatusOK, nil,
- "Your profile has been successfully updated")
- }
- func handleWebLogout(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- c := jwtTokenClaims{}
- c.removeCookie(w, r, webBaseAdminPath)
- http.Redirect(w, r, webLoginPath, http.StatusFound)
- }
- func handleWebMaintenance(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderMaintenancePage(w, r, "")
- }
- func handleWebRestore(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, MaxRestoreSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- err = r.ParseMultipartForm(MaxRestoreSize)
- if err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- defer r.MultipartForm.RemoveAll() //nolint:errcheck
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- restoreMode, err := strconv.Atoi(r.Form.Get("mode"))
- if err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- scanQuota, err := strconv.Atoi(r.Form.Get("quota"))
- if err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- backupFile, _, err := r.FormFile("backup_file")
- if err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- defer backupFile.Close()
- backupContent, err := io.ReadAll(backupFile)
- if err != nil || len(backupContent) == 0 {
- if len(backupContent) == 0 {
- err = errors.New("backup file size must be greater than 0")
- }
- renderMaintenancePage(w, r, err.Error())
- return
- }
- if err := restoreBackup(backupContent, "", scanQuota, restoreMode, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- renderMessagePage(w, r, "Data restored", "", http.StatusOK, nil, "Your backup was successfully restored")
- }
- func handleGetWebAdmins(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- limit := defaultQueryLimit
- if _, ok := r.URL.Query()["qlimit"]; ok {
- var err error
- limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
- if err != nil {
- limit = defaultQueryLimit
- }
- }
- admins := make([]dataprovider.Admin, 0, limit)
- for {
- a, err := dataprovider.GetAdmins(limit, len(admins), dataprovider.OrderASC)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- admins = append(admins, a...)
- if len(a) < limit {
- break
- }
- }
- data := adminsPage{
- basePage: getBasePageData(pageAdminsTitle, webAdminsPath, r),
- Admins: admins,
- }
- renderAdminTemplate(w, templateAdmins, data)
- }
- func handleWebAdminSetupGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
- if dataprovider.HasAdmin() {
- http.Redirect(w, r, webLoginPath, http.StatusFound)
- return
- }
- renderAdminSetupPage(w, r, "", "")
- }
- func handleWebAddAdminGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- admin := &dataprovider.Admin{Status: 1}
- renderAddUpdateAdminPage(w, r, admin, "", true)
- }
- func handleWebUpdateAdminGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- username := getURLParam(r, "username")
- admin, err := dataprovider.AdminExists(username)
- if err == nil {
- renderAddUpdateAdminPage(w, r, &admin, "", false)
- } else if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- }
- func handleWebAddAdminPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- admin, err := getAdminFromPostFields(r)
- if err != nil {
- renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- err = dataprovider.AddAdmin(&admin, claims.Username, util.GetIPFromRemoteAddress(r.Method))
- if err != nil {
- renderAddUpdateAdminPage(w, r, &admin, err.Error(), true)
- return
- }
- http.Redirect(w, r, webAdminsPath, http.StatusSeeOther)
- }
- func handleWebUpdateAdminPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- username := getURLParam(r, "username")
- admin, err := dataprovider.AdminExists(username)
- if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- return
- } else if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- updatedAdmin, err := getAdminFromPostFields(r)
- if err != nil {
- renderAddUpdateAdminPage(w, r, &updatedAdmin, err.Error(), false)
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- updatedAdmin.ID = admin.ID
- updatedAdmin.Username = admin.Username
- if updatedAdmin.Password == "" {
- updatedAdmin.Password = admin.Password
- }
- updatedAdmin.Filters.TOTPConfig = admin.Filters.TOTPConfig
- updatedAdmin.Filters.RecoveryCodes = admin.Filters.RecoveryCodes
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderAddUpdateAdminPage(w, r, &updatedAdmin, "Invalid token claims", false)
- return
- }
- if username == claims.Username {
- if claims.isCriticalPermRemoved(updatedAdmin.Permissions) {
- renderAddUpdateAdminPage(w, r, &updatedAdmin, "You cannot remove these permissions to yourself", false)
- return
- }
- if updatedAdmin.Status == 0 {
- renderAddUpdateAdminPage(w, r, &updatedAdmin, "You cannot disable yourself", false)
- return
- }
- }
- err = dataprovider.UpdateAdmin(&updatedAdmin, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr))
- if err != nil {
- renderAddUpdateAdminPage(w, r, &admin, err.Error(), false)
- return
- }
- http.Redirect(w, r, webAdminsPath, http.StatusSeeOther)
- }
- func handleWebDefenderPage(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- data := defenderHostsPage{
- basePage: getBasePageData(pageDefenderTitle, webDefenderPath, r),
- DefenderHostsURL: webDefenderHostsPath,
- }
- renderAdminTemplate(w, templateDefender, data)
- }
- func handleGetWebUsers(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- limit := defaultQueryLimit
- if _, ok := r.URL.Query()["qlimit"]; ok {
- var err error
- limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
- if err != nil {
- limit = defaultQueryLimit
- }
- }
- users := make([]dataprovider.User, 0, limit)
- for {
- u, err := dataprovider.GetUsers(limit, len(users), dataprovider.OrderASC)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- users = append(users, u...)
- if len(u) < limit {
- break
- }
- }
- data := usersPage{
- basePage: getBasePageData(pageUsersTitle, webUsersPath, r),
- Users: users,
- }
- renderAdminTemplate(w, templateUsers, data)
- }
- func handleWebTemplateFolderGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- if r.URL.Query().Get("from") != "" {
- name := r.URL.Query().Get("from")
- folder, err := dataprovider.GetFolderByName(name)
- if err == nil {
- folder.FsConfig.SetEmptySecrets()
- renderFolderPage(w, r, folder, folderPageModeTemplate, "")
- } else if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- } else {
- folder := vfs.BaseVirtualFolder{}
- renderFolderPage(w, r, folder, folderPageModeTemplate, "")
- }
- }
- func handleWebTemplateFolderPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- templateFolder := vfs.BaseVirtualFolder{}
- err = r.ParseMultipartForm(maxRequestSize)
- if err != nil {
- renderMessagePage(w, r, "Error parsing folders fields", "", http.StatusBadRequest, err, "")
- return
- }
- defer r.MultipartForm.RemoveAll() //nolint:errcheck
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- templateFolder.MappedPath = r.Form.Get("mapped_path")
- templateFolder.Description = r.Form.Get("description")
- fsConfig, err := getFsConfigFromPostFields(r)
- if err != nil {
- renderMessagePage(w, r, "Error parsing folders fields", "", http.StatusBadRequest, err, "")
- return
- }
- templateFolder.FsConfig = fsConfig
- var dump dataprovider.BackupData
- dump.Version = dataprovider.DumpVersion
- foldersFields := getFoldersForTemplate(r)
- for _, tmpl := range foldersFields {
- f := getFolderFromTemplate(templateFolder, tmpl)
- if err := dataprovider.ValidateFolder(&f); err != nil {
- renderMessagePage(w, r, "Folder validation error", fmt.Sprintf("Error validating folder %#v", f.Name),
- http.StatusBadRequest, err, "")
- return
- }
- dump.Folders = append(dump.Folders, f)
- }
- if len(dump.Folders) == 0 {
- renderMessagePage(w, r, "No folders defined", "No valid folders defined, unable to complete the requested action",
- http.StatusBadRequest, nil, "")
- return
- }
- if r.Form.Get("form_action") == "export_from_template" {
- w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-folders-from-template.json\"",
- len(dump.Folders)))
- render.JSON(w, r, dump)
- return
- }
- if err = RestoreFolders(dump.Folders, "", 1, 0, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
- renderMessagePage(w, r, "Unable to save folders", "Cannot save the defined folders:",
- getRespStatus(err), err, "")
- return
- }
- http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
- }
- func handleWebTemplateUserGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- if r.URL.Query().Get("from") != "" {
- username := r.URL.Query().Get("from")
- user, err := dataprovider.UserExists(username)
- if err == nil {
- user.SetEmptySecrets()
- user.PublicKeys = nil
- user.Email = ""
- user.Description = ""
- renderUserPage(w, r, &user, userPageModeTemplate, "")
- } else if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- } else {
- user := dataprovider.User{BaseUser: sdk.BaseUser{
- Status: 1,
- Permissions: map[string][]string{
- "/": {dataprovider.PermAny},
- },
- }}
- renderUserPage(w, r, &user, userPageModeTemplate, "")
- }
- }
- func handleWebTemplateUserPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- templateUser, err := getUserFromPostFields(r)
- if err != nil {
- renderMessagePage(w, r, "Error parsing user fields", "", http.StatusBadRequest, err, "")
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- var dump dataprovider.BackupData
- dump.Version = dataprovider.DumpVersion
- userTmplFields := getUsersForTemplate(r)
- for _, tmpl := range userTmplFields {
- u := getUserFromTemplate(templateUser, tmpl)
- if err := dataprovider.ValidateUser(&u); err != nil {
- renderMessagePage(w, r, "User validation error", fmt.Sprintf("Error validating user %#v", u.Username),
- http.StatusBadRequest, err, "")
- return
- }
- dump.Users = append(dump.Users, u)
- for _, folder := range u.VirtualFolders {
- if !dump.HasFolder(folder.Name) {
- dump.Folders = append(dump.Folders, folder.BaseVirtualFolder)
- }
- }
- }
- if len(dump.Users) == 0 {
- renderMessagePage(w, r, "No users defined", "No valid users defined, unable to complete the requested action",
- http.StatusBadRequest, nil, "")
- return
- }
- if r.Form.Get("form_action") == "export_from_template" {
- w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-users-from-template.json\"",
- len(dump.Users)))
- render.JSON(w, r, dump)
- return
- }
- if err = RestoreUsers(dump.Users, "", 1, 0, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr)); err != nil {
- renderMessagePage(w, r, "Unable to save users", "Cannot save the defined users:",
- getRespStatus(err), err, "")
- return
- }
- http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
- }
- func handleWebAddUserGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- user := dataprovider.User{BaseUser: sdk.BaseUser{
- Status: 1,
- Permissions: map[string][]string{
- "/": {dataprovider.PermAny},
- },
- }}
- renderUserPage(w, r, &user, userPageModeAdd, "")
- }
- func handleWebUpdateUserGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- username := getURLParam(r, "username")
- user, err := dataprovider.UserExists(username)
- if err == nil {
- renderUserPage(w, r, &user, userPageModeUpdate, "")
- } else if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- }
- func handleWebAddUserPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- user, err := getUserFromPostFields(r)
- if err != nil {
- renderUserPage(w, r, &user, userPageModeAdd, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- err = dataprovider.AddUser(&user, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr))
- if err == nil {
- http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
- } else {
- renderUserPage(w, r, &user, userPageModeAdd, err.Error())
- }
- }
- func handleWebUpdateUserPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- username := getURLParam(r, "username")
- user, err := dataprovider.UserExists(username)
- if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- return
- } else if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- updatedUser, err := getUserFromPostFields(r)
- if err != nil {
- renderUserPage(w, r, &user, userPageModeUpdate, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- updatedUser.ID = user.ID
- updatedUser.Username = user.Username
- updatedUser.Filters.RecoveryCodes = user.Filters.RecoveryCodes
- updatedUser.Filters.TOTPConfig = user.Filters.TOTPConfig
- updatedUser.SetEmptySecretsIfNil()
- if updatedUser.Password == redactedSecret {
- updatedUser.Password = user.Password
- }
- updateEncryptedSecrets(&updatedUser.FsConfig, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
- user.FsConfig.AzBlobConfig.SASURL, user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase,
- user.FsConfig.SFTPConfig.Password, user.FsConfig.SFTPConfig.PrivateKey)
- err = dataprovider.UpdateUser(&updatedUser, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr))
- if err == nil {
- if len(r.Form.Get("disconnect")) > 0 {
- disconnectUser(user.Username)
- }
- http.Redirect(w, r, webUsersPath, http.StatusSeeOther)
- } else {
- renderUserPage(w, r, &user, userPageModeUpdate, err.Error())
- }
- }
- func handleWebGetStatus(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- data := statusPage{
- basePage: getBasePageData(pageStatusTitle, webStatusPath, r),
- Status: getServicesStatus(),
- }
- renderAdminTemplate(w, templateStatus, data)
- }
- func handleWebGetConnections(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- connectionStats := common.Connections.GetStats()
- data := connectionsPage{
- basePage: getBasePageData(pageConnectionsTitle, webConnectionsPath, r),
- Connections: connectionStats,
- }
- renderAdminTemplate(w, templateConnections, data)
- }
- func handleWebAddFolderGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- renderFolderPage(w, r, vfs.BaseVirtualFolder{}, folderPageModeAdd, "")
- }
- func handleWebAddFolderPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- folder := vfs.BaseVirtualFolder{}
- err := r.ParseMultipartForm(maxRequestSize)
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
- return
- }
- defer r.MultipartForm.RemoveAll() //nolint:errcheck
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- folder.MappedPath = r.Form.Get("mapped_path")
- folder.Name = r.Form.Get("name")
- folder.Description = r.Form.Get("description")
- fsConfig, err := getFsConfigFromPostFields(r)
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
- return
- }
- folder.FsConfig = fsConfig
- err = dataprovider.AddFolder(&folder)
- if err == nil {
- http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
- } else {
- renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
- }
- }
- func handleWebUpdateFolderGet(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- name := getURLParam(r, "name")
- folder, err := dataprovider.GetFolderByName(name)
- if err == nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, "")
- } else if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- }
- func handleWebUpdateFolderPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderBadRequestPage(w, r, errors.New("invalid token claims"))
- return
- }
- name := getURLParam(r, "name")
- folder, err := dataprovider.GetFolderByName(name)
- if _, ok := err.(*util.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- return
- } else if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- err = r.ParseMultipartForm(maxRequestSize)
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
- return
- }
- defer r.MultipartForm.RemoveAll() //nolint:errcheck
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- fsConfig, err := getFsConfigFromPostFields(r)
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
- return
- }
- updatedFolder := &vfs.BaseVirtualFolder{
- MappedPath: r.Form.Get("mapped_path"),
- Description: r.Form.Get("description"),
- }
- updatedFolder.ID = folder.ID
- updatedFolder.Name = folder.Name
- updatedFolder.FsConfig = fsConfig
- updatedFolder.FsConfig.SetEmptySecretsIfNil()
- updateEncryptedSecrets(&updatedFolder.FsConfig, folder.FsConfig.S3Config.AccessSecret, folder.FsConfig.AzBlobConfig.AccountKey,
- folder.FsConfig.AzBlobConfig.SASURL, folder.FsConfig.GCSConfig.Credentials, folder.FsConfig.CryptConfig.Passphrase,
- folder.FsConfig.SFTPConfig.Password, folder.FsConfig.SFTPConfig.PrivateKey)
- err = dataprovider.UpdateFolder(updatedFolder, folder.Users, claims.Username, util.GetIPFromRemoteAddress(r.RemoteAddr))
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
- return
- }
- http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
- }
- func getWebVirtualFolders(w http.ResponseWriter, r *http.Request, limit int) ([]vfs.BaseVirtualFolder, error) {
- folders := make([]vfs.BaseVirtualFolder, 0, limit)
- for {
- f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return folders, err
- }
- folders = append(folders, f...)
- if len(f) < limit {
- break
- }
- }
- return folders, nil
- }
- func handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- limit := defaultQueryLimit
- if _, ok := r.URL.Query()["qlimit"]; ok {
- var err error
- limit, err = strconv.Atoi(r.URL.Query().Get("qlimit"))
- if err != nil {
- limit = defaultQueryLimit
- }
- }
- folders, err := getWebVirtualFolders(w, r, limit)
- if err != nil {
- return
- }
- data := foldersPage{
- basePage: getBasePageData(pageFoldersTitle, webFoldersPath, r),
- Folders: folders,
- }
- renderAdminTemplate(w, templateFolders, data)
- }
|