diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/structs/admin_user.go | 2 | ||||
-rw-r--r-- | modules/validation/binding.go | 20 | ||||
-rw-r--r-- | modules/validation/helpers.go | 12 | ||||
-rw-r--r-- | modules/validation/helpers_test.go | 31 | ||||
-rw-r--r-- | modules/web/middleware/binding.go | 2 |
5 files changed, 66 insertions, 1 deletions
diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index eccbf29a46..2f6f502af6 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -10,7 +10,7 @@ type CreateUserOption struct { SourceID int64 `json:"source_id"` LoginName string `json:"login_name"` // required: true - Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(40)"` + Username string `json:"username" binding:"Required;Username;MaxSize(40)"` FullName string `json:"full_name" binding:"MaxSize(100)"` // required: true // swagger:strfmt email diff --git a/modules/validation/binding.go b/modules/validation/binding.go index f08f632426..c054fbe7b6 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -24,6 +24,9 @@ const ( // ErrRegexPattern is returned when a regex pattern is invalid ErrRegexPattern = "RegexPattern" + + // ErrUsername is username error + ErrUsername = "UsernameError" ) // AddBindingRules adds additional binding rules @@ -34,6 +37,7 @@ func AddBindingRules() { addGlobPatternRule() addRegexPatternRule() addGlobOrRegexPatternRule() + addUsernamePatternRule() } func addGitRefNameBindingRule() { @@ -148,6 +152,22 @@ func addGlobOrRegexPatternRule() { }) } +func addUsernamePatternRule() { + binding.AddRule(&binding.Rule{ + IsMatch: func(rule string) bool { + return rule == "Username" + }, + IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + str := fmt.Sprintf("%v", val) + if !IsValidUsername(str) { + errs.Add([]string{name}, ErrUsername, "invalid username") + return false, errs + } + return true, errs + }, + }) +} + func portOnly(hostport string) string { colon := strings.IndexByte(hostport, ':') if colon == -1 { diff --git a/modules/validation/helpers.go b/modules/validation/helpers.go index 484b12b2a2..8e49c7855e 100644 --- a/modules/validation/helpers.go +++ b/modules/validation/helpers.go @@ -91,3 +91,15 @@ func IsValidExternalTrackerURLFormat(uri string) bool { return true } + +var ( + validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`) + invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars +) + +// IsValidUsername checks if username is valid +func IsValidUsername(name string) bool { + // It is difficult to find a single pattern that is both readable and effective, + // but it's easier to use positive and negative checks. + return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name) +} diff --git a/modules/validation/helpers_test.go b/modules/validation/helpers_test.go index f6f897e821..9bdbdb4a77 100644 --- a/modules/validation/helpers_test.go +++ b/modules/validation/helpers_test.go @@ -155,3 +155,34 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) { }) } } + +func TestIsValidUsername(t *testing.T) { + tests := []struct { + arg string + want bool + }{ + {arg: "a", want: true}, + {arg: "abc", want: true}, + {arg: "0.b-c", want: true}, + {arg: "a.b-c_d", want: true}, + {arg: "", want: false}, + {arg: ".abc", want: false}, + {arg: "abc.", want: false}, + {arg: "a..bc", want: false}, + {arg: "a...bc", want: false}, + {arg: "a.-bc", want: false}, + {arg: "a._bc", want: false}, + {arg: "a_-bc", want: false}, + {arg: "a/bc", want: false}, + {arg: "☁️", want: false}, + {arg: "-", want: false}, + {arg: "--diff", want: false}, + {arg: "-im-here", want: false}, + {arg: "a space", want: false}, + } + for _, tt := range tests { + t.Run(tt.arg, func(t *testing.T) { + assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername(%v)", tt.arg) + }) + } +} diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 636e655b9e..cced9717be 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -135,6 +135,8 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) case validation.ErrRegexPattern: data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message) + case validation.ErrUsername: + data["ErrorMsg"] = trName + l.Tr("form.username_error") default: msg := errs[0].Classification if msg != "" && errs[0].Message != "" { |