* Move EmailAddress into models/user/ * Fix test * rename user_mail to user_email * Fix test * Move UserRedirect into models/user/ * Fix lint & test * Fix lint * Fix lint * remove nolint comment * Fix linttags/v1.16.0-rc1
@@ -123,21 +123,6 @@ func (err ErrUserNotExist) Error() string { | |||
return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID) | |||
} | |||
// ErrUserRedirectNotExist represents a "UserRedirectNotExist" kind of error. | |||
type ErrUserRedirectNotExist struct { | |||
Name string | |||
} | |||
// IsErrUserRedirectNotExist check if an error is an ErrUserRedirectNotExist. | |||
func IsErrUserRedirectNotExist(err error) bool { | |||
_, ok := err.(ErrUserRedirectNotExist) | |||
return ok | |||
} | |||
func (err ErrUserRedirectNotExist) Error() string { | |||
return fmt.Sprintf("user redirect does not exist [name: %s]", err.Name) | |||
} | |||
// ErrUserProhibitLogin represents a "ErrUserProhibitLogin" kind of error. | |||
type ErrUserProhibitLogin struct { | |||
UID int64 | |||
@@ -170,66 +155,6 @@ func (err ErrUserInactive) Error() string { | |||
return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name) | |||
} | |||
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error. | |||
type ErrEmailAlreadyUsed struct { | |||
Email string | |||
} | |||
// IsErrEmailAlreadyUsed checks if an error is a ErrEmailAlreadyUsed. | |||
func IsErrEmailAlreadyUsed(err error) bool { | |||
_, ok := err.(ErrEmailAlreadyUsed) | |||
return ok | |||
} | |||
func (err ErrEmailAlreadyUsed) Error() string { | |||
return fmt.Sprintf("e-mail already in use [email: %s]", err.Email) | |||
} | |||
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322 | |||
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) | |||
} | |||
// ErrEmailAddressNotExist email address not exist | |||
type ErrEmailAddressNotExist struct { | |||
Email string | |||
} | |||
// IsErrEmailAddressNotExist checks if an error is an ErrEmailAddressNotExist | |||
func IsErrEmailAddressNotExist(err error) bool { | |||
_, ok := err.(ErrEmailAddressNotExist) | |||
return ok | |||
} | |||
func (err ErrEmailAddressNotExist) Error() string { | |||
return fmt.Sprintf("Email address does not exist [email: %s]", err.Email) | |||
} | |||
// ErrPrimaryEmailCannotDelete primary email address cannot be deleted | |||
type ErrPrimaryEmailCannotDelete struct { | |||
Email string | |||
} | |||
// IsErrPrimaryEmailCannotDelete checks if an error is an ErrPrimaryEmailCannotDelete | |||
func IsErrPrimaryEmailCannotDelete(err error) bool { | |||
_, ok := err.(ErrPrimaryEmailCannotDelete) | |||
return ok | |||
} | |||
func (err ErrPrimaryEmailCannotDelete) Error() string { | |||
return fmt.Sprintf("Primary email address cannot be deleted [email: %s]", err.Email) | |||
} | |||
// ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error. | |||
type ErrOpenIDAlreadyUsed struct { | |||
OpenID string |
@@ -10,6 +10,7 @@ import ( | |||
"time" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
@@ -36,7 +37,7 @@ type GPGKey struct { | |||
ExpiredUnix timeutil.TimeStamp | |||
AddedUnix timeutil.TimeStamp | |||
SubsKey []*GPGKey `xorm:"-"` | |||
Emails []*EmailAddress | |||
Emails []*user_model.EmailAddress | |||
Verified bool `xorm:"NOT NULL DEFAULT false"` | |||
CanSign bool | |||
CanEncryptComms bool | |||
@@ -148,12 +149,12 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity, verified bool) (*GPGKey, erro | |||
} | |||
// Check emails | |||
userEmails, err := GetEmailAddresses(ownerID) | |||
userEmails, err := user_model.GetEmailAddresses(ownerID) | |||
if err != nil { | |||
return nil, err | |||
} | |||
emails := make([]*EmailAddress, 0, len(e.Identities)) | |||
emails := make([]*user_model.EmailAddress, 0, len(e.Identities)) | |||
for _, ident := range e.Identities { | |||
if ident.Revocation != nil { | |||
continue | |||
@@ -242,7 +243,7 @@ func DeleteGPGKey(doer *User, id int64) (err error) { | |||
func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { | |||
uid := int64(0) | |||
var userEmails []*EmailAddress | |||
var userEmails []*user_model.EmailAddress | |||
var user *User | |||
for _, key := range keys { | |||
for _, e := range key.Emails { | |||
@@ -252,7 +253,7 @@ func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { | |||
} | |||
if key.Verified && key.OwnerID != 0 { | |||
if uid != key.OwnerID { | |||
userEmails, _ = GetEmailAddresses(key.OwnerID) | |||
userEmails, _ = user_model.GetEmailAddresses(key.OwnerID) | |||
uid = key.OwnerID | |||
user = &User{ID: uid} | |||
_, _ = GetUser(user) |
@@ -10,6 +10,7 @@ import ( | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -167,7 +168,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { | |||
} | |||
} | |||
committerEmailAddresses, _ := GetEmailAddresses(committer.ID) | |||
committerEmailAddresses, _ := user_model.GetEmailAddresses(committer.ID) | |||
activated := false | |||
for _, e := range committerEmailAddresses { | |||
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) { |
@@ -11,6 +11,7 @@ import ( | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/unit" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
@@ -163,25 +164,25 @@ func CreateOrganization(org, owner *User) (err error) { | |||
org.NumMembers = 1 | |||
org.Type = UserTypeOrganization | |||
sess := db.NewSession(db.DefaultContext) | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
ctx, committer, err := db.TxContext() | |||
if err != nil { | |||
return err | |||
} | |||
defer committer.Close() | |||
if err = deleteUserRedirect(sess, org.Name); err != nil { | |||
if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil { | |||
return err | |||
} | |||
if _, err = sess.Insert(org); err != nil { | |||
if err = db.Insert(ctx, org); err != nil { | |||
return fmt.Errorf("insert organization: %v", err) | |||
} | |||
if err = org.generateRandomAvatar(sess); err != nil { | |||
if err = org.generateRandomAvatar(db.GetEngine(ctx)); err != nil { | |||
return fmt.Errorf("generate random avatar: %v", err) | |||
} | |||
// Add initial creator to organization and owner team. | |||
if _, err = sess.Insert(&OrgUser{ | |||
if err = db.Insert(ctx, &OrgUser{ | |||
UID: owner.ID, | |||
OrgID: org.ID, | |||
}); err != nil { | |||
@@ -198,7 +199,7 @@ func CreateOrganization(org, owner *User) (err error) { | |||
IncludesAllRepositories: true, | |||
CanCreateOrgRepo: true, | |||
} | |||
if _, err = sess.Insert(t); err != nil { | |||
if err = db.Insert(ctx, t); err != nil { | |||
return fmt.Errorf("insert owner team: %v", err) | |||
} | |||
@@ -212,14 +213,11 @@ func CreateOrganization(org, owner *User) (err error) { | |||
}) | |||
} | |||
if _, err = sess.Insert(&units); err != nil { | |||
if err := sess.Rollback(); err != nil { | |||
log.Error("CreateOrganization: sess.Rollback: %v", err) | |||
} | |||
if err = db.Insert(ctx, &units); err != nil { | |||
return err | |||
} | |||
if _, err = sess.Insert(&TeamUser{ | |||
if err = db.Insert(ctx, &TeamUser{ | |||
UID: owner.ID, | |||
OrgID: org.ID, | |||
TeamID: t.ID, | |||
@@ -227,7 +225,7 @@ func CreateOrganization(org, owner *User) (err error) { | |||
return fmt.Errorf("insert team-user relation: %v", err) | |||
} | |||
return sess.Commit() | |||
return committer.Commit() | |||
} | |||
// GetOrgByName returns organization by given name. |
@@ -10,6 +10,7 @@ import ( | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/setting" | |||
) | |||
@@ -88,7 +89,7 @@ func CheckPrincipalKeyString(user *User, content string) (_ string, err error) { | |||
case "anything": | |||
return content, nil | |||
case "email": | |||
emails, err := GetEmailAddresses(user.ID) | |||
emails, err := user_model.GetEmailAddresses(user.ID) | |||
if err != nil { | |||
return "", err | |||
} |
@@ -23,6 +23,7 @@ import ( | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/login" | |||
"code.gitea.io/gitea/models/unit" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -77,9 +78,6 @@ const ( | |||
) | |||
var ( | |||
// ErrEmailNotActivated e-mail address has not been activated error | |||
ErrEmailNotActivated = errors.New("E-mail address has not been activated") | |||
// ErrUserNameIllegal user name contains illegal characters error | |||
ErrUserNameIllegal = errors.New("User name contains illegal characters") | |||
@@ -890,14 +888,15 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
u.Visibility = overwriteDefault[0].Visibility | |||
} | |||
sess := db.NewSession(db.DefaultContext) | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
ctx, committer, err := db.TxContext() | |||
if err != nil { | |||
return err | |||
} | |||
defer committer.Close() | |||
// validate data | |||
sess := db.GetEngine(ctx) | |||
// validate data | |||
if err := validateUser(u); err != nil { | |||
return err | |||
} | |||
@@ -909,11 +908,13 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
return ErrUserAlreadyExist{u.Name} | |||
} | |||
isExist, err = isEmailUsed(sess, u.Email) | |||
isExist, err = user_model.IsEmailUsed(ctx, u.Email) | |||
if err != nil { | |||
return err | |||
} else if isExist { | |||
return ErrEmailAlreadyUsed{u.Email} | |||
return user_model.ErrEmailAlreadyUsed{ | |||
Email: u.Email, | |||
} | |||
} | |||
// prepare for database | |||
@@ -929,16 +930,16 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
// save changes to database | |||
if err = deleteUserRedirect(sess, u.Name); err != nil { | |||
if err = user_model.DeleteUserRedirect(ctx, u.Name); err != nil { | |||
return err | |||
} | |||
if _, err = sess.Insert(u); err != nil { | |||
if err = db.Insert(ctx, u); err != nil { | |||
return err | |||
} | |||
// insert email address | |||
if _, err := sess.Insert(&EmailAddress{ | |||
if err := db.Insert(ctx, &user_model.EmailAddress{ | |||
UID: u.ID, | |||
Email: u.Email, | |||
LowerEmail: strings.ToLower(u.Email), | |||
@@ -948,7 +949,7 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||
return err | |||
} | |||
return sess.Commit() | |||
return committer.Commit() | |||
} | |||
func countUsers(e db.Engine) int64 { | |||
@@ -998,7 +999,7 @@ func VerifyUserActiveCode(code string) (user *User) { | |||
} | |||
// VerifyActiveEmailCode verifies active email code when active account | |||
func VerifyActiveEmailCode(code, email string) *EmailAddress { | |||
func VerifyActiveEmailCode(code, email string) *user_model.EmailAddress { | |||
minutes := setting.Service.ActiveCodeLives | |||
if user := getVerifyUser(code); user != nil { | |||
@@ -1007,7 +1008,7 @@ func VerifyActiveEmailCode(code, email string) *EmailAddress { | |||
data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands) | |||
if base.VerifyTimeLimitCode(data, minutes, prefix) { | |||
emailAddress := &EmailAddress{UID: user.ID, Email: email} | |||
emailAddress := &user_model.EmailAddress{UID: user.ID, Email: email} | |||
if has, _ := db.GetEngine(db.DefaultContext).Get(emailAddress); has { | |||
return emailAddress | |||
} | |||
@@ -1023,11 +1024,12 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||
return err | |||
} | |||
sess := db.NewSession(db.DefaultContext) | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
ctx, committer, err := db.TxContext() | |||
if err != nil { | |||
return err | |||
} | |||
defer committer.Close() | |||
sess := db.GetEngine(ctx) | |||
isExist, err := isUserExist(sess, 0, newUserName) | |||
if err != nil { | |||
@@ -1045,11 +1047,11 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||
return fmt.Errorf("Rename user directory: %v", err) | |||
} | |||
if err = newUserRedirect(sess, u.ID, oldUserName, newUserName); err != nil { | |||
if err = user_model.NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { | |||
return err | |||
} | |||
if err = sess.Commit(); err != nil { | |||
if err = committer.Commit(); err != nil { | |||
if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { | |||
log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) | |||
return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) | |||
@@ -1071,7 +1073,9 @@ func checkDupEmail(e db.Engine, u *User) error { | |||
if err != nil { | |||
return err | |||
} else if has { | |||
return ErrEmailAlreadyUsed{u.Email} | |||
return user_model.ErrEmailAlreadyUsed{ | |||
Email: u.Email, | |||
} | |||
} | |||
return nil | |||
} | |||
@@ -1083,7 +1087,7 @@ func validateUser(u *User) error { | |||
} | |||
u.Email = strings.ToLower(u.Email) | |||
return ValidateEmail(u.Email) | |||
return user_model.ValidateEmail(u.Email) | |||
} | |||
func updateUser(e db.Engine, u *User) error { | |||
@@ -1211,7 +1215,7 @@ func deleteUser(e db.Engine, u *User) error { | |||
&Follow{FollowID: u.ID}, | |||
&Action{UserID: u.ID}, | |||
&IssueUser{UID: u.ID}, | |||
&EmailAddress{UID: u.ID}, | |||
&user_model.EmailAddress{UID: u.ID}, | |||
&UserOpenID{UID: u.ID}, | |||
&Reaction{UserID: u.ID}, | |||
&TeamUser{UID: u.ID}, | |||
@@ -1370,7 +1374,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) (err erro | |||
_, err = db.GetEngine(db.DefaultContext). | |||
Where("is_activated = ?", false). | |||
Delete(new(EmailAddress)) | |||
Delete(new(user_model.EmailAddress)) | |||
return err | |||
} | |||
@@ -1576,7 +1580,7 @@ func GetUserByEmailContext(ctx context.Context, email string) (*User, error) { | |||
} | |||
// Otherwise, check in alternative list for activated email addresses | |||
emailAddress := &EmailAddress{Email: email, IsActivated: true} | |||
emailAddress := &user_model.EmailAddress{Email: email, IsActivated: true} | |||
has, err = db.GetEngine(ctx).Get(emailAddress) | |||
if err != nil { | |||
return nil, err |
@@ -0,0 +1,269 @@ | |||
// Copyright 2016 The Gogs Authors. All rights reserved. | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package user | |||
import ( | |||
"context" | |||
"errors" | |||
"fmt" | |||
"net/mail" | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"xorm.io/builder" | |||
) | |||
var ( | |||
// ErrEmailNotActivated e-mail address has not been activated error | |||
ErrEmailNotActivated = errors.New("E-mail address has not been activated") | |||
) | |||
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322 | |||
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) | |||
} | |||
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error. | |||
type ErrEmailAlreadyUsed struct { | |||
Email string | |||
} | |||
// IsErrEmailAlreadyUsed checks if an error is a ErrEmailAlreadyUsed. | |||
func IsErrEmailAlreadyUsed(err error) bool { | |||
_, ok := err.(ErrEmailAlreadyUsed) | |||
return ok | |||
} | |||
func (err ErrEmailAlreadyUsed) Error() string { | |||
return fmt.Sprintf("e-mail already in use [email: %s]", err.Email) | |||
} | |||
// ErrEmailAddressNotExist email address not exist | |||
type ErrEmailAddressNotExist struct { | |||
Email string | |||
} | |||
// IsErrEmailAddressNotExist checks if an error is an ErrEmailAddressNotExist | |||
func IsErrEmailAddressNotExist(err error) bool { | |||
_, ok := err.(ErrEmailAddressNotExist) | |||
return ok | |||
} | |||
func (err ErrEmailAddressNotExist) Error() string { | |||
return fmt.Sprintf("Email address does not exist [email: %s]", err.Email) | |||
} | |||
// ErrPrimaryEmailCannotDelete primary email address cannot be deleted | |||
type ErrPrimaryEmailCannotDelete struct { | |||
Email string | |||
} | |||
// IsErrPrimaryEmailCannotDelete checks if an error is an ErrPrimaryEmailCannotDelete | |||
func IsErrPrimaryEmailCannotDelete(err error) bool { | |||
_, ok := err.(ErrPrimaryEmailCannotDelete) | |||
return ok | |||
} | |||
func (err ErrPrimaryEmailCannotDelete) Error() string { | |||
return fmt.Sprintf("Primary email address cannot be deleted [email: %s]", err.Email) | |||
} | |||
// EmailAddress is the list of all email addresses of a user. It also contains the | |||
// primary email address which is saved in user table. | |||
type EmailAddress struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
UID int64 `xorm:"INDEX NOT NULL"` | |||
Email string `xorm:"UNIQUE NOT NULL"` | |||
LowerEmail string `xorm:"UNIQUE NOT NULL"` | |||
IsActivated bool | |||
IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` | |||
} | |||
func init() { | |||
db.RegisterModel(new(EmailAddress)) | |||
} | |||
// BeforeInsert will be invoked by XORM before inserting a record | |||
func (email *EmailAddress) BeforeInsert() { | |||
if email.LowerEmail == "" { | |||
email.LowerEmail = strings.ToLower(email.Email) | |||
} | |||
} | |||
// ValidateEmail check if email is a allowed address | |||
func ValidateEmail(email string) error { | |||
if len(email) == 0 { | |||
return nil | |||
} | |||
if _, err := mail.ParseAddress(email); err != nil { | |||
return ErrEmailInvalid{email} | |||
} | |||
// TODO: add an email allow/block list | |||
return nil | |||
} | |||
// GetEmailAddresses returns all email addresses belongs to given user. | |||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) { | |||
emails := make([]*EmailAddress, 0, 5) | |||
if err := db.GetEngine(db.DefaultContext). | |||
Where("uid=?", uid). | |||
Asc("id"). | |||
Find(&emails); err != nil { | |||
return nil, err | |||
} | |||
return emails, nil | |||
} | |||
// GetEmailAddressByID gets a user's email address by ID | |||
func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) { | |||
// User ID is required for security reasons | |||
email := &EmailAddress{UID: uid} | |||
if has, err := db.GetEngine(db.DefaultContext).ID(id).Get(email); err != nil { | |||
return nil, err | |||
} else if !has { | |||
return nil, nil | |||
} | |||
return email, nil | |||
} | |||
// IsEmailActive check if email is activated with a different emailID | |||
func IsEmailActive(ctx context.Context, email string, excludeEmailID int64) (bool, error) { | |||
if len(email) == 0 { | |||
return true, nil | |||
} | |||
// Can't filter by boolean field unless it's explicit | |||
cond := builder.NewCond() | |||
cond = cond.And(builder.Eq{"lower_email": strings.ToLower(email)}, builder.Neq{"id": excludeEmailID}) | |||
if setting.Service.RegisterEmailConfirm { | |||
// Inactive (unvalidated) addresses don't count as active if email validation is required | |||
cond = cond.And(builder.Eq{"is_activated": true}) | |||
} | |||
var em EmailAddress | |||
if has, err := db.GetEngine(ctx).Where(cond).Get(&em); has || err != nil { | |||
if has { | |||
log.Info("isEmailActive(%q, %d) found duplicate in email ID %d", email, excludeEmailID, em.ID) | |||
} | |||
return has, err | |||
} | |||
return false, nil | |||
} | |||
// IsEmailUsed returns true if the email has been used. | |||
func IsEmailUsed(ctx context.Context, email string) (bool, error) { | |||
if len(email) == 0 { | |||
return true, nil | |||
} | |||
return db.GetEngine(ctx).Where("lower_email=?", strings.ToLower(email)).Get(&EmailAddress{}) | |||
} | |||
func addEmailAddress(ctx context.Context, email *EmailAddress) error { | |||
email.Email = strings.TrimSpace(email.Email) | |||
used, err := IsEmailUsed(ctx, email.Email) | |||
if err != nil { | |||
return err | |||
} else if used { | |||
return ErrEmailAlreadyUsed{email.Email} | |||
} | |||
if err = ValidateEmail(email.Email); err != nil { | |||
return err | |||
} | |||
return db.Insert(ctx, email) | |||
} | |||
// AddEmailAddress adds an email address to given user. | |||
func AddEmailAddress(email *EmailAddress) error { | |||
return addEmailAddress(db.DefaultContext, email) | |||
} | |||
// AddEmailAddresses adds an email address to given user. | |||
func AddEmailAddresses(emails []*EmailAddress) error { | |||
if len(emails) == 0 { | |||
return nil | |||
} | |||
// Check if any of them has been used | |||
for i := range emails { | |||
emails[i].Email = strings.TrimSpace(emails[i].Email) | |||
used, err := IsEmailUsed(db.DefaultContext, emails[i].Email) | |||
if err != nil { | |||
return err | |||
} else if used { | |||
return ErrEmailAlreadyUsed{emails[i].Email} | |||
} | |||
if err = ValidateEmail(emails[i].Email); err != nil { | |||
return err | |||
} | |||
} | |||
if err := db.Insert(db.DefaultContext, emails); err != nil { | |||
return fmt.Errorf("Insert: %v", err) | |||
} | |||
return nil | |||
} | |||
// DeleteEmailAddress deletes an email address of given user. | |||
func DeleteEmailAddress(email *EmailAddress) (err error) { | |||
if email.IsPrimary { | |||
return ErrPrimaryEmailCannotDelete{Email: email.Email} | |||
} | |||
var deleted int64 | |||
// ask to check UID | |||
address := EmailAddress{ | |||
UID: email.UID, | |||
} | |||
if email.ID > 0 { | |||
deleted, err = db.GetEngine(db.DefaultContext).ID(email.ID).Delete(&address) | |||
} else { | |||
if email.Email != "" && email.LowerEmail == "" { | |||
email.LowerEmail = strings.ToLower(email.Email) | |||
} | |||
deleted, err = db.GetEngine(db.DefaultContext). | |||
Where("lower_email=?", email.LowerEmail). | |||
Delete(&address) | |||
} | |||
if err != nil { | |||
return err | |||
} else if deleted != 1 { | |||
return ErrEmailAddressNotExist{Email: email.Email} | |||
} | |||
return nil | |||
} | |||
// DeleteEmailAddresses deletes multiple email addresses | |||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) { | |||
for i := range emails { | |||
if err = DeleteEmailAddress(emails[i]); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,131 @@ | |||
// Copyright 2017 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package user | |||
import ( | |||
"testing" | |||
"code.gitea.io/gitea/models/db" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestGetEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
emails, _ := GetEmailAddresses(int64(1)) | |||
if assert.Len(t, emails, 3) { | |||
assert.True(t, emails[0].IsPrimary) | |||
assert.True(t, emails[2].IsActivated) | |||
assert.False(t, emails[2].IsPrimary) | |||
} | |||
emails, _ = GetEmailAddresses(int64(2)) | |||
if assert.Len(t, emails, 2) { | |||
assert.True(t, emails[0].IsPrimary) | |||
assert.True(t, emails[0].IsActivated) | |||
} | |||
} | |||
func TestIsEmailUsed(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
isExist, _ := IsEmailUsed(db.DefaultContext, "") | |||
assert.True(t, isExist) | |||
isExist, _ = IsEmailUsed(db.DefaultContext, "user11@example.com") | |||
assert.True(t, isExist) | |||
isExist, _ = IsEmailUsed(db.DefaultContext, "user1234567890@example.com") | |||
assert.False(t, isExist) | |||
} | |||
func TestAddEmailAddress(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
assert.NoError(t, AddEmailAddress(&EmailAddress{ | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
IsPrimary: true, | |||
IsActivated: true, | |||
})) | |||
// ErrEmailAlreadyUsed | |||
err := AddEmailAddress(&EmailAddress{ | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
}) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrEmailAlreadyUsed(err)) | |||
} | |||
func TestAddEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
// insert multiple email address | |||
emails := make([]*EmailAddress, 2) | |||
emails[0] = &EmailAddress{ | |||
Email: "user1234@example.com", | |||
LowerEmail: "user1234@example.com", | |||
IsActivated: true, | |||
} | |||
emails[1] = &EmailAddress{ | |||
Email: "user5678@example.com", | |||
LowerEmail: "user5678@example.com", | |||
IsActivated: true, | |||
} | |||
assert.NoError(t, AddEmailAddresses(emails)) | |||
// ErrEmailAlreadyUsed | |||
err := AddEmailAddresses(emails) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrEmailAlreadyUsed(err)) | |||
} | |||
func TestDeleteEmailAddress(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
assert.NoError(t, DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
ID: int64(33), | |||
Email: "user1-2@example.com", | |||
LowerEmail: "user1-2@example.com", | |||
})) | |||
assert.NoError(t, DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
Email: "user1-3@example.com", | |||
LowerEmail: "user1-3@example.com", | |||
})) | |||
// Email address does not exist | |||
err := DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
}) | |||
assert.Error(t, err) | |||
} | |||
func TestDeleteEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
// delete multiple email address | |||
emails := make([]*EmailAddress, 2) | |||
emails[0] = &EmailAddress{ | |||
UID: int64(2), | |||
ID: int64(3), | |||
Email: "user2@example.com", | |||
LowerEmail: "user2@example.com", | |||
} | |||
emails[1] = &EmailAddress{ | |||
UID: int64(2), | |||
Email: "user2-2@example.com", | |||
LowerEmail: "user2-2@example.com", | |||
} | |||
assert.NoError(t, DeleteEmailAddresses(emails)) | |||
// ErrEmailAlreadyUsed | |||
err := DeleteEmailAddresses(emails) | |||
assert.Error(t, err) | |||
} |
@@ -0,0 +1,19 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package user | |||
import ( | |||
"path/filepath" | |||
"testing" | |||
"code.gitea.io/gitea/models/db" | |||
) | |||
func TestMain(m *testing.M) { | |||
db.MainTest(m, filepath.Join("..", ".."), | |||
"email_address.yml", | |||
"user_redirect.yml", | |||
) | |||
} |
@@ -0,0 +1,79 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package user | |||
import ( | |||
"context" | |||
"fmt" | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
) | |||
// ErrUserRedirectNotExist represents a "UserRedirectNotExist" kind of error. | |||
type ErrUserRedirectNotExist struct { | |||
Name string | |||
} | |||
// IsErrUserRedirectNotExist check if an error is an ErrUserRedirectNotExist. | |||
func IsErrUserRedirectNotExist(err error) bool { | |||
_, ok := err.(ErrUserRedirectNotExist) | |||
return ok | |||
} | |||
func (err ErrUserRedirectNotExist) Error() string { | |||
return fmt.Sprintf("user redirect does not exist [name: %s]", err.Name) | |||
} | |||
// Redirect represents that a user name should be redirected to another | |||
type Redirect struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
RedirectUserID int64 // userID to redirect to | |||
} | |||
// TableName provides the real table name | |||
func (Redirect) TableName() string { | |||
return "user_redirect" | |||
} | |||
func init() { | |||
db.RegisterModel(new(Redirect)) | |||
} | |||
// LookupUserRedirect look up userID if a user has a redirect name | |||
func LookupUserRedirect(userName string) (int64, error) { | |||
userName = strings.ToLower(userName) | |||
redirect := &Redirect{LowerName: userName} | |||
if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil { | |||
return 0, err | |||
} else if !has { | |||
return 0, ErrUserRedirectNotExist{Name: userName} | |||
} | |||
return redirect.RedirectUserID, nil | |||
} | |||
// NewUserRedirect create a new user redirect | |||
func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName string) error { | |||
oldUserName = strings.ToLower(oldUserName) | |||
newUserName = strings.ToLower(newUserName) | |||
if err := DeleteUserRedirect(ctx, newUserName); err != nil { | |||
return err | |||
} | |||
return db.Insert(ctx, &Redirect{ | |||
LowerName: oldUserName, | |||
RedirectUserID: ID, | |||
}) | |||
} | |||
// DeleteUserRedirect delete any redirect from the specified user name to | |||
// anything else | |||
func DeleteUserRedirect(ctx context.Context, userName string) error { | |||
userName = strings.ToLower(userName) | |||
_, err := db.GetEngine(ctx).Delete(&Redirect{LowerName: userName}) | |||
return err | |||
} |
@@ -0,0 +1,23 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package user | |||
import ( | |||
"testing" | |||
"code.gitea.io/gitea/models/db" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestLookupUserRedirect(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
userID, err := LookupUserRedirect("olduser1") | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 1, userID) | |||
_, err = LookupUserRedirect("doesnotexist") | |||
assert.True(t, IsErrUserRedirectNotExist(err)) | |||
} |
@@ -1,5 +1,4 @@ | |||
// Copyright 2016 The Gogs Authors. All rights reserved. | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Copyright 2021 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
@@ -7,179 +6,28 @@ package models | |||
import ( | |||
"fmt" | |||
"net/mail" | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/util" | |||
"xorm.io/builder" | |||
) | |||
// EmailAddress is the list of all email addresses of a user. It also contains the | |||
// primary email address which is saved in user table. | |||
type EmailAddress struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
UID int64 `xorm:"INDEX NOT NULL"` | |||
Email string `xorm:"UNIQUE NOT NULL"` | |||
LowerEmail string `xorm:"UNIQUE NOT NULL"` | |||
IsActivated bool | |||
IsPrimary bool `xorm:"DEFAULT(false) NOT NULL"` | |||
} | |||
func init() { | |||
db.RegisterModel(new(EmailAddress)) | |||
} | |||
// BeforeInsert will be invoked by XORM before inserting a record | |||
func (email *EmailAddress) BeforeInsert() { | |||
if email.LowerEmail == "" { | |||
email.LowerEmail = strings.ToLower(email.Email) | |||
} | |||
} | |||
// ValidateEmail check if email is a allowed address | |||
func ValidateEmail(email string) error { | |||
if len(email) == 0 { | |||
return nil | |||
} | |||
if _, err := mail.ParseAddress(email); err != nil { | |||
return ErrEmailInvalid{email} | |||
} | |||
// TODO: add an email allow/block list | |||
return nil | |||
} | |||
// GetEmailAddresses returns all email addresses belongs to given user. | |||
func GetEmailAddresses(uid int64) ([]*EmailAddress, error) { | |||
emails := make([]*EmailAddress, 0, 5) | |||
if err := db.GetEngine(db.DefaultContext). | |||
Where("uid=?", uid). | |||
Asc("id"). | |||
Find(&emails); err != nil { | |||
return nil, err | |||
} | |||
return emails, nil | |||
} | |||
// GetEmailAddressByID gets a user's email address by ID | |||
func GetEmailAddressByID(uid, id int64) (*EmailAddress, error) { | |||
// User ID is required for security reasons | |||
email := &EmailAddress{UID: uid} | |||
if has, err := db.GetEngine(db.DefaultContext).ID(id).Get(email); err != nil { | |||
return nil, err | |||
} else if !has { | |||
return nil, nil | |||
} | |||
return email, nil | |||
} | |||
// isEmailActive check if email is activated with a different emailID | |||
func isEmailActive(e db.Engine, email string, excludeEmailID int64) (bool, error) { | |||
if len(email) == 0 { | |||
return true, nil | |||
} | |||
// Can't filter by boolean field unless it's explicit | |||
cond := builder.NewCond() | |||
cond = cond.And(builder.Eq{"lower_email": strings.ToLower(email)}, builder.Neq{"id": excludeEmailID}) | |||
if setting.Service.RegisterEmailConfirm { | |||
// Inactive (unvalidated) addresses don't count as active if email validation is required | |||
cond = cond.And(builder.Eq{"is_activated": true}) | |||
} | |||
var em EmailAddress | |||
if has, err := e.Where(cond).Get(&em); has || err != nil { | |||
if has { | |||
log.Info("isEmailActive(%q, %d) found duplicate in email ID %d", email, excludeEmailID, em.ID) | |||
} | |||
return has, err | |||
} | |||
return false, nil | |||
} | |||
func isEmailUsed(e db.Engine, email string) (bool, error) { | |||
if len(email) == 0 { | |||
return true, nil | |||
} | |||
return e.Where("lower_email=?", strings.ToLower(email)).Get(&EmailAddress{}) | |||
} | |||
// IsEmailUsed returns true if the email has been used. | |||
func IsEmailUsed(email string) (bool, error) { | |||
return isEmailUsed(db.GetEngine(db.DefaultContext), email) | |||
} | |||
func addEmailAddress(e db.Engine, email *EmailAddress) error { | |||
email.Email = strings.TrimSpace(email.Email) | |||
used, err := isEmailUsed(e, email.Email) | |||
if err != nil { | |||
return err | |||
} else if used { | |||
return ErrEmailAlreadyUsed{email.Email} | |||
} | |||
if err = ValidateEmail(email.Email); err != nil { | |||
return err | |||
} | |||
_, err = e.Insert(email) | |||
return err | |||
} | |||
// AddEmailAddress adds an email address to given user. | |||
func AddEmailAddress(email *EmailAddress) error { | |||
return addEmailAddress(db.GetEngine(db.DefaultContext), email) | |||
} | |||
// AddEmailAddresses adds an email address to given user. | |||
func AddEmailAddresses(emails []*EmailAddress) error { | |||
if len(emails) == 0 { | |||
return nil | |||
} | |||
// Check if any of them has been used | |||
for i := range emails { | |||
emails[i].Email = strings.TrimSpace(emails[i].Email) | |||
used, err := IsEmailUsed(emails[i].Email) | |||
if err != nil { | |||
return err | |||
} else if used { | |||
return ErrEmailAlreadyUsed{emails[i].Email} | |||
} | |||
if err = ValidateEmail(emails[i].Email); err != nil { | |||
return err | |||
} | |||
} | |||
if _, err := db.GetEngine(db.DefaultContext).Insert(emails); err != nil { | |||
return fmt.Errorf("Insert: %v", err) | |||
} | |||
return nil | |||
} | |||
// Activate activates the email address to given user. | |||
func (email *EmailAddress) Activate() error { | |||
// ActivateEmail activates the email address to given user. | |||
func ActivateEmail(email *user_model.EmailAddress) error { | |||
sess := db.NewSession(db.DefaultContext) | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err := email.updateActivation(sess, true); err != nil { | |||
if err := updateActivation(sess, email, true); err != nil { | |||
return err | |||
} | |||
return sess.Commit() | |||
} | |||
func (email *EmailAddress) updateActivation(e db.Engine, activate bool) error { | |||
func updateActivation(e db.Engine, email *user_model.EmailAddress, activate bool) error { | |||
user, err := getUserByID(e, email.UID) | |||
if err != nil { | |||
return err | |||
@@ -194,58 +42,17 @@ func (email *EmailAddress) updateActivation(e db.Engine, activate bool) error { | |||
return updateUserCols(e, user, "rands") | |||
} | |||
// DeleteEmailAddress deletes an email address of given user. | |||
func DeleteEmailAddress(email *EmailAddress) (err error) { | |||
if email.IsPrimary { | |||
return ErrPrimaryEmailCannotDelete{Email: email.Email} | |||
} | |||
var deleted int64 | |||
// ask to check UID | |||
address := EmailAddress{ | |||
UID: email.UID, | |||
} | |||
if email.ID > 0 { | |||
deleted, err = db.GetEngine(db.DefaultContext).ID(email.ID).Delete(&address) | |||
} else { | |||
if email.Email != "" && email.LowerEmail == "" { | |||
email.LowerEmail = strings.ToLower(email.Email) | |||
} | |||
deleted, err = db.GetEngine(db.DefaultContext). | |||
Where("lower_email=?", email.LowerEmail). | |||
Delete(&address) | |||
} | |||
if err != nil { | |||
return err | |||
} else if deleted != 1 { | |||
return ErrEmailAddressNotExist{Email: email.Email} | |||
} | |||
return nil | |||
} | |||
// DeleteEmailAddresses deletes multiple email addresses | |||
func DeleteEmailAddresses(emails []*EmailAddress) (err error) { | |||
for i := range emails { | |||
if err = DeleteEmailAddress(emails[i]); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
// MakeEmailPrimary sets primary email address of given user. | |||
func MakeEmailPrimary(email *EmailAddress) error { | |||
func MakeEmailPrimary(email *user_model.EmailAddress) error { | |||
has, err := db.GetEngine(db.DefaultContext).Get(email) | |||
if err != nil { | |||
return err | |||
} else if !has { | |||
return ErrEmailAddressNotExist{Email: email.Email} | |||
return user_model.ErrEmailAddressNotExist{Email: email.Email} | |||
} | |||
if !email.IsActivated { | |||
return ErrEmailNotActivated | |||
return user_model.ErrEmailNotActivated | |||
} | |||
user := &User{} | |||
@@ -269,7 +76,7 @@ func MakeEmailPrimary(email *EmailAddress) error { | |||
} | |||
// 2. Update old primary email | |||
if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{ | |||
if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&user_model.EmailAddress{ | |||
IsPrimary: false, | |||
}); err != nil { | |||
return err | |||
@@ -347,7 +154,7 @@ func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) | |||
} | |||
count, err := db.GetEngine(db.DefaultContext).Join("INNER", "`user`", "`user`.ID = email_address.uid"). | |||
Where(cond).Count(new(EmailAddress)) | |||
Where(cond).Count(new(user_model.EmailAddress)) | |||
if err != nil { | |||
return nil, 0, fmt.Errorf("Count: %v", err) | |||
} | |||
@@ -374,15 +181,16 @@ func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) | |||
// ActivateUserEmail will change the activated state of an email address, | |||
// either primary or secondary (all in the email_address table) | |||
func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | |||
sess := db.NewSession(db.DefaultContext) | |||
defer sess.Close() | |||
if err = sess.Begin(); err != nil { | |||
ctx, committer, err := db.TxContext() | |||
if err != nil { | |||
return err | |||
} | |||
defer committer.Close() | |||
sess := db.GetEngine(ctx) | |||
// Activate/deactivate a user's secondary email address | |||
// First check if there's another user active with the same address | |||
addr := EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} | |||
addr := user_model.EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} | |||
if has, err := sess.Get(&addr); err != nil { | |||
return err | |||
} else if !has { | |||
@@ -393,13 +201,13 @@ func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | |||
return nil | |||
} | |||
if activate { | |||
if used, err := isEmailActive(sess, email, addr.ID); err != nil { | |||
if used, err := user_model.IsEmailActive(ctx, email, addr.ID); err != nil { | |||
return fmt.Errorf("unable to check isEmailActive() for %s: %v", email, err) | |||
} else if used { | |||
return ErrEmailAlreadyUsed{Email: email} | |||
return user_model.ErrEmailAlreadyUsed{Email: email} | |||
} | |||
} | |||
if err = addr.updateActivation(sess, activate); err != nil { | |||
if err = updateActivation(sess, &addr, activate); err != nil { | |||
return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) | |||
} | |||
@@ -423,5 +231,5 @@ func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | |||
} | |||
} | |||
return sess.Commit() | |||
return committer.Commit() | |||
} |
@@ -1,4 +1,4 @@ | |||
// Copyright 2017 The Gitea Authors. All rights reserved. | |||
// Copyright 2021 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
@@ -8,154 +8,37 @@ import ( | |||
"testing" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/util" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestGetEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
emails, _ := GetEmailAddresses(int64(1)) | |||
if assert.Len(t, emails, 3) { | |||
assert.True(t, emails[0].IsPrimary) | |||
assert.True(t, emails[2].IsActivated) | |||
assert.False(t, emails[2].IsPrimary) | |||
} | |||
emails, _ = GetEmailAddresses(int64(2)) | |||
if assert.Len(t, emails, 2) { | |||
assert.True(t, emails[0].IsPrimary) | |||
assert.True(t, emails[0].IsActivated) | |||
} | |||
} | |||
func TestIsEmailUsed(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
isExist, _ := IsEmailUsed("") | |||
assert.True(t, isExist) | |||
isExist, _ = IsEmailUsed("user11@example.com") | |||
assert.True(t, isExist) | |||
isExist, _ = IsEmailUsed("user1234567890@example.com") | |||
assert.False(t, isExist) | |||
} | |||
func TestAddEmailAddress(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
assert.NoError(t, AddEmailAddress(&EmailAddress{ | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
IsPrimary: true, | |||
IsActivated: true, | |||
})) | |||
// ErrEmailAlreadyUsed | |||
err := AddEmailAddress(&EmailAddress{ | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
}) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrEmailAlreadyUsed(err)) | |||
} | |||
func TestAddEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
// insert multiple email address | |||
emails := make([]*EmailAddress, 2) | |||
emails[0] = &EmailAddress{ | |||
Email: "user1234@example.com", | |||
LowerEmail: "user1234@example.com", | |||
IsActivated: true, | |||
} | |||
emails[1] = &EmailAddress{ | |||
Email: "user5678@example.com", | |||
LowerEmail: "user5678@example.com", | |||
IsActivated: true, | |||
} | |||
assert.NoError(t, AddEmailAddresses(emails)) | |||
// ErrEmailAlreadyUsed | |||
err := AddEmailAddresses(emails) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrEmailAlreadyUsed(err)) | |||
} | |||
func TestDeleteEmailAddress(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
assert.NoError(t, DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
ID: int64(33), | |||
Email: "user1-2@example.com", | |||
LowerEmail: "user1-2@example.com", | |||
})) | |||
assert.NoError(t, DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
Email: "user1-3@example.com", | |||
LowerEmail: "user1-3@example.com", | |||
})) | |||
// Email address does not exist | |||
err := DeleteEmailAddress(&EmailAddress{ | |||
UID: int64(1), | |||
Email: "user1234567890@example.com", | |||
LowerEmail: "user1234567890@example.com", | |||
}) | |||
assert.Error(t, err) | |||
} | |||
func TestDeleteEmailAddresses(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
// delete multiple email address | |||
emails := make([]*EmailAddress, 2) | |||
emails[0] = &EmailAddress{ | |||
UID: int64(2), | |||
ID: int64(3), | |||
Email: "user2@example.com", | |||
LowerEmail: "user2@example.com", | |||
} | |||
emails[1] = &EmailAddress{ | |||
UID: int64(2), | |||
Email: "user2-2@example.com", | |||
LowerEmail: "user2-2@example.com", | |||
} | |||
assert.NoError(t, DeleteEmailAddresses(emails)) | |||
// ErrEmailAlreadyUsed | |||
err := DeleteEmailAddresses(emails) | |||
assert.Error(t, err) | |||
} | |||
func TestMakeEmailPrimary(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
email := &EmailAddress{ | |||
email := &user_model.EmailAddress{ | |||
Email: "user567890@example.com", | |||
} | |||
err := MakeEmailPrimary(email) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, ErrEmailAddressNotExist{email.Email}.Error()) | |||
assert.EqualError(t, err, user_model.ErrEmailAddressNotExist{Email: email.Email}.Error()) | |||
email = &EmailAddress{ | |||
email = &user_model.EmailAddress{ | |||
Email: "user11@example.com", | |||
} | |||
err = MakeEmailPrimary(email) | |||
assert.Error(t, err) | |||
assert.EqualError(t, err, ErrEmailNotActivated.Error()) | |||
assert.EqualError(t, err, user_model.ErrEmailNotActivated.Error()) | |||
email = &EmailAddress{ | |||
email = &user_model.EmailAddress{ | |||
Email: "user9999999@example.com", | |||
} | |||
err = MakeEmailPrimary(email) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrUserNotExist(err)) | |||
email = &EmailAddress{ | |||
email = &user_model.EmailAddress{ | |||
Email: "user101@example.com", | |||
} | |||
err = MakeEmailPrimary(email) | |||
@@ -168,14 +51,14 @@ func TestMakeEmailPrimary(t *testing.T) { | |||
func TestActivate(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
email := &EmailAddress{ | |||
email := &user_model.EmailAddress{ | |||
ID: int64(1), | |||
UID: int64(1), | |||
Email: "user11@example.com", | |||
} | |||
assert.NoError(t, email.Activate()) | |||
assert.NoError(t, ActivateEmail(email)) | |||
emails, _ := GetEmailAddresses(int64(1)) | |||
emails, _ := user_model.GetEmailAddresses(int64(1)) | |||
assert.Len(t, emails, 3) | |||
assert.True(t, emails[0].IsActivated) | |||
assert.True(t, emails[0].IsPrimary) |
@@ -1,60 +0,0 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import ( | |||
"strings" | |||
"code.gitea.io/gitea/models/db" | |||
) | |||
// UserRedirect represents that a user name should be redirected to another | |||
type UserRedirect struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
RedirectUserID int64 // userID to redirect to | |||
} | |||
func init() { | |||
db.RegisterModel(new(UserRedirect)) | |||
} | |||
// LookupUserRedirect look up userID if a user has a redirect name | |||
func LookupUserRedirect(userName string) (int64, error) { | |||
userName = strings.ToLower(userName) | |||
redirect := &UserRedirect{LowerName: userName} | |||
if has, err := db.GetEngine(db.DefaultContext).Get(redirect); err != nil { | |||
return 0, err | |||
} else if !has { | |||
return 0, ErrUserRedirectNotExist{Name: userName} | |||
} | |||
return redirect.RedirectUserID, nil | |||
} | |||
// newUserRedirect create a new user redirect | |||
func newUserRedirect(e db.Engine, ID int64, oldUserName, newUserName string) error { | |||
oldUserName = strings.ToLower(oldUserName) | |||
newUserName = strings.ToLower(newUserName) | |||
if err := deleteUserRedirect(e, newUserName); err != nil { | |||
return err | |||
} | |||
if _, err := e.Insert(&UserRedirect{ | |||
LowerName: oldUserName, | |||
RedirectUserID: ID, | |||
}); err != nil { | |||
return err | |||
} | |||
return nil | |||
} | |||
// deleteUserRedirect delete any redirect from the specified user name to | |||
// anything else | |||
func deleteUserRedirect(e db.Engine, userName string) error { | |||
userName = strings.ToLower(userName) | |||
_, err := e.Delete(&UserRedirect{LowerName: userName}) | |||
return err | |||
} |
@@ -1,70 +0,0 @@ | |||
// Copyright 2020 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import ( | |||
"testing" | |||
"code.gitea.io/gitea/models/db" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestLookupUserRedirect(t *testing.T) { | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
userID, err := LookupUserRedirect("olduser1") | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 1, userID) | |||
_, err = LookupUserRedirect("doesnotexist") | |||
assert.True(t, IsErrUserRedirectNotExist(err)) | |||
} | |||
func TestNewUserRedirect(t *testing.T) { | |||
// redirect to a completely new name | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) | |||
assert.NoError(t, newUserRedirect(db.GetEngine(db.DefaultContext), user.ID, user.Name, "newusername")) | |||
db.AssertExistsAndLoadBean(t, &UserRedirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
db.AssertExistsAndLoadBean(t, &UserRedirect{ | |||
LowerName: "olduser1", | |||
RedirectUserID: user.ID, | |||
}) | |||
} | |||
func TestNewUserRedirect2(t *testing.T) { | |||
// redirect to previously used name | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) | |||
assert.NoError(t, newUserRedirect(db.GetEngine(db.DefaultContext), user.ID, user.Name, "olduser1")) | |||
db.AssertExistsAndLoadBean(t, &UserRedirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
db.AssertNotExistsBean(t, &UserRedirect{ | |||
LowerName: "olduser1", | |||
RedirectUserID: user.ID, | |||
}) | |||
} | |||
func TestNewUserRedirect3(t *testing.T) { | |||
// redirect for a previously-unredirected user | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | |||
assert.NoError(t, newUserRedirect(db.GetEngine(db.DefaultContext), user.ID, user.Name, "newusername")) | |||
db.AssertExistsAndLoadBean(t, &UserRedirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
} |
@@ -12,6 +12,7 @@ import ( | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/modules/util" | |||
@@ -358,7 +359,7 @@ func TestCreateUserInvalidEmail(t *testing.T) { | |||
err := CreateUser(user) | |||
assert.Error(t, err) | |||
assert.True(t, IsErrEmailInvalid(err)) | |||
assert.True(t, user_model.IsErrEmailInvalid(err)) | |||
} | |||
func TestCreateUser_Issue5882(t *testing.T) { | |||
@@ -511,3 +512,50 @@ func TestUpdateUser(t *testing.T) { | |||
user.Email = "no mail@mail.org" | |||
assert.Error(t, UpdateUser(user)) | |||
} | |||
func TestNewUserRedirect(t *testing.T) { | |||
// redirect to a completely new name | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) | |||
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) | |||
db.AssertExistsAndLoadBean(t, &user_model.Redirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
db.AssertExistsAndLoadBean(t, &user_model.Redirect{ | |||
LowerName: "olduser1", | |||
RedirectUserID: user.ID, | |||
}) | |||
} | |||
func TestNewUserRedirect2(t *testing.T) { | |||
// redirect to previously used name | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) | |||
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1")) | |||
db.AssertExistsAndLoadBean(t, &user_model.Redirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
db.AssertNotExistsBean(t, &user_model.Redirect{ | |||
LowerName: "olduser1", | |||
RedirectUserID: user.ID, | |||
}) | |||
} | |||
func TestNewUserRedirect3(t *testing.T) { | |||
// redirect for a previously-unredirected user | |||
assert.NoError(t, db.PrepareTestDatabase()) | |||
user := db.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | |||
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) | |||
db.AssertExistsAndLoadBean(t, &user_model.Redirect{ | |||
LowerName: user.LowerName, | |||
RedirectUserID: user.ID, | |||
}) | |||
} |
@@ -9,6 +9,7 @@ import ( | |||
"strings" | |||
"code.gitea.io/gitea/models" | |||
user_model "code.gitea.io/gitea/models/user" | |||
) | |||
// Organization contains organization context | |||
@@ -51,10 +52,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||
ctx.Org.Organization, err = models.GetUserByName(orgName) | |||
if err != nil { | |||
if models.IsErrUserNotExist(err) { | |||
redirectUserID, err := models.LookupUserRedirect(orgName) | |||
redirectUserID, err := user_model.LookupUserRedirect(orgName) | |||
if err == nil { | |||
RedirectToUser(ctx, orgName, redirectUserID) | |||
} else if models.IsErrUserRedirectNotExist(err) { | |||
} else if user_model.IsErrUserRedirectNotExist(err) { | |||
ctx.NotFound("GetUserByName", err) | |||
} else { | |||
ctx.ServerError("LookupUserRedirect", err) |
@@ -14,6 +14,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/login" | |||
"code.gitea.io/gitea/models/unit" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/models/webhook" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -23,7 +24,7 @@ import ( | |||
) | |||
// ToEmail convert models.EmailAddress to api.Email | |||
func ToEmail(email *models.EmailAddress) *api.Email { | |||
func ToEmail(email *user_model.EmailAddress) *api.Email { | |||
return &api.Email{ | |||
Email: email.Email, | |||
Verified: email.IsActivated, | |||
@@ -219,7 +220,7 @@ func ToGPGKey(key *models.GPGKey) *api.GPGKey { | |||
} | |||
// ToGPGKeyEmail convert models.EmailAddress to api.GPGKeyEmail | |||
func ToGPGKeyEmail(email *models.EmailAddress) *api.GPGKeyEmail { | |||
func ToGPGKeyEmail(email *user_model.EmailAddress) *api.GPGKeyEmail { | |||
return &api.GPGKeyEmail{ | |||
Email: email.Email, | |||
Verified: email.IsActivated, |
@@ -12,6 +12,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/convert" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -109,10 +110,10 @@ func CreateUser(ctx *context.APIContext) { | |||
if err := models.CreateUser(u, overwriteDefault); err != nil { | |||
if models.IsErrUserAlreadyExist(err) || | |||
models.IsErrEmailAlreadyUsed(err) || | |||
user_model.IsErrEmailAlreadyUsed(err) || | |||
models.IsErrNameReserved(err) || | |||
models.IsErrNameCharsNotAllowed(err) || | |||
models.IsErrEmailInvalid(err) || | |||
user_model.IsErrEmailInvalid(err) || | |||
models.IsErrNamePatternNotAllowed(err) { | |||
ctx.Error(http.StatusUnprocessableEntity, "", err) | |||
} else { | |||
@@ -245,7 +246,7 @@ func EditUser(ctx *context.APIContext) { | |||
} | |||
if err := models.UpdateUser(u); err != nil { | |||
if models.IsErrEmailAlreadyUsed(err) || models.IsErrEmailInvalid(err) { | |||
if user_model.IsErrEmailAlreadyUsed(err) || user_model.IsErrEmailInvalid(err) { | |||
ctx.Error(http.StatusUnprocessableEntity, "", err) | |||
} else { | |||
ctx.Error(http.StatusInternalServerError, "UpdateUser", err) |
@@ -71,6 +71,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/unit" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -138,9 +139,9 @@ func repoAssignment() func(ctx *context.APIContext) { | |||
owner, err = models.GetUserByName(userName) | |||
if err != nil { | |||
if models.IsErrUserNotExist(err) { | |||
if redirectUserID, err := models.LookupUserRedirect(userName); err == nil { | |||
if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil { | |||
context.RedirectToUser(ctx.Context, userName, redirectUserID) | |||
} else if models.IsErrUserRedirectNotExist(err) { | |||
} else if user_model.IsErrUserRedirectNotExist(err) { | |||
ctx.NotFound("GetUserByName", err) | |||
} else { | |||
ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) | |||
@@ -421,10 +422,10 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) { | |||
ctx.Org.Organization, err = models.GetOrgByName(ctx.Params(":org")) | |||
if err != nil { | |||
if models.IsErrOrgNotExist(err) { | |||
redirectUserID, err := models.LookupUserRedirect(ctx.Params(":org")) | |||
redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org")) | |||
if err == nil { | |||
context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID) | |||
} else if models.IsErrUserRedirectNotExist(err) { | |||
} else if user_model.IsErrUserRedirectNotExist(err) { | |||
ctx.NotFound("GetOrgByName", err) | |||
} else { | |||
ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) |
@@ -8,7 +8,7 @@ import ( | |||
"fmt" | |||
"net/http" | |||
"code.gitea.io/gitea/models" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/convert" | |||
"code.gitea.io/gitea/modules/setting" | |||
@@ -28,7 +28,7 @@ func ListEmails(ctx *context.APIContext) { | |||
// "200": | |||
// "$ref": "#/responses/EmailList" | |||
emails, err := models.GetEmailAddresses(ctx.User.ID) | |||
emails, err := user_model.GetEmailAddresses(ctx.User.ID) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, "GetEmailAddresses", err) | |||
return | |||
@@ -68,20 +68,20 @@ func AddEmail(ctx *context.APIContext) { | |||
return | |||
} | |||
emails := make([]*models.EmailAddress, len(form.Emails)) | |||
emails := make([]*user_model.EmailAddress, len(form.Emails)) | |||
for i := range form.Emails { | |||
emails[i] = &models.EmailAddress{ | |||
emails[i] = &user_model.EmailAddress{ | |||
UID: ctx.User.ID, | |||
Email: form.Emails[i], | |||
IsActivated: !setting.Service.RegisterEmailConfirm, | |||
} | |||
} | |||
if err := models.AddEmailAddresses(emails); err != nil { | |||
if models.IsErrEmailAlreadyUsed(err) { | |||
ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(models.ErrEmailAlreadyUsed).Email) | |||
} else if models.IsErrEmailInvalid(err) { | |||
errMsg := fmt.Sprintf("Email address %s invalid", err.(models.ErrEmailInvalid).Email) | |||
if err := user_model.AddEmailAddresses(emails); err != nil { | |||
if user_model.IsErrEmailAlreadyUsed(err) { | |||
ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email) | |||
} else if user_model.IsErrEmailInvalid(err) { | |||
errMsg := fmt.Sprintf("Email address %s invalid", err.(user_model.ErrEmailInvalid).Email) | |||
ctx.Error(http.StatusUnprocessableEntity, "", errMsg) | |||
} else { | |||
ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err) | |||
@@ -119,16 +119,16 @@ func DeleteEmail(ctx *context.APIContext) { | |||
return | |||
} | |||
emails := make([]*models.EmailAddress, len(form.Emails)) | |||
emails := make([]*user_model.EmailAddress, len(form.Emails)) | |||
for i := range form.Emails { | |||
emails[i] = &models.EmailAddress{ | |||
emails[i] = &user_model.EmailAddress{ | |||
Email: form.Emails[i], | |||
UID: ctx.User.ID, | |||
} | |||
} | |||
if err := models.DeleteEmailAddresses(emails); err != nil { | |||
if models.IsErrEmailAddressNotExist(err) { | |||
if err := user_model.DeleteEmailAddresses(emails); err != nil { | |||
if user_model.IsErrEmailAddressNotExist(err) { | |||
ctx.Error(http.StatusNotFound, "DeleteEmailAddresses", err) | |||
return | |||
} |
@@ -8,6 +8,7 @@ import ( | |||
"net/http" | |||
"code.gitea.io/gitea/models" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
) | |||
@@ -17,7 +18,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *models.User { | |||
user, err := models.GetUserByName(username) | |||
if err != nil { | |||
if models.IsErrUserNotExist(err) { | |||
if redirectUserID, err2 := models.LookupUserRedirect(username); err2 == nil { | |||
if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil { | |||
context.RedirectToUser(ctx.Context, username, redirectUserID) | |||
} else { | |||
ctx.NotFound("GetUserByName", err) |
@@ -11,6 +11,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -128,7 +129,7 @@ func ActivateEmail(ctx *context.Context) { | |||
if err := models.ActivateUserEmail(uid, email, activate); err != nil { | |||
log.Error("ActivateUserEmail(%v,%v,%v): %v", uid, email, activate, err) | |||
if models.IsErrEmailAlreadyUsed(err) { | |||
if user_model.IsErrEmailAlreadyUsed(err) { | |||
ctx.Flash.Error(ctx.Tr("admin.emails.duplicate_active")) | |||
} else { | |||
ctx.Flash.Error(ctx.Tr("admin.emails.not_updated", err)) |
@@ -14,6 +14,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -159,10 +160,10 @@ func NewUserPost(ctx *context.Context) { | |||
case models.IsErrUserAlreadyExist(err): | |||
ctx.Data["Err_UserName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplUserNew, &form) | |||
case models.IsErrEmailAlreadyUsed(err): | |||
case user_model.IsErrEmailAlreadyUsed(err): | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form) | |||
case models.IsErrEmailInvalid(err): | |||
case user_model.IsErrEmailInvalid(err): | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form) | |||
case models.IsErrNameReserved(err): | |||
@@ -351,10 +352,10 @@ func EditUserPost(ctx *context.Context) { | |||
} | |||
if err := models.UpdateUser(u); err != nil { | |||
if models.IsErrEmailAlreadyUsed(err) { | |||
if user_model.IsErrEmailAlreadyUsed(err) { | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form) | |||
} else if models.IsErrEmailInvalid(err) { | |||
} else if user_model.IsErrEmailInvalid(err) { | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form) | |||
} else { |
@@ -23,6 +23,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/login" | |||
"code.gitea.io/gitea/models/unit" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -112,7 +113,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { | |||
owner, err := models.GetUserByName(username) | |||
if err != nil { | |||
if models.IsErrUserNotExist(err) { | |||
if redirectUserID, err := models.LookupUserRedirect(username); err == nil { | |||
if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil { | |||
context.RedirectToUser(ctx, username, redirectUserID) | |||
} else { | |||
ctx.NotFound(fmt.Sprintf("User %s does not exist", username), nil) |
@@ -15,6 +15,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/eventsource" | |||
@@ -181,7 +182,7 @@ func SignInPost(ctx *context.Context) { | |||
if models.IsErrUserNotExist(err) { | |||
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) | |||
log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | |||
} else if models.IsErrEmailAlreadyUsed(err) { | |||
} else if user_model.IsErrEmailAlreadyUsed(err) { | |||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignIn, &form) | |||
log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | |||
} else if models.IsErrUserProhibitLogin(err) { | |||
@@ -1273,7 +1274,7 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form int | |||
// Optionally a template can be specified. | |||
func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *models.User, gothUser *goth.User, allowLink bool) (ok bool) { | |||
if err := models.CreateUser(u); err != nil { | |||
if allowLink && (models.IsErrUserAlreadyExist(err) || models.IsErrEmailAlreadyUsed(err)) { | |||
if allowLink && (models.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { | |||
if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { | |||
var user *models.User | |||
user = &models.User{Name: u.Name} | |||
@@ -1307,10 +1308,10 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{ | |||
case models.IsErrUserAlreadyExist(err): | |||
ctx.Data["Err_UserName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tpl, form) | |||
case models.IsErrEmailAlreadyUsed(err): | |||
case user_model.IsErrEmailAlreadyUsed(err): | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form) | |||
case models.IsErrEmailInvalid(err): | |||
case user_model.IsErrEmailInvalid(err): | |||
ctx.Data["Err_Email"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) | |||
case models.IsErrNameReserved(err): | |||
@@ -1499,7 +1500,7 @@ func ActivateEmail(ctx *context.Context) { | |||
// Verify code. | |||
if email := models.VerifyActiveEmailCode(code, emailStr); email != nil { | |||
if err := email.Activate(); err != nil { | |||
if err := models.ActivateEmail(email); err != nil { | |||
ctx.ServerError("ActivateEmail", err) | |||
} | |||
@@ -13,6 +13,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/markup" | |||
"code.gitea.io/gitea/modules/markup/markdown" | |||
@@ -27,7 +28,7 @@ func GetUserByName(ctx *context.Context, name string) *models.User { | |||
user, err := models.GetUserByName(name) | |||
if err != nil { | |||
if models.IsErrUserNotExist(err) { | |||
if redirectUserID, err := models.LookupUserRedirect(name); err == nil { | |||
if redirectUserID, err := user_model.LookupUserRedirect(name); err == nil { | |||
context.RedirectToUser(ctx, name, redirectUserID) | |||
} else { | |||
ctx.NotFound("GetUserByName", err) |
@@ -11,6 +11,7 @@ import ( | |||
"time" | |||
"code.gitea.io/gitea/models" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -91,7 +92,7 @@ func EmailPost(ctx *context.Context) { | |||
// Make emailaddress primary. | |||
if ctx.FormString("_method") == "PRIMARY" { | |||
if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.FormInt64("id")}); err != nil { | |||
if err := models.MakeEmailPrimary(&user_model.EmailAddress{ID: ctx.FormInt64("id")}); err != nil { | |||
ctx.ServerError("MakeEmailPrimary", err) | |||
return | |||
} | |||
@@ -110,7 +111,7 @@ func EmailPost(ctx *context.Context) { | |||
} | |||
id := ctx.FormInt64("id") | |||
email, err := models.GetEmailAddressByID(ctx.User.ID, id) | |||
email, err := user_model.GetEmailAddressByID(ctx.User.ID, id) | |||
if err != nil { | |||
log.Error("GetEmailAddressByID(%d,%d) error: %v", ctx.User.ID, id, err) | |||
ctx.Redirect(setting.AppSubURL + "/user/settings/account") | |||
@@ -174,18 +175,18 @@ func EmailPost(ctx *context.Context) { | |||
return | |||
} | |||
email := &models.EmailAddress{ | |||
email := &user_model.EmailAddress{ | |||
UID: ctx.User.ID, | |||
Email: form.Email, | |||
IsActivated: !setting.Service.RegisterEmailConfirm, | |||
} | |||
if err := models.AddEmailAddress(email); err != nil { | |||
if models.IsErrEmailAlreadyUsed(err) { | |||
if err := user_model.AddEmailAddress(email); err != nil { | |||
if user_model.IsErrEmailAlreadyUsed(err) { | |||
loadAccountData(ctx) | |||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) | |||
return | |||
} else if models.IsErrEmailInvalid(err) { | |||
} else if user_model.IsErrEmailInvalid(err) { | |||
loadAccountData(ctx) | |||
ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form) | |||
@@ -212,7 +213,7 @@ func EmailPost(ctx *context.Context) { | |||
// DeleteEmail response for delete user's email | |||
func DeleteEmail(ctx *context.Context) { | |||
if err := models.DeleteEmailAddress(&models.EmailAddress{ID: ctx.FormInt64("id"), UID: ctx.User.ID}); err != nil { | |||
if err := user_model.DeleteEmailAddress(&user_model.EmailAddress{ID: ctx.FormInt64("id"), UID: ctx.User.ID}); err != nil { | |||
ctx.ServerError("DeleteEmail", err) | |||
return | |||
} | |||
@@ -258,13 +259,13 @@ func DeleteAccount(ctx *context.Context) { | |||
} | |||
func loadAccountData(ctx *context.Context) { | |||
emlist, err := models.GetEmailAddresses(ctx.User.ID) | |||
emlist, err := user_model.GetEmailAddresses(ctx.User.ID) | |||
if err != nil { | |||
ctx.ServerError("GetEmailAddresses", err) | |||
return | |||
} | |||
type UserEmail struct { | |||
models.EmailAddress | |||
user_model.EmailAddress | |||
CanBePrimary bool | |||
} | |||
pendingActivation := ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) |
@@ -16,6 +16,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -60,7 +61,7 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin | |||
switch { | |||
case models.IsErrUserAlreadyExist(err): | |||
ctx.Flash.Error(ctx.Tr("form.username_been_taken")) | |||
case models.IsErrEmailAlreadyUsed(err): | |||
case user_model.IsErrEmailAlreadyUsed(err): | |||
ctx.Flash.Error(ctx.Tr("form.email_been_used")) | |||
case models.IsErrNameReserved(err): | |||
ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName)) | |||
@@ -120,7 +121,7 @@ func ProfilePost(ctx *context.Context) { | |||
ctx.User.KeepActivityPrivate = form.KeepActivityPrivate | |||
ctx.User.Visibility = form.Visibility | |||
if err := models.UpdateUserSetting(ctx.User); err != nil { | |||
if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | |||
if _, ok := err.(user_model.ErrEmailAlreadyUsed); ok { | |||
ctx.Flash.Error(ctx.Tr("form.email_been_used")) | |||
ctx.Redirect(setting.AppSubURL + "/user/settings") | |||
return |
@@ -10,6 +10,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/db" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/log" | |||
// Register the sources | |||
@@ -32,7 +33,7 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error) | |||
return nil, nil, err | |||
} | |||
if cnt > 1 { | |||
return nil, nil, models.ErrEmailAlreadyUsed{ | |||
return nil, nil, user_model.ErrEmailAlreadyUsed{ | |||
Email: user.Email, | |||
} | |||
} |
@@ -10,6 +10,7 @@ import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/models/login" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/auth/pam" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/services/mailer" | |||
@@ -39,13 +40,13 @@ func (source *Source) Authenticate(user *models.User, userName, password string) | |||
if idx > -1 { | |||
username = pamLogin[:idx] | |||
} | |||
if models.ValidateEmail(email) != nil { | |||
if user_model.ValidateEmail(email) != nil { | |||
if source.EmailDomain != "" { | |||
email = fmt.Sprintf("%s@%s", username, source.EmailDomain) | |||
} else { | |||
email = fmt.Sprintf("%s@%s", username, setting.Service.NoReplyAddress) | |||
} | |||
if models.ValidateEmail(email) != nil { | |||
if user_model.ValidateEmail(email) != nil { | |||
email = uuid.New().String() + "@localhost" | |||
} | |||
} |
@@ -16,6 +16,7 @@ import ( | |||
texttmpl "text/template" | |||
"code.gitea.io/gitea/models" | |||
user_model "code.gitea.io/gitea/models/user" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/emoji" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -112,7 +113,7 @@ func SendResetPasswordMail(u *models.User) { | |||
} | |||
// SendActivateEmailMail sends confirmation email to confirm new email address | |||
func SendActivateEmailMail(u *models.User, email *models.EmailAddress) { | |||
func SendActivateEmailMail(u *models.User, email *user_model.EmailAddress) { | |||
if setting.MailService == nil { | |||
// No mail service configured | |||
return |