aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/structs/admin_user.go2
-rw-r--r--modules/validation/binding.go20
-rw-r--r--modules/validation/helpers.go12
-rw-r--r--modules/validation/helpers_test.go31
-rw-r--r--modules/web/middleware/binding.go2
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 != "" {