]> source.dussan.org Git - gitea.git/commitdiff
Move more model into models/user (#17826)
authorLunny Xiao <xiaolunwen@gmail.com>
Sun, 28 Nov 2021 14:11:58 +0000 (22:11 +0800)
committerGitHub <noreply@github.com>
Sun, 28 Nov 2021 14:11:58 +0000 (22:11 +0800)
* Move more model into models/user

* Remove unnecessary comment

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
24 files changed:
models/error.go
models/external_login_user.go [deleted file]
models/migrate.go
models/org.go
models/user.go
models/user/email_address.go
models/user/email_address_test.go
models/user/external_login_user.go [new file with mode: 0644]
models/user/list.go [new file with mode: 0644]
models/user_email.go [deleted file]
models/user_email_test.go [deleted file]
models/userlist.go
models/userlist_test.go
routers/api/v1/repo/issue_subscription.go
routers/web/admin/emails.go
routers/web/explore/user.go
routers/web/org/members.go
routers/web/user/auth.go
routers/web/user/setting/account.go
routers/web/user/setting/security.go
services/auth/login_source.go
services/externalaccount/user.go
services/migrations/gitea_uploader.go
services/migrations/update.go

index 0b6c22450d2203a677d0ac47ebc6b75ad00cccd0..36c70e49ad8c6ea05d478f957801ef432a93597a 100644 (file)
@@ -1624,46 +1624,6 @@ func (err ErrUploadNotExist) Error() string {
        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)
-}
-
 // .___                            ________                                   .___                   .__
 // |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______
 // |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/
diff --git a/models/external_login_user.go b/models/external_login_user.go
deleted file mode 100644 (file)
index 4be1307..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-// 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 models
-
-import (
-       "time"
-
-       "code.gitea.io/gitea/models/db"
-       "code.gitea.io/gitea/models/login"
-       user_model "code.gitea.io/gitea/models/user"
-       "code.gitea.io/gitea/modules/structs"
-
-       "github.com/markbates/goth"
-       "xorm.io/builder"
-)
-
-// ExternalLoginUser makes the connecting between some existing user and additional external login sources
-type ExternalLoginUser struct {
-       ExternalID        string                 `xorm:"pk NOT NULL"`
-       UserID            int64                  `xorm:"INDEX NOT NULL"`
-       LoginSourceID     int64                  `xorm:"pk NOT NULL"`
-       RawData           map[string]interface{} `xorm:"TEXT JSON"`
-       Provider          string                 `xorm:"index VARCHAR(25)"`
-       Email             string
-       Name              string
-       FirstName         string
-       LastName          string
-       NickName          string
-       Description       string
-       AvatarURL         string
-       Location          string
-       AccessToken       string `xorm:"TEXT"`
-       AccessTokenSecret string `xorm:"TEXT"`
-       RefreshToken      string `xorm:"TEXT"`
-       ExpiresAt         time.Time
-}
-
-func init() {
-       db.RegisterModel(new(ExternalLoginUser))
-}
-
-// GetExternalLogin checks if a externalID in loginSourceID scope already exists
-func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) {
-       return db.GetEngine(db.DefaultContext).Get(externalLoginUser)
-}
-
-// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
-func ListAccountLinks(user *user_model.User) ([]*ExternalLoginUser, error) {
-       externalAccounts := make([]*ExternalLoginUser, 0, 5)
-       err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID).
-               Desc("login_source_id").
-               Find(&externalAccounts)
-       if err != nil {
-               return nil, err
-       }
-
-       return externalAccounts, nil
-}
-
-// LinkExternalToUser link the external user to the user
-func LinkExternalToUser(user *user_model.User, externalLoginUser *ExternalLoginUser) error {
-       has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
-               NoAutoCondition().
-               Exist(externalLoginUser)
-       if err != nil {
-               return err
-       } else if has {
-               return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
-       }
-
-       _, err = db.GetEngine(db.DefaultContext).Insert(externalLoginUser)
-       return err
-}
-
-// RemoveAccountLink will remove all external login sources for the given user
-func RemoveAccountLink(user *user_model.User, loginSourceID int64) (int64, error) {
-       deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID})
-       if err != nil {
-               return deleted, err
-       }
-       if deleted < 1 {
-               return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID}
-       }
-       return deleted, err
-}
-
-// removeAllAccountLinks will remove all external login sources for the given user
-func removeAllAccountLinks(e db.Engine, user *user_model.User) error {
-       _, err := e.Delete(&ExternalLoginUser{UserID: user.ID})
-       return err
-}
-
-// GetUserIDByExternalUserID get user id according to provider and userID
-func GetUserIDByExternalUserID(provider, userID string) (int64, error) {
-       var id int64
-       _, err := db.GetEngine(db.DefaultContext).Table("external_login_user").
-               Select("user_id").
-               Where("provider=?", provider).
-               And("external_id=?", userID).
-               Get(&id)
-       if err != nil {
-               return 0, err
-       }
-       return id, nil
-}
-
-// UpdateExternalUser updates external user's information
-func UpdateExternalUser(user *user_model.User, gothUser goth.User) error {
-       loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider)
-       if err != nil {
-               return err
-       }
-       externalLoginUser := &ExternalLoginUser{
-               ExternalID:        gothUser.UserID,
-               UserID:            user.ID,
-               LoginSourceID:     loginSource.ID,
-               RawData:           gothUser.RawData,
-               Provider:          gothUser.Provider,
-               Email:             gothUser.Email,
-               Name:              gothUser.Name,
-               FirstName:         gothUser.FirstName,
-               LastName:          gothUser.LastName,
-               NickName:          gothUser.NickName,
-               Description:       gothUser.Description,
-               AvatarURL:         gothUser.AvatarURL,
-               Location:          gothUser.Location,
-               AccessToken:       gothUser.AccessToken,
-               AccessTokenSecret: gothUser.AccessTokenSecret,
-               RefreshToken:      gothUser.RefreshToken,
-               ExpiresAt:         gothUser.ExpiresAt,
-       }
-
-       has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).
-               NoAutoCondition().
-               Exist(externalLoginUser)
-       if err != nil {
-               return err
-       } else if !has {
-               return ErrExternalLoginUserNotExist{user.ID, loginSource.ID}
-       }
-
-       _, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser)
-       return err
-}
-
-// FindExternalUserOptions represents an options to find external users
-type FindExternalUserOptions struct {
-       Provider string
-       Limit    int
-       Start    int
-}
-
-func (opts FindExternalUserOptions) toConds() builder.Cond {
-       cond := builder.NewCond()
-       if len(opts.Provider) > 0 {
-               cond = cond.And(builder.Eq{"provider": opts.Provider})
-       }
-       return cond
-}
-
-// FindExternalUsersByProvider represents external users via provider
-func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
-       var users []ExternalLoginUser
-       err := db.GetEngine(db.DefaultContext).Where(opts.toConds()).
-               Limit(opts.Limit, opts.Start).
-               OrderBy("login_source_id ASC, external_id ASC").
-               Find(&users)
-       if err != nil {
-               return nil, err
-       }
-       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)
-}
index efe4bd0c5e6bded019d8c9fe80a40f568c9a7c2b..07d2b0f2d96702287e514bf6f873ed0abded2e04 100644 (file)
@@ -245,3 +245,23 @@ func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID s
                })
        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)
+}
index 040d129692e068abbd210901536cf99ed07c2f0f..becfa4cb05b14b91a340e1fc883bb89ef17fde8f 100644 (file)
@@ -81,7 +81,7 @@ func (org *Organization) LoadTeams() ([]*Team, error) {
 }
 
 // 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{
                OrgID: org.ID,
        })
@@ -149,7 +149,7 @@ func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) {
 }
 
 // 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)
        if err != nil {
                return nil, nil, err
@@ -162,7 +162,7 @@ func FindOrgMembers(opts *FindOrgMembersOpts) (UserList, map[int64]bool, error)
                idsIsPublic[ou.UID] = ou.IsPublic
        }
 
-       users, err := GetUsersByIDs(ids)
+       users, err := user_model.GetUsersByIDs(ids)
        if err != nil {
                return nil, nil, err
        }
index 9816fcc71305b5ca2c2e692e17b676a8c6b673a0..e604c871b252d917d43b3c077ba19a9f3470099a 100644 (file)
@@ -260,7 +260,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
        }
 
        // ***** START: ExternalLoginUser *****
-       if err = removeAllAccountLinks(e, u); err != nil {
+       if err = user_model.RemoveAllAccountLinks(ctx, u); err != nil {
                return fmt.Errorf("ExternalLoginUser: %v", err)
        }
        // ***** END: ExternalLoginUser *****
index 98cd14a6a5212f13d415a4756b17fb8bae11df02..0ff62fb6a8056799d0bb08a3fb8b798d4de044f4 100644 (file)
@@ -13,8 +13,10 @@ import (
        "strings"
 
        "code.gitea.io/gitea/models/db"
+       "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
+       "code.gitea.io/gitea/modules/util"
 
        "xorm.io/builder"
 )
@@ -275,3 +277,247 @@ func DeleteInactiveEmailAddresses(ctx context.Context) error {
                Delete(new(EmailAddress))
        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()
+}
index 642c2b387697f69a18b9d54989386b351ad01209..4a539e150a10d815f6df224ba351554545da5052 100644 (file)
@@ -9,6 +9,7 @@ import (
 
        "code.gitea.io/gitea/models/db"
        "code.gitea.io/gitea/models/unittest"
+       "code.gitea.io/gitea/modules/util"
 
        "github.com/stretchr/testify/assert"
 )
@@ -130,3 +131,124 @@ func TestDeleteEmailAddresses(t *testing.T) {
        err := DeleteEmailAddresses(emails)
        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)))
+}
diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go
new file mode 100644 (file)
index 0000000..354dee0
--- /dev/null
@@ -0,0 +1,207 @@
+// 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 (
+       "context"
+       "fmt"
+       "time"
+
+       "code.gitea.io/gitea/models/db"
+       "code.gitea.io/gitea/models/login"
+
+       "github.com/markbates/goth"
+       "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
+type ExternalLoginUser struct {
+       ExternalID        string                 `xorm:"pk NOT NULL"`
+       UserID            int64                  `xorm:"INDEX NOT NULL"`
+       LoginSourceID     int64                  `xorm:"pk NOT NULL"`
+       RawData           map[string]interface{} `xorm:"TEXT JSON"`
+       Provider          string                 `xorm:"index VARCHAR(25)"`
+       Email             string
+       Name              string
+       FirstName         string
+       LastName          string
+       NickName          string
+       Description       string
+       AvatarURL         string
+       Location          string
+       AccessToken       string `xorm:"TEXT"`
+       AccessTokenSecret string `xorm:"TEXT"`
+       RefreshToken      string `xorm:"TEXT"`
+       ExpiresAt         time.Time
+}
+
+func init() {
+       db.RegisterModel(new(ExternalLoginUser))
+}
+
+// GetExternalLogin checks if a externalID in loginSourceID scope already exists
+func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) {
+       return db.GetEngine(db.DefaultContext).Get(externalLoginUser)
+}
+
+// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
+func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
+       externalAccounts := make([]*ExternalLoginUser, 0, 5)
+       err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.ID).
+               Desc("login_source_id").
+               Find(&externalAccounts)
+       if err != nil {
+               return nil, err
+       }
+
+       return externalAccounts, nil
+}
+
+// LinkExternalToUser link the external user to the user
+func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error {
+       has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
+               NoAutoCondition().
+               Exist(externalLoginUser)
+       if err != nil {
+               return err
+       } else if has {
+               return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
+       }
+
+       _, err = db.GetEngine(db.DefaultContext).Insert(externalLoginUser)
+       return err
+}
+
+// RemoveAccountLink will remove all external login sources for the given user
+func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) {
+       deleted, err := db.GetEngine(db.DefaultContext).Delete(&ExternalLoginUser{UserID: user.ID, LoginSourceID: loginSourceID})
+       if err != nil {
+               return deleted, err
+       }
+       if deleted < 1 {
+               return deleted, ErrExternalLoginUserNotExist{user.ID, loginSourceID}
+       }
+       return deleted, err
+}
+
+// RemoveAllAccountLinks will remove all external login sources for the given user
+func RemoveAllAccountLinks(ctx context.Context, user *User) error {
+       _, err := db.GetEngine(ctx).Delete(&ExternalLoginUser{UserID: user.ID})
+       return err
+}
+
+// GetUserIDByExternalUserID get user id according to provider and userID
+func GetUserIDByExternalUserID(provider, userID string) (int64, error) {
+       var id int64
+       _, err := db.GetEngine(db.DefaultContext).Table("external_login_user").
+               Select("user_id").
+               Where("provider=?", provider).
+               And("external_id=?", userID).
+               Get(&id)
+       if err != nil {
+               return 0, err
+       }
+       return id, nil
+}
+
+// UpdateExternalUser updates external user's information
+func UpdateExternalUser(user *User, gothUser goth.User) error {
+       loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider)
+       if err != nil {
+               return err
+       }
+       externalLoginUser := &ExternalLoginUser{
+               ExternalID:        gothUser.UserID,
+               UserID:            user.ID,
+               LoginSourceID:     loginSource.ID,
+               RawData:           gothUser.RawData,
+               Provider:          gothUser.Provider,
+               Email:             gothUser.Email,
+               Name:              gothUser.Name,
+               FirstName:         gothUser.FirstName,
+               LastName:          gothUser.LastName,
+               NickName:          gothUser.NickName,
+               Description:       gothUser.Description,
+               AvatarURL:         gothUser.AvatarURL,
+               Location:          gothUser.Location,
+               AccessToken:       gothUser.AccessToken,
+               AccessTokenSecret: gothUser.AccessTokenSecret,
+               RefreshToken:      gothUser.RefreshToken,
+               ExpiresAt:         gothUser.ExpiresAt,
+       }
+
+       has, err := db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).
+               NoAutoCondition().
+               Exist(externalLoginUser)
+       if err != nil {
+               return err
+       } else if !has {
+               return ErrExternalLoginUserNotExist{user.ID, loginSource.ID}
+       }
+
+       _, err = db.GetEngine(db.DefaultContext).Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser)
+       return err
+}
+
+// FindExternalUserOptions represents an options to find external users
+type FindExternalUserOptions struct {
+       Provider string
+       Limit    int
+       Start    int
+}
+
+func (opts FindExternalUserOptions) toConds() builder.Cond {
+       cond := builder.NewCond()
+       if len(opts.Provider) > 0 {
+               cond = cond.And(builder.Eq{"provider": opts.Provider})
+       }
+       return cond
+}
+
+// FindExternalUsersByProvider represents external users via provider
+func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
+       var users []ExternalLoginUser
+       err := db.GetEngine(db.DefaultContext).Where(opts.toConds()).
+               Limit(opts.Limit, opts.Start).
+               OrderBy("login_source_id ASC, external_id ASC").
+               Find(&users)
+       if err != nil {
+               return nil, err
+       }
+       return users, nil
+}
diff --git a/models/user/list.go b/models/user/list.go
new file mode 100644 (file)
index 0000000..6ca4613
--- /dev/null
@@ -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
+}
diff --git a/models/user_email.go b/models/user_email.go
deleted file mode 100644 (file)
index 528ebb0..0000000
+++ /dev/null
@@ -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()
-}
diff --git a/models/user_email_test.go b/models/user_email_test.go
deleted file mode 100644 (file)
index a91e24d..0000000
+++ /dev/null
@@ -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)))
-}
index ae39de3da42fa06f85eddcb8eb91bccdd0e020ba..102c587dfe9f48d35323247f7818285b1ed64e86 100644 (file)
@@ -8,30 +8,17 @@ import (
        "fmt"
 
        "code.gitea.io/gitea/models/db"
-       "code.gitea.io/gitea/models/login"
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/log"
 )
 
-// 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.
-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))
        for _, user := range users {
                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 {
                for _, owner := range ownerMaps {
                        results[owner.UID] = true
@@ -40,7 +27,7 @@ func (users UserList) IsUserOrgOwner(orgID int64) map[int64]bool {
        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 {
                return nil, nil
        }
@@ -53,7 +40,7 @@ func (users UserList) loadOrganizationOwners(e db.Engine, orgID int64) (map[int6
                return nil, err
        }
 
-       userIDs := users.getUserIDs()
+       userIDs := users.GetUserIDs()
        ownerMaps := make(map[int64]*TeamUser)
        err = e.In("uid", userIDs).
                And("org_id=?", orgID).
@@ -64,47 +51,3 @@ func (users UserList) loadOrganizationOwners(e db.Engine, orgID int64) (map[int6
        }
        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
-}
index c16a7b6eec498ba5535d37dc0554239fdfc0f741..6776850b602b93f360c4829f8a2269bda178d491 100644 (file)
@@ -64,32 +64,5 @@ func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bo
        assert.NoError(t, err)
        members, _, err := org.GetMembers()
        assert.NoError(t, err)
-       assert.Equal(t, expected, members.IsUserOrgOwner(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())
+       assert.Equal(t, expected, IsUserOrgOwner(members, orgID))
 }
index 09be3cc19315244bbbc4181ef89521ec0f4139ab..ae7661cfab7a3d1d7768c25f877762b11a004175 100644 (file)
@@ -273,7 +273,7 @@ func GetIssueSubscribers(ctx *context.APIContext) {
                userIDs = append(userIDs, iw.UserID)
        }
 
-       users, err := models.GetUsersByIDs(userIDs)
+       users, err := user_model.GetUsersByIDs(userIDs)
        if err != nil {
                ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err)
                return
index 4872ecb9206e14d30ad8347220a133f4e758df83..cbf0ec5e983818b933ed97ad87d593224a43898e 100644 (file)
@@ -9,7 +9,6 @@ import (
        "net/http"
        "net/url"
 
-       "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/base"
@@ -29,7 +28,7 @@ func Emails(ctx *context.Context) {
        ctx.Data["PageIsAdmin"] = true
        ctx.Data["PageIsAdminEmails"] = true
 
-       opts := &models.SearchEmailOptions{
+       opts := &user_model.SearchEmailOptions{
                ListOptions: db.ListOptions{
                        PageSize: setting.UI.Admin.UserPagingNum,
                        Page:     ctx.FormInt("page"),
@@ -41,31 +40,31 @@ func Emails(ctx *context.Context) {
        }
 
        type ActiveEmail struct {
-               models.SearchEmailResult
+               user_model.SearchEmailResult
                CanChange bool
        }
 
        var (
-               baseEmails []*models.SearchEmailResult
+               baseEmails []*user_model.SearchEmailResult
                emails     []ActiveEmail
                count      int64
                err        error
-               orderBy    models.SearchEmailOrderBy
+               orderBy    user_model.SearchEmailOrderBy
        )
 
        ctx.Data["SortType"] = ctx.FormString("sort")
        switch ctx.FormString("sort") {
        case "email":
-               orderBy = models.SearchEmailOrderByEmail
+               orderBy = user_model.SearchEmailOrderByEmail
        case "reverseemail":
-               orderBy = models.SearchEmailOrderByEmailReverse
+               orderBy = user_model.SearchEmailOrderByEmailReverse
        case "username":
-               orderBy = models.SearchEmailOrderByName
+               orderBy = user_model.SearchEmailOrderByName
        case "reverseusername":
-               orderBy = models.SearchEmailOrderByNameReverse
+               orderBy = user_model.SearchEmailOrderByNameReverse
        default:
                ctx.Data["SortType"] = "email"
-               orderBy = models.SearchEmailOrderByEmail
+               orderBy = user_model.SearchEmailOrderByEmail
        }
 
        opts.Keyword = ctx.FormTrim("q")
@@ -78,7 +77,7 @@ func Emails(ctx *context.Context) {
        }
 
        if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
-               baseEmails, count, err = models.SearchEmails(opts)
+               baseEmails, count, err = user_model.SearchEmails(opts)
                if err != nil {
                        ctx.ServerError("SearchEmails", err)
                        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)
 
-       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)
                if user_model.IsErrEmailAlreadyUsed(err) {
                        ctx.Flash.Error(ctx.Tr("admin.emails.duplicate_active"))
index 85603784479e8df1149e882a3a0b45d2929a256f..27634c3d4ef9b251c9fe4fa477664f43a8a6fdf9 100644 (file)
@@ -8,7 +8,6 @@ import (
        "bytes"
        "net/http"
 
-       "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
        user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/base"
@@ -79,7 +78,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
        ctx.Data["Keyword"] = opts.Keyword
        ctx.Data["Total"] = count
        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["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 
index ef5a69e157272839150afd479af8f4ea0b4709c0..e9b7317c98371e4546c408a447e3fc607bd2c0db 100644 (file)
@@ -62,7 +62,7 @@ func Members(ctx *context.Context) {
        ctx.Data["Page"] = pager
        ctx.Data["Members"] = members
        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.HTML(http.StatusOK, tplMembers)
index a71a2789582c65511910b1d372685f2a563623eb..42cd977b54dc548dc0c298b8004cff622e1e7162 100644 (file)
@@ -12,7 +12,6 @@ import (
        "net/http"
        "strings"
 
-       "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
        "code.gitea.io/gitea/models/login"
        user_model "code.gitea.io/gitea/models/user"
@@ -781,7 +780,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_mode
                }
 
                // 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)
                }
 
@@ -844,11 +843,11 @@ func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, r
        }
 
        // search in external linked users
-       externalLoginUser := &models.ExternalLoginUser{
+       externalLoginUser := &user_model.ExternalLoginUser{
                ExternalID:    gothUser.UserID,
                LoginSourceID: loginSource.ID,
        }
-       hasUser, err = models.GetExternalLogin(externalLoginUser)
+       hasUser, err = user_model.GetExternalLogin(externalLoginUser)
        if err != nil {
                return nil, goth.User{}, err
        }
@@ -1355,7 +1354,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
 
        // update external user information
        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)
                }
        }
@@ -1477,7 +1476,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
                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)
                ctx.ServerError("ActivateUserEmail", err)
                return
@@ -1505,8 +1504,8 @@ func ActivateEmail(ctx *context.Context) {
        emailStr := ctx.FormString("email")
 
        // Verify code.
-       if email := models.VerifyActiveEmailCode(code, emailStr); email != nil {
-               if err := models.ActivateEmail(email); err != nil {
+       if email := user_model.VerifyActiveEmailCode(code, emailStr); email != nil {
+               if err := user_model.ActivateEmail(email); err != nil {
                        ctx.ServerError("ActivateEmail", err)
                }
 
index 3b51fdb77752790bf4f007e9ed07519548d8201e..b73122fa12a178b2f7e8c4481400f2a3c26062dd 100644 (file)
@@ -94,7 +94,7 @@ func EmailPost(ctx *context.Context) {
 
        // Make emailaddress 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)
                        return
                }
index f0b1d8232af5374290fda0793629e1aa0cb98d3a..d34de519b7e68b132110d048eee96c7b3267bfda 100644 (file)
@@ -43,7 +43,7 @@ func DeleteAccountLink(ctx *context.Context) {
        if id <= 0 {
                ctx.Flash.Error("Account link id is not given")
        } 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())
                } else {
                        ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
@@ -76,7 +76,7 @@ func loadSecurityData(ctx *context.Context) {
        }
        ctx.Data["Tokens"] = tokens
 
-       accountLinks, err := models.ListAccountLinks(ctx.User)
+       accountLinks, err := user_model.ListAccountLinks(ctx.User)
        if err != nil {
                ctx.ServerError("ListAccountLinks", err)
                return
index edce14cd8b34f95085b0a07dc178984201b4efef..47a687f63b7f196fc77d68a18a8c27e50ca3765c 100644 (file)
@@ -5,7 +5,6 @@
 package auth
 
 import (
-       "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
        "code.gitea.io/gitea/models/login"
        user_model "code.gitea.io/gitea/models/user"
@@ -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 {
                return err
        } else if count > 0 {
index c11499d7651beefd6e619c284e4d557efbd5e173..f7280e90e4d72e08e6047a1e3be05bcc3d0b45b8 100644 (file)
@@ -22,7 +22,7 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error {
                return err
        }
 
-       externalLoginUser := &models.ExternalLoginUser{
+       externalLoginUser := &user_model.ExternalLoginUser{
                ExternalID:        gothUser.UserID,
                UserID:            user.ID,
                LoginSourceID:     loginSource.ID,
@@ -42,7 +42,7 @@ func LinkAccountToUser(user *user_model.User, gothUser goth.User) error {
                ExpiresAt:         gothUser.ExpiresAt,
        }
 
-       if err := models.LinkExternalToUser(user, externalLoginUser); err != nil {
+       if err := user_model.LinkExternalToUser(user, externalLoginUser); err != nil {
                return err
        }
 
index 3bc8992c3a6afe4a3824f7dd4c522bb8eb26e387..4808916c1d878a4e8e924aa93d7a3e5d870709fd 100644 (file)
@@ -260,7 +260,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
                tp := g.gitServiceType.Name()
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
@@ -400,7 +400,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
                tp := g.gitServiceType.Name()
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
@@ -425,7 +425,7 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
                        userid, ok := g.userMap[reaction.UserID]
                        if !ok && tp != "" {
                                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 {
                                        log.Error("GetUserIDByExternalUserID: %v", err)
                                }
@@ -483,7 +483,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
                tp := g.gitServiceType.Name()
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
@@ -520,7 +520,7 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
                        userid, ok := g.userMap[reaction.UserID]
                        if !ok && tp != "" {
                                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 {
                                        log.Error("GetUserIDByExternalUserID: %v", err)
                                }
@@ -564,7 +564,7 @@ func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error
                tp := g.gitServiceType.Name()
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
@@ -744,7 +744,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
        userid, ok := g.userMap[pr.PosterID]
        if !ok && tp != "" {
                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 {
                        log.Error("GetUserIDByExternalUserID: %v", err)
                }
@@ -766,7 +766,7 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
                userid, ok := g.userMap[reaction.UserID]
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
@@ -850,7 +850,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
                tp := g.gitServiceType.Name()
                if !ok && tp != "" {
                        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 {
                                log.Error("GetUserIDByExternalUserID: %v", err)
                        }
index ddc9401eadfcc6e32762d0419f07d83ba3008450..cdec73cd1bb659968deade45a2d39919782423a1 100644 (file)
@@ -9,6 +9,7 @@ import (
 
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/models/db"
+       user_model "code.gitea.io/gitea/models/user"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/structs"
 )
@@ -45,7 +46,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ
                default:
                }
 
-               users, err := models.FindExternalUsersByProvider(models.FindExternalUserOptions{
+               users, err := user_model.FindExternalUsersByProvider(user_model.FindExternalUserOptions{
                        Provider: provider,
                        Start:    start,
                        Limit:    batchSize,