| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539 |
- package httpd
- import (
- "errors"
- "fmt"
- "html/template"
- "io/ioutil"
- "net/http"
- "net/url"
- "path"
- "path/filepath"
- "strconv"
- "strings"
- "time"
- "github.com/go-chi/render"
- "github.com/drakkan/sftpgo/common"
- "github.com/drakkan/sftpgo/dataprovider"
- "github.com/drakkan/sftpgo/kms"
- "github.com/drakkan/sftpgo/utils"
- "github.com/drakkan/sftpgo/version"
- "github.com/drakkan/sftpgo/vfs"
- )
- type userPageMode int
- const (
- userPageModeAdd userPageMode = iota + 1
- userPageModeUpdate
- userPageModeTemplate
- )
- type folderPageMode int
- const (
- folderPageModeAdd folderPageMode = iota + 1
- folderPageModeUpdate
- folderPageModeTemplate
- )
- const (
- templateBase = "base.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"
- templateChangePwd = "changepwd.html"
- templateMaintenance = "maintenance.html"
- pageUsersTitle = "Users"
- pageAdminsTitle = "Admins"
- pageConnectionsTitle = "Connections"
- pageStatusTitle = "Status"
- pageFoldersTitle = "Folders"
- pageChangePwdTitle = "Change password"
- pageMaintenanceTitle = "Maintenance"
- page400Title = "Bad request"
- page403Title = "Forbidden"
- page404Title = "Not found"
- page404Body = "The page you are looking for does not exist."
- page500Title = "Internal Server Error"
- page500Body = "The server is unable to fulfill your request."
- defaultQueryLimit = 500
- webDateTimeFormat = "2006-01-02 15:04:05" // YYYY-MM-DD HH:MM:SS
- redactedSecret = "[**redacted**]"
- csrfFormToken = "_form_token"
- csrfHeaderToken = "X-CSRF-TOKEN"
- )
- var (
- templates = 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
- LogoutURL string
- ChangeAdminPwdURL string
- FolderQuotaScanURL string
- StatusURL string
- MaintenanceURL string
- UsersTitle string
- AdminsTitle string
- ConnectionsTitle string
- FoldersTitle string
- StatusTitle string
- MaintenanceTitle string
- Version string
- CSRFToken string
- 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 userPage struct {
- basePage
- User *dataprovider.User
- RootPerms []string
- Error string
- ValidPerms []string
- ValidSSHLoginMethods []string
- ValidProtocols []string
- RootDirPerms []string
- RedactedSecret string
- Mode userPageMode
- }
- type adminPage struct {
- basePage
- Admin *dataprovider.Admin
- Error string
- IsAdd bool
- }
- type changePwdPage struct {
- basePage
- Error string
- }
- type maintenancePage struct {
- basePage
- BackupPath string
- RestorePath string
- Error string
- }
- type folderPage struct {
- basePage
- Folder vfs.BaseVirtualFolder
- Error string
- Mode folderPageMode
- }
- type messagePage struct {
- basePage
- Error string
- Success string
- }
- type loginPage struct {
- CurrentURL string
- Version string
- Error string
- CSRFToken string
- }
- type userTemplateFields struct {
- Username string
- Password string
- PublicKey string
- }
- func loadTemplates(templatesPath string) {
- usersPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateUsers),
- }
- userPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateUser),
- }
- adminsPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateAdmins),
- }
- adminPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateAdmin),
- }
- changePwdPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateChangePwd),
- }
- connectionsPaths := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateConnections),
- }
- messagePath := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateMessage),
- }
- foldersPath := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateFolders),
- }
- folderPath := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateFolder),
- }
- statusPath := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateStatus),
- }
- loginPath := []string{
- filepath.Join(templatesPath, templateLogin),
- }
- maintenancePath := []string{
- filepath.Join(templatesPath, templateBase),
- filepath.Join(templatesPath, templateMaintenance),
- }
- usersTmpl := utils.LoadTemplate(template.ParseFiles(usersPaths...))
- userTmpl := utils.LoadTemplate(template.ParseFiles(userPaths...))
- adminsTmpl := utils.LoadTemplate(template.ParseFiles(adminsPaths...))
- adminTmpl := utils.LoadTemplate(template.ParseFiles(adminPaths...))
- connectionsTmpl := utils.LoadTemplate(template.ParseFiles(connectionsPaths...))
- messageTmpl := utils.LoadTemplate(template.ParseFiles(messagePath...))
- foldersTmpl := utils.LoadTemplate(template.ParseFiles(foldersPath...))
- folderTmpl := utils.LoadTemplate(template.ParseFiles(folderPath...))
- statusTmpl := utils.LoadTemplate(template.ParseFiles(statusPath...))
- loginTmpl := utils.LoadTemplate(template.ParseFiles(loginPath...))
- changePwdTmpl := utils.LoadTemplate(template.ParseFiles(changePwdPaths...))
- maintenanceTmpl := utils.LoadTemplate(template.ParseFiles(maintenancePath...))
- templates[templateUsers] = usersTmpl
- templates[templateUser] = userTmpl
- templates[templateAdmins] = adminsTmpl
- templates[templateAdmin] = adminTmpl
- templates[templateConnections] = connectionsTmpl
- templates[templateMessage] = messageTmpl
- templates[templateFolders] = foldersTmpl
- templates[templateFolder] = folderTmpl
- templates[templateStatus] = statusTmpl
- templates[templateLogin] = loginTmpl
- templates[templateChangePwd] = changePwdTmpl
- templates[templateMaintenance] = maintenanceTmpl
- }
- 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,
- LogoutURL: webLogoutPath,
- ChangeAdminPwdURL: webChangeAdminPwdPath,
- QuotaScanURL: webQuotaScanPath,
- ConnectionsURL: webConnectionsPath,
- StatusURL: webStatusPath,
- FolderQuotaScanURL: webScanVFolderPath,
- MaintenanceURL: webMaintenancePath,
- UsersTitle: pageUsersTitle,
- AdminsTitle: pageAdminsTitle,
- ConnectionsTitle: pageConnectionsTitle,
- FoldersTitle: pageFoldersTitle,
- StatusTitle: pageStatusTitle,
- MaintenanceTitle: pageMaintenanceTitle,
- Version: version.GetAsString(),
- LoggedAdmin: getAdminFromToken(r),
- CSRFToken: csrfToken,
- }
- }
- func renderTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
- err := templates[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)
- renderTemplate(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 renderChangePwdPage(w http.ResponseWriter, r *http.Request, error string) {
- data := changePwdPage{
- basePage: getBasePageData(pageChangePwdTitle, webChangeAdminPwdPath, r),
- Error: error,
- }
- renderTemplate(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,
- }
- renderTemplate(w, templateMaintenance, data)
- }
- func renderAddUpdateAdminPage(w http.ResponseWriter, r *http.Request, admin *dataprovider.Admin,
- error string, isAdd bool) {
- currentURL := webAdminPath
- if !isAdd {
- currentURL = fmt.Sprintf("%v/%v", webAdminPath, url.PathEscape(admin.Username))
- }
- data := adminPage{
- basePage: getBasePageData("Add a new user", currentURL, r),
- Admin: admin,
- Error: error,
- IsAdd: isAdd,
- }
- renderTemplate(w, templateAdmin, data)
- }
- func renderUserPage(w http.ResponseWriter, r *http.Request, user *dataprovider.User, mode userPageMode, error string) {
- 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
- }
- data := userPage{
- basePage: getBasePageData(title, currentURL, r),
- Mode: mode,
- Error: error,
- User: user,
- ValidPerms: dataprovider.ValidPerms,
- ValidSSHLoginMethods: dataprovider.ValidSSHLoginMethods,
- ValidProtocols: dataprovider.ValidProtocols,
- RootDirPerms: user.GetPermissionsForPath("/"),
- RedactedSecret: redactedSecret,
- }
- renderTemplate(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
- }
- data := folderPage{
- basePage: getBasePageData(title, currentURL, r),
- Error: error,
- Folder: folder,
- Mode: mode,
- }
- renderTemplate(w, templateFolder, data)
- }
- func getFoldersForTemplate(r *http.Request) []string {
- var res []string
- formValue := r.Form.Get("folders")
- folders := make(map[string]bool)
- for _, name := range getSliceFromDelimitedValues(formValue, "\n") {
- if _, ok := folders[name]; ok {
- continue
- }
- folders[name] = true
- res = append(res, name)
- }
- return res
- }
- func getUsersForTemplate(r *http.Request) []userTemplateFields {
- var res []userTemplateFields
- formValue := r.Form.Get("users")
- users := make(map[string]bool)
- for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
- if strings.Contains(cleaned, "::") {
- mapping := strings.Split(cleaned, "::")
- if len(mapping) > 1 {
- username := strings.TrimSpace(mapping[0])
- password := strings.TrimSpace(mapping[1])
- var publicKey string
- if len(mapping) > 2 {
- publicKey = strings.TrimSpace(mapping[2])
- }
- 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
- formValue := r.Form.Get("virtual_folders")
- for _, cleaned := range getSliceFromDelimitedValues(formValue, "\n") {
- if strings.Contains(cleaned, "::") {
- mapping := strings.Split(cleaned, "::")
- if len(mapping) > 1 {
- vfolder := vfs.VirtualFolder{
- BaseVirtualFolder: vfs.BaseVirtualFolder{
- Name: strings.TrimSpace(mapping[1]),
- },
- VirtualPath: strings.TrimSpace(mapping[0]),
- QuotaFiles: -1,
- QuotaSize: -1,
- }
- if len(mapping) > 2 {
- quotaFiles, err := strconv.Atoi(strings.TrimSpace(mapping[2]))
- if err == nil {
- vfolder.QuotaFiles = quotaFiles
- }
- }
- if len(mapping) > 3 {
- quotaSize, err := strconv.ParseInt(strings.TrimSpace(mapping[3]), 10, 64)
- if err == nil {
- vfolder.QuotaSize = quotaSize
- }
- }
- virtualFolders = append(virtualFolders, vfolder)
- }
- }
- }
- return virtualFolders
- }
- func getUserPermissionsFromPostFields(r *http.Request) map[string][]string {
- permissions := make(map[string][]string)
- permissions["/"] = r.Form["permissions"]
- subDirsPermsValue := r.Form.Get("sub_dirs_permissions")
- for _, cleaned := range getSliceFromDelimitedValues(subDirsPermsValue, "\n") {
- if strings.Contains(cleaned, "::") {
- dirPerms := strings.Split(cleaned, "::")
- if len(dirPerms) > 1 {
- dir := dirPerms[0]
- dir = strings.TrimSpace(dir)
- perms := []string{}
- for _, p := range strings.Split(dirPerms[1], ",") {
- cleanedPerm := strings.TrimSpace(p)
- if cleanedPerm != "" {
- perms = append(perms, cleanedPerm)
- }
- }
- if dir != "" {
- permissions[dir] = perms
- }
- }
- }
- }
- return permissions
- }
- func getSliceFromDelimitedValues(values, delimiter string) []string {
- result := []string{}
- for _, v := range strings.Split(values, delimiter) {
- cleaned := strings.TrimSpace(v)
- if cleaned != "" {
- result = append(result, cleaned)
- }
- }
- return result
- }
- func getListFromPostFields(value string) map[string][]string {
- result := make(map[string][]string)
- for _, cleaned := range getSliceFromDelimitedValues(value, "\n") {
- if strings.Contains(cleaned, "::") {
- dirExts := strings.Split(cleaned, "::")
- if len(dirExts) > 1 {
- dir := dirExts[0]
- dir = path.Clean(strings.TrimSpace(dir))
- exts := []string{}
- for _, e := range strings.Split(dirExts[1], ",") {
- cleanedExt := strings.TrimSpace(e)
- if cleanedExt != "" {
- exts = append(exts, cleanedExt)
- }
- }
- if dir != "" {
- if _, ok := result[dir]; ok {
- result[dir] = append(result[dir], exts...)
- } else {
- result[dir] = exts
- }
- result[dir] = utils.RemoveDuplicates(result[dir])
- }
- }
- }
- }
- return result
- }
- func getFilePatternsFromPostField(valueAllowed, valuesDenied string) []dataprovider.PatternsFilter {
- var result []dataprovider.PatternsFilter
- allowedPatterns := getListFromPostFields(valueAllowed)
- deniedPatterns := getListFromPostFields(valuesDenied)
- for dirAllowed, allowPatterns := range allowedPatterns {
- filter := dataprovider.PatternsFilter{
- Path: dirAllowed,
- AllowedPatterns: allowPatterns,
- }
- 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, dataprovider.PatternsFilter{
- Path: dirDenied,
- DeniedPatterns: denPatterns,
- })
- }
- }
- return result
- }
- func getFileExtensionsFromPostField(valueAllowed, valuesDenied string) []dataprovider.ExtensionsFilter {
- var result []dataprovider.ExtensionsFilter
- allowedExtensions := getListFromPostFields(valueAllowed)
- deniedExtensions := getListFromPostFields(valuesDenied)
- for dirAllowed, allowedExts := range allowedExtensions {
- filter := dataprovider.ExtensionsFilter{
- Path: dirAllowed,
- AllowedExtensions: allowedExts,
- }
- for dirDenied, deniedExts := range deniedExtensions {
- if dirAllowed == dirDenied {
- filter.DeniedExtensions = deniedExts
- break
- }
- }
- result = append(result, filter)
- }
- for dirDenied, deniedExts := range deniedExtensions {
- found := false
- for _, res := range result {
- if res.Path == dirDenied {
- found = true
- break
- }
- }
- if !found {
- result = append(result, dataprovider.ExtensionsFilter{
- Path: dirDenied,
- DeniedExtensions: deniedExts,
- })
- }
- }
- return result
- }
- func getFiltersFromUserPostFields(r *http.Request) dataprovider.UserFilters {
- var filters dataprovider.UserFilters
- 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.FileExtensions = getFileExtensionsFromPostField(r.Form.Get("allowed_extensions"), r.Form.Get("denied_extensions"))
- filters.FilePatterns = getFilePatternsFromPostField(r.Form.Get("allowed_patterns"), r.Form.Get("denied_patterns"))
- return filters
- }
- func getSecretFromFormField(r *http.Request, field string) *kms.Secret {
- secret := kms.NewPlainSecret(r.Form.Get(field))
- if strings.TrimSpace(secret.GetPayload()) == redactedSecret {
- secret.SetStatus(kms.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.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"))
- 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.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 := ioutil.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 {
- 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")
- return config
- }
- 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 = r.Form.Get("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 getFsConfigFromUserPostFields(r *http.Request) (dataprovider.Filesystem, error) {
- var fs dataprovider.Filesystem
- provider, err := strconv.Atoi(r.Form.Get("fs_provider"))
- if err != nil {
- provider = int(dataprovider.LocalFilesystemProvider)
- }
- fs.Provider = dataprovider.FilesystemProvider(provider)
- switch fs.Provider {
- case dataprovider.S3FilesystemProvider:
- config, err := getS3Config(r)
- if err != nil {
- return fs, err
- }
- fs.S3Config = config
- case dataprovider.AzureBlobFilesystemProvider:
- config, err := getAzureConfig(r)
- if err != nil {
- return fs, err
- }
- fs.AzBlobConfig = config
- case dataprovider.GCSFilesystemProvider:
- config, err := getGCSConfig(r)
- if err != nil {
- return fs, err
- }
- fs.GCSConfig = config
- case dataprovider.CryptedFilesystemProvider:
- fs.CryptConfig.Passphrase = getSecretFromFormField(r, "crypt_passphrase")
- case dataprovider.SFTPFilesystemProvider:
- fs.SFTPConfig = getSFTPConfig(r)
- }
- 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.AdditionalInfo = r.Form.Get("additional_info")
- 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)
- 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.AdditionalInfo = replacePlaceholders(user.AdditionalInfo, replacements)
- switch user.FsConfig.Provider {
- case dataprovider.CryptedFilesystemProvider:
- user.FsConfig.CryptConfig = getCryptFsFromTemplate(user.FsConfig.CryptConfig, replacements)
- case dataprovider.S3FilesystemProvider:
- user.FsConfig.S3Config = getS3FsFromTemplate(user.FsConfig.S3Config, replacements)
- case dataprovider.GCSFilesystemProvider:
- user.FsConfig.GCSConfig = getGCSFsFromTemplate(user.FsConfig.GCSConfig, replacements)
- case dataprovider.AzureBlobFilesystemProvider:
- user.FsConfig.AzBlobConfig = getAzBlobFsFromTemplate(user.FsConfig.AzBlobConfig, replacements)
- case dataprovider.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
- }
- publicKeysFormValue := r.Form.Get("public_keys")
- publicKeys := getSliceFromDelimitedValues(publicKeysFormValue, "\n")
- 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 len(strings.TrimSpace(expirationDateString)) > 0 {
- expirationDate, err := time.Parse(webDateTimeFormat, expirationDateString)
- if err != nil {
- return user, err
- }
- expirationDateMillis = utils.GetTimeAsMsSinceEpoch(expirationDate)
- }
- fsConfig, err := getFsConfigFromUserPostFields(r)
- if err != nil {
- return user, err
- }
- user = dataprovider.User{
- Username: r.Form.Get("username"),
- Password: r.Form.Get("password"),
- PublicKeys: publicKeys,
- HomeDir: r.Form.Get("home_dir"),
- VirtualFolders: getVirtualFoldersFromPostFields(r),
- UID: uid,
- GID: gid,
- Permissions: getUserPermissionsFromPostFields(r),
- MaxSessions: maxSessions,
- QuotaSize: quotaSize,
- QuotaFiles: quotaFiles,
- UploadBandwidth: bandwidthUL,
- DownloadBandwidth: bandwidthDL,
- Status: status,
- ExpirationDate: expirationDateMillis,
- Filters: getFiltersFromUserPostFields(r),
- FsConfig: fsConfig,
- AdditionalInfo: r.Form.Get("additional_info"),
- }
- maxFileSize, err := strconv.ParseInt(r.Form.Get("max_upload_file_size"), 10, 64)
- user.Filters.MaxUploadFileSize = maxFileSize
- return user, err
- }
- func renderLoginPage(w http.ResponseWriter, error string) {
- data := loginPage{
- CurrentURL: webLoginPath,
- Version: version.Get().Version,
- Error: error,
- CSRFToken: createCSRFToken(),
- }
- renderTemplate(w, templateLogin, data)
- }
- func handleWebAdminChangePwd(w http.ResponseWriter, r *http.Request) {
- renderChangePwdPage(w, r, "")
- }
- func handleWebAdminChangePwdPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- err := r.ParseForm()
- if err != nil {
- renderChangePwdPage(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 {
- renderChangePwdPage(w, r, err.Error())
- return
- }
- handleWebLogout(w, r)
- }
- func handleWebLogout(w http.ResponseWriter, r *http.Request) {
- c := jwtTokenClaims{}
- c.removeCookie(w, r)
- http.Redirect(w, r, webLoginPath, http.StatusFound)
- }
- func handleWebLogin(w http.ResponseWriter, r *http.Request) {
- renderLoginPage(w, "")
- }
- func handleWebMaintenance(w http.ResponseWriter, r *http.Request) {
- renderMaintenancePage(w, r, "")
- }
- func handleWebRestore(w http.ResponseWriter, r *http.Request) {
- err := r.ParseMultipartForm(MaxRestoreSize)
- if err != nil {
- renderMaintenancePage(w, r, err.Error())
- return
- }
- 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 := ioutil.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); 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) {
- 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,
- }
- renderTemplate(w, templateAdmins, data)
- }
- func handleWebAddAdminGet(w http.ResponseWriter, r *http.Request) {
- admin := &dataprovider.Admin{Status: 1}
- renderAddUpdateAdminPage(w, r, admin, "", true)
- }
- func handleWebUpdateAdminGet(w http.ResponseWriter, r *http.Request) {
- username := getURLParam(r, "username")
- admin, err := dataprovider.AdminExists(username)
- if err == nil {
- renderAddUpdateAdminPage(w, r, &admin, "", false)
- } else if _, ok := err.(*dataprovider.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)
- 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)
- 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.(*dataprovider.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
- }
- claims, err := getTokenClaims(r)
- if err != nil || claims.Username == "" {
- renderAddUpdateAdminPage(w, r, &updatedAdmin, fmt.Sprintf("Invalid token claims: %v", err), 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)
- if err != nil {
- renderAddUpdateAdminPage(w, r, &admin, err.Error(), false)
- return
- }
- http.Redirect(w, r, webAdminsPath, http.StatusSeeOther)
- }
- func handleGetWebUsers(w http.ResponseWriter, r *http.Request) {
- 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,
- }
- renderTemplate(w, templateUsers, data)
- }
- func handleWebTemplateFolderGet(w http.ResponseWriter, r *http.Request) {
- if r.URL.Query().Get("from") != "" {
- name := r.URL.Query().Get("from")
- folder, err := dataprovider.GetFolderByName(name)
- if err == nil {
- renderFolderPage(w, r, folder, folderPageModeTemplate, "")
- } else if _, ok := err.(*dataprovider.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)
- templateFolder := vfs.BaseVirtualFolder{}
- err := r.ParseForm()
- if err != nil {
- renderMessagePage(w, r, "Error parsing folders fields", "", http.StatusBadRequest, err, "")
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- templateFolder.MappedPath = r.Form.Get("mapped_path")
- 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, 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 to export", "No valid folders found, export is not possible", http.StatusBadRequest, nil, "")
- return
- }
- w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-folders-from-template.json\"", len(dump.Folders)))
- render.JSON(w, r, dump)
- }
- func handleWebTemplateUserGet(w http.ResponseWriter, r *http.Request) {
- if r.URL.Query().Get("from") != "" {
- username := r.URL.Query().Get("from")
- user, err := dataprovider.UserExists(username)
- if err == nil {
- user.SetEmptySecrets()
- renderUserPage(w, r, &user, userPageModeTemplate, "")
- } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- } else {
- user := dataprovider.User{Status: 1}
- renderUserPage(w, r, &user, userPageModeTemplate, "")
- }
- }
- func handleWebTemplateUserPost(w http.ResponseWriter, r *http.Request) {
- r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
- 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, 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 to export", "No valid users found, export is not possible", http.StatusBadRequest, nil, "")
- return
- }
- w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"sftpgo-%v-users-from-template.json\"", len(dump.Users)))
- render.JSON(w, r, dump)
- }
- func handleWebAddUserGet(w http.ResponseWriter, r *http.Request) {
- if r.URL.Query().Get("clone-from") != "" {
- username := r.URL.Query().Get("clone-from")
- user, err := dataprovider.UserExists(username)
- if err == nil {
- user.ID = 0
- user.Username = ""
- user.Password = ""
- user.SetEmptySecrets()
- renderUserPage(w, r, &user, userPageModeAdd, "")
- } else if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- } else {
- renderInternalServerErrorPage(w, r, err)
- }
- } else {
- user := dataprovider.User{Status: 1}
- renderUserPage(w, r, &user, userPageModeAdd, "")
- }
- }
- func handleWebUpdateUserGet(w http.ResponseWriter, r *http.Request) {
- username := getURLParam(r, "username")
- user, err := dataprovider.UserExists(username)
- if err == nil {
- renderUserPage(w, r, &user, userPageModeUpdate, "")
- } else if _, ok := err.(*dataprovider.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)
- 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)
- 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)
- username := getURLParam(r, "username")
- user, err := dataprovider.UserExists(username)
- if _, ok := err.(*dataprovider.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.SetEmptySecretsIfNil()
- if updatedUser.Password == redactedSecret {
- updatedUser.Password = user.Password
- }
- updateEncryptedSecrets(&updatedUser, user.FsConfig.S3Config.AccessSecret, user.FsConfig.AzBlobConfig.AccountKey,
- user.FsConfig.GCSConfig.Credentials, user.FsConfig.CryptConfig.Passphrase, user.FsConfig.SFTPConfig.Password,
- user.FsConfig.SFTPConfig.PrivateKey)
- err = dataprovider.UpdateUser(&updatedUser)
- 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) {
- data := statusPage{
- basePage: getBasePageData(pageStatusTitle, webStatusPath, r),
- Status: getServicesStatus(),
- }
- renderTemplate(w, templateStatus, data)
- }
- func handleWebGetConnections(w http.ResponseWriter, r *http.Request) {
- connectionStats := common.Connections.GetStats()
- data := connectionsPage{
- basePage: getBasePageData(pageConnectionsTitle, webConnectionsPath, r),
- Connections: connectionStats,
- }
- renderTemplate(w, templateConnections, data)
- }
- func handleWebAddFolderGet(w http.ResponseWriter, r *http.Request) {
- 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.ParseForm()
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeAdd, err.Error())
- return
- }
- 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")
- 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) {
- name := getURLParam(r, "name")
- folder, err := dataprovider.GetFolderByName(name)
- if err == nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, "")
- } else if _, ok := err.(*dataprovider.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)
- name := getURLParam(r, "name")
- folder, err := dataprovider.GetFolderByName(name)
- if _, ok := err.(*dataprovider.RecordNotFoundError); ok {
- renderNotFoundPage(w, r, err)
- return
- } else if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- err = r.ParseForm()
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
- return
- }
- if err := verifyCSRFToken(r.Form.Get(csrfFormToken)); err != nil {
- renderForbiddenPage(w, r, err.Error())
- return
- }
- folder.MappedPath = r.Form.Get("mapped_path")
- err = dataprovider.UpdateFolder(&folder)
- if err != nil {
- renderFolderPage(w, r, folder, folderPageModeUpdate, err.Error())
- return
- }
- http.Redirect(w, r, webFoldersPath, http.StatusSeeOther)
- }
- func handleWebGetFolders(w http.ResponseWriter, r *http.Request) {
- 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 := make([]vfs.BaseVirtualFolder, 0, limit)
- for {
- f, err := dataprovider.GetFolders(limit, len(folders), dataprovider.OrderASC)
- if err != nil {
- renderInternalServerErrorPage(w, r, err)
- return
- }
- folders = append(folders, f...)
- if len(f) < limit {
- break
- }
- }
- data := foldersPage{
- basePage: getBasePageData(pageFoldersTitle, webFoldersPath, r),
- Folders: folders,
- }
- renderTemplate(w, templateFolders, data)
- }
|