diff --git a/app/import_functions.go b/app/import_functions.go index a5043ae9fd..add7e37212 100644 --- a/app/import_functions.go +++ b/app/import_functions.go @@ -334,7 +334,7 @@ func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError { authData = nil } else { // If no AuthData or Password is specified, we must generate a password. - password = model.NewId() + password = model.GeneratePassword(*a.Config().PasswordSettings.MinimumLength) authData = nil } diff --git a/model/user.go b/model/user.go index ee8c8e7b6b..5a308775cc 100644 --- a/model/user.go +++ b/model/user.go @@ -9,10 +9,12 @@ import ( "fmt" "io" "io/ioutil" + "math/rand" "net/http" "regexp" "sort" "strings" + "time" "unicode/utf8" "github.com/mattermost/mattermost-server/services/timezones" @@ -836,3 +838,27 @@ func UsersWithGroupsAndCountFromJson(data io.Reader) *UsersWithGroupsAndCount { json.Unmarshal(bodyBytes, uwg) return uwg } + +var passwordRandomSource = rand.NewSource(time.Now().Unix()) +var passwordSpecialChars = "!$%^&*(),." +var passwordNumbers = "0123456789" +var passwordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +var passwordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz" +var passwordAllChars = passwordSpecialChars + passwordNumbers + passwordUpperCaseLetters + passwordLowerCaseLetters + +func GeneratePassword(minimumLength int) string { + r := rand.New(passwordRandomSource) + + // Make sure we are guaranteed at least one of each type to meet any possible password complexity requirements. + password := string([]rune(passwordUpperCaseLetters)[r.Intn(len(passwordUpperCaseLetters))]) + + string([]rune(passwordNumbers)[r.Intn(len(passwordNumbers))]) + + string([]rune(passwordLowerCaseLetters)[r.Intn(len(passwordLowerCaseLetters))]) + + string([]rune(passwordSpecialChars)[r.Intn(len(passwordSpecialChars))]) + + for len(password) < minimumLength { + i := r.Intn(len(passwordAllChars)) + password = password + string([]rune(passwordAllChars)[i]) + } + + return password +} diff --git a/model/user_test.go b/model/user_test.go index 5fe39917c6..97e513894d 100644 --- a/model/user_test.go +++ b/model/user_test.go @@ -5,6 +5,7 @@ package model import ( "fmt" + "math/rand" "net/http" "strings" "testing" @@ -350,3 +351,25 @@ func TestUserSlice(t *testing.T) { assert.Equal(t, 1, len(nonBotUsers)) }) } + +func TestGeneratePassword(t *testing.T) { + passwordRandomSource = rand.NewSource(12345) + + t.Run("Should be the minimum length or 4, whichever is less", func(t *testing.T) { + password1 := GeneratePassword(5) + assert.Len(t, password1, 5) + password2 := GeneratePassword(10) + assert.Len(t, password2, 10) + password3 := GeneratePassword(1) + assert.Len(t, password3, 4) + }) + + t.Run("Should contain at least one of symbols, upper case, lower case and numbers", func(t *testing.T) { + password := GeneratePassword(4) + require.Len(t, password, 4) + assert.Contains(t, []rune(passwordUpperCaseLetters), []rune(password)[0]) + assert.Contains(t, []rune(passwordNumbers), []rune(password)[1]) + assert.Contains(t, []rune(passwordLowerCaseLetters), []rune(password)[2]) + assert.Contains(t, []rune(passwordSpecialChars), []rune(password)[3]) + }) +}