Template
1
0
Fork 0
mirror of https://codeberg.org/forgejo/forgejo synced 2024-11-22 01:44:24 +01:00

fix merge errors

This commit is contained in:
Michael Jerger 2024-11-20 08:54:10 +01:00
parent 109d09de1a
commit a5caf6a1c1
3 changed files with 68 additions and 252 deletions

View file

@ -7,66 +7,17 @@ package user
import ( import (
"context" "context"
"fmt" "fmt"
"net/mail"
"net/url"
"regexp"
"strings" "strings"
"time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"github.com/gobwas/glob"
"xorm.io/builder" "xorm.io/builder"
) )
// ErrEmailNotActivated e-mail address has not been activated error
var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated")
// ErrEmailCharIsNotSupported e-mail address contains unsupported character
type ErrEmailCharIsNotSupported struct {
Email string
}
// IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported
func IsErrEmailCharIsNotSupported(err error) bool {
_, ok := err.(ErrEmailCharIsNotSupported)
return ok
}
func (err ErrEmailCharIsNotSupported) Error() string {
return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email)
}
func (err ErrEmailCharIsNotSupported) Unwrap() error {
return util.ErrInvalidArgument
}
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322
// or has a leading '-' character
type ErrEmailInvalid struct {
Email string
}
// IsErrEmailInvalid checks if an error is an ErrEmailInvalid
func IsErrEmailInvalid(err error) bool {
_, ok := err.(ErrEmailInvalid)
return ok
}
func (err ErrEmailInvalid) Error() string {
return fmt.Sprintf("e-mail invalid [email: %s]", err.Email)
}
func (err ErrEmailInvalid) Unwrap() error {
return util.ErrInvalidArgument
}
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error. // ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error.
type ErrEmailAlreadyUsed struct { type ErrEmailAlreadyUsed struct {
Email string Email string
@ -158,22 +109,6 @@ func UpdateEmailAddress(ctx context.Context, email *EmailAddress) error {
return err return err
} }
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])?)*$")
// ValidateEmail check if email is a valid & allowed address
func ValidateEmail(email string) error {
if err := validateEmailBasic(email); err != nil {
return err
}
return validateEmailDomain(email)
}
// ValidateEmailForAdmin check if email is a valid address when admins manually add or edit users
func ValidateEmailForAdmin(email string) error {
return validateEmailBasic(email)
// In this case we do not need to check the email domain
}
func GetEmailAddressByEmail(ctx context.Context, email string) (*EmailAddress, error) { func GetEmailAddressByEmail(ctx context.Context, email string) (*EmailAddress, error) {
ea := &EmailAddress{} ea := &EmailAddress{}
if has, err := db.GetEngine(ctx).Where("lower_email=?", strings.ToLower(email)).Get(ea); err != nil { if has, err := db.GetEngine(ctx).Where("lower_email=?", strings.ToLower(email)).Get(ea); err != nil {
@ -309,23 +244,6 @@ func updateActivation(ctx context.Context, email *EmailAddress, activate bool) e
return UpdateUserCols(ctx, user, "rands") return UpdateUserCols(ctx, user, "rands")
} }
// VerifyActiveEmailCode verifies active email code when active account
func VerifyActiveEmailCode(ctx context.Context, code, email string) *EmailAddress {
if user := GetVerifyUser(ctx, code); user != nil {
// time limit code
prefix := code[:base.TimeLimitCodeLength]
data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands)
if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
emailAddress := &EmailAddress{UID: user.ID, Email: email}
if has, _ := db.GetEngine(ctx).Get(emailAddress); has {
return emailAddress
}
}
}
return nil
}
// SearchEmailOrderBy is used to sort the results from SearchEmails() // SearchEmailOrderBy is used to sort the results from SearchEmails()
type SearchEmailOrderBy string type SearchEmailOrderBy string
@ -464,70 +382,3 @@ func ActivateUserEmail(ctx context.Context, userID int64, email string, activate
return committer.Commit() return committer.Commit()
} }
// validateEmailBasic checks whether the email complies with the rules
func validateEmailBasic(email string) error {
if len(email) == 0 {
return ErrEmailInvalid{email}
}
if !emailRegexp.MatchString(email) {
return ErrEmailCharIsNotSupported{email}
}
if email[0] == '-' {
return ErrEmailInvalid{email}
}
if _, err := mail.ParseAddress(email); err != nil {
return ErrEmailInvalid{email}
}
return nil
}
// validateEmailDomain checks whether the email domain is allowed or blocked
func validateEmailDomain(email string) error {
if !IsEmailDomainAllowed(email) {
return ErrEmailInvalid{email}
}
return nil
}
func IsEmailDomainAllowed(email string) bool {
return IsEmailDomainAllowedInternal(
email,
setting.Service.EmailDomainAllowList,
setting.Service.EmailDomainBlockList,
setting.Federation.Enabled,
setting.AppURL)
}
func IsEmailDomainAllowedInternal(
email string,
emailDomainAllowList []glob.Glob,
emailDomainBlockList []glob.Glob,
isFederation bool,
fqdn string,
) bool {
var result bool
if len(emailDomainAllowList) == 0 {
result = !validation.IsEmailDomainListed(emailDomainBlockList, email)
} else if isFederation {
localFqdn, err := url.ParseRequestURI(fqdn)
if err != nil {
return false
}
globber, err := glob.Compile(localFqdn.Hostname(), ',')
if err != nil {
return false
}
emailDomainAllowList = append(emailDomainAllowList, globber)
result = validation.IsEmailDomainListed(emailDomainAllowList, email)
} else {
result = validation.IsEmailDomainListed(emailDomainAllowList, email)
}
return result
}

View file

@ -12,54 +12,10 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"github.com/gobwas/glob"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestEmailDomainAllowList(t *testing.T) {
res := user_model.IsEmailDomainAllowed("someuser@localhost.localdomain")
assert.True(t, res)
}
func TestEmailDomainAllowListInternal(t *testing.T) {
domain, _ := glob.Compile("domain.de", ',')
emailDomainAllowList := []glob.Glob{domain}
emailDomainBlockList := []glob.Glob{}
res := user_model.IsEmailDomainAllowedInternal(
"user@repo.domain.de",
emailDomainAllowList,
emailDomainBlockList,
false,
"https://repo.domain.de")
assert.False(t, res)
res = user_model.IsEmailDomainAllowedInternal(
"user@repo.domain.de",
emailDomainAllowList,
emailDomainBlockList,
true,
"xttps://repo")
assert.False(t, res)
res = user_model.IsEmailDomainAllowedInternal(
"user@repo.Domain.de",
emailDomainAllowList,
emailDomainBlockList,
true,
"https://repo.domain.de")
assert.True(t, res)
res = user_model.IsEmailDomainAllowedInternal(
"user@repo.domain.de",
emailDomainAllowList,
emailDomainBlockList,
true,
"https://repo.domain.de")
assert.True(t, res)
}
func TestGetEmailAddresses(t *testing.T) { func TestGetEmailAddresses(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())
@ -173,64 +129,6 @@ func TestListEmails(t *testing.T) {
assert.Len(t, emails, 5) assert.Len(t, emails, 5)
assert.Greater(t, count, int64(len(emails))) assert.Greater(t, count, int64(len(emails)))
} }
func TestEmailAddressValidate(t *testing.T) {
kases := map[string]error{
"abc@gmail.com": nil,
"132@hotmail.com": nil,
"1-3-2@test.org": nil,
"1.3.2@test.org": nil,
"a_123@test.org.cn": nil,
`first.last@iana.org`: nil,
`first!last@iana.org`: nil,
`first#last@iana.org`: nil,
`first$last@iana.org`: nil,
`first%last@iana.org`: nil,
`first&last@iana.org`: nil,
`first'last@iana.org`: nil,
`first*last@iana.org`: nil,
`first+last@iana.org`: nil,
`first/last@iana.org`: nil,
`first=last@iana.org`: nil,
`first?last@iana.org`: nil,
`first^last@iana.org`: nil,
"first`last@iana.org": nil,
`first{last@iana.org`: nil,
`first|last@iana.org`: nil,
`first}last@iana.org`: nil,
`first~last@iana.org`: nil,
`first;last@iana.org`: user_model.ErrEmailCharIsNotSupported{`first;last@iana.org`},
".233@qq.com": user_model.ErrEmailInvalid{".233@qq.com"},
"!233@qq.com": nil,
"#233@qq.com": nil,
"$233@qq.com": nil,
"%233@qq.com": nil,
"&233@qq.com": nil,
"'233@qq.com": nil,
"*233@qq.com": nil,
"+233@qq.com": nil,
"-233@qq.com": user_model.ErrEmailInvalid{"-233@qq.com"},
"/233@qq.com": nil,
"=233@qq.com": nil,
"?233@qq.com": nil,
"^233@qq.com": nil,
"_233@qq.com": nil,
"`233@qq.com": nil,
"{233@qq.com": nil,
"|233@qq.com": nil,
"}233@qq.com": nil,
"~233@qq.com": nil,
";233@qq.com": user_model.ErrEmailCharIsNotSupported{";233@qq.com"},
"Foo <foo@bar.com>": user_model.ErrEmailCharIsNotSupported{"Foo <foo@bar.com>"},
string([]byte{0xE2, 0x84, 0xAA}): user_model.ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})},
}
for kase, err := range kases {
t.Run(kase, func(t *testing.T) {
assert.EqualValues(t, err, user_model.ValidateEmail(kase))
})
}
}
func TestGetActivatedEmailAddresses(t *testing.T) { func TestGetActivatedEmailAddresses(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase()) require.NoError(t, unittest.PrepareTestDatabase())

View file

@ -7,6 +7,7 @@ package user_test
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"encoding/hex"
"fmt" "fmt"
"strings" "strings"
"testing" "testing"
@ -21,7 +22,10 @@ import (
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -321,7 +325,7 @@ func TestCreateUserInvalidEmail(t *testing.T) {
err := user_model.CreateUser(db.DefaultContext, user) err := user_model.CreateUser(db.DefaultContext, user)
require.Error(t, err) require.Error(t, err)
assert.True(t, user_model.IsErrEmailCharIsNotSupported(err)) assert.True(t, validation.IsErrEmailCharIsNotSupported(err))
} }
func TestCreateUserEmailAlreadyUsed(t *testing.T) { func TestCreateUserEmailAlreadyUsed(t *testing.T) {
@ -603,6 +607,69 @@ func Test_ValidateUser(t *testing.T) {
} }
} }
func TestGenerateEmailAuthorizationCode(t *testing.T) {
defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 2)()
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
code, err := user.GenerateEmailAuthorizationCode(db.DefaultContext, auth.UserActivation)
require.NoError(t, err)
lookupKey, validator, ok := strings.Cut(code, ":")
assert.True(t, ok)
rawValidator, err := hex.DecodeString(validator)
require.NoError(t, err)
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
require.NoError(t, err)
assert.False(t, authToken.IsExpired())
assert.EqualValues(t, authToken.HashedValidator, auth.HashValidator(rawValidator))
authToken.Expiry = authToken.Expiry.Add(-int64(setting.Service.ActiveCodeLives) * 60)
assert.True(t, authToken.IsExpired())
}
func TestVerifyUserAuthorizationToken(t *testing.T) {
defer test.MockVariableValue(&setting.Service.ActiveCodeLives, 2)()
require.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
code, err := user.GenerateEmailAuthorizationCode(db.DefaultContext, auth.UserActivation)
require.NoError(t, err)
lookupKey, _, ok := strings.Cut(code, ":")
assert.True(t, ok)
t.Run("Wrong purpose", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.PasswordReset, false)
require.NoError(t, err)
assert.Nil(t, u)
})
t.Run("No delete", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, false)
require.NoError(t, err)
assert.EqualValues(t, user.ID, u.ID)
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
require.NoError(t, err)
assert.NotNil(t, authToken)
})
t.Run("Delete", func(t *testing.T) {
u, err := user_model.VerifyUserAuthorizationToken(db.DefaultContext, code, auth.UserActivation, true)
require.NoError(t, err)
assert.EqualValues(t, user.ID, u.ID)
authToken, err := auth.FindAuthToken(db.DefaultContext, lookupKey, auth.UserActivation)
require.ErrorIs(t, err, util.ErrNotExist)
assert.Nil(t, authToken)
})
}
func Test_NormalizeUserFromEmail(t *testing.T) { func Test_NormalizeUserFromEmail(t *testing.T) {
oldSetting := setting.Service.AllowDotsInUsernames oldSetting := setting.Service.AllowDotsInUsernames
defer func() { defer func() {