diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2021-11-24 17:49:20 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-24 17:49:20 +0800 |
commit | a666829a37be6f9fd98f9e7dd1767c420f7f3b32 (patch) | |
tree | 9ab1434b759a8a2cb275a83149903a823851e309 /models | |
parent | 4e7ca946da2a2642a62f114825129bf5d7ed9196 (diff) | |
download | gitea-a666829a37be6f9fd98f9e7dd1767c420f7f3b32.tar.gz gitea-a666829a37be6f9fd98f9e7dd1767c420f7f3b32.zip |
Move user related model into models/user (#17781)
* Move user related model into models/user
* Fix lint for windows
* Fix windows lint
* Fix windows lint
* Move some tests in models
* Merge
Diffstat (limited to 'models')
93 files changed, 2642 insertions, 2467 deletions
diff --git a/models/access.go b/models/access.go index 7af88e1b4a..01b37dd7ae 100644 --- a/models/access.go +++ b/models/access.go @@ -9,6 +9,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" ) @@ -76,7 +77,7 @@ func init() { db.RegisterModel(new(Access)) } -func accessLevel(e db.Engine, user *User, repo *Repository) (AccessMode, error) { +func accessLevel(e db.Engine, user *user_model.User, repo *Repository) (AccessMode, error) { mode := AccessModeNone var userID int64 restricted := false @@ -116,12 +117,12 @@ func maxAccessMode(modes ...AccessMode) AccessMode { } type userAccess struct { - User *User + User *user_model.User Mode AccessMode } // updateUserAccess updates an access map so that user has at least mode -func updateUserAccess(accessMap map[int64]*userAccess, user *User, mode AccessMode) { +func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mode AccessMode) { if ua, ok := accessMap[user.ID]; ok { ua.Mode = maxAccessMode(ua.Mode, mode) } else { diff --git a/models/access_test.go b/models/access_test.go index 81e36ed2e0..a2fd05a033 100644 --- a/models/access_test.go +++ b/models/access_test.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -16,9 +17,9 @@ import ( func TestAccessLevel(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user5 := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) - user29 := unittest.AssertExistsAndLoadBean(t, &User{ID: 29}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) + user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}).(*user_model.User) // A public repository owned by User 2 repo1 := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) assert.False(t, repo1.IsPrivate) @@ -67,8 +68,8 @@ func TestAccessLevel(t *testing.T) { func TestHasAccess(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) // A public repository owned by User 2 repo1 := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) assert.False(t, repo1.IsPrivate) @@ -125,7 +126,7 @@ func TestRepository_RecalculateAccesses2(t *testing.T) { func TestRepository_RecalculateAccesses3(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) team5 := unittest.AssertExistsAndLoadBean(t, &Team{ID: 5}).(*Team) - user29 := unittest.AssertExistsAndLoadBean(t, &User{ID: 29}).(*User) + user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}).(*user_model.User) has, err := db.GetEngine(db.DefaultContext).Get(&Access{UserID: 29, RepoID: 23}) assert.NoError(t, err) diff --git a/models/action.go b/models/action.go index d790cd6678..66fa78f268 100644 --- a/models/action.go +++ b/models/action.go @@ -14,6 +14,7 @@ import ( "time" "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/git" "code.gitea.io/gitea/modules/log" @@ -63,13 +64,13 @@ type Action struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"INDEX"` // Receiver user id. OpType ActionType - ActUserID int64 `xorm:"INDEX"` // Action user id. - ActUser *User `xorm:"-"` - RepoID int64 `xorm:"INDEX"` - Repo *Repository `xorm:"-"` - CommentID int64 `xorm:"INDEX"` - Comment *Comment `xorm:"-"` - IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"` + ActUserID int64 `xorm:"INDEX"` // Action user id. + ActUser *user_model.User `xorm:"-"` + RepoID int64 `xorm:"INDEX"` + Repo *Repository `xorm:"-"` + CommentID int64 `xorm:"INDEX"` + Comment *Comment `xorm:"-"` + IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"` RefName string IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"` Content string `xorm:"TEXT"` @@ -91,11 +92,11 @@ func (a *Action) LoadActUser() { return } var err error - a.ActUser, err = GetUserByID(a.ActUserID) + a.ActUser, err = user_model.GetUserByID(a.ActUserID) if err == nil { return - } else if IsErrUserNotExist(err) { - a.ActUser = NewGhostUser() + } else if user_model.IsErrUserNotExist(err) { + a.ActUser = user_model.NewGhostUser() } else { log.Error("GetUserByID(%d): %v", a.ActUserID, err) } @@ -294,13 +295,13 @@ func (a *Action) GetIssueContent() string { // GetFeedsOptions options for retrieving feeds type GetFeedsOptions struct { - RequestedUser *User // the user we want activity for - RequestedTeam *Team // the team we want activity for - Actor *User // the user viewing the activity - IncludePrivate bool // include private actions - OnlyPerformedBy bool // only actions performed by requested user - IncludeDeleted bool // include deleted actions - Date string // the day we want activity for: YYYY-MM-DD + RequestedUser *user_model.User // the user we want activity for + RequestedTeam *Team // the team we want activity for + Actor *user_model.User // the user viewing the activity + IncludePrivate bool // include private actions + OnlyPerformedBy bool // only actions performed by requested user + IncludeDeleted bool // include deleted actions + Date string // the day we want activity for: YYYY-MM-DD } // GetFeeds returns actions according to the provided options @@ -327,7 +328,7 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) { return actions, nil } -func activityReadable(user, doer *User) bool { +func activityReadable(user, doer *user_model.User) bool { var doerID int64 if doer != nil { doerID = doer.ID diff --git a/models/action_list.go b/models/action_list.go index 69e6aa7312..4f2024cfcf 100644 --- a/models/action_list.go +++ b/models/action_list.go @@ -8,6 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" ) // ActionList defines a list of actions @@ -23,13 +24,13 @@ func (actions ActionList) getUserIDs() []int64 { return keysInt64(userIDs) } -func (actions ActionList) loadUsers(e db.Engine) ([]*User, error) { +func (actions ActionList) loadUsers(e db.Engine) ([]*user_model.User, error) { if len(actions) == 0 { return nil, nil } userIDs := actions.getUserIDs() - userMaps := make(map[int64]*User, len(userIDs)) + userMaps := make(map[int64]*user_model.User, len(userIDs)) err := e. In("id", userIDs). Find(&userMaps) @@ -44,7 +45,7 @@ func (actions ActionList) loadUsers(e db.Engine) ([]*User, error) { } // LoadUsers loads actions' all users -func (actions ActionList) LoadUsers() ([]*User, error) { +func (actions ActionList) LoadUsers() ([]*user_model.User, error) { return actions.loadUsers(db.GetEngine(db.DefaultContext)) } diff --git a/models/action_test.go b/models/action_test.go index 87bbcf3fb7..8ab88bb97e 100644 --- a/models/action_test.go +++ b/models/action_test.go @@ -9,6 +9,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -17,7 +18,7 @@ import ( func TestAction_GetRepoPath(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &Repository{}).(*Repository) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) action := &Action{RepoID: repo.ID} assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath()) } @@ -25,7 +26,7 @@ func TestAction_GetRepoPath(t *testing.T) { func TestAction_GetRepoLink(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &Repository{}).(*Repository) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) action := &Action{RepoID: repo.ID} setting.AppSubURL = "/suburl" expected := path.Join(setting.AppSubURL, owner.Name, repo.Name) @@ -35,7 +36,7 @@ func TestAction_GetRepoLink(t *testing.T) { func TestGetFeeds(t *testing.T) { // test with an individual user assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) actions, err := GetFeeds(GetFeedsOptions{ RequestedUser: user, @@ -63,8 +64,8 @@ func TestGetFeeds(t *testing.T) { func TestGetFeeds2(t *testing.T) { // test with an organization user assert.NoError(t, unittest.PrepareTestDatabase()) - org := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) actions, err := GetFeeds(GetFeedsOptions{ RequestedUser: org, diff --git a/models/branches.go b/models/branches.go index fc0da58e38..f5ed570ea2 100644 --- a/models/branches.go +++ b/models/branches.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -69,7 +70,7 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool { } if !protectBranch.EnableWhitelist { - if user, err := GetUserByID(userID); err != nil { + if user, err := user_model.GetUserByID(userID); err != nil { log.Error("GetUserByID: %v", err) return false } else if repo, err := GetRepositoryByID(protectBranch.RepoID); err != nil { @@ -123,11 +124,11 @@ func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64, permi } // IsUserOfficialReviewer check if user is official reviewer for the branch (counts towards required approvals) -func (protectBranch *ProtectedBranch) IsUserOfficialReviewer(user *User) (bool, error) { +func (protectBranch *ProtectedBranch) IsUserOfficialReviewer(user *user_model.User) (bool, error) { return protectBranch.isUserOfficialReviewer(db.GetEngine(db.DefaultContext), user) } -func (protectBranch *ProtectedBranch) isUserOfficialReviewer(e db.Engine, user *User) (bool, error) { +func (protectBranch *ProtectedBranch) isUserOfficialReviewer(e db.Engine, user *user_model.User) (bool, error) { repo, err := getRepositoryByID(e, protectBranch.RepoID) if err != nil { return false, err @@ -446,7 +447,7 @@ func updateUserWhitelist(repo *Repository, currentWhitelist, newWhitelist []int6 whitelist = make([]int64, 0, len(newWhitelist)) for _, userID := range newWhitelist { - user, err := GetUserByID(userID) + user, err := user_model.GetUserByID(userID) if err != nil { return nil, fmt.Errorf("GetUserByID [user_id: %d, repo_id: %d]: %v", userID, repo.ID, err) } @@ -511,7 +512,7 @@ type DeletedBranch struct { Name string `xorm:"UNIQUE(s) NOT NULL"` Commit string `xorm:"UNIQUE(s) NOT NULL"` DeletedByID int64 `xorm:"INDEX"` - DeletedBy *User `xorm:"-"` + DeletedBy *user_model.User `xorm:"-"` DeletedUnix timeutil.TimeStamp `xorm:"INDEX created"` } @@ -564,11 +565,11 @@ func (repo *Repository) RemoveDeletedBranch(id int64) (err error) { } // LoadUser loads the user that deleted the branch -// When there's no user found it returns a NewGhostUser +// When there's no user found it returns a user_model.NewGhostUser func (deletedBranch *DeletedBranch) LoadUser() { - user, err := GetUserByID(deletedBranch.DeletedByID) + user, err := user_model.GetUserByID(deletedBranch.DeletedByID) if err != nil { - user = NewGhostUser() + user = user_model.NewGhostUser() } deletedBranch.DeletedBy = user } diff --git a/models/commit.go b/models/commit.go index 474825820a..12aecdae84 100644 --- a/models/commit.go +++ b/models/commit.go @@ -5,6 +5,7 @@ package models import ( + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" ) @@ -12,7 +13,7 @@ import ( func ConvertFromGitCommit(commits []*git.Commit, repo *Repository) []*SignCommitWithStatuses { return ParseCommitsWithStatus( ParseCommitsWithSignature( - ValidateCommitsWithEmails(commits), + user_model.ValidateCommitsWithEmails(commits), repo, ), repo, diff --git a/models/commit_status.go b/models/commit_status.go index df34b93ec5..bba39fde87 100644 --- a/models/commit_status.go +++ b/models/commit_status.go @@ -12,6 +12,7 @@ import ( "time" "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/setting" api "code.gitea.io/gitea/modules/structs" @@ -32,7 +33,7 @@ type CommitStatus struct { Description string `xorm:"TEXT"` ContextHash string `xorm:"char(40) index"` Context string `xorm:"TEXT"` - Creator *User `xorm:"-"` + Creator *user_model.User `xorm:"-"` CreatorID int64 CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` @@ -127,7 +128,7 @@ func (status *CommitStatus) loadAttributes(e db.Engine) (err error) { } } if status.Creator == nil && status.CreatorID > 0 { - status.Creator, err = getUserByID(e, status.CreatorID) + status.Creator, err = user_model.GetUserByIDEngine(e, status.CreatorID) if err != nil { return fmt.Errorf("getUserByID [%d]: %v", status.CreatorID, err) } @@ -274,7 +275,7 @@ func FindRepoRecentCommitStatusContexts(repoID int64, before time.Duration) ([]s // NewCommitStatusOptions holds options for creating a CommitStatus type NewCommitStatusOptions struct { Repo *Repository - Creator *User + Creator *user_model.User SHA string CommitStatus *CommitStatus } diff --git a/models/consistency.go b/models/consistency.go index ba669628b9..d16e7f5e99 100644 --- a/models/consistency.go +++ b/models/consistency.go @@ -7,6 +7,7 @@ package models import ( admin_model "code.gitea.io/gitea/models/admin" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "xorm.io/builder" ) @@ -169,12 +170,12 @@ func FixNullArchivedRepository() (int64, error) { // CountWrongUserType count OrgUser who have wrong type func CountWrongUserType() (int64, error) { - return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(User)) + return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(user_model.User)) } // FixWrongUserType fix OrgUser who have wrong type func FixWrongUserType() (int64, error) { - return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&User{Type: 1}) + return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1}) } // CountCommentTypeLabelWithEmptyLabel count label comments with empty label diff --git a/models/db/error.go b/models/db/error.go index 675247ed87..adaeedcc09 100644 --- a/models/db/error.go +++ b/models/db/error.go @@ -4,7 +4,9 @@ package db -import "fmt" +import ( + "fmt" +) // ErrCancelled represents an error due to context cancellation type ErrCancelled struct { diff --git a/models/db/name.go b/models/db/name.go new file mode 100644 index 0000000000..9c9d18f184 --- /dev/null +++ b/models/db/name.go @@ -0,0 +1,91 @@ +// 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 db + +import ( + "errors" + "fmt" + "regexp" + "strings" + "unicode/utf8" +) + +var ( + // ErrNameEmpty name is empty error + ErrNameEmpty = errors.New("Name is empty") + + // AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-) + AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) +) + +// ErrNameReserved represents a "reserved name" error. +type ErrNameReserved struct { + Name string +} + +// IsErrNameReserved checks if an error is a ErrNameReserved. +func IsErrNameReserved(err error) bool { + _, ok := err.(ErrNameReserved) + return ok +} + +func (err ErrNameReserved) Error() string { + return fmt.Sprintf("name is reserved [name: %s]", err.Name) +} + +// ErrNamePatternNotAllowed represents a "pattern not allowed" error. +type ErrNamePatternNotAllowed struct { + Pattern string +} + +// IsErrNamePatternNotAllowed checks if an error is an ErrNamePatternNotAllowed. +func IsErrNamePatternNotAllowed(err error) bool { + _, ok := err.(ErrNamePatternNotAllowed) + return ok +} + +func (err ErrNamePatternNotAllowed) Error() string { + return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern) +} + +// ErrNameCharsNotAllowed represents a "character not allowed in name" error. +type ErrNameCharsNotAllowed struct { + Name string +} + +// IsErrNameCharsNotAllowed checks if an error is an ErrNameCharsNotAllowed. +func IsErrNameCharsNotAllowed(err error) bool { + _, ok := err.(ErrNameCharsNotAllowed) + return ok +} + +func (err ErrNameCharsNotAllowed) Error() string { + return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name) +} + +// IsUsableName checks if name is reserved or pattern of name is not allowed +// based on given reserved names and patterns. +// Names are exact match, patterns can be prefix or suffix match with placeholder '*'. +func IsUsableName(names, patterns []string, name string) error { + name = strings.TrimSpace(strings.ToLower(name)) + if utf8.RuneCountInString(name) == 0 { + return ErrNameEmpty + } + + for i := range names { + if name == names[i] { + return ErrNameReserved{name} + } + } + + for _, pat := range patterns { + if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) || + (pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) { + return ErrNamePatternNotAllowed{pat} + } + } + + return nil +} diff --git a/models/db/search.go b/models/db/search.go new file mode 100644 index 0000000000..704a48ed1e --- /dev/null +++ b/models/db/search.go @@ -0,0 +1,30 @@ +// 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 db + +// SearchOrderBy is used to sort the result +type SearchOrderBy string + +func (s SearchOrderBy) String() string { + return string(s) +} + +// Strings for sorting result +const ( + SearchOrderByAlphabetically SearchOrderBy = "name ASC" + SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC" + SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC" + SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC" + SearchOrderByOldest SearchOrderBy = "created_unix ASC" + SearchOrderByNewest SearchOrderBy = "created_unix DESC" + SearchOrderBySize SearchOrderBy = "size ASC" + SearchOrderBySizeReverse SearchOrderBy = "size DESC" + SearchOrderByID SearchOrderBy = "id ASC" + SearchOrderByIDReverse SearchOrderBy = "id DESC" + SearchOrderByStars SearchOrderBy = "num_stars ASC" + SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC" + SearchOrderByForks SearchOrderBy = "num_forks ASC" + SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" +) diff --git a/models/error.go b/models/error.go index d0691c1c69..5ef96683e7 100644 --- a/models/error.go +++ b/models/error.go @@ -26,51 +26,6 @@ func (err ErrNotExist) Error() string { return fmt.Sprintf("record does not exist [id: %d]", err.ID) } -// ErrNameReserved represents a "reserved name" error. -type ErrNameReserved struct { - Name string -} - -// IsErrNameReserved checks if an error is a ErrNameReserved. -func IsErrNameReserved(err error) bool { - _, ok := err.(ErrNameReserved) - return ok -} - -func (err ErrNameReserved) Error() string { - return fmt.Sprintf("name is reserved [name: %s]", err.Name) -} - -// ErrNamePatternNotAllowed represents a "pattern not allowed" error. -type ErrNamePatternNotAllowed struct { - Pattern string -} - -// IsErrNamePatternNotAllowed checks if an error is an ErrNamePatternNotAllowed. -func IsErrNamePatternNotAllowed(err error) bool { - _, ok := err.(ErrNamePatternNotAllowed) - return ok -} - -func (err ErrNamePatternNotAllowed) Error() string { - return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern) -} - -// ErrNameCharsNotAllowed represents a "character not allowed in name" error. -type ErrNameCharsNotAllowed struct { - Name string -} - -// IsErrNameCharsNotAllowed checks if an error is an ErrNameCharsNotAllowed. -func IsErrNameCharsNotAllowed(err error) bool { - _, ok := err.(ErrNameCharsNotAllowed) - return ok -} - -func (err ErrNameCharsNotAllowed) Error() string { - return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name) -} - // ErrSSHDisabled represents an "SSH disabled" error. type ErrSSHDisabled struct{} @@ -84,77 +39,6 @@ func (err ErrSSHDisabled) Error() string { return "SSH is disabled" } -// ____ ___ -// | | \______ ___________ -// | | / ___// __ \_ __ \ -// | | /\___ \\ ___/| | \/ -// |______//____ >\___ >__| -// \/ \/ - -// ErrUserAlreadyExist represents a "user already exists" error. -type ErrUserAlreadyExist struct { - Name string -} - -// IsErrUserAlreadyExist checks if an error is a ErrUserAlreadyExists. -func IsErrUserAlreadyExist(err error) bool { - _, ok := err.(ErrUserAlreadyExist) - return ok -} - -func (err ErrUserAlreadyExist) Error() string { - return fmt.Sprintf("user already exists [name: %s]", err.Name) -} - -// ErrUserNotExist represents a "UserNotExist" kind of error. -type ErrUserNotExist struct { - UID int64 - Name string - KeyID int64 -} - -// IsErrUserNotExist checks if an error is a ErrUserNotExist. -func IsErrUserNotExist(err error) bool { - _, ok := err.(ErrUserNotExist) - return ok -} - -func (err ErrUserNotExist) Error() string { - return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID) -} - -// ErrUserProhibitLogin represents a "ErrUserProhibitLogin" kind of error. -type ErrUserProhibitLogin struct { - UID int64 - Name string -} - -// IsErrUserProhibitLogin checks if an error is a ErrUserProhibitLogin -func IsErrUserProhibitLogin(err error) bool { - _, ok := err.(ErrUserProhibitLogin) - return ok -} - -func (err ErrUserProhibitLogin) Error() string { - return fmt.Sprintf("user is not allowed login [uid: %d, name: %s]", err.UID, err.Name) -} - -// ErrUserInactive represents a "ErrUserInactive" kind of error. -type ErrUserInactive struct { - UID int64 - Name string -} - -// IsErrUserInactive checks if an error is a ErrUserInactive -func IsErrUserInactive(err error) bool { - _, ok := err.(ErrUserInactive) - return ok -} - -func (err ErrUserInactive) Error() string { - return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name) -} - // ErrUserOwnRepos represents a "UserOwnRepos" kind of error. type ErrUserOwnRepos struct { UID int64 diff --git a/models/external_login_user.go b/models/external_login_user.go index 6b023a4cb2..4be1307225 100644 --- a/models/external_login_user.go +++ b/models/external_login_user.go @@ -9,6 +9,7 @@ import ( "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" @@ -46,7 +47,7 @@ func GetExternalLogin(externalLoginUser *ExternalLoginUser) (bool, error) { } // ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource -func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) { +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"). @@ -59,7 +60,7 @@ func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) { } // LinkExternalToUser link the external user to the user -func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error { +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) @@ -74,7 +75,7 @@ func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error } // RemoveAccountLink will remove all external login sources for the given user -func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) { +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 @@ -86,7 +87,7 @@ func RemoveAccountLink(user *User, loginSourceID int64) (int64, error) { } // removeAllAccountLinks will remove all external login sources for the given user -func removeAllAccountLinks(e db.Engine, user *User) error { +func removeAllAccountLinks(e db.Engine, user *user_model.User) error { _, err := e.Delete(&ExternalLoginUser{UserID: user.ID}) return err } @@ -106,7 +107,7 @@ func GetUserIDByExternalUserID(provider, userID string) (int64, error) { } // UpdateExternalUser updates external user's information -func UpdateExternalUser(user *User, gothUser goth.User) error { +func UpdateExternalUser(user *user_model.User, gothUser goth.User) error { loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider) if err != nil { return err diff --git a/models/gpg_key.go b/models/gpg_key.go index 357dc2b964..ce27a9237e 100644 --- a/models/gpg_key.go +++ b/models/gpg_key.go @@ -214,7 +214,7 @@ func deleteGPGKey(e db.Engine, keyID string) (int64, error) { } // DeleteGPGKey deletes GPG key information in database. -func DeleteGPGKey(doer *User, id int64) (err error) { +func DeleteGPGKey(doer *user_model.User, id int64) (err error) { key, err := GetGPGKeyByID(id) if err != nil { if IsErrGPGKeyNotExist(err) { @@ -244,7 +244,7 @@ func DeleteGPGKey(doer *User, id int64) (err error) { func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { uid := int64(0) var userEmails []*user_model.EmailAddress - var user *User + var user *user_model.User for _, key := range keys { for _, e := range key.Emails { if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) { @@ -255,8 +255,8 @@ func checkKeyEmails(email string, keys ...*GPGKey) (bool, string) { if uid != key.OwnerID { userEmails, _ = user_model.GetEmailAddresses(key.OwnerID) uid = key.OwnerID - user = &User{ID: uid} - _, _ = GetUser(user) + user = &user_model.User{ID: uid} + _, _ = user_model.GetUser(user) } for _, e := range userEmails { if e.IsActivated && (email == "" || strings.EqualFold(e.Email, email)) { diff --git a/models/gpg_key_commit_verification.go b/models/gpg_key_commit_verification.go index 6da65d3697..5eeeb69ef5 100644 --- a/models/gpg_key_commit_verification.go +++ b/models/gpg_key_commit_verification.go @@ -44,8 +44,8 @@ type CommitVerification struct { Verified bool Warning bool Reason string - SigningUser *User - CommittingUser *User + SigningUser *user_model.User + CommittingUser *user_model.User SigningEmail string SigningKey *GPGKey TrustStatus string @@ -54,7 +54,7 @@ type CommitVerification struct { // SignCommit represents a commit with validation of signature. type SignCommit struct { Verification *CommitVerification - *UserCommit + *user_model.UserCommit } const ( @@ -69,7 +69,7 @@ const ( ) // ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys. -func ParseCommitsWithSignature(oldCommits []*UserCommit, repository *Repository) []*SignCommit { +func ParseCommitsWithSignature(oldCommits []*user_model.UserCommit, repository *Repository) []*SignCommit { newCommits := make([]*SignCommit, 0, len(oldCommits)) keyMap := map[string]bool{} @@ -88,19 +88,19 @@ func ParseCommitsWithSignature(oldCommits []*UserCommit, repository *Repository) // ParseCommitWithSignature check if signature is good against keystore. func ParseCommitWithSignature(c *git.Commit) *CommitVerification { - var committer *User + var committer *user_model.User if c.Committer != nil { var err error // Find Committer account - committer, err = GetUserByEmail(c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not - if err != nil { // Skipping not user for committer - committer = &User{ + committer, err = user_model.GetUserByEmail(c.Committer.Email) // This finds the user by primary email or activated email so commit will not be valid if email is not + if err != nil { // Skipping not user for committer + committer = &user_model.User{ Name: c.Committer.Name, Email: c.Committer.Email, } // We can expect this to often be an ErrUserNotExist. in the case // it is not, however, it is important to log it. - if !IsErrUserNotExist(err) { + if !user_model.IsErrUserNotExist(err) { log.Error("GetUserByEmail: %v", err) return &CommitVerification{ CommittingUser: committer, @@ -250,7 +250,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification { } } -func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *User, keyID string) *CommitVerification { +func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *user_model.User, keyID string) *CommitVerification { // First try to find the key in the db if commitVerification := hashAndVerifyForKeyID(sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil { return commitVerification @@ -296,7 +296,7 @@ func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, KeyID: subKey.PublicKey.KeyIdString(), }) } - if commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &User{ + if commitVerification := hashAndVerifyWithSubKeysCommitVerification(sig, payload, k, committer, &user_model.User{ Name: gpgSettings.Name, Email: gpgSettings.Email, }, gpgSettings.Email); commitVerification != nil { @@ -357,7 +357,7 @@ func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey) return nil, nil } -func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *User, email string) *CommitVerification { +func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload string, k *GPGKey, committer, signer *user_model.User, email string) *CommitVerification { key, err := hashAndVerifyWithSubKeys(sig, payload, k) if err != nil { // Skipping failed to generate hash return &CommitVerification{ @@ -380,7 +380,7 @@ func hashAndVerifyWithSubKeysCommitVerification(sig *packet.Signature, payload s return nil } -func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *User, keyID, name, email string) *CommitVerification { +func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *user_model.User, keyID, name, email string) *CommitVerification { if keyID == "" { return nil } @@ -415,16 +415,16 @@ func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *Use continue } - signer := &User{ + signer := &user_model.User{ Name: name, Email: email, } if key.OwnerID != 0 { - owner, err := GetUserByID(key.OwnerID) + owner, err := user_model.GetUserByID(key.OwnerID) if err == nil { signer = owner - } else if !IsErrUserNotExist(err) { - log.Error("Failed to GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err) + } else if !user_model.IsErrUserNotExist(err) { + log.Error("Failed to user_model.GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err) return &CommitVerification{ CommittingUser: committer, Verified: false, diff --git a/models/gpg_key_test.go b/models/gpg_key_test.go index e5f4960b76..8f51c146aa 100644 --- a/models/gpg_key_test.go +++ b/models/gpg_key_test.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" @@ -195,7 +196,7 @@ Unknown GPG key with good email func TestCheckGPGUserEmail(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - _ = unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) testEmailWithUpperCaseLetters := `-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 diff --git a/models/gpg_key_verify.go b/models/gpg_key_verify.go index 1c6b79ec5f..1824086021 100644 --- a/models/gpg_key_verify.go +++ b/models/gpg_key_verify.go @@ -9,6 +9,7 @@ import ( "time" "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/log" ) @@ -104,7 +105,7 @@ func VerifyGPGKey(ownerID int64, keyID, token, signature string) (string, error) } // VerificationToken returns token for the user that will be valid in minutes (time) -func VerificationToken(user *User, minutes int) string { +func VerificationToken(user *user_model.User, minutes int) string { return base.EncodeSha256( time.Now().Truncate(1*time.Minute).Add(time.Duration(minutes)*time.Minute).Format(time.RFC1123Z) + ":" + user.CreatedUnix.FormatLong() + ":" + diff --git a/models/helper.go b/models/helper.go index 710c15a978..41dd1ccd53 100644 --- a/models/helper.go +++ b/models/helper.go @@ -7,6 +7,7 @@ package models import ( "encoding/binary" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" ) @@ -26,8 +27,8 @@ func valuesRepository(m map[int64]*Repository) []*Repository { return values } -func valuesUser(m map[int64]*User) []*User { - values := make([]*User, 0, len(m)) +func valuesUser(m map[int64]*user_model.User) []*user_model.User { + values := make([]*user_model.User, 0, len(m)) for _, v := range m { values = append(values, v) } diff --git a/models/helper_environment.go b/models/helper_environment.go index 922a5790be..3e98f60ffd 100644 --- a/models/helper_environment.go +++ b/models/helper_environment.go @@ -9,6 +9,7 @@ import ( "os" "strings" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" ) @@ -32,19 +33,19 @@ const ( // It is recommended to avoid using this unless you are pushing within a transaction // or if you absolutely are sure that post-receive and pre-receive will do nothing // We provide the full pushing-environment for other hook providers -func InternalPushingEnvironment(doer *User, repo *Repository) []string { +func InternalPushingEnvironment(doer *user_model.User, repo *Repository) []string { return append(PushingEnvironment(doer, repo), EnvIsInternal+"=true", ) } // PushingEnvironment returns an os environment to allow hooks to work on push -func PushingEnvironment(doer *User, repo *Repository) []string { +func PushingEnvironment(doer *user_model.User, repo *Repository) []string { return FullPushingEnvironment(doer, doer, repo, repo.Name, 0) } // FullPushingEnvironment returns an os environment to allow hooks to work on push -func FullPushingEnvironment(author, committer *User, repo *Repository, repoName string, prID int64) []string { +func FullPushingEnvironment(author, committer *user_model.User, repo *Repository, repoName string, prID int64) []string { isWiki := "false" if strings.HasSuffix(repoName, ".wiki") { isWiki = "true" diff --git a/models/issue.go b/models/issue.go index 7aab8b9399..557d19e739 100644 --- a/models/issue.go +++ b/models/issue.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/references" @@ -30,12 +31,12 @@ import ( // Issue represents an issue or pull request of repository. type Issue struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"` - Repo *Repository `xorm:"-"` - Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository. - PosterID int64 `xorm:"INDEX"` - Poster *User `xorm:"-"` + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"` + Repo *Repository `xorm:"-"` + Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository. + PosterID int64 `xorm:"INDEX"` + Poster *user_model.User `xorm:"-"` OriginalAuthor string OriginalAuthorID int64 `xorm:"index"` Title string `xorm:"name"` @@ -46,12 +47,12 @@ type Issue struct { Milestone *Milestone `xorm:"-"` Project *Project `xorm:"-"` Priority int - AssigneeID int64 `xorm:"-"` - Assignee *User `xorm:"-"` - IsClosed bool `xorm:"INDEX"` - IsRead bool `xorm:"-"` - IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. - PullRequest *PullRequest `xorm:"-"` + AssigneeID int64 `xorm:"-"` + Assignee *user_model.User `xorm:"-"` + IsClosed bool `xorm:"INDEX"` + IsRead bool `xorm:"-"` + IsPull bool `xorm:"INDEX"` // Indicates whether is a pull request or not. + PullRequest *PullRequest `xorm:"-"` NumComments int Ref string @@ -65,7 +66,7 @@ type Issue struct { Comments []*Comment `xorm:"-"` Reactions ReactionList `xorm:"-"` TotalTrackedTime int64 `xorm:"-"` - Assignees []*User `xorm:"-"` + Assignees []*user_model.User `xorm:"-"` // IsLocked limits commenting abilities to users on an issue // with write access @@ -177,11 +178,11 @@ func (issue *Issue) LoadPoster() error { func (issue *Issue) loadPoster(e db.Engine) (err error) { if issue.Poster == nil { - issue.Poster, err = getUserByID(e, issue.PosterID) + issue.Poster, err = user_model.GetUserByIDEngine(e, issue.PosterID) if err != nil { issue.PosterID = -1 - issue.Poster = NewGhostUser() - if !IsErrUserNotExist(err) { + issue.Poster = user_model.NewGhostUser() + if !user_model.IsErrUserNotExist(err) { return fmt.Errorf("getUserByID.(poster) [%d]: %v", issue.PosterID, err) } err = nil @@ -428,11 +429,11 @@ func (issue *Issue) HasLabel(labelID int64) bool { return issue.hasLabel(db.GetEngine(db.DefaultContext), labelID) } -func (issue *Issue) addLabel(ctx context.Context, label *Label, doer *User) error { +func (issue *Issue) addLabel(ctx context.Context, label *Label, doer *user_model.User) error { return newIssueLabel(ctx, issue, label, doer) } -func (issue *Issue) addLabels(ctx context.Context, labels []*Label, doer *User) error { +func (issue *Issue) addLabels(ctx context.Context, labels []*Label, doer *user_model.User) error { return newIssueLabels(ctx, issue, labels, doer) } @@ -448,11 +449,11 @@ func (issue *Issue) getLabels(e db.Engine) (err error) { return nil } -func (issue *Issue) removeLabel(ctx context.Context, doer *User, label *Label) error { +func (issue *Issue) removeLabel(ctx context.Context, doer *user_model.User, label *Label) error { return deleteIssueLabel(ctx, issue, label, doer) } -func (issue *Issue) clearLabels(ctx context.Context, doer *User) (err error) { +func (issue *Issue) clearLabels(ctx context.Context, doer *user_model.User) (err error) { if err = issue.getLabels(db.GetEngine(ctx)); err != nil { return fmt.Errorf("getLabels: %v", err) } @@ -468,7 +469,7 @@ func (issue *Issue) clearLabels(ctx context.Context, doer *User) (err error) { // ClearLabels removes all issue labels as the given user. // Triggers appropriate WebHooks, if any. -func (issue *Issue) ClearLabels(doer *User) (err error) { +func (issue *Issue) ClearLabels(doer *user_model.User) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -516,7 +517,7 @@ func (ts labelSorter) Swap(i, j int) { // ReplaceLabels removes all current labels and add new labels to the issue. // Triggers appropriate WebHooks, if any. -func (issue *Issue) ReplaceLabels(labels []*Label, doer *User) (err error) { +func (issue *Issue) ReplaceLabels(labels []*Label, doer *user_model.User) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -598,7 +599,7 @@ func updateIssueCols(e db.Engine, issue *Issue, cols ...string) error { return nil } -func (issue *Issue) changeStatus(ctx context.Context, doer *User, isClosed, isMergePull bool) (*Comment, error) { +func (issue *Issue) changeStatus(ctx context.Context, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { // Reload the issue currentIssue, err := getIssueByID(db.GetEngine(ctx), issue.ID) if err != nil { @@ -621,7 +622,7 @@ func (issue *Issue) changeStatus(ctx context.Context, doer *User, isClosed, isMe return issue.doChangeStatus(ctx, doer, isMergePull) } -func (issue *Issue) doChangeStatus(ctx context.Context, doer *User, isMergePull bool) (*Comment, error) { +func (issue *Issue) doChangeStatus(ctx context.Context, doer *user_model.User, isMergePull bool) (*Comment, error) { e := db.GetEngine(ctx) // Check for open dependencies if issue.IsClosed && issue.Repo.isDependenciesEnabled(e) { @@ -684,7 +685,7 @@ func (issue *Issue) doChangeStatus(ctx context.Context, doer *User, isMergePull } // ChangeStatus changes issue status to open or closed. -func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (*Comment, error) { +func (issue *Issue) ChangeStatus(doer *user_model.User, isClosed bool) (*Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -711,7 +712,7 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (*Comment, error) { } // ChangeTitle changes the title of this issue, as the given user. -func (issue *Issue) ChangeTitle(doer *User, oldTitle string) (err error) { +func (issue *Issue) ChangeTitle(doer *user_model.User, oldTitle string) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -745,7 +746,7 @@ func (issue *Issue) ChangeTitle(doer *User, oldTitle string) (err error) { } // ChangeRef changes the branch of this issue, as the given user. -func (issue *Issue) ChangeRef(doer *User, oldRef string) (err error) { +func (issue *Issue) ChangeRef(doer *user_model.User, oldRef string) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -778,7 +779,7 @@ func (issue *Issue) ChangeRef(doer *User, oldRef string) (err error) { } // AddDeletePRBranchComment adds delete branch comment for pull request issue -func AddDeletePRBranchComment(doer *User, repo *Repository, issueID int64, branchName string) error { +func AddDeletePRBranchComment(doer *user_model.User, repo *Repository, issueID int64, branchName string) error { issue, err := getIssueByID(db.GetEngine(db.DefaultContext), issueID) if err != nil { return err @@ -823,7 +824,7 @@ func (issue *Issue) UpdateAttachments(uuids []string) (err error) { } // ChangeContent changes issue content, as the given user. -func (issue *Issue) ChangeContent(doer *User, content string) (err error) { +func (issue *Issue) ChangeContent(doer *user_model.User, content string) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -922,7 +923,7 @@ type NewIssueOptions struct { IsPull bool } -func newIssue(ctx context.Context, doer *User, opts NewIssueOptions) (err error) { +func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) (err error) { e := db.GetEngine(ctx) opts.Issue.Title = strings.TrimSpace(opts.Issue.Title) @@ -1380,7 +1381,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) { } // GetRepoIDsForIssuesOptions find all repo ids for the given options -func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *User) ([]int64, error) { +func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]int64, error) { repoIDs := make([]int64, 0, 5) e := db.GetEngine(db.DefaultContext) @@ -1455,7 +1456,7 @@ func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) { } // IsUserParticipantsOfIssue return true if user is participants of an issue -func IsUserParticipantsOfIssue(user *User, issue *Issue) bool { +func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool { userIDs, err := issue.getParticipantIDsByIssue(db.GetEngine(db.DefaultContext)) if err != nil { log.Error(err.Error()) @@ -1465,7 +1466,7 @@ func IsUserParticipantsOfIssue(user *User, issue *Issue) bool { } // UpdateIssueMentions updates issue-user relations for mentioned users. -func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*User) error { +func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_model.User) error { if len(mentions) == 0 { return nil } @@ -1843,7 +1844,7 @@ func SearchIssueIDsByKeyword(kw string, repoIDs []int64, limit, start int) (int6 // UpdateIssueByAPI updates all allowed fields of given issue. // If the issue status is changed a statusChangeComment is returned // similarly if the title is changed the titleChanged bool is set to true -func UpdateIssueByAPI(issue *Issue, doer *User) (statusChangeComment *Comment, titleChanged bool, err error) { +func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) { ctx, committer, err := db.TxContext() if err != nil { return nil, false, err @@ -1898,7 +1899,7 @@ func UpdateIssueByAPI(issue *Issue, doer *User) (statusChangeComment *Comment, t } // UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it. -func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *User) (err error) { +func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) { // if the deadline hasn't changed do nothing if issue.DeadlineUnix == deadlineUnix { return nil @@ -2017,7 +2018,7 @@ func (issue *Issue) updateClosedNum(e db.Engine) (err error) { } // FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database. -func (issue *Issue) FindAndUpdateIssueMentions(ctx context.Context, doer *User, content string) (mentions []*User, err error) { +func (issue *Issue) FindAndUpdateIssueMentions(ctx context.Context, doer *user_model.User, content string) (mentions []*user_model.User, err error) { rawMentions := references.FindAllMentionsMarkdown(content) mentions, err = issue.ResolveMentionsByVisibility(ctx, doer, rawMentions) if err != nil { @@ -2031,7 +2032,7 @@ func (issue *Issue) FindAndUpdateIssueMentions(ctx context.Context, doer *User, // ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that // don't have access to reading it. Teams are expanded into their users, but organizations are ignored. -func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *User, mentions []string) (users []*User, err error) { +func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *user_model.User, mentions []string) (users []*user_model.User, err error) { if len(mentions) == 0 { return } @@ -2100,7 +2101,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *User, } } if len(checked) != 0 { - teamusers := make([]*User, 0, 20) + teamusers := make([]*user_model.User, 0, 20) if err := db.GetEngine(ctx). Join("INNER", "team_user", "team_user.uid = `user`.id"). In("`team_user`.team_id", checked). @@ -2110,7 +2111,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *User, return nil, fmt.Errorf("get teams users: %v", err) } if len(teamusers) > 0 { - users = make([]*User, 0, len(teamusers)) + users = make([]*user_model.User, 0, len(teamusers)) for _, user := range teamusers { if already, ok := resolved[user.LowerName]; !ok || !already { users = append(users, user) @@ -2134,10 +2135,10 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *User, } if users == nil { - users = make([]*User, 0, len(mentionUsers)) + users = make([]*user_model.User, 0, len(mentionUsers)) } - unchecked := make([]*User, 0, len(mentionUsers)) + unchecked := make([]*user_model.User, 0, len(mentionUsers)) if err := db.GetEngine(ctx). Where("`user`.is_active = ?", true). And("`user`.prohibit_login = ?", false). diff --git a/models/issue_assignees.go b/models/issue_assignees.go index 996a3a8563..527549171e 100644 --- a/models/issue_assignees.go +++ b/models/issue_assignees.go @@ -9,6 +9,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/util" ) @@ -31,7 +32,7 @@ func (issue *Issue) LoadAssignees() error { // This loads all assignees of an issue func (issue *Issue) loadAssignees(e db.Engine) (err error) { // Reset maybe preexisting assignees - issue.Assignees = []*User{} + issue.Assignees = []*user_model.User{} err = e.Table("`user`"). Join("INNER", "issue_assignees", "assignee_id = `user`.id"). @@ -63,11 +64,11 @@ func GetAssigneeIDsByIssue(issueID int64) ([]int64, error) { } // GetAssigneesByIssue returns everyone assigned to that issue -func GetAssigneesByIssue(issue *Issue) (assignees []*User, err error) { +func GetAssigneesByIssue(issue *Issue) (assignees []*user_model.User, err error) { return getAssigneesByIssue(db.GetEngine(db.DefaultContext), issue) } -func getAssigneesByIssue(e db.Engine, issue *Issue) (assignees []*User, err error) { +func getAssigneesByIssue(e db.Engine, issue *Issue) (assignees []*user_model.User, err error) { err = issue.loadAssignees(e) if err != nil { return assignees, err @@ -77,11 +78,11 @@ func getAssigneesByIssue(e db.Engine, issue *Issue) (assignees []*User, err erro } // IsUserAssignedToIssue returns true when the user is assigned to the issue -func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) { +func IsUserAssignedToIssue(issue *Issue, user *user_model.User) (isAssigned bool, err error) { return isUserAssignedToIssue(db.GetEngine(db.DefaultContext), issue, user) } -func isUserAssignedToIssue(e db.Engine, issue *Issue, user *User) (isAssigned bool, err error) { +func isUserAssignedToIssue(e db.Engine, issue *Issue, user *user_model.User) (isAssigned bool, err error) { return e.Get(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID}) } @@ -92,7 +93,7 @@ func clearAssigneeByUserID(sess db.Engine, userID int64) (err error) { } // ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it. -func (issue *Issue) ToggleAssignee(doer *User, assigneeID int64) (removed bool, comment *Comment, err error) { +func (issue *Issue) ToggleAssignee(doer *user_model.User, assigneeID int64) (removed bool, comment *Comment, err error) { ctx, committer, err := db.TxContext() if err != nil { return false, nil, err @@ -111,7 +112,7 @@ func (issue *Issue) ToggleAssignee(doer *User, assigneeID int64) (removed bool, return removed, comment, nil } -func (issue *Issue) toggleAssignee(ctx context.Context, doer *User, assigneeID int64, isCreate bool) (removed bool, comment *Comment, err error) { +func (issue *Issue) toggleAssignee(ctx context.Context, doer *user_model.User, assigneeID int64, isCreate bool) (removed bool, comment *Comment, err error) { sess := db.GetEngine(ctx) removed, err = toggleUserAssignee(sess, issue, assigneeID) if err != nil { @@ -148,7 +149,7 @@ func (issue *Issue) toggleAssignee(ctx context.Context, doer *User, assigneeID i // toggles user assignee state in database func toggleUserAssignee(e db.Engine, issue *Issue, assigneeID int64) (removed bool, err error) { // Check if the user exists - assignee, err := getUserByID(e, assigneeID) + assignee, err := user_model.GetUserByIDEngine(e, assigneeID) if err != nil { return false, err } @@ -196,7 +197,7 @@ func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string } // Get the IDs of all assignees - assigneeIDs, err = GetUserIDsByNames(requestAssignees, false) + assigneeIDs, err = user_model.GetUserIDsByNames(requestAssignees, false) return } diff --git a/models/issue_assignees_test.go b/models/issue_assignees_test.go index b604f13bd5..dd9a42b572 100644 --- a/models/issue_assignees_test.go +++ b/models/issue_assignees_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -20,19 +21,19 @@ func TestUpdateAssignee(t *testing.T) { assert.NoError(t, err) // Assign multiple users - user2, err := GetUserByID(2) + user2, err := user_model.GetUserByID(2) assert.NoError(t, err) - _, _, err = issue.ToggleAssignee(&User{ID: 1}, user2.ID) + _, _, err = issue.ToggleAssignee(&user_model.User{ID: 1}, user2.ID) assert.NoError(t, err) - user3, err := GetUserByID(3) + user3, err := user_model.GetUserByID(3) assert.NoError(t, err) - _, _, err = issue.ToggleAssignee(&User{ID: 1}, user3.ID) + _, _, err = issue.ToggleAssignee(&user_model.User{ID: 1}, user3.ID) assert.NoError(t, err) - user1, err := GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him + user1, err := user_model.GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running UpdateAssignee should unassign him assert.NoError(t, err) - _, _, err = issue.ToggleAssignee(&User{ID: 1}, user1.ID) + _, _, err = issue.ToggleAssignee(&user_model.User{ID: 1}, user1.ID) assert.NoError(t, err) // Check if he got removed @@ -44,7 +45,7 @@ func TestUpdateAssignee(t *testing.T) { assignees, err := GetAssigneesByIssue(issue) assert.NoError(t, err) - var expectedAssignees []*User + var expectedAssignees []*user_model.User expectedAssignees = append(expectedAssignees, user2, user3) for in, assignee := range assignees { @@ -57,7 +58,7 @@ func TestUpdateAssignee(t *testing.T) { assert.True(t, isAssigned) // This user should not be assigned - isAssigned, err = IsUserAssignedToIssue(issue, &User{ID: 4}) + isAssigned, err = IsUserAssignedToIssue(issue, &user_model.User{ID: 4}) assert.NoError(t, err) assert.False(t, isAssigned) } @@ -65,8 +66,8 @@ func TestUpdateAssignee(t *testing.T) { func TestMakeIDsFromAPIAssigneesToAdd(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - _ = unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - _ = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) + _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) IDs, err := MakeIDsFromAPIAssigneesToAdd("", []string{""}) assert.NoError(t, err) diff --git a/models/issue_comment.go b/models/issue_comment.go index 8429596c82..417caccfaf 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -148,10 +149,10 @@ func (rd RoleDescriptor) HasRole(role string) bool { // Comment represents a comment in commit and issue page. type Comment struct { - ID int64 `xorm:"pk autoincr"` - Type CommentType `xorm:"INDEX"` - PosterID int64 `xorm:"INDEX"` - Poster *User `xorm:"-"` + ID int64 `xorm:"pk autoincr"` + Type CommentType `xorm:"INDEX"` + PosterID int64 `xorm:"INDEX"` + Poster *user_model.User `xorm:"-"` OriginalAuthor string OriginalAuthorID int64 IssueID int64 `xorm:"INDEX"` @@ -172,11 +173,11 @@ type Comment struct { Time *TrackedTime `xorm:"-"` AssigneeID int64 RemovedAssignee bool - Assignee *User `xorm:"-"` - AssigneeTeamID int64 `xorm:"NOT NULL DEFAULT 0"` - AssigneeTeam *Team `xorm:"-"` + Assignee *user_model.User `xorm:"-"` + AssigneeTeamID int64 `xorm:"NOT NULL DEFAULT 0"` + AssigneeTeam *Team `xorm:"-"` ResolveDoerID int64 - ResolveDoer *User `xorm:"-"` + ResolveDoer *user_model.User `xorm:"-"` OldTitle string NewTitle string OldRef string @@ -284,11 +285,11 @@ func (c *Comment) loadPoster(e db.Engine) (err error) { return nil } - c.Poster, err = getUserByID(e, c.PosterID) + c.Poster, err = user_model.GetUserByIDEngine(e, c.PosterID) if err != nil { - if IsErrUserNotExist(err) { + if user_model.IsErrUserNotExist(err) { c.PosterID = -1 - c.Poster = NewGhostUser() + c.Poster = user_model.NewGhostUser() } else { log.Error("getUserByID[%d]: %v", c.ID, err) } @@ -519,12 +520,12 @@ func (c *Comment) LoadAssigneeUserAndTeam() error { var err error if c.AssigneeID > 0 && c.Assignee == nil { - c.Assignee, err = GetUserByIDCtx(db.DefaultContext, c.AssigneeID) + c.Assignee, err = user_model.GetUserByIDCtx(db.DefaultContext, c.AssigneeID) if err != nil { - if !IsErrUserNotExist(err) { + if !user_model.IsErrUserNotExist(err) { return err } - c.Assignee = NewGhostUser() + c.Assignee = user_model.NewGhostUser() } } else if c.AssigneeTeamID > 0 && c.AssigneeTeam == nil { if err = c.LoadIssue(); err != nil { @@ -554,10 +555,10 @@ func (c *Comment) LoadResolveDoer() (err error) { if c.ResolveDoerID == 0 || c.Type != CommentTypeCode { return nil } - c.ResolveDoer, err = GetUserByIDCtx(db.DefaultContext, c.ResolveDoerID) + c.ResolveDoer, err = user_model.GetUserByIDCtx(db.DefaultContext, c.ResolveDoerID) if err != nil { - if IsErrUserNotExist(err) { - c.ResolveDoer = NewGhostUser() + if user_model.IsErrUserNotExist(err) { + c.ResolveDoer = user_model.NewGhostUser() err = nil } } @@ -628,7 +629,7 @@ func (c *Comment) LoadReview() error { var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`) -func (c *Comment) checkInvalidation(doer *User, repo *git.Repository, branch string) error { +func (c *Comment) checkInvalidation(doer *user_model.User, repo *git.Repository, branch string) error { // FIXME differentiate between previous and proposed line commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { @@ -647,7 +648,7 @@ func (c *Comment) checkInvalidation(doer *User, repo *git.Repository, branch str // CheckInvalidation checks if the line of code comment got changed by another commit. // If the line got changed the comment is going to be invalidated. -func (c *Comment) CheckInvalidation(repo *git.Repository, doer *User, branch string) error { +func (c *Comment) CheckInvalidation(repo *git.Repository, doer *user_model.User, branch string) error { return c.checkInvalidation(doer, repo, branch) } @@ -824,7 +825,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment return updateIssueCols(e, opts.Issue, "updated_unix") } -func createDeadlineComment(ctx context.Context, doer *User, issue *Issue, newDeadlineUnix timeutil.TimeStamp) (*Comment, error) { +func createDeadlineComment(ctx context.Context, doer *user_model.User, issue *Issue, newDeadlineUnix timeutil.TimeStamp) (*Comment, error) { var content string var commentType CommentType @@ -861,7 +862,7 @@ func createDeadlineComment(ctx context.Context, doer *User, issue *Issue, newDea } // Creates issue dependency comment -func createIssueDependencyComment(ctx context.Context, doer *User, issue, dependentIssue *Issue, add bool) (err error) { +func createIssueDependencyComment(ctx context.Context, doer *user_model.User, issue, dependentIssue *Issue, add bool) (err error) { cType := CommentTypeAddDependency if !add { cType = CommentTypeRemoveDependency @@ -896,7 +897,7 @@ func createIssueDependencyComment(ctx context.Context, doer *User, issue, depend // CreateCommentOptions defines options for creating comment type CreateCommentOptions struct { Type CommentType - Doer *User + Doer *user_model.User Repo *Repository Issue *Issue Label *Label @@ -952,7 +953,7 @@ func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { } // CreateRefComment creates a commit reference comment to issue. -func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error { +func CreateRefComment(doer *user_model.User, repo *Repository, issue *Issue, content, commitSHA string) error { if len(commitSHA) == 0 { return fmt.Errorf("cannot create reference with empty commit SHA") } @@ -1072,7 +1073,7 @@ func CountComments(opts *FindCommentsOptions) (int64, error) { } // UpdateComment updates information of comment. -func UpdateComment(c *Comment, doer *User) error { +func UpdateComment(c *Comment, doer *user_model.User) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -1143,11 +1144,11 @@ func deleteComment(e db.Engine, comment *Comment) error { // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS type CodeComments map[string]map[int64][]*Comment -func fetchCodeComments(e db.Engine, issue *Issue, currentUser *User) (CodeComments, error) { +func fetchCodeComments(e db.Engine, issue *Issue, currentUser *user_model.User) (CodeComments, error) { return fetchCodeCommentsByReview(e, issue, currentUser, nil) } -func fetchCodeCommentsByReview(e db.Engine, issue *Issue, currentUser *User, review *Review) (CodeComments, error) { +func fetchCodeCommentsByReview(e db.Engine, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) { pathToLineToComment := make(CodeComments) if review == nil { review = &Review{ID: 0} @@ -1172,7 +1173,7 @@ func fetchCodeCommentsByReview(e db.Engine, issue *Issue, currentUser *User, rev return pathToLineToComment, nil } -func findCodeComments(e db.Engine, opts FindCommentsOptions, issue *Issue, currentUser *User, review *Review) ([]*Comment, error) { +func findCodeComments(e db.Engine, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) { var comments []*Comment if review == nil { review = &Review{ID: 0} @@ -1241,7 +1242,7 @@ func findCodeComments(e db.Engine, opts FindCommentsOptions, issue *Issue, curre } // FetchCodeCommentsByLine fetches the code comments for a given treePath and line number -func FetchCodeCommentsByLine(issue *Issue, currentUser *User, treePath string, line int64) ([]*Comment, error) { +func FetchCodeCommentsByLine(issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) { opts := FindCommentsOptions{ Type: CommentTypeCode, IssueID: issue.ID, @@ -1252,7 +1253,7 @@ func FetchCodeCommentsByLine(issue *Issue, currentUser *User, treePath string, l } // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line -func FetchCodeComments(issue *Issue, currentUser *User) (CodeComments, error) { +func FetchCodeComments(issue *Issue, currentUser *user_model.User) (CodeComments, error) { return fetchCodeComments(db.GetEngine(db.DefaultContext), issue, currentUser) } @@ -1277,7 +1278,7 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID } // CreatePushPullComment create push code to pull base comment -func CreatePushPullComment(pusher *User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) { +func CreatePushPullComment(pusher *user_model.User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) { if pr.HasMerged || oldCommitID == "" || newCommitID == "" { return nil, nil } diff --git a/models/issue_comment_list.go b/models/issue_comment_list.go index 3c587fc8f7..ef6aff9cbe 100644 --- a/models/issue_comment_list.go +++ b/models/issue_comment_list.go @@ -7,6 +7,7 @@ package models import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" ) // CommentList defines a list of comments @@ -28,7 +29,7 @@ func (comments CommentList) loadPosters(e db.Engine) error { } posterIDs := comments.getPosterIDs() - posterMaps := make(map[int64]*User, len(posterIDs)) + posterMaps := make(map[int64]*user_model.User, len(posterIDs)) left := len(posterIDs) for left > 0 { limit := defaultMaxInSize @@ -51,7 +52,7 @@ func (comments CommentList) loadPosters(e db.Engine) error { } var ok bool if comment.Poster, ok = posterMaps[comment.PosterID]; !ok { - comment.Poster = NewGhostUser() + comment.Poster = user_model.NewGhostUser() } } return nil @@ -217,7 +218,7 @@ func (comments CommentList) loadAssignees(e db.Engine) error { } assigneeIDs := comments.getAssigneeIDs() - assignees := make(map[int64]*User, len(assigneeIDs)) + assignees := make(map[int64]*user_model.User, len(assigneeIDs)) left := len(assigneeIDs) for left > 0 { limit := defaultMaxInSize @@ -226,13 +227,13 @@ func (comments CommentList) loadAssignees(e db.Engine) error { } rows, err := e. In("id", assigneeIDs[:limit]). - Rows(new(User)) + Rows(new(user_model.User)) if err != nil { return err } for rows.Next() { - var user User + var user user_model.User err = rows.Scan(&user) if err != nil { rows.Close() diff --git a/models/issue_comment_test.go b/models/issue_comment_test.go index 4d3607b3e1..5ab4892bc7 100644 --- a/models/issue_comment_test.go +++ b/models/issue_comment_test.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -18,7 +19,7 @@ func TestCreateComment(t *testing.T) { issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue) repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: issue.RepoID}).(*Repository) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) now := time.Now().Unix() comment, err := CreateComment(&CreateCommentOptions{ @@ -46,7 +47,7 @@ func TestFetchCodeComments(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) res, err := FetchCodeComments(issue, user) assert.NoError(t, err) assert.Contains(t, res, "README.md") @@ -54,7 +55,7 @@ func TestFetchCodeComments(t *testing.T) { assert.Len(t, res["README.md"][4], 1) assert.Equal(t, int64(4), res["README.md"][4][0].ID) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) res, err = FetchCodeComments(issue, user2) assert.NoError(t, err) assert.Len(t, res, 1) diff --git a/models/issue_dependency.go b/models/issue_dependency.go index ab81ef0c68..9fef652b4e 100644 --- a/models/issue_dependency.go +++ b/models/issue_dependency.go @@ -7,6 +7,7 @@ package models import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -36,7 +37,7 @@ const ( ) // CreateIssueDependency creates a new dependency for an issue -func CreateIssueDependency(user *User, issue, dep *Issue) error { +func CreateIssueDependency(user *user_model.User, issue, dep *Issue) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -78,7 +79,7 @@ func CreateIssueDependency(user *User, issue, dep *Issue) error { } // RemoveIssueDependency removes a dependency from an issue -func RemoveIssueDependency(user *User, issue, dep *Issue, depType DependencyType) (err error) { +func RemoveIssueDependency(user *user_model.User, issue, dep *Issue, depType DependencyType) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err diff --git a/models/issue_dependency_test.go b/models/issue_dependency_test.go index 86ac98b733..61215dedef 100644 --- a/models/issue_dependency_test.go +++ b/models/issue_dependency_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -16,7 +17,7 @@ func TestCreateIssueDependency(t *testing.T) { // Prepare assert.NoError(t, unittest.PrepareTestDatabase()) - user1, err := GetUserByID(1) + user1, err := user_model.GetUserByID(1) assert.NoError(t, err) issue1, err := GetIssueByID(1) diff --git a/models/issue_label.go b/models/issue_label.go index 2858b12dde..9b36d8dfdf 100644 --- a/models/issue_label.go +++ b/models/issue_label.go @@ -15,6 +15,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" @@ -665,7 +666,7 @@ func HasIssueLabel(issueID, labelID int64) bool { // newIssueLabel this function creates a new label it does not check if the label is valid for the issue // YOU MUST CHECK THIS BEFORE THIS FUNCTION -func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *User) (err error) { +func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) { e := db.GetEngine(ctx) if _, err = e.Insert(&IssueLabel{ IssueID: issue.ID, @@ -694,7 +695,7 @@ func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *User) } // NewIssueLabel creates a new issue-label relation. -func NewIssueLabel(issue *Issue, label *Label, doer *User) (err error) { +func NewIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error) { if HasIssueLabel(issue.ID, label.ID) { return nil } @@ -728,7 +729,7 @@ func NewIssueLabel(issue *Issue, label *Label, doer *User) (err error) { } // newIssueLabels add labels to an issue. It will check if the labels are valid for the issue -func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *User) (err error) { +func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *user_model.User) (err error) { e := db.GetEngine(ctx) if err = issue.loadRepo(e); err != nil { return err @@ -749,7 +750,7 @@ func newIssueLabels(ctx context.Context, issue *Issue, labels []*Label, doer *Us } // NewIssueLabels creates a list of issue-label relations. -func NewIssueLabels(issue *Issue, labels []*Label, doer *User) (err error) { +func NewIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err @@ -768,7 +769,7 @@ func NewIssueLabels(issue *Issue, labels []*Label, doer *User) (err error) { return committer.Commit() } -func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *User) (err error) { +func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_model.User) (err error) { e := db.GetEngine(ctx) if count, err := e.Delete(&IssueLabel{ IssueID: issue.ID, @@ -798,7 +799,7 @@ func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *Use } // DeleteIssueLabel deletes issue-label relation. -func DeleteIssueLabel(issue *Issue, label *Label, doer *User) (err error) { +func DeleteIssueLabel(issue *Issue, label *Label, doer *user_model.User) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err diff --git a/models/issue_label_test.go b/models/issue_label_test.go index d0f807a56e..aa3c92b282 100644 --- a/models/issue_label_test.go +++ b/models/issue_label_test.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -296,7 +297,7 @@ func TestNewIssueLabel(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // add new IssueLabel prevNumIssues := label.NumIssues @@ -322,7 +323,7 @@ func TestNewIssueLabels(t *testing.T) { label1 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) label2 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 5}).(*Issue) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) assert.NoError(t, NewIssueLabels(issue, []*Label{label1, label2}, doer)) unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) @@ -352,7 +353,7 @@ func TestDeleteIssueLabel(t *testing.T) { testSuccess := func(labelID, issueID, doerID int64) { label := unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: doerID}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User) expectedNumIssues := label.NumIssues expectedNumClosedIssues := label.NumClosedIssues diff --git a/models/issue_list.go b/models/issue_list.go index 6eeb2a961a..7c98f60b3a 100644 --- a/models/issue_list.go +++ b/models/issue_list.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "xorm.io/builder" ) @@ -84,7 +85,7 @@ func (issues IssueList) loadPosters(e db.Engine) error { } posterIDs := issues.getPosterIDs() - posterMaps := make(map[int64]*User, len(posterIDs)) + posterMaps := make(map[int64]*user_model.User, len(posterIDs)) left := len(posterIDs) for left > 0 { limit := defaultMaxInSize @@ -107,7 +108,7 @@ func (issues IssueList) loadPosters(e db.Engine) error { } var ok bool if issue.Poster, ok = posterMaps[issue.PosterID]; !ok { - issue.Poster = NewGhostUser() + issue.Poster = user_model.NewGhostUser() } } return nil @@ -219,11 +220,11 @@ func (issues IssueList) loadAssignees(e db.Engine) error { } type AssigneeIssue struct { - IssueAssignee *IssueAssignees `xorm:"extends"` - Assignee *User `xorm:"extends"` + IssueAssignee *IssueAssignees `xorm:"extends"` + Assignee *user_model.User `xorm:"extends"` } - assignees := make(map[int64][]*User, len(issues)) + assignees := make(map[int64][]*user_model.User, len(issues)) issueIDs := issues.getIssueIDs() left := len(issueIDs) for left > 0 { diff --git a/models/issue_lock.go b/models/issue_lock.go index d0b9c660c2..66a932225a 100644 --- a/models/issue_lock.go +++ b/models/issue_lock.go @@ -4,11 +4,14 @@ package models -import "code.gitea.io/gitea/models/db" +import ( + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" +) // IssueLockOptions defines options for locking and/or unlocking an issue/PR type IssueLockOptions struct { - Doer *User + Doer *user_model.User Issue *Issue Reason string } diff --git a/models/issue_milestone.go b/models/issue_milestone.go index de4e13b4bb..a85c865464 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -11,6 +11,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -267,7 +268,7 @@ func changeMilestoneStatus(e db.Engine, m *Milestone, isClosed bool) error { return updateRepoMilestoneNum(e, m.RepoID) } -func changeMilestoneAssign(ctx context.Context, doer *User, issue *Issue, oldMilestoneID int64) error { +func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *Issue, oldMilestoneID int64) error { e := db.GetEngine(ctx) if err := updateIssueCols(e, issue, "milestone_id"); err != nil { return err @@ -307,7 +308,7 @@ func changeMilestoneAssign(ctx context.Context, doer *User, issue *Issue, oldMil } // ChangeMilestoneAssign changes assignment of milestone for issue. -func ChangeMilestoneAssign(issue *Issue, doer *User, oldMilestoneID int64) (err error) { +func ChangeMilestoneAssign(issue *Issue, doer *user_model.User, oldMilestoneID int64) (err error) { ctx, committer, err := db.TxContext() if err != nil { return err diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go index 2956e40f06..21d90a6118 100644 --- a/models/issue_milestone_test.go +++ b/models/issue_milestone_test.go @@ -10,6 +10,7 @@ import ( "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/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -240,7 +241,7 @@ func TestUpdateMilestoneCounters(t *testing.T) { func TestChangeMilestoneAssign(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{RepoID: 1}).(*Issue) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) assert.NotNil(t, issue) assert.NotNil(t, doer) diff --git a/models/issue_reaction.go b/models/issue_reaction.go index 933246544b..fb34db4ed5 100644 --- a/models/issue_reaction.go +++ b/models/issue_reaction.go @@ -9,6 +9,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -24,7 +25,7 @@ type Reaction struct { UserID int64 `xorm:"INDEX UNIQUE(s) NOT NULL"` OriginalAuthorID int64 `xorm:"INDEX UNIQUE(s) NOT NULL DEFAULT(0)"` OriginalAuthor string `xorm:"INDEX UNIQUE(s)"` - User *User `xorm:"-"` + User *user_model.User `xorm:"-"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` } @@ -136,7 +137,7 @@ func createReaction(e db.Engine, opts *ReactionOptions) (*Reaction, error) { // ReactionOptions defines options for creating or deleting reactions type ReactionOptions struct { Type string - Doer *User + Doer *user_model.User Issue *Issue Comment *Comment } @@ -165,7 +166,7 @@ func CreateReaction(opts *ReactionOptions) (*Reaction, error) { } // CreateIssueReaction creates a reaction on issue. -func CreateIssueReaction(doer *User, issue *Issue, content string) (*Reaction, error) { +func CreateIssueReaction(doer *user_model.User, issue *Issue, content string) (*Reaction, error) { return CreateReaction(&ReactionOptions{ Type: content, Doer: doer, @@ -174,7 +175,7 @@ func CreateIssueReaction(doer *User, issue *Issue, content string) (*Reaction, e } // CreateCommentReaction creates a reaction on comment. -func CreateCommentReaction(doer *User, issue *Issue, comment *Comment, content string) (*Reaction, error) { +func CreateCommentReaction(doer *user_model.User, issue *Issue, comment *Comment, content string) (*Reaction, error) { return CreateReaction(&ReactionOptions{ Type: content, Doer: doer, @@ -216,7 +217,7 @@ func DeleteReaction(opts *ReactionOptions) error { } // DeleteIssueReaction deletes a reaction on issue. -func DeleteIssueReaction(doer *User, issue *Issue, content string) error { +func DeleteIssueReaction(doer *user_model.User, issue *Issue, content string) error { return DeleteReaction(&ReactionOptions{ Type: content, Doer: doer, @@ -225,7 +226,7 @@ func DeleteIssueReaction(doer *User, issue *Issue, content string) error { } // DeleteCommentReaction deletes a reaction on comment. -func DeleteCommentReaction(doer *User, issue *Issue, comment *Comment, content string) error { +func DeleteCommentReaction(doer *user_model.User, issue *Issue, comment *Comment, content string) error { return DeleteReaction(&ReactionOptions{ Type: content, Doer: doer, @@ -235,11 +236,11 @@ func DeleteCommentReaction(doer *User, issue *Issue, comment *Comment, content s } // LoadUser load user of reaction -func (r *Reaction) LoadUser() (*User, error) { +func (r *Reaction) LoadUser() (*user_model.User, error) { if r.User != nil { return r.User, nil } - user, err := GetUserByIDCtx(db.DefaultContext, r.UserID) + user, err := user_model.GetUserByIDCtx(db.DefaultContext, r.UserID) if err != nil { return nil, err } @@ -285,13 +286,13 @@ func (list ReactionList) getUserIDs() []int64 { return keysInt64(userIDs) } -func (list ReactionList) loadUsers(e db.Engine, repo *Repository) ([]*User, error) { +func (list ReactionList) loadUsers(e db.Engine, repo *Repository) ([]*user_model.User, error) { if len(list) == 0 { return nil, nil } userIDs := list.getUserIDs() - userMaps := make(map[int64]*User, len(userIDs)) + userMaps := make(map[int64]*user_model.User, len(userIDs)) err := e. In("id", userIDs). Find(&userMaps) @@ -301,18 +302,18 @@ func (list ReactionList) loadUsers(e db.Engine, repo *Repository) ([]*User, erro for _, reaction := range list { if reaction.OriginalAuthor != "" { - reaction.User = NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name())) + reaction.User = user_model.NewReplaceUser(fmt.Sprintf("%s(%s)", reaction.OriginalAuthor, repo.OriginalServiceType.Name())) } else if user, ok := userMaps[reaction.UserID]; ok { reaction.User = user } else { - reaction.User = NewGhostUser() + reaction.User = user_model.NewGhostUser() } } return valuesUser(userMaps), nil } // LoadUsers loads reactions' all users -func (list ReactionList) LoadUsers(repo *Repository) ([]*User, error) { +func (list ReactionList) LoadUsers(repo *Repository) ([]*user_model.User, error) { return list.loadUsers(db.GetEngine(db.DefaultContext), repo) } diff --git a/models/issue_reaction_test.go b/models/issue_reaction_test.go index c01e4add7a..458250ac58 100644 --- a/models/issue_reaction_test.go +++ b/models/issue_reaction_test.go @@ -8,12 +8,13 @@ import ( "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/setting" "github.com/stretchr/testify/assert" ) -func addReaction(t *testing.T, doer *User, issue *Issue, comment *Comment, content string) { +func addReaction(t *testing.T, doer *user_model.User, issue *Issue, comment *Comment, content string) { var reaction *Reaction var err error if comment == nil { @@ -28,7 +29,7 @@ func addReaction(t *testing.T, doer *User, issue *Issue, comment *Comment, conte func TestIssueAddReaction(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) @@ -40,7 +41,7 @@ func TestIssueAddReaction(t *testing.T) { func TestIssueAddDuplicateReaction(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) @@ -61,7 +62,7 @@ func TestIssueAddDuplicateReaction(t *testing.T) { func TestIssueDeleteReaction(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) @@ -78,11 +79,11 @@ func TestIssueReactionCount(t *testing.T) { setting.UI.ReactionMaxUserNum = 2 - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - user4 := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) - ghost := NewGhostUser() + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) + ghost := user_model.NewGhostUser() issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) @@ -114,7 +115,7 @@ func TestIssueReactionCount(t *testing.T) { func TestIssueCommentAddReaction(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) @@ -128,10 +129,10 @@ func TestIssueCommentAddReaction(t *testing.T) { func TestIssueCommentDeleteReaction(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - user4 := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) repo1 := unittest.AssertExistsAndLoadBean(t, &Repository{ID: issue1.RepoID}).(*Repository) @@ -155,7 +156,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) { func TestIssueCommentReactionCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1 := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) issue1 := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) diff --git a/models/issue_stopwatch.go b/models/issue_stopwatch.go index eaec5d924e..7754e90a86 100644 --- a/models/issue_stopwatch.go +++ b/models/issue_stopwatch.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" ) @@ -104,7 +105,7 @@ func hasUserStopwatch(e db.Engine, userID int64) (exists bool, sw *Stopwatch, er } // FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore -func FinishIssueStopwatchIfPossible(ctx context.Context, user *User, issue *Issue) error { +func FinishIssueStopwatchIfPossible(ctx context.Context, user *user_model.User, issue *Issue) error { _, exists, err := getStopwatch(ctx, user.ID, issue.ID) if err != nil { return err @@ -116,7 +117,7 @@ func FinishIssueStopwatchIfPossible(ctx context.Context, user *User, issue *Issu } // CreateOrStopIssueStopwatch create an issue stopwatch if it's not exist, otherwise finish it -func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { +func CreateOrStopIssueStopwatch(user *user_model.User, issue *Issue) error { _, exists, err := getStopwatch(db.DefaultContext, user.ID, issue.ID) if err != nil { return err @@ -128,7 +129,7 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error { } // FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error -func FinishIssueStopwatch(ctx context.Context, user *User, issue *Issue) error { +func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { sw, exists, err := getStopwatch(ctx, user.ID, issue.ID) if err != nil { return err @@ -174,7 +175,7 @@ func FinishIssueStopwatch(ctx context.Context, user *User, issue *Issue) error { } // CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error -func CreateIssueStopwatch(ctx context.Context, user *User, issue *Issue) error { +func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { e := db.GetEngine(ctx) if err := issue.loadRepo(e); err != nil { return err @@ -223,7 +224,7 @@ func CreateIssueStopwatch(ctx context.Context, user *User, issue *Issue) error { } // CancelStopwatch removes the given stopwatch and logs it into issue's timeline. -func CancelStopwatch(user *User, issue *Issue) error { +func CancelStopwatch(user *user_model.User, issue *Issue) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -235,7 +236,7 @@ func CancelStopwatch(user *User, issue *Issue) error { return committer.Commit() } -func cancelStopwatch(ctx context.Context, user *User, issue *Issue) error { +func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { e := db.GetEngine(ctx) sw, exists, err := getStopwatch(ctx, user.ID, issue.ID) if err != nil { diff --git a/models/issue_stopwatch_test.go b/models/issue_stopwatch_test.go index ceb579d095..0f578f101f 100644 --- a/models/issue_stopwatch_test.go +++ b/models/issue_stopwatch_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" @@ -16,7 +17,7 @@ import ( func TestCancelStopwatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1, err := GetUserByID(1) + user1, err := user_model.GetUserByID(1) assert.NoError(t, err) issue1, err := GetIssueByID(1) @@ -56,9 +57,9 @@ func TestHasUserStopwatch(t *testing.T) { func TestCreateOrStopIssueStopwatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user2, err := GetUserByID(2) + user2, err := user_model.GetUserByID(2) assert.NoError(t, err) - user3, err := GetUserByID(3) + user3, err := user_model.GetUserByID(3) assert.NoError(t, err) issue1, err := GetIssueByID(1) diff --git a/models/issue_test.go b/models/issue_test.go index 7942b7e785..a9e19438d6 100644 --- a/models/issue_test.go +++ b/models/issue_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -23,7 +24,7 @@ func TestIssue_ReplaceLabels(t *testing.T) { testSuccess := func(issueID int64, labelIDs []int64) { issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue) repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: issue.RepoID}).(*Repository) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: repo.OwnerID}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) labels := make([]*Label, len(labelIDs)) for i, labelID := range labelIDs { @@ -110,7 +111,7 @@ func TestIssue_ClearLabels(t *testing.T) { for _, test := range tests { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User) assert.NoError(t, issue.ClearLabels(doer)) unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: test.issueID}) } @@ -322,7 +323,7 @@ func TestIssue_SearchIssueIDsByKeyword(t *testing.T) { func TestGetRepoIDsForIssuesOptions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) for _, test := range []struct { Opts IssuesOptions ExpectedRepoIDs []int64 @@ -354,7 +355,7 @@ func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Is var newIssue Issue t.Run(title, func(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) issue := Issue{ RepoID: repo.ID, @@ -396,10 +397,10 @@ func TestIssue_ResolveMentions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) { - o := unittest.AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User) + o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User) r := unittest.AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository) issue := &Issue{RepoID: r.ID} - d := unittest.AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User) resolved, err := issue.ResolveMentionsByVisibility(db.DefaultContext, d, mentions) assert.NoError(t, err) ids := make([]int64, len(resolved)) diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go index 55d585a55c..392bf91e19 100644 --- a/models/issue_tracked_time.go +++ b/models/issue_tracked_time.go @@ -8,6 +8,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "xorm.io/builder" @@ -15,15 +16,15 @@ import ( // TrackedTime represents a time that was spent for a specific issue. type TrackedTime struct { - ID int64 `xorm:"pk autoincr"` - IssueID int64 `xorm:"INDEX"` - Issue *Issue `xorm:"-"` - UserID int64 `xorm:"INDEX"` - User *User `xorm:"-"` - Created time.Time `xorm:"-"` - CreatedUnix int64 `xorm:"created"` - Time int64 `xorm:"NOT NULL"` - Deleted bool `xorm:"NOT NULL DEFAULT false"` + ID int64 `xorm:"pk autoincr"` + IssueID int64 `xorm:"INDEX"` + Issue *Issue `xorm:"-"` + UserID int64 `xorm:"INDEX"` + User *user_model.User `xorm:"-"` + Created time.Time `xorm:"-"` + CreatedUnix int64 `xorm:"created"` + Time int64 `xorm:"NOT NULL"` + Deleted bool `xorm:"NOT NULL DEFAULT false"` } func init() { @@ -55,7 +56,7 @@ func (t *TrackedTime) loadAttributes(e db.Engine) (err error) { } } if t.User == nil { - t.User, err = getUserByID(e, t.UserID) + t.User, err = user_model.GetUserByIDEngine(e, t.UserID) if err != nil { return } @@ -153,7 +154,7 @@ func GetTrackedSeconds(opts FindTrackedTimesOptions) (int64, error) { } // AddTime will add the given time (in seconds) to the issue -func AddTime(user *User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { +func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -184,7 +185,7 @@ func AddTime(user *User, issue *Issue, amount int64, created time.Time) (*Tracke return t, committer.Commit() } -func addTime(e db.Engine, user *User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { +func addTime(e db.Engine, user *user_model.User, issue *Issue, amount int64, created time.Time) (*TrackedTime, error) { if created.IsZero() { created = time.Now() } @@ -202,7 +203,7 @@ func addTime(e db.Engine, user *User, issue *Issue, amount int64, created time.T } // TotalTimes returns the spent time for each user by an issue -func TotalTimes(options *FindTrackedTimesOptions) (map[*User]string, error) { +func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]string, error) { trackedTimes, err := GetTrackedTimes(options) if err != nil { return nil, err @@ -213,12 +214,12 @@ func TotalTimes(options *FindTrackedTimesOptions) (map[*User]string, error) { totalTimesByUser[t.UserID] += t.Time } - totalTimes := make(map[*User]string) + totalTimes := make(map[*user_model.User]string) // Fetching User and making time human readable for userID, total := range totalTimesByUser { - user, err := GetUserByID(userID) + user, err := user_model.GetUserByID(userID) if err != nil { - if IsErrUserNotExist(err) { + if user_model.IsErrUserNotExist(err) { continue } return nil, err @@ -229,7 +230,7 @@ func TotalTimes(options *FindTrackedTimesOptions) (map[*User]string, error) { } // DeleteIssueUserTimes deletes times for issue -func DeleteIssueUserTimes(issue *Issue, user *User) error { +func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error { ctx, committer, err := db.TxContext() if err != nil { return err diff --git a/models/issue_tracked_time_test.go b/models/issue_tracked_time_test.go index 83420f5a15..97efd8bb72 100644 --- a/models/issue_tracked_time_test.go +++ b/models/issue_tracked_time_test.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -16,7 +17,7 @@ import ( func TestAddTime(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user3, err := GetUserByID(3) + user3, err := user_model.GetUserByID(3) assert.NoError(t, err) issue1, err := GetIssueByID(1) diff --git a/models/issue_watch.go b/models/issue_watch.go index 5bac406ad0..bf5c2593a3 100644 --- a/models/issue_watch.go +++ b/models/issue_watch.go @@ -6,6 +6,7 @@ package models import ( "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" ) @@ -71,7 +72,7 @@ func getIssueWatch(e db.Engine, userID, issueID int64) (iw *IssueWatch, exists b // CheckIssueWatch check if an user is watching an issue // it takes participants and repo watch into account -func CheckIssueWatch(user *User, issue *Issue) (bool, error) { +func CheckIssueWatch(user *user_model.User, issue *Issue) (bool, error) { iw, exist, err := getIssueWatch(db.GetEngine(db.DefaultContext), user.ID, issue.ID) if err != nil { return false, err diff --git a/models/issue_xref.go b/models/issue_xref.go index 55483ce57b..ceaff62be1 100644 --- a/models/issue_xref.go +++ b/models/issue_xref.go @@ -9,6 +9,7 @@ import ( "fmt" "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/references" ) @@ -21,7 +22,7 @@ type crossReference struct { // crossReferencesContext is context to pass along findCrossReference functions type crossReferencesContext struct { Type CommentType - Doer *User + Doer *user_model.User OrigIssue *Issue OrigComment *Comment RemoveOld bool @@ -60,7 +61,7 @@ func neuterCrossReferencesIds(e db.Engine, ids []int64) error { // \/ \/ \/ // -func (issue *Issue) addCrossReferences(stdCtx context.Context, doer *User, removeOld bool) error { +func (issue *Issue) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { var commentType CommentType if issue.IsPull { commentType = CommentTypePullRef @@ -242,7 +243,7 @@ func (issue *Issue) verifyReferencedIssue(e db.Engine, ctx *crossReferencesConte // \/ \/ \/ \/ \/ // -func (comment *Comment) addCrossReferences(stdCtx context.Context, doer *User, removeOld bool) error { +func (comment *Comment) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { if comment.Type != CommentTypeCode && comment.Type != CommentTypeComment { return nil } diff --git a/models/issue_xref_test.go b/models/issue_xref_test.go index c40e545fa8..06f937c80d 100644 --- a/models/issue_xref_test.go +++ b/models/issue_xref_test.go @@ -10,6 +10,7 @@ import ( "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/references" "github.com/stretchr/testify/assert" @@ -79,7 +80,7 @@ func TestXRef_NeuterCrossReferences(t *testing.T) { assert.Equal(t, CommentTypeIssueRef, ref.Type) assert.Equal(t, references.XRefActionNone, ref.RefAction) - d := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) i.Title = "title2, no mentions" assert.NoError(t, i.ChangeTitle(d, title)) @@ -91,7 +92,7 @@ func TestXRef_NeuterCrossReferences(t *testing.T) { func TestXRef_ResolveCrossReferences(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - d := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) i1 := testCreateIssue(t, 1, 2, "title1", "content1", false) i2 := testCreateIssue(t, 1, 2, "title2", "content2", false) @@ -126,7 +127,7 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *Issue { r := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository) - d := unittest.AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) idx, err := db.GetNextResourceIndex("issue_index", r.ID) assert.NoError(t, err) @@ -157,7 +158,7 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRequest { r := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repo}).(*Repository) - d := unittest.AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true} pr := &PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: PullRequestStatusMergeable} assert.NoError(t, NewPullRequest(r, i, nil, nil, pr)) @@ -166,7 +167,7 @@ func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRe } func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *Comment { - d := unittest.AssertExistsAndLoadBean(t, &User{ID: doer}).(*User) + d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) i := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue}).(*Issue) c := &Comment{Type: CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} diff --git a/models/lfs.go b/models/lfs.go index 40539d2b78..da5e61ed0b 100644 --- a/models/lfs.go +++ b/models/lfs.go @@ -8,6 +8,7 @@ import ( "errors" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/timeutil" @@ -132,7 +133,7 @@ func (repo *Repository) CountLFSMetaObjects() (int64, error) { } // LFSObjectAccessible checks if a provided Oid is accessible to the user -func LFSObjectAccessible(user *User, oid string) (bool, error) { +func LFSObjectAccessible(user *user_model.User, oid string) (bool, error) { if user.IsAdmin { count, err := db.GetEngine(db.DefaultContext).Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: oid}}) return count > 0, err @@ -143,7 +144,7 @@ func LFSObjectAccessible(user *User, oid string) (bool, error) { } // LFSAutoAssociate auto associates accessible LFSMetaObjects -func LFSAutoAssociate(metas []*LFSMetaObject, user *User, repoID int64) error { +func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err diff --git a/models/lfs_lock.go b/models/lfs_lock.go index 943699290f..15513ea1dd 100644 --- a/models/lfs_lock.go +++ b/models/lfs_lock.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "xorm.io/xorm" @@ -22,7 +23,6 @@ type LFSLock struct { ID int64 `xorm:"pk autoincr"` Repo *Repository `xorm:"-"` RepoID int64 `xorm:"INDEX NOT NULL"` - Owner *User `xorm:"-"` OwnerID int64 `xorm:"INDEX NOT NULL"` Path string `xorm:"TEXT"` Created time.Time `xorm:"created"` @@ -34,7 +34,6 @@ func init() { // BeforeInsert is invoked from XORM before inserting an object of this type. func (l *LFSLock) BeforeInsert() { - l.OwnerID = l.Owner.ID l.RepoID = l.Repo.ID l.Path = cleanPath(l.Path) } @@ -42,10 +41,6 @@ func (l *LFSLock) BeforeInsert() { // AfterLoad is invoked from XORM after setting the values of all fields of this object. func (l *LFSLock) AfterLoad(session *xorm.Session) { var err error - l.Owner, err = getUserByID(session, l.OwnerID) - if err != nil { - log.Error("LFS lock AfterLoad failed OwnerId[%d] not found: %v", l.OwnerID, err) - } l.Repo, err = getRepositoryByID(session, l.RepoID) if err != nil { log.Error("LFS lock AfterLoad failed RepoId[%d] not found: %v", l.RepoID, err) @@ -58,7 +53,7 @@ func cleanPath(p string) string { // CreateLFSLock creates a new lock. func CreateLFSLock(lock *LFSLock) (*LFSLock, error) { - err := CheckLFSAccessForRepo(lock.Owner, lock.Repo, AccessModeWrite) + err := CheckLFSAccessForRepo(lock.OwnerID, lock.Repo, AccessModeWrite) if err != nil { return nil, err } @@ -123,13 +118,13 @@ func CountLFSLockByRepoID(repoID int64) (int64, error) { } // DeleteLFSLockByID deletes a lock by given ID. -func DeleteLFSLockByID(id int64, u *User, force bool) (*LFSLock, error) { +func DeleteLFSLockByID(id int64, u *user_model.User, force bool) (*LFSLock, error) { lock, err := GetLFSLockByID(id) if err != nil { return nil, err } - err = CheckLFSAccessForRepo(u, lock.Repo, AccessModeWrite) + err = CheckLFSAccessForRepo(u.ID, lock.Repo, AccessModeWrite) if err != nil { return nil, err } @@ -143,10 +138,14 @@ func DeleteLFSLockByID(id int64, u *User, force bool) (*LFSLock, error) { } // CheckLFSAccessForRepo check needed access mode base on action -func CheckLFSAccessForRepo(u *User, repo *Repository, mode AccessMode) error { - if u == nil { +func CheckLFSAccessForRepo(ownerID int64, repo *Repository, mode AccessMode) error { + if ownerID == 0 { return ErrLFSUnauthorizedAction{repo.ID, "undefined", mode} } + u, err := user_model.GetUserByID(ownerID) + if err != nil { + return err + } perm, err := GetUserRepoPermission(repo, u) if err != nil { return err diff --git a/models/main_test.go b/models/main_test.go index 5c99a2b2f7..87e59f5b11 100644 --- a/models/main_test.go +++ b/models/main_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -16,7 +17,7 @@ import ( func TestFixturesAreConsistent(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) unittest.CheckConsistencyFor(t, - &User{}, + &user_model.User{}, &Repository{}, &Issue{}, &PullRequest{}, diff --git a/models/notification.go b/models/notification.go index db78558647..ef8a1e83b8 100644 --- a/models/notification.go +++ b/models/notification.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -62,10 +63,10 @@ type Notification struct { UpdatedBy int64 `xorm:"INDEX NOT NULL"` - Issue *Issue `xorm:"-"` - Repository *Repository `xorm:"-"` - Comment *Comment `xorm:"-"` - User *User `xorm:"-"` + Issue *Issue `xorm:"-"` + Repository *Repository `xorm:"-"` + Comment *Comment `xorm:"-"` + User *user_model.User `xorm:"-"` CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"` UpdatedUnix timeutil.TimeStamp `xorm:"updated INDEX NOT NULL"` @@ -139,7 +140,7 @@ func CountNotifications(opts *FindNotificationOptions) (int64, error) { } // CreateRepoTransferNotification creates notification for the user a repository was transferred to -func CreateRepoTransferNotification(doer, newOwner *User, repo *Repository) error { +func CreateRepoTransferNotification(doer, newOwner *user_model.User, repo *Repository) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -258,9 +259,9 @@ func createOrUpdateIssueNotifications(e db.Engine, issueID, commentID, notificat // notify for userID := range toNotify { issue.Repo.Units = nil - user, err := getUserByID(e, userID) + user, err := user_model.GetUserByIDEngine(e, userID) if err != nil { - if IsErrUserNotExist(err) { + if user_model.IsErrUserNotExist(err) { continue } @@ -355,11 +356,11 @@ func getIssueNotification(e db.Engine, userID, issueID int64) (*Notification, er } // NotificationsForUser returns notifications for a given user and status -func NotificationsForUser(user *User, statuses []NotificationStatus, page, perPage int) (NotificationList, error) { +func NotificationsForUser(user *user_model.User, statuses []NotificationStatus, page, perPage int) (NotificationList, error) { return notificationsForUser(db.GetEngine(db.DefaultContext), user, statuses, page, perPage) } -func notificationsForUser(e db.Engine, user *User, statuses []NotificationStatus, page, perPage int) (notifications []*Notification, err error) { +func notificationsForUser(e db.Engine, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications []*Notification, err error) { if len(statuses) == 0 { return } @@ -378,7 +379,7 @@ func notificationsForUser(e db.Engine, user *User, statuses []NotificationStatus } // CountUnread count unread notifications for a user -func CountUnread(user *User) int64 { +func CountUnread(user *user_model.User) int64 { return countUnread(db.GetEngine(db.DefaultContext), user.ID) } @@ -452,7 +453,7 @@ func (n *Notification) loadComment(e db.Engine) (err error) { func (n *Notification) loadUser(e db.Engine) (err error) { if n.User == nil { - n.User, err = getUserByID(e, n.UserID) + n.User, err = user_model.GetUserByIDEngine(e, n.UserID) if err != nil { return fmt.Errorf("getUserByID [%d]: %v", n.UserID, err) } @@ -733,11 +734,11 @@ func (nl NotificationList) LoadComments() ([]int, error) { } // GetNotificationCount returns the notification count for user -func GetNotificationCount(user *User, status NotificationStatus) (int64, error) { +func GetNotificationCount(user *user_model.User, status NotificationStatus) (int64, error) { return getNotificationCount(db.GetEngine(db.DefaultContext), user, status) } -func getNotificationCount(e db.Engine, user *User, status NotificationStatus) (count int64, err error) { +func getNotificationCount(e db.Engine, user *user_model.User, status NotificationStatus) (count int64, err error) { count, err = e. Where("user_id = ?", user.ID). And("status = ?", status). @@ -788,7 +789,7 @@ func setRepoNotificationStatusReadIfUnread(e db.Engine, userID, repoID int64) er } // SetNotificationStatus change the notification status -func SetNotificationStatus(notificationID int64, user *User, status NotificationStatus) (*Notification, error) { +func SetNotificationStatus(notificationID int64, user *user_model.User, status NotificationStatus) (*Notification, error) { notification, err := getNotificationByID(db.GetEngine(db.DefaultContext), notificationID) if err != nil { return notification, err @@ -826,7 +827,7 @@ func getNotificationByID(e db.Engine, notificationID int64) (*Notification, erro } // UpdateNotificationStatuses updates the statuses of all of a user's notifications that are of the currentStatus type to the desiredStatus -func UpdateNotificationStatuses(user *User, currentStatus, desiredStatus NotificationStatus) error { +func UpdateNotificationStatuses(user *user_model.User, currentStatus, desiredStatus NotificationStatus) error { n := &Notification{Status: desiredStatus, UpdatedBy: user.ID} _, err := db.GetEngine(db.DefaultContext). Where("user_id = ? AND status = ?", user.ID, currentStatus). diff --git a/models/notification_test.go b/models/notification_test.go index 19fad25f99..3b05f34c55 100644 --- a/models/notification_test.go +++ b/models/notification_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -29,7 +30,7 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) { func TestNotificationsForUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread} notfs, err := NotificationsForUser(user, statuses, 1, 10) assert.NoError(t, err) @@ -63,7 +64,7 @@ func TestNotification_GetIssue(t *testing.T) { func TestGetNotificationCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) cnt, err := GetNotificationCount(user, NotificationStatusRead) assert.NoError(t, err) assert.EqualValues(t, 0, cnt) @@ -75,7 +76,7 @@ func TestGetNotificationCount(t *testing.T) { func TestSetNotificationStatus(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: user.ID, Status: NotificationStatusRead}).(*Notification) _, err := SetNotificationStatus(notf.ID, user, NotificationStatusPinned) @@ -91,7 +92,7 @@ func TestSetNotificationStatus(t *testing.T) { func TestUpdateNotificationStatuses(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) notfUnread := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: user.ID, Status: NotificationStatusUnread}).(*Notification) notfRead := unittest.AssertExistsAndLoadBean(t, diff --git a/models/org.go b/models/org.go index ea73d03d42..63e1b99af5 100644 --- a/models/org.go +++ b/models/org.go @@ -21,10 +21,10 @@ import ( ) // Organization represents an organization -type Organization User +type Organization user_model.User // OrgFromUser converts user to organization -func OrgFromUser(user *User) *Organization { +func OrgFromUser(user *user_model.User) *Organization { return (*Organization)(user) } @@ -188,8 +188,8 @@ func (org *Organization) RemoveOrgRepo(repoID int64) error { } // AsUser returns the org as user object -func (org *Organization) AsUser() *User { - return (*User)(org) +func (org *Organization) AsUser() *user_model.User { + return (*user_model.User)(org) } // DisplayName returns full name if it's not empty, @@ -204,34 +204,34 @@ func (org *Organization) CustomAvatarRelativePath() string { } // CreateOrganization creates record of a new organization. -func CreateOrganization(org *Organization, owner *User) (err error) { +func CreateOrganization(org *Organization, owner *user_model.User) (err error) { if !owner.CanCreateOrganization() { return ErrUserNotAllowedCreateOrg{} } - if err = IsUsableUsername(org.Name); err != nil { + if err = user_model.IsUsableUsername(org.Name); err != nil { return err } - isExist, err := IsUserExist(0, org.Name) + isExist, err := user_model.IsUserExist(0, org.Name) if err != nil { return err } else if isExist { - return ErrUserAlreadyExist{org.Name} + return user_model.ErrUserAlreadyExist{Name: org.Name} } org.LowerName = strings.ToLower(org.Name) - if org.Rands, err = GetUserSalt(); err != nil { + if org.Rands, err = user_model.GetUserSalt(); err != nil { return err } - if org.Salt, err = GetUserSalt(); err != nil { + if org.Salt, err = user_model.GetUserSalt(); err != nil { return err } org.UseCustomAvatar = true org.MaxRepoCreation = -1 org.NumTeams = 1 org.NumMembers = 1 - org.Type = UserTypeOrganization + org.Type = user_model.UserTypeOrganization ctx, committer, err := db.TxContext() if err != nil { @@ -246,7 +246,7 @@ func CreateOrganization(org *Organization, owner *User) (err error) { if err = db.Insert(ctx, org); err != nil { return fmt.Errorf("insert organization: %v", err) } - if err = generateRandomAvatar(db.GetEngine(ctx), org.AsUser()); err != nil { + if err = user_model.GenerateRandomAvatarCtx(ctx, org.AsUser()); err != nil { return fmt.Errorf("generate random avatar: %v", err) } @@ -304,7 +304,7 @@ func GetOrgByName(name string) (*Organization, error) { } u := &Organization{ LowerName: strings.ToLower(name), - Type: UserTypeOrganization, + Type: user_model.UserTypeOrganization, } has, err := db.GetEngine(db.DefaultContext).Get(u) if err != nil { @@ -325,7 +325,7 @@ func CountOrganizations() int64 { // DeleteOrganization deletes models associated to an organization. func DeleteOrganization(ctx context.Context, org *Organization) error { - if org.Type != UserTypeOrganization { + if org.Type != user_model.UserTypeOrganization { return fmt.Errorf("%s is a user not an organization", org.Name) } @@ -340,7 +340,7 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { return fmt.Errorf("deleteBeans: %v", err) } - if _, err := e.ID(org.ID).Delete(new(User)); err != nil { + if _, err := e.ID(org.ID).Delete(new(user_model.User)); err != nil { return fmt.Errorf("Delete: %v", err) } @@ -433,12 +433,12 @@ func (org *Organization) GetOrgUserMaxAuthorizeLevel(uid int64) (AccessMode, err } // GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization -func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*User, error) { +func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*user_model.User, error) { return getUsersWhoCanCreateOrgRepo(db.GetEngine(db.DefaultContext), orgID) } -func getUsersWhoCanCreateOrgRepo(e db.Engine, orgID int64) ([]*User, error) { - users := make([]*User, 0, 10) +func getUsersWhoCanCreateOrgRepo(e db.Engine, orgID int64) ([]*user_model.User, error) { + users := make([]*user_model.User, 0, 10) return users, e. Join("INNER", "`team_user`", "`team_user`.uid=`user`.id"). Join("INNER", "`team`", "`team`.id=`team_user`.team_id"). @@ -450,8 +450,8 @@ func getUsersWhoCanCreateOrgRepo(e db.Engine, orgID int64) ([]*User, error) { type MinimalOrg = Organization // GetUserOrgsList returns one user's all orgs list -func GetUserOrgsList(user *User) ([]*MinimalOrg, error) { - schema, err := db.TableInfo(new(User)) +func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) { + schema, err := db.TableInfo(new(user_model.User)) if err != nil { return nil, err } @@ -507,6 +507,12 @@ func GetUserOrgsList(user *User) ([]*MinimalOrg, error) { return orgs, nil } +// SearchOrganizationsOptions options to filter organizations +type SearchOrganizationsOptions struct { + db.ListOptions + All bool +} + // FindOrgOptions finds orgs options type FindOrgOptions struct { db.ListOptions @@ -549,7 +555,7 @@ func FindOrgs(opts FindOrgOptions) ([]*Organization, error) { func CountOrgs(opts FindOrgOptions) (int64, error) { return db.GetEngine(db.DefaultContext). Where(opts.toConds()). - Count(new(User)) + Count(new(user_model.User)) } func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*Organization, error) { @@ -564,11 +570,11 @@ func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*Organization, error) } // HasOrgOrUserVisible tells if the given user can see the given org or user -func HasOrgOrUserVisible(org, user *User) bool { +func HasOrgOrUserVisible(org, user *user_model.User) bool { return hasOrgOrUserVisible(db.GetEngine(db.DefaultContext), org, user) } -func hasOrgOrUserVisible(e db.Engine, orgOrUser, user *User) bool { +func hasOrgOrUserVisible(e db.Engine, orgOrUser, user *user_model.User) bool { // Not SignedUser if user == nil { return orgOrUser.Visibility == structs.VisibleTypePublic @@ -585,7 +591,7 @@ func hasOrgOrUserVisible(e db.Engine, orgOrUser, user *User) bool { } // HasOrgsVisible tells if the given user can see at least one of the orgs provided -func HasOrgsVisible(orgs []*Organization, user *User) bool { +func HasOrgsVisible(orgs []*Organization, user *user_model.User) bool { if len(orgs) == 0 { return false } @@ -718,7 +724,11 @@ func GetOrgByIDCtx(ctx context.Context, id int64) (*Organization, error) { if err != nil { return nil, err } else if !has { - return nil, ErrUserNotExist{id, "", 0} + return nil, user_model.ErrUserNotExist{ + UID: id, + Name: "", + KeyID: 0, + } } return u, nil } @@ -896,17 +906,17 @@ type AccessibleReposEnvironment interface { Repos(page, pageSize int) ([]*Repository, error) MirrorRepos() ([]*Repository, error) AddKeyword(keyword string) - SetSort(SearchOrderBy) + SetSort(db.SearchOrderBy) } type accessibleReposEnv struct { org *Organization - user *User + user *user_model.User team *Team teamIDs []int64 e db.Engine keyword string - orderBy SearchOrderBy + orderBy db.SearchOrderBy } // AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org` @@ -916,10 +926,10 @@ func (org *Organization) AccessibleReposEnv(userID int64) (AccessibleReposEnviro } func (org *Organization) accessibleReposEnv(e db.Engine, userID int64) (AccessibleReposEnvironment, error) { - var user *User + var user *user_model.User if userID > 0 { - u, err := getUserByID(e, userID) + u, err := user_model.GetUserByIDEngine(e, userID) if err != nil { return nil, err } @@ -935,7 +945,7 @@ func (org *Organization) accessibleReposEnv(e db.Engine, userID int64) (Accessib user: user, teamIDs: teamIDs, e: e, - orderBy: SearchOrderByRecentUpdated, + orderBy: db.SearchOrderByRecentUpdated, }, nil } @@ -946,7 +956,7 @@ func (org *Organization) AccessibleTeamReposEnv(team *Team) AccessibleReposEnvir org: org, team: team, e: db.GetEngine(db.DefaultContext), - orderBy: SearchOrderByRecentUpdated, + orderBy: db.SearchOrderByRecentUpdated, } } @@ -1049,6 +1059,6 @@ func (env *accessibleReposEnv) AddKeyword(keyword string) { env.keyword = keyword } -func (env *accessibleReposEnv) SetSort(orderBy SearchOrderBy) { +func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) { env.orderBy = orderBy } diff --git a/models/org_team.go b/models/org_team.go index 1bdffe2cb1..510a0dcfaa 100644 --- a/models/org_team.go +++ b/models/org_team.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -30,8 +31,8 @@ type Team struct { Name string Description string Authorize AccessMode - Repos []*Repository `xorm:"-"` - Members []*User `xorm:"-"` + Repos []*Repository `xorm:"-"` + Members []*user_model.User `xorm:"-"` NumRepos int NumMembers int Units []*TeamUnit `xorm:"-"` @@ -456,7 +457,7 @@ func (t *Team) unitEnabled(e db.Engine, tp unit.Type) bool { func IsUsableTeamName(name string) error { switch name { case "new": - return ErrNameReserved{name} + return db.ErrNameReserved{Name: name} default: return nil } @@ -473,7 +474,7 @@ func NewTeam(t *Team) (err error) { return err } - has, err := db.GetEngine(db.DefaultContext).ID(t.OrgID).Get(new(User)) + has, err := db.GetEngine(db.DefaultContext).ID(t.OrgID).Get(new(user_model.User)) if err != nil { return err } @@ -760,14 +761,14 @@ func getTeamUsersByTeamID(e db.Engine, teamID int64) ([]*TeamUser, error) { Find(&teamUsers) } -func getTeamMembers(e db.Engine, teamID int64) (_ []*User, err error) { +func getTeamMembers(e db.Engine, teamID int64) (_ []*user_model.User, err error) { teamUsers, err := getTeamUsersByTeamID(e, teamID) if err != nil { return nil, fmt.Errorf("get team-users: %v", err) } - members := make([]*User, len(teamUsers)) + members := make([]*user_model.User, len(teamUsers)) for i, teamUser := range teamUsers { - member, err := getUserByID(e, teamUser.UID) + member, err := user_model.GetUserByIDEngine(e, teamUser.UID) if err != nil { return nil, fmt.Errorf("get user '%d': %v", teamUser.UID, err) } @@ -780,7 +781,7 @@ func getTeamMembers(e db.Engine, teamID int64) (_ []*User, err error) { } // GetTeamMembers returns all members in given team of organization. -func GetTeamMembers(teamID int64) ([]*User, error) { +func GetTeamMembers(teamID int64) ([]*user_model.User, error) { return getTeamMembers(db.GetEngine(db.DefaultContext), teamID) } diff --git a/models/org_team_test.go b/models/org_team_test.go index b912dd83f2..450cf7dd55 100644 --- a/models/org_team_test.go +++ b/models/org_team_test.go @@ -8,7 +8,9 @@ import ( "strings" "testing" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -74,7 +76,7 @@ func TestTeam_AddMember(t *testing.T) { team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) assert.NoError(t, team.AddMember(userID)) unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: userID, TeamID: teamID}) - unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &User{ID: team.OrgID}) + unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &user_model.User{ID: team.OrgID}) } test(1, 2) test(1, 4) @@ -151,7 +153,7 @@ func TestTeam_RemoveRepository(t *testing.T) { func TestIsUsableTeamName(t *testing.T) { assert.NoError(t, IsUsableTeamName("usable")) - assert.True(t, IsErrNameReserved(IsUsableTeamName("new"))) + assert.True(t, db.IsErrNameReserved(IsUsableTeamName("new"))) } func TestNewTeam(t *testing.T) { @@ -161,7 +163,7 @@ func TestNewTeam(t *testing.T) { team := &Team{Name: teamName, OrgID: 3} assert.NoError(t, NewTeam(team)) unittest.AssertExistsAndLoadBean(t, &Team{Name: teamName}) - unittest.CheckConsistencyFor(t, &Team{}, &User{ID: team.OrgID}) + unittest.CheckConsistencyFor(t, &Team{}, &user_model.User{ID: team.OrgID}) } func TestGetTeam(t *testing.T) { @@ -243,7 +245,7 @@ func TestDeleteTeam(t *testing.T) { unittest.AssertNotExistsBean(t, &TeamUser{TeamID: team.ID}) // check that team members don't have "leftover" access to repos - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) accessMode, err := AccessLevel(user, repo) assert.NoError(t, err) @@ -321,7 +323,7 @@ func TestAddTeamMember(t *testing.T) { team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) assert.NoError(t, AddTeamMember(team, userID)) unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: userID, TeamID: teamID}) - unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &User{ID: team.OrgID}) + unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &user_model.User{ID: team.OrgID}) } test(1, 2) test(1, 4) diff --git a/models/org_test.go b/models/org_test.go index 805c63300b..c24064c5b8 100644 --- a/models/org_test.go +++ b/models/org_test.go @@ -9,6 +9,7 @@ import ( "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/setting" "code.gitea.io/gitea/modules/structs" @@ -130,7 +131,7 @@ func TestUser_AddMember(t *testing.T) { org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization) assert.Equal(t, prevNumMembers, org.NumMembers) - unittest.CheckConsistencyFor(t, &User{}) + unittest.CheckConsistencyFor(t, &user_model.User{}) } func TestUser_RemoveMember(t *testing.T) { @@ -153,7 +154,7 @@ func TestUser_RemoveMember(t *testing.T) { org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization) assert.Equal(t, prevNumMembers, org.NumMembers) - unittest.CheckConsistencyFor(t, &User{}, &Team{}) + unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{}) } func TestUser_RemoveOrgRepo(t *testing.T) { @@ -174,7 +175,7 @@ func TestUser_RemoveOrgRepo(t *testing.T) { assert.NoError(t, org.RemoveOrgRepo(unittest.NonexistentID)) unittest.CheckConsistencyFor(t, - &User{ID: org.ID}, + &user_model.User{ID: org.ID}, &Team{OrgID: org.ID}, &Repository{ID: repo.ID}) } @@ -183,37 +184,37 @@ func TestCreateOrganization(t *testing.T) { // successful creation of org assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) const newOrgName = "neworg" org := &Organization{ Name: newOrgName, } - unittest.AssertNotExistsBean(t, &User{Name: newOrgName, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &user_model.User{Name: newOrgName, Type: user_model.UserTypeOrganization}) assert.NoError(t, CreateOrganization(org, owner)) org = unittest.AssertExistsAndLoadBean(t, - &Organization{Name: newOrgName, Type: UserTypeOrganization}).(*Organization) + &Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}).(*Organization) ownerTeam := unittest.AssertExistsAndLoadBean(t, &Team{Name: ownerTeamName, OrgID: org.ID}).(*Team) unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: owner.ID, TeamID: ownerTeam.ID}) - unittest.CheckConsistencyFor(t, &User{}, &Team{}) + unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{}) } func TestCreateOrganization2(t *testing.T) { // unauthorized creation of org assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) const newOrgName = "neworg" org := &Organization{ Name: newOrgName, } - unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}) err := CreateOrganization(org, owner) assert.Error(t, err) assert.True(t, IsErrUserNotAllowedCreateOrg(err)) - unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}) unittest.CheckConsistencyFor(t, &Organization{}, &Team{}) } @@ -221,23 +222,23 @@ func TestCreateOrganization3(t *testing.T) { // create org with same name as existent org assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - org := &Organization{Name: "user3"} // should already exist - unittest.AssertExistsAndLoadBean(t, &User{Name: org.Name}) // sanity check + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + org := &Organization{Name: "user3"} // should already exist + unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: org.Name}) // sanity check err := CreateOrganization(org, owner) assert.Error(t, err) - assert.True(t, IsErrUserAlreadyExist(err)) - unittest.CheckConsistencyFor(t, &User{}, &Team{}) + assert.True(t, user_model.IsErrUserAlreadyExist(err)) + unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{}) } func TestCreateOrganization4(t *testing.T) { // create org with unusable name assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) err := CreateOrganization(&Organization{Name: "assets"}, owner) assert.Error(t, err) - assert.True(t, IsErrNameReserved(err)) + assert.True(t, db.IsErrNameReserved(err)) unittest.CheckConsistencyFor(t, &Organization{}, &Team{}) } @@ -258,7 +259,7 @@ func TestGetOrgByName(t *testing.T) { func TestCountOrganizations(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - expected, err := db.GetEngine(db.DefaultContext).Where("type=?", UserTypeOrganization).Count(&User{}) + expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&user_model.User{}) assert.NoError(t, err) assert.Equal(t, expected, CountOrganizations()) } @@ -444,7 +445,7 @@ func TestChangeOrgUserStatus(t *testing.T) { func TestAddOrgUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(orgID, userID int64, isPublic bool) { - org := unittest.AssertExistsAndLoadBean(t, &User{ID: orgID}).(*User) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User) expectedNumMembers := org.NumMembers if !unittest.BeanExists(t, &OrgUser{OrgID: orgID, UID: userID}) { expectedNumMembers++ @@ -453,7 +454,7 @@ func TestAddOrgUser(t *testing.T) { ou := &OrgUser{OrgID: orgID, UID: userID} unittest.AssertExistsAndLoadBean(t, ou) assert.Equal(t, isPublic, ou.IsPublic) - org = unittest.AssertExistsAndLoadBean(t, &User{ID: orgID}).(*User) + org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User) assert.EqualValues(t, expectedNumMembers, org.NumMembers) } @@ -465,20 +466,20 @@ func TestAddOrgUser(t *testing.T) { setting.Service.DefaultOrgMemberVisible = true testSuccess(6, 3, true) - unittest.CheckConsistencyFor(t, &User{}, &Team{}) + unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{}) } func TestRemoveOrgUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(orgID, userID int64) { - org := unittest.AssertExistsAndLoadBean(t, &User{ID: orgID}).(*User) + org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User) expectedNumMembers := org.NumMembers if unittest.BeanExists(t, &OrgUser{OrgID: orgID, UID: userID}) { expectedNumMembers-- } assert.NoError(t, RemoveOrgUser(orgID, userID)) unittest.AssertNotExistsBean(t, &OrgUser{OrgID: orgID, UID: userID}) - org = unittest.AssertExistsAndLoadBean(t, &User{ID: orgID}).(*User) + org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User) assert.EqualValues(t, expectedNumMembers, org.NumMembers) } testSuccess(3, 4) @@ -488,7 +489,7 @@ func TestRemoveOrgUser(t *testing.T) { assert.Error(t, err) assert.True(t, IsErrLastOrgOwner(err)) unittest.AssertExistsAndLoadBean(t, &OrgUser{OrgID: 7, UID: 5}) - unittest.CheckConsistencyFor(t, &User{}, &Team{}) + unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{}) } func TestUser_GetUserTeamIDs(t *testing.T) { @@ -572,8 +573,8 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { func TestHasOrgVisibleTypePublic(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) const newOrgName = "test-org-public" org := &Organization{ @@ -581,10 +582,10 @@ func TestHasOrgVisibleTypePublic(t *testing.T) { Visibility: structs.VisibleTypePublic, } - unittest.AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization}) assert.NoError(t, CreateOrganization(org, owner)) org = unittest.AssertExistsAndLoadBean(t, - &Organization{Name: org.Name, Type: UserTypeOrganization}).(*Organization) + &Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*Organization) test1 := HasOrgOrUserVisible(org.AsUser(), owner) test2 := HasOrgOrUserVisible(org.AsUser(), user3) test3 := HasOrgOrUserVisible(org.AsUser(), nil) @@ -595,8 +596,8 @@ func TestHasOrgVisibleTypePublic(t *testing.T) { func TestHasOrgVisibleTypeLimited(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) const newOrgName = "test-org-limited" org := &Organization{ @@ -604,10 +605,10 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) { Visibility: structs.VisibleTypeLimited, } - unittest.AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization}) assert.NoError(t, CreateOrganization(org, owner)) org = unittest.AssertExistsAndLoadBean(t, - &Organization{Name: org.Name, Type: UserTypeOrganization}).(*Organization) + &Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*Organization) test1 := HasOrgOrUserVisible(org.AsUser(), owner) test2 := HasOrgOrUserVisible(org.AsUser(), user3) test3 := HasOrgOrUserVisible(org.AsUser(), nil) @@ -618,8 +619,8 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) { func TestHasOrgVisibleTypePrivate(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) const newOrgName = "test-org-private" org := &Organization{ @@ -627,10 +628,10 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) { Visibility: structs.VisibleTypePrivate, } - unittest.AssertNotExistsBean(t, &User{Name: org.Name, Type: UserTypeOrganization}) + unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization}) assert.NoError(t, CreateOrganization(org, owner)) org = unittest.AssertExistsAndLoadBean(t, - &Organization{Name: org.Name, Type: UserTypeOrganization}).(*Organization) + &Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*Organization) test1 := HasOrgOrUserVisible(org.AsUser(), owner) test2 := HasOrgOrUserVisible(org.AsUser(), user3) test3 := HasOrgOrUserVisible(org.AsUser(), nil) diff --git a/models/project_issue.go b/models/project_issue.go index 6f314ca931..fb08efa994 100644 --- a/models/project_issue.go +++ b/models/project_issue.go @@ -9,6 +9,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" ) // ProjectIssue saves relation from issue to a project @@ -130,7 +131,7 @@ func (p *Project) NumOpenIssues() int { } // ChangeProjectAssign changes the project associated with an issue -func ChangeProjectAssign(issue *Issue, doer *User, newProjectID int64) error { +func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -144,7 +145,7 @@ func ChangeProjectAssign(issue *Issue, doer *User, newProjectID int64) error { return committer.Commit() } -func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *User, newProjectID int64) error { +func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { e := db.GetEngine(ctx) oldProjectID := issue.projectID(e) diff --git a/models/pull.go b/models/pull.go index 1e1dd13c57..85ca0b3fc8 100644 --- a/models/pull.go +++ b/models/pull.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -78,7 +79,7 @@ type PullRequest struct { HasMerged bool `xorm:"INDEX"` MergedCommitID string `xorm:"VARCHAR(40)"` MergerID int64 `xorm:"INDEX"` - Merger *User `xorm:"-"` + Merger *user_model.User `xorm:"-"` MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` isHeadRepoLoaded bool `xorm:"-"` @@ -109,10 +110,10 @@ func (pr *PullRequest) MustHeadUserName() string { // Note: don't try to get Issue because will end up recursive querying. func (pr *PullRequest) loadAttributes(e db.Engine) (err error) { if pr.HasMerged && pr.Merger == nil { - pr.Merger, err = getUserByID(e, pr.MergerID) - if IsErrUserNotExist(err) { + pr.Merger, err = user_model.GetUserByIDEngine(e, pr.MergerID) + if user_model.IsErrUserNotExist(err) { pr.MergerID = -1 - pr.Merger = NewGhostUser() + pr.Merger = user_model.NewGhostUser() } else if err != nil { return fmt.Errorf("getUserByID [%d]: %v", pr.MergerID, err) } @@ -310,7 +311,7 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error { break } - if err := review.loadReviewer(sess); err != nil && !IsErrUserNotExist(err) { + if err := review.loadReviewer(sess); err != nil && !user_model.IsErrUserNotExist(err) { log.Error("Unable to LoadReviewer[%d] for PR ID %d : %v", review.ReviewerID, pr.ID, err) return err } else if review.Reviewer == nil { diff --git a/models/pull_list.go b/models/pull_list.go index 57ef210213..7c5b83ae5f 100644 --- a/models/pull_list.go +++ b/models/pull_list.go @@ -8,6 +8,7 @@ import ( "fmt" "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/git" "code.gitea.io/gitea/modules/log" @@ -147,7 +148,7 @@ func (prs PullRequestList) LoadAttributes() error { return prs.loadAttributes(db.GetEngine(db.DefaultContext)) } -func (prs PullRequestList) invalidateCodeComments(e db.Engine, doer *User, repo *git.Repository, branch string) error { +func (prs PullRequestList) invalidateCodeComments(e db.Engine, doer *user_model.User, repo *git.Repository, branch string) error { if len(prs) == 0 { return nil } @@ -168,6 +169,6 @@ func (prs PullRequestList) invalidateCodeComments(e db.Engine, doer *User, repo } // InvalidateCodeComments will lookup the prs for code comments which got invalidated by change -func (prs PullRequestList) InvalidateCodeComments(doer *User, repo *git.Repository, branch string) error { +func (prs PullRequestList) InvalidateCodeComments(doer *user_model.User, repo *git.Repository, branch string) error { return prs.invalidateCodeComments(db.GetEngine(db.DefaultContext), doer, repo, branch) } diff --git a/models/pull_sign.go b/models/pull_sign.go index 028a3e5c3b..269e8e06d2 100644 --- a/models/pull_sign.go +++ b/models/pull_sign.go @@ -7,13 +7,14 @@ package models import ( "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/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) // SignMerge determines if we should sign a PR merge commit to the base repository -func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) { +func (pr *PullRequest) SignMerge(u *user_model.User, tmpBasePath, baseCommit, headCommit string) (bool, string, *git.Signature, error) { if err := pr.LoadBaseRepo(); err != nil { log.Error("Unable to get Base Repo for pull request") return false, "", nil, err diff --git a/models/pull_test.go b/models/pull_test.go index c967cca313..2281dbffda 100644 --- a/models/pull_test.go +++ b/models/pull_test.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -264,7 +265,7 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) { }, } baseRepo := &Repository{Name: "testRepo", ID: 1} - baseRepo.Owner = &User{Name: "testOwner"} + baseRepo.Owner = &user_model.User{Name: "testOwner"} baseRepo.Units = []*RepoUnit{&externalTracker} pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2, BaseRepo: baseRepo}).(*PullRequest) diff --git a/models/release.go b/models/release.go index 69d2ceda5e..f60024c710 100644 --- a/models/release.go +++ b/models/release.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -24,12 +25,12 @@ import ( // Release represents a release of repository. type Release struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"INDEX UNIQUE(n)"` - Repo *Repository `xorm:"-"` - PublisherID int64 `xorm:"INDEX"` - Publisher *User `xorm:"-"` - TagName string `xorm:"INDEX UNIQUE(n)"` + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(n)"` + Repo *Repository `xorm:"-"` + PublisherID int64 `xorm:"INDEX"` + Publisher *user_model.User `xorm:"-"` + TagName string `xorm:"INDEX UNIQUE(n)"` OriginalAuthor string OriginalAuthorID int64 `xorm:"index"` LowerTagName string @@ -60,10 +61,10 @@ func (r *Release) loadAttributes(e db.Engine) error { } } if r.Publisher == nil { - r.Publisher, err = getUserByID(e, r.PublisherID) + r.Publisher, err = user_model.GetUserByIDEngine(e, r.PublisherID) if err != nil { - if IsErrUserNotExist(err) { - r.Publisher = NewGhostUser() + if user_model.IsErrUserNotExist(err) { + r.Publisher = user_model.NewGhostUser() } else { return err } diff --git a/models/repo.go b/models/repo.go index 860c5c4813..bf5c160297 100644 --- a/models/repo.go +++ b/models/repo.go @@ -27,6 +27,7 @@ import ( "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -44,9 +45,6 @@ import ( var ( // ErrMirrorNotExist mirror does not exist error ErrMirrorNotExist = errors.New("Mirror does not exist") - - // ErrNameEmpty name is empty error - ErrNameEmpty = errors.New("Name is empty") ) var ( @@ -195,7 +193,7 @@ type Repository struct { ID int64 `xorm:"pk autoincr"` OwnerID int64 `xorm:"UNIQUE(s) index"` OwnerName string - Owner *User `xorm:"-"` + Owner *user_model.User `xorm:"-"` LowerName string `xorm:"UNIQUE(s) INDEX NOT NULL"` Name string `xorm:"INDEX NOT NULL"` Description string `xorm:"TEXT"` @@ -308,11 +306,11 @@ func (repo *Repository) AfterLoad() { repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects } -// MustOwner always returns a valid *User object to avoid +// MustOwner always returns a valid *user_model.User object to avoid // conceptually impossible error handling. // It creates a fake object that contains error details // when error occurs. -func (repo *Repository) MustOwner() *User { +func (repo *Repository) MustOwner() *user_model.User { return repo.mustOwner(db.GetEngine(db.DefaultContext)) } @@ -364,11 +362,11 @@ func (repo *Repository) getUnits(e db.Engine) (err error) { } // CheckUnitUser check whether user could visit the unit of this repository -func (repo *Repository) CheckUnitUser(user *User, unitType unit.Type) bool { +func (repo *Repository) CheckUnitUser(user *user_model.User, unitType unit.Type) bool { return repo.checkUnitUser(db.GetEngine(db.DefaultContext), user, unitType) } -func (repo *Repository) checkUnitUser(e db.Engine, user *User, unitType unit.Type) bool { +func (repo *Repository) checkUnitUser(e db.Engine, user *user_model.User, unitType unit.Type) bool { if user.IsAdmin { return true } @@ -465,7 +463,7 @@ func (repo *Repository) getOwner(e db.Engine) (err error) { return nil } - repo.Owner, err = getUserByID(e, repo.OwnerID) + repo.Owner, err = user_model.GetUserByIDEngine(e, repo.OwnerID) return err } @@ -474,9 +472,9 @@ func (repo *Repository) GetOwner() error { return repo.getOwner(db.GetEngine(db.DefaultContext)) } -func (repo *Repository) mustOwner(e db.Engine) *User { +func (repo *Repository) mustOwner(e db.Engine) *user_model.User { if err := repo.getOwner(e); err != nil { - return &User{ + return &user_model.User{ Name: "error", FullName: err.Error(), } @@ -537,7 +535,7 @@ func (repo *Repository) ComposeDocumentMetas() map[string]string { return repo.DocumentRenderingMetas } -func (repo *Repository) getAssignees(e db.Engine) (_ []*User, err error) { +func (repo *Repository) getAssignees(e db.Engine) (_ []*user_model.User, err error) { if err = repo.getOwner(e); err != nil { return nil, err } @@ -551,7 +549,7 @@ func (repo *Repository) getAssignees(e db.Engine) (_ []*User, err error) { // Leave a seat for owner itself to append later, but if owner is an organization // and just waste 1 unit is cheaper than re-allocate memory once. - users := make([]*User, 0, len(accesses)+1) + users := make([]*user_model.User, 0, len(accesses)+1) if len(accesses) > 0 { userIDs := make([]int64, len(accesses)) for i := 0; i < len(accesses); i++ { @@ -571,17 +569,17 @@ func (repo *Repository) getAssignees(e db.Engine) (_ []*User, err error) { // GetAssignees returns all users that have write access and can be assigned to issues // of the repository, -func (repo *Repository) GetAssignees() (_ []*User, err error) { +func (repo *Repository) GetAssignees() (_ []*user_model.User, err error) { return repo.getAssignees(db.GetEngine(db.DefaultContext)) } -func (repo *Repository) getReviewers(e db.Engine, doerID, posterID int64) ([]*User, error) { +func (repo *Repository) getReviewers(e db.Engine, doerID, posterID int64) ([]*user_model.User, error) { // Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries if err := repo.getOwner(e); err != nil { return nil, err } - var users []*User + var users []*user_model.User if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate { // This a private repository: @@ -623,7 +621,7 @@ func (repo *Repository) getReviewers(e db.Engine, doerID, posterID int64) ([]*Us // * for public repositories this returns all users that have read access or higher to the repository, // all repo watchers and all organization members. // TODO: may be we should have a busy choice for users to block review request to them. -func (repo *Repository) GetReviewers(doerID, posterID int64) ([]*User, error) { +func (repo *Repository) GetReviewers(doerID, posterID int64) ([]*user_model.User, error) { return repo.getReviewers(db.GetEngine(db.DefaultContext), doerID, posterID) } @@ -761,7 +759,7 @@ func (repo *Repository) UpdateSize(ctx context.Context) error { } // CanUserForkRepo returns true if specified user can fork repository. -func CanUserForkRepo(user *User, repo *Repository) (bool, error) { +func CanUserForkRepo(user *user_model.User, repo *Repository) (bool, error) { if user == nil { return false, nil } @@ -781,7 +779,7 @@ func CanUserForkRepo(user *User, repo *Repository) (bool, error) { } // CanUserDelete returns true if user could delete the repository -func (repo *Repository) CanUserDelete(user *User) (bool, error) { +func (repo *Repository) CanUserDelete(user *user_model.User) (bool, error) { if user.IsAdmin || user.ID == repo.OwnerID { return true, nil } @@ -818,12 +816,12 @@ func (repo *Repository) CanEnableEditor() bool { } // GetReaders returns all users that have explicit read access or higher to the repository. -func (repo *Repository) GetReaders() (_ []*User, err error) { +func (repo *Repository) GetReaders() (_ []*user_model.User, err error) { return repo.getUsersWithAccessMode(db.GetEngine(db.DefaultContext), AccessModeRead) } // GetWriters returns all users that have write access to the repository. -func (repo *Repository) GetWriters() (_ []*User, err error) { +func (repo *Repository) GetWriters() (_ []*user_model.User, err error) { return repo.getUsersWithAccessMode(db.GetEngine(db.DefaultContext), AccessModeWrite) } @@ -836,7 +834,7 @@ func (repo *Repository) IsReader(userID int64) (bool, error) { } // getUsersWithAccessMode returns users that have at least given access mode to the repository. -func (repo *Repository) getUsersWithAccessMode(e db.Engine, mode AccessMode) (_ []*User, err error) { +func (repo *Repository) getUsersWithAccessMode(e db.Engine, mode AccessMode) (_ []*user_model.User, err error) { if err = repo.getOwner(e); err != nil { return nil, err } @@ -848,7 +846,7 @@ func (repo *Repository) getUsersWithAccessMode(e db.Engine, mode AccessMode) (_ // Leave a seat for owner itself to append later, but if owner is an organization // and just waste 1 unit is cheaper than re-allocate memory once. - users := make([]*User, 0, len(accesses)+1) + users := make([]*user_model.User, 0, len(accesses)+1) if len(accesses) > 0 { userIDs := make([]int64, len(accesses)) for i := 0; i < len(accesses); i++ { @@ -884,7 +882,7 @@ func (repo *Repository) ReadBy(userID int64) error { return setRepoNotificationStatusReadIfUnread(db.GetEngine(db.DefaultContext), userID, repo.ID) } -func isRepositoryExist(e db.Engine, u *User, repoName string) (bool, error) { +func isRepositoryExist(e db.Engine, u *user_model.User, repoName string) (bool, error) { has, err := e.Get(&Repository{ OwnerID: u.ID, LowerName: strings.ToLower(repoName), @@ -897,7 +895,7 @@ func isRepositoryExist(e db.Engine, u *User, repoName string) (bool, error) { } // IsRepositoryExist returns true if the repository with given name under user has already existed. -func IsRepositoryExist(u *User, repoName string) (bool, error) { +func IsRepositoryExist(u *user_model.User, repoName string) (bool, error) { return isRepositoryExist(db.GetEngine(db.DefaultContext), u, repoName) } @@ -951,7 +949,7 @@ func (repo *Repository) CloneLink() (cl *CloneLink) { } // CheckCreateRepository check if could created a repository -func CheckCreateRepository(doer, u *User, name string, overwriteOrAdopt bool) error { +func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error { if !doer.CanCreateRepo() { return ErrReachLimitOfRepo{u.MaxRepoCreation} } @@ -1041,15 +1039,15 @@ var ( // IsUsableRepoName returns true when repository is usable func IsUsableRepoName(name string) error { - if alphaDashDotPattern.MatchString(name) { + if db.AlphaDashDotPattern.MatchString(name) { // Note: usually this error is normally caught up earlier in the UI - return ErrNameCharsNotAllowed{Name: name} + return db.ErrNameCharsNotAllowed{Name: name} } - return isUsableName(reservedRepoNames, reservedRepoPatterns, name) + return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name) } // CreateRepository creates a repository for the user/organization. -func CreateRepository(ctx context.Context, doer, u *User, repo *Repository, overwriteOrAdopt bool) (err error) { +func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *Repository, overwriteOrAdopt bool) (err error) { if err = IsUsableRepoName(repo.Name); err != nil { return err } @@ -1115,11 +1113,11 @@ func CreateRepository(ctx context.Context, doer, u *User, repo *Repository, over // Remember visibility preference. u.LastRepoVisibility = repo.IsPrivate - if err = updateUserCols(db.GetEngine(ctx), u, "last_repo_visibility"); err != nil { + if err = user_model.UpdateUserColsEngine(db.GetEngine(ctx), u, "last_repo_visibility"); err != nil { return fmt.Errorf("updateUser: %v", err) } - if _, err = db.GetEngine(ctx).Incr("num_repos").ID(u.ID).Update(new(User)); err != nil { + if _, err = db.GetEngine(ctx).Incr("num_repos").ID(u.ID).Update(new(user_model.User)); err != nil { return fmt.Errorf("increment user total_repos: %v", err) } u.NumRepos++ @@ -1232,7 +1230,7 @@ func CountUserRepositories(userID int64, private bool) int64 { // RepoPath returns repository path by given user and repository name. func RepoPath(userName, repoName string) string { - return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".git") + return filepath.Join(user_model.UserPath(userName), strings.ToLower(repoName)+".git") } // IncrementRepoForkNum increment repository fork number @@ -1248,7 +1246,7 @@ func DecrementRepoForkNum(ctx context.Context, repoID int64) error { } // ChangeRepositoryName changes all corresponding setting from old repository name to new one. -func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err error) { +func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName string) (err error) { oldRepoName := repo.Name newRepoName = strings.ToLower(newRepoName) if err = IsUsableRepoName(newRepoName); err != nil { @@ -1441,7 +1439,7 @@ func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes [ // DeleteRepository deletes a repository for a user or organization. // make sure if you call this func to close open sessions (sqlite will otherwise get a deadlock) -func DeleteRepository(doer *User, uid, repoID int64) error { +func DeleteRepository(doer *user_model.User, uid, repoID int64) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -1450,7 +1448,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error { sess := db.GetEngine(ctx) // In case is a organization. - org, err := getUserByID(sess, uid) + org, err := user_model.GetUserByIDEngine(sess, uid) if err != nil { return err } @@ -1802,11 +1800,11 @@ func getRepositoryCount(e db.Engine, ownerID int64) (int64, error) { return e.Count(&Repository{OwnerID: ownerID}) } -func getPublicRepositoryCount(e db.Engine, u *User) (int64, error) { +func getPublicRepositoryCount(e db.Engine, u *user_model.User) (int64, error) { return e.Where("is_private = ?", false).Count(&Repository{OwnerID: u.ID}) } -func getPrivateRepositoryCount(e db.Engine, u *User) (int64, error) { +func getPrivateRepositoryCount(e db.Engine, u *user_model.User) (int64, error) { return e.Where("is_private = ?", true).Count(&Repository{OwnerID: u.ID}) } @@ -1816,12 +1814,12 @@ func GetRepositoryCount(ctx context.Context, ownerID int64) (int64, error) { } // GetPublicRepositoryCount returns the total number of public repositories of user. -func GetPublicRepositoryCount(u *User) (int64, error) { +func GetPublicRepositoryCount(u *user_model.User) (int64, error) { return getPublicRepositoryCount(db.GetEngine(db.DefaultContext), u) } // GetPrivateRepositoryCount returns the total number of private repositories of user. -func GetPrivateRepositoryCount(u *User) (int64, error) { +func GetPrivateRepositoryCount(u *user_model.User) (int64, error) { return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u) } @@ -2155,7 +2153,7 @@ func (repo *Repository) GetTrustModel() TrustModelType { return trustModel } -func updateUserStarNumbers(users []User) error { +func updateUserStarNumbers(users []user_model.User) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -2176,7 +2174,7 @@ func DoctorUserStarNum() (err error) { const batchSize = 100 for start := 0; ; start += batchSize { - users := make([]User, 0, batchSize) + users := make([]user_model.User, 0, batchSize) if err = db.GetEngine(db.DefaultContext).Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil { return } diff --git a/models/repo_activity.go b/models/repo_activity.go index 5986da7e77..d3d4c7bbd7 100644 --- a/models/repo_activity.go +++ b/models/repo_activity.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "xorm.io/xorm" @@ -94,13 +95,13 @@ func GetActivityStatsTopAuthors(repo *Repository, timeFrom time.Time, count int) } users := make(map[int64]*ActivityAuthorData) var unknownUserID int64 - unknownUserAvatarLink := NewGhostUser().AvatarLink() + unknownUserAvatarLink := user_model.NewGhostUser().AvatarLink() for _, v := range code.Authors { if len(v.Email) == 0 { continue } - u, err := GetUserByEmail(v.Email) - if u == nil || IsErrUserNotExist(err) { + u, err := user_model.GetUserByEmail(v.Email) + if u == nil || user_model.IsErrUserNotExist(err) { unknownUserID-- users[unknownUserID] = &ActivityAuthorData{ Name: v.Name, diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go index 6502d7959c..11d1a0e1d0 100644 --- a/models/repo_collaboration.go +++ b/models/repo_collaboration.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -30,7 +31,7 @@ func init() { db.RegisterModel(new(Collaboration)) } -func (repo *Repository) addCollaborator(e db.Engine, u *User) error { +func (repo *Repository) addCollaborator(e db.Engine, u *user_model.User) error { collaboration := &Collaboration{ RepoID: repo.ID, UserID: u.ID, @@ -52,7 +53,7 @@ func (repo *Repository) addCollaborator(e db.Engine, u *User) error { } // AddCollaborator adds new collaboration to a repository with default access mode. -func (repo *Repository) AddCollaborator(u *User) error { +func (repo *Repository) AddCollaborator(u *user_model.User) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -80,7 +81,7 @@ func (repo *Repository) getCollaborations(e db.Engine, listOptions db.ListOption // Collaborator represents a user with collaboration details. type Collaborator struct { - *User + *user_model.User Collaboration *Collaboration } @@ -92,11 +93,11 @@ func (repo *Repository) getCollaborators(e db.Engine, listOptions db.ListOptions collaborators := make([]*Collaborator, 0, len(collaborations)) for _, c := range collaborations { - user, err := getUserByID(e, c.UserID) + user, err := user_model.GetUserByIDEngine(e, c.UserID) if err != nil { - if IsErrUserNotExist(err) { + if user_model.IsErrUserNotExist(err) { log.Warn("Inconsistent DB: User: %d is listed as collaborator of %-v but does not exist", c.UserID, repo) - user = NewGhostUser() + user = user_model.NewGhostUser() } else { return nil, err } @@ -227,7 +228,7 @@ func (repo *Repository) DeleteCollaboration(uid int64) (err error) { } func (repo *Repository) reconsiderIssueAssignees(e db.Engine, uid int64) error { - user, err := getUserByID(e, uid) + user, err := user_model.GetUserByIDEngine(e, uid) if err != nil { return err } diff --git a/models/repo_collaboration_test.go b/models/repo_collaboration_test.go index 060afc2139..94dd238cb6 100644 --- a/models/repo_collaboration_test.go +++ b/models/repo_collaboration_test.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -19,9 +20,9 @@ func TestRepository_AddCollaborator(t *testing.T) { testSuccess := func(repoID, userID int64) { repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) assert.NoError(t, repo.GetOwner()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: userID}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User) assert.NoError(t, repo.AddCollaborator(user)) - unittest.CheckConsistencyFor(t, &Repository{ID: repoID}, &User{ID: userID}) + unittest.CheckConsistencyFor(t, &Repository{ID: repoID}, &user_model.User{ID: userID}) } testSuccess(1, 4) testSuccess(1, 4) diff --git a/models/repo_list.go b/models/repo_list.go index 511b446711..ff86f61577 100644 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -55,7 +56,7 @@ func (repos RepositoryList) loadAttributes(e db.Engine) error { } // Load owners. - users := make(map[int64]*User, len(set)) + users := make(map[int64]*user_model.User, len(set)) if err := e. Where("id > 0"). In("id", keysInt64(set)). @@ -135,12 +136,12 @@ func (repos MirrorRepositoryList) LoadAttributes() error { // SearchRepoOptions holds the search options type SearchRepoOptions struct { db.ListOptions - Actor *User + Actor *user_model.User Keyword string OwnerID int64 PriorityOwnerID int64 TeamID int64 - OrderBy SearchOrderBy + OrderBy db.SearchOrderBy Private bool // Include private repositories in results StarredByID int64 WatchedByID int64 @@ -182,31 +183,6 @@ type SearchRepoOptions struct { LowerNames []string } -// SearchOrderBy is used to sort the result -type SearchOrderBy string - -func (s SearchOrderBy) String() string { - return string(s) -} - -// Strings for sorting result -const ( - SearchOrderByAlphabetically SearchOrderBy = "name ASC" - SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC" - SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC" - SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC" - SearchOrderByOldest SearchOrderBy = "created_unix ASC" - SearchOrderByNewest SearchOrderBy = "created_unix DESC" - SearchOrderBySize SearchOrderBy = "size ASC" - SearchOrderBySizeReverse SearchOrderBy = "size DESC" - SearchOrderByID SearchOrderBy = "id ASC" - SearchOrderByIDReverse SearchOrderBy = "id DESC" - SearchOrderByStars SearchOrderBy = "num_stars ASC" - SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC" - SearchOrderByForks SearchOrderBy = "num_forks ASC" - SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" -) - // SearchRepositoryCondition creates a query condition according search repository options func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { cond := builder.NewCond() @@ -278,7 +254,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { Join("INNER", "`user`", "`user`.id = `org_user`.org_id"). Where(builder.Eq{ "`org_user`.uid": opts.OwnerID, - "`user`.type": UserTypeOrganization, + "`user`.type": user_model.UserTypeOrganization, "`user`.visibility": structs.VisibleTypePrivate, })))), ) @@ -401,11 +377,11 @@ func searchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (db } if len(opts.OrderBy) == 0 { - opts.OrderBy = SearchOrderByAlphabetically + opts.OrderBy = db.SearchOrderByAlphabetically } if opts.PriorityOwnerID > 0 { - opts.OrderBy = SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = %d THEN 0 ELSE owner_id END, %s", opts.PriorityOwnerID, opts.OrderBy)) + opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = %d THEN 0 ELSE owner_id END, %s", opts.PriorityOwnerID, opts.OrderBy)) } sess := db.GetEngine(db.DefaultContext) @@ -429,7 +405,7 @@ func searchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond) (db } // accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible -func accessibleRepositoryCondition(user *User) builder.Cond { +func accessibleRepositoryCondition(user *user_model.User) builder.Cond { cond := builder.NewCond() if user == nil || !user.IsRestricted || user.ID <= 0 { @@ -443,7 +419,7 @@ func accessibleRepositoryCondition(user *User) builder.Cond { // 2. Aren't in an private organisation or limited organisation if we're not logged in builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where( builder.And( - builder.Eq{"type": UserTypeOrganization}, + builder.Eq{"type": user_model.UserTypeOrganization}, builder.In("visibility", orgVisibilityLimit)), )))) } @@ -508,13 +484,13 @@ func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) { } // AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered. -func AccessibleRepoIDsQuery(user *User) *builder.Builder { +func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder { // NB: Please note this code needs to still work if user is nil return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(user)) } // FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id -func FindUserAccessibleRepoIDs(user *User) ([]int64, error) { +func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) { repoIDs := make([]int64, 0, 10) if err := db.GetEngine(db.DefaultContext). Table("repository"). diff --git a/models/repo_permission.go b/models/repo_permission.go index a6304cf1aa..96abb9037a 100644 --- a/models/repo_permission.go +++ b/models/repo_permission.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" ) @@ -140,11 +141,11 @@ func (p *Permission) ColorFormat(s fmt.State) { } // GetUserRepoPermission returns the user permissions to the repository -func GetUserRepoPermission(repo *Repository, user *User) (Permission, error) { +func GetUserRepoPermission(repo *Repository, user *user_model.User) (Permission, error) { return getUserRepoPermission(db.GetEngine(db.DefaultContext), repo, user) } -func getUserRepoPermission(e db.Engine, repo *Repository, user *User) (perm Permission, err error) { +func getUserRepoPermission(e db.Engine, repo *Repository, user *user_model.User) (perm Permission, err error) { if log.IsTrace() { defer func() { if user == nil { @@ -274,7 +275,7 @@ func getUserRepoPermission(e db.Engine, repo *Repository, user *User) (perm Perm } // IsUserRealRepoAdmin check if this user is real repo admin -func IsUserRealRepoAdmin(repo *Repository, user *User) (bool, error) { +func IsUserRealRepoAdmin(repo *Repository, user *user_model.User) (bool, error) { if repo.OwnerID == user.ID { return true, nil } @@ -294,11 +295,11 @@ func IsUserRealRepoAdmin(repo *Repository, user *User) (bool, error) { } // IsUserRepoAdmin return true if user has admin right of a repo -func IsUserRepoAdmin(repo *Repository, user *User) (bool, error) { +func IsUserRepoAdmin(repo *Repository, user *user_model.User) (bool, error) { return isUserRepoAdmin(db.GetEngine(db.DefaultContext), repo, user) } -func isUserRepoAdmin(e db.Engine, repo *Repository, user *User) (bool, error) { +func isUserRepoAdmin(e db.Engine, repo *Repository, user *user_model.User) (bool, error) { if user == nil || repo == nil { return false, nil } @@ -329,17 +330,17 @@ func isUserRepoAdmin(e db.Engine, repo *Repository, user *User) (bool, error) { // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the // user does not have access. -func AccessLevel(user *User, repo *Repository) (AccessMode, error) { +func AccessLevel(user *user_model.User, repo *Repository) (AccessMode, error) { return accessLevelUnit(db.GetEngine(db.DefaultContext), user, repo, unit.TypeCode) } // AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the // user does not have access. -func AccessLevelUnit(user *User, repo *Repository, unitType unit.Type) (AccessMode, error) { +func AccessLevelUnit(user *user_model.User, repo *Repository, unitType unit.Type) (AccessMode, error) { return accessLevelUnit(db.GetEngine(db.DefaultContext), user, repo, unitType) } -func accessLevelUnit(e db.Engine, user *User, repo *Repository, unitType unit.Type) (AccessMode, error) { +func accessLevelUnit(e db.Engine, user *user_model.User, repo *Repository, unitType unit.Type) (AccessMode, error) { perm, err := getUserRepoPermission(e, repo, user) if err != nil { return AccessModeNone, err @@ -347,24 +348,24 @@ func accessLevelUnit(e db.Engine, user *User, repo *Repository, unitType unit.Ty return perm.UnitAccessMode(unitType), nil } -func hasAccessUnit(e db.Engine, user *User, repo *Repository, unitType unit.Type, testMode AccessMode) (bool, error) { +func hasAccessUnit(e db.Engine, user *user_model.User, repo *Repository, unitType unit.Type, testMode AccessMode) (bool, error) { mode, err := accessLevelUnit(e, user, repo, unitType) return testMode <= mode, err } // HasAccessUnit returns true if user has testMode to the unit of the repository -func HasAccessUnit(user *User, repo *Repository, unitType unit.Type, testMode AccessMode) (bool, error) { +func HasAccessUnit(user *user_model.User, repo *Repository, unitType unit.Type, testMode AccessMode) (bool, error) { return hasAccessUnit(db.GetEngine(db.DefaultContext), user, repo, unitType, testMode) } // CanBeAssigned return true if user can be assigned to issue or pull requests in repo // Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface. // FIXME: user could send PullRequest also could be assigned??? -func CanBeAssigned(user *User, repo *Repository, isPull bool) (bool, error) { +func CanBeAssigned(user *user_model.User, repo *Repository, isPull bool) (bool, error) { return canBeAssigned(db.GetEngine(db.DefaultContext), user, repo, isPull) } -func canBeAssigned(e db.Engine, user *User, repo *Repository, _ bool) (bool, error) { +func canBeAssigned(e db.Engine, user *user_model.User, repo *Repository, _ bool) (bool, error) { if user.IsOrganization() { return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID) } @@ -376,10 +377,10 @@ func canBeAssigned(e db.Engine, user *User, repo *Repository, _ bool) (bool, err } func hasAccess(e db.Engine, userID int64, repo *Repository) (bool, error) { - var user *User + var user *user_model.User var err error if userID > 0 { - user, err = getUserByID(e, userID) + user, err = user_model.GetUserByIDEngine(e, userID) if err != nil { return false, err } @@ -397,7 +398,7 @@ func HasAccess(userID int64, repo *Repository) (bool, error) { } // FilterOutRepoIdsWithoutUnitAccess filter out repos where user has no access to repositories -func FilterOutRepoIdsWithoutUnitAccess(u *User, repoIDs []int64, units ...unit.Type) ([]int64, error) { +func FilterOutRepoIdsWithoutUnitAccess(u *user_model.User, repoIDs []int64, units ...unit.Type) ([]int64, error) { i := 0 for _, rID := range repoIDs { repo, err := GetRepositoryByID(rID) diff --git a/models/repo_permission_test.go b/models/repo_permission_test.go index 180a85a2f5..ce046eba7a 100644 --- a/models/repo_permission_test.go +++ b/models/repo_permission_test.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -22,7 +23,7 @@ func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { assert.NoError(t, repo.getUnits(db.GetEngine(db.DefaultContext))) // plain user - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) perm, err := GetUserRepoPermission(repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -40,7 +41,7 @@ func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { } // collaborator - collaborator := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) perm, err = GetUserRepoPermission(repo, collaborator) assert.NoError(t, err) for _, unit := range repo.Units { @@ -49,7 +50,7 @@ func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { } // owner - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) perm, err = GetUserRepoPermission(repo, owner) assert.NoError(t, err) for _, unit := range repo.Units { @@ -58,7 +59,7 @@ func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { } // admin - admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) perm, err = GetUserRepoPermission(repo, admin) assert.NoError(t, err) for _, unit := range repo.Units { @@ -75,7 +76,7 @@ func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { assert.NoError(t, repo.getUnits(db.GetEngine(db.DefaultContext))) // plain user - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) perm, err := GetUserRepoPermission(repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -101,7 +102,7 @@ func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { } // owner - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) perm, err = GetUserRepoPermission(repo, owner) assert.NoError(t, err) for _, unit := range repo.Units { @@ -110,7 +111,7 @@ func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { } // admin - admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) perm, err = GetUserRepoPermission(repo, admin) assert.NoError(t, err) for _, unit := range repo.Units { @@ -127,7 +128,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { assert.NoError(t, repo.getUnits(db.GetEngine(db.DefaultContext))) // plain user - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) perm, err := GetUserRepoPermission(repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -153,7 +154,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { } // org member team owner - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) perm, err = GetUserRepoPermission(repo, owner) assert.NoError(t, err) for _, unit := range repo.Units { @@ -162,7 +163,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { } // org member team tester - member := unittest.AssertExistsAndLoadBean(t, &User{ID: 15}).(*User) + member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}).(*user_model.User) perm, err = GetUserRepoPermission(repo, member) assert.NoError(t, err) for _, unit := range repo.Units { @@ -172,7 +173,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { assert.False(t, perm.CanWrite(unit.TypeCode)) // admin - admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) perm, err = GetUserRepoPermission(repo, admin) assert.NoError(t, err) for _, unit := range repo.Units { @@ -189,7 +190,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { assert.NoError(t, repo.getUnits(db.GetEngine(db.DefaultContext))) // plain user - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) perm, err := GetUserRepoPermission(repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -215,7 +216,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { } // org member team owner - owner := unittest.AssertExistsAndLoadBean(t, &User{ID: 15}).(*User) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}).(*user_model.User) perm, err = GetUserRepoPermission(repo, owner) assert.NoError(t, err) for _, unit := range repo.Units { @@ -235,7 +236,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { } // org member team tester - tester := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) perm, err = GetUserRepoPermission(repo, tester) assert.NoError(t, err) assert.True(t, perm.CanWrite(unit.TypeIssues)) @@ -243,7 +244,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { assert.False(t, perm.CanRead(unit.TypeCode)) // org member team reviewer - reviewer := unittest.AssertExistsAndLoadBean(t, &User{ID: 20}).(*User) + reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}).(*user_model.User) perm, err = GetUserRepoPermission(repo, reviewer) assert.NoError(t, err) assert.False(t, perm.CanRead(unit.TypeIssues)) @@ -251,7 +252,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { assert.True(t, perm.CanRead(unit.TypeCode)) // admin - admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) perm, err = GetUserRepoPermission(repo, admin) assert.NoError(t, err) for _, unit := range repo.Units { diff --git a/models/repo_sign.go b/models/repo_sign.go index f7a303b0c1..f30ba4748c 100644 --- a/models/repo_sign.go +++ b/models/repo_sign.go @@ -9,6 +9,7 @@ import ( "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/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" @@ -107,7 +108,7 @@ func PublicSigningKey(repoPath string) (string, error) { } // SignInitialCommit determines if we should sign the initial commit to this repository -func SignInitialCommit(repoPath string, u *User) (bool, string, *git.Signature, error) { +func SignInitialCommit(repoPath string, u *user_model.User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit) signingKey, sig := SigningKey(repoPath) if signingKey == "" { @@ -143,7 +144,7 @@ Loop: } // SignWikiCommit determines if we should sign the commits to this repository wiki -func (repo *Repository) SignWikiCommit(u *User) (bool, string, *git.Signature, error) { +func (repo *Repository) SignWikiCommit(u *user_model.User) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.Wiki) signingKey, sig := SigningKey(repo.WikiPath()) if signingKey == "" { @@ -196,7 +197,7 @@ Loop: } // SignCRUDAction determines if we should sign a CRUD commit to this repository -func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { +func (repo *Repository) SignCRUDAction(u *user_model.User, tmpBasePath, parentCommit string) (bool, string, *git.Signature, error) { rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) signingKey, sig := SigningKey(repo.RepoPath()) if signingKey == "" { diff --git a/models/repo_test.go b/models/repo_test.go index 5cc396eb76..e6aaedaae6 100644 --- a/models/repo_test.go +++ b/models/repo_test.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/markup" "github.com/stretchr/testify/assert" @@ -25,7 +26,7 @@ func TestMetas(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := &Repository{Name: "testRepo"} - repo.Owner = &User{Name: "testOwner"} + repo.Owner = &user_model.User{Name: "testOwner"} repo.OwnerName = repo.Owner.Name repo.Units = nil @@ -73,8 +74,8 @@ func TestGetRepositoryCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) count, err1 := GetRepositoryCount(db.DefaultContext, 10) - privateCount, err2 := GetPrivateRepositoryCount(&User{ID: int64(10)}) - publicCount, err3 := GetPublicRepositoryCount(&User{ID: int64(10)}) + privateCount, err2 := GetPrivateRepositoryCount(&user_model.User{ID: int64(10)}) + publicCount, err3 := GetPublicRepositoryCount(&user_model.User{ID: int64(10)}) assert.NoError(t, err1) assert.NoError(t, err2) assert.NoError(t, err3) @@ -85,7 +86,7 @@ func TestGetRepositoryCount(t *testing.T) { func TestGetPublicRepositoryCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - count, err := GetPublicRepositoryCount(&User{ID: int64(10)}) + count, err := GetPublicRepositoryCount(&user_model.User{ID: int64(10)}) assert.NoError(t, err) assert.Equal(t, int64(1), count) } @@ -93,7 +94,7 @@ func TestGetPublicRepositoryCount(t *testing.T) { func TestGetPrivateRepositoryCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - count, err := GetPrivateRepositoryCount(&User{ID: int64(10)}) + count, err := GetPrivateRepositoryCount(&user_model.User{ID: int64(10)}) assert.NoError(t, err) assert.Equal(t, int64(2), count) } diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 3b5ac21b09..9918a10d76 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -9,6 +9,7 @@ import ( "os" "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/timeutil" "code.gitea.io/gitea/modules/util" @@ -18,9 +19,9 @@ import ( type RepoTransfer struct { ID int64 `xorm:"pk autoincr"` DoerID int64 - Doer *User `xorm:"-"` + Doer *user_model.User `xorm:"-"` RecipientID int64 - Recipient *User `xorm:"-"` + Recipient *user_model.User `xorm:"-"` RepoID int64 TeamIDs []int64 Teams []*Team `xorm:"-"` @@ -36,7 +37,7 @@ func init() { // LoadAttributes fetches the transfer recipient from the database func (r *RepoTransfer) LoadAttributes() error { if r.Recipient == nil { - u, err := GetUserByID(r.RecipientID) + u, err := user_model.GetUserByID(r.RecipientID) if err != nil { return err } @@ -60,7 +61,7 @@ func (r *RepoTransfer) LoadAttributes() error { } if r.Doer == nil { - u, err := GetUserByID(r.DoerID) + u, err := user_model.GetUserByID(r.DoerID) if err != nil { return err } @@ -74,7 +75,7 @@ func (r *RepoTransfer) LoadAttributes() error { // CanUserAcceptTransfer checks if the user has the rights to accept/decline a repo transfer. // For user, it checks if it's himself // For organizations, it checks if the user is able to create repos -func (r *RepoTransfer) CanUserAcceptTransfer(u *User) bool { +func (r *RepoTransfer) CanUserAcceptTransfer(u *user_model.User) bool { if err := r.LoadAttributes(); err != nil { log.Error("LoadAttributes: %v", err) return false @@ -150,7 +151,7 @@ func TestRepositoryReadyForTransfer(status RepositoryStatus) error { // CreatePendingRepositoryTransfer transfer a repo from one owner to a new one. // it marks the repository transfer as "pending" -func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams []*Team) error { +func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int64, teams []*Team) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -201,7 +202,7 @@ func CreatePendingRepositoryTransfer(doer, newOwner *User, repoID int64, teams [ } // TransferOwnership transfers all corresponding repository items from old user to new one. -func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err error) { +func TransferOwnership(doer *user_model.User, newOwnerName string, repo *Repository) (err error) { repoRenamed := false wikiRenamed := false oldOwnerName := doer.Name @@ -242,7 +243,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e sess := db.GetEngine(ctx) - newOwner, err := getUserByName(sess, newOwnerName) + newOwner, err := user_model.GetUserByNameCtx(ctx, newOwnerName) if err != nil { return fmt.Errorf("get new owner '%s': %v", newOwnerName, err) } @@ -371,7 +372,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e } // Rename remote repository to new path and delete local copy. - dir := UserPath(newOwner.Name) + dir := user_model.UserPath(newOwner.Name) if err := os.MkdirAll(dir, os.ModePerm); err != nil { return fmt.Errorf("Failed to create dir %s: %v", dir, err) diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 368fea598d..1bde5628c1 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -15,7 +16,7 @@ import ( func TestRepositoryTransfer(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - doer := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) transfer, err := GetPendingRepositoryTransfer(repo) @@ -30,7 +31,7 @@ func TestRepositoryTransfer(t *testing.T) { assert.Nil(t, transfer) assert.True(t, IsErrNoPendingTransfer(err)) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) assert.NoError(t, CreatePendingRepositoryTransfer(doer, user2, repo.ID, nil)) @@ -39,7 +40,7 @@ func TestRepositoryTransfer(t *testing.T) { assert.NoError(t, transfer.LoadAttributes()) assert.Equal(t, "user2", transfer.Recipient.Name) - user6 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // Only transfer can be started at any given time err = CreatePendingRepositoryTransfer(doer, user6, repo.ID, nil) @@ -47,7 +48,7 @@ func TestRepositoryTransfer(t *testing.T) { assert.True(t, IsErrRepoTransferInProgress(err)) // Unknown user - err = CreatePendingRepositoryTransfer(doer, &User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) + err = CreatePendingRepositoryTransfer(doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) assert.Error(t, err) // Cancel transfer diff --git a/models/repo_watch.go b/models/repo_watch.go index fb1b1086aa..e225c945e9 100644 --- a/models/repo_watch.go +++ b/models/repo_watch.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" ) @@ -166,18 +167,18 @@ func getRepoWatchersIDs(e db.Engine, repoID int64) ([]int64, error) { } // GetWatchers returns range of users watching given repository. -func (repo *Repository) GetWatchers(opts db.ListOptions) ([]*User, error) { +func (repo *Repository) GetWatchers(opts db.ListOptions) ([]*user_model.User, error) { sess := db.GetEngine(db.DefaultContext).Where("watch.repo_id=?", repo.ID). Join("LEFT", "watch", "`user`.id=`watch`.user_id"). And("`watch`.mode<>?", RepoWatchModeDont) if opts.Page > 0 { sess = db.SetSessionPagination(sess, &opts) - users := make([]*User, 0, opts.PageSize) + users := make([]*user_model.User, 0, opts.PageSize) return users, sess.Find(&users) } - users := make([]*User, 0, 8) + users := make([]*user_model.User, 0, 8) return users, sess.Find(&users) } @@ -232,7 +233,7 @@ func notifyWatchers(e db.Engine, actions ...*Action) error { permIssue = make([]bool, len(watchers)) permPR = make([]bool, len(watchers)) for i, watcher := range watchers { - user, err := getUserByID(e, watcher.UserID) + user, err := user_model.GetUserByIDEngine(e, watcher.UserID) if err != nil { permCode[i] = false permIssue[i] = false diff --git a/models/review.go b/models/review.go index 00906c7a27..1c4943f699 100644 --- a/models/review.go +++ b/models/review.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/timeutil" @@ -55,10 +56,10 @@ func (rt ReviewType) Icon() string { type Review struct { ID int64 `xorm:"pk autoincr"` Type ReviewType - Reviewer *User `xorm:"-"` - ReviewerID int64 `xorm:"index"` - ReviewerTeamID int64 `xorm:"NOT NULL DEFAULT 0"` - ReviewerTeam *Team `xorm:"-"` + Reviewer *user_model.User `xorm:"-"` + ReviewerID int64 `xorm:"index"` + ReviewerTeamID int64 `xorm:"NOT NULL DEFAULT 0"` + ReviewerTeam *Team `xorm:"-"` OriginalAuthor string OriginalAuthorID int64 Issue *Issue `xorm:"-"` @@ -111,7 +112,7 @@ func (r *Review) loadReviewer(e db.Engine) (err error) { if r.ReviewerID == 0 || r.Reviewer != nil { return } - r.Reviewer, err = getUserByID(e, r.ReviewerID) + r.Reviewer, err = user_model.GetUserByIDEngine(e, r.ReviewerID) return } @@ -224,7 +225,7 @@ type CreateReviewOptions struct { Content string Type ReviewType Issue *Issue - Reviewer *User + Reviewer *user_model.User ReviewerTeam *Team Official bool CommitID string @@ -232,11 +233,11 @@ type CreateReviewOptions struct { } // IsOfficialReviewer check if at least one of the provided reviewers can make official reviews in issue (counts towards required approvals) -func IsOfficialReviewer(issue *Issue, reviewers ...*User) (bool, error) { +func IsOfficialReviewer(issue *Issue, reviewers ...*user_model.User) (bool, error) { return isOfficialReviewer(db.GetEngine(db.DefaultContext), issue, reviewers...) } -func isOfficialReviewer(e db.Engine, issue *Issue, reviewers ...*User) (bool, error) { +func isOfficialReviewer(e db.Engine, issue *Issue, reviewers ...*user_model.User) (bool, error) { pr, err := getPullRequestByIssueID(e, issue.ID) if err != nil { return false, err @@ -314,7 +315,7 @@ func CreateReview(opts CreateReviewOptions) (*Review, error) { return createReview(db.GetEngine(db.DefaultContext), opts) } -func getCurrentReview(e db.Engine, reviewer *User, issue *Issue) (*Review, error) { +func getCurrentReview(e db.Engine, reviewer *user_model.User, issue *Issue) (*Review, error) { if reviewer == nil { return nil, nil } @@ -340,7 +341,7 @@ func ReviewExists(issue *Issue, treePath string, line int64) (bool, error) { } // GetCurrentReview returns the current pending review of reviewer for given issue -func GetCurrentReview(reviewer *User, issue *Issue) (*Review, error) { +func GetCurrentReview(reviewer *user_model.User, issue *Issue) (*Review, error) { return getCurrentReview(db.GetEngine(db.DefaultContext), reviewer, issue) } @@ -358,7 +359,7 @@ func IsContentEmptyErr(err error) bool { } // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist -func SubmitReview(doer *User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool, attachmentUUIDs []string) (*Review, *Comment, error) { +func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, content, commitID string, stale bool, attachmentUUIDs []string) (*Review, *Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, nil, err @@ -627,7 +628,7 @@ func InsertReviews(reviews []*Review) error { } // AddReviewRequest add a review request from one reviewer -func AddReviewRequest(issue *Issue, reviewer, doer *User) (*Comment, error) { +func AddReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -682,7 +683,7 @@ func AddReviewRequest(issue *Issue, reviewer, doer *User) (*Comment, error) { } // RemoveReviewRequest remove a review request from one reviewer -func RemoveReviewRequest(issue *Issue, reviewer, doer *User) (*Comment, error) { +func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -736,7 +737,7 @@ func RemoveReviewRequest(issue *Issue, reviewer, doer *User) (*Comment, error) { } // AddTeamReviewRequest add a review request from one team -func AddTeamReviewRequest(issue *Issue, reviewer *Team, doer *User) (*Comment, error) { +func AddTeamReviewRequest(issue *Issue, reviewer *Team, doer *user_model.User) (*Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -796,7 +797,7 @@ func AddTeamReviewRequest(issue *Issue, reviewer *Team, doer *User) (*Comment, e } // RemoveTeamReviewRequest remove a review request from one team -func RemoveTeamReviewRequest(issue *Issue, reviewer *Team, doer *User) (*Comment, error) { +func RemoveTeamReviewRequest(issue *Issue, reviewer *Team, doer *user_model.User) (*Comment, error) { ctx, committer, err := db.TxContext() if err != nil { return nil, err @@ -856,7 +857,7 @@ func RemoveTeamReviewRequest(issue *Issue, reviewer *Team, doer *User) (*Comment } // MarkConversation Add or remove Conversation mark for a code comment -func MarkConversation(comment *Comment, doer *User, isResolve bool) (err error) { +func MarkConversation(comment *Comment, doer *user_model.User, isResolve bool) (err error) { if comment.Type != CommentTypeCode { return nil } @@ -884,7 +885,7 @@ func MarkConversation(comment *Comment, doer *User, isResolve bool) (err error) // CanMarkConversation Add or remove Conversation mark for a code comment permission check // the PR writer , offfcial reviewer and poster can do it -func CanMarkConversation(issue *Issue, doer *User) (permResult bool, err error) { +func CanMarkConversation(issue *Issue, doer *user_model.User) (permResult bool, err error) { if doer == nil || issue == nil { return false, fmt.Errorf("issue or doer is nil") } diff --git a/models/review_test.go b/models/review_test.go index 7b8b86df13..b9d156ceb1 100644 --- a/models/review_test.go +++ b/models/review_test.go @@ -8,6 +8,7 @@ import ( "testing" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" ) @@ -72,7 +73,7 @@ func TestFindReviews(t *testing.T) { func TestGetCurrentReview(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) review, err := GetCurrentReview(user, issue) assert.NoError(t, err) @@ -80,7 +81,7 @@ func TestGetCurrentReview(t *testing.T) { assert.Equal(t, ReviewTypePending, review.Type) assert.Equal(t, "Pending Review", review.Content) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 7}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User) review2, err := GetCurrentReview(user2, issue) assert.Error(t, err) assert.True(t, IsErrReviewNotExist(err)) @@ -91,7 +92,7 @@ func TestCreateReview(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) review, err := CreateReview(CreateReviewOptions{ Content: "New Review", @@ -108,9 +109,9 @@ func TestGetReviewersByIssueID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 3}).(*Issue) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user3 := unittest.AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) - user4 := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) expectedReviews := []*Review{} expectedReviews = append(expectedReviews, diff --git a/models/ssh_key.go b/models/ssh_key.go index 21d5b7d496..780eb2e15c 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -12,6 +12,7 @@ import ( "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" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -324,7 +325,7 @@ func PublicKeyIsExternallyManaged(id int64) (bool, error) { } // DeletePublicKey deletes SSH key information both in database and authorized_keys file. -func DeletePublicKey(doer *User, id int64) (err error) { +func DeletePublicKey(doer *user_model.User, id int64) (err error) { key, err := GetPublicKeyByID(id) if err != nil { return err @@ -390,7 +391,7 @@ func deleteKeysMarkedForDeletion(keys []string) (bool, error) { } // AddPublicKeysBySource add a users public keys. Returns true if there are changes. -func AddPublicKeysBySource(usr *User, s *login.Source, sshPublicKeys []string) bool { +func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool { var sshKeysNeedUpdate bool for _, sshKey := range sshPublicKeys { var err error @@ -428,7 +429,7 @@ func AddPublicKeysBySource(usr *User, s *login.Source, sshPublicKeys []string) b } // SynchronizePublicKeys updates a users public keys. Returns true if there are changes. -func SynchronizePublicKeys(usr *User, s *login.Source, sshPublicKeys []string) bool { +func SynchronizePublicKeys(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool { var sshKeysNeedUpdate bool log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name) diff --git a/models/ssh_key_deploy.go b/models/ssh_key_deploy.go index c31379dcb4..cf439f6282 100644 --- a/models/ssh_key_deploy.go +++ b/models/ssh_key_deploy.go @@ -9,6 +9,7 @@ import ( "time" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" @@ -212,7 +213,7 @@ func UpdateDeployKey(key *DeployKey) error { } // DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed. -func DeleteDeployKey(doer *User, id int64) error { +func DeleteDeployKey(doer *user_model.User, id int64) error { ctx, committer, err := db.TxContext() if err != nil { return err @@ -225,7 +226,7 @@ func DeleteDeployKey(doer *User, id int64) error { return committer.Commit() } -func deleteDeployKey(sess db.Engine, doer *User, id int64) error { +func deleteDeployKey(sess db.Engine, doer *user_model.User, id int64) error { key, err := getDeployKeyByID(sess, id) if err != nil { if IsErrDeployKeyNotExist(err) { diff --git a/models/ssh_key_principals.go b/models/ssh_key_principals.go index 55d8e40397..63c38f6886 100644 --- a/models/ssh_key_principals.go +++ b/models/ssh_key_principals.go @@ -73,7 +73,7 @@ func addPrincipalKey(e db.Engine, key *PublicKey) (err error) { } // CheckPrincipalKeyString strips spaces and returns an error if the given principal contains newlines -func CheckPrincipalKeyString(user *User, content string) (_ string, err error) { +func CheckPrincipalKeyString(user *user_model.User, content string) (_ string, err error) { if setting.SSH.Disabled { return "", ErrSSHDisabled{} } diff --git a/models/star.go b/models/star.go index c0b15be21a..58bc77132d 100644 --- a/models/star.go +++ b/models/star.go @@ -6,6 +6,7 @@ package models import ( "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" ) @@ -74,16 +75,16 @@ func isStaring(e db.Engine, userID, repoID int64) bool { } // GetStargazers returns the users that starred the repo. -func GetStargazers(repo *Repository, opts db.ListOptions) ([]*User, error) { +func GetStargazers(repo *Repository, opts db.ListOptions) ([]*user_model.User, error) { sess := db.GetEngine(db.DefaultContext).Where("star.repo_id = ?", repo.ID). Join("LEFT", "star", "`user`.id = star.uid") if opts.Page > 0 { sess = db.SetSessionPagination(sess, &opts) - users := make([]*User, 0, opts.PageSize) + users := make([]*user_model.User, 0, opts.PageSize) return users, sess.Find(&users) } - users := make([]*User, 0, 8) + users := make([]*user_model.User, 0, 8) return users, sess.Find(&users) } diff --git a/models/statistic.go b/models/statistic.go index cb300c1a87..9a2a0d5c47 100644 --- a/models/statistic.go +++ b/models/statistic.go @@ -45,7 +45,7 @@ type IssueByRepositoryCount struct { // GetStatistic returns the database statistics func GetStatistic() (stats Statistic) { e := db.GetEngine(db.DefaultContext) - stats.Counter.User = CountUsers() + stats.Counter.User = user_model.CountUsers() stats.Counter.Org = CountOrganizations() stats.Counter.PublicKey, _ = e.Count(new(PublicKey)) stats.Counter.Repo = CountRepositories(true) diff --git a/models/task.go b/models/task.go index 9bff00cd8c..1999957270 100644 --- a/models/task.go +++ b/models/task.go @@ -8,6 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/secret" @@ -22,12 +23,12 @@ import ( // Task represents a task type Task struct { ID int64 - DoerID int64 `xorm:"index"` // operator - Doer *User `xorm:"-"` - OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero - Owner *User `xorm:"-"` - RepoID int64 `xorm:"index"` - Repo *Repository `xorm:"-"` + DoerID int64 `xorm:"index"` // operator + Doer *user_model.User `xorm:"-"` + OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero + Owner *user_model.User `xorm:"-"` + RepoID int64 `xorm:"index"` + Repo *Repository `xorm:"-"` Type structs.TaskType Status structs.TaskStatus `xorm:"index"` StartTime timeutil.TimeStamp @@ -75,12 +76,12 @@ func (task *Task) LoadDoer() error { return nil } - var doer User + var doer user_model.User has, err := db.GetEngine(db.DefaultContext).ID(task.DoerID).Get(&doer) if err != nil { return err } else if !has { - return ErrUserNotExist{ + return user_model.ErrUserNotExist{ UID: task.DoerID, } } @@ -95,12 +96,12 @@ func (task *Task) LoadOwner() error { return nil } - var owner User + var owner user_model.User has, err := db.GetEngine(db.DefaultContext).ID(task.OwnerID).Get(&owner) if err != nil { return err } else if !has { - return ErrUserNotExist{ + return user_model.ErrUserNotExist{ UID: task.OwnerID, } } diff --git a/models/user.go b/models/user.go index 4126606e24..9816fcc713 100644 --- a/models/user.go +++ b/models/user.go @@ -7,491 +7,22 @@ package models import ( "context" - "crypto/sha256" - "crypto/subtle" - "encoding/hex" - "errors" "fmt" - "net/url" - "os" - "path/filepath" - "regexp" - "strings" "time" - "unicode/utf8" _ "image/jpeg" // Needed for jpeg support "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/login" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/auth/openid" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" - "golang.org/x/crypto/argon2" - "golang.org/x/crypto/bcrypt" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/crypto/scrypt" "xorm.io/builder" - "xorm.io/xorm" ) -// UserType defines the user type -type UserType int - -const ( - // UserTypeIndividual defines an individual user - UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. - - // UserTypeOrganization defines an organization - UserTypeOrganization -) - -const ( - algoBcrypt = "bcrypt" - algoScrypt = "scrypt" - algoArgon2 = "argon2" - algoPbkdf2 = "pbkdf2" -) - -// AvailableHashAlgorithms represents the available password hashing algorithms -var AvailableHashAlgorithms = []string{ - algoPbkdf2, - algoArgon2, - algoScrypt, - algoBcrypt, -} - -const ( - // EmailNotificationsEnabled indicates that the user would like to receive all email notifications - EmailNotificationsEnabled = "enabled" - // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned. - EmailNotificationsOnMention = "onmention" - // EmailNotificationsDisabled indicates that the user would not like to be notified via email. - EmailNotificationsDisabled = "disabled" -) - -var ( - // ErrUserNameIllegal user name contains illegal characters error - ErrUserNameIllegal = errors.New("User name contains illegal characters") - - // ErrLoginSourceNotActived login source is not actived error - ErrLoginSourceNotActived = errors.New("Login source is not actived") - - // ErrUnsupportedLoginType login source is unknown error - ErrUnsupportedLoginType = errors.New("Login source is unknown") - - // Characters prohibited in a user name (anything except A-Za-z0-9_.-) - alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`) -) - -// User represents the object of individual and member of organization. -type User struct { - ID int64 `xorm:"pk autoincr"` - LowerName string `xorm:"UNIQUE NOT NULL"` - Name string `xorm:"UNIQUE NOT NULL"` - FullName string - // Email is the primary email address (to be used for communication) - Email string `xorm:"NOT NULL"` - KeepEmailPrivate bool - EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"` - Passwd string `xorm:"NOT NULL"` - PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` - - // MustChangePassword is an attribute that determines if a user - // is to change his/her password after registration. - MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` - - LoginType login.Type - LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` - LoginName string - Type UserType - Location string - Website string - Rands string `xorm:"VARCHAR(10)"` - Salt string `xorm:"VARCHAR(10)"` - Language string `xorm:"VARCHAR(5)"` - Description string - - CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` - UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` - LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"` - - // Remember visibility choice for convenience, true for private - LastRepoVisibility bool - // Maximum repository creation limit, -1 means use global default - MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"` - - // IsActive true: primary email is activated, user can access Web UI and Git SSH. - // false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access. - IsActive bool `xorm:"INDEX"` - // the user is a Gitea admin, who can access all repositories and the admin pages. - IsAdmin bool - // true: the user is only allowed to see organizations/repositories that they has explicit rights to. - // (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public) - IsRestricted bool `xorm:"NOT NULL DEFAULT false"` - - AllowGitHook bool - AllowImportLocal bool // Allow migrate repository by local path - AllowCreateOrganization bool `xorm:"DEFAULT true"` - - // true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents) - ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` - - // Avatar - Avatar string `xorm:"VARCHAR(2048) NOT NULL"` - AvatarEmail string `xorm:"NOT NULL"` - UseCustomAvatar bool - - // Counters - NumFollowers int - NumFollowing int `xorm:"NOT NULL DEFAULT 0"` - NumStars int - NumRepos int - - // For organization - NumTeams int - NumMembers int - Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` - RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"` - - // Preferences - DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` - Theme string `xorm:"NOT NULL DEFAULT ''"` - KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"` -} - -func init() { - db.RegisterModel(new(User)) -} - -// SearchOrganizationsOptions options to filter organizations -type SearchOrganizationsOptions struct { - db.ListOptions - All bool -} - -// ColorFormat writes a colored string to identify this struct -func (u *User) ColorFormat(s fmt.State) { - log.ColorFprintf(s, "%d:%s", - log.NewColoredIDValue(u.ID), - log.NewColoredValue(u.Name)) -} - -// BeforeUpdate is invoked from XORM before updating this object. -func (u *User) BeforeUpdate() { - if u.MaxRepoCreation < -1 { - u.MaxRepoCreation = -1 - } - - // Organization does not need email - u.Email = strings.ToLower(u.Email) - if !u.IsOrganization() { - if len(u.AvatarEmail) == 0 { - u.AvatarEmail = u.Email - } - } - - u.LowerName = strings.ToLower(u.Name) - u.Location = base.TruncateString(u.Location, 255) - u.Website = base.TruncateString(u.Website, 255) - u.Description = base.TruncateString(u.Description, 255) -} - -// AfterLoad is invoked from XORM after filling all the fields of this object. -func (u *User) AfterLoad() { - if u.Theme == "" { - u.Theme = setting.UI.DefaultTheme - } -} - -// SetLastLogin set time to last login -func (u *User) SetLastLogin() { - u.LastLoginUnix = timeutil.TimeStampNow() -} - -// UpdateUserDiffViewStyle updates the users diff view style -func UpdateUserDiffViewStyle(u *User, style string) error { - u.DiffViewStyle = style - return UpdateUserCols(db.DefaultContext, u, "diff_view_style") -} - -// UpdateUserTheme updates a users' theme irrespective of the site wide theme -func UpdateUserTheme(u *User, themeName string) error { - u.Theme = themeName - return UpdateUserCols(db.DefaultContext, u, "theme") -} - -// GetEmail returns an noreply email, if the user has set to keep his -// email address private, otherwise the primary email address. -func (u *User) GetEmail() string { - if u.KeepEmailPrivate { - return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress) - } - return u.Email -} - -// GetAllUsers returns a slice of all individual users found in DB. -func GetAllUsers() ([]*User, error) { - users := make([]*User, 0) - return users, db.GetEngine(db.DefaultContext).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users) -} - -// IsLocal returns true if user login type is LoginPlain. -func (u *User) IsLocal() bool { - return u.LoginType <= login.Plain -} - -// IsOAuth2 returns true if user login type is LoginOAuth2. -func (u *User) IsOAuth2() bool { - return u.LoginType == login.OAuth2 -} - -// MaxCreationLimit returns the number of repositories a user is allowed to create -func (u *User) MaxCreationLimit() int { - if u.MaxRepoCreation <= -1 { - return setting.Repository.MaxCreationLimit - } - return u.MaxRepoCreation -} - -// CanCreateRepo returns if user login can create a repository -// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised -func (u *User) CanCreateRepo() bool { - if u.IsAdmin { - return true - } - if u.MaxRepoCreation <= -1 { - if setting.Repository.MaxCreationLimit <= -1 { - return true - } - return u.NumRepos < setting.Repository.MaxCreationLimit - } - return u.NumRepos < u.MaxRepoCreation -} - -// CanCreateOrganization returns true if user can create organisation. -func (u *User) CanCreateOrganization() bool { - return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation) -} - -// CanEditGitHook returns true if user can edit Git hooks. -func (u *User) CanEditGitHook() bool { - return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook) -} - -// CanImportLocal returns true if user can migrate repository by local path. -func (u *User) CanImportLocal() bool { - if !setting.ImportLocalPaths || u == nil { - return false - } - return u.IsAdmin || u.AllowImportLocal -} - -// DashboardLink returns the user dashboard page link. -func (u *User) DashboardLink() string { - if u.IsOrganization() { - return u.OrganisationLink() + "/dashboard" - } - return setting.AppSubURL + "/" -} - -// HomeLink returns the user or organization home page link. -func (u *User) HomeLink() string { - return setting.AppSubURL + "/" + url.PathEscape(u.Name) -} - -// HTMLURL returns the user or organization's full link. -func (u *User) HTMLURL() string { - return setting.AppURL + url.PathEscape(u.Name) -} - -// OrganisationLink returns the organization sub page link. -func (u *User) OrganisationLink() string { - return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) -} - -// GenerateEmailActivateCode generates an activate code based on user information and given e-mail. -func (u *User) GenerateEmailActivateCode(email string) string { - code := base.CreateTimeLimitCode( - fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands), - setting.Service.ActiveCodeLives, nil) - - // Add tail hex username - code += hex.EncodeToString([]byte(u.LowerName)) - return code -} - -// GetUserFollowers returns range of user's followers. -func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) { - sess := db.GetEngine(db.DefaultContext). - Where("follow.follow_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.user_id") - - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) - } - - users := make([]*User, 0, 8) - return users, sess.Find(&users) -} - -// GetUserFollowing returns range of user's following. -func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) { - sess := db.GetEngine(db.DefaultContext). - Where("follow.user_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.follow_id") - - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) - } - - users := make([]*User, 0, 8) - return users, sess.Find(&users) -} - -// NewGitSig generates and returns the signature of given user. -func (u *User) NewGitSig() *git.Signature { - return &git.Signature{ - Name: u.GitName(), - Email: u.GetEmail(), - When: time.Now(), - } -} - -func hashPassword(passwd, salt, algo string) string { - var tempPasswd []byte - - switch algo { - case algoBcrypt: - tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost) - return string(tempPasswd) - case algoScrypt: - tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50) - case algoArgon2: - tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50) - case algoPbkdf2: - fallthrough - default: - tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New) - } - - return fmt.Sprintf("%x", tempPasswd) -} - -// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO -// change passwd, salt and passwd_hash_algo fields -func (u *User) SetPassword(passwd string) (err error) { - if len(passwd) == 0 { - u.Passwd = "" - u.Salt = "" - u.PasswdHashAlgo = "" - return nil - } - - if u.Salt, err = GetUserSalt(); err != nil { - return err - } - u.PasswdHashAlgo = setting.PasswordHashAlgo - u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo) - - return nil -} - -// ValidatePassword checks if given password matches the one belongs to the user. -func (u *User) ValidatePassword(passwd string) bool { - tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo) - - if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 { - return true - } - if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil { - return true - } - return false -} - -// IsPasswordSet checks if the password is set or left empty -func (u *User) IsPasswordSet() bool { - return len(u.Passwd) != 0 -} - -// IsUserVisibleToViewer check if viewer is able to see user profile -func IsUserVisibleToViewer(u *User, viewer *User) bool { - return isUserVisibleToViewer(db.GetEngine(db.DefaultContext), u, viewer) -} - -func isUserVisibleToViewer(e db.Engine, u *User, viewer *User) bool { - if viewer != nil && viewer.IsAdmin { - return true - } - - switch u.Visibility { - case structs.VisibleTypePublic: - return true - case structs.VisibleTypeLimited: - if viewer == nil || viewer.IsRestricted { - return false - } - return true - case structs.VisibleTypePrivate: - if viewer == nil || viewer.IsRestricted { - return false - } - - // If they follow - they see each over - follower := user_model.IsFollowing(u.ID, viewer.ID) - if follower { - return true - } - - // Now we need to check if they in some organization together - count, err := e.Table("team_user"). - Where( - builder.And( - builder.Eq{"uid": viewer.ID}, - builder.Or( - builder.Eq{"org_id": u.ID}, - builder.In("org_id", - builder.Select("org_id"). - From("team_user", "t2"). - Where(builder.Eq{"uid": u.ID}))))). - Count(new(TeamUser)) - if err != nil { - return false - } - - if count < 0 { - // No common organization - return false - } - - // they are in an organization together - return true - } - return false -} - -// IsOrganization returns true if user is actually a organization. -func (u *User) IsOrganization() bool { - return u.Type == UserTypeOrganization -} - // GetOrganizationCount returns count of membership of organization of the user. -func GetOrganizationCount(ctx context.Context, u *User) (int64, error) { +func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) { return db.GetEngine(ctx). Where("uid=?", u.ID). Count(new(OrgUser)) @@ -499,7 +30,7 @@ func GetOrganizationCount(ctx context.Context, u *User) (int64, error) { // GetRepositoryIDs returns repositories IDs where user owned and has unittypes // Caller shall check that units is not globally disabled -func (u *User) GetRepositoryIDs(units ...unit.Type) ([]int64, error) { +func GetRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { var ids []int64 sess := db.GetEngine(db.DefaultContext).Table("repository").Cols("repository.id") @@ -514,7 +45,7 @@ func (u *User) GetRepositoryIDs(units ...unit.Type) ([]int64, error) { // GetActiveRepositoryIDs returns non-archived repositories IDs where user owned and has unittypes // Caller shall check that units is not globally disabled -func (u *User) GetActiveRepositoryIDs(units ...unit.Type) ([]int64, error) { +func GetActiveRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { var ids []int64 sess := db.GetEngine(db.DefaultContext).Table("repository").Cols("repository.id") @@ -531,7 +62,7 @@ func (u *User) GetActiveRepositoryIDs(units ...unit.Type) ([]int64, error) { // GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes // Caller shall check that units is not globally disabled -func (u *User) GetOrgRepositoryIDs(units ...unit.Type) ([]int64, error) { +func GetOrgRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { var ids []int64 if err := db.GetEngine(db.DefaultContext).Table("repository"). @@ -552,7 +83,7 @@ func (u *User) GetOrgRepositoryIDs(units ...unit.Type) ([]int64, error) { // GetActiveOrgRepositoryIDs returns non-archived repositories IDs where user's team owned and has unittypes // Caller shall check that units is not globally disabled -func (u *User) GetActiveOrgRepositoryIDs(units ...unit.Type) ([]int64, error) { +func GetActiveOrgRepositoryIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { var ids []int64 if err := db.GetEngine(db.DefaultContext).Table("repository"). @@ -574,12 +105,12 @@ func (u *User) GetActiveOrgRepositoryIDs(units ...unit.Type) ([]int64, error) { // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations // Caller shall check that units is not globally disabled -func (u *User) GetAccessRepoIDs(units ...unit.Type) ([]int64, error) { - ids, err := u.GetRepositoryIDs(units...) +func GetAccessRepoIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { + ids, err := GetRepositoryIDs(u, units...) if err != nil { return nil, err } - ids2, err := u.GetOrgRepositoryIDs(units...) + ids2, err := GetOrgRepositoryIDs(u, units...) if err != nil { return nil, err } @@ -588,467 +119,18 @@ func (u *User) GetAccessRepoIDs(units ...unit.Type) ([]int64, error) { // GetActiveAccessRepoIDs returns all non-archived repositories IDs where user's or user is a team member organizations // Caller shall check that units is not globally disabled -func (u *User) GetActiveAccessRepoIDs(units ...unit.Type) ([]int64, error) { - ids, err := u.GetActiveRepositoryIDs(units...) +func GetActiveAccessRepoIDs(u *user_model.User, units ...unit.Type) ([]int64, error) { + ids, err := GetActiveRepositoryIDs(u, units...) if err != nil { return nil, err } - ids2, err := u.GetActiveOrgRepositoryIDs(units...) + ids2, err := GetActiveOrgRepositoryIDs(u, units...) if err != nil { return nil, err } return append(ids, ids2...), nil } -// DisplayName returns full name if it's not empty, -// returns username otherwise. -func (u *User) DisplayName() string { - trimmed := strings.TrimSpace(u.FullName) - if len(trimmed) > 0 { - return trimmed - } - return u.Name -} - -// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set, -// returns username otherwise. -func (u *User) GetDisplayName() string { - if setting.UI.DefaultShowFullName { - trimmed := strings.TrimSpace(u.FullName) - if len(trimmed) > 0 { - return trimmed - } - } - return u.Name -} - -func gitSafeName(name string) string { - return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name)) -} - -// GitName returns a git safe name -func (u *User) GitName() string { - gitName := gitSafeName(u.FullName) - if len(gitName) > 0 { - return gitName - } - // Although u.Name should be safe if created in our system - // LDAP users may have bad names - gitName = gitSafeName(u.Name) - if len(gitName) > 0 { - return gitName - } - // Totally pathological name so it's got to be: - return fmt.Sprintf("user-%d", u.ID) -} - -// ShortName ellipses username to length -func (u *User) ShortName(length int) string { - return base.EllipsisString(u.Name, length) -} - -// IsMailable checks if a user is eligible -// to receive emails. -func (u *User) IsMailable() bool { - return u.IsActive -} - -// EmailNotifications returns the User's email notification preference -func (u *User) EmailNotifications() string { - return u.EmailNotificationsPreference -} - -// SetEmailNotifications sets the user's email notification preference -func SetEmailNotifications(u *User, set string) error { - u.EmailNotificationsPreference = set - if err := UpdateUserCols(db.DefaultContext, u, "email_notifications_preference"); err != nil { - log.Error("SetEmailNotifications: %v", err) - return err - } - return nil -} - -func isUserExist(e db.Engine, uid int64, name string) (bool, error) { - if len(name) == 0 { - return false, nil - } - return e. - Where("id!=?", uid). - Get(&User{LowerName: strings.ToLower(name)}) -} - -// IsUserExist checks if given user name exist, -// the user name should be noncased unique. -// If uid is presented, then check will rule out that one, -// it is used when update a user name in settings page. -func IsUserExist(uid int64, name string) (bool, error) { - return isUserExist(db.GetEngine(db.DefaultContext), uid, name) -} - -// GetUserSalt returns a random user salt token. -func GetUserSalt() (string, error) { - return util.RandomString(10) -} - -// NewGhostUser creates and returns a fake user for someone has deleted his/her account. -func NewGhostUser() *User { - return &User{ - ID: -1, - Name: "Ghost", - LowerName: "ghost", - } -} - -// NewReplaceUser creates and returns a fake user for external user -func NewReplaceUser(name string) *User { - return &User{ - ID: -1, - Name: name, - LowerName: strings.ToLower(name), - } -} - -// IsGhost check if user is fake user for a deleted account -func (u *User) IsGhost() bool { - if u == nil { - return false - } - return u.ID == -1 && u.Name == "Ghost" -} - -var ( - reservedUsernames = []string{ - ".", - "..", - ".well-known", - "admin", - "api", - "assets", - "attachments", - "avatars", - "captcha", - "commits", - "debug", - "error", - "explore", - "favicon.ico", - "ghost", - "help", - "install", - "issues", - "less", - "login", - "manifest.json", - "metrics", - "milestones", - "new", - "notifications", - "org", - "plugins", - "pulls", - "raw", - "repo", - "robots.txt", - "search", - "serviceworker.js", - "stars", - "template", - "user", - } - - reservedUserPatterns = []string{"*.keys", "*.gpg", "*.rss", "*.atom"} -) - -// isUsableName checks if name is reserved or pattern of name is not allowed -// based on given reserved names and patterns. -// Names are exact match, patterns can be prefix or suffix match with placeholder '*'. -func isUsableName(names, patterns []string, name string) error { - name = strings.TrimSpace(strings.ToLower(name)) - if utf8.RuneCountInString(name) == 0 { - return ErrNameEmpty - } - - for i := range names { - if name == names[i] { - return ErrNameReserved{name} - } - } - - for _, pat := range patterns { - if pat[0] == '*' && strings.HasSuffix(name, pat[1:]) || - (pat[len(pat)-1] == '*' && strings.HasPrefix(name, pat[:len(pat)-1])) { - return ErrNamePatternNotAllowed{pat} - } - } - - return nil -} - -// IsUsableUsername returns an error when a username is reserved -func IsUsableUsername(name string) error { - // Validate username make sure it satisfies requirement. - if alphaDashDotPattern.MatchString(name) { - // Note: usually this error is normally caught up earlier in the UI - return ErrNameCharsNotAllowed{Name: name} - } - return isUsableName(reservedUsernames, reservedUserPatterns, name) -} - -// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation -type CreateUserOverwriteOptions struct { - Visibility structs.VisibleType -} - -// CreateUser creates record of a new user. -func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { - if err = IsUsableUsername(u.Name); err != nil { - return err - } - - // set system defaults - u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate - u.Visibility = setting.Service.DefaultUserVisibilityMode - u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation - u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification - u.MaxRepoCreation = -1 - u.Theme = setting.UI.DefaultTheme - - // overwrite defaults if set - if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { - u.Visibility = overwriteDefault[0].Visibility - } - - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - - sess := db.GetEngine(ctx) - - // validate data - if err := validateUser(u); err != nil { - return err - } - - isExist, err := isUserExist(sess, 0, u.Name) - if err != nil { - return err - } else if isExist { - return ErrUserAlreadyExist{u.Name} - } - - isExist, err = user_model.IsEmailUsed(ctx, u.Email) - if err != nil { - return err - } else if isExist { - return user_model.ErrEmailAlreadyUsed{ - Email: u.Email, - } - } - - // prepare for database - - u.LowerName = strings.ToLower(u.Name) - u.AvatarEmail = u.Email - if u.Rands, err = GetUserSalt(); err != nil { - return err - } - if err = u.SetPassword(u.Passwd); err != nil { - return err - } - - // save changes to database - - if err = user_model.DeleteUserRedirect(ctx, u.Name); err != nil { - return err - } - - if err = db.Insert(ctx, u); err != nil { - return err - } - - // insert email address - if err := db.Insert(ctx, &user_model.EmailAddress{ - UID: u.ID, - Email: u.Email, - LowerEmail: strings.ToLower(u.Email), - IsActivated: u.IsActive, - IsPrimary: true, - }); err != nil { - return err - } - - return committer.Commit() -} - -func countUsers(e db.Engine) int64 { - count, _ := e. - Where("type=0"). - Count(new(User)) - return count -} - -// CountUsers returns number of users. -func CountUsers() int64 { - return countUsers(db.GetEngine(db.DefaultContext)) -} - -// get user by verify code -func getVerifyUser(code string) (user *User) { - if len(code) <= base.TimeLimitCodeLength { - return nil - } - - // use tail hex username query user - hexStr := code[base.TimeLimitCodeLength:] - if b, err := hex.DecodeString(hexStr); err == nil { - if user, err = GetUserByName(string(b)); user != nil { - return user - } - log.Error("user.getVerifyUser: %v", err) - } - - return nil -} - -// VerifyUserActiveCode verifies active code when active account -func VerifyUserActiveCode(code string) (user *User) { - 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, user.Email, user.LowerName, user.Passwd, user.Rands) - - if base.VerifyTimeLimitCode(data, minutes, prefix) { - return user - } - } - return nil -} - -// ChangeUserName changes all corresponding setting from old user name to new one. -func ChangeUserName(u *User, newUserName string) (err error) { - oldUserName := u.Name - if err = IsUsableUsername(newUserName); err != nil { - return err - } - - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) - - isExist, err := isUserExist(sess, 0, newUserName) - if err != nil { - return err - } else if isExist { - return ErrUserAlreadyExist{newUserName} - } - - if _, err = sess.Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil { - return fmt.Errorf("Change repo owner name: %v", err) - } - - // Do not fail if directory does not exist - if err = util.Rename(UserPath(oldUserName), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("Rename user directory: %v", err) - } - - if err = user_model.NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { - return err - } - - if err = committer.Commit(); err != nil { - if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { - log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) - return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) - } - return err - } - - return nil -} - -// checkDupEmail checks whether there are the same email with the user -func checkDupEmail(e db.Engine, u *User) error { - u.Email = strings.ToLower(u.Email) - has, err := e. - Where("id!=?", u.ID). - And("type=?", u.Type). - And("email=?", u.Email). - Get(new(User)) - if err != nil { - return err - } else if has { - return user_model.ErrEmailAlreadyUsed{ - Email: u.Email, - } - } - return nil -} - -// validateUser check if user is valid to insert / update into database -func validateUser(u *User) error { - if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() { - return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String()) - } - - u.Email = strings.ToLower(u.Email) - return user_model.ValidateEmail(u.Email) -} - -func updateUser(e db.Engine, u *User) error { - if err := validateUser(u); err != nil { - return err - } - - _, err := e.ID(u.ID).AllCols().Update(u) - return err -} - -// UpdateUser updates user's information. -func UpdateUser(u *User) error { - return updateUser(db.GetEngine(db.DefaultContext), u) -} - -// UpdateUserCols update user according special columns -func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { - return updateUserCols(db.GetEngine(ctx), u, cols...) -} - -func updateUserCols(e db.Engine, u *User, cols ...string) error { - if err := validateUser(u); err != nil { - return err - } - - _, err := e.ID(u.ID).Cols(cols...).Update(u) - return err -} - -// UpdateUserSetting updates user's settings. -func UpdateUserSetting(u *User) (err error) { - ctx, committer, err := db.TxContext() - if err != nil { - return err - } - defer committer.Close() - sess := db.GetEngine(ctx) - - if !u.IsOrganization() { - if err = checkDupEmail(sess, u); err != nil { - return err - } - } - if err = updateUser(sess, u); err != nil { - return err - } - return committer.Commit() -} - // deleteBeans deletes all given beans, beans should contain delete conditions. func deleteBeans(e db.Engine, beans ...interface{}) (err error) { for i := range beans { @@ -1060,7 +142,7 @@ func deleteBeans(e db.Engine, beans ...interface{}) (err error) { } // DeleteUser deletes models associated to an user. -func DeleteUser(ctx context.Context, u *User) (err error) { +func DeleteUser(ctx context.Context, u *user_model.User) (err error) { e := db.GetEngine(ctx) // ***** START: Watch ***** @@ -1089,7 +171,7 @@ func DeleteUser(ctx context.Context, u *User) (err error) { if err = e.Table("follow").Cols("follow.follow_id"). Where("follow.user_id = ?", u.ID).Find(&followeeIDs); err != nil { return fmt.Errorf("get all followees: %v", err) - } else if _, err = e.Decr("num_followers").In("id", followeeIDs).Update(new(User)); err != nil { + } else if _, err = e.Decr("num_followers").In("id", followeeIDs).Update(new(user_model.User)); err != nil { return fmt.Errorf("decrease user num_followers: %v", err) } @@ -1097,7 +179,7 @@ func DeleteUser(ctx context.Context, u *User) (err error) { if err = e.Table("follow").Cols("follow.user_id"). Where("follow.follow_id = ?", u.ID).Find(&followerIDs); err != nil { return fmt.Errorf("get all followers: %v", err) - } else if _, err = e.Decr("num_following").In("id", followerIDs).Update(new(User)); err != nil { + } else if _, err = e.Decr("num_following").In("id", followerIDs).Update(new(user_model.User)); err != nil { return fmt.Errorf("decrease user num_following: %v", err) } // ***** END: Follow ***** @@ -1183,386 +265,13 @@ func DeleteUser(ctx context.Context, u *User) (err error) { } // ***** END: ExternalLoginUser ***** - if _, err = e.ID(u.ID).Delete(new(User)); err != nil { + if _, err = e.ID(u.ID).Delete(new(user_model.User)); err != nil { return fmt.Errorf("Delete: %v", err) } return nil } -// GetInactiveUsers gets all inactive users -func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) { - var cond builder.Cond = builder.Eq{"is_active": false} - - if olderThan > 0 { - cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()}) - } - - users := make([]*User, 0, 10) - return users, db.GetEngine(ctx). - Where(cond). - Find(&users) -} - -// UserPath returns the path absolute path of user repositories. -func UserPath(userName string) string { - return filepath.Join(setting.RepoRootPath, strings.ToLower(userName)) -} - -func getUserByID(e db.Engine, id int64) (*User, error) { - u := new(User) - has, err := e.ID(id).Get(u) - if err != nil { - return nil, err - } else if !has { - return nil, ErrUserNotExist{id, "", 0} - } - return u, nil -} - -// GetUserByID returns the user object by given ID if exists. -func GetUserByID(id int64) (*User, error) { - return GetUserByIDCtx(db.DefaultContext, id) -} - -// GetUserByIDCtx returns the user object by given ID if exists. -func GetUserByIDCtx(ctx context.Context, id int64) (*User, error) { - return getUserByID(db.GetEngine(ctx), id) -} - -// GetUserByName returns user by given name. -func GetUserByName(name string) (*User, error) { - return getUserByName(db.GetEngine(db.DefaultContext), name) -} - -func getUserByName(e db.Engine, name string) (*User, error) { - if len(name) == 0 { - return nil, ErrUserNotExist{0, name, 0} - } - u := &User{LowerName: strings.ToLower(name)} - has, err := e.Get(u) - if err != nil { - return nil, err - } else if !has { - return nil, ErrUserNotExist{0, name, 0} - } - return u, nil -} - -// GetUserEmailsByNames returns a list of e-mails corresponds to names of users -// that have their email notifications set to enabled or onmention. -func GetUserEmailsByNames(names []string) []string { - return getUserEmailsByNames(db.GetEngine(db.DefaultContext), names) -} - -func getUserEmailsByNames(e db.Engine, names []string) []string { - mails := make([]string, 0, len(names)) - for _, name := range names { - u, err := getUserByName(e, name) - if err != nil { - continue - } - if u.IsMailable() && u.EmailNotifications() != EmailNotificationsDisabled { - mails = append(mails, u.Email) - } - } - return mails -} - -// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails -func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) { - if len(ids) == 0 { - return nil, nil - } - ous := make([]*User, 0, len(ids)) - - if isMention { - return ous, db.GetEngine(db.DefaultContext).In("id", ids). - Where("`type` = ?", UserTypeIndividual). - And("`prohibit_login` = ?", false). - And("`is_active` = ?", true). - And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention). - Find(&ous) - } - - return ous, db.GetEngine(db.DefaultContext).In("id", ids). - Where("`type` = ?", UserTypeIndividual). - And("`prohibit_login` = ?", false). - And("`is_active` = ?", true). - And("`email_notifications_preference` = ?", EmailNotificationsEnabled). - Find(&ous) -} - -// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids. -func GetUserNamesByIDs(ids []int64) ([]string, error) { - unames := make([]string, 0, len(ids)) - err := db.GetEngine(db.DefaultContext).In("id", ids). - Table("user"). - Asc("name"). - Cols("name"). - Find(&unames) - return unames, err -} - -// 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 -} - -// GetUserIDsByNames returns a slice of ids corresponds to names. -func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) { - ids := make([]int64, 0, len(names)) - for _, name := range names { - u, err := GetUserByName(name) - if err != nil { - if ignoreNonExistent { - continue - } else { - return nil, err - } - } - ids = append(ids, u.ID) - } - return ids, nil -} - -// GetUsersBySource returns a list of Users for a login source -func GetUsersBySource(s *login.Source) ([]*User, error) { - var users []*User - err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users) - return users, err -} - -// UserCommit represents a commit with validation of user. -type UserCommit struct { - User *User - *git.Commit -} - -// ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user. -func ValidateCommitWithEmail(c *git.Commit) *User { - if c.Author == nil { - return nil - } - u, err := GetUserByEmail(c.Author.Email) - if err != nil { - return nil - } - return u -} - -// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users. -func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit { - var ( - emails = make(map[string]*User) - newCommits = make([]*UserCommit, 0, len(oldCommits)) - ) - for _, c := range oldCommits { - var u *User - if c.Author != nil { - if v, ok := emails[c.Author.Email]; !ok { - u, _ = GetUserByEmail(c.Author.Email) - emails[c.Author.Email] = u - } else { - u = v - } - } - - newCommits = append(newCommits, &UserCommit{ - User: u, - Commit: c, - }) - } - return newCommits -} - -// GetUserByEmail returns the user object by given e-mail if exists. -func GetUserByEmail(email string) (*User, error) { - return GetUserByEmailContext(db.DefaultContext, email) -} - -// GetUserByEmailContext returns the user object by given e-mail if exists with db context -func GetUserByEmailContext(ctx context.Context, email string) (*User, error) { - if len(email) == 0 { - return nil, ErrUserNotExist{0, email, 0} - } - - email = strings.ToLower(email) - // First try to find the user by primary email - user := &User{Email: email} - has, err := db.GetEngine(ctx).Get(user) - if err != nil { - return nil, err - } - if has { - return user, nil - } - - // Otherwise, check in alternative list for activated email addresses - emailAddress := &user_model.EmailAddress{Email: email, IsActivated: true} - has, err = db.GetEngine(ctx).Get(emailAddress) - if err != nil { - return nil, err - } - if has { - return GetUserByIDCtx(ctx, emailAddress.UID) - } - - // Finally, if email address is the protected email address: - if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) { - username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) - user := &User{} - has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user) - if err != nil { - return nil, err - } - if has { - return user, nil - } - } - - return nil, ErrUserNotExist{0, email, 0} -} - -// GetUser checks if a user already exists -func GetUser(user *User) (bool, error) { - return db.GetEngine(db.DefaultContext).Get(user) -} - -// SearchUserOptions contains the options for searching -type SearchUserOptions struct { - db.ListOptions - Keyword string - Type UserType - UID int64 - OrderBy SearchOrderBy - Visible []structs.VisibleType - Actor *User // The user doing the search - SearchByEmail bool // Search by email as well as username/full name - - IsActive util.OptionalBool - IsAdmin util.OptionalBool - IsRestricted util.OptionalBool - IsTwoFactorEnabled util.OptionalBool - IsProhibitLogin util.OptionalBool -} - -func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session { - var cond builder.Cond = builder.Eq{"type": opts.Type} - if len(opts.Keyword) > 0 { - lowerKeyword := strings.ToLower(opts.Keyword) - keywordCond := builder.Or( - builder.Like{"lower_name", lowerKeyword}, - builder.Like{"LOWER(full_name)", lowerKeyword}, - ) - if opts.SearchByEmail { - keywordCond = keywordCond.Or(builder.Like{"LOWER(email)", lowerKeyword}) - } - - cond = cond.And(keywordCond) - } - - // If visibility filtered - if len(opts.Visible) > 0 { - cond = cond.And(builder.In("visibility", opts.Visible)) - } - - if opts.Actor != nil { - var exprCond builder.Cond = builder.Expr("org_user.org_id = `user`.id") - - // If Admin - they see all users! - if !opts.Actor.IsAdmin { - // Force visibility for privacy - var accessCond builder.Cond - if !opts.Actor.IsRestricted { - accessCond = builder.Or( - builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))), - builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited)) - } else { - // restricted users only see orgs they are a member of - accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}))) - } - // Don't forget about self - accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID}) - cond = cond.And(accessCond) - } - - } else { - // Force visibility for privacy - // Not logged in - only public users - cond = cond.And(builder.In("visibility", structs.VisibleTypePublic)) - } - - if opts.UID > 0 { - cond = cond.And(builder.Eq{"id": opts.UID}) - } - - if !opts.IsActive.IsNone() { - cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) - } - - if !opts.IsAdmin.IsNone() { - cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()}) - } - - if !opts.IsRestricted.IsNone() { - cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.IsTrue()}) - } - - if !opts.IsProhibitLogin.IsNone() { - cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.IsTrue()}) - } - - e := db.GetEngine(db.DefaultContext) - if opts.IsTwoFactorEnabled.IsNone() { - return e.Where(cond) - } - - // 2fa filter uses LEFT JOIN to check whether a user has a 2fa record - // TODO: bad performance here, maybe there will be a column "is_2fa_enabled" in the future - if opts.IsTwoFactorEnabled.IsTrue() { - cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL")) - } else { - cond = cond.And(builder.Expr("two_factor.uid IS NULL")) - } - - return e.Join("LEFT OUTER", "two_factor", "two_factor.uid = `user`.id"). - Where(cond) -} - -// SearchUsers takes options i.e. keyword and part of user name to search, -// it returns results in given range and number of total results. -func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) { - sessCount := opts.toSearchQueryBase() - defer sessCount.Close() - count, err := sessCount.Count(new(User)) - if err != nil { - return nil, 0, fmt.Errorf("Count: %v", err) - } - - if len(opts.OrderBy) == 0 { - opts.OrderBy = SearchOrderByAlphabetically - } - - sessQuery := opts.toSearchQueryBase().OrderBy(opts.OrderBy.String()) - defer sessQuery.Close() - if opts.Page != 0 { - sessQuery = db.SetSessionPagination(sessQuery, opts) - } - - // the sql may contain JOIN, so we must only select User related columns - sessQuery = sessQuery.Select("`user`.*") - users = make([]*User, 0, opts.PageSize) - return users, count, sessQuery.Find(&users) -} - // GetStarredRepos returns the repos starred by a particular user func GetStarredRepos(userID int64, private bool, listOptions db.ListOptions) ([]*Repository, error) { sess := db.GetEngine(db.DefaultContext).Where("star.uid=?", userID). @@ -1604,63 +313,58 @@ func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([] return repos, total, err } -// IterateUser iterate users -func IterateUser(f func(user *User) error) error { - var start int - batchSize := setting.Database.IterateBufferSize - for { - users := make([]*User, 0, batchSize) - if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&users); err != nil { - return err - } - if len(users) == 0 { - return nil - } - start += len(users) - - for _, user := range users { - if err := f(user); err != nil { - return err - } - } - } +// IsUserVisibleToViewer check if viewer is able to see user profile +func IsUserVisibleToViewer(u *user_model.User, viewer *user_model.User) bool { + return isUserVisibleToViewer(db.GetEngine(db.DefaultContext), u, viewer) } -// GetUserByOpenID returns the user object by given OpenID if exists. -func GetUserByOpenID(uri string) (*User, error) { - if len(uri) == 0 { - return nil, ErrUserNotExist{0, uri, 0} +func isUserVisibleToViewer(e db.Engine, u *user_model.User, viewer *user_model.User) bool { + if viewer != nil && viewer.IsAdmin { + return true } - uri, err := openid.Normalize(uri) - if err != nil { - return nil, err - } + switch u.Visibility { + case structs.VisibleTypePublic: + return true + case structs.VisibleTypeLimited: + if viewer == nil || viewer.IsRestricted { + return false + } + return true + case structs.VisibleTypePrivate: + if viewer == nil || viewer.IsRestricted { + return false + } - log.Trace("Normalized OpenID URI: " + uri) + // If they follow - they see each over + follower := user_model.IsFollowing(u.ID, viewer.ID) + if follower { + return true + } - // Otherwise, check in openid table - oid := &user_model.UserOpenID{} - has, err := db.GetEngine(db.DefaultContext).Where("uri=?", uri).Get(oid) - if err != nil { - return nil, err - } - if has { - return GetUserByID(oid.UID) - } + // Now we need to check if they in some organization together + count, err := e.Table("team_user"). + Where( + builder.And( + builder.Eq{"uid": viewer.ID}, + builder.Or( + builder.Eq{"org_id": u.ID}, + builder.In("org_id", + builder.Select("org_id"). + From("team_user", "t2"). + Where(builder.Eq{"uid": u.ID}))))). + Count(new(TeamUser)) + if err != nil { + return false + } - return nil, ErrUserNotExist{0, uri, 0} -} + if count < 0 { + // No common organization + return false + } -// GetAdminUser returns the first administrator -func GetAdminUser() (*User, error) { - var admin User - has, err := db.GetEngine(db.DefaultContext).Where("is_admin=?", true).Get(&admin) - if err != nil { - return nil, err - } else if !has { - return nil, ErrUserNotExist{} + // they are in an organization together + return true } - - return &admin, nil + return false } diff --git a/models/user_avatar.go b/models/user/avatar.go index ae4cd1e5ec..c881642b56 100644 --- a/models/user_avatar.go +++ b/models/user/avatar.go @@ -2,9 +2,10 @@ // 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" "crypto/md5" "fmt" "image/png" @@ -26,10 +27,11 @@ func (u *User) CustomAvatarRelativePath() string { // GenerateRandomAvatar generates a random avatar for user. func GenerateRandomAvatar(u *User) error { - return generateRandomAvatar(db.GetEngine(db.DefaultContext), u) + return GenerateRandomAvatarCtx(db.DefaultContext, u) } -func generateRandomAvatar(e db.Engine, u *User) error { +// GenerateRandomAvatarCtx generates a random avatar for user. +func GenerateRandomAvatarCtx(ctx context.Context, u *User) error { seed := u.Email if len(seed) == 0 { seed = u.Name @@ -52,7 +54,7 @@ func generateRandomAvatar(e db.Engine, u *User) error { return fmt.Errorf("Failed to create dir %s: %v", u.CustomAvatarRelativePath(), err) } - if _, err := e.ID(u.ID).Cols("avatar").Update(u); err != nil { + if _, err := db.GetEngine(ctx).ID(u.ID).Cols("avatar").Update(u); err != nil { return err } diff --git a/models/user/error.go b/models/user/error.go new file mode 100644 index 0000000000..25e0d8ea8a --- /dev/null +++ b/models/user/error.go @@ -0,0 +1,80 @@ +// 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 user + +import ( + "fmt" +) + +// ____ ___ +// | | \______ ___________ +// | | / ___// __ \_ __ \ +// | | /\___ \\ ___/| | \/ +// |______//____ >\___ >__| +// \/ \/ + +// ErrUserAlreadyExist represents a "user already exists" error. +type ErrUserAlreadyExist struct { + Name string +} + +// IsErrUserAlreadyExist checks if an error is a ErrUserAlreadyExists. +func IsErrUserAlreadyExist(err error) bool { + _, ok := err.(ErrUserAlreadyExist) + return ok +} + +func (err ErrUserAlreadyExist) Error() string { + return fmt.Sprintf("user already exists [name: %s]", err.Name) +} + +// ErrUserNotExist represents a "UserNotExist" kind of error. +type ErrUserNotExist struct { + UID int64 + Name string + KeyID int64 +} + +// IsErrUserNotExist checks if an error is a ErrUserNotExist. +func IsErrUserNotExist(err error) bool { + _, ok := err.(ErrUserNotExist) + return ok +} + +func (err ErrUserNotExist) Error() string { + return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID) +} + +// ErrUserProhibitLogin represents a "ErrUserProhibitLogin" kind of error. +type ErrUserProhibitLogin struct { + UID int64 + Name string +} + +// IsErrUserProhibitLogin checks if an error is a ErrUserProhibitLogin +func IsErrUserProhibitLogin(err error) bool { + _, ok := err.(ErrUserProhibitLogin) + return ok +} + +func (err ErrUserProhibitLogin) Error() string { + return fmt.Sprintf("user is not allowed login [uid: %d, name: %s]", err.UID, err.Name) +} + +// ErrUserInactive represents a "ErrUserInactive" kind of error. +type ErrUserInactive struct { + UID int64 + Name string +} + +// IsErrUserInactive checks if an error is a ErrUserInactive +func IsErrUserInactive(err error) bool { + _, ok := err.(ErrUserInactive) + return ok +} + +func (err ErrUserInactive) Error() string { + return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name) +} diff --git a/models/user/main_test.go b/models/user/main_test.go index 925af0e8b2..55e5f9341c 100644 --- a/models/user/main_test.go +++ b/models/user/main_test.go @@ -17,5 +17,8 @@ func TestMain(m *testing.M) { "user_redirect.yml", "follow.yml", "user_open_id.yml", + "two_factor.yml", + "oauth2_application.yml", + "user.yml", ) } diff --git a/models/user/search.go b/models/user/search.go new file mode 100644 index 0000000000..5c37bb9961 --- /dev/null +++ b/models/user/search.go @@ -0,0 +1,167 @@ +// 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 user + +import ( + "fmt" + "strings" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" + "xorm.io/xorm" +) + +// SearchUserOptions contains the options for searching +type SearchUserOptions struct { + db.ListOptions + Keyword string + Type UserType + UID int64 + OrderBy db.SearchOrderBy + Visible []structs.VisibleType + Actor *User // The user doing the search + SearchByEmail bool // Search by email as well as username/full name + + IsActive util.OptionalBool + IsAdmin util.OptionalBool + IsRestricted util.OptionalBool + IsTwoFactorEnabled util.OptionalBool + IsProhibitLogin util.OptionalBool +} + +func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session { + var cond builder.Cond = builder.Eq{"type": opts.Type} + if len(opts.Keyword) > 0 { + lowerKeyword := strings.ToLower(opts.Keyword) + keywordCond := builder.Or( + builder.Like{"lower_name", lowerKeyword}, + builder.Like{"LOWER(full_name)", lowerKeyword}, + ) + if opts.SearchByEmail { + keywordCond = keywordCond.Or(builder.Like{"LOWER(email)", lowerKeyword}) + } + + cond = cond.And(keywordCond) + } + + // If visibility filtered + if len(opts.Visible) > 0 { + cond = cond.And(builder.In("visibility", opts.Visible)) + } + + if opts.Actor != nil { + var exprCond builder.Cond = builder.Expr("org_user.org_id = `user`.id") + + // If Admin - they see all users! + if !opts.Actor.IsAdmin { + // Force visibility for privacy + var accessCond builder.Cond + if !opts.Actor.IsRestricted { + accessCond = builder.Or( + builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))), + builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited)) + } else { + // restricted users only see orgs they are a member of + accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}))) + } + // Don't forget about self + accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID}) + cond = cond.And(accessCond) + } + + } else { + // Force visibility for privacy + // Not logged in - only public users + cond = cond.And(builder.In("visibility", structs.VisibleTypePublic)) + } + + if opts.UID > 0 { + cond = cond.And(builder.Eq{"id": opts.UID}) + } + + if !opts.IsActive.IsNone() { + cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) + } + + if !opts.IsAdmin.IsNone() { + cond = cond.And(builder.Eq{"is_admin": opts.IsAdmin.IsTrue()}) + } + + if !opts.IsRestricted.IsNone() { + cond = cond.And(builder.Eq{"is_restricted": opts.IsRestricted.IsTrue()}) + } + + if !opts.IsProhibitLogin.IsNone() { + cond = cond.And(builder.Eq{"prohibit_login": opts.IsProhibitLogin.IsTrue()}) + } + + e := db.GetEngine(db.DefaultContext) + if opts.IsTwoFactorEnabled.IsNone() { + return e.Where(cond) + } + + // 2fa filter uses LEFT JOIN to check whether a user has a 2fa record + // TODO: bad performance here, maybe there will be a column "is_2fa_enabled" in the future + if opts.IsTwoFactorEnabled.IsTrue() { + cond = cond.And(builder.Expr("two_factor.uid IS NOT NULL")) + } else { + cond = cond.And(builder.Expr("two_factor.uid IS NULL")) + } + + return e.Join("LEFT OUTER", "two_factor", "two_factor.uid = `user`.id"). + Where(cond) +} + +// SearchUsers takes options i.e. keyword and part of user name to search, +// it returns results in given range and number of total results. +func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) { + sessCount := opts.toSearchQueryBase() + defer sessCount.Close() + count, err := sessCount.Count(new(User)) + if err != nil { + return nil, 0, fmt.Errorf("Count: %v", err) + } + + if len(opts.OrderBy) == 0 { + opts.OrderBy = db.SearchOrderByAlphabetically + } + + sessQuery := opts.toSearchQueryBase().OrderBy(opts.OrderBy.String()) + defer sessQuery.Close() + if opts.Page != 0 { + sessQuery = db.SetSessionPagination(sessQuery, opts) + } + + // the sql may contain JOIN, so we must only select User related columns + sessQuery = sessQuery.Select("`user`.*") + users = make([]*User, 0, opts.PageSize) + return users, count, sessQuery.Find(&users) +} + +// IterateUser iterate users +func IterateUser(f func(user *User) error) error { + var start int + batchSize := setting.Database.IterateBufferSize + for { + users := make([]*User, 0, batchSize) + if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&users); err != nil { + return err + } + if len(users) == 0 { + return nil + } + start += len(users) + + for _, user := range users { + if err := f(user); err != nil { + return err + } + } + } +} diff --git a/models/user/user.go b/models/user/user.go new file mode 100644 index 0000000000..f8ccee0b38 --- /dev/null +++ b/models/user/user.go @@ -0,0 +1,1125 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// 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 ( + "context" + "crypto/sha256" + "crypto/subtle" + "encoding/hex" + "errors" + "fmt" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + _ "image/jpeg" // Needed for jpeg support + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/login" + "code.gitea.io/gitea/modules/auth/openid" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" + + "golang.org/x/crypto/argon2" + "golang.org/x/crypto/bcrypt" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/crypto/scrypt" + "xorm.io/builder" +) + +// UserType defines the user type +type UserType int //revive:disable-line:exported + +const ( + // UserTypeIndividual defines an individual user + UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. + + // UserTypeOrganization defines an organization + UserTypeOrganization +) + +const ( + algoBcrypt = "bcrypt" + algoScrypt = "scrypt" + algoArgon2 = "argon2" + algoPbkdf2 = "pbkdf2" +) + +// AvailableHashAlgorithms represents the available password hashing algorithms +var AvailableHashAlgorithms = []string{ + algoPbkdf2, + algoArgon2, + algoScrypt, + algoBcrypt, +} + +const ( + // EmailNotificationsEnabled indicates that the user would like to receive all email notifications + EmailNotificationsEnabled = "enabled" + // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned. + EmailNotificationsOnMention = "onmention" + // EmailNotificationsDisabled indicates that the user would not like to be notified via email. + EmailNotificationsDisabled = "disabled" +) + +var ( + // ErrUserNameIllegal user name contains illegal characters error + ErrUserNameIllegal = errors.New("User name contains illegal characters") +) + +// User represents the object of individual and member of organization. +type User struct { + ID int64 `xorm:"pk autoincr"` + LowerName string `xorm:"UNIQUE NOT NULL"` + Name string `xorm:"UNIQUE NOT NULL"` + FullName string + // Email is the primary email address (to be used for communication) + Email string `xorm:"NOT NULL"` + KeepEmailPrivate bool + EmailNotificationsPreference string `xorm:"VARCHAR(20) NOT NULL DEFAULT 'enabled'"` + Passwd string `xorm:"NOT NULL"` + PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"` + + // MustChangePassword is an attribute that determines if a user + // is to change his/her password after registration. + MustChangePassword bool `xorm:"NOT NULL DEFAULT false"` + + LoginType login.Type + LoginSource int64 `xorm:"NOT NULL DEFAULT 0"` + LoginName string + Type UserType + Location string + Website string + Rands string `xorm:"VARCHAR(10)"` + Salt string `xorm:"VARCHAR(10)"` + Language string `xorm:"VARCHAR(5)"` + Description string + + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + LastLoginUnix timeutil.TimeStamp `xorm:"INDEX"` + + // Remember visibility choice for convenience, true for private + LastRepoVisibility bool + // Maximum repository creation limit, -1 means use global default + MaxRepoCreation int `xorm:"NOT NULL DEFAULT -1"` + + // IsActive true: primary email is activated, user can access Web UI and Git SSH. + // false: an inactive user can only log in Web UI for account operations (ex: activate the account by email), no other access. + IsActive bool `xorm:"INDEX"` + // the user is a Gitea admin, who can access all repositories and the admin pages. + IsAdmin bool + // true: the user is only allowed to see organizations/repositories that they has explicit rights to. + // (ex: in private Gitea instances user won't be allowed to see even organizations/repositories that are set as public) + IsRestricted bool `xorm:"NOT NULL DEFAULT false"` + + AllowGitHook bool + AllowImportLocal bool // Allow migrate repository by local path + AllowCreateOrganization bool `xorm:"DEFAULT true"` + + // true: the user is not allowed to log in Web UI. Git/SSH access could still be allowed (please refer to Git/SSH access related code/documents) + ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"` + + // Avatar + Avatar string `xorm:"VARCHAR(2048) NOT NULL"` + AvatarEmail string `xorm:"NOT NULL"` + UseCustomAvatar bool + + // Counters + NumFollowers int + NumFollowing int `xorm:"NOT NULL DEFAULT 0"` + NumStars int + NumRepos int + + // For organization + NumTeams int + NumMembers int + Visibility structs.VisibleType `xorm:"NOT NULL DEFAULT 0"` + RepoAdminChangeTeamAccess bool `xorm:"NOT NULL DEFAULT false"` + + // Preferences + DiffViewStyle string `xorm:"NOT NULL DEFAULT ''"` + Theme string `xorm:"NOT NULL DEFAULT ''"` + KeepActivityPrivate bool `xorm:"NOT NULL DEFAULT false"` +} + +func init() { + db.RegisterModel(new(User)) +} + +// SearchOrganizationsOptions options to filter organizations +type SearchOrganizationsOptions struct { + db.ListOptions + All bool +} + +// ColorFormat writes a colored string to identify this struct +func (u *User) ColorFormat(s fmt.State) { + log.ColorFprintf(s, "%d:%s", + log.NewColoredIDValue(u.ID), + log.NewColoredValue(u.Name)) +} + +// BeforeUpdate is invoked from XORM before updating this object. +func (u *User) BeforeUpdate() { + if u.MaxRepoCreation < -1 { + u.MaxRepoCreation = -1 + } + + // Organization does not need email + u.Email = strings.ToLower(u.Email) + if !u.IsOrganization() { + if len(u.AvatarEmail) == 0 { + u.AvatarEmail = u.Email + } + } + + u.LowerName = strings.ToLower(u.Name) + u.Location = base.TruncateString(u.Location, 255) + u.Website = base.TruncateString(u.Website, 255) + u.Description = base.TruncateString(u.Description, 255) +} + +// AfterLoad is invoked from XORM after filling all the fields of this object. +func (u *User) AfterLoad() { + if u.Theme == "" { + u.Theme = setting.UI.DefaultTheme + } +} + +// SetLastLogin set time to last login +func (u *User) SetLastLogin() { + u.LastLoginUnix = timeutil.TimeStampNow() +} + +// UpdateUserDiffViewStyle updates the users diff view style +func UpdateUserDiffViewStyle(u *User, style string) error { + u.DiffViewStyle = style + return UpdateUserCols(db.DefaultContext, u, "diff_view_style") +} + +// UpdateUserTheme updates a users' theme irrespective of the site wide theme +func UpdateUserTheme(u *User, themeName string) error { + u.Theme = themeName + return UpdateUserCols(db.DefaultContext, u, "theme") +} + +// GetEmail returns an noreply email, if the user has set to keep his +// email address private, otherwise the primary email address. +func (u *User) GetEmail() string { + if u.KeepEmailPrivate { + return fmt.Sprintf("%s@%s", u.LowerName, setting.Service.NoReplyAddress) + } + return u.Email +} + +// GetAllUsers returns a slice of all individual users found in DB. +func GetAllUsers() ([]*User, error) { + users := make([]*User, 0) + return users, db.GetEngine(db.DefaultContext).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users) +} + +// IsLocal returns true if user login type is LoginPlain. +func (u *User) IsLocal() bool { + return u.LoginType <= login.Plain +} + +// IsOAuth2 returns true if user login type is LoginOAuth2. +func (u *User) IsOAuth2() bool { + return u.LoginType == login.OAuth2 +} + +// MaxCreationLimit returns the number of repositories a user is allowed to create +func (u *User) MaxCreationLimit() int { + if u.MaxRepoCreation <= -1 { + return setting.Repository.MaxCreationLimit + } + return u.MaxRepoCreation +} + +// CanCreateRepo returns if user login can create a repository +// NOTE: functions calling this assume a failure due to repository count limit; if new checks are added, those functions should be revised +func (u *User) CanCreateRepo() bool { + if u.IsAdmin { + return true + } + if u.MaxRepoCreation <= -1 { + if setting.Repository.MaxCreationLimit <= -1 { + return true + } + return u.NumRepos < setting.Repository.MaxCreationLimit + } + return u.NumRepos < u.MaxRepoCreation +} + +// CanCreateOrganization returns true if user can create organisation. +func (u *User) CanCreateOrganization() bool { + return u.IsAdmin || (u.AllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation) +} + +// CanEditGitHook returns true if user can edit Git hooks. +func (u *User) CanEditGitHook() bool { + return !setting.DisableGitHooks && (u.IsAdmin || u.AllowGitHook) +} + +// CanImportLocal returns true if user can migrate repository by local path. +func (u *User) CanImportLocal() bool { + if !setting.ImportLocalPaths || u == nil { + return false + } + return u.IsAdmin || u.AllowImportLocal +} + +// DashboardLink returns the user dashboard page link. +func (u *User) DashboardLink() string { + if u.IsOrganization() { + return u.OrganisationLink() + "/dashboard" + } + return setting.AppSubURL + "/" +} + +// HomeLink returns the user or organization home page link. +func (u *User) HomeLink() string { + return setting.AppSubURL + "/" + url.PathEscape(u.Name) +} + +// HTMLURL returns the user or organization's full link. +func (u *User) HTMLURL() string { + return setting.AppURL + url.PathEscape(u.Name) +} + +// OrganisationLink returns the organization sub page link. +func (u *User) OrganisationLink() string { + return setting.AppSubURL + "/org/" + url.PathEscape(u.Name) +} + +// GenerateEmailActivateCode generates an activate code based on user information and given e-mail. +func (u *User) GenerateEmailActivateCode(email string) string { + code := base.CreateTimeLimitCode( + fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands), + setting.Service.ActiveCodeLives, nil) + + // Add tail hex username + code += hex.EncodeToString([]byte(u.LowerName)) + return code +} + +// GetUserFollowers returns range of user's followers. +func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) { + sess := db.GetEngine(db.DefaultContext). + Where("follow.follow_id=?", u.ID). + Join("LEFT", "follow", "`user`.id=follow.user_id") + + if listOptions.Page != 0 { + sess = db.SetSessionPagination(sess, &listOptions) + + users := make([]*User, 0, listOptions.PageSize) + return users, sess.Find(&users) + } + + users := make([]*User, 0, 8) + return users, sess.Find(&users) +} + +// GetUserFollowing returns range of user's following. +func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) { + sess := db.GetEngine(db.DefaultContext). + Where("follow.user_id=?", u.ID). + Join("LEFT", "follow", "`user`.id=follow.follow_id") + + if listOptions.Page != 0 { + sess = db.SetSessionPagination(sess, &listOptions) + + users := make([]*User, 0, listOptions.PageSize) + return users, sess.Find(&users) + } + + users := make([]*User, 0, 8) + return users, sess.Find(&users) +} + +// NewGitSig generates and returns the signature of given user. +func (u *User) NewGitSig() *git.Signature { + return &git.Signature{ + Name: u.GitName(), + Email: u.GetEmail(), + When: time.Now(), + } +} + +func hashPassword(passwd, salt, algo string) string { + var tempPasswd []byte + + switch algo { + case algoBcrypt: + tempPasswd, _ = bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost) + return string(tempPasswd) + case algoScrypt: + tempPasswd, _ = scrypt.Key([]byte(passwd), []byte(salt), 65536, 16, 2, 50) + case algoArgon2: + tempPasswd = argon2.IDKey([]byte(passwd), []byte(salt), 2, 65536, 8, 50) + case algoPbkdf2: + fallthrough + default: + tempPasswd = pbkdf2.Key([]byte(passwd), []byte(salt), 10000, 50, sha256.New) + } + + return fmt.Sprintf("%x", tempPasswd) +} + +// SetPassword hashes a password using the algorithm defined in the config value of PASSWORD_HASH_ALGO +// change passwd, salt and passwd_hash_algo fields +func (u *User) SetPassword(passwd string) (err error) { + if len(passwd) == 0 { + u.Passwd = "" + u.Salt = "" + u.PasswdHashAlgo = "" + return nil + } + + if u.Salt, err = GetUserSalt(); err != nil { + return err + } + u.PasswdHashAlgo = setting.PasswordHashAlgo + u.Passwd = hashPassword(passwd, u.Salt, setting.PasswordHashAlgo) + + return nil +} + +// ValidatePassword checks if given password matches the one belongs to the user. +func (u *User) ValidatePassword(passwd string) bool { + tempHash := hashPassword(passwd, u.Salt, u.PasswdHashAlgo) + + if u.PasswdHashAlgo != algoBcrypt && subtle.ConstantTimeCompare([]byte(u.Passwd), []byte(tempHash)) == 1 { + return true + } + if u.PasswdHashAlgo == algoBcrypt && bcrypt.CompareHashAndPassword([]byte(u.Passwd), []byte(passwd)) == nil { + return true + } + return false +} + +// IsPasswordSet checks if the password is set or left empty +func (u *User) IsPasswordSet() bool { + return len(u.Passwd) != 0 +} + +// IsOrganization returns true if user is actually a organization. +func (u *User) IsOrganization() bool { + return u.Type == UserTypeOrganization +} + +// DisplayName returns full name if it's not empty, +// returns username otherwise. +func (u *User) DisplayName() string { + trimmed := strings.TrimSpace(u.FullName) + if len(trimmed) > 0 { + return trimmed + } + return u.Name +} + +// GetDisplayName returns full name if it's not empty and DEFAULT_SHOW_FULL_NAME is set, +// returns username otherwise. +func (u *User) GetDisplayName() string { + if setting.UI.DefaultShowFullName { + trimmed := strings.TrimSpace(u.FullName) + if len(trimmed) > 0 { + return trimmed + } + } + return u.Name +} + +func gitSafeName(name string) string { + return strings.TrimSpace(strings.NewReplacer("\n", "", "<", "", ">", "").Replace(name)) +} + +// GitName returns a git safe name +func (u *User) GitName() string { + gitName := gitSafeName(u.FullName) + if len(gitName) > 0 { + return gitName + } + // Although u.Name should be safe if created in our system + // LDAP users may have bad names + gitName = gitSafeName(u.Name) + if len(gitName) > 0 { + return gitName + } + // Totally pathological name so it's got to be: + return fmt.Sprintf("user-%d", u.ID) +} + +// ShortName ellipses username to length +func (u *User) ShortName(length int) string { + return base.EllipsisString(u.Name, length) +} + +// IsMailable checks if a user is eligible +// to receive emails. +func (u *User) IsMailable() bool { + return u.IsActive +} + +// EmailNotifications returns the User's email notification preference +func (u *User) EmailNotifications() string { + return u.EmailNotificationsPreference +} + +// SetEmailNotifications sets the user's email notification preference +func SetEmailNotifications(u *User, set string) error { + u.EmailNotificationsPreference = set + if err := UpdateUserCols(db.DefaultContext, u, "email_notifications_preference"); err != nil { + log.Error("SetEmailNotifications: %v", err) + return err + } + return nil +} + +func isUserExist(e db.Engine, uid int64, name string) (bool, error) { + if len(name) == 0 { + return false, nil + } + return e. + Where("id!=?", uid). + Get(&User{LowerName: strings.ToLower(name)}) +} + +// IsUserExist checks if given user name exist, +// the user name should be noncased unique. +// If uid is presented, then check will rule out that one, +// it is used when update a user name in settings page. +func IsUserExist(uid int64, name string) (bool, error) { + return isUserExist(db.GetEngine(db.DefaultContext), uid, name) +} + +// GetUserSalt returns a random user salt token. +func GetUserSalt() (string, error) { + return util.RandomString(10) +} + +// NewGhostUser creates and returns a fake user for someone has deleted his/her account. +func NewGhostUser() *User { + return &User{ + ID: -1, + Name: "Ghost", + LowerName: "ghost", + } +} + +// NewReplaceUser creates and returns a fake user for external user +func NewReplaceUser(name string) *User { + return &User{ + ID: -1, + Name: name, + LowerName: strings.ToLower(name), + } +} + +// IsGhost check if user is fake user for a deleted account +func (u *User) IsGhost() bool { + if u == nil { + return false + } + return u.ID == -1 && u.Name == "Ghost" +} + +var ( + reservedUsernames = []string{ + ".", + "..", + ".well-known", + "admin", + "api", + "assets", + "attachments", + "avatars", + "captcha", + "commits", + "debug", + "error", + "explore", + "favicon.ico", + "ghost", + "help", + "install", + "issues", + "less", + "login", + "manifest.json", + "metrics", + "milestones", + "new", + "notifications", + "org", + "plugins", + "pulls", + "raw", + "repo", + "robots.txt", + "search", + "serviceworker.js", + "stars", + "template", + "user", + } + + reservedUserPatterns = []string{"*.keys", "*.gpg", "*.rss", "*.atom"} +) + +// IsUsableUsername returns an error when a username is reserved +func IsUsableUsername(name string) error { + // Validate username make sure it satisfies requirement. + if db.AlphaDashDotPattern.MatchString(name) { + // Note: usually this error is normally caught up earlier in the UI + return db.ErrNameCharsNotAllowed{Name: name} + } + return db.IsUsableName(reservedUsernames, reservedUserPatterns, name) +} + +// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation +type CreateUserOverwriteOptions struct { + Visibility structs.VisibleType +} + +// CreateUser creates record of a new user. +func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err error) { + if err = IsUsableUsername(u.Name); err != nil { + return err + } + + // set system defaults + u.KeepEmailPrivate = setting.Service.DefaultKeepEmailPrivate + u.Visibility = setting.Service.DefaultUserVisibilityMode + u.AllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization && !setting.Admin.DisableRegularOrgCreation + u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification + u.MaxRepoCreation = -1 + u.Theme = setting.UI.DefaultTheme + + // overwrite defaults if set + if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { + u.Visibility = overwriteDefault[0].Visibility + } + + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + + sess := db.GetEngine(ctx) + + // validate data + if err := validateUser(u); err != nil { + return err + } + + isExist, err := isUserExist(sess, 0, u.Name) + if err != nil { + return err + } else if isExist { + return ErrUserAlreadyExist{u.Name} + } + + isExist, err = IsEmailUsed(ctx, u.Email) + if err != nil { + return err + } else if isExist { + return ErrEmailAlreadyUsed{ + Email: u.Email, + } + } + + // prepare for database + + u.LowerName = strings.ToLower(u.Name) + u.AvatarEmail = u.Email + if u.Rands, err = GetUserSalt(); err != nil { + return err + } + if err = u.SetPassword(u.Passwd); err != nil { + return err + } + + // save changes to database + + if err = DeleteUserRedirect(ctx, u.Name); err != nil { + return err + } + + if err = db.Insert(ctx, u); err != nil { + return err + } + + // insert email address + if err := db.Insert(ctx, &EmailAddress{ + UID: u.ID, + Email: u.Email, + LowerEmail: strings.ToLower(u.Email), + IsActivated: u.IsActive, + IsPrimary: true, + }); err != nil { + return err + } + + return committer.Commit() +} + +func countUsers(e db.Engine) int64 { + count, _ := e. + Where("type=0"). + Count(new(User)) + return count +} + +// CountUsers returns number of users. +func CountUsers() int64 { + return countUsers(db.GetEngine(db.DefaultContext)) +} + +// GetVerifyUser get user by verify code +func GetVerifyUser(code string) (user *User) { + if len(code) <= base.TimeLimitCodeLength { + return nil + } + + // use tail hex username query user + hexStr := code[base.TimeLimitCodeLength:] + if b, err := hex.DecodeString(hexStr); err == nil { + if user, err = GetUserByName(string(b)); user != nil { + return user + } + log.Error("user.getVerifyUser: %v", err) + } + + return nil +} + +// VerifyUserActiveCode verifies active code when active account +func VerifyUserActiveCode(code string) (user *User) { + 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, user.Email, user.LowerName, user.Passwd, user.Rands) + + if base.VerifyTimeLimitCode(data, minutes, prefix) { + return user + } + } + return nil +} + +// ChangeUserName changes all corresponding setting from old user name to new one. +func ChangeUserName(u *User, newUserName string) (err error) { + oldUserName := u.Name + if err = IsUsableUsername(newUserName); err != nil { + return err + } + + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + sess := db.GetEngine(ctx) + + isExist, err := isUserExist(sess, 0, newUserName) + if err != nil { + return err + } else if isExist { + return ErrUserAlreadyExist{newUserName} + } + + if _, err = sess.Exec("UPDATE `repository` SET owner_name=? WHERE owner_name=?", newUserName, oldUserName); err != nil { + return fmt.Errorf("Change repo owner name: %v", err) + } + + // Do not fail if directory does not exist + if err = util.Rename(UserPath(oldUserName), UserPath(newUserName)); err != nil && !os.IsNotExist(err) { + return fmt.Errorf("Rename user directory: %v", err) + } + + if err = NewUserRedirect(ctx, u.ID, oldUserName, newUserName); err != nil { + return err + } + + if err = committer.Commit(); err != nil { + if err2 := util.Rename(UserPath(newUserName), UserPath(oldUserName)); err2 != nil && !os.IsNotExist(err2) { + log.Critical("Unable to rollback directory change during failed username change from: %s to: %s. DB Error: %v. Filesystem Error: %v", oldUserName, newUserName, err, err2) + return fmt.Errorf("failed to rollback directory change during failed username change from: %s to: %s. DB Error: %w. Filesystem Error: %v", oldUserName, newUserName, err, err2) + } + return err + } + + return nil +} + +// checkDupEmail checks whether there are the same email with the user +func checkDupEmail(e db.Engine, u *User) error { + u.Email = strings.ToLower(u.Email) + has, err := e. + Where("id!=?", u.ID). + And("type=?", u.Type). + And("email=?", u.Email). + Get(new(User)) + if err != nil { + return err + } else if has { + return ErrEmailAlreadyUsed{ + Email: u.Email, + } + } + return nil +} + +// validateUser check if user is valid to insert / update into database +func validateUser(u *User) error { + if !setting.Service.AllowedUserVisibilityModesSlice.IsAllowedVisibility(u.Visibility) && !u.IsOrganization() { + return fmt.Errorf("visibility Mode not allowed: %s", u.Visibility.String()) + } + + u.Email = strings.ToLower(u.Email) + return ValidateEmail(u.Email) +} + +func updateUser(e db.Engine, u *User) error { + if err := validateUser(u); err != nil { + return err + } + + _, err := e.ID(u.ID).AllCols().Update(u) + return err +} + +// UpdateUser updates user's information. +func UpdateUser(u *User) error { + return updateUser(db.GetEngine(db.DefaultContext), u) +} + +// UpdateUserCols update user according special columns +func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { + return updateUserCols(db.GetEngine(ctx), u, cols...) +} + +// UpdateUserColsEngine update user according special columns +func UpdateUserColsEngine(e db.Engine, u *User, cols ...string) error { + return updateUserCols(e, u, cols...) +} + +func updateUserCols(e db.Engine, u *User, cols ...string) error { + if err := validateUser(u); err != nil { + return err + } + + _, err := e.ID(u.ID).Cols(cols...).Update(u) + return err +} + +// UpdateUserSetting updates user's settings. +func UpdateUserSetting(u *User) (err error) { + ctx, committer, err := db.TxContext() + if err != nil { + return err + } + defer committer.Close() + sess := db.GetEngine(ctx) + + if !u.IsOrganization() { + if err = checkDupEmail(sess, u); err != nil { + return err + } + } + if err = updateUser(sess, u); err != nil { + return err + } + return committer.Commit() +} + +// GetInactiveUsers gets all inactive users +func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) { + var cond builder.Cond = builder.Eq{"is_active": false} + + if olderThan > 0 { + cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()}) + } + + users := make([]*User, 0, 10) + return users, db.GetEngine(ctx). + Where(cond). + Find(&users) +} + +// UserPath returns the path absolute path of user repositories. +func UserPath(userName string) string { //revive:disable-line:exported + return filepath.Join(setting.RepoRootPath, strings.ToLower(userName)) +} + +// GetUserByIDEngine returns the user object by given ID if exists. +func GetUserByIDEngine(e db.Engine, id int64) (*User, error) { + u := new(User) + has, err := e.ID(id).Get(u) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUserNotExist{id, "", 0} + } + return u, nil +} + +// GetUserByID returns the user object by given ID if exists. +func GetUserByID(id int64) (*User, error) { + return GetUserByIDCtx(db.DefaultContext, id) +} + +// GetUserByIDCtx returns the user object by given ID if exists. +func GetUserByIDCtx(ctx context.Context, id int64) (*User, error) { + return GetUserByIDEngine(db.GetEngine(ctx), id) +} + +// GetUserByName returns user by given name. +func GetUserByName(name string) (*User, error) { + return GetUserByNameCtx(db.DefaultContext, name) +} + +// GetUserByNameCtx returns user by given name. +func GetUserByNameCtx(ctx context.Context, name string) (*User, error) { + if len(name) == 0 { + return nil, ErrUserNotExist{0, name, 0} + } + u := &User{LowerName: strings.ToLower(name)} + has, err := db.GetEngine(ctx).Get(u) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUserNotExist{0, name, 0} + } + return u, nil +} + +// GetUserEmailsByNames returns a list of e-mails corresponds to names of users +// that have their email notifications set to enabled or onmention. +func GetUserEmailsByNames(names []string) []string { + return getUserEmailsByNames(db.DefaultContext, names) +} + +func getUserEmailsByNames(ctx context.Context, names []string) []string { + mails := make([]string, 0, len(names)) + for _, name := range names { + u, err := GetUserByNameCtx(ctx, name) + if err != nil { + continue + } + if u.IsMailable() && u.EmailNotifications() != EmailNotificationsDisabled { + mails = append(mails, u.Email) + } + } + return mails +} + +// GetMaileableUsersByIDs gets users from ids, but only if they can receive mails +func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) { + if len(ids) == 0 { + return nil, nil + } + ous := make([]*User, 0, len(ids)) + + if isMention { + return ous, db.GetEngine(db.DefaultContext).In("id", ids). + Where("`type` = ?", UserTypeIndividual). + And("`prohibit_login` = ?", false). + And("`is_active` = ?", true). + And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention). + Find(&ous) + } + + return ous, db.GetEngine(db.DefaultContext).In("id", ids). + Where("`type` = ?", UserTypeIndividual). + And("`prohibit_login` = ?", false). + And("`is_active` = ?", true). + And("`email_notifications_preference` = ?", EmailNotificationsEnabled). + Find(&ous) +} + +// GetUserNamesByIDs returns usernames for all resolved users from a list of Ids. +func GetUserNamesByIDs(ids []int64) ([]string, error) { + unames := make([]string, 0, len(ids)) + err := db.GetEngine(db.DefaultContext).In("id", ids). + Table("user"). + Asc("name"). + Cols("name"). + Find(&unames) + return unames, err +} + +// GetUserIDsByNames returns a slice of ids corresponds to names. +func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) { + ids := make([]int64, 0, len(names)) + for _, name := range names { + u, err := GetUserByName(name) + if err != nil { + if ignoreNonExistent { + continue + } else { + return nil, err + } + } + ids = append(ids, u.ID) + } + return ids, nil +} + +// GetUsersBySource returns a list of Users for a login source +func GetUsersBySource(s *login.Source) ([]*User, error) { + var users []*User + err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users) + return users, err +} + +// UserCommit represents a commit with validation of user. +type UserCommit struct { //revive:disable-line:exported + User *User + *git.Commit +} + +// ValidateCommitWithEmail check if author's e-mail of commit is corresponding to a user. +func ValidateCommitWithEmail(c *git.Commit) *User { + if c.Author == nil { + return nil + } + u, err := GetUserByEmail(c.Author.Email) + if err != nil { + return nil + } + return u +} + +// ValidateCommitsWithEmails checks if authors' e-mails of commits are corresponding to users. +func ValidateCommitsWithEmails(oldCommits []*git.Commit) []*UserCommit { + var ( + emails = make(map[string]*User) + newCommits = make([]*UserCommit, 0, len(oldCommits)) + ) + for _, c := range oldCommits { + var u *User + if c.Author != nil { + if v, ok := emails[c.Author.Email]; !ok { + u, _ = GetUserByEmail(c.Author.Email) + emails[c.Author.Email] = u + } else { + u = v + } + } + + newCommits = append(newCommits, &UserCommit{ + User: u, + Commit: c, + }) + } + return newCommits +} + +// GetUserByEmail returns the user object by given e-mail if exists. +func GetUserByEmail(email string) (*User, error) { + return GetUserByEmailContext(db.DefaultContext, email) +} + +// GetUserByEmailContext returns the user object by given e-mail if exists with db context +func GetUserByEmailContext(ctx context.Context, email string) (*User, error) { + if len(email) == 0 { + return nil, ErrUserNotExist{0, email, 0} + } + + email = strings.ToLower(email) + // First try to find the user by primary email + user := &User{Email: email} + has, err := db.GetEngine(ctx).Get(user) + if err != nil { + return nil, err + } + if has { + return user, nil + } + + // Otherwise, check in alternative list for activated email addresses + emailAddress := &EmailAddress{Email: email, IsActivated: true} + has, err = db.GetEngine(ctx).Get(emailAddress) + if err != nil { + return nil, err + } + if has { + return GetUserByIDCtx(ctx, emailAddress.UID) + } + + // Finally, if email address is the protected email address: + if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) { + username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) + user := &User{} + has, err := db.GetEngine(ctx).Where("lower_name=?", username).Get(user) + if err != nil { + return nil, err + } + if has { + return user, nil + } + } + + return nil, ErrUserNotExist{0, email, 0} +} + +// GetUser checks if a user already exists +func GetUser(user *User) (bool, error) { + return db.GetEngine(db.DefaultContext).Get(user) +} + +// GetUserByOpenID returns the user object by given OpenID if exists. +func GetUserByOpenID(uri string) (*User, error) { + if len(uri) == 0 { + return nil, ErrUserNotExist{0, uri, 0} + } + + uri, err := openid.Normalize(uri) + if err != nil { + return nil, err + } + + log.Trace("Normalized OpenID URI: " + uri) + + // Otherwise, check in openid table + oid := &UserOpenID{} + has, err := db.GetEngine(db.DefaultContext).Where("uri=?", uri).Get(oid) + if err != nil { + return nil, err + } + if has { + return GetUserByID(oid.UID) + } + + return nil, ErrUserNotExist{0, uri, 0} +} + +// GetAdminUser returns the first administrator +func GetAdminUser() (*User, error) { + var admin User + has, err := db.GetEngine(db.DefaultContext).Where("is_admin=?", true).Get(&admin) + if err != nil { + return nil, err + } else if !has { + return nil, ErrUserNotExist{} + } + + return &admin, nil +} diff --git a/models/user/user_test.go b/models/user/user_test.go new file mode 100644 index 0000000000..ac49852254 --- /dev/null +++ b/models/user/user_test.go @@ -0,0 +1,355 @@ +// 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 ( + "math/rand" + "strings" + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/login" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + + "github.com/stretchr/testify/assert" +) + +func TestOAuth2Application_LoadUser(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + app := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: 1}).(*login.OAuth2Application) + user, err := GetUserByID(app.UID) + assert.NoError(t, err) + assert.NotNil(t, user) +} + +func TestGetUserEmailsByNames(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // ignore none active user email + assert.Equal(t, []string{"user8@example.com"}, GetUserEmailsByNames([]string{"user8", "user9"})) + assert.Equal(t, []string{"user8@example.com", "user5@example.com"}, GetUserEmailsByNames([]string{"user8", "user5"})) + + assert.Equal(t, []string{"user8@example.com"}, GetUserEmailsByNames([]string{"user8", "user7"})) +} + +func TestCanCreateOrganization(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + assert.True(t, admin.CanCreateOrganization()) + + user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + assert.True(t, user.CanCreateOrganization()) + // Disable user create organization permission. + user.AllowCreateOrganization = false + assert.False(t, user.CanCreateOrganization()) + + setting.Admin.DisableRegularOrgCreation = true + user.AllowCreateOrganization = true + assert.True(t, admin.CanCreateOrganization()) + assert.False(t, user.CanCreateOrganization()) +} + +func TestSearchUsers(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + testSuccess := func(opts *SearchUserOptions, expectedUserOrOrgIDs []int64) { + users, _, err := SearchUsers(opts) + assert.NoError(t, err) + if assert.Len(t, users, len(expectedUserOrOrgIDs), opts) { + for i, expectedID := range expectedUserOrOrgIDs { + assert.EqualValues(t, expectedID, users[i].ID) + } + } + } + + // test orgs + testOrgSuccess := func(opts *SearchUserOptions, expectedOrgIDs []int64) { + opts.Type = UserTypeOrganization + testSuccess(opts, expectedOrgIDs) + } + + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}}, + []int64{3, 6}) + + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}}, + []int64{7, 17}) + + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}}, + []int64{19, 25}) + + testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}}, + []int64{26}) + + testOrgSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 5, PageSize: 2}}, + []int64{}) + + // test users + testUserSuccess := func(opts *SearchUserOptions, expectedUserIDs []int64) { + opts.Type = UserTypeIndividual + testSuccess(opts, expectedUserIDs) + } + + testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32}) + + testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse}, + []int64{9}) + + testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29, 30, 32}) + + testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) + + // order by name asc default + testUserSuccess(&SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, + []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) + + testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: util.OptionalBoolTrue}, + []int64{1}) + + testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: util.OptionalBoolTrue}, + []int64{29, 30}) + + testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: util.OptionalBoolTrue}, + []int64{30}) + + testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: util.OptionalBoolTrue}, + []int64{24}) +} + +func TestEmailNotificationPreferences(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + for _, test := range []struct { + expected string + userID int64 + }{ + {EmailNotificationsEnabled, 1}, + {EmailNotificationsEnabled, 2}, + {EmailNotificationsOnMention, 3}, + {EmailNotificationsOnMention, 4}, + {EmailNotificationsEnabled, 5}, + {EmailNotificationsEnabled, 6}, + {EmailNotificationsDisabled, 7}, + {EmailNotificationsEnabled, 8}, + {EmailNotificationsOnMention, 9}, + } { + user := unittest.AssertExistsAndLoadBean(t, &User{ID: test.userID}).(*User) + assert.Equal(t, test.expected, user.EmailNotifications()) + + // Try all possible settings + assert.NoError(t, SetEmailNotifications(user, EmailNotificationsEnabled)) + assert.Equal(t, EmailNotificationsEnabled, user.EmailNotifications()) + + assert.NoError(t, SetEmailNotifications(user, EmailNotificationsOnMention)) + assert.Equal(t, EmailNotificationsOnMention, user.EmailNotifications()) + + assert.NoError(t, SetEmailNotifications(user, EmailNotificationsDisabled)) + assert.Equal(t, EmailNotificationsDisabled, user.EmailNotifications()) + } +} + +func TestHashPasswordDeterministic(t *testing.T) { + b := make([]byte, 16) + u := &User{} + algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"} + for j := 0; j < len(algos); j++ { + u.PasswdHashAlgo = algos[j] + for i := 0; i < 50; i++ { + // generate a random password + rand.Read(b) + pass := string(b) + + // save the current password in the user - hash it and store the result + u.SetPassword(pass) + r1 := u.Passwd + + // run again + u.SetPassword(pass) + r2 := u.Passwd + + assert.NotEqual(t, r1, r2) + assert.True(t, u.ValidatePassword(pass)) + } + } +} + +func BenchmarkHashPassword(b *testing.B) { + // BenchmarkHashPassword ensures that it takes a reasonable amount of time + // to hash a password - in order to protect from brute-force attacks. + pass := "password1337" + u := &User{Passwd: pass} + b.ResetTimer() + for i := 0; i < b.N; i++ { + u.SetPassword(pass) + } +} + +func TestNewGitSig(t *testing.T) { + users := make([]*User, 0, 20) + err := db.GetEngine(db.DefaultContext).Find(&users) + assert.NoError(t, err) + + for _, user := range users { + sig := user.NewGitSig() + assert.NotContains(t, sig.Name, "<") + assert.NotContains(t, sig.Name, ">") + assert.NotContains(t, sig.Name, "\n") + assert.NotEqual(t, len(strings.TrimSpace(sig.Name)), 0) + } +} + +func TestDisplayName(t *testing.T) { + users := make([]*User, 0, 20) + err := db.GetEngine(db.DefaultContext).Find(&users) + assert.NoError(t, err) + + for _, user := range users { + displayName := user.DisplayName() + assert.Equal(t, strings.TrimSpace(displayName), displayName) + if len(strings.TrimSpace(user.FullName)) == 0 { + assert.Equal(t, user.Name, displayName) + } + assert.NotEqual(t, len(strings.TrimSpace(displayName)), 0) + } +} + +func TestCreateUserInvalidEmail(t *testing.T) { + user := &User{ + Name: "GiteaBot", + Email: "GiteaBot@gitea.io\r\n", + Passwd: ";p['////..-++']", + IsAdmin: false, + Theme: setting.UI.DefaultTheme, + MustChangePassword: false, + } + + err := CreateUser(user) + assert.Error(t, err) + assert.True(t, IsErrEmailInvalid(err)) +} + +func TestGetUserIDsByNames(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // ignore non existing + IDs, err := GetUserIDsByNames([]string{"user1", "user2", "none_existing_user"}, true) + assert.NoError(t, err) + assert.Equal(t, []int64{1, 2}, IDs) + + // ignore non existing + IDs, err = GetUserIDsByNames([]string{"user1", "do_not_exist"}, false) + assert.Error(t, err) + assert.Equal(t, []int64(nil), IDs) +} + +func TestGetMaileableUsersByIDs(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + results, err := GetMaileableUsersByIDs([]int64{1, 4}, false) + assert.NoError(t, err) + assert.Len(t, results, 1) + if len(results) > 1 { + assert.Equal(t, results[0].ID, 1) + } + + results, err = GetMaileableUsersByIDs([]int64{1, 4}, true) + assert.NoError(t, err) + assert.Len(t, results, 2) + if len(results) > 2 { + assert.Equal(t, results[0].ID, 1) + assert.Equal(t, results[1].ID, 4) + } +} + +func TestUpdateUser(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + + user.KeepActivityPrivate = true + assert.NoError(t, UpdateUser(user)) + user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + assert.True(t, user.KeepActivityPrivate) + + setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false} + user.KeepActivityPrivate = false + user.Visibility = structs.VisibleTypePrivate + assert.Error(t, UpdateUser(user)) + user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + assert.True(t, user.KeepActivityPrivate) + + user.Email = "no mail@mail.org" + assert.Error(t, UpdateUser(user)) +} + +func TestNewUserRedirect(t *testing.T) { + // redirect to a completely new name + assert.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + assert.NoError(t, NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + LowerName: user.LowerName, + RedirectUserID: user.ID, + }) + unittest.AssertExistsAndLoadBean(t, &Redirect{ + LowerName: "olduser1", + RedirectUserID: user.ID, + }) +} + +func TestNewUserRedirect2(t *testing.T) { + // redirect to previously used name + assert.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) + assert.NoError(t, NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + LowerName: user.LowerName, + RedirectUserID: user.ID, + }) + unittest.AssertNotExistsBean(t, &Redirect{ + LowerName: "olduser1", + RedirectUserID: user.ID, + }) +} + +func TestNewUserRedirect3(t *testing.T) { + // redirect for a previously-unredirected user + assert.NoError(t, unittest.PrepareTestDatabase()) + + user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + assert.NoError(t, NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) + + unittest.AssertExistsAndLoadBean(t, &Redirect{ + LowerName: user.LowerName, + RedirectUserID: user.ID, + }) +} + +func TestGetUserByOpenID(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + _, err := GetUserByOpenID("https://unknown") + if assert.Error(t, err) { + assert.True(t, IsErrUserNotExist(err)) + } + + user, err := GetUserByOpenID("https://user1.domain1.tld") + if assert.NoError(t, err) { + assert.Equal(t, int64(1), user.ID) + } + + user, err = GetUserByOpenID("https://domain1.tld/user2/") + if assert.NoError(t, err) { + assert.Equal(t, int64(2), user.ID) + } +} diff --git a/models/user_email.go b/models/user_email.go index 8c5fbf6d6a..528ebb0540 100644 --- a/models/user_email.go +++ b/models/user_email.go @@ -31,18 +31,18 @@ func ActivateEmail(email *user_model.EmailAddress) error { } func updateActivation(e db.Engine, email *user_model.EmailAddress, activate bool) error { - user, err := getUserByID(e, email.UID) + user, err := user_model.GetUserByIDEngine(e, email.UID) if err != nil { return err } - if user.Rands, err = GetUserSalt(); err != nil { + 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 updateUserCols(e, user, "rands") + return user_model.UpdateUserColsEngine(e, user, "rands") } // MakeEmailPrimary sets primary email address of given user. @@ -58,12 +58,16 @@ func MakeEmailPrimary(email *user_model.EmailAddress) error { return user_model.ErrEmailNotActivated } - user := &User{} + user := &user_model.User{} has, err = db.GetEngine(db.DefaultContext).ID(email.UID).Get(user) if err != nil { return err } else if !has { - return ErrUserNotExist{email.UID, "", 0} + return user_model.ErrUserNotExist{ + UID: email.UID, + Name: "", + KeyID: 0, + } } ctx, committer, err := db.TxContext() @@ -99,7 +103,7 @@ func MakeEmailPrimary(email *user_model.EmailAddress) error { func VerifyActiveEmailCode(code, email string) *user_model.EmailAddress { minutes := setting.Service.ActiveCodeLives - if user := getVerifyUser(code); user != nil { + 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) @@ -152,7 +156,7 @@ type SearchEmailResult struct { // 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} + 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( @@ -236,7 +240,7 @@ func ActivateUserEmail(userID int64, email string, activate bool) (err error) { // Activate/deactivate a user's primary email address and account if addr.IsPrimary { - user := User{ID: userID, Email: email} + user := user_model.User{ID: userID, Email: email} if has, err := sess.Get(&user); err != nil { return err } else if !has { @@ -245,10 +249,10 @@ func ActivateUserEmail(userID int64, email string, activate bool) (err error) { // 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 { + if user.Rands, err = user_model.GetUserSalt(); err != nil { return fmt.Errorf("unable to generate salt: %v", err) } - if err = updateUserCols(sess, &user, "is_active", "rands"); err != nil { + 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) } } diff --git a/models/user_email_test.go b/models/user_email_test.go index bed6a18867..a91e24d363 100644 --- a/models/user_email_test.go +++ b/models/user_email_test.go @@ -37,7 +37,7 @@ func TestMakeEmailPrimary(t *testing.T) { } err = MakeEmailPrimary(email) assert.Error(t, err) - assert.True(t, IsErrUserNotExist(err)) + assert.True(t, user_model.IsErrUserNotExist(err)) email = &user_model.EmailAddress{ Email: "user101@example.com", @@ -45,7 +45,7 @@ func TestMakeEmailPrimary(t *testing.T) { err = MakeEmailPrimary(email) assert.NoError(t, err) - user, _ := GetUserByID(int64(10)) + user, _ := user_model.GetUserByID(int64(10)) assert.Equal(t, "user101@example.com", user.Email) } diff --git a/models/user_heatmap.go b/models/user_heatmap.go index 3e94a6f9b7..f331a0c16b 100644 --- a/models/user_heatmap.go +++ b/models/user_heatmap.go @@ -6,6 +6,7 @@ package models import ( "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" ) @@ -17,16 +18,16 @@ type UserHeatmapData struct { } // GetUserHeatmapDataByUser returns an array of UserHeatmapData -func GetUserHeatmapDataByUser(user, doer *User) ([]*UserHeatmapData, error) { +func GetUserHeatmapDataByUser(user, doer *user_model.User) ([]*UserHeatmapData, error) { return getUserHeatmapData(user, nil, doer) } // GetUserHeatmapDataByUserTeam returns an array of UserHeatmapData -func GetUserHeatmapDataByUserTeam(user *User, team *Team, doer *User) ([]*UserHeatmapData, error) { +func GetUserHeatmapDataByUserTeam(user *user_model.User, team *Team, doer *user_model.User) ([]*UserHeatmapData, error) { return getUserHeatmapData(user, team, doer) } -func getUserHeatmapData(user *User, team *Team, doer *User) ([]*UserHeatmapData, error) { +func getUserHeatmapData(user *user_model.User, team *Team, doer *user_model.User) ([]*UserHeatmapData, error) { hdata := make([]*UserHeatmapData, 0) if !activityReadable(user, doer) { diff --git a/models/user_heatmap_test.go b/models/user_heatmap_test.go index 2abbbfc6f3..7d2997648d 100644 --- a/models/user_heatmap_test.go +++ b/models/user_heatmap_test.go @@ -10,6 +10,7 @@ import ( "time" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/timeutil" @@ -46,9 +47,9 @@ func TestGetUserHeatmapDataByUser(t *testing.T) { defer timeutil.Unset() for i, tc := range testCases { - user := unittest.AssertExistsAndLoadBean(t, &User{ID: tc.userID}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID}).(*user_model.User) - doer := &User{ID: tc.doerID} + doer := &user_model.User{ID: tc.doerID} _, err := unittest.LoadBeanIfExists(doer) assert.NoError(t, err) if tc.doerID == 0 { diff --git a/models/user_test.go b/models/user_test.go index cfbd849640..d3c7718d69 100644 --- a/models/user_test.go +++ b/models/user_test.go @@ -6,27 +6,42 @@ package models import ( "fmt" - "math/rand" - "strings" "testing" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/login" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) -func TestOAuth2Application_LoadUser(t *testing.T) { +func TestFollowUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - app := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: 1}).(*login.OAuth2Application) - user, err := GetUserByID(app.UID) - assert.NoError(t, err) - assert.NotNil(t, user) + + testSuccess := func(followerID, followedID int64) { + assert.NoError(t, user_model.FollowUser(followerID, followedID)) + unittest.AssertExistsAndLoadBean(t, &user_model.Follow{UserID: followerID, FollowID: followedID}) + } + testSuccess(4, 2) + testSuccess(5, 2) + + assert.NoError(t, user_model.FollowUser(2, 2)) + + unittest.CheckConsistencyFor(t, &user_model.User{}) +} + +func TestUnfollowUser(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + testSuccess := func(followerID, followedID int64) { + assert.NoError(t, user_model.UnfollowUser(followerID, followedID)) + unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: followerID, FollowID: followedID}) + } + testSuccess(4, 2) + testSuccess(5, 2) + testSuccess(2, 2) + + unittest.CheckConsistencyFor(t, &user_model.User{}) } func TestUserIsPublicMember(t *testing.T) { @@ -50,7 +65,7 @@ func TestUserIsPublicMember(t *testing.T) { } func testUserIsPublicMember(t *testing.T, uid, orgID int64, expected bool) { - user, err := GetUserByID(uid) + user, err := user_model.GetUserByID(uid) assert.NoError(t, err) is, err := IsPublicMembership(orgID, user.ID) assert.NoError(t, err) @@ -78,280 +93,39 @@ func TestIsUserOrgOwner(t *testing.T) { } func testIsUserOrgOwner(t *testing.T, uid, orgID int64, expected bool) { - user, err := GetUserByID(uid) + user, err := user_model.GetUserByID(uid) assert.NoError(t, err) is, err := IsOrganizationOwner(orgID, user.ID) assert.NoError(t, err) assert.Equal(t, expected, is) } -func TestGetUserEmailsByNames(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - // ignore none active user email - assert.Equal(t, []string{"user8@example.com"}, GetUserEmailsByNames([]string{"user8", "user9"})) - assert.Equal(t, []string{"user8@example.com", "user5@example.com"}, GetUserEmailsByNames([]string{"user8", "user5"})) - - assert.Equal(t, []string{"user8@example.com"}, GetUserEmailsByNames([]string{"user8", "user7"})) -} - -func TestCanCreateOrganization(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - admin := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - assert.True(t, admin.CanCreateOrganization()) - - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.True(t, user.CanCreateOrganization()) - // Disable user create organization permission. - user.AllowCreateOrganization = false - assert.False(t, user.CanCreateOrganization()) - - setting.Admin.DisableRegularOrgCreation = true - user.AllowCreateOrganization = true - assert.True(t, admin.CanCreateOrganization()) - assert.False(t, user.CanCreateOrganization()) -} - -func TestSearchUsers(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - testSuccess := func(opts *SearchUserOptions, expectedUserOrOrgIDs []int64) { - users, _, err := SearchUsers(opts) - assert.NoError(t, err) - if assert.Len(t, users, len(expectedUserOrOrgIDs)) { - for i, expectedID := range expectedUserOrOrgIDs { - assert.EqualValues(t, expectedID, users[i].ID) - } - } - } - - // test orgs - testOrgSuccess := func(opts *SearchUserOptions, expectedOrgIDs []int64) { - opts.Type = UserTypeOrganization - testSuccess(opts, expectedOrgIDs) - } - - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}}, - []int64{3, 6}) - - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}}, - []int64{7, 17}) - - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}}, - []int64{19, 25}) - - testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}}, - []int64{26}) - - testOrgSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 5, PageSize: 2}}, - []int64{}) - - // test users - testUserSuccess := func(opts *SearchUserOptions, expectedUserIDs []int64) { - opts.Type = UserTypeIndividual - testSuccess(opts, expectedUserIDs) - } - - testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32}) - - testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse}, - []int64{9}) - - testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, - []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29, 30, 32}) - - testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, - []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) - - // order by name asc default - testUserSuccess(&SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue}, - []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) - - testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: util.OptionalBoolTrue}, - []int64{1}) - - testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: util.OptionalBoolTrue}, - []int64{29, 30}) - - testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: util.OptionalBoolTrue}, - []int64{30}) - - testUserSuccess(&SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: util.OptionalBoolTrue}, - []int64{24}) -} - -func TestEmailNotificationPreferences(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - for _, test := range []struct { - expected string - userID int64 - }{ - {EmailNotificationsEnabled, 1}, - {EmailNotificationsEnabled, 2}, - {EmailNotificationsOnMention, 3}, - {EmailNotificationsOnMention, 4}, - {EmailNotificationsEnabled, 5}, - {EmailNotificationsEnabled, 6}, - {EmailNotificationsDisabled, 7}, - {EmailNotificationsEnabled, 8}, - {EmailNotificationsOnMention, 9}, - } { - user := unittest.AssertExistsAndLoadBean(t, &User{ID: test.userID}).(*User) - assert.Equal(t, test.expected, user.EmailNotifications()) - - // Try all possible settings - assert.NoError(t, SetEmailNotifications(user, EmailNotificationsEnabled)) - assert.Equal(t, EmailNotificationsEnabled, user.EmailNotifications()) - - assert.NoError(t, SetEmailNotifications(user, EmailNotificationsOnMention)) - assert.Equal(t, EmailNotificationsOnMention, user.EmailNotifications()) - - assert.NoError(t, SetEmailNotifications(user, EmailNotificationsDisabled)) - assert.Equal(t, EmailNotificationsDisabled, user.EmailNotifications()) - } -} - -func TestHashPasswordDeterministic(t *testing.T) { - b := make([]byte, 16) - u := &User{} - algos := []string{"argon2", "pbkdf2", "scrypt", "bcrypt"} - for j := 0; j < len(algos); j++ { - u.PasswdHashAlgo = algos[j] - for i := 0; i < 50; i++ { - // generate a random password - rand.Read(b) - pass := string(b) - - // save the current password in the user - hash it and store the result - u.SetPassword(pass) - r1 := u.Passwd - - // run again - u.SetPassword(pass) - r2 := u.Passwd - - assert.NotEqual(t, r1, r2) - assert.True(t, u.ValidatePassword(pass)) - } - } -} - -func BenchmarkHashPassword(b *testing.B) { - // BenchmarkHashPassword ensures that it takes a reasonable amount of time - // to hash a password - in order to protect from brute-force attacks. - pass := "password1337" - u := &User{Passwd: pass} - b.ResetTimer() - for i := 0; i < b.N; i++ { - u.SetPassword(pass) - } -} - func TestGetOrgRepositoryIDs(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user2 := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - user4 := unittest.AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) - user5 := unittest.AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) + user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) - accessibleRepos, err := user2.GetOrgRepositoryIDs() + accessibleRepos, err := GetOrgRepositoryIDs(user2) assert.NoError(t, err) // User 2's team has access to private repos 3, 5, repo 32 is a public repo of the organization assert.Equal(t, []int64{3, 5, 23, 24, 32}, accessibleRepos) - accessibleRepos, err = user4.GetOrgRepositoryIDs() + accessibleRepos, err = GetOrgRepositoryIDs(user4) assert.NoError(t, err) // User 4's team has access to private repo 3, repo 32 is a public repo of the organization assert.Equal(t, []int64{3, 32}, accessibleRepos) - accessibleRepos, err = user5.GetOrgRepositoryIDs() + accessibleRepos, err = GetOrgRepositoryIDs(user5) assert.NoError(t, err) // User 5's team has no access to any repo assert.Len(t, accessibleRepos, 0) } -func TestNewGitSig(t *testing.T) { - users := make([]*User, 0, 20) - err := db.GetEngine(db.DefaultContext).Find(&users) - assert.NoError(t, err) - - for _, user := range users { - sig := user.NewGitSig() - assert.NotContains(t, sig.Name, "<") - assert.NotContains(t, sig.Name, ">") - assert.NotContains(t, sig.Name, "\n") - assert.NotEqual(t, len(strings.TrimSpace(sig.Name)), 0) - } -} - -func TestDisplayName(t *testing.T) { - users := make([]*User, 0, 20) - err := db.GetEngine(db.DefaultContext).Find(&users) - assert.NoError(t, err) - - for _, user := range users { - displayName := user.DisplayName() - assert.Equal(t, strings.TrimSpace(displayName), displayName) - if len(strings.TrimSpace(user.FullName)) == 0 { - assert.Equal(t, user.Name, displayName) - } - assert.NotEqual(t, len(strings.TrimSpace(displayName)), 0) - } -} - -func TestCreateUserInvalidEmail(t *testing.T) { - user := &User{ - Name: "GiteaBot", - Email: "GiteaBot@gitea.io\r\n", - Passwd: ";p['////..-++']", - IsAdmin: false, - Theme: setting.UI.DefaultTheme, - MustChangePassword: false, - } - - err := CreateUser(user) - assert.Error(t, err) - assert.True(t, user_model.IsErrEmailInvalid(err)) -} - -func TestGetUserIDsByNames(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - // ignore non existing - IDs, err := GetUserIDsByNames([]string{"user1", "user2", "none_existing_user"}, true) - assert.NoError(t, err) - assert.Equal(t, []int64{1, 2}, IDs) - - // ignore non existing - IDs, err = GetUserIDsByNames([]string{"user1", "do_not_exist"}, false) - assert.Error(t, err) - assert.Equal(t, []int64(nil), IDs) -} - -func TestGetMaileableUsersByIDs(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - results, err := GetMaileableUsersByIDs([]int64{1, 4}, false) - assert.NoError(t, err) - assert.Len(t, results, 1) - if len(results) > 1 { - assert.Equal(t, results[0].ID, 1) - } - - results, err = GetMaileableUsersByIDs([]int64{1, 4}, true) - assert.NoError(t, err) - assert.Len(t, results, 2) - if len(results) > 2 { - assert.Equal(t, results[0].ID, 1) - assert.Equal(t, results[1].ID, 4) - } -} - func TestAddLdapSSHPublicKeys(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) s := &login.Source{ID: 1} testCases := []struct { @@ -415,118 +189,3 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib } } } - -func TestUpdateUser(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - - user.KeepActivityPrivate = true - assert.NoError(t, UpdateUser(user)) - user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.True(t, user.KeepActivityPrivate) - - setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false} - user.KeepActivityPrivate = false - user.Visibility = structs.VisibleTypePrivate - assert.Error(t, UpdateUser(user)) - user = unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.True(t, user.KeepActivityPrivate) - - user.Email = "no mail@mail.org" - assert.Error(t, UpdateUser(user)) -} - -func TestNewUserRedirect(t *testing.T) { - // redirect to a completely new name - assert.NoError(t, unittest.PrepareTestDatabase()) - - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) - - unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{ - LowerName: user.LowerName, - RedirectUserID: user.ID, - }) - unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{ - LowerName: "olduser1", - RedirectUserID: user.ID, - }) -} - -func TestNewUserRedirect2(t *testing.T) { - // redirect to previously used name - assert.NoError(t, unittest.PrepareTestDatabase()) - - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 1}).(*User) - assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1")) - - unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{ - LowerName: user.LowerName, - RedirectUserID: user.ID, - }) - unittest.AssertNotExistsBean(t, &user_model.Redirect{ - LowerName: "olduser1", - RedirectUserID: user.ID, - }) -} - -func TestNewUserRedirect3(t *testing.T) { - // redirect for a previously-unredirected user - assert.NoError(t, unittest.PrepareTestDatabase()) - - user := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) - assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername")) - - unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{ - LowerName: user.LowerName, - RedirectUserID: user.ID, - }) -} - -func TestFollowUser(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - testSuccess := func(followerID, followedID int64) { - assert.NoError(t, user_model.FollowUser(followerID, followedID)) - unittest.AssertExistsAndLoadBean(t, &user_model.Follow{UserID: followerID, FollowID: followedID}) - } - testSuccess(4, 2) - testSuccess(5, 2) - - assert.NoError(t, user_model.FollowUser(2, 2)) - - unittest.CheckConsistencyFor(t, &User{}) -} - -func TestUnfollowUser(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - testSuccess := func(followerID, followedID int64) { - assert.NoError(t, user_model.UnfollowUser(followerID, followedID)) - unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: followerID, FollowID: followedID}) - } - testSuccess(4, 2) - testSuccess(5, 2) - testSuccess(2, 2) - - unittest.CheckConsistencyFor(t, &User{}) -} - -func TestGetUserByOpenID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - _, err := GetUserByOpenID("https://unknown") - if assert.Error(t, err) { - assert.True(t, IsErrUserNotExist(err)) - } - - user, err := GetUserByOpenID("https://user1.domain1.tld") - if assert.NoError(t, err) { - assert.Equal(t, int64(1), user.ID) - } - - user, err = GetUserByOpenID("https://domain1.tld/user2/") - if assert.NoError(t, err) { - assert.Equal(t, int64(2), user.ID) - } -} diff --git a/models/userlist.go b/models/userlist.go index aebdb4f48c..ae39de3da4 100644 --- a/models/userlist.go +++ b/models/userlist.go @@ -9,12 +9,13 @@ import ( "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 +type UserList []*user_model.User func (users UserList) getUserIDs() []int64 { userIDs := make([]int64, len(users)) @@ -95,3 +96,15 @@ func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFact } 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/wiki.go b/models/wiki.go index beab48d45a..f80696542e 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" ) @@ -20,7 +21,7 @@ func (repo *Repository) WikiCloneLink() *CloneLink { // WikiPath returns wiki data path by given user and repository name. func WikiPath(userName, repoName string) string { - return filepath.Join(UserPath(userName), strings.ToLower(repoName)+".wiki.git") + return filepath.Join(user_model.UserPath(userName), strings.ToLower(repoName)+".wiki.git") } // WikiPath returns wiki data path for given repository. |