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)
-}
-
// .___ ________ .___ .__
// | | ______ ________ __ ____ \______ \ ____ ______ ____ ____ __| _/____ ____ ____ |__| ____ ______
// | |/ ___// ___/ | \_/ __ \ | | \_/ __ \\____ \_/ __ \ / \ / __ |/ __ \ / \_/ ___\| |/ __ \ / ___/
+++ /dev/null
-// 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)
-}
})
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)
+}
}
// 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,
})
}
// 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
idsIsPublic[ou.UID] = ou.IsPublic
}
- users, err := GetUsersByIDs(ids)
+ users, err := user_model.GetUsersByIDs(ids)
if err != nil {
return nil, nil, err
}
}
// ***** 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 *****
"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"
)
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()
+}
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
+ "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
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)))
+}
--- /dev/null
+// 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
+}
--- /dev/null
+// 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
+}
+++ /dev/null
-// 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()
-}
+++ /dev/null
-// 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)))
-}
"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
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
}
return nil, err
}
- userIDs := users.getUserIDs()
+ userIDs := users.GetUserIDs()
ownerMaps := make(map[int64]*TeamUser)
err = e.In("uid", userIDs).
And("org_id=?", orgID).
}
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
-}
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))
}
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
"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"
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"),
}
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")
}
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
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"))
"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"
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
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)
"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"
}
// 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)
}
}
// 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
}
// 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)
}
}
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
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)
}
// 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
}
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"))
}
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
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"
}
}
- 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 {
return err
}
- externalLoginUser := &models.ExternalLoginUser{
+ externalLoginUser := &user_model.ExternalLoginUser{
ExternalID: gothUser.UserID,
UserID: user.ID,
LoginSourceID: loginSource.ID,
ExpiresAt: gothUser.ExpiresAt,
}
- if err := models.LinkExternalToUser(user, externalLoginUser); err != nil {
+ if err := user_model.LinkExternalToUser(user, externalLoginUser); err != nil {
return err
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
"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"
)
default:
}
- users, err := models.FindExternalUsersByProvider(models.FindExternalUserOptions{
+ users, err := user_model.FindExternalUsersByProvider(user_model.FindExternalUserOptions{
Provider: provider,
Start: start,
Limit: batchSize,