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 @@