From 9defddb2862aa0a54da21328ca091284291363f7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 28 Nov 2021 22:11:58 +0800 Subject: [PATCH] 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 --- models/error.go | 40 ---- models/migrate.go | 20 ++ models/org.go | 6 +- models/user.go | 2 +- models/user/email_address.go | 246 ++++++++++++++++++++ models/user/email_address_test.go | 122 ++++++++++ models/{ => user}/external_login_user.go | 73 +++--- models/user/list.go | 69 ++++++ models/user_email.go | 262 ---------------------- models/user_email_test.go | 137 ----------- models/userlist.go | 65 +----- models/userlist_test.go | 29 +-- routers/api/v1/repo/issue_subscription.go | 2 +- routers/web/admin/emails.go | 23 +- routers/web/explore/user.go | 3 +- routers/web/org/members.go | 2 +- routers/web/user/auth.go | 15 +- routers/web/user/setting/account.go | 2 +- routers/web/user/setting/security.go | 4 +- services/auth/login_source.go | 3 +- services/externalaccount/user.go | 4 +- services/migrations/gitea_uploader.go | 18 +- services/migrations/update.go | 3 +- 23 files changed, 547 insertions(+), 603 deletions(-) rename models/{ => user}/external_login_user.go (74%) create mode 100644 models/user/list.go delete mode 100644 models/user_email.go delete mode 100644 models/user_email_test.go diff --git a/models/error.go b/models/error.go index 0b6c22450d..36c70e49ad 100644 --- a/models/error.go +++ b/models/error.go @@ -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/migrate.go b/models/migrate.go index efe4bd0c5e..07d2b0f2d9 100644 --- a/models/migrate.go +++ b/models/migrate.go @@ -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) +} diff --git a/models/org.go b/models/org.go index 040d129692..becfa4cb05 100644 --- a/models/org.go +++ b/models/org.go @@ -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 } diff --git a/models/user.go b/models/user.go index 9816fcc713..e604c871b2 100644 --- a/models/user.go +++ b/models/user.go @@ -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 ***** diff --git a/models/user/email_address.go b/models/user/email_address.go index 98cd14a6a5..0ff62fb6a8 100644 --- a/models/user/email_address.go +++ b/models/user/email_address.go @@ -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() +} diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go index 642c2b3876..4a539e150a 100644 --- a/models/user/email_address_test.go +++ b/models/user/email_address_test.go @@ -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/external_login_user.go b/models/user/external_login_user.go similarity index 74% rename from models/external_login_user.go rename to models/user/external_login_user.go index 4be1307225..354dee04a9 100644 --- a/models/external_login_user.go +++ b/models/user/external_login_user.go @@ -2,20 +2,53 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package user import ( + "context" + "fmt" "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" ) +// 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"` @@ -47,7 +80,7 @@ func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) { } // 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) err := db.GetEngine(db.DefaultContext).Where("user_id=?", user.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 -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). NoAutoCondition(). 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 -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}) if err != nil { return deleted, err @@ -86,9 +119,9 @@ func RemoveAccountLink(user *user_model.User, loginSourceID int64) (int64, error 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}) +// 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 } @@ -107,7 +140,7 @@ func GetUserIDByExternalUserID(provider, userID string) (int64, error) { } // 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) if err != nil { return err @@ -172,23 +205,3 @@ func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginU } 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) -} diff --git a/models/user/list.go b/models/user/list.go new file mode 100644 index 0000000000..6ca4613aa7 --- /dev/null +++ b/models/user/list.go @@ -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 index 528ebb0540..0000000000 --- a/models/user_email.go +++ /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 index a91e24d363..0000000000 --- a/models/user_email_test.go +++ /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))) -} diff --git a/models/userlist.go b/models/userlist.go index ae39de3da4..102c587dfe 100644 --- a/models/userlist.go +++ b/models/userlist.go @@ -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 -} diff --git a/models/userlist_test.go b/models/userlist_test.go index c16a7b6eec..6776850b60 100644 --- a/models/userlist_test.go +++ b/models/userlist_test.go @@ -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)) } diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go index 09be3cc193..ae7661cfab 100644 --- a/routers/api/v1/repo/issue_subscription.go +++ b/routers/api/v1/repo/issue_subscription.go @@ -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 diff --git a/routers/web/admin/emails.go b/routers/web/admin/emails.go index 4872ecb920..cbf0ec5e98 100644 --- a/routers/web/admin/emails.go +++ b/routers/web/admin/emails.go @@ -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")) diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index 8560378447..27634c3d4e 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -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 diff --git a/routers/web/org/members.go b/routers/web/org/members.go index ef5a69e157..e9b7317c98 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -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) diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go index a71a278958..42cd977b54 100644 --- a/routers/web/user/auth.go +++ b/routers/web/user/auth.go @@ -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) } diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 3b51fdb777..b73122fa12 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -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 } diff --git a/routers/web/user/setting/security.go b/routers/web/user/setting/security.go index f0b1d8232a..d34de519b7 100644 --- a/routers/web/user/setting/security.go +++ b/routers/web/user/setting/security.go @@ -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 diff --git a/services/auth/login_source.go b/services/auth/login_source.go index edce14cd8b..47a687f63b 100644 --- a/services/auth/login_source.go +++ b/services/auth/login_source.go @@ -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 { diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go index c11499d765..f7280e90e4 100644 --- a/services/externalaccount/user.go +++ b/services/externalaccount/user.go @@ -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 } diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 3bc8992c3a..4808916c1d 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -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) } diff --git a/services/migrations/update.go b/services/migrations/update.go index ddc9401ead..cdec73cd1b 100644 --- a/services/migrations/update.go +++ b/services/migrations/update.go @@ -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,