mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Move EmailAddress & UserRedirect into models/user/ (#17607)
* 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 lint
This commit is contained in:
		| @@ -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) | 	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. | // ErrUserProhibitLogin represents a "ErrUserProhibitLogin" kind of error. | ||||||
| type ErrUserProhibitLogin struct { | type ErrUserProhibitLogin struct { | ||||||
| 	UID  int64 | 	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) | 	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. | // ErrOpenIDAlreadyUsed represents a "OpenIDAlreadyUsed" kind of error. | ||||||
| type ErrOpenIDAlreadyUsed struct { | type ErrOpenIDAlreadyUsed struct { | ||||||
| 	OpenID string | 	OpenID string | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/log" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  |  | ||||||
| @@ -36,7 +37,7 @@ type GPGKey struct { | |||||||
| 	ExpiredUnix       timeutil.TimeStamp | 	ExpiredUnix       timeutil.TimeStamp | ||||||
| 	AddedUnix         timeutil.TimeStamp | 	AddedUnix         timeutil.TimeStamp | ||||||
| 	SubsKey           []*GPGKey `xorm:"-"` | 	SubsKey           []*GPGKey `xorm:"-"` | ||||||
| 	Emails            []*EmailAddress | 	Emails            []*user_model.EmailAddress | ||||||
| 	Verified          bool `xorm:"NOT NULL DEFAULT false"` | 	Verified          bool `xorm:"NOT NULL DEFAULT false"` | ||||||
| 	CanSign           bool | 	CanSign           bool | ||||||
| 	CanEncryptComms   bool | 	CanEncryptComms   bool | ||||||
| @@ -148,12 +149,12 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity, verified bool) (*GPGKey, erro | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Check emails | 	// Check emails | ||||||
| 	userEmails, err := GetEmailAddresses(ownerID) | 	userEmails, err := user_model.GetEmailAddresses(ownerID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	emails := make([]*EmailAddress, 0, len(e.Identities)) | 	emails := make([]*user_model.EmailAddress, 0, len(e.Identities)) | ||||||
| 	for _, ident := range e.Identities { | 	for _, ident := range e.Identities { | ||||||
| 		if ident.Revocation != nil { | 		if ident.Revocation != nil { | ||||||
| 			continue | 			continue | ||||||
| @@ -242,7 +243,7 @@ func DeleteGPGKey(doer *User, id int64) (err error) { | |||||||
|  |  | ||||||
| func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { | func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { | ||||||
| 	uid := int64(0) | 	uid := int64(0) | ||||||
| 	var userEmails []*EmailAddress | 	var userEmails []*user_model.EmailAddress | ||||||
| 	var user *User | 	var user *User | ||||||
| 	for _, key := range keys { | 	for _, key := range keys { | ||||||
| 		for _, e := range key.Emails { | 		for _, e := range key.Emails { | ||||||
| @@ -252,7 +253,7 @@ func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { | |||||||
| 		} | 		} | ||||||
| 		if key.Verified && key.OwnerID != 0 { | 		if key.Verified && key.OwnerID != 0 { | ||||||
| 			if uid != key.OwnerID { | 			if uid != key.OwnerID { | ||||||
| 				userEmails, _ = GetEmailAddresses(key.OwnerID) | 				userEmails, _ = user_model.GetEmailAddresses(key.OwnerID) | ||||||
| 				uid = key.OwnerID | 				uid = key.OwnerID | ||||||
| 				user = &User{ID: uid} | 				user = &User{ID: uid} | ||||||
| 				_, _ = GetUser(user) | 				_, _ = GetUser(user) | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"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 | 		activated := false | ||||||
| 		for _, e := range committerEmailAddresses { | 		for _, e := range committerEmailAddresses { | ||||||
| 			if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) { | 			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/db" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"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/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/storage" | 	"code.gitea.io/gitea/modules/storage" | ||||||
| @@ -163,25 +164,25 @@ func CreateOrganization(org, owner *User) (err error) { | |||||||
| 	org.NumMembers = 1 | 	org.NumMembers = 1 | ||||||
| 	org.Type = UserTypeOrganization | 	org.Type = UserTypeOrganization | ||||||
|  |  | ||||||
| 	sess := db.NewSession(db.DefaultContext) | 	ctx, committer, err := db.TxContext() | ||||||
| 	defer sess.Close() | 	if err != nil { | ||||||
| 	if err = sess.Begin(); err != nil { | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  |  | ||||||
|  | 	if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err = deleteUserRedirect(sess, org.Name); err != nil { | 	if err = db.Insert(ctx, org); err != nil { | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if _, err = sess.Insert(org); err != nil { |  | ||||||
| 		return fmt.Errorf("insert organization: %v", err) | 		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) | 		return fmt.Errorf("generate random avatar: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Add initial creator to organization and owner team. | 	// Add initial creator to organization and owner team. | ||||||
| 	if _, err = sess.Insert(&OrgUser{ | 	if err = db.Insert(ctx, &OrgUser{ | ||||||
| 		UID:   owner.ID, | 		UID:   owner.ID, | ||||||
| 		OrgID: org.ID, | 		OrgID: org.ID, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| @@ -198,7 +199,7 @@ func CreateOrganization(org, owner *User) (err error) { | |||||||
| 		IncludesAllRepositories: true, | 		IncludesAllRepositories: true, | ||||||
| 		CanCreateOrgRepo:        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) | 		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 = db.Insert(ctx, &units); err != nil { | ||||||
| 		if err := sess.Rollback(); err != nil { |  | ||||||
| 			log.Error("CreateOrganization: sess.Rollback: %v", err) |  | ||||||
| 		} |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, err = sess.Insert(&TeamUser{ | 	if err = db.Insert(ctx, &TeamUser{ | ||||||
| 		UID:    owner.ID, | 		UID:    owner.ID, | ||||||
| 		OrgID:  org.ID, | 		OrgID:  org.ID, | ||||||
| 		TeamID: t.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 fmt.Errorf("insert team-user relation: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return sess.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetOrgByName returns organization by given name. | // GetOrgByName returns organization by given name. | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -88,7 +89,7 @@ func CheckPrincipalKeyString(user *User, content string) (_ string, err error) { | |||||||
| 		case "anything": | 		case "anything": | ||||||
| 			return content, nil | 			return content, nil | ||||||
| 		case "email": | 		case "email": | ||||||
| 			emails, err := GetEmailAddresses(user.ID) | 			emails, err := user_model.GetEmailAddresses(user.ID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return "", err | 				return "", err | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/login" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"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/base" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -77,9 +78,6 @@ const ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | 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 user name contains illegal characters error | ||||||
| 	ErrUserNameIllegal = errors.New("User name contains illegal characters") | 	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 | 		u.Visibility = overwriteDefault[0].Visibility | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sess := db.NewSession(db.DefaultContext) | 	ctx, committer, err := db.TxContext() | ||||||
| 	defer sess.Close() | 	if err != nil { | ||||||
| 	if err = sess.Begin(); err != nil { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  |  | ||||||
|  | 	sess := db.GetEngine(ctx) | ||||||
|  |  | ||||||
| 	// validate data | 	// validate data | ||||||
|  |  | ||||||
| 	if err := validateUser(u); err != nil { | 	if err := validateUser(u); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -909,11 +908,13 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||||||
| 		return ErrUserAlreadyExist{u.Name} | 		return ErrUserAlreadyExist{u.Name} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	isExist, err = isEmailUsed(sess, u.Email) | 	isExist, err = user_model.IsEmailUsed(ctx, u.Email) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if isExist { | 	} else if isExist { | ||||||
| 		return ErrEmailAlreadyUsed{u.Email} | 		return user_model.ErrEmailAlreadyUsed{ | ||||||
|  | 			Email: u.Email, | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// prepare for database | 	// prepare for database | ||||||
| @@ -929,16 +930,16 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||||||
|  |  | ||||||
| 	// save changes to database | 	// save changes to database | ||||||
|  |  | ||||||
| 	if err = deleteUserRedirect(sess, u.Name); err != nil { | 	if err = user_model.DeleteUserRedirect(ctx, u.Name); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if _, err = sess.Insert(u); err != nil { | 	if err = db.Insert(ctx, u); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// insert email address | 	// insert email address | ||||||
| 	if _, err := sess.Insert(&EmailAddress{ | 	if err := db.Insert(ctx, &user_model.EmailAddress{ | ||||||
| 		UID:         u.ID, | 		UID:         u.ID, | ||||||
| 		Email:       u.Email, | 		Email:       u.Email, | ||||||
| 		LowerEmail:  strings.ToLower(u.Email), | 		LowerEmail:  strings.ToLower(u.Email), | ||||||
| @@ -948,7 +949,7 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return sess.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
| func countUsers(e db.Engine) int64 { | func countUsers(e db.Engine) int64 { | ||||||
| @@ -998,7 +999,7 @@ func VerifyUserActiveCode(code string) (user *User) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // VerifyActiveEmailCode verifies active email code when active account | // 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 | 	minutes := setting.Service.ActiveCodeLives | ||||||
|  |  | ||||||
| 	if user := getVerifyUser(code); user != nil { | 	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) | 		data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands) | ||||||
|  |  | ||||||
| 		if base.VerifyTimeLimitCode(data, minutes, prefix) { | 		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 { | 			if has, _ := db.GetEngine(db.DefaultContext).Get(emailAddress); has { | ||||||
| 				return emailAddress | 				return emailAddress | ||||||
| 			} | 			} | ||||||
| @@ -1023,11 +1024,12 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sess := db.NewSession(db.DefaultContext) | 	ctx, committer, err := db.TxContext() | ||||||
| 	defer sess.Close() | 	if err != nil { | ||||||
| 	if err = sess.Begin(); err != nil { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 	sess := db.GetEngine(ctx) | ||||||
|  |  | ||||||
| 	isExist, err := isUserExist(sess, 0, newUserName) | 	isExist, err := isUserExist(sess, 0, newUserName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -1045,11 +1047,11 @@ func ChangeUserName(u *User, newUserName string) (err error) { | |||||||
| 		return fmt.Errorf("Rename user directory: %v", err) | 		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 | 		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) { | 		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) | 			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) | 			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 { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if has { | 	} else if has { | ||||||
| 		return ErrEmailAlreadyUsed{u.Email} | 		return user_model.ErrEmailAlreadyUsed{ | ||||||
|  | 			Email: u.Email, | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -1083,7 +1087,7 @@ func validateUser(u *User) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	u.Email = strings.ToLower(u.Email) | 	u.Email = strings.ToLower(u.Email) | ||||||
| 	return ValidateEmail(u.Email) | 	return user_model.ValidateEmail(u.Email) | ||||||
| } | } | ||||||
|  |  | ||||||
| func updateUser(e db.Engine, u *User) error { | func updateUser(e db.Engine, u *User) error { | ||||||
| @@ -1211,7 +1215,7 @@ func deleteUser(e db.Engine, u *User) error { | |||||||
| 		&Follow{FollowID: u.ID}, | 		&Follow{FollowID: u.ID}, | ||||||
| 		&Action{UserID: u.ID}, | 		&Action{UserID: u.ID}, | ||||||
| 		&IssueUser{UID: u.ID}, | 		&IssueUser{UID: u.ID}, | ||||||
| 		&EmailAddress{UID: u.ID}, | 		&user_model.EmailAddress{UID: u.ID}, | ||||||
| 		&UserOpenID{UID: u.ID}, | 		&UserOpenID{UID: u.ID}, | ||||||
| 		&Reaction{UserID: u.ID}, | 		&Reaction{UserID: u.ID}, | ||||||
| 		&TeamUser{UID: 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). | 	_, err = db.GetEngine(db.DefaultContext). | ||||||
| 		Where("is_activated = ?", false). | 		Where("is_activated = ?", false). | ||||||
| 		Delete(new(EmailAddress)) | 		Delete(new(user_model.EmailAddress)) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1576,7 +1580,7 @@ func GetUserByEmailContext(ctx context.Context, email string) (*User, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Otherwise, check in alternative list for activated email addresses | 	// 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) | 	has, err = db.GetEngine(ctx).Get(emailAddress) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|   | |||||||
							
								
								
									
										269
									
								
								models/user/email_address.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								models/user/email_address.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								models/user/email_address_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								models/user/email_address_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								models/user/main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								models/user/main_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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", | ||||||
|  | 	) | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								models/user/redirect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								models/user/redirect.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										23
									
								
								models/user/redirect_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								models/user/redirect_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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 2021 The Gitea Authors. All rights reserved. | ||||||
| // Copyright 2020 The Gitea Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| @@ -7,179 +6,28 @@ package models | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/mail" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/setting" |  | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 
 |  | ||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // EmailAddress is the list of all email addresses of a user. It also contains the | // ActivateEmail activates the email address to given user. | ||||||
| // primary email address which is saved in user table. | func ActivateEmail(email *user_model.EmailAddress) error { | ||||||
| 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 { |  | ||||||
| 	sess := db.NewSession(db.DefaultContext) | 	sess := db.NewSession(db.DefaultContext) | ||||||
| 	defer sess.Close() | 	defer sess.Close() | ||||||
| 	if err := sess.Begin(); err != nil { | 	if err := sess.Begin(); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := email.updateActivation(sess, true); err != nil { | 	if err := updateActivation(sess, email, true); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return sess.Commit() | 	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) | 	user, err := getUserByID(e, email.UID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -194,58 +42,17 @@ func (email *EmailAddress) updateActivation(e db.Engine, activate bool) error { | |||||||
| 	return updateUserCols(e, user, "rands") | 	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. | // 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) | 	has, err := db.GetEngine(db.DefaultContext).Get(email) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if !has { | 	} else if !has { | ||||||
| 		return ErrEmailAddressNotExist{Email: email.Email} | 		return user_model.ErrEmailAddressNotExist{Email: email.Email} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if !email.IsActivated { | 	if !email.IsActivated { | ||||||
| 		return ErrEmailNotActivated | 		return user_model.ErrEmailNotActivated | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	user := &User{} | 	user := &User{} | ||||||
| @@ -269,7 +76,7 @@ func MakeEmailPrimary(email *EmailAddress) error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// 2. Update old primary email | 	// 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, | 		IsPrimary: false, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		return err | 		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"). | 	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 { | 	if err != nil { | ||||||
| 		return nil, 0, fmt.Errorf("Count: %v", err) | 		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, | // ActivateUserEmail will change the activated state of an email address, | ||||||
| // either primary or secondary (all in the email_address table) | // either primary or secondary (all in the email_address table) | ||||||
| func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | ||||||
| 	sess := db.NewSession(db.DefaultContext) | 	ctx, committer, err := db.TxContext() | ||||||
| 	defer sess.Close() | 	if err != nil { | ||||||
| 	if err = sess.Begin(); err != nil { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 	sess := db.GetEngine(ctx) | ||||||
| 
 | 
 | ||||||
| 	// Activate/deactivate a user's secondary email address | 	// Activate/deactivate a user's secondary email address | ||||||
| 	// First check if there's another user active with the same 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 { | 	if has, err := sess.Get(&addr); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if !has { | 	} else if !has { | ||||||
| @@ -393,13 +201,13 @@ func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	if activate { | 	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) | 			return fmt.Errorf("unable to check isEmailActive() for %s: %v", email, err) | ||||||
| 		} else if used { | 		} 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) | 		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 | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
| 
 | 
 | ||||||
| @@ -8,154 +8,37 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"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) { | func TestMakeEmailPrimary(t *testing.T) { | ||||||
| 	assert.NoError(t, db.PrepareTestDatabase()) | 	assert.NoError(t, db.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	email := &EmailAddress{ | 	email := &user_model.EmailAddress{ | ||||||
| 		Email: "user567890@example.com", | 		Email: "user567890@example.com", | ||||||
| 	} | 	} | ||||||
| 	err := MakeEmailPrimary(email) | 	err := MakeEmailPrimary(email) | ||||||
| 	assert.Error(t, err) | 	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", | 		Email: "user11@example.com", | ||||||
| 	} | 	} | ||||||
| 	err = MakeEmailPrimary(email) | 	err = MakeEmailPrimary(email) | ||||||
| 	assert.Error(t, err) | 	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", | 		Email: "user9999999@example.com", | ||||||
| 	} | 	} | ||||||
| 	err = MakeEmailPrimary(email) | 	err = MakeEmailPrimary(email) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrUserNotExist(err)) | 	assert.True(t, IsErrUserNotExist(err)) | ||||||
| 
 | 
 | ||||||
| 	email = &EmailAddress{ | 	email = &user_model.EmailAddress{ | ||||||
| 		Email: "user101@example.com", | 		Email: "user101@example.com", | ||||||
| 	} | 	} | ||||||
| 	err = MakeEmailPrimary(email) | 	err = MakeEmailPrimary(email) | ||||||
| @@ -168,14 +51,14 @@ func TestMakeEmailPrimary(t *testing.T) { | |||||||
| func TestActivate(t *testing.T) { | func TestActivate(t *testing.T) { | ||||||
| 	assert.NoError(t, db.PrepareTestDatabase()) | 	assert.NoError(t, db.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	email := &EmailAddress{ | 	email := &user_model.EmailAddress{ | ||||||
| 		ID:    int64(1), | 		ID:    int64(1), | ||||||
| 		UID:   int64(1), | 		UID:   int64(1), | ||||||
| 		Email: "user11@example.com", | 		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.Len(t, emails, 3) | ||||||
| 	assert.True(t, emails[0].IsActivated) | 	assert.True(t, emails[0].IsActivated) | ||||||
| 	assert.True(t, emails[0].IsPrimary) | 	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/db" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"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/setting" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| @@ -358,7 +359,7 @@ func TestCreateUserInvalidEmail(t *testing.T) { | |||||||
|  |  | ||||||
| 	err := CreateUser(user) | 	err := CreateUser(user) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrEmailInvalid(err)) | 	assert.True(t, user_model.IsErrEmailInvalid(err)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestCreateUser_Issue5882(t *testing.T) { | func TestCreateUser_Issue5882(t *testing.T) { | ||||||
| @@ -511,3 +512,50 @@ func TestUpdateUser(t *testing.T) { | |||||||
| 	user.Email = "no mail@mail.org" | 	user.Email = "no mail@mail.org" | ||||||
| 	assert.Error(t, UpdateUser(user)) | 	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" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Organization contains organization context | // Organization contains organization context | ||||||
| @@ -51,10 +52,10 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||||||
| 	ctx.Org.Organization, err = models.GetUserByName(orgName) | 	ctx.Org.Organization, err = models.GetUserByName(orgName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if models.IsErrUserNotExist(err) { | 		if models.IsErrUserNotExist(err) { | ||||||
| 			redirectUserID, err := models.LookupUserRedirect(orgName) | 			redirectUserID, err := user_model.LookupUserRedirect(orgName) | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| 				RedirectToUser(ctx, orgName, redirectUserID) | 				RedirectToUser(ctx, orgName, redirectUserID) | ||||||
| 			} else if models.IsErrUserRedirectNotExist(err) { | 			} else if user_model.IsErrUserRedirectNotExist(err) { | ||||||
| 				ctx.NotFound("GetUserByName", err) | 				ctx.NotFound("GetUserByName", err) | ||||||
| 			} else { | 			} else { | ||||||
| 				ctx.ServerError("LookupUserRedirect", err) | 				ctx.ServerError("LookupUserRedirect", err) | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/login" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/models/webhook" | 	"code.gitea.io/gitea/models/webhook" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -23,7 +24,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // ToEmail convert models.EmailAddress to api.Email | // 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{ | 	return &api.Email{ | ||||||
| 		Email:    email.Email, | 		Email:    email.Email, | ||||||
| 		Verified: email.IsActivated, | 		Verified: email.IsActivated, | ||||||
| @@ -219,7 +220,7 @@ func ToGPGKey(key *models.GPGKey) *api.GPGKey { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToGPGKeyEmail convert models.EmailAddress to api.GPGKeyEmail | // 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{ | 	return &api.GPGKeyEmail{ | ||||||
| 		Email:    email.Email, | 		Email:    email.Email, | ||||||
| 		Verified: email.IsActivated, | 		Verified: email.IsActivated, | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"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/context" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -109,10 +110,10 @@ func CreateUser(ctx *context.APIContext) { | |||||||
|  |  | ||||||
| 	if err := models.CreateUser(u, overwriteDefault); err != nil { | 	if err := models.CreateUser(u, overwriteDefault); err != nil { | ||||||
| 		if models.IsErrUserAlreadyExist(err) || | 		if models.IsErrUserAlreadyExist(err) || | ||||||
| 			models.IsErrEmailAlreadyUsed(err) || | 			user_model.IsErrEmailAlreadyUsed(err) || | ||||||
| 			models.IsErrNameReserved(err) || | 			models.IsErrNameReserved(err) || | ||||||
| 			models.IsErrNameCharsNotAllowed(err) || | 			models.IsErrNameCharsNotAllowed(err) || | ||||||
| 			models.IsErrEmailInvalid(err) || | 			user_model.IsErrEmailInvalid(err) || | ||||||
| 			models.IsErrNamePatternNotAllowed(err) { | 			models.IsErrNamePatternNotAllowed(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", err) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
| @@ -245,7 +246,7 @@ func EditUser(ctx *context.APIContext) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.UpdateUser(u); err != nil { | 	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) | 			ctx.Error(http.StatusUnprocessableEntity, "", err) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "UpdateUser", err) | 			ctx.Error(http.StatusInternalServerError, "UpdateUser", err) | ||||||
|   | |||||||
| @@ -71,6 +71,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"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/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -138,9 +139,9 @@ func repoAssignment() func(ctx *context.APIContext) { | |||||||
| 			owner, err = models.GetUserByName(userName) | 			owner, err = models.GetUserByName(userName) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				if models.IsErrUserNotExist(err) { | 				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) | 						context.RedirectToUser(ctx.Context, userName, redirectUserID) | ||||||
| 					} else if models.IsErrUserRedirectNotExist(err) { | 					} else if user_model.IsErrUserRedirectNotExist(err) { | ||||||
| 						ctx.NotFound("GetUserByName", err) | 						ctx.NotFound("GetUserByName", err) | ||||||
| 					} else { | 					} else { | ||||||
| 						ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) | 						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")) | 			ctx.Org.Organization, err = models.GetOrgByName(ctx.Params(":org")) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				if models.IsErrOrgNotExist(err) { | 				if models.IsErrOrgNotExist(err) { | ||||||
| 					redirectUserID, err := models.LookupUserRedirect(ctx.Params(":org")) | 					redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org")) | ||||||
| 					if err == nil { | 					if err == nil { | ||||||
| 						context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID) | 						context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID) | ||||||
| 					} else if models.IsErrUserRedirectNotExist(err) { | 					} else if user_model.IsErrUserRedirectNotExist(err) { | ||||||
| 						ctx.NotFound("GetOrgByName", err) | 						ctx.NotFound("GetOrgByName", err) | ||||||
| 					} else { | 					} else { | ||||||
| 						ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) | 						ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"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/context" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| @@ -28,7 +28,7 @@ func ListEmails(ctx *context.APIContext) { | |||||||
| 	//   "200": | 	//   "200": | ||||||
| 	//     "$ref": "#/responses/EmailList" | 	//     "$ref": "#/responses/EmailList" | ||||||
|  |  | ||||||
| 	emails, err := models.GetEmailAddresses(ctx.User.ID) | 	emails, err := user_model.GetEmailAddresses(ctx.User.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "GetEmailAddresses", err) | 		ctx.Error(http.StatusInternalServerError, "GetEmailAddresses", err) | ||||||
| 		return | 		return | ||||||
| @@ -68,20 +68,20 @@ func AddEmail(ctx *context.APIContext) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	emails := make([]*models.EmailAddress, len(form.Emails)) | 	emails := make([]*user_model.EmailAddress, len(form.Emails)) | ||||||
| 	for i := range form.Emails { | 	for i := range form.Emails { | ||||||
| 		emails[i] = &models.EmailAddress{ | 		emails[i] = &user_model.EmailAddress{ | ||||||
| 			UID:         ctx.User.ID, | 			UID:         ctx.User.ID, | ||||||
| 			Email:       form.Emails[i], | 			Email:       form.Emails[i], | ||||||
| 			IsActivated: !setting.Service.RegisterEmailConfirm, | 			IsActivated: !setting.Service.RegisterEmailConfirm, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.AddEmailAddresses(emails); err != nil { | 	if err := user_model.AddEmailAddresses(emails); err != nil { | ||||||
| 		if models.IsErrEmailAlreadyUsed(err) { | 		if user_model.IsErrEmailAlreadyUsed(err) { | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(models.ErrEmailAlreadyUsed).Email) | 			ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email) | ||||||
| 		} else if models.IsErrEmailInvalid(err) { | 		} else if user_model.IsErrEmailInvalid(err) { | ||||||
| 			errMsg := fmt.Sprintf("Email address %s invalid", err.(models.ErrEmailInvalid).Email) | 			errMsg := fmt.Sprintf("Email address %s invalid", err.(user_model.ErrEmailInvalid).Email) | ||||||
| 			ctx.Error(http.StatusUnprocessableEntity, "", errMsg) | 			ctx.Error(http.StatusUnprocessableEntity, "", errMsg) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err) | 			ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err) | ||||||
| @@ -119,16 +119,16 @@ func DeleteEmail(ctx *context.APIContext) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	emails := make([]*models.EmailAddress, len(form.Emails)) | 	emails := make([]*user_model.EmailAddress, len(form.Emails)) | ||||||
| 	for i := range form.Emails { | 	for i := range form.Emails { | ||||||
| 		emails[i] = &models.EmailAddress{ | 		emails[i] = &user_model.EmailAddress{ | ||||||
| 			Email: form.Emails[i], | 			Email: form.Emails[i], | ||||||
| 			UID:   ctx.User.ID, | 			UID:   ctx.User.ID, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.DeleteEmailAddresses(emails); err != nil { | 	if err := user_model.DeleteEmailAddresses(emails); err != nil { | ||||||
| 		if models.IsErrEmailAddressNotExist(err) { | 		if user_model.IsErrEmailAddressNotExist(err) { | ||||||
| 			ctx.Error(http.StatusNotFound, "DeleteEmailAddresses", err) | 			ctx.Error(http.StatusNotFound, "DeleteEmailAddresses", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -17,7 +18,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *models.User { | |||||||
| 	user, err := models.GetUserByName(username) | 	user, err := models.GetUserByName(username) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if models.IsErrUserNotExist(err) { | 		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) | 				context.RedirectToUser(ctx.Context, username, redirectUserID) | ||||||
| 			} else { | 			} else { | ||||||
| 				ctx.NotFound("GetUserByName", err) | 				ctx.NotFound("GetUserByName", err) | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -128,7 +129,7 @@ func ActivateEmail(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	if err := models.ActivateUserEmail(uid, email, activate); err != nil { | 	if err := models.ActivateUserEmail(uid, email, activate); err != nil { | ||||||
| 		log.Error("ActivateUserEmail(%v,%v,%v): %v", uid, email, activate, err) | 		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")) | 			ctx.Flash.Error(ctx.Tr("admin.emails.duplicate_active")) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Flash.Error(ctx.Tr("admin.emails.not_updated", err)) | 			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" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"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/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -159,10 +160,10 @@ func NewUserPost(ctx *context.Context) { | |||||||
| 		case models.IsErrUserAlreadyExist(err): | 		case models.IsErrUserAlreadyExist(err): | ||||||
| 			ctx.Data["Err_UserName"] = true | 			ctx.Data["Err_UserName"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplUserNew, &form) | 			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.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserNew, &form) | 			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.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form) | 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form) | ||||||
| 		case models.IsErrNameReserved(err): | 		case models.IsErrNameReserved(err): | ||||||
| @@ -351,10 +352,10 @@ func EditUserPost(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.UpdateUser(u); err != nil { | 	if err := models.UpdateUser(u); err != nil { | ||||||
| 		if models.IsErrEmailAlreadyUsed(err) { | 		if user_model.IsErrEmailAlreadyUsed(err) { | ||||||
| 			ctx.Data["Err_Email"] = true | 			ctx.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form) | 			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.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form) | 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserEdit, &form) | ||||||
| 		} else { | 		} else { | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/login" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"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/context" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -112,7 +113,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { | |||||||
| 	owner, err := models.GetUserByName(username) | 	owner, err := models.GetUserByName(username) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if models.IsErrUserNotExist(err) { | 		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) | 				context.RedirectToUser(ctx, username, redirectUserID) | ||||||
| 			} else { | 			} else { | ||||||
| 				ctx.NotFound(fmt.Sprintf("User %s does not exist", username), nil) | 				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" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"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/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/eventsource" | 	"code.gitea.io/gitea/modules/eventsource" | ||||||
| @@ -181,7 +182,7 @@ func SignInPost(ctx *context.Context) { | |||||||
| 		if models.IsErrUserNotExist(err) { | 		if models.IsErrUserNotExist(err) { | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) | 			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) | 			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) | 			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) | 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) | ||||||
| 		} else if models.IsErrUserProhibitLogin(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. | // 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) { | 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 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 { | 			if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { | ||||||
| 				var user *models.User | 				var user *models.User | ||||||
| 				user = &models.User{Name: u.Name} | 				user = &models.User{Name: u.Name} | ||||||
| @@ -1307,10 +1308,10 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{ | |||||||
| 		case models.IsErrUserAlreadyExist(err): | 		case models.IsErrUserAlreadyExist(err): | ||||||
| 			ctx.Data["Err_UserName"] = true | 			ctx.Data["Err_UserName"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tpl, form) | 			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.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tpl, form) | 			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.Data["Err_Email"] = true | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) | 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form) | ||||||
| 		case models.IsErrNameReserved(err): | 		case models.IsErrNameReserved(err): | ||||||
| @@ -1499,7 +1500,7 @@ func ActivateEmail(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	// Verify code. | 	// Verify code. | ||||||
| 	if email := models.VerifyActiveEmailCode(code, emailStr); email != nil { | 	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) | 			ctx.ServerError("ActivateEmail", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/context" | ||||||
| 	"code.gitea.io/gitea/modules/markup" | 	"code.gitea.io/gitea/modules/markup" | ||||||
| 	"code.gitea.io/gitea/modules/markup/markdown" | 	"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) | 	user, err := models.GetUserByName(name) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if models.IsErrUserNotExist(err) { | 		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) | 				context.RedirectToUser(ctx, name, redirectUserID) | ||||||
| 			} else { | 			} else { | ||||||
| 				ctx.NotFound("GetUserByName", err) | 				ctx.NotFound("GetUserByName", err) | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -91,7 +92,7 @@ func EmailPost(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	// Make emailaddress primary. | 	// Make emailaddress primary. | ||||||
| 	if ctx.FormString("_method") == "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) | 			ctx.ServerError("MakeEmailPrimary", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -110,7 +111,7 @@ func EmailPost(ctx *context.Context) { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		id := ctx.FormInt64("id") | 		id := ctx.FormInt64("id") | ||||||
| 		email, err := models.GetEmailAddressByID(ctx.User.ID, id) | 		email, err := user_model.GetEmailAddressByID(ctx.User.ID, id) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetEmailAddressByID(%d,%d) error: %v", ctx.User.ID, id, err) | 			log.Error("GetEmailAddressByID(%d,%d) error: %v", ctx.User.ID, id, err) | ||||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings/account") | 			ctx.Redirect(setting.AppSubURL + "/user/settings/account") | ||||||
| @@ -174,18 +175,18 @@ func EmailPost(ctx *context.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	email := &models.EmailAddress{ | 	email := &user_model.EmailAddress{ | ||||||
| 		UID:         ctx.User.ID, | 		UID:         ctx.User.ID, | ||||||
| 		Email:       form.Email, | 		Email:       form.Email, | ||||||
| 		IsActivated: !setting.Service.RegisterEmailConfirm, | 		IsActivated: !setting.Service.RegisterEmailConfirm, | ||||||
| 	} | 	} | ||||||
| 	if err := models.AddEmailAddress(email); err != nil { | 	if err := user_model.AddEmailAddress(email); err != nil { | ||||||
| 		if models.IsErrEmailAlreadyUsed(err) { | 		if user_model.IsErrEmailAlreadyUsed(err) { | ||||||
| 			loadAccountData(ctx) | 			loadAccountData(ctx) | ||||||
|  |  | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) | 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) | ||||||
| 			return | 			return | ||||||
| 		} else if models.IsErrEmailInvalid(err) { | 		} else if user_model.IsErrEmailInvalid(err) { | ||||||
| 			loadAccountData(ctx) | 			loadAccountData(ctx) | ||||||
|  |  | ||||||
| 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSettingsAccount, &form) | 			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 | // DeleteEmail response for delete user's email | ||||||
| func DeleteEmail(ctx *context.Context) { | 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) | 		ctx.ServerError("DeleteEmail", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -258,13 +259,13 @@ func DeleteAccount(ctx *context.Context) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func loadAccountData(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 { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetEmailAddresses", err) | 		ctx.ServerError("GetEmailAddresses", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	type UserEmail struct { | 	type UserEmail struct { | ||||||
| 		models.EmailAddress | 		user_model.EmailAddress | ||||||
| 		CanBePrimary bool | 		CanBePrimary bool | ||||||
| 	} | 	} | ||||||
| 	pendingActivation := ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) | 	pendingActivation := ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"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/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -60,7 +61,7 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin | |||||||
| 			switch { | 			switch { | ||||||
| 			case models.IsErrUserAlreadyExist(err): | 			case models.IsErrUserAlreadyExist(err): | ||||||
| 				ctx.Flash.Error(ctx.Tr("form.username_been_taken")) | 				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")) | 				ctx.Flash.Error(ctx.Tr("form.email_been_used")) | ||||||
| 			case models.IsErrNameReserved(err): | 			case models.IsErrNameReserved(err): | ||||||
| 				ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName)) | 				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.KeepActivityPrivate = form.KeepActivityPrivate | ||||||
| 	ctx.User.Visibility = form.Visibility | 	ctx.User.Visibility = form.Visibility | ||||||
| 	if err := models.UpdateUserSetting(ctx.User); err != nil { | 	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.Flash.Error(ctx.Tr("form.email_been_used")) | ||||||
| 			ctx.Redirect(setting.AppSubURL + "/user/settings") | 			ctx.Redirect(setting.AppSubURL + "/user/settings") | ||||||
| 			return | 			return | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"code.gitea.io/gitea/models/login" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
|  |  | ||||||
| 	// Register the sources | 	// Register the sources | ||||||
| @@ -32,7 +33,7 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error) | |||||||
| 			return nil, nil, err | 			return nil, nil, err | ||||||
| 		} | 		} | ||||||
| 		if cnt > 1 { | 		if cnt > 1 { | ||||||
| 			return nil, nil, models.ErrEmailAlreadyUsed{ | 			return nil, nil, user_model.ErrEmailAlreadyUsed{ | ||||||
| 				Email: user.Email, | 				Email: user.Email, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/login" | 	"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/auth/pam" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/services/mailer" | 	"code.gitea.io/gitea/services/mailer" | ||||||
| @@ -39,13 +40,13 @@ func (source *Source) Authenticate(user *models.User, userName, password string) | |||||||
| 	if idx > -1 { | 	if idx > -1 { | ||||||
| 		username = pamLogin[:idx] | 		username = pamLogin[:idx] | ||||||
| 	} | 	} | ||||||
| 	if models.ValidateEmail(email) != nil { | 	if user_model.ValidateEmail(email) != nil { | ||||||
| 		if source.EmailDomain != "" { | 		if source.EmailDomain != "" { | ||||||
| 			email = fmt.Sprintf("%s@%s", username, source.EmailDomain) | 			email = fmt.Sprintf("%s@%s", username, source.EmailDomain) | ||||||
| 		} else { | 		} else { | ||||||
| 			email = fmt.Sprintf("%s@%s", username, setting.Service.NoReplyAddress) | 			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" | 			email = uuid.New().String() + "@localhost" | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
| 	texttmpl "text/template" | 	texttmpl "text/template" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/emoji" | 	"code.gitea.io/gitea/modules/emoji" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -112,7 +113,7 @@ func SendResetPasswordMail(u *models.User) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // SendActivateEmailMail sends confirmation email to confirm new email address | // 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 { | 	if setting.MailService == nil { | ||||||
| 		// No mail service configured | 		// No mail service configured | ||||||
| 		return | 		return | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lunny Xiao
					Lunny Xiao