loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'fix: use ValidateEmail as binding across web forms' (#5158) from solomonv/consolidate-email-validation into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5158
Reviewed-by: Gusted <gusted@noreply.codeberg.org>

Gusted f298bf12 ea70757f

+281 -221
-99
models/user/email_address.go
··· 7 7 import ( 8 8 "context" 9 9 "fmt" 10 - "net/mail" 11 - "regexp" 12 10 "strings" 13 11 "time" 14 12 ··· 18 16 "code.gitea.io/gitea/modules/optional" 19 17 "code.gitea.io/gitea/modules/setting" 20 18 "code.gitea.io/gitea/modules/util" 21 - "code.gitea.io/gitea/modules/validation" 22 19 23 20 "xorm.io/builder" 24 21 ) 25 - 26 - // ErrEmailNotActivated e-mail address has not been activated error 27 - var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated") 28 - 29 - // ErrEmailCharIsNotSupported e-mail address contains unsupported character 30 - type ErrEmailCharIsNotSupported struct { 31 - Email string 32 - } 33 - 34 - // IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported 35 - func IsErrEmailCharIsNotSupported(err error) bool { 36 - _, ok := err.(ErrEmailCharIsNotSupported) 37 - return ok 38 - } 39 - 40 - func (err ErrEmailCharIsNotSupported) Error() string { 41 - return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email) 42 - } 43 - 44 - func (err ErrEmailCharIsNotSupported) Unwrap() error { 45 - return util.ErrInvalidArgument 46 - } 47 - 48 - // ErrEmailInvalid represents an error where the email address does not comply with RFC 5322 49 - // or has a leading '-' character 50 - type ErrEmailInvalid struct { 51 - Email string 52 - } 53 - 54 - // IsErrEmailInvalid checks if an error is an ErrEmailInvalid 55 - func IsErrEmailInvalid(err error) bool { 56 - _, ok := err.(ErrEmailInvalid) 57 - return ok 58 - } 59 - 60 - func (err ErrEmailInvalid) Error() string { 61 - return fmt.Sprintf("e-mail invalid [email: %s]", err.Email) 62 - } 63 - 64 - func (err ErrEmailInvalid) Unwrap() error { 65 - return util.ErrInvalidArgument 66 - } 67 22 68 23 // ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error. 69 24 type ErrEmailAlreadyUsed struct { ··· 154 109 func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error { 155 110 _, err := db.GetEngine(ctx).ID(email.ID).AllCols().Update(email) 156 111 return err 157 - } 158 - 159 - var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") 160 - 161 - // ValidateEmail check if email is a valid & allowed address 162 - func ValidateEmail(email string) error { 163 - if err := validateEmailBasic(email); err != nil { 164 - return err 165 - } 166 - return validateEmailDomain(email) 167 - } 168 - 169 - // ValidateEmailForAdmin check if email is a valid address when admins manually add or edit users 170 - func ValidateEmailForAdmin(email string) error { 171 - return validateEmailBasic(email) 172 - // In this case we do not need to check the email domain 173 112 } 174 113 175 114 func GetEmailAddressByEmail(ctx context.Context, email string) (*EmailAddress, error) { ··· 462 401 463 402 return committer.Commit() 464 403 } 465 - 466 - // validateEmailBasic checks whether the email complies with the rules 467 - func validateEmailBasic(email string) error { 468 - if len(email) == 0 { 469 - return ErrEmailInvalid{email} 470 - } 471 - 472 - if !emailRegexp.MatchString(email) { 473 - return ErrEmailCharIsNotSupported{email} 474 - } 475 - 476 - if email[0] == '-' { 477 - return ErrEmailInvalid{email} 478 - } 479 - 480 - if _, err := mail.ParseAddress(email); err != nil { 481 - return ErrEmailInvalid{email} 482 - } 483 - 484 - return nil 485 - } 486 - 487 - // validateEmailDomain checks whether the email domain is allowed or blocked 488 - func validateEmailDomain(email string) error { 489 - if !IsEmailDomainAllowed(email) { 490 - return ErrEmailInvalid{email} 491 - } 492 - 493 - return nil 494 - } 495 - 496 - func IsEmailDomainAllowed(email string) bool { 497 - if len(setting.Service.EmailDomainAllowList) == 0 { 498 - return !validation.IsEmailDomainListed(setting.Service.EmailDomainBlockList, email) 499 - } 500 - 501 - return validation.IsEmailDomainListed(setting.Service.EmailDomainAllowList, email) 502 - }
-57
models/user/email_address_test.go
··· 130 130 assert.Greater(t, count, int64(len(emails))) 131 131 } 132 132 133 - func TestEmailAddressValidate(t *testing.T) { 134 - kases := map[string]error{ 135 - "abc@gmail.com": nil, 136 - "132@hotmail.com": nil, 137 - "1-3-2@test.org": nil, 138 - "1.3.2@test.org": nil, 139 - "a_123@test.org.cn": nil, 140 - `first.last@iana.org`: nil, 141 - `first!last@iana.org`: nil, 142 - `first#last@iana.org`: nil, 143 - `first$last@iana.org`: nil, 144 - `first%last@iana.org`: nil, 145 - `first&last@iana.org`: nil, 146 - `first'last@iana.org`: nil, 147 - `first*last@iana.org`: nil, 148 - `first+last@iana.org`: nil, 149 - `first/last@iana.org`: nil, 150 - `first=last@iana.org`: nil, 151 - `first?last@iana.org`: nil, 152 - `first^last@iana.org`: nil, 153 - "first`last@iana.org": nil, 154 - `first{last@iana.org`: nil, 155 - `first|last@iana.org`: nil, 156 - `first}last@iana.org`: nil, 157 - `first~last@iana.org`: nil, 158 - `first;last@iana.org`: user_model.ErrEmailCharIsNotSupported{`first;last@iana.org`}, 159 - ".233@qq.com": user_model.ErrEmailInvalid{".233@qq.com"}, 160 - "!233@qq.com": nil, 161 - "#233@qq.com": nil, 162 - "$233@qq.com": nil, 163 - "%233@qq.com": nil, 164 - "&233@qq.com": nil, 165 - "'233@qq.com": nil, 166 - "*233@qq.com": nil, 167 - "+233@qq.com": nil, 168 - "-233@qq.com": user_model.ErrEmailInvalid{"-233@qq.com"}, 169 - "/233@qq.com": nil, 170 - "=233@qq.com": nil, 171 - "?233@qq.com": nil, 172 - "^233@qq.com": nil, 173 - "_233@qq.com": nil, 174 - "`233@qq.com": nil, 175 - "{233@qq.com": nil, 176 - "|233@qq.com": nil, 177 - "}233@qq.com": nil, 178 - "~233@qq.com": nil, 179 - ";233@qq.com": user_model.ErrEmailCharIsNotSupported{";233@qq.com"}, 180 - "Foo <foo@bar.com>": user_model.ErrEmailCharIsNotSupported{"Foo <foo@bar.com>"}, 181 - string([]byte{0xE2, 0x84, 0xAA}): user_model.ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})}, 182 - } 183 - for kase, err := range kases { 184 - t.Run(kase, func(t *testing.T) { 185 - assert.EqualValues(t, err, user_model.ValidateEmail(kase)) 186 - }) 187 - } 188 - } 189 - 190 133 func TestGetActivatedEmailAddresses(t *testing.T) { 191 134 require.NoError(t, unittest.PrepareTestDatabase()) 192 135
+3 -3
models/user/user.go
··· 717 717 } 718 718 719 719 if createdByAdmin { 720 - if err := ValidateEmailForAdmin(u.Email); err != nil { 720 + if err := validation.ValidateEmailForAdmin(u.Email); err != nil { 721 721 return err 722 722 } 723 723 } else { 724 - if err := ValidateEmail(u.Email); err != nil { 724 + if err := validation.ValidateEmail(u.Email); err != nil { 725 725 return err 726 726 } 727 727 } ··· 885 885 if err := ValidateUser(&u); err != nil { 886 886 result = append(result, err.Error()) 887 887 } 888 - if err := ValidateEmail(u.Email); err != nil { 888 + if err := validation.ValidateEmail(u.Email); err != nil { 889 889 result = append(result, err.Error()) 890 890 } 891 891 return result
+2 -1
models/user/user_test.go
··· 22 22 "code.gitea.io/gitea/modules/setting" 23 23 "code.gitea.io/gitea/modules/structs" 24 24 "code.gitea.io/gitea/modules/timeutil" 25 + "code.gitea.io/gitea/modules/validation" 25 26 "code.gitea.io/gitea/tests" 26 27 27 28 "github.com/stretchr/testify/assert" ··· 320 321 321 322 err := user_model.CreateUser(db.DefaultContext, user) 322 323 require.Error(t, err) 323 - assert.True(t, user_model.IsErrEmailCharIsNotSupported(err)) 324 + assert.True(t, validation.IsErrEmailCharIsNotSupported(err)) 324 325 } 325 326 326 327 func TestCreateUserEmailAlreadyUsed(t *testing.T) {
+1 -1
modules/structs/admin_user.go
··· 15 15 FullName string `json:"full_name" binding:"MaxSize(100)"` 16 16 // required: true 17 17 // swagger:strfmt email 18 - Email string `json:"email" binding:"Required;Email;MaxSize(254)"` 18 + Email string `json:"email" binding:"Required;EmailForAdmin;MaxSize(254)"` 19 19 Password string `json:"password" binding:"MaxSize(255)"` 20 20 MustChangePassword *bool `json:"must_change_password"` 21 21 SendNotify bool `json:"send_notify"`
+1 -1
modules/structs/user_email.go
··· 7 7 // Email an email address belonging to a user 8 8 type Email struct { 9 9 // swagger:strfmt email 10 - Email string `json:"email"` 10 + Email string `json:"email" binding:"EmailWithAllowedDomain"` 11 11 Verified bool `json:"verified"` 12 12 Primary bool `json:"primary"` 13 13 UserID int64 `json:"user_id"`
+31
modules/validation/binding.go
··· 26 26 ErrUsername = "UsernameError" 27 27 // ErrInvalidGroupTeamMap is returned when a group team mapping is invalid 28 28 ErrInvalidGroupTeamMap = "InvalidGroupTeamMap" 29 + // ErrEmail is returned when an email address is invalid 30 + ErrEmail = "Email" 29 31 ) 30 32 31 33 // AddBindingRules adds additional binding rules ··· 38 40 addGlobOrRegexPatternRule() 39 41 addUsernamePatternRule() 40 42 addValidGroupTeamMapRule() 43 + addEmailBindingRules() 41 44 } 42 45 43 46 func addGitRefNameBindingRule() { ··· 180 183 return false, errs 181 184 } 182 185 186 + return true, errs 187 + }, 188 + }) 189 + } 190 + 191 + func addEmailBindingRules() { 192 + binding.AddRule(&binding.Rule{ 193 + IsMatch: func(rule string) bool { 194 + return strings.HasPrefix(rule, "EmailWithAllowedDomain") 195 + }, 196 + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { 197 + if err := ValidateEmail(fmt.Sprintf("%v", val)); err != nil { 198 + errs.Add([]string{name}, ErrEmail, err.Error()) 199 + return false, errs 200 + } 201 + return true, errs 202 + }, 203 + }) 204 + 205 + binding.AddRule(&binding.Rule{ 206 + IsMatch: func(rule string) bool { 207 + return strings.HasPrefix(rule, "EmailForAdmin") 208 + }, 209 + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { 210 + if err := ValidateEmailForAdmin(fmt.Sprintf("%v", val)); err != nil { 211 + errs.Add([]string{name}, ErrEmail, err.Error()) 212 + return false, errs 213 + } 183 214 return true, errs 184 215 }, 185 216 })
+131
modules/validation/email.go
··· 1 + // Copyright 2016 The Gogs Authors. All rights reserved. 2 + // Copyright 2020 The Gitea Authors. All rights reserved. 3 + // SPDX-License-Identifier: MIT 4 + 5 + package validation 6 + 7 + import ( 8 + "fmt" 9 + "net/mail" 10 + "regexp" 11 + "strings" 12 + 13 + "code.gitea.io/gitea/modules/setting" 14 + "code.gitea.io/gitea/modules/util" 15 + 16 + "github.com/gobwas/glob" 17 + ) 18 + 19 + // ErrEmailNotActivated e-mail address has not been activated error 20 + var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated") 21 + 22 + // ErrEmailCharIsNotSupported e-mail address contains unsupported character 23 + type ErrEmailCharIsNotSupported struct { 24 + Email string 25 + } 26 + 27 + // IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported 28 + func IsErrEmailCharIsNotSupported(err error) bool { 29 + _, ok := err.(ErrEmailCharIsNotSupported) 30 + return ok 31 + } 32 + 33 + func (err ErrEmailCharIsNotSupported) Error() string { 34 + return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email) 35 + } 36 + 37 + // ErrEmailInvalid represents an error where the email address does not comply with RFC 5322 38 + // or has a leading '-' character 39 + type ErrEmailInvalid struct { 40 + Email string 41 + } 42 + 43 + // IsErrEmailInvalid checks if an error is an ErrEmailInvalid 44 + func IsErrEmailInvalid(err error) bool { 45 + _, ok := err.(ErrEmailInvalid) 46 + return ok 47 + } 48 + 49 + func (err ErrEmailInvalid) Error() string { 50 + return fmt.Sprintf("e-mail invalid [email: %s]", err.Email) 51 + } 52 + 53 + func (err ErrEmailInvalid) Unwrap() error { 54 + return util.ErrInvalidArgument 55 + } 56 + 57 + var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") 58 + 59 + // check if email is a valid address with allowed domain 60 + func ValidateEmail(email string) error { 61 + if err := validateEmailBasic(email); err != nil { 62 + return err 63 + } 64 + return validateEmailDomain(email) 65 + } 66 + 67 + // check if email is a valid address when admins manually add or edit users 68 + func ValidateEmailForAdmin(email string) error { 69 + return validateEmailBasic(email) 70 + // In this case we do not need to check the email domain 71 + } 72 + 73 + // validateEmailBasic checks whether the email complies with the rules 74 + func validateEmailBasic(email string) error { 75 + if len(email) == 0 { 76 + return ErrEmailInvalid{email} 77 + } 78 + 79 + if !emailRegexp.MatchString(email) { 80 + return ErrEmailCharIsNotSupported{email} 81 + } 82 + 83 + if email[0] == '-' { 84 + return ErrEmailInvalid{email} 85 + } 86 + 87 + if _, err := mail.ParseAddress(email); err != nil { 88 + return ErrEmailInvalid{email} 89 + } 90 + 91 + return nil 92 + } 93 + 94 + func validateEmailDomain(email string) error { 95 + if !IsEmailDomainAllowed(email) { 96 + return ErrEmailInvalid{email} 97 + } 98 + 99 + return nil 100 + } 101 + 102 + func IsEmailDomainAllowed(email string) bool { 103 + if len(setting.Service.EmailDomainAllowList) == 0 { 104 + return !isEmailDomainListed(setting.Service.EmailDomainBlockList, email) 105 + } 106 + 107 + return isEmailDomainListed(setting.Service.EmailDomainAllowList, email) 108 + } 109 + 110 + // isEmailDomainListed checks whether the domain of an email address 111 + // matches a list of domains 112 + func isEmailDomainListed(globs []glob.Glob, email string) bool { 113 + if len(globs) == 0 { 114 + return false 115 + } 116 + 117 + n := strings.LastIndex(email, "@") 118 + if n <= 0 { 119 + return false 120 + } 121 + 122 + domain := strings.ToLower(email[n+1:]) 123 + 124 + for _, g := range globs { 125 + if g.Match(domain) { 126 + return true 127 + } 128 + } 129 + 130 + return false 131 + }
+67
modules/validation/email_test.go
··· 1 + // Copyright 2017 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package validation 5 + 6 + import ( 7 + "testing" 8 + 9 + "github.com/stretchr/testify/assert" 10 + ) 11 + 12 + func TestEmailAddressValidate(t *testing.T) { 13 + kases := map[string]error{ 14 + "abc@gmail.com": nil, 15 + "132@hotmail.com": nil, 16 + "1-3-2@test.org": nil, 17 + "1.3.2@test.org": nil, 18 + "a_123@test.org.cn": nil, 19 + `first.last@iana.org`: nil, 20 + `first!last@iana.org`: nil, 21 + `first#last@iana.org`: nil, 22 + `first$last@iana.org`: nil, 23 + `first%last@iana.org`: nil, 24 + `first&last@iana.org`: nil, 25 + `first'last@iana.org`: nil, 26 + `first*last@iana.org`: nil, 27 + `first+last@iana.org`: nil, 28 + `first/last@iana.org`: nil, 29 + `first=last@iana.org`: nil, 30 + `first?last@iana.org`: nil, 31 + `first^last@iana.org`: nil, 32 + "first`last@iana.org": nil, 33 + `first{last@iana.org`: nil, 34 + `first|last@iana.org`: nil, 35 + `first}last@iana.org`: nil, 36 + `first~last@iana.org`: nil, 37 + `first;last@iana.org`: ErrEmailCharIsNotSupported{`first;last@iana.org`}, 38 + ".233@qq.com": ErrEmailInvalid{".233@qq.com"}, 39 + "!233@qq.com": nil, 40 + "#233@qq.com": nil, 41 + "$233@qq.com": nil, 42 + "%233@qq.com": nil, 43 + "&233@qq.com": nil, 44 + "'233@qq.com": nil, 45 + "*233@qq.com": nil, 46 + "+233@qq.com": nil, 47 + "-233@qq.com": ErrEmailInvalid{"-233@qq.com"}, 48 + "/233@qq.com": nil, 49 + "=233@qq.com": nil, 50 + "?233@qq.com": nil, 51 + "^233@qq.com": nil, 52 + "_233@qq.com": nil, 53 + "`233@qq.com": nil, 54 + "{233@qq.com": nil, 55 + "|233@qq.com": nil, 56 + "}233@qq.com": nil, 57 + "~233@qq.com": nil, 58 + ";233@qq.com": ErrEmailCharIsNotSupported{";233@qq.com"}, 59 + "Foo <foo@bar.com>": ErrEmailCharIsNotSupported{"Foo <foo@bar.com>"}, 60 + string([]byte{0xE2, 0x84, 0xAA}): ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})}, 61 + } 62 + for kase, err := range kases { 63 + t.Run(kase, func(t *testing.T) { 64 + assert.EqualValues(t, err, ValidateEmail(kase)) 65 + }) 66 + } 67 + }
-25
modules/validation/helpers.go
··· 10 10 "strings" 11 11 12 12 "code.gitea.io/gitea/modules/setting" 13 - 14 - "github.com/gobwas/glob" 15 13 ) 16 14 17 15 var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`) ··· 47 45 return true 48 46 } 49 47 } 50 - return false 51 - } 52 - 53 - // IsEmailDomainListed checks whether the domain of an email address 54 - // matches a list of domains 55 - func IsEmailDomainListed(globs []glob.Glob, email string) bool { 56 - if len(globs) == 0 { 57 - return false 58 - } 59 - 60 - n := strings.LastIndex(email, "@") 61 - if n <= 0 { 62 - return false 63 - } 64 - 65 - domain := strings.ToLower(email[n+1:]) 66 - 67 - for _, g := range globs { 68 - if g.Match(domain) { 69 - return true 70 - } 71 - } 72 - 73 48 return false 74 49 } 75 50
+2
modules/web/middleware/binding.go
··· 143 143 } 144 144 case validation.ErrInvalidGroupTeamMap: 145 145 data["ErrorMsg"] = trName + l.TrString("form.invalid_group_team_map_error", errs[0].Message) 146 + case validation.ErrEmail: 147 + data["ErrorMsg"] = trName + l.TrString("form.email_error") 146 148 default: 147 149 msg := errs[0].Classification 148 150 if msg != "" && errs[0].Message != "" {
+4 -4
routers/api/v1/activitypub/repository_test.go
··· 6 6 import ( 7 7 "testing" 8 8 9 - "code.gitea.io/gitea/models/user" 9 + "code.gitea.io/gitea/modules/validation" 10 10 ) 11 11 12 12 func Test_UserEmailValidate(t *testing.T) { 13 13 sut := "ab@cd.ef" 14 - if err := user.ValidateEmail(sut); err != nil { 14 + if err := validation.ValidateEmail(sut); err != nil { 15 15 t.Errorf("sut should be valid, %v, %v", sut, err) 16 16 } 17 17 18 18 sut = "83ce13c8-af0b-4112-8327-55a54e54e664@code.cartoon-aa.xyz" 19 - if err := user.ValidateEmail(sut); err != nil { 19 + if err := validation.ValidateEmail(sut); err != nil { 20 20 t.Errorf("sut should be valid, %v, %v", sut, err) 21 21 } 22 22 23 23 sut = "1" 24 - if err := user.ValidateEmail(sut); err == nil { 24 + if err := validation.ValidateEmail(sut); err == nil { 25 25 t.Errorf("sut should not be valid, %v", sut) 26 26 } 27 27 }
+6 -5
routers/api/v1/admin/user.go
··· 20 20 "code.gitea.io/gitea/modules/setting" 21 21 api "code.gitea.io/gitea/modules/structs" 22 22 "code.gitea.io/gitea/modules/timeutil" 23 + "code.gitea.io/gitea/modules/validation" 23 24 "code.gitea.io/gitea/modules/web" 24 25 "code.gitea.io/gitea/routers/api/v1/user" 25 26 "code.gitea.io/gitea/routers/api/v1/utils" ··· 138 139 user_model.IsErrEmailAlreadyUsed(err) || 139 140 db.IsErrNameReserved(err) || 140 141 db.IsErrNameCharsNotAllowed(err) || 141 - user_model.IsErrEmailCharIsNotSupported(err) || 142 - user_model.IsErrEmailInvalid(err) || 142 + validation.IsErrEmailCharIsNotSupported(err) || 143 + validation.IsErrEmailInvalid(err) || 143 144 db.IsErrNamePatternNotAllowed(err) { 144 145 ctx.Error(http.StatusUnprocessableEntity, "", err) 145 146 } else { ··· 148 149 return 149 150 } 150 151 151 - if !user_model.IsEmailDomainAllowed(u.Email) { 152 + if !validation.IsEmailDomainAllowed(u.Email) { 152 153 ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", u.Email)) 153 154 } 154 155 ··· 224 225 if form.Email != nil { 225 226 if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, ctx.ContextUser, *form.Email); err != nil { 226 227 switch { 227 - case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err): 228 + case validation.IsErrEmailCharIsNotSupported(err), validation.IsErrEmailInvalid(err): 228 229 ctx.Error(http.StatusBadRequest, "EmailInvalid", err) 229 230 case user_model.IsErrEmailAlreadyUsed(err): 230 231 ctx.Error(http.StatusBadRequest, "EmailUsed", err) ··· 234 235 return 235 236 } 236 237 237 - if !user_model.IsEmailDomainAllowed(*form.Email) { 238 + if !validation.IsEmailDomainAllowed(*form.Email) { 238 239 ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", *form.Email)) 239 240 } 240 241 }
+4 -3
routers/api/v1/user/email.go
··· 9 9 10 10 user_model "code.gitea.io/gitea/models/user" 11 11 api "code.gitea.io/gitea/modules/structs" 12 + "code.gitea.io/gitea/modules/validation" 12 13 "code.gitea.io/gitea/modules/web" 13 14 "code.gitea.io/gitea/services/context" 14 15 "code.gitea.io/gitea/services/convert" ··· 66 67 if err := user_service.AddEmailAddresses(ctx, ctx.Doer, form.Emails); err != nil { 67 68 if user_model.IsErrEmailAlreadyUsed(err) { 68 69 ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email) 69 - } else if user_model.IsErrEmailCharIsNotSupported(err) || user_model.IsErrEmailInvalid(err) { 70 + } else if validation.IsErrEmailCharIsNotSupported(err) || validation.IsErrEmailInvalid(err) { 70 71 email := "" 71 - if typedError, ok := err.(user_model.ErrEmailInvalid); ok { 72 + if typedError, ok := err.(validation.ErrEmailInvalid); ok { 72 73 email = typedError.Email 73 74 } 74 - if typedError, ok := err.(user_model.ErrEmailCharIsNotSupported); ok { 75 + if typedError, ok := err.(validation.ErrEmailCharIsNotSupported); ok { 75 76 email = typedError.Email 76 77 } 77 78
+5 -4
routers/web/admin/users.go
··· 23 23 "code.gitea.io/gitea/modules/optional" 24 24 "code.gitea.io/gitea/modules/setting" 25 25 "code.gitea.io/gitea/modules/util" 26 + "code.gitea.io/gitea/modules/validation" 26 27 "code.gitea.io/gitea/modules/web" 27 28 "code.gitea.io/gitea/routers/web/explore" 28 29 user_setting "code.gitea.io/gitea/routers/web/user/setting" ··· 185 186 case user_model.IsErrEmailAlreadyUsed(err): 186 187 ctx.Data["Err_Email"] = true 187 188 ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form) 188 - case user_model.IsErrEmailInvalid(err), user_model.IsErrEmailCharIsNotSupported(err): 189 + case validation.IsErrEmailInvalid(err), validation.IsErrEmailCharIsNotSupported(err): 189 190 ctx.Data["Err_Email"] = true 190 191 ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form) 191 192 case db.IsErrNameReserved(err): ··· 203 204 return 204 205 } 205 206 206 - if !user_model.IsEmailDomainAllowed(u.Email) { 207 + if !validation.IsEmailDomainAllowed(u.Email) { 207 208 ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", u.Email)) 208 209 } 209 210 ··· 414 415 if form.Email != "" { 415 416 if err := user_service.AdminAddOrSetPrimaryEmailAddress(ctx, u, form.Email); err != nil { 416 417 switch { 417 - case user_model.IsErrEmailCharIsNotSupported(err), user_model.IsErrEmailInvalid(err): 418 + case validation.IsErrEmailCharIsNotSupported(err), validation.IsErrEmailInvalid(err): 418 419 ctx.Data["Err_Email"] = true 419 420 ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form) 420 421 case user_model.IsErrEmailAlreadyUsed(err): ··· 425 426 } 426 427 return 427 428 } 428 - if !user_model.IsEmailDomainAllowed(form.Email) { 429 + if !validation.IsEmailDomainAllowed(form.Email) { 429 430 ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", form.Email)) 430 431 } 431 432 }
+3 -2
routers/web/auth/auth.go
··· 25 25 "code.gitea.io/gitea/modules/setting" 26 26 "code.gitea.io/gitea/modules/timeutil" 27 27 "code.gitea.io/gitea/modules/util" 28 + "code.gitea.io/gitea/modules/validation" 28 29 "code.gitea.io/gitea/modules/web" 29 30 "code.gitea.io/gitea/modules/web/middleware" 30 31 auth_service "code.gitea.io/gitea/services/auth" ··· 575 576 case user_model.IsErrEmailAlreadyUsed(err): 576 577 ctx.Data["Err_Email"] = true 577 578 ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form) 578 - case user_model.IsErrEmailCharIsNotSupported(err): 579 + case validation.IsErrEmailCharIsNotSupported(err): 579 580 ctx.Data["Err_Email"] = true 580 581 ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) 581 - case user_model.IsErrEmailInvalid(err): 582 + case validation.IsErrEmailInvalid(err): 582 583 ctx.Data["Err_Email"] = true 583 584 ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) 584 585 case db.IsErrNameReserved(err):
+2 -1
routers/web/org/teams.go
··· 22 22 "code.gitea.io/gitea/modules/base" 23 23 "code.gitea.io/gitea/modules/log" 24 24 "code.gitea.io/gitea/modules/setting" 25 + "code.gitea.io/gitea/modules/validation" 25 26 "code.gitea.io/gitea/modules/web" 26 27 shared_user "code.gitea.io/gitea/routers/web/shared/user" 27 28 "code.gitea.io/gitea/services/context" ··· 131 132 u, err = user_model.GetUserByName(ctx, uname) 132 133 if err != nil { 133 134 if user_model.IsErrUserNotExist(err) { 134 - if setting.MailService != nil && user_model.ValidateEmail(uname) == nil { 135 + if setting.MailService != nil && validation.ValidateEmail(uname) == nil { 135 136 if err := org_service.CreateTeamInvite(ctx, ctx.Doer, ctx.Org.Team, uname); err != nil { 136 137 if org_model.IsErrTeamInviteAlreadyExist(err) { 137 138 ctx.Flash.Error(ctx.Tr("form.duplicate_invite_to_team"))
+2 -1
routers/web/user/setting/account.go
··· 17 17 "code.gitea.io/gitea/modules/optional" 18 18 "code.gitea.io/gitea/modules/setting" 19 19 "code.gitea.io/gitea/modules/timeutil" 20 + "code.gitea.io/gitea/modules/validation" 20 21 "code.gitea.io/gitea/modules/web" 21 22 "code.gitea.io/gitea/services/auth" 22 23 "code.gitea.io/gitea/services/auth/source/db" ··· 205 206 loadAccountData(ctx) 206 207 207 208 ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) 208 - } else if user_model.IsErrEmailCharIsNotSupported(err) || user_model.IsErrEmailInvalid(err) { 209 + } else if validation.IsErrEmailCharIsNotSupported(err) || validation.IsErrEmailInvalid(err) { 209 210 loadAccountData(ctx) 210 211 211 212 ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form)
+3 -2
services/auth/source/pam/source_authenticate.go
··· 13 13 "code.gitea.io/gitea/modules/auth/pam" 14 14 "code.gitea.io/gitea/modules/optional" 15 15 "code.gitea.io/gitea/modules/setting" 16 + "code.gitea.io/gitea/modules/validation" 16 17 17 18 "github.com/google/uuid" 18 19 ) ··· 39 40 if idx > -1 { 40 41 username = pamLogin[:idx] 41 42 } 42 - if user_model.ValidateEmail(email) != nil { 43 + if validation.ValidateEmail(email) != nil { 43 44 if source.EmailDomain != "" { 44 45 email = fmt.Sprintf("%s@%s", username, source.EmailDomain) 45 46 } else { 46 47 email = fmt.Sprintf("%s@%s", username, setting.Service.NoReplyAddress) 47 48 } 48 - if user_model.ValidateEmail(email) != nil { 49 + if validation.ValidateEmail(email) != nil { 49 50 email = uuid.New().String() + "@localhost" 50 51 } 51 52 }
+3 -2
services/doctor/breaking.go
··· 10 10 "code.gitea.io/gitea/models/db" 11 11 "code.gitea.io/gitea/models/user" 12 12 "code.gitea.io/gitea/modules/log" 13 + "code.gitea.io/gitea/modules/validation" 13 14 14 15 "xorm.io/builder" 15 16 ) ··· 31 32 func checkUserEmail(ctx context.Context, logger log.Logger, _ bool) error { 32 33 // We could use quirky SQL to get all users that start without a [a-zA-Z0-9], but that would mean 33 34 // DB provider-specific SQL and only works _now_. So instead we iterate through all user accounts 34 - // and use the user.ValidateEmail function to be future-proof. 35 + // and use the validation.ValidateEmail function to be future-proof. 35 36 var invalidUserCount int64 36 37 if err := iterateUserAccounts(ctx, func(u *user.User) error { 37 38 // Only check for users, skip ··· 39 40 return nil 40 41 } 41 42 42 - if err := user.ValidateEmail(u.Email); err != nil { 43 + if err := validation.ValidateEmail(u.Email); err != nil { 43 44 invalidUserCount++ 44 45 logger.Warn("User[id=%d name=%q] have not a valid e-mail: %v", u.ID, u.Name, err) 45 46 }
+2 -2
services/forms/admin.go
··· 18 18 LoginType string `binding:"Required"` 19 19 LoginName string 20 20 UserName string `binding:"Required;Username;MaxSize(40)"` 21 - Email string `binding:"Required;Email;MaxSize(254)"` 21 + Email string `binding:"Required;EmailForAdmin;MaxSize(254)"` 22 22 Password string `binding:"MaxSize(255)"` 23 23 SendNotify bool 24 24 MustChangePassword bool ··· 37 37 UserName string `binding:"Username;MaxSize(40)"` 38 38 LoginName string 39 39 FullName string `binding:"MaxSize(100)"` 40 - Email string `binding:"Required;Email;MaxSize(254)"` 40 + Email string `binding:"Required;EmailForAdmin;MaxSize(254)"` 41 41 Password string `binding:"MaxSize(255)"` 42 42 Website string `binding:"ValidUrl;MaxSize(255)"` 43 43 Location string `binding:"MaxSize(50)"`
+4 -4
services/forms/user_form.go
··· 10 10 "strings" 11 11 12 12 auth_model "code.gitea.io/gitea/models/auth" 13 - user_model "code.gitea.io/gitea/models/user" 14 13 "code.gitea.io/gitea/modules/setting" 15 14 "code.gitea.io/gitea/modules/structs" 15 + "code.gitea.io/gitea/modules/validation" 16 16 "code.gitea.io/gitea/modules/web/middleware" 17 17 "code.gitea.io/gitea/services/context" 18 18 ··· 110 110 // domains in the whitelist or if it doesn't match any of 111 111 // domains in the blocklist, if any such list is not empty. 112 112 func (f *RegisterForm) IsEmailDomainAllowed() bool { 113 - return user_model.IsEmailDomainAllowed(f.Email) 113 + return validation.IsEmailDomainAllowed(f.Email) 114 114 } 115 115 116 116 // MustChangePasswordForm form for updating your password after account creation ··· 258 258 type AvatarForm struct { 259 259 Source string 260 260 Avatar *multipart.FileHeader 261 - Gravatar string `binding:"OmitEmpty;Email;MaxSize(254)"` 261 + Gravatar string `binding:"OmitEmpty;EmailWithAllowedDomain;MaxSize(254)"` 262 262 Federavatar bool 263 263 } 264 264 ··· 270 270 271 271 // AddEmailForm form for adding new email 272 272 type AddEmailForm struct { 273 - Email string `binding:"Required;Email;MaxSize(254)"` 273 + Email string `binding:"Required;EmailWithAllowedDomain;MaxSize(254)"` 274 274 } 275 275 276 276 // Validate validates the fields
+1 -1
services/forms/user_form_auth_openid.go
··· 27 27 // SignUpOpenIDForm form for signin up with OpenID 28 28 type SignUpOpenIDForm struct { 29 29 UserName string `binding:"Required;Username;MaxSize(40)"` 30 - Email string `binding:"Required;Email;MaxSize(254)"` 30 + Email string `binding:"Required;EmailWithAllowedDomain;MaxSize(254)"` 31 31 } 32 32 33 33 // Validate validates the fields
+4 -3
services/user/email.go
··· 12 12 user_model "code.gitea.io/gitea/models/user" 13 13 "code.gitea.io/gitea/modules/setting" 14 14 "code.gitea.io/gitea/modules/util" 15 + "code.gitea.io/gitea/modules/validation" 15 16 "code.gitea.io/gitea/services/mailer" 16 17 ) 17 18 ··· 21 22 return nil 22 23 } 23 24 24 - if err := user_model.ValidateEmailForAdmin(emailStr); err != nil { 25 + if err := validation.ValidateEmailForAdmin(emailStr); err != nil { 25 26 return err 26 27 } 27 28 ··· 74 75 return nil 75 76 } 76 77 77 - if err := user_model.ValidateEmail(emailStr); err != nil { 78 + if err := validation.ValidateEmail(emailStr); err != nil { 78 79 return err 79 80 } 80 81 ··· 119 120 120 121 func AddEmailAddresses(ctx context.Context, u *user_model.User, emails []string) error { 121 122 for _, emailStr := range emails { 122 - if err := user_model.ValidateEmail(emailStr); err != nil { 123 + if err := validation.ValidateEmail(emailStr); err != nil { 123 124 return err 124 125 } 125 126