diff --git a/cmd/root.go b/cmd/root.go
index 1beace66..5b314b61 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -312,9 +312,10 @@ func setupLog(logMethod string) {
func quickSetup(flags *pflag.FlagSet, d pythonData) {
set := &settings.Settings{
- Key: generateKey(),
- Signup: false,
- CreateUserDir: false,
+ Key: generateKey(),
+ Signup: false,
+ CreateUserDir: false,
+ UserHomeBasePath: settings.DefaultUsersHomeBasePath,
Defaults: settings.UserDefaults{
Scope: ".",
Locale: "en",
@@ -330,6 +331,11 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) {
Download: true,
},
},
+ AuthMethod: "",
+ Branding: settings.Branding{},
+ Commands: nil,
+ Shell: nil,
+ Rules: nil,
}
var err error
diff --git a/frontend/src/components/settings/UserForm.vue b/frontend/src/components/settings/UserForm.vue
index 15504a11..b6bd3877 100644
--- a/frontend/src/components/settings/UserForm.vue
+++ b/frontend/src/components/settings/UserForm.vue
@@ -24,12 +24,18 @@
+
+
+ {{ $t("settings.createUserHomeDirectory") }}
+
@@ -69,17 +75,35 @@ import { enableExec } from "@/utils/constants";
export default {
name: "user",
+ data: () => {
+ return {
+ createUserDirData: false,
+ originalUserScope: "/",
+ };
+ },
components: {
Permissions,
Languages,
Rules,
Commands,
},
- props: ["user", "isNew", "isDefault"],
+ props: ["user", "createUserDir", "isNew", "isDefault"],
+ created() {
+ this.originalUserScope = this.user.scope;
+ this.createUserDirData = this.createUserDir;
+ },
computed: {
passwordPlaceholder() {
return this.isNew ? "" : this.$t("settings.avoidChanges");
},
+ scopePlaceholder() {
+ return this.createUserDir
+ ? this.$t("settings.userScopeGenerationPlaceholder")
+ : "";
+ },
+ displayHomeDirectoryCheckbox() {
+ return this.isNew && this.createUserDir;
+ },
isExecEnabled: () => enableExec,
},
watch: {
@@ -87,6 +111,9 @@ export default {
if (!this.user.perm.admin) return;
this.user.lockPassword = false;
},
+ createUserDirData() {
+ this.user.scope = this.createUserDirData ? "" : this.originalUserScope;
+ },
},
};
diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json
index 182c6c08..6aaa6145 100644
--- a/frontend/src/i18n/en.json
+++ b/frontend/src/i18n/en.json
@@ -182,6 +182,9 @@
"commandRunnerHelp": "Here you can set commands that are executed in the named events. You must write one per line. The environment variables {0} and {1} will be available, being {0} relative to {1}. For more information about this feature and the available environment variables, please read the {2}.",
"commandsUpdated": "Commands updated!",
"createUserDir": "Auto create user home dir while adding new user",
+ "userHomeBasePath": "Base path for user home directories",
+ "userScopeGenerationPlaceholder": "The scope will be auto generated",
+ "createUserHomeDirectory": "Create user home directory",
"customStylesheet": "Custom Stylesheet",
"defaultUserDescription": "This are the default settings for new users.",
"disableExternalLinks": "Disable external links (except documentation)",
diff --git a/frontend/src/views/settings/Global.vue b/frontend/src/views/settings/Global.vue
index 7d4e91fc..10993325 100644
--- a/frontend/src/views/settings/Global.vue
+++ b/frontend/src/views/settings/Global.vue
@@ -18,6 +18,15 @@
{{ $t("settings.createUserDir") }}
+
+
{{ $t("settings.userHomeBasePath") }}
+
+
+
{{ $t("settings.rules") }}
{{ $t("settings.globalRules") }}
diff --git a/frontend/src/views/settings/User.vue b/frontend/src/views/settings/User.vue
index 09193e33..95d0352a 100644
--- a/frontend/src/views/settings/User.vue
+++ b/frontend/src/views/settings/User.vue
@@ -9,7 +9,12 @@
-
+
@@ -73,6 +78,7 @@ export default {
error: null,
originalUser: null,
user: {},
+ createUserDir: false,
};
},
created() {
@@ -98,7 +104,8 @@ export default {
try {
if (this.isNew) {
- let { defaults } = await settings.get();
+ let { defaults, createUserDir } = await settings.get();
+ this.createUserDir = createUserDir;
this.user = {
...defaults,
username: "",
diff --git a/go.mod b/go.mod
index 6253cbb5..f0c025fe 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
github.com/mholt/archiver/v3 v3.5.1
github.com/mitchellh/go-homedir v1.1.0
github.com/pelletier/go-toml/v2 v2.0.0
+ github.com/shirou/gopsutil/v3 v3.22.5
github.com/spf13/afero v1.8.2
github.com/spf13/cobra v1.4.0
github.com/spf13/pflag v1.0.5
@@ -39,6 +40,7 @@ require (
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-acme/lego v2.5.0+incompatible // indirect
github.com/go-errors/errors v1.1.1 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
github.com/golang/snappy v0.0.2 // indirect
github.com/google/uuid v1.1.2 // indirect
@@ -55,12 +57,13 @@ require (
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/shirou/gopsutil/v3 v3.22.5 // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ulikunitz/xz v0.5.9 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
+ github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/net v0.0.0-20220412020605-290c469a71a5 // indirect
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
golang.org/x/text v0.3.7 // indirect
diff --git a/go.sum b/go.sum
index 1ef3e038..6cc2c5d4 100644
--- a/go.sum
+++ b/go.sum
@@ -101,6 +101,7 @@ github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@@ -245,6 +246,7 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -289,6 +291,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
diff --git a/http/settings.go b/http/settings.go
index 0148b383..78ce9b0d 100644
--- a/http/settings.go
+++ b/http/settings.go
@@ -9,24 +9,26 @@ import (
)
type settingsData struct {
- Signup bool `json:"signup"`
- CreateUserDir bool `json:"createUserDir"`
- Defaults settings.UserDefaults `json:"defaults"`
- Rules []rules.Rule `json:"rules"`
- Branding settings.Branding `json:"branding"`
- Shell []string `json:"shell"`
- Commands map[string][]string `json:"commands"`
+ Signup bool `json:"signup"`
+ CreateUserDir bool `json:"createUserDir"`
+ UserHomeBasePath string `json:"userHomeBasePath"`
+ Defaults settings.UserDefaults `json:"defaults"`
+ Rules []rules.Rule `json:"rules"`
+ Branding settings.Branding `json:"branding"`
+ Shell []string `json:"shell"`
+ Commands map[string][]string `json:"commands"`
}
var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
data := &settingsData{
- Signup: d.settings.Signup,
- CreateUserDir: d.settings.CreateUserDir,
- Defaults: d.settings.Defaults,
- Rules: d.settings.Rules,
- Branding: d.settings.Branding,
- Shell: d.settings.Shell,
- Commands: d.settings.Commands,
+ Signup: d.settings.Signup,
+ CreateUserDir: d.settings.CreateUserDir,
+ UserHomeBasePath: d.settings.UserHomeBasePath,
+ Defaults: d.settings.Defaults,
+ Rules: d.settings.Rules,
+ Branding: d.settings.Branding,
+ Shell: d.settings.Shell,
+ Commands: d.settings.Commands,
}
return renderJSON(w, r, data)
@@ -41,6 +43,7 @@ var settingsPutHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
d.settings.Signup = req.Signup
d.settings.CreateUserDir = req.CreateUserDir
+ d.settings.UserHomeBasePath = req.UserHomeBasePath
d.settings.Defaults = req.Defaults
d.settings.Rules = req.Rules
d.settings.Branding = req.Branding
diff --git a/settings/dir.go b/settings/dir.go
index a1971c05..25289ee4 100644
--- a/settings/dir.go
+++ b/settings/dir.go
@@ -2,9 +2,10 @@ package settings
import (
"errors"
+ "fmt"
"log"
"os"
- "path/filepath"
+ "path"
"regexp"
"strings"
@@ -19,47 +20,23 @@ var (
// MakeUserDir makes the user directory according to settings.
func (s *Settings) MakeUserDir(username, userScope, serverRoot string) (string, error) {
- var err error
userScope = strings.TrimSpace(userScope)
- if userScope == "" || userScope == "./" {
- userScope = "."
+ if userScope == "" && s.CreateUserDir {
+ username = cleanUsername(username)
+ if username == "" || username == "-" || username == "." {
+ log.Printf("create user: invalid user for home dir creation: [%s]", username)
+ return "", errors.New("invalid user for home dir creation")
+ }
+ userScope = path.Join(s.UserHomeBasePath, username)
}
- if !s.CreateUserDir {
- return userScope, nil
- }
+ userScope = path.Join("/", userScope)
fs := afero.NewBasePathFs(afero.NewOsFs(), serverRoot)
-
- // Use the default auto create logic only if specific scope is not the default scope
- if userScope != s.Defaults.Scope {
- // Try create the dir, for example: settings.Defaults.Scope == "." and userScope == "./foo"
- if userScope != "." {
- err = fs.MkdirAll(userScope, os.ModePerm)
- if err != nil {
- log.Printf("create user: failed to mkdir user home dir: [%s]", userScope)
- }
- }
- return userScope, err
+ if err := fs.MkdirAll(userScope, os.ModePerm); err != nil {
+ return "", fmt.Errorf("failed to create user home dir: [%s]: %w", userScope, err)
}
-
- // Clean username first
- username = cleanUsername(username)
- if username == "" || username == "-" || username == "." {
- log.Printf("create user: invalid user for home dir creation: [%s]", username)
- return "", errors.New("invalid user for home dir creation")
- }
-
- // Create default user dir
- userHomeBase := filepath.Join(s.Defaults.Scope, "users")
- userHome := filepath.Join(userHomeBase, username)
- err = fs.MkdirAll(userHome, os.ModePerm)
- if err != nil {
- log.Printf("create user: failed to mkdir user home dir: [%s]", userHome)
- } else {
- log.Printf("create user: mkdir user home dir: [%s] successfully.", userHome)
- }
- return userHome, err
+ return userScope, nil
}
func cleanUsername(s string) string {
diff --git a/settings/settings.go b/settings/settings.go
index 9cd45af6..9e0c4fe2 100644
--- a/settings/settings.go
+++ b/settings/settings.go
@@ -7,20 +7,23 @@ import (
"github.com/filebrowser/filebrowser/v2/rules"
)
+const DefaultUsersHomeBasePath = "/users"
+
// AuthMethod describes an authentication method.
type AuthMethod string
// Settings contain the main settings of the application.
type Settings struct {
- Key []byte `json:"key"`
- Signup bool `json:"signup"`
- CreateUserDir bool `json:"createUserDir"`
- Defaults UserDefaults `json:"defaults"`
- AuthMethod AuthMethod `json:"authMethod"`
- Branding Branding `json:"branding"`
- Commands map[string][]string `json:"commands"`
- Shell []string `json:"shell"`
- Rules []rules.Rule `json:"rules"`
+ Key []byte `json:"key"`
+ Signup bool `json:"signup"`
+ CreateUserDir bool `json:"createUserDir"`
+ UserHomeBasePath string `json:"userHomeBasePath"`
+ Defaults UserDefaults `json:"defaults"`
+ AuthMethod AuthMethod `json:"authMethod"`
+ Branding Branding `json:"branding"`
+ Commands map[string][]string `json:"commands"`
+ Shell []string `json:"shell"`
+ Rules []rules.Rule `json:"rules"`
}
// GetRules implements rules.Provider.
diff --git a/settings/storage.go b/settings/storage.go
index d88f5c28..8498d3bf 100644
--- a/settings/storage.go
+++ b/settings/storage.go
@@ -26,7 +26,14 @@ func NewStorage(back StorageBackend) *Storage {
// Get returns the settings for the current instance.
func (s *Storage) Get() (*Settings, error) {
- return s.back.Get()
+ set, err := s.back.Get()
+ if err != nil {
+ return nil, err
+ }
+ if set.UserHomeBasePath == "" {
+ set.UserHomeBasePath = DefaultUsersHomeBasePath
+ }
+ return set, nil
}
var defaultEvents = []string{
diff --git a/users/users.go b/users/users.go
index ab5421bf..120e4599 100644
--- a/users/users.go
+++ b/users/users.go
@@ -92,11 +92,7 @@ func (u *User) Clean(baseScope string, fields ...string) error {
if u.Fs == nil {
scope := u.Scope
-
- if !filepath.IsAbs(scope) {
- scope = filepath.Join(baseScope, scope)
- }
-
+ scope = filepath.Join(baseScope, filepath.Join("/", scope)) //nolint:gocritic
u.Fs = afero.NewBasePathFs(afero.NewOsFs(), scope)
}