mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Move more model into models/user (#17826)
* Move more model into models/user * Remove unnecessary comment Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		| @@ -1624,46 +1624,6 @@ func (err ErrUploadNotExist) Error() string { | |||||||
| 	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) | 	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) | ||||||
| } | } | ||||||
|  |  | ||||||
| //  ___________         __                             .__    .____                 .__          ____ ___ |  | ||||||
| //  \_   _____/__  ____/  |_  ___________  ____ _____  |  |   |    |    ____   ____ |__| ____   |    |   \______ ___________ |  | ||||||
| //   |    __)_\  \/  /\   __\/ __ \_  __ \/    \\__  \ |  |   |    |   /  _ \ / ___\|  |/    \  |    |   /  ___// __ \_  __ \ |  | ||||||
| //   |        \>    <  |  | \  ___/|  | \/   |  \/ __ \|  |__ |    |__(  <_> ) /_/  >  |   |  \ |    |  /\___ \\  ___/|  | \/ |  | ||||||
| //  /_______  /__/\_ \ |__|  \___  >__|  |___|  (____  /____/ |_______ \____/\___  /|__|___|  / |______//____  >\___  >__| |  | ||||||
| //          \/      \/           \/           \/     \/               \/    /_____/         \/               \/     \/ |  | ||||||
|  |  | ||||||
| // ErrExternalLoginUserAlreadyExist represents a "ExternalLoginUserAlreadyExist" kind of error. |  | ||||||
| type ErrExternalLoginUserAlreadyExist struct { |  | ||||||
| 	ExternalID    string |  | ||||||
| 	UserID        int64 |  | ||||||
| 	LoginSourceID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist. |  | ||||||
| func IsErrExternalLoginUserAlreadyExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrExternalLoginUserAlreadyExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrExternalLoginUserAlreadyExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error. |  | ||||||
| type ErrExternalLoginUserNotExist struct { |  | ||||||
| 	UserID        int64 |  | ||||||
| 	LoginSourceID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist. |  | ||||||
| func IsErrExternalLoginUserNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrExternalLoginUserNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrExternalLoginUserNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // .___                            ________                                   .___                   .__ | // .___                            ________                                   .___                   .__ | ||||||
| // |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______ | // |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______ | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/ | // |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/ | ||||||
|   | |||||||
| @@ -245,3 +245,23 @@ func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID s | |||||||
| 		}) | 		}) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID | ||||||
|  | func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error { | ||||||
|  | 	if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return UpdateReviewsMigrationsByType(tp, externalUserID, userID) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -81,7 +81,7 @@ func (org *Organization) LoadTeams() ([]*Team, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // GetMembers returns all members of organization. | // GetMembers returns all members of organization. | ||||||
| func (org *Organization) GetMembers() (UserList, map[int64]bool, error) { | func (org *Organization) GetMembers() (user_model.UserList, map[int64]bool, error) { | ||||||
| 	return FindOrgMembers(&FindOrgMembersOpts{ | 	return FindOrgMembers(&FindOrgMembersOpts{ | ||||||
| 		OrgID: org.ID, | 		OrgID: org.ID, | ||||||
| 	}) | 	}) | ||||||
| @@ -149,7 +149,7 @@ func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // FindOrgMembers loads organization members according conditions | // FindOrgMembers loads organization members according conditions | ||||||
| func FindOrgMembers(opts *FindOrgMembersOpts) (UserList, map[int64]bool, error) { | func FindOrgMembers(opts *FindOrgMembersOpts) (user_model.UserList, map[int64]bool, error) { | ||||||
| 	ous, err := GetOrgUsersByOrgID(opts) | 	ous, err := GetOrgUsersByOrgID(opts) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| @@ -162,7 +162,7 @@ func FindOrgMembers(opts *FindOrgMembersOpts) (UserList, map[int64]bool, error) | |||||||
| 		idsIsPublic[ou.UID] = ou.IsPublic | 		idsIsPublic[ou.UID] = ou.IsPublic | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	users, err := GetUsersByIDs(ids) | 	users, err := user_model.GetUsersByIDs(ids) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, nil, err | 		return nil, nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -260,7 +260,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// ***** START: ExternalLoginUser ***** | 	// ***** START: ExternalLoginUser ***** | ||||||
| 	if err = removeAllAccountLinks(e, u); err != nil { | 	if err = user_model.RemoveAllAccountLinks(ctx, u); err != nil { | ||||||
| 		return fmt.Errorf("ExternalLoginUser: %v", err) | 		return fmt.Errorf("ExternalLoginUser: %v", err) | ||||||
| 	} | 	} | ||||||
| 	// ***** END: ExternalLoginUser ***** | 	// ***** END: ExternalLoginUser ***** | ||||||
|   | |||||||
| @@ -13,8 +13,10 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| @@ -275,3 +277,247 @@ func DeleteInactiveEmailAddresses(ctx context.Context) error { | |||||||
| 		Delete(new(EmailAddress)) | 		Delete(new(EmailAddress)) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ActivateEmail activates the email address to given user. | ||||||
|  | func ActivateEmail(email *EmailAddress) error { | ||||||
|  | 	ctx, committer, err := db.TxContext() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 	if err := updateActivation(db.GetEngine(ctx), email, true); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return committer.Commit() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateActivation(e db.Engine, email *EmailAddress, activate bool) error { | ||||||
|  | 	user, err := GetUserByIDEngine(e, email.UID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if user.Rands, err = GetUserSalt(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	email.IsActivated = activate | ||||||
|  | 	if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return UpdateUserColsEngine(e, user, "rands") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // MakeEmailPrimary sets primary email address of given user. | ||||||
|  | func MakeEmailPrimary(email *EmailAddress) error { | ||||||
|  | 	has, err := db.GetEngine(db.DefaultContext).Get(email) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if !has { | ||||||
|  | 		return ErrEmailAddressNotExist{Email: email.Email} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !email.IsActivated { | ||||||
|  | 		return ErrEmailNotActivated | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	user := &User{} | ||||||
|  | 	has, err = db.GetEngine(db.DefaultContext).ID(email.UID).Get(user) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if !has { | ||||||
|  | 		return ErrUserNotExist{ | ||||||
|  | 			UID:   email.UID, | ||||||
|  | 			Name:  "", | ||||||
|  | 			KeyID: 0, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx, committer, err := db.TxContext() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 	sess := db.GetEngine(ctx) | ||||||
|  |  | ||||||
|  | 	// 1. Update user table | ||||||
|  | 	user.Email = email.Email | ||||||
|  | 	if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 2. Update old primary email | ||||||
|  | 	if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&EmailAddress{ | ||||||
|  | 		IsPrimary: false, | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 3. update new primary email | ||||||
|  | 	email.IsPrimary = true | ||||||
|  | 	if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return committer.Commit() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // VerifyActiveEmailCode verifies active email code when active account | ||||||
|  | func VerifyActiveEmailCode(code, email string) *EmailAddress { | ||||||
|  | 	minutes := setting.Service.ActiveCodeLives | ||||||
|  |  | ||||||
|  | 	if user := GetVerifyUser(code); user != nil { | ||||||
|  | 		// time limit code | ||||||
|  | 		prefix := code[:base.TimeLimitCodeLength] | ||||||
|  | 		data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands) | ||||||
|  |  | ||||||
|  | 		if base.VerifyTimeLimitCode(data, minutes, prefix) { | ||||||
|  | 			emailAddress := &EmailAddress{UID: user.ID, Email: email} | ||||||
|  | 			if has, _ := db.GetEngine(db.DefaultContext).Get(emailAddress); has { | ||||||
|  | 				return emailAddress | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SearchEmailOrderBy is used to sort the results from SearchEmails() | ||||||
|  | type SearchEmailOrderBy string | ||||||
|  |  | ||||||
|  | func (s SearchEmailOrderBy) String() string { | ||||||
|  | 	return string(s) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Strings for sorting result | ||||||
|  | const ( | ||||||
|  | 	SearchEmailOrderByEmail        SearchEmailOrderBy = "email_address.lower_email ASC, email_address.is_primary DESC, email_address.id ASC" | ||||||
|  | 	SearchEmailOrderByEmailReverse SearchEmailOrderBy = "email_address.lower_email DESC, email_address.is_primary ASC, email_address.id DESC" | ||||||
|  | 	SearchEmailOrderByName         SearchEmailOrderBy = "`user`.lower_name ASC, email_address.is_primary DESC, email_address.id ASC" | ||||||
|  | 	SearchEmailOrderByNameReverse  SearchEmailOrderBy = "`user`.lower_name DESC, email_address.is_primary ASC, email_address.id DESC" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // SearchEmailOptions are options to search e-mail addresses for the admin panel | ||||||
|  | type SearchEmailOptions struct { | ||||||
|  | 	db.ListOptions | ||||||
|  | 	Keyword     string | ||||||
|  | 	SortType    SearchEmailOrderBy | ||||||
|  | 	IsPrimary   util.OptionalBool | ||||||
|  | 	IsActivated util.OptionalBool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SearchEmailResult is an e-mail address found in the user or email_address table | ||||||
|  | type SearchEmailResult struct { | ||||||
|  | 	UID         int64 | ||||||
|  | 	Email       string | ||||||
|  | 	IsActivated bool | ||||||
|  | 	IsPrimary   bool | ||||||
|  | 	// From User | ||||||
|  | 	Name     string | ||||||
|  | 	FullName string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SearchEmails takes options i.e. keyword and part of email name to search, | ||||||
|  | // it returns results in given range and number of total results. | ||||||
|  | func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) { | ||||||
|  | 	var cond builder.Cond = builder.Eq{"`user`.`type`": UserTypeIndividual} | ||||||
|  | 	if len(opts.Keyword) > 0 { | ||||||
|  | 		likeStr := "%" + strings.ToLower(opts.Keyword) + "%" | ||||||
|  | 		cond = cond.And(builder.Or( | ||||||
|  | 			builder.Like{"lower(`user`.full_name)", likeStr}, | ||||||
|  | 			builder.Like{"`user`.lower_name", likeStr}, | ||||||
|  | 			builder.Like{"email_address.lower_email", likeStr}, | ||||||
|  | 		)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	case opts.IsPrimary.IsTrue(): | ||||||
|  | 		cond = cond.And(builder.Eq{"email_address.is_primary": true}) | ||||||
|  | 	case opts.IsPrimary.IsFalse(): | ||||||
|  | 		cond = cond.And(builder.Eq{"email_address.is_primary": false}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch { | ||||||
|  | 	case opts.IsActivated.IsTrue(): | ||||||
|  | 		cond = cond.And(builder.Eq{"email_address.is_activated": true}) | ||||||
|  | 	case opts.IsActivated.IsFalse(): | ||||||
|  | 		cond = cond.And(builder.Eq{"email_address.is_activated": false}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	count, err := db.GetEngine(db.DefaultContext).Join("INNER", "`user`", "`user`.ID = email_address.uid"). | ||||||
|  | 		Where(cond).Count(new(EmailAddress)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, 0, fmt.Errorf("Count: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	orderby := opts.SortType.String() | ||||||
|  | 	if orderby == "" { | ||||||
|  | 		orderby = SearchEmailOrderByEmail.String() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	opts.SetDefaultValues() | ||||||
|  |  | ||||||
|  | 	emails := make([]*SearchEmailResult, 0, opts.PageSize) | ||||||
|  | 	err = db.GetEngine(db.DefaultContext).Table("email_address"). | ||||||
|  | 		Select("email_address.*, `user`.name, `user`.full_name"). | ||||||
|  | 		Join("INNER", "`user`", "`user`.ID = email_address.uid"). | ||||||
|  | 		Where(cond). | ||||||
|  | 		OrderBy(orderby). | ||||||
|  | 		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). | ||||||
|  | 		Find(&emails) | ||||||
|  |  | ||||||
|  | 	return emails, count, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ActivateUserEmail will change the activated state of an email address, | ||||||
|  | // either primary or secondary (all in the email_address table) | ||||||
|  | func ActivateUserEmail(userID int64, email string, activate bool) (err error) { | ||||||
|  | 	ctx, committer, err := db.TxContext() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 	sess := db.GetEngine(ctx) | ||||||
|  |  | ||||||
|  | 	// Activate/deactivate a user's secondary email address | ||||||
|  | 	// First check if there's another user active with the same address | ||||||
|  | 	addr := EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} | ||||||
|  | 	if has, err := sess.Get(&addr); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if !has { | ||||||
|  | 		return fmt.Errorf("no such email: %d (%s)", userID, email) | ||||||
|  | 	} | ||||||
|  | 	if addr.IsActivated == activate { | ||||||
|  | 		// Already in the desired state; no action | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	if activate { | ||||||
|  | 		if used, err := IsEmailActive(ctx, email, addr.ID); err != nil { | ||||||
|  | 			return fmt.Errorf("unable to check isEmailActive() for %s: %v", email, err) | ||||||
|  | 		} else if used { | ||||||
|  | 			return ErrEmailAlreadyUsed{Email: email} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err = updateActivation(sess, &addr, activate); err != nil { | ||||||
|  | 		return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Activate/deactivate a user's primary email address and account | ||||||
|  | 	if addr.IsPrimary { | ||||||
|  | 		user := User{ID: userID, Email: email} | ||||||
|  | 		if has, err := sess.Get(&user); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} else if !has { | ||||||
|  | 			return fmt.Errorf("no user with ID: %d and Email: %s", userID, email) | ||||||
|  | 		} | ||||||
|  | 		// The user's activation state should be synchronized with the primary email | ||||||
|  | 		if user.IsActive != activate { | ||||||
|  | 			user.IsActive = activate | ||||||
|  | 			if user.Rands, err = GetUserSalt(); err != nil { | ||||||
|  | 				return fmt.Errorf("unable to generate salt: %v", err) | ||||||
|  | 			} | ||||||
|  | 			if err = UpdateUserColsEngine(sess, &user, "is_active", "rands"); err != nil { | ||||||
|  | 				return fmt.Errorf("unable to updateUserCols() for user ID: %d: %v", userID, err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return committer.Commit() | ||||||
|  | } | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	"code.gitea.io/gitea/modules/util" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
| @@ -130,3 +131,124 @@ func TestDeleteEmailAddresses(t *testing.T) { | |||||||
| 	err := DeleteEmailAddresses(emails) | 	err := DeleteEmailAddresses(emails) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestMakeEmailPrimary(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	email := &EmailAddress{ | ||||||
|  | 		Email: "user567890@example.com", | ||||||
|  | 	} | ||||||
|  | 	err := MakeEmailPrimary(email) | ||||||
|  | 	assert.Error(t, err) | ||||||
|  | 	assert.EqualError(t, err, ErrEmailAddressNotExist{Email: email.Email}.Error()) | ||||||
|  |  | ||||||
|  | 	email = &EmailAddress{ | ||||||
|  | 		Email: "user11@example.com", | ||||||
|  | 	} | ||||||
|  | 	err = MakeEmailPrimary(email) | ||||||
|  | 	assert.Error(t, err) | ||||||
|  | 	assert.EqualError(t, err, ErrEmailNotActivated.Error()) | ||||||
|  |  | ||||||
|  | 	email = &EmailAddress{ | ||||||
|  | 		Email: "user9999999@example.com", | ||||||
|  | 	} | ||||||
|  | 	err = MakeEmailPrimary(email) | ||||||
|  | 	assert.Error(t, err) | ||||||
|  | 	assert.True(t, IsErrUserNotExist(err)) | ||||||
|  |  | ||||||
|  | 	email = &EmailAddress{ | ||||||
|  | 		Email: "user101@example.com", | ||||||
|  | 	} | ||||||
|  | 	err = MakeEmailPrimary(email) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	user, _ := GetUserByID(int64(10)) | ||||||
|  | 	assert.Equal(t, "user101@example.com", user.Email) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestActivate(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	email := &EmailAddress{ | ||||||
|  | 		ID:    int64(1), | ||||||
|  | 		UID:   int64(1), | ||||||
|  | 		Email: "user11@example.com", | ||||||
|  | 	} | ||||||
|  | 	assert.NoError(t, ActivateEmail(email)) | ||||||
|  |  | ||||||
|  | 	emails, _ := GetEmailAddresses(int64(1)) | ||||||
|  | 	assert.Len(t, emails, 3) | ||||||
|  | 	assert.True(t, emails[0].IsActivated) | ||||||
|  | 	assert.True(t, emails[0].IsPrimary) | ||||||
|  | 	assert.False(t, emails[1].IsPrimary) | ||||||
|  | 	assert.True(t, emails[2].IsActivated) | ||||||
|  | 	assert.False(t, emails[2].IsPrimary) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestListEmails(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	// Must find all users and their emails | ||||||
|  | 	opts := &SearchEmailOptions{ | ||||||
|  | 		ListOptions: db.ListOptions{ | ||||||
|  | 			PageSize: 10000, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	emails, count, err := SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.NotEqual(t, int64(0), count) | ||||||
|  | 	assert.True(t, count > 5) | ||||||
|  |  | ||||||
|  | 	contains := func(match func(s *SearchEmailResult) bool) bool { | ||||||
|  | 		for _, v := range emails { | ||||||
|  | 			if match(v) { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 18 })) | ||||||
|  | 	// 'user3' is an organization | ||||||
|  | 	assert.False(t, contains(func(s *SearchEmailResult) bool { return s.UID == 3 })) | ||||||
|  |  | ||||||
|  | 	// Must find no records | ||||||
|  | 	opts = &SearchEmailOptions{Keyword: "NOTFOUND"} | ||||||
|  | 	emails, count, err = SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Equal(t, int64(0), count) | ||||||
|  |  | ||||||
|  | 	// Must find users 'user2', 'user28', etc. | ||||||
|  | 	opts = &SearchEmailOptions{Keyword: "user2"} | ||||||
|  | 	emails, count, err = SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.NotEqual(t, int64(0), count) | ||||||
|  | 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 2 })) | ||||||
|  | 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 27 })) | ||||||
|  |  | ||||||
|  | 	// Must find only primary addresses (i.e. from the `user` table) | ||||||
|  | 	opts = &SearchEmailOptions{IsPrimary: util.OptionalBoolTrue} | ||||||
|  | 	emails, _, err = SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.IsPrimary })) | ||||||
|  | 	assert.False(t, contains(func(s *SearchEmailResult) bool { return !s.IsPrimary })) | ||||||
|  |  | ||||||
|  | 	// Must find only inactive addresses (i.e. not validated) | ||||||
|  | 	opts = &SearchEmailOptions{IsActivated: util.OptionalBoolFalse} | ||||||
|  | 	emails, _, err = SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.True(t, contains(func(s *SearchEmailResult) bool { return !s.IsActivated })) | ||||||
|  | 	assert.False(t, contains(func(s *SearchEmailResult) bool { return s.IsActivated })) | ||||||
|  |  | ||||||
|  | 	// Must find more than one page, but retrieve only one | ||||||
|  | 	opts = &SearchEmailOptions{ | ||||||
|  | 		ListOptions: db.ListOptions{ | ||||||
|  | 			PageSize: 5, | ||||||
|  | 			Page:     1, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	emails, count, err = SearchEmails(opts) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Len(t, emails, 5) | ||||||
|  | 	assert.Greater(t, count, int64(len(emails))) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -2,20 +2,53 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package user | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"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/structs" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/markbates/goth" | 	"github.com/markbates/goth" | ||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrExternalLoginUserAlreadyExist represents a "ExternalLoginUserAlreadyExist" kind of error. | ||||||
|  | type ErrExternalLoginUserAlreadyExist struct { | ||||||
|  | 	ExternalID    string | ||||||
|  | 	UserID        int64 | ||||||
|  | 	LoginSourceID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrExternalLoginUserAlreadyExist checks if an error is a ExternalLoginUserAlreadyExist. | ||||||
|  | func IsErrExternalLoginUserAlreadyExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrExternalLoginUserAlreadyExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrExternalLoginUserAlreadyExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error. | ||||||
|  | type ErrExternalLoginUserNotExist struct { | ||||||
|  | 	UserID        int64 | ||||||
|  | 	LoginSourceID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrExternalLoginUserNotExist checks if an error is a ExternalLoginUserNotExist. | ||||||
|  | func IsErrExternalLoginUserNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrExternalLoginUserNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrExternalLoginUserNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ExternalLoginUser makes the connecting between some existing user and additional external login sources | // ExternalLoginUser makes the connecting between some existing user and additional external login sources | ||||||
| type ExternalLoginUser struct { | type ExternalLoginUser struct { | ||||||
| 	ExternalID        string                 `xorm:"pk NOT NULL"` | 	ExternalID        string                 `xorm:"pk NOT NULL"` | ||||||
| @@ -47,7 +80,7 @@ func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource | // ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource | ||||||
| func ListAccountLinks(user *user_model.User) ([]*ExternalLoginUser, error) { | func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) { | ||||||
| 	externalAccounts := make([]*ExternalLoginUser, 0, 5) | 	externalAccounts := make([]*ExternalLoginUser, 0, 5) | ||||||
| 	err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID). | 	err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID). | ||||||
| 		Desc("login_source_id"). | 		Desc("login_source_id"). | ||||||
| @@ -60,7 +93,7 @@ func ListAccountLinks(user *user_model.User) ([]*ExternalLoginUser, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LinkExternalToUser link the external user to the user | // LinkExternalToUser link the external user to the user | ||||||
| func LinkExternalToUser(user *user_model.User, externalLoginUser *ExternalLoginUser) error { | func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error { | ||||||
| 	has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). | 	has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). | ||||||
| 		NoAutoCondition(). | 		NoAutoCondition(). | ||||||
| 		Exist(externalLoginUser) | 		Exist(externalLoginUser) | ||||||
| @@ -75,7 +108,7 @@ func LinkExternalToUser(user *user_model.User, externalLoginUser *ExternalLoginU | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RemoveAccountLink will remove all external login sources for the given user | // RemoveAccountLink will remove all external login sources for the given user | ||||||
| func RemoveAccountLink(user *user_model.User, loginSourceID int64) (int64, error) { | func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) { | ||||||
| 	deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID}) | 	deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return deleted, err | 		return deleted, err | ||||||
| @@ -86,9 +119,9 @@ func RemoveAccountLink(user *user_model.User, loginSourceID int64) (int64, error | |||||||
| 	return deleted, err | 	return deleted, err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // removeAllAccountLinks will remove all external login sources for the given user | // RemoveAllAccountLinks will remove all external login sources for the given user | ||||||
| func removeAllAccountLinks(e db.Engine, user *user_model.User) error { | func RemoveAllAccountLinks(ctx context.Context, user *User) error { | ||||||
| 	_, err := e.Delete(&ExternalLoginUser{UserID: user.ID}) | 	_, err := db.GetEngine(ctx).Delete(&ExternalLoginUser{UserID: user.ID}) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -107,7 +140,7 @@ func GetUserIDByExternalUserID(provider, userID string) (int64, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateExternalUser updates external user's information | // UpdateExternalUser updates external user's information | ||||||
| func UpdateExternalUser(user *user_model.User, gothUser goth.User) error { | func UpdateExternalUser(user *User, gothUser goth.User) error { | ||||||
| 	loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) | 	loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -172,23 +205,3 @@ func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginU | |||||||
| 	} | 	} | ||||||
| 	return users, nil | 	return users, nil | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID |  | ||||||
| func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error { |  | ||||||
| 	if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if err := UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return UpdateReviewsMigrationsByType(tp, externalUserID, userID) |  | ||||||
| } |  | ||||||
							
								
								
									
										69
									
								
								models/user/list.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								models/user/list.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | // Copyright 2019 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 ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/models/login" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // UserList is a list of user. | ||||||
|  | // This type provide valuable methods to retrieve information for a group of users efficiently. | ||||||
|  | type UserList []*User //revive:disable-line:exported | ||||||
|  |  | ||||||
|  | // GetUserIDs returns a slice of user's id | ||||||
|  | func (users UserList) GetUserIDs() []int64 { | ||||||
|  | 	userIDs := make([]int64, len(users)) | ||||||
|  | 	for _, user := range users { | ||||||
|  | 		userIDs = append(userIDs, user.ID) // Considering that user id are unique in the list | ||||||
|  | 	} | ||||||
|  | 	return userIDs | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTwoFaStatus return state of 2FA enrollement | ||||||
|  | func (users UserList) GetTwoFaStatus() map[int64]bool { | ||||||
|  | 	results := make(map[int64]bool, len(users)) | ||||||
|  | 	for _, user := range users { | ||||||
|  | 		results[user.ID] = false // Set default to false | ||||||
|  | 	} | ||||||
|  | 	tokenMaps, err := users.loadTwoFactorStatus(db.GetEngine(db.DefaultContext)) | ||||||
|  | 	if err == nil { | ||||||
|  | 		for _, token := range tokenMaps { | ||||||
|  | 			results[token.UID] = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return results | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFactor, error) { | ||||||
|  | 	if len(users) == 0 { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	userIDs := users.GetUserIDs() | ||||||
|  | 	tokenMaps := make(map[int64]*login.TwoFactor, len(userIDs)) | ||||||
|  | 	err := e. | ||||||
|  | 		In("uid", userIDs). | ||||||
|  | 		Find(&tokenMaps) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("find two factor: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return tokenMaps, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetUsersByIDs returns all resolved users from a list of Ids. | ||||||
|  | func GetUsersByIDs(ids []int64) (UserList, error) { | ||||||
|  | 	ous := make([]*User, 0, len(ids)) | ||||||
|  | 	if len(ids) == 0 { | ||||||
|  | 		return ous, nil | ||||||
|  | 	} | ||||||
|  | 	err := db.GetEngine(db.DefaultContext).In("id", ids). | ||||||
|  | 		Asc("name"). | ||||||
|  | 		Find(&ous) | ||||||
|  | 	return ous, err | ||||||
|  | } | ||||||
| @@ -1,262 +0,0 @@ | |||||||
| // Copyright 2021 The Gitea Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"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/setting" |  | ||||||
| 	"code.gitea.io/gitea/modules/util" |  | ||||||
|  |  | ||||||
| 	"xorm.io/builder" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // ActivateEmail activates the email address to given user. |  | ||||||
| func ActivateEmail(email *user_model.EmailAddress) error { |  | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 	if err := updateActivation(db.GetEngine(ctx), email, true); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return committer.Commit() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func updateActivation(e db.Engine, email *user_model.EmailAddress, activate bool) error { |  | ||||||
| 	user, err := user_model.GetUserByIDEngine(e, email.UID) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if user.Rands, err = user_model.GetUserSalt(); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	email.IsActivated = activate |  | ||||||
| 	if _, err := e.ID(email.ID).Cols("is_activated").Update(email); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return user_model.UpdateUserColsEngine(e, user, "rands") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MakeEmailPrimary sets primary email address of given user. |  | ||||||
| func MakeEmailPrimary(email *user_model.EmailAddress) error { |  | ||||||
| 	has, err := db.GetEngine(db.DefaultContext).Get(email) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} else if !has { |  | ||||||
| 		return user_model.ErrEmailAddressNotExist{Email: email.Email} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if !email.IsActivated { |  | ||||||
| 		return user_model.ErrEmailNotActivated |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	user := &user_model.User{} |  | ||||||
| 	has, err = db.GetEngine(db.DefaultContext).ID(email.UID).Get(user) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} else if !has { |  | ||||||
| 		return user_model.ErrUserNotExist{ |  | ||||||
| 			UID:   email.UID, |  | ||||||
| 			Name:  "", |  | ||||||
| 			KeyID: 0, |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 	sess := db.GetEngine(ctx) |  | ||||||
|  |  | ||||||
| 	// 1. Update user table |  | ||||||
| 	user.Email = email.Email |  | ||||||
| 	if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// 2. Update old primary email |  | ||||||
| 	if _, err = sess.Where("uid=? AND is_primary=?", email.UID, true).Cols("is_primary").Update(&user_model.EmailAddress{ |  | ||||||
| 		IsPrimary: false, |  | ||||||
| 	}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// 3. update new primary email |  | ||||||
| 	email.IsPrimary = true |  | ||||||
| 	if _, err = sess.ID(email.ID).Cols("is_primary").Update(email); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return committer.Commit() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // VerifyActiveEmailCode verifies active email code when active account |  | ||||||
| func VerifyActiveEmailCode(code, email string) *user_model.EmailAddress { |  | ||||||
| 	minutes := setting.Service.ActiveCodeLives |  | ||||||
|  |  | ||||||
| 	if user := user_model.GetVerifyUser(code); user != nil { |  | ||||||
| 		// time limit code |  | ||||||
| 		prefix := code[:base.TimeLimitCodeLength] |  | ||||||
| 		data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands) |  | ||||||
|  |  | ||||||
| 		if base.VerifyTimeLimitCode(data, minutes, prefix) { |  | ||||||
| 			emailAddress := &user_model.EmailAddress{UID: user.ID, Email: email} |  | ||||||
| 			if has, _ := db.GetEngine(db.DefaultContext).Get(emailAddress); has { |  | ||||||
| 				return emailAddress |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SearchEmailOrderBy is used to sort the results from SearchEmails() |  | ||||||
| type SearchEmailOrderBy string |  | ||||||
|  |  | ||||||
| func (s SearchEmailOrderBy) String() string { |  | ||||||
| 	return string(s) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Strings for sorting result |  | ||||||
| const ( |  | ||||||
| 	SearchEmailOrderByEmail        SearchEmailOrderBy = "email_address.lower_email ASC, email_address.is_primary DESC, email_address.id ASC" |  | ||||||
| 	SearchEmailOrderByEmailReverse SearchEmailOrderBy = "email_address.lower_email DESC, email_address.is_primary ASC, email_address.id DESC" |  | ||||||
| 	SearchEmailOrderByName         SearchEmailOrderBy = "`user`.lower_name ASC, email_address.is_primary DESC, email_address.id ASC" |  | ||||||
| 	SearchEmailOrderByNameReverse  SearchEmailOrderBy = "`user`.lower_name DESC, email_address.is_primary ASC, email_address.id DESC" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // SearchEmailOptions are options to search e-mail addresses for the admin panel |  | ||||||
| type SearchEmailOptions struct { |  | ||||||
| 	db.ListOptions |  | ||||||
| 	Keyword     string |  | ||||||
| 	SortType    SearchEmailOrderBy |  | ||||||
| 	IsPrimary   util.OptionalBool |  | ||||||
| 	IsActivated util.OptionalBool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SearchEmailResult is an e-mail address found in the user or email_address table |  | ||||||
| type SearchEmailResult struct { |  | ||||||
| 	UID         int64 |  | ||||||
| 	Email       string |  | ||||||
| 	IsActivated bool |  | ||||||
| 	IsPrimary   bool |  | ||||||
| 	// From User |  | ||||||
| 	Name     string |  | ||||||
| 	FullName string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SearchEmails takes options i.e. keyword and part of email name to search, |  | ||||||
| // it returns results in given range and number of total results. |  | ||||||
| func SearchEmails(opts *SearchEmailOptions) ([]*SearchEmailResult, int64, error) { |  | ||||||
| 	var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeIndividual} |  | ||||||
| 	if len(opts.Keyword) > 0 { |  | ||||||
| 		likeStr := "%" + strings.ToLower(opts.Keyword) + "%" |  | ||||||
| 		cond = cond.And(builder.Or( |  | ||||||
| 			builder.Like{"lower(`user`.full_name)", likeStr}, |  | ||||||
| 			builder.Like{"`user`.lower_name", likeStr}, |  | ||||||
| 			builder.Like{"email_address.lower_email", likeStr}, |  | ||||||
| 		)) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch { |  | ||||||
| 	case opts.IsPrimary.IsTrue(): |  | ||||||
| 		cond = cond.And(builder.Eq{"email_address.is_primary": true}) |  | ||||||
| 	case opts.IsPrimary.IsFalse(): |  | ||||||
| 		cond = cond.And(builder.Eq{"email_address.is_primary": false}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch { |  | ||||||
| 	case opts.IsActivated.IsTrue(): |  | ||||||
| 		cond = cond.And(builder.Eq{"email_address.is_activated": true}) |  | ||||||
| 	case opts.IsActivated.IsFalse(): |  | ||||||
| 		cond = cond.And(builder.Eq{"email_address.is_activated": false}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	count, err := db.GetEngine(db.DefaultContext).Join("INNER", "`user`", "`user`.ID = email_address.uid"). |  | ||||||
| 		Where(cond).Count(new(user_model.EmailAddress)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, 0, fmt.Errorf("Count: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	orderby := opts.SortType.String() |  | ||||||
| 	if orderby == "" { |  | ||||||
| 		orderby = SearchEmailOrderByEmail.String() |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	opts.SetDefaultValues() |  | ||||||
|  |  | ||||||
| 	emails := make([]*SearchEmailResult, 0, opts.PageSize) |  | ||||||
| 	err = db.GetEngine(db.DefaultContext).Table("email_address"). |  | ||||||
| 		Select("email_address.*, `user`.name, `user`.full_name"). |  | ||||||
| 		Join("INNER", "`user`", "`user`.ID = email_address.uid"). |  | ||||||
| 		Where(cond). |  | ||||||
| 		OrderBy(orderby). |  | ||||||
| 		Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). |  | ||||||
| 		Find(&emails) |  | ||||||
|  |  | ||||||
| 	return emails, count, err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ActivateUserEmail will change the activated state of an email address, |  | ||||||
| // either primary or secondary (all in the email_address table) |  | ||||||
| func ActivateUserEmail(userID int64, email string, activate bool) (err error) { |  | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 	sess := db.GetEngine(ctx) |  | ||||||
|  |  | ||||||
| 	// Activate/deactivate a user's secondary email address |  | ||||||
| 	// First check if there's another user active with the same address |  | ||||||
| 	addr := user_model.EmailAddress{UID: userID, LowerEmail: strings.ToLower(email)} |  | ||||||
| 	if has, err := sess.Get(&addr); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} else if !has { |  | ||||||
| 		return fmt.Errorf("no such email: %d (%s)", userID, email) |  | ||||||
| 	} |  | ||||||
| 	if addr.IsActivated == activate { |  | ||||||
| 		// Already in the desired state; no action |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	if activate { |  | ||||||
| 		if used, err := user_model.IsEmailActive(ctx, email, addr.ID); err != nil { |  | ||||||
| 			return fmt.Errorf("unable to check isEmailActive() for %s: %v", email, err) |  | ||||||
| 		} else if used { |  | ||||||
| 			return user_model.ErrEmailAlreadyUsed{Email: email} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if err = updateActivation(sess, &addr, activate); err != nil { |  | ||||||
| 		return fmt.Errorf("unable to updateActivation() for %d:%s: %w", addr.ID, addr.Email, err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Activate/deactivate a user's primary email address and account |  | ||||||
| 	if addr.IsPrimary { |  | ||||||
| 		user := user_model.User{ID: userID, Email: email} |  | ||||||
| 		if has, err := sess.Get(&user); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} else if !has { |  | ||||||
| 			return fmt.Errorf("no user with ID: %d and Email: %s", userID, email) |  | ||||||
| 		} |  | ||||||
| 		// The user's activation state should be synchronized with the primary email |  | ||||||
| 		if user.IsActive != activate { |  | ||||||
| 			user.IsActive = activate |  | ||||||
| 			if user.Rands, err = user_model.GetUserSalt(); err != nil { |  | ||||||
| 				return fmt.Errorf("unable to generate salt: %v", err) |  | ||||||
| 			} |  | ||||||
| 			if err = user_model.UpdateUserColsEngine(sess, &user, "is_active", "rands"); err != nil { |  | ||||||
| 				return fmt.Errorf("unable to updateUserCols() for user ID: %d: %v", userID, err) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return committer.Commit() |  | ||||||
| } |  | ||||||
| @@ -1,137 +0,0 @@ | |||||||
| // Copyright 2021 The Gitea Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" |  | ||||||
| 	"code.gitea.io/gitea/modules/util" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestMakeEmailPrimary(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	email := &user_model.EmailAddress{ |  | ||||||
| 		Email: "user567890@example.com", |  | ||||||
| 	} |  | ||||||
| 	err := MakeEmailPrimary(email) |  | ||||||
| 	assert.Error(t, err) |  | ||||||
| 	assert.EqualError(t, err, user_model.ErrEmailAddressNotExist{Email: email.Email}.Error()) |  | ||||||
|  |  | ||||||
| 	email = &user_model.EmailAddress{ |  | ||||||
| 		Email: "user11@example.com", |  | ||||||
| 	} |  | ||||||
| 	err = MakeEmailPrimary(email) |  | ||||||
| 	assert.Error(t, err) |  | ||||||
| 	assert.EqualError(t, err, user_model.ErrEmailNotActivated.Error()) |  | ||||||
|  |  | ||||||
| 	email = &user_model.EmailAddress{ |  | ||||||
| 		Email: "user9999999@example.com", |  | ||||||
| 	} |  | ||||||
| 	err = MakeEmailPrimary(email) |  | ||||||
| 	assert.Error(t, err) |  | ||||||
| 	assert.True(t, user_model.IsErrUserNotExist(err)) |  | ||||||
|  |  | ||||||
| 	email = &user_model.EmailAddress{ |  | ||||||
| 		Email: "user101@example.com", |  | ||||||
| 	} |  | ||||||
| 	err = MakeEmailPrimary(email) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	user, _ := user_model.GetUserByID(int64(10)) |  | ||||||
| 	assert.Equal(t, "user101@example.com", user.Email) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestActivate(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	email := &user_model.EmailAddress{ |  | ||||||
| 		ID:    int64(1), |  | ||||||
| 		UID:   int64(1), |  | ||||||
| 		Email: "user11@example.com", |  | ||||||
| 	} |  | ||||||
| 	assert.NoError(t, ActivateEmail(email)) |  | ||||||
|  |  | ||||||
| 	emails, _ := user_model.GetEmailAddresses(int64(1)) |  | ||||||
| 	assert.Len(t, emails, 3) |  | ||||||
| 	assert.True(t, emails[0].IsActivated) |  | ||||||
| 	assert.True(t, emails[0].IsPrimary) |  | ||||||
| 	assert.False(t, emails[1].IsPrimary) |  | ||||||
| 	assert.True(t, emails[2].IsActivated) |  | ||||||
| 	assert.False(t, emails[2].IsPrimary) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestListEmails(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	// Must find all users and their emails |  | ||||||
| 	opts := &SearchEmailOptions{ |  | ||||||
| 		ListOptions: db.ListOptions{ |  | ||||||
| 			PageSize: 10000, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	emails, count, err := SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.NotEqual(t, int64(0), count) |  | ||||||
| 	assert.True(t, count > 5) |  | ||||||
|  |  | ||||||
| 	contains := func(match func(s *SearchEmailResult) bool) bool { |  | ||||||
| 		for _, v := range emails { |  | ||||||
| 			if match(v) { |  | ||||||
| 				return true |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 18 })) |  | ||||||
| 	// 'user3' is an organization |  | ||||||
| 	assert.False(t, contains(func(s *SearchEmailResult) bool { return s.UID == 3 })) |  | ||||||
|  |  | ||||||
| 	// Must find no records |  | ||||||
| 	opts = &SearchEmailOptions{Keyword: "NOTFOUND"} |  | ||||||
| 	emails, count, err = SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.Equal(t, int64(0), count) |  | ||||||
|  |  | ||||||
| 	// Must find users 'user2', 'user28', etc. |  | ||||||
| 	opts = &SearchEmailOptions{Keyword: "user2"} |  | ||||||
| 	emails, count, err = SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.NotEqual(t, int64(0), count) |  | ||||||
| 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 2 })) |  | ||||||
| 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.UID == 27 })) |  | ||||||
|  |  | ||||||
| 	// Must find only primary addresses (i.e. from the `user` table) |  | ||||||
| 	opts = &SearchEmailOptions{IsPrimary: util.OptionalBoolTrue} |  | ||||||
| 	emails, _, err = SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, contains(func(s *SearchEmailResult) bool { return s.IsPrimary })) |  | ||||||
| 	assert.False(t, contains(func(s *SearchEmailResult) bool { return !s.IsPrimary })) |  | ||||||
|  |  | ||||||
| 	// Must find only inactive addresses (i.e. not validated) |  | ||||||
| 	opts = &SearchEmailOptions{IsActivated: util.OptionalBoolFalse} |  | ||||||
| 	emails, _, err = SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, contains(func(s *SearchEmailResult) bool { return !s.IsActivated })) |  | ||||||
| 	assert.False(t, contains(func(s *SearchEmailResult) bool { return s.IsActivated })) |  | ||||||
|  |  | ||||||
| 	// Must find more than one page, but retrieve only one |  | ||||||
| 	opts = &SearchEmailOptions{ |  | ||||||
| 		ListOptions: db.ListOptions{ |  | ||||||
| 			PageSize: 5, |  | ||||||
| 			Page:     1, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	emails, count, err = SearchEmails(opts) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.Len(t, emails, 5) |  | ||||||
| 	assert.Greater(t, count, int64(len(emails))) |  | ||||||
| } |  | ||||||
| @@ -8,30 +8,17 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/login" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // UserList is a list of user. |  | ||||||
| // This type provide valuable methods to retrieve information for a group of users efficiently. |  | ||||||
| type UserList []*user_model.User |  | ||||||
|  |  | ||||||
| func (users UserList) getUserIDs() []int64 { |  | ||||||
| 	userIDs := make([]int64, len(users)) |  | ||||||
| 	for _, user := range users { |  | ||||||
| 		userIDs = append(userIDs, user.ID) // Considering that user id are unique in the list |  | ||||||
| 	} |  | ||||||
| 	return userIDs |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsUserOrgOwner returns true if user is in the owner team of given organization. | // IsUserOrgOwner returns true if user is in the owner team of given organization. | ||||||
| func (users UserList) IsUserOrgOwner(orgID int64) map[int64]bool { | func IsUserOrgOwner(users user_model.UserList, orgID int64) map[int64]bool { | ||||||
| 	results := make(map[int64]bool, len(users)) | 	results := make(map[int64]bool, len(users)) | ||||||
| 	for _, user := range users { | 	for _, user := range users { | ||||||
| 		results[user.ID] = false // Set default to false | 		results[user.ID] = false // Set default to false | ||||||
| 	} | 	} | ||||||
| 	ownerMaps, err := users.loadOrganizationOwners(db.GetEngine(db.DefaultContext), orgID) | 	ownerMaps, err := loadOrganizationOwners(db.GetEngine(db.DefaultContext), users, orgID) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		for _, owner := range ownerMaps { | 		for _, owner := range ownerMaps { | ||||||
| 			results[owner.UID] = true | 			results[owner.UID] = true | ||||||
| @@ -40,7 +27,7 @@ func (users UserList) IsUserOrgOwner(orgID int64) map[int64]bool { | |||||||
| 	return results | 	return results | ||||||
| } | } | ||||||
|  |  | ||||||
| func (users UserList) loadOrganizationOwners(e db.Engine, orgID int64) (map[int64]*TeamUser, error) { | func loadOrganizationOwners(e db.Engine, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) { | ||||||
| 	if len(users) == 0 { | 	if len(users) == 0 { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
| @@ -53,7 +40,7 @@ func (users UserList) loadOrganizationOwners(e db.Engine, orgID int64) (map[int6 | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	userIDs := users.getUserIDs() | 	userIDs := users.GetUserIDs() | ||||||
| 	ownerMaps := make(map[int64]*TeamUser) | 	ownerMaps := make(map[int64]*TeamUser) | ||||||
| 	err = e.In("uid", userIDs). | 	err = e.In("uid", userIDs). | ||||||
| 		And("org_id=?", orgID). | 		And("org_id=?", orgID). | ||||||
| @@ -64,47 +51,3 @@ func (users UserList) loadOrganizationOwners(e db.Engine, orgID int64) (map[int6 | |||||||
| 	} | 	} | ||||||
| 	return ownerMaps, nil | 	return ownerMaps, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetTwoFaStatus return state of 2FA enrollement |  | ||||||
| func (users UserList) GetTwoFaStatus() map[int64]bool { |  | ||||||
| 	results := make(map[int64]bool, len(users)) |  | ||||||
| 	for _, user := range users { |  | ||||||
| 		results[user.ID] = false // Set default to false |  | ||||||
| 	} |  | ||||||
| 	tokenMaps, err := users.loadTwoFactorStatus(db.GetEngine(db.DefaultContext)) |  | ||||||
| 	if err == nil { |  | ||||||
| 		for _, token := range tokenMaps { |  | ||||||
| 			results[token.UID] = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return results |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFactor, error) { |  | ||||||
| 	if len(users) == 0 { |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	userIDs := users.getUserIDs() |  | ||||||
| 	tokenMaps := make(map[int64]*login.TwoFactor, len(userIDs)) |  | ||||||
| 	err := e. |  | ||||||
| 		In("uid", userIDs). |  | ||||||
| 		Find(&tokenMaps) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, fmt.Errorf("find two factor: %v", err) |  | ||||||
| 	} |  | ||||||
| 	return tokenMaps, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetUsersByIDs returns all resolved users from a list of Ids. |  | ||||||
| func GetUsersByIDs(ids []int64) (UserList, error) { |  | ||||||
| 	ous := make([]*user_model.User, 0, len(ids)) |  | ||||||
| 	if len(ids) == 0 { |  | ||||||
| 		return ous, nil |  | ||||||
| 	} |  | ||||||
| 	err := db.GetEngine(db.DefaultContext).In("id", ids). |  | ||||||
| 		Asc("name"). |  | ||||||
| 		Find(&ous) |  | ||||||
| 	return ous, err |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -64,32 +64,5 @@ func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bo | |||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	members, _, err := org.GetMembers() | 	members, _, err := org.GetMembers() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, expected, members.IsUserOrgOwner(orgID)) | 	assert.Equal(t, expected, IsUserOrgOwner(members, orgID)) | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUserListIsTwoFaEnrolled(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	tt := []struct { |  | ||||||
| 		orgid    int64 |  | ||||||
| 		expected map[int64]bool |  | ||||||
| 	}{ |  | ||||||
| 		{3, map[int64]bool{2: false, 4: false, 28: false}}, |  | ||||||
| 		{6, map[int64]bool{5: false, 28: false}}, |  | ||||||
| 		{7, map[int64]bool{5: false}}, |  | ||||||
| 		{25, map[int64]bool{24: true}}, |  | ||||||
| 		{22, map[int64]bool{}}, |  | ||||||
| 	} |  | ||||||
| 	for _, v := range tt { |  | ||||||
| 		t.Run(fmt.Sprintf("IsTwoFaEnrolledOfOrdIg%d", v.orgid), func(t *testing.T) { |  | ||||||
| 			testUserListIsTwoFaEnrolled(t, v.orgid, v.expected) |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func testUserListIsTwoFaEnrolled(t *testing.T, orgID int64, expected map[int64]bool) { |  | ||||||
| 	org, err := GetOrgByID(orgID) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	members, _, err := org.GetMembers() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.Equal(t, expected, members.GetTwoFaStatus()) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -273,7 +273,7 @@ func GetIssueSubscribers(ctx *context.APIContext) { | |||||||
| 		userIDs = append(userIDs, iw.UserID) | 		userIDs = append(userIDs, iw.UserID) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	users, err := models.GetUsersByIDs(userIDs) | 	users, err := user_model.GetUsersByIDs(userIDs) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) | 		ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) | ||||||
| 		return | 		return | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  |  | ||||||
| 	"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" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| @@ -29,7 +28,7 @@ func Emails(ctx *context.Context) { | |||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminEmails"] = true | 	ctx.Data["PageIsAdminEmails"] = true | ||||||
|  |  | ||||||
| 	opts := &models.SearchEmailOptions{ | 	opts := &user_model.SearchEmailOptions{ | ||||||
| 		ListOptions: db.ListOptions{ | 		ListOptions: db.ListOptions{ | ||||||
| 			PageSize: setting.UI.Admin.UserPagingNum, | 			PageSize: setting.UI.Admin.UserPagingNum, | ||||||
| 			Page:     ctx.FormInt("page"), | 			Page:     ctx.FormInt("page"), | ||||||
| @@ -41,31 +40,31 @@ func Emails(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	type ActiveEmail struct { | 	type ActiveEmail struct { | ||||||
| 		models.SearchEmailResult | 		user_model.SearchEmailResult | ||||||
| 		CanChange bool | 		CanChange bool | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		baseEmails []*models.SearchEmailResult | 		baseEmails []*user_model.SearchEmailResult | ||||||
| 		emails     []ActiveEmail | 		emails     []ActiveEmail | ||||||
| 		count      int64 | 		count      int64 | ||||||
| 		err        error | 		err        error | ||||||
| 		orderBy    models.SearchEmailOrderBy | 		orderBy    user_model.SearchEmailOrderBy | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	ctx.Data["SortType"] = ctx.FormString("sort") | 	ctx.Data["SortType"] = ctx.FormString("sort") | ||||||
| 	switch ctx.FormString("sort") { | 	switch ctx.FormString("sort") { | ||||||
| 	case "email": | 	case "email": | ||||||
| 		orderBy = models.SearchEmailOrderByEmail | 		orderBy = user_model.SearchEmailOrderByEmail | ||||||
| 	case "reverseemail": | 	case "reverseemail": | ||||||
| 		orderBy = models.SearchEmailOrderByEmailReverse | 		orderBy = user_model.SearchEmailOrderByEmailReverse | ||||||
| 	case "username": | 	case "username": | ||||||
| 		orderBy = models.SearchEmailOrderByName | 		orderBy = user_model.SearchEmailOrderByName | ||||||
| 	case "reverseusername": | 	case "reverseusername": | ||||||
| 		orderBy = models.SearchEmailOrderByNameReverse | 		orderBy = user_model.SearchEmailOrderByNameReverse | ||||||
| 	default: | 	default: | ||||||
| 		ctx.Data["SortType"] = "email" | 		ctx.Data["SortType"] = "email" | ||||||
| 		orderBy = models.SearchEmailOrderByEmail | 		orderBy = user_model.SearchEmailOrderByEmail | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	opts.Keyword = ctx.FormTrim("q") | 	opts.Keyword = ctx.FormTrim("q") | ||||||
| @@ -78,7 +77,7 @@ func Emails(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { | 	if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) { | ||||||
| 		baseEmails, count, err = models.SearchEmails(opts) | 		baseEmails, count, err = user_model.SearchEmails(opts) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("SearchEmails", err) | 			ctx.ServerError("SearchEmails", err) | ||||||
| 			return | 			return | ||||||
| @@ -127,7 +126,7 @@ func ActivateEmail(ctx *context.Context) { | |||||||
|  |  | ||||||
| 	log.Info("Changing activation for User ID: %d, email: %s, primary: %v to %v", uid, email, primary, activate) | 	log.Info("Changing activation for User ID: %d, email: %s, primary: %v to %v", uid, email, primary, activate) | ||||||
|  |  | ||||||
| 	if err := models.ActivateUserEmail(uid, email, activate); err != nil { | 	if err := user_model.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 user_model.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")) | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ import ( | |||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
| 	"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" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
| @@ -79,7 +78,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, | |||||||
| 	ctx.Data["Keyword"] = opts.Keyword | 	ctx.Data["Keyword"] = opts.Keyword | ||||||
| 	ctx.Data["Total"] = count | 	ctx.Data["Total"] = count | ||||||
| 	ctx.Data["Users"] = users | 	ctx.Data["Users"] = users | ||||||
| 	ctx.Data["UsersTwoFaStatus"] = models.UserList(users).GetTwoFaStatus() | 	ctx.Data["UsersTwoFaStatus"] = user_model.UserList(users).GetTwoFaStatus() | ||||||
| 	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail | 	ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail | ||||||
| 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled | ||||||
|  |  | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ func Members(ctx *context.Context) { | |||||||
| 	ctx.Data["Page"] = pager | 	ctx.Data["Page"] = pager | ||||||
| 	ctx.Data["Members"] = members | 	ctx.Data["Members"] = members | ||||||
| 	ctx.Data["MembersIsPublicMember"] = membersIsPublic | 	ctx.Data["MembersIsPublicMember"] = membersIsPublic | ||||||
| 	ctx.Data["MembersIsUserOrgOwner"] = members.IsUserOrgOwner(org.ID) | 	ctx.Data["MembersIsUserOrgOwner"] = models.IsUserOrgOwner(members, org.ID) | ||||||
| 	ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus() | 	ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus() | ||||||
|  |  | ||||||
| 	ctx.HTML(http.StatusOK, tplMembers) | 	ctx.HTML(http.StatusOK, tplMembers) | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"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" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -781,7 +780,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_mode | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// update external user information | 		// update external user information | ||||||
| 		if err := models.UpdateExternalUser(u, gothUser); err != nil { | 		if err := user_model.UpdateExternalUser(u, gothUser); err != nil { | ||||||
| 			log.Error("UpdateExternalUser failed: %v", err) | 			log.Error("UpdateExternalUser failed: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -844,11 +843,11 @@ func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, r | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// search in external linked users | 	// search in external linked users | ||||||
| 	externalLoginUser := &models.ExternalLoginUser{ | 	externalLoginUser := &user_model.ExternalLoginUser{ | ||||||
| 		ExternalID:    gothUser.UserID, | 		ExternalID:    gothUser.UserID, | ||||||
| 		LoginSourceID: loginSource.ID, | 		LoginSourceID: loginSource.ID, | ||||||
| 	} | 	} | ||||||
| 	hasUser, err = models.GetExternalLogin(externalLoginUser) | 	hasUser, err = user_model.GetExternalLogin(externalLoginUser) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, goth.User{}, err | 		return nil, goth.User{}, err | ||||||
| 	} | 	} | ||||||
| @@ -1355,7 +1354,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. | |||||||
|  |  | ||||||
| 	// update external user information | 	// update external user information | ||||||
| 	if gothUser != nil { | 	if gothUser != nil { | ||||||
| 		if err := models.UpdateExternalUser(u, *gothUser); err != nil { | 		if err := user_model.UpdateExternalUser(u, *gothUser); err != nil { | ||||||
| 			log.Error("UpdateExternalUser failed: %v", err) | 			log.Error("UpdateExternalUser failed: %v", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -1477,7 +1476,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.ActivateUserEmail(user.ID, user.Email, true); err != nil { | 	if err := user_model.ActivateUserEmail(user.ID, user.Email, true); err != nil { | ||||||
| 		log.Error("Unable to activate email for user: %-v with email: %s: %v", user, user.Email, err) | 		log.Error("Unable to activate email for user: %-v with email: %s: %v", user, user.Email, err) | ||||||
| 		ctx.ServerError("ActivateUserEmail", err) | 		ctx.ServerError("ActivateUserEmail", err) | ||||||
| 		return | 		return | ||||||
| @@ -1505,8 +1504,8 @@ func ActivateEmail(ctx *context.Context) { | |||||||
| 	emailStr := ctx.FormString("email") | 	emailStr := ctx.FormString("email") | ||||||
|  |  | ||||||
| 	// Verify code. | 	// Verify code. | ||||||
| 	if email := models.VerifyActiveEmailCode(code, emailStr); email != nil { | 	if email := user_model.VerifyActiveEmailCode(code, emailStr); email != nil { | ||||||
| 		if err := models.ActivateEmail(email); err != nil { | 		if err := user_model.ActivateEmail(email); err != nil { | ||||||
| 			ctx.ServerError("ActivateEmail", err) | 			ctx.ServerError("ActivateEmail", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -94,7 +94,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(&user_model.EmailAddress{ID: ctx.FormInt64("id")}); err != nil { | 		if err := user_model.MakeEmailPrimary(&user_model.EmailAddress{ID: ctx.FormInt64("id")}); err != nil { | ||||||
| 			ctx.ServerError("MakeEmailPrimary", err) | 			ctx.ServerError("MakeEmailPrimary", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ func DeleteAccountLink(ctx *context.Context) { | |||||||
| 	if id <= 0 { | 	if id <= 0 { | ||||||
| 		ctx.Flash.Error("Account link id is not given") | 		ctx.Flash.Error("Account link id is not given") | ||||||
| 	} else { | 	} else { | ||||||
| 		if _, err := models.RemoveAccountLink(ctx.User, id); err != nil { | 		if _, err := user_model.RemoveAccountLink(ctx.User, id); err != nil { | ||||||
| 			ctx.Flash.Error("RemoveAccountLink: " + err.Error()) | 			ctx.Flash.Error("RemoveAccountLink: " + err.Error()) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) | 			ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) | ||||||
| @@ -76,7 +76,7 @@ func loadSecurityData(ctx *context.Context) { | |||||||
| 	} | 	} | ||||||
| 	ctx.Data["Tokens"] = tokens | 	ctx.Data["Tokens"] = tokens | ||||||
|  |  | ||||||
| 	accountLinks, err := models.ListAccountLinks(ctx.User) | 	accountLinks, err := user_model.ListAccountLinks(ctx.User) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("ListAccountLinks", err) | 		ctx.ServerError("ListAccountLinks", err) | ||||||
| 		return | 		return | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ | |||||||
| package auth | package auth | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"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" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -22,7 +21,7 @@ func DeleteLoginSource(source *login.Source) error { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	count, err = db.GetEngine(db.DefaultContext).Count(&models.ExternalLoginUser{LoginSourceID: source.ID}) | 	count, err = db.GetEngine(db.DefaultContext).Count(&user_model.ExternalLoginUser{LoginSourceID: source.ID}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} else if count > 0 { | 	} else if count > 0 { | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	externalLoginUser := &models.ExternalLoginUser{ | 	externalLoginUser := &user_model.ExternalLoginUser{ | ||||||
| 		ExternalID:        gothUser.UserID, | 		ExternalID:        gothUser.UserID, | ||||||
| 		UserID:            user.ID, | 		UserID:            user.ID, | ||||||
| 		LoginSourceID:     loginSource.ID, | 		LoginSourceID:     loginSource.ID, | ||||||
| @@ -42,7 +42,7 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error { | |||||||
| 		ExpiresAt:         gothUser.ExpiresAt, | 		ExpiresAt:         gothUser.ExpiresAt, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := models.LinkExternalToUser(user, externalLoginUser); err != nil { | 	if err := user_model.LinkExternalToUser(user, externalLoginUser); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -260,7 +260,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { | |||||||
| 		tp := g.gitServiceType.Name() | 		tp := g.gitServiceType.Name() | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", release.PublisherID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", release.PublisherID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -400,7 +400,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error { | |||||||
| 		tp := g.gitServiceType.Name() | 		tp := g.gitServiceType.Name() | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", issue.PosterID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", issue.PosterID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -425,7 +425,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error { | |||||||
| 			userid, ok := g.userMap[reaction.UserID] | 			userid, ok := g.userMap[reaction.UserID] | ||||||
| 			if !ok && tp != "" { | 			if !ok && tp != "" { | ||||||
| 				var err error | 				var err error | ||||||
| 				userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | 				userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Error("GetUserIDByExternalUserID: %v", err) | 					log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 				} | 				} | ||||||
| @@ -483,7 +483,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { | |||||||
| 		tp := g.gitServiceType.Name() | 		tp := g.gitServiceType.Name() | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", comment.PosterID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", comment.PosterID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -520,7 +520,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { | |||||||
| 			userid, ok := g.userMap[reaction.UserID] | 			userid, ok := g.userMap[reaction.UserID] | ||||||
| 			if !ok && tp != "" { | 			if !ok && tp != "" { | ||||||
| 				var err error | 				var err error | ||||||
| 				userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | 				userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Error("GetUserIDByExternalUserID: %v", err) | 					log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 				} | 				} | ||||||
| @@ -564,7 +564,7 @@ func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error | |||||||
| 		tp := g.gitServiceType.Name() | 		tp := g.gitServiceType.Name() | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -744,7 +744,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | |||||||
| 	userid, ok := g.userMap[pr.PosterID] | 	userid, ok := g.userMap[pr.PosterID] | ||||||
| 	if !ok && tp != "" { | 	if !ok && tp != "" { | ||||||
| 		var err error | 		var err error | ||||||
| 		userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID)) | 		userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetUserIDByExternalUserID: %v", err) | 			log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -766,7 +766,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR | |||||||
| 		userid, ok := g.userMap[reaction.UserID] | 		userid, ok := g.userMap[reaction.UserID] | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", reaction.UserID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
| @@ -850,7 +850,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error { | |||||||
| 		tp := g.gitServiceType.Name() | 		tp := g.gitServiceType.Name() | ||||||
| 		if !ok && tp != "" { | 		if !ok && tp != "" { | ||||||
| 			var err error | 			var err error | ||||||
| 			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", review.ReviewerID)) | 			userid, err = user_model.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", review.ReviewerID)) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("GetUserIDByExternalUserID: %v", err) | 				log.Error("GetUserIDByExternalUserID: %v", err) | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -9,6 +9,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/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
| ) | ) | ||||||
| @@ -45,7 +46,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ | |||||||
| 		default: | 		default: | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		users, err := models.FindExternalUsersByProvider(models.FindExternalUserOptions{ | 		users, err := user_model.FindExternalUsersByProvider(user_model.FindExternalUserOptions{ | ||||||
| 			Provider: provider, | 			Provider: provider, | ||||||
| 			Start:    start, | 			Start:    start, | ||||||
| 			Limit:    batchSize, | 			Limit:    batchSize, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Lunny Xiao
					Lunny Xiao