From 2f3517e627acd073ac155309caba3bcea50eb46f Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sat, 28 Jun 2025 09:41:11 +0200 Subject: [PATCH] fix: add configurable minimum password length --- auth/hook.go | 4 +-- auth/proxy.go | 7 ++--- cmd/config.go | 2 ++ cmd/config_init.go | 13 +++++---- cmd/config_set.go | 2 ++ cmd/root.go | 13 +++++---- cmd/users_add.go | 2 +- cmd/users_update.go | 6 ++-- errors/errors.go | 1 + frontend/src/i18n/en.json | 1 + frontend/src/types/settings.d.ts | 1 + frontend/src/views/settings/Global.vue | 28 ++++++++++++------ http/auth.go | 2 +- http/data.go | 3 ++ http/settings.go | 39 ++++++++++++++------------ http/users.go | 14 +++++++-- settings/settings.go | 24 ++++++++-------- settings/storage.go | 3 ++ users/password.go | 15 +++++++--- 19 files changed, 113 insertions(+), 67 deletions(-) diff --git a/auth/hook.go b/auth/hook.go index c659e57b..9ccbd2fe 100644 --- a/auth/hook.go +++ b/auth/hook.go @@ -150,7 +150,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) { } if u == nil { - pass, err := users.HashPwd(a.Cred.Password) + pass, err := users.HashAndValidatePwd(a.Cred.Password, a.Settings.MinimumPasswordLength) if err != nil { return nil, err } @@ -186,7 +186,7 @@ func (a *HookAuth) SaveUser() (*users.User, error) { // update the password when it doesn't match the current if p { - pass, err := users.HashPwd(a.Cred.Password) + pass, err := users.HashAndValidatePwd(a.Cred.Password, a.Settings.MinimumPasswordLength) if err != nil { return nil, err } diff --git a/auth/proxy.go b/auth/proxy.go index 0e954309..c60be9e6 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -1,7 +1,6 @@ package auth import ( - "crypto/rand" "errors" "net/http" @@ -29,15 +28,13 @@ func (a ProxyAuth) Auth(r *http.Request, usr users.Store, setting *settings.Sett } func (a ProxyAuth) createUser(usr users.Store, setting *settings.Settings, srv *settings.Server, username string) (*users.User, error) { - const passwordSize = 32 - randomPasswordBytes := make([]byte, passwordSize) - _, err := rand.Read(randomPasswordBytes) + pwd, err := users.RandomPwd(setting.MinimumPasswordLength + 10) if err != nil { return nil, err } var hashedRandomPassword string - hashedRandomPassword, err = users.HashPwd(string(randomPasswordBytes)) + hashedRandomPassword, err = users.HashAndValidatePwd(pwd, setting.MinimumPasswordLength) if err != nil { return nil, err } diff --git a/cmd/config.go b/cmd/config.go index de55c28e..d5b7fc08 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -32,6 +32,7 @@ func addConfigFlags(flags *pflag.FlagSet) { addUserFlags(flags) flags.BoolP("signup", "s", false, "allow users to signup") flags.Bool("create-user-dir", false, "generate user's home directory automatically") + flags.Uint("minimum-password-length", 12, "minimum password length for new users") flags.String("shell", "", "shell command to which other commands should be appended") flags.String("auth.method", string(auth.MethodJSONAuth), "authentication type") @@ -144,6 +145,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut fmt.Fprintf(w, "Sign up:\t%t\n", set.Signup) fmt.Fprintf(w, "Create User Dir:\t%t\n", set.CreateUserDir) + fmt.Fprintf(w, "Minimum Password Length:\t%d\n", set.MinimumPasswordLength) fmt.Fprintf(w, "Auth method:\t%s\n", set.AuthMethod) fmt.Fprintf(w, "Shell:\t%s\t\n", strings.Join(set.Shell, " ")) fmt.Fprintln(w, "\nBranding:") diff --git a/cmd/config_init.go b/cmd/config_init.go index 60a0f37b..d9710514 100644 --- a/cmd/config_init.go +++ b/cmd/config_init.go @@ -29,12 +29,13 @@ override the options.`, authMethod, auther := getAuthentication(flags) s := &settings.Settings{ - Key: generateKey(), - Signup: mustGetBool(flags, "signup"), - CreateUserDir: mustGetBool(flags, "create-user-dir"), - Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")), - AuthMethod: authMethod, - Defaults: defaults, + Key: generateKey(), + Signup: mustGetBool(flags, "signup"), + CreateUserDir: mustGetBool(flags, "create-user-dir"), + MinimumPasswordLength: mustGetUint(flags, "minimum-password-length"), + Shell: convertCmdStrToCmdArray(mustGetString(flags, "shell")), + AuthMethod: authMethod, + Defaults: defaults, Branding: settings.Branding{ Name: mustGetString(flags, "branding.name"), DisableExternal: mustGetBool(flags, "branding.disableExternal"), diff --git a/cmd/config_set.go b/cmd/config_set.go index 23ff7e1b..05816795 100644 --- a/cmd/config_set.go +++ b/cmd/config_set.go @@ -51,6 +51,8 @@ you want to change. Other options will remain unchanged.`, set.Shell = convertCmdStrToCmdArray(mustGetString(flags, flag.Name)) case "create-user-dir": set.CreateUserDir = mustGetBool(flags, flag.Name) + case "minimum-password-length": + set.MinimumPasswordLength = mustGetUint(flags, flag.Name) case "branding.name": set.Branding.Name = mustGetString(flags, flag.Name) case "branding.color": diff --git a/cmd/root.go b/cmd/root.go index d9f37889..c2ee7c73 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -365,10 +365,11 @@ func setupLog(logMethod string) { func quickSetup(flags *pflag.FlagSet, d pythonData) { set := &settings.Settings{ - Key: generateKey(), - Signup: false, - CreateUserDir: false, - UserHomeBasePath: settings.DefaultUsersHomeBasePath, + Key: generateKey(), + Signup: false, + CreateUserDir: false, + MinimumPasswordLength: settings.DefaultMinimumPasswordLength, + UserHomeBasePath: settings.DefaultUsersHomeBasePath, Defaults: settings.UserDefaults{ Scope: ".", Locale: "en", @@ -426,12 +427,12 @@ func quickSetup(flags *pflag.FlagSet, d pythonData) { if password == "" { var pwd string - pwd, err = users.RandomPwd() + pwd, err = users.RandomPwd(set.MinimumPasswordLength) checkErr(err) log.Println("Randomly generated password for user 'admin':", pwd) - password, err = users.HashPwd(pwd) + password, err = users.HashAndValidatePwd(pwd, set.MinimumPasswordLength) checkErr(err) } diff --git a/cmd/users_add.go b/cmd/users_add.go index e7f132ed..4b344107 100644 --- a/cmd/users_add.go +++ b/cmd/users_add.go @@ -21,7 +21,7 @@ var usersAddCmd = &cobra.Command{ checkErr(err) getUserDefaults(cmd.Flags(), &s.Defaults, false) - password, err := users.HashPwd(args[1]) + password, err := users.HashAndValidatePwd(args[1], s.MinimumPasswordLength) checkErr(err) user := &users.User{ diff --git a/cmd/users_update.go b/cmd/users_update.go index 822bb6dc..aa06abee 100644 --- a/cmd/users_update.go +++ b/cmd/users_update.go @@ -27,8 +27,10 @@ options you want to change.`, password := mustGetString(flags, "password") newUsername := mustGetString(flags, "username") + s, err := d.store.Settings.Get() + checkErr(err) + var ( - err error user *users.User ) @@ -64,7 +66,7 @@ options you want to change.`, } if password != "" { - user.Password, err = users.HashPwd(password) + user.Password, err = users.HashAndValidatePwd(password, s.MinimumPasswordLength) checkErr(err) } diff --git a/errors/errors.go b/errors/errors.go index 5ec364c0..7bb10e81 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -7,6 +7,7 @@ var ( ErrExist = errors.New("the resource already exists") ErrNotExist = errors.New("the resource does not exist") ErrEmptyPassword = errors.New("password is empty") + ErrShortPassword = errors.New("password is too short") ErrEmptyUsername = errors.New("username is empty") ErrEmptyRequest = errors.New("empty request") ErrScopeIsRelative = errors.New("scope is a relative path") diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 1360bbec..7de128ed 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -170,6 +170,7 @@ "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", + "minimumPasswordLength": "Minimum password length", "tusUploads": "Chunked Uploads", "tusUploadsHelp": "File Browser supports chunked file uploads, allowing for the creation of efficient, reliable, resumable and chunked file uploads even on unreliable networks.", "tusUploadsChunkSize": "Indicates to maximum size of a request (direct uploads will be used for smaller uploads). You may input a plain integer denoting byte size input or a string like 10MB, 1GB etc.", diff --git a/frontend/src/types/settings.d.ts b/frontend/src/types/settings.d.ts index a2c19f76..90ca6ae7 100644 --- a/frontend/src/types/settings.d.ts +++ b/frontend/src/types/settings.d.ts @@ -1,6 +1,7 @@ interface ISettings { signup: boolean; createUserDir: boolean; + minimumPasswordLength: number; userHomeBasePath: string; defaults: SettingsDefaults; rules: any[]; diff --git a/frontend/src/views/settings/Global.vue b/frontend/src/views/settings/Global.vue index 1997576e..331dd438 100644 --- a/frontend/src/views/settings/Global.vue +++ b/frontend/src/views/settings/Global.vue @@ -18,14 +18,26 @@ {{ t("settings.createUserDir") }}

-
-

{{ t("settings.userHomeBasePath") }}

+

+ -

+

+ +

+ + +

{{ t("settings.rules") }}

{{ t("settings.globalRules") }}

@@ -229,17 +241,17 @@