Skip to content

Move more model into models/user #17826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

// .___ ________ .___ .__
// | | ______ ________ __ ____ \______ \ ____ ______ ____ ____ __| _/____ ____ ____ |__| ____ ______
// | |/ ___// ___/ | \_/ __ \ | | \_/ __ \\____ \_/ __ \ / \ / __ |/ __ \ / \_/ ___\| |/ __ \ / ___/
Expand Down
20 changes: 20 additions & 0 deletions models/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
6 changes: 3 additions & 3 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 *****
Expand Down
246 changes: 246 additions & 0 deletions models/user/email_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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()
}
Loading