fix(user): unify email validation for registration and settings (#39304)

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
Abhay Pratap Singh
2026-09-16 23:04:41 +05:30
committed by GitHub
parent c6c671e113
commit 7ebb2caa9e
21 changed files with 138 additions and 209 deletions
+13
View File
@@ -48,8 +48,21 @@ func newFieldError(field reflect.StructField, cls, msg string) *BindingError {
return &BindingError{[]string{field.Name}, cls, msg} //nolint:govet // make sure no missing fields
}
func AddValidationError(errs BindingErrors, fieldName, errorMsg string) BindingErrors {
errs.Add([]string{fieldName}, ErrCustomMessage, errorMsg)
return errs
}
// AddBindingRules adds additional binding rules
func AddBindingRules(b *binding.Binder) {
b.ClearRules("Email")
b.AddRuleNonZero("Email", func(_ context.Context, f *binding.ValidationField) *binding.Error {
if !IsEmailAddressValid(f.ValueMustString()) {
return newFieldError(f.StructField, binding.ERR_EMAIL, "invalid email")
}
return nil
})
b.AddRuleNonZero("GitRefName", func(ctx context.Context, f *binding.ValidationField) *binding.Error {
if !git.IsValidRefPattern(f.ValueMustString()) {
return newFieldError(f.StructField, ErrGitRefName, "GitRefName")
+7
View File
@@ -21,9 +21,16 @@ type (
URL string `form:"ValidUrl" binding:"ValidUrl"`
GlobPattern string `form:"GlobPattern" binding:"GlobPattern"`
RegexPattern string `form:"RegexPattern" binding:"RegexPattern"`
Email string `form:"Email" binding:"Email"`
}
)
func performValidationTest(t *testing.T, testCase validationTestCase) {
assert.Equal(t, testCase.expectedErrors, Binder().Validate(t.Context(), testCase.data))
}
func TestEmailValidation(t *testing.T) {
assert.Nil(t, Binder().Validate(t.Context(), &TestForm{Email: "b@a"}))
assert.Equal(t, BindingErrors{{FieldNames: []string{"Email"}, Classification: "EmailError", Message: "invalid email"}},
Binder().Validate(t.Context(), &TestForm{Email: "abc"}))
}
+23
View File
@@ -4,14 +4,18 @@
package validation
import (
"net/mail"
"net/url"
"regexp"
"slices"
"strings"
"sync"
"unicode/utf8"
"gitea.dev/modules/glob"
"gitea.dev/modules/setting"
"golang.org/x/net/idna"
)
type globalVarsStruct struct {
@@ -108,3 +112,22 @@ func IsValidBadgeSlug(slug string) bool {
vars := globalVars()
return vars.validBadgeSlugPattern.MatchString(slug) && !vars.invalidBadgeSlugPattern.MatchString(slug)
}
func IsEmailAddressValid(email string) bool {
if strings.ContainsFunc(email, func(r rune) bool { return r >= utf8.RuneSelf }) {
// At the moment, we don't support UTF8 email address. To support it, need to correctly handle IDN/punycode
return false
}
addr, err := mail.ParseAddress(email)
if err != nil || addr.Address != email {
// email must be parseable, and the "email" string must be the address, no other parts
return false
}
_, domain, _ := strings.Cut(email, "@")
if strings.HasPrefix(domain, "[") {
// address like "foo@[192.168.1.2]"
return true
}
_, err = idna.Registration.ToASCII(domain)
return err == nil
}
-5
View File
@@ -88,11 +88,6 @@ func getRuleBody(field reflect.StructField, ruleName string) string {
return ""
}
func AddValidationError(errs validation.BindingErrors, fieldName, errorMsg string) validation.BindingErrors {
errs.Add([]string{fieldName}, validation.ErrCustomMessage, errorMsg)
return errs
}
func getFieldDisplayNameForMessage(f any, l translation.Locale, fieldNames []string) (field reflect.StructField, ok bool, displayName string) {
if len(fieldNames) == 0 {
return field, false, ""