diff --git a/cmd/admin.go b/cmd/admin.go
index b4fc7f57067..d11b824fa12 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -16,6 +16,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
@@ -358,7 +359,7 @@ func runChangePassword(c *cli.Context) error {
 		return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
 	}
 	uname := c.String("username")
-	user, err := models.GetUserByName(uname)
+	user, err := user_model.GetUserByName(uname)
 	if err != nil {
 		return err
 	}
@@ -366,7 +367,7 @@ func runChangePassword(c *cli.Context) error {
 		return err
 	}
 
-	if err = models.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
+	if err = user_model.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
 		return err
 	}
 
@@ -424,7 +425,7 @@ func runCreateUser(c *cli.Context) error {
 
 	// If this is the first user being created.
 	// Take it as the admin and don't force a password update.
-	if n := models.CountUsers(); n == 0 {
+	if n := user_model.CountUsers(); n == 0 {
 		changePassword = false
 	}
 
@@ -432,7 +433,7 @@ func runCreateUser(c *cli.Context) error {
 		changePassword = c.Bool("must-change-password")
 	}
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:               username,
 		Email:              c.String("email"),
 		Passwd:             password,
@@ -442,7 +443,7 @@ func runCreateUser(c *cli.Context) error {
 		Theme:              setting.UI.DefaultTheme,
 	}
 
-	if err := models.CreateUser(u); err != nil {
+	if err := user_model.CreateUser(u); err != nil {
 		return fmt.Errorf("CreateUser: %v", err)
 	}
 
@@ -471,7 +472,7 @@ func runListUsers(c *cli.Context) error {
 		return err
 	}
 
-	users, err := models.GetAllUsers()
+	users, err := user_model.GetAllUsers()
 
 	if err != nil {
 		return err
@@ -516,13 +517,13 @@ func runDeleteUser(c *cli.Context) error {
 	}
 
 	var err error
-	var user *models.User
+	var user *user_model.User
 	if c.IsSet("email") {
-		user, err = models.GetUserByEmail(c.String("email"))
+		user, err = user_model.GetUserByEmail(c.String("email"))
 	} else if c.IsSet("username") {
-		user, err = models.GetUserByName(c.String("username"))
+		user, err = user_model.GetUserByName(c.String("username"))
 	} else {
-		user, err = models.GetUserByID(c.Int64("id"))
+		user, err = user_model.GetUserByID(c.Int64("id"))
 	}
 	if err != nil {
 		return err
diff --git a/cmd/migrate_storage.go b/cmd/migrate_storage.go
index 9f1d9057f9c..afb0fc02a3a 100644
--- a/cmd/migrate_storage.go
+++ b/cmd/migrate_storage.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/migrations"
 	repo_model "code.gitea.io/gitea/models/repo"
+	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/storage"
@@ -94,7 +95,7 @@ func migrateLFS(dstStorage storage.ObjectStorage) error {
 }
 
 func migrateAvatars(dstStorage storage.ObjectStorage) error {
-	return models.IterateUser(func(user *models.User) error {
+	return user_model.IterateUser(func(user *user_model.User) error {
 		_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
 		return err
 	})
diff --git a/integrations/admin_user_test.go b/integrations/admin_user_test.go
index f9e9afac34c..d657f65fa55 100644
--- a/integrations/admin_user_test.go
+++ b/integrations/admin_user_test.go
@@ -9,8 +9,8 @@ import (
 	"strconv"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -42,14 +42,14 @@ func TestAdminViewUser(t *testing.T) {
 func TestAdminEditUser(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	testSuccessfullEdit(t, models.User{ID: 2, Name: "newusername", LoginName: "otherlogin", Email: "new@e-mail.gitea"})
+	testSuccessfullEdit(t, user_model.User{ID: 2, Name: "newusername", LoginName: "otherlogin", Email: "new@e-mail.gitea"})
 }
 
-func testSuccessfullEdit(t *testing.T, formData models.User) {
+func testSuccessfullEdit(t *testing.T, formData user_model.User) {
 	makeRequest(t, formData, http.StatusFound)
 }
 
-func makeRequest(t *testing.T, formData models.User, headerCode int) {
+func makeRequest(t *testing.T, formData user_model.User, headerCode int) {
 	session := loginUser(t, "user1")
 	csrf := GetCSRF(t, session, "/admin/users/"+strconv.Itoa(int(formData.ID)))
 	req := NewRequestWithValues(t, "POST", "/admin/users/"+strconv.Itoa(int(formData.ID)), map[string]string{
@@ -61,7 +61,7 @@ func makeRequest(t *testing.T, formData models.User, headerCode int) {
 	})
 
 	session.MakeRequest(t, req, headerCode)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: formData.ID}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: formData.ID}).(*user_model.User)
 	assert.Equal(t, formData.Name, user.Name)
 	assert.Equal(t, formData.LoginName, user.LoginName)
 	assert.Equal(t, formData.Email, user.Email)
@@ -79,5 +79,5 @@ func TestAdminDeleteUser(t *testing.T) {
 	session.MakeRequest(t, req, http.StatusOK)
 
 	assertUserDeleted(t, 8)
-	unittest.CheckConsistencyFor(t, &models.User{})
+	unittest.CheckConsistencyFor(t, &user_model.User{})
 }
diff --git a/integrations/api_admin_org_test.go b/integrations/api_admin_org_test.go
index a4ef0ed5ef3..4b5e2481e65 100644
--- a/integrations/api_admin_org_test.go
+++ b/integrations/api_admin_org_test.go
@@ -10,8 +10,8 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -43,7 +43,7 @@ func TestAPIAdminOrgCreate(t *testing.T) {
 		assert.Equal(t, org.Location, apiOrg.Location)
 		assert.Equal(t, org.Visibility, apiOrg.Visibility)
 
-		unittest.AssertExistsAndLoadBean(t, &models.User{
+		unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name:      org.UserName,
 			LowerName: strings.ToLower(org.UserName),
 			FullName:  org.FullName,
diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go
index 86a3784b175..98270ed5875 100644
--- a/integrations/api_admin_test.go
+++ b/integrations/api_admin_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/json"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -21,7 +22,7 @@ func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) {
 	defer prepareTestEnv(t)()
 	// user1 is an admin user
 	session := loginUser(t, "user1")
-	keyOwner := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
+	keyOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}).(*user_model.User)
 
 	token := getTokenForLoggedInUser(t, session)
 	urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys?token=%s", keyOwner.Name, token)
@@ -128,7 +129,7 @@ func TestAPIListUsers(t *testing.T) {
 		}
 	}
 	assert.True(t, found)
-	numberOfUsers := unittest.GetCount(t, &models.User{}, "type = 0")
+	numberOfUsers := unittest.GetCount(t, &user_model.User{}, "type = 0")
 	assert.Equal(t, numberOfUsers, len(users))
 }
 
@@ -194,7 +195,7 @@ func TestAPIEditUser(t *testing.T) {
 	json.Unmarshal(resp.Body.Bytes(), &errMap)
 	assert.EqualValues(t, "email is not allowed to be empty string", errMap["message"].(string))
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{LoginName: "user2"}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}).(*user_model.User)
 	assert.False(t, user2.IsRestricted)
 	bTrue := true
 	req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
@@ -205,6 +206,6 @@ func TestAPIEditUser(t *testing.T) {
 		Restricted: &bTrue,
 	})
 	session.MakeRequest(t, req, http.StatusOK)
-	user2 = unittest.AssertExistsAndLoadBean(t, &models.User{LoginName: "user2"}).(*models.User)
+	user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}).(*user_model.User)
 	assert.True(t, user2.IsRestricted)
 }
diff --git a/integrations/api_comment_test.go b/integrations/api_comment_test.go
index cf26fa0e16a..9ab97c0f40d 100644
--- a/integrations/api_comment_test.go
+++ b/integrations/api_comment_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -25,7 +26,7 @@ func TestAPIListRepoComments(t *testing.T) {
 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments", repoOwner.Name, repo.Name))
@@ -71,7 +72,7 @@ func TestAPIListIssueComments(t *testing.T) {
 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/issues/%d/comments",
@@ -91,7 +92,7 @@ func TestAPICreateComment(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{}).(*models.Issue)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -114,7 +115,7 @@ func TestAPIGetComment(t *testing.T) {
 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment)
 	assert.NoError(t, comment.LoadIssue())
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: comment.Issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -143,7 +144,7 @@ func TestAPIEditComment(t *testing.T) {
 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -168,7 +169,7 @@ func TestAPIDeleteComment(t *testing.T) {
 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issue.RepoID}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_issue_label_test.go b/integrations/api_issue_label_test.go
index 9e569c85a2b..9ee8b75b508 100644
--- a/integrations/api_issue_label_test.go
+++ b/integrations/api_issue_label_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -21,7 +22,7 @@ func TestAPIModifyLabels(t *testing.T) {
 	assert.NoError(t, unittest.LoadFixtures())
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 2}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/labels?token=%s", owner.Name, repo.Name, token)
@@ -93,7 +94,7 @@ func TestAPIAddIssueLabels(t *testing.T) {
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue)
 	_ = unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID, ID: 2}).(*models.Label)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -116,7 +117,7 @@ func TestAPIReplaceIssueLabels(t *testing.T) {
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue)
 	label := unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID}).(*models.Label)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -140,7 +141,7 @@ func TestAPIModifyOrgLabels(t *testing.T) {
 	assert.NoError(t, unittest.LoadFixtures())
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	user := "user1"
 	session := loginUser(t, user)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_issue_milestone_test.go b/integrations/api_issue_milestone_test.go
index 1ac3fb2ed83..6a94321d3b1 100644
--- a/integrations/api_issue_milestone_test.go
+++ b/integrations/api_issue_milestone_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -21,7 +22,7 @@ func TestAPIIssuesMilestone(t *testing.T) {
 
 	milestone := unittest.AssertExistsAndLoadBean(t, &models.Milestone{ID: 1}).(*models.Milestone)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: milestone.RepoID}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	assert.Equal(t, int64(1), int64(milestone.NumIssues))
 	assert.Equal(t, structs.StateOpen, milestone.State())
 
diff --git a/integrations/api_issue_reaction_test.go b/integrations/api_issue_reaction_test.go
index 7b3632ca672..fa0f016ef2c 100644
--- a/integrations/api_issue_reaction_test.go
+++ b/integrations/api_issue_reaction_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -23,12 +24,12 @@ func TestAPIIssuesReactions(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
 	_ = issue.LoadRepo()
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/reactions?token=%s",
 		owner.Name, issue.Repo.Name, issue.Index, token)
 
@@ -82,13 +83,13 @@ func TestAPICommentReactions(t *testing.T) {
 	_ = comment.LoadIssue()
 	issue := comment.Issue
 	_ = issue.LoadRepo()
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
 
-	user1 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.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)
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/reactions?token=%s",
 		owner.Name, issue.Repo.Name, comment.ID, token)
 
diff --git a/integrations/api_issue_stopwatch_test.go b/integrations/api_issue_stopwatch_test.go
index 92822ed8117..8b9af3f0c0b 100644
--- a/integrations/api_issue_stopwatch_test.go
+++ b/integrations/api_issue_stopwatch_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -19,7 +20,7 @@ func TestAPIListStopWatches(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -44,8 +45,8 @@ func TestAPIStopStopWatches(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
 	_ = issue.LoadRepo()
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -60,8 +61,8 @@ func TestAPICancelStopWatches(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue)
 	_ = issue.LoadRepo()
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -76,8 +77,8 @@ func TestAPIStartStopWatches(t *testing.T) {
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue)
 	_ = issue.LoadRepo()
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue.Repo.OwnerID}).(*models.User)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_issue_subscription_test.go b/integrations/api_issue_subscription_test.go
index dd8e4311496..18ff4159b68 100644
--- a/integrations/api_issue_subscription_test.go
+++ b/integrations/api_issue_subscription_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -25,7 +26,7 @@ func TestAPIIssueSubscriptions(t *testing.T) {
 	issue4 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 4}).(*models.Issue)
 	issue5 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 8}).(*models.Issue)
 
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: issue1.PosterID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_issue_test.go b/integrations/api_issue_test.go
index a46cba59909..87bec7a8f3e 100644
--- a/integrations/api_issue_test.go
+++ b/integrations/api_issue_test.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -22,7 +23,7 @@ func TestAPIListIssues(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -73,7 +74,7 @@ func TestAPICreateIssue(t *testing.T) {
 	const body, title = "apiTestBody", "apiTestTitle"
 
 	repoBefore := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -106,7 +107,7 @@ func TestAPIEditIssue(t *testing.T) {
 
 	issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
 	repoBefore := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
 	assert.NoError(t, issueBefore.LoadAttributes())
 	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
 	assert.Equal(t, api.StateOpen, issueBefore.State())
diff --git a/integrations/api_issue_tracked_time_test.go b/integrations/api_issue_tracked_time_test.go
index bcaa0213c8e..94e569113c1 100644
--- a/integrations/api_issue_tracked_time_test.go
+++ b/integrations/api_issue_tracked_time_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -20,7 +21,7 @@ import (
 func TestAPIGetTrackedTimes(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
 	assert.NoError(t, issue2.LoadRepo())
 
@@ -41,7 +42,7 @@ func TestAPIGetTrackedTimes(t *testing.T) {
 		assert.EqualValues(t, issue2.ID, apiTimes[i].IssueID)
 		assert.Equal(t, time.Created.Unix(), apiTimes[i].Created.Unix())
 		assert.Equal(t, time.Time, apiTimes[i].Time)
-		user, err := models.GetUserByID(time.UserID)
+		user, err := user_model.GetUserByID(time.UserID)
 		assert.NoError(t, err)
 		assert.Equal(t, user.Name, apiTimes[i].UserName)
 	}
@@ -65,7 +66,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) {
 	time6 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 6}).(*models.TrackedTime)
 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
 	assert.NoError(t, issue2.LoadRepo())
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	session := loginUser(t, user2.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -99,8 +100,8 @@ func TestAPIAddTrackedTimes(t *testing.T) {
 
 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
 	assert.NoError(t, issue2.LoadRepo())
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
-	admin := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	session := loginUser(t, admin.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_keys_test.go b/integrations/api_keys_test.go
index 244ece56cc5..7c82bfbb69c 100644
--- a/integrations/api_keys_test.go
+++ b/integrations/api_keys_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -47,7 +48,7 @@ func TestDeleteDeployKeyNoLogin(t *testing.T) {
 func TestCreateReadOnlyDeployKey(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{Name: "repo1"}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -73,7 +74,7 @@ func TestCreateReadOnlyDeployKey(t *testing.T) {
 func TestCreateReadWriteDeployKey(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{Name: "repo1"}).(*models.Repository)
-	repoOwner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, repoOwner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -97,7 +98,7 @@ func TestCreateReadWriteDeployKey(t *testing.T) {
 
 func TestCreateUserKey(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}).(*user_model.User)
 
 	session := loginUser(t, "user1")
 	token := url.QueryEscape(getTokenForLoggedInUser(t, session))
diff --git a/integrations/api_notification_test.go b/integrations/api_notification_test.go
index 24d17173ac3..182e9829cb8 100644
--- a/integrations/api_notification_test.go
+++ b/integrations/api_notification_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -19,7 +20,7 @@ import (
 func TestAPINotification(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
 	assert.NoError(t, thread5.LoadAttributes())
diff --git a/integrations/api_oauth2_apps_test.go b/integrations/api_oauth2_apps_test.go
index d84477ace5c..52922b98485 100644
--- a/integrations/api_oauth2_apps_test.go
+++ b/integrations/api_oauth2_apps_test.go
@@ -9,9 +9,9 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -27,7 +27,7 @@ func TestOAuth2Application(t *testing.T) {
 }
 
 func testAPICreateOAuth2Application(t *testing.T) {
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	appBody := api.CreateOAuth2ApplicationOptions{
 		Name: "test-app-1",
 		RedirectURIs: []string{
@@ -51,7 +51,7 @@ func testAPICreateOAuth2Application(t *testing.T) {
 }
 
 func testAPIListOAuth2Applications(t *testing.T) {
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -80,7 +80,7 @@ func testAPIListOAuth2Applications(t *testing.T) {
 }
 
 func testAPIDeleteOAuth2Application(t *testing.T) {
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -101,7 +101,7 @@ func testAPIDeleteOAuth2Application(t *testing.T) {
 }
 
 func testAPIGetOAuth2Application(t *testing.T) {
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -131,7 +131,7 @@ func testAPIGetOAuth2Application(t *testing.T) {
 }
 
 func testAPIUpdateOAuth2Application(t *testing.T) {
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	existApp := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{
 		UID:  user.ID,
diff --git a/integrations/api_org_test.go b/integrations/api_org_test.go
index ced78d7ee95..76979c674dc 100644
--- a/integrations/api_org_test.go
+++ b/integrations/api_org_test.go
@@ -10,8 +10,8 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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"
 
@@ -44,7 +44,7 @@ func TestAPIOrgCreate(t *testing.T) {
 		assert.Equal(t, org.Location, apiOrg.Location)
 		assert.Equal(t, org.Visibility, apiOrg.Visibility)
 
-		unittest.AssertExistsAndLoadBean(t, &models.User{
+		unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name:      org.UserName,
 			LowerName: strings.ToLower(org.UserName),
 			FullName:  org.FullName,
diff --git a/integrations/api_pull_test.go b/integrations/api_pull_test.go
index 060eb16d89c..db53d415993 100644
--- a/integrations/api_pull_test.go
+++ b/integrations/api_pull_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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/services/forms"
@@ -22,7 +23,7 @@ import (
 func TestAPIViewPulls(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, "user2")
 	token := getTokenForLoggedInUser(t, session)
@@ -39,7 +40,7 @@ func TestAPIViewPulls(t *testing.T) {
 func TestAPIMergePullWIP(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{Status: models.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false)).(*models.PullRequest)
 	pr.LoadIssue()
 	issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)
@@ -65,8 +66,8 @@ func TestAPICreatePullSuccess(t *testing.T) {
 	// repo10 have code, pulls units.
 	repo11 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 11}).(*models.Repository)
 	// repo11 only have code unit but should still create pulls
-	owner10 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo10.OwnerID}).(*models.User)
-	owner11 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo11.OwnerID}).(*models.User)
+	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}).(*user_model.User)
+	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner11.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -83,10 +84,10 @@ func TestAPICreatePullWithFieldsSuccess(t *testing.T) {
 	defer prepareTestEnv(t)()
 	// repo10 have code, pulls units.
 	repo10 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)
-	owner10 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo10.OwnerID}).(*models.User)
+	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}).(*user_model.User)
 	// repo11 only have code unit but should still create pulls
 	repo11 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 11}).(*models.Repository)
-	owner11 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo11.OwnerID}).(*models.User)
+	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner11.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -120,10 +121,10 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) {
 	defer prepareTestEnv(t)()
 	// repo10 have code, pulls units.
 	repo10 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)
-	owner10 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo10.OwnerID}).(*models.User)
+	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}).(*user_model.User)
 	// repo11 only have code unit but should still create pulls
 	repo11 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 11}).(*models.Repository)
-	owner11 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo11.OwnerID}).(*models.User)
+	owner11 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo11.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner11.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -153,7 +154,7 @@ func TestAPICreatePullWithFieldsFailure(t *testing.T) {
 func TestAPIEditPull(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo10 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)
-	owner10 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo10.OwnerID}).(*models.User)
+	owner10 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo10.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner10.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go
index ac2beb7bdf3..5a16e60d735 100644
--- a/integrations/api_releases_test.go
+++ b/integrations/api_releases_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -22,7 +23,7 @@ func TestAPIListReleases(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user2.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -70,7 +71,7 @@ func TestAPIListReleases(t *testing.T) {
 	testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft")
 }
 
-func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *models.User, repo *models.Repository, name, target, title, desc string) *api.Release {
+func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *user_model.User, repo *models.Repository, name, target, title, desc string) *api.Release {
 	urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases?token=%s",
 		owner.Name, repo.Name, token)
 	req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{
@@ -99,7 +100,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -150,7 +151,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -161,7 +162,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -179,7 +180,7 @@ func TestAPIGetReleaseByTag(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 
 	tag := "v1.1"
@@ -212,7 +213,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
diff --git a/integrations/api_repo_archive_test.go b/integrations/api_repo_archive_test.go
index 77940f4c360..6de61eea9f9 100644
--- a/integrations/api_repo_archive_test.go
+++ b/integrations/api_repo_archive_test.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -21,7 +22,7 @@ func TestAPIDownloadArchive(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user2.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
diff --git a/integrations/api_repo_edit_test.go b/integrations/api_repo_edit_test.go
index f862afa2c3f..defa86fca8c 100644
--- a/integrations/api_repo_edit_test.go
+++ b/integrations/api_repo_edit_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	unit_model "code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -134,9 +135,9 @@ func TestAPIRepoEdit(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
 		bFalse, bTrue := false, true
 
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-		user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-		user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+		user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+		user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 		repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 		repo15 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 15}).(*models.Repository) // empty repo
diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go
index 07ddbbbfe6e..5092b8095b8 100644
--- a/integrations/api_repo_file_create_test.go
+++ b/integrations/api_repo_file_create_test.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
@@ -109,7 +110,7 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
 func BenchmarkAPICreateFileSmall(b *testing.B) {
 	onGiteaRunTB(b, func(t testing.TB, u *url.URL) {
 		b := t.(*testing.B)
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)             // owner of the repo1 & repo16
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)     // owner of the repo1 & repo16
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
 
 		for n := 0; n < b.N; n++ {
@@ -124,7 +125,7 @@ func BenchmarkAPICreateFileMedium(b *testing.B) {
 
 	onGiteaRunTB(b, func(t testing.TB, u *url.URL) {
 		b := t.(*testing.B)
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)             // owner of the repo1 & repo16
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)     // owner of the repo1 & repo16
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) // public repo
 
 		b.ResetTimer()
@@ -138,9 +139,9 @@ func BenchmarkAPICreateFileMedium(b *testing.B) {
 
 func TestAPICreateFile(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-		user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-		user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+		user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+		user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 		repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 		repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_file_delete_test.go b/integrations/api_repo_file_delete_test.go
index 93da12700d8..0a87c092428 100644
--- a/integrations/api_repo_file_delete_test.go
+++ b/integrations/api_repo_file_delete_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -38,9 +39,9 @@ func getDeleteFileOptions() *api.DeleteFileOptions {
 
 func TestAPIDeleteFile(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-		user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-		user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+		user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+		user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 		repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 		repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_file_helpers.go b/integrations/api_repo_file_helpers.go
index ef3b8c610bb..8477d65a3ed 100644
--- a/integrations/api_repo_file_helpers.go
+++ b/integrations/api_repo_file_helpers.go
@@ -6,11 +6,12 @@ package integrations
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 	files_service "code.gitea.io/gitea/services/repository/files"
 )
 
-func createFileInBranch(user *models.User, repo *models.Repository, treePath, branchName, content string) (*api.FileResponse, error) {
+func createFileInBranch(user *user_model.User, repo *models.Repository, treePath, branchName, content string) (*api.FileResponse, error) {
 	opts := &files_service.UpdateRepoFileOptions{
 		OldBranch: branchName,
 		TreePath:  treePath,
@@ -22,6 +23,6 @@ func createFileInBranch(user *models.User, repo *models.Repository, treePath, br
 	return files_service.CreateOrUpdateRepoFile(repo, user, opts)
 }
 
-func createFile(user *models.User, repo *models.Repository, treePath string) (*api.FileResponse, error) {
+func createFile(user *user_model.User, repo *models.Repository, treePath string) (*api.FileResponse, error) {
 	return createFileInBranch(user, repo, treePath, repo.DefaultBranch, "This is a NEW file")
 }
diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go
index fb554efe7ab..1fc84d5dea1 100644
--- a/integrations/api_repo_file_update_test.go
+++ b/integrations/api_repo_file_update_test.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
@@ -104,9 +105,9 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
 
 func TestAPIUpdateFile(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-		user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-		user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+		user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+		user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 		repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 		repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_get_contents_list_test.go b/integrations/api_repo_get_contents_list_test.go
index abcfc1fd829..63ac9cfb530 100644
--- a/integrations/api_repo_get_contents_list_test.go
+++ b/integrations/api_repo_get_contents_list_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -53,9 +54,9 @@ func TestAPIGetContentsList(t *testing.T) {
 
 func testAPIGetContentsList(t *testing.T, u *url.URL) {
 	/*** SETUP ***/
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 	repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_get_contents_test.go b/integrations/api_repo_get_contents_test.go
index 00fef78b887..9843239ec4a 100644
--- a/integrations/api_repo_get_contents_test.go
+++ b/integrations/api_repo_get_contents_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -54,9 +55,9 @@ func TestAPIGetContents(t *testing.T) {
 
 func testAPIGetContents(t *testing.T, u *url.URL) {
 	/*** SETUP ***/
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3, is an org
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3, is an org
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 	repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_git_blobs_test.go b/integrations/api_repo_git_blobs_test.go
index 53c266e9f97..0311b6120f2 100644
--- a/integrations/api_repo_git_blobs_test.go
+++ b/integrations/api_repo_git_blobs_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -17,9 +18,9 @@ import (
 
 func TestAPIReposGitBlobs(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 	repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_git_commits_test.go b/integrations/api_repo_git_commits_test.go
index 627f563dafe..1e138eb9152 100644
--- a/integrations/api_repo_git_commits_test.go
+++ b/integrations/api_repo_git_commits_test.go
@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -25,7 +25,7 @@ func compareCommitFiles(t *testing.T, expect []string, files []*api.CommitAffect
 
 func TestAPIReposGitCommits(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -53,7 +53,7 @@ func TestAPIReposGitCommits(t *testing.T) {
 
 func TestAPIReposGitCommitList(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -76,7 +76,7 @@ func TestAPIReposGitCommitList(t *testing.T) {
 
 func TestAPIReposGitCommitListPage2Empty(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -93,7 +93,7 @@ func TestAPIReposGitCommitListPage2Empty(t *testing.T) {
 
 func TestAPIReposGitCommitListDifferentBranch(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -112,7 +112,7 @@ func TestAPIReposGitCommitListDifferentBranch(t *testing.T) {
 
 func TestDownloadCommitDiffOrPatch(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_git_hook_test.go b/integrations/api_repo_git_hook_test.go
index f9cfdf44934..ee59321761a 100644
--- a/integrations/api_repo_git_hook_test.go
+++ b/integrations/api_repo_git_hook_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -25,7 +26,7 @@ func TestAPIListGitHooks(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 37}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	// user1 is an admin user
 	session := loginUser(t, "user1")
@@ -51,7 +52,7 @@ func TestAPIListGitHooksNoHooks(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	// user1 is an admin user
 	session := loginUser(t, "user1")
@@ -72,7 +73,7 @@ func TestAPIListGitHooksNoAccess(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -85,7 +86,7 @@ func TestAPIGetGitHook(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 37}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	// user1 is an admin user
 	session := loginUser(t, "user1")
@@ -103,7 +104,7 @@ func TestAPIGetGitHookNoAccess(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -116,7 +117,7 @@ func TestAPIEditGitHook(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	// user1 is an admin user
 	session := loginUser(t, "user1")
@@ -146,7 +147,7 @@ func TestAPIEditGitHookNoAccess(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -162,7 +163,7 @@ func TestAPIDeleteGitHook(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 37}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	// user1 is an admin user
 	session := loginUser(t, "user1")
@@ -185,7 +186,7 @@ func TestAPIDeleteGitHookNoAccess(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_git_notes_test.go b/integrations/api_repo_git_notes_test.go
index 293bd769f43..08b885bd2fc 100644
--- a/integrations/api_repo_git_notes_test.go
+++ b/integrations/api_repo_git_notes_test.go
@@ -9,8 +9,8 @@ import (
 	"net/url"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -18,7 +18,7 @@ import (
 
 func TestAPIReposGitNotes(t *testing.T) {
 	onGiteaRun(t, func(*testing.T, *url.URL) {
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 		// Login as User2.
 		session := loginUser(t, user.Name)
 		token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_git_ref_test.go b/integrations/api_repo_git_ref_test.go
index 9137f03adf2..f0fc3c81fa2 100644
--- a/integrations/api_repo_git_ref_test.go
+++ b/integrations/api_repo_git_ref_test.go
@@ -8,13 +8,13 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 func TestAPIReposGitRefs(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_git_tags_test.go b/integrations/api_repo_git_tags_test.go
index f091286e2c2..73c04531f19 100644
--- a/integrations/api_repo_git_tags_test.go
+++ b/integrations/api_repo_git_tags_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
@@ -20,7 +21,7 @@ import (
 
 func TestAPIGitTags(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	// Login as User2.
 	session := loginUser(t, user.Name)
@@ -66,7 +67,7 @@ func TestAPIDeleteTagByName(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	session := loginUser(t, owner.LowerName)
 	token := getTokenForLoggedInUser(t, session)
 
diff --git a/integrations/api_repo_git_trees_test.go b/integrations/api_repo_git_trees_test.go
index 27088156bac..c54d3e3245a 100644
--- a/integrations/api_repo_git_trees_test.go
+++ b/integrations/api_repo_git_trees_test.go
@@ -10,13 +10,14 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 func TestAPIReposGitTrees(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)               // owner of the repo1 & repo16
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)               // owner of the repo3
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)               // owner of neither repos
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)       // owner of the repo1 & repo16
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)       // owner of the repo3
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)       // owner of neither repos
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)   // public repo
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)   // public repo
 	repo16 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 16}).(*models.Repository) // private repo
diff --git a/integrations/api_repo_lfs_locks_test.go b/integrations/api_repo_lfs_locks_test.go
index 6a69145589a..f4d1ad12440 100644
--- a/integrations/api_repo_lfs_locks_test.go
+++ b/integrations/api_repo_lfs_locks_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -22,7 +23,7 @@ import (
 func TestAPILFSLocksNotStarted(t *testing.T) {
 	defer prepareTestEnv(t)()
 	setting.LFS.StartServer = false
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
@@ -38,7 +39,7 @@ func TestAPILFSLocksNotStarted(t *testing.T) {
 func TestAPILFSLocksNotLogin(t *testing.T) {
 	defer prepareTestEnv(t)()
 	setting.LFS.StartServer = true
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	req := NewRequestf(t, "GET", "/%s/%s.git/info/lfs/locks", user.Name, repo.Name)
@@ -52,14 +53,14 @@ func TestAPILFSLocksNotLogin(t *testing.T) {
 func TestAPILFSLocksLogged(t *testing.T) {
 	defer prepareTestEnv(t)()
 	setting.LFS.StartServer = true
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) //in org 3
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) //in org 3
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) //in org 3
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) //in org 3
 
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository) // own by org 3
 
 	tests := []struct {
-		user       *models.User
+		user       *user_model.User
 		repo       *models.Repository
 		path       string
 		httpResult int
@@ -81,21 +82,21 @@ func TestAPILFSLocksLogged(t *testing.T) {
 	}
 
 	resultsTests := []struct {
-		user        *models.User
+		user        *user_model.User
 		repo        *models.Repository
 		totalCount  int
 		oursCount   int
 		theirsCount int
-		locksOwners []*models.User
+		locksOwners []*user_model.User
 		locksTimes  []time.Time
 	}{
-		{user: user2, repo: repo1, totalCount: 4, oursCount: 4, theirsCount: 0, locksOwners: []*models.User{user2, user2, user2, user2}, locksTimes: []time.Time{}},
-		{user: user2, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
-		{user: user4, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*models.User{user2, user4}, locksTimes: []time.Time{}},
+		{user: user2, repo: repo1, totalCount: 4, oursCount: 4, theirsCount: 0, locksOwners: []*user_model.User{user2, user2, user2, user2}, locksTimes: []time.Time{}},
+		{user: user2, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*user_model.User{user2, user4}, locksTimes: []time.Time{}},
+		{user: user4, repo: repo3, totalCount: 2, oursCount: 1, theirsCount: 1, locksOwners: []*user_model.User{user2, user4}, locksTimes: []time.Time{}},
 	}
 
 	deleteTests := []struct {
-		user   *models.User
+		user   *user_model.User
 		repo   *models.Repository
 		lockID string
 	}{}
@@ -143,7 +144,7 @@ func TestAPILFSLocksLogged(t *testing.T) {
 		for _, lock := range lfsLocksVerify.Ours {
 			assert.EqualValues(t, test.user.DisplayName(), lock.Owner.Name)
 			deleteTests = append(deleteTests, struct {
-				user   *models.User
+				user   *user_model.User
 				repo   *models.Repository
 				lockID string
 			}{test.user, test.repo, lock.ID})
diff --git a/integrations/api_repo_lfs_migrate_test.go b/integrations/api_repo_lfs_migrate_test.go
index 2873b425c5e..6d41a48529c 100644
--- a/integrations/api_repo_lfs_migrate_test.go
+++ b/integrations/api_repo_lfs_migrate_test.go
@@ -9,8 +9,8 @@ import (
 	"path"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -28,7 +28,7 @@ func TestAPIRepoLFSMigrateLocal(t *testing.T) {
 	setting.Migrations.AllowLocalNetworks = true
 	assert.NoError(t, migrations.Init())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
diff --git a/integrations/api_repo_lfs_test.go b/integrations/api_repo_lfs_test.go
index 4addb422c39..6f3becf17b1 100644
--- a/integrations/api_repo_lfs_test.go
+++ b/integrations/api_repo_lfs_test.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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/lfs"
 	"code.gitea.io/gitea/modules/setting"
@@ -26,7 +27,7 @@ func TestAPILFSNotStarted(t *testing.T) {
 
 	setting.LFS.StartServer = false
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
@@ -46,7 +47,7 @@ func TestAPILFSMediaType(t *testing.T) {
 
 	setting.LFS.StartServer = true
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	req := NewRequestf(t, "POST", "/%s/%s.git/info/lfs/objects/batch", user.Name, repo.Name)
diff --git a/integrations/api_repo_raw_test.go b/integrations/api_repo_raw_test.go
index e7eb60ac56c..2a77d1ba630 100644
--- a/integrations/api_repo_raw_test.go
+++ b/integrations/api_repo_raw_test.go
@@ -8,13 +8,13 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 func TestAPIReposRaw(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_tags_test.go b/integrations/api_repo_tags_test.go
index 79a2880bcf5..c076bb42edf 100644
--- a/integrations/api_repo_tags_test.go
+++ b/integrations/api_repo_tags_test.go
@@ -9,8 +9,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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"
 
@@ -19,7 +19,7 @@ import (
 
 func TestAPIRepoTags(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	// Login as User2.
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/api_repo_teams_test.go b/integrations/api_repo_teams_test.go
index 59025e0e53e..ca1e33eabf2 100644
--- a/integrations/api_repo_teams_test.go
+++ b/integrations/api_repo_teams_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -22,7 +23,7 @@ func TestAPIRepoTeams(t *testing.T) {
 	// publicOrgRepo = user3/repo21
 	publicOrgRepo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 32}).(*models.Repository)
 	// user4
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -62,7 +63,7 @@ func TestAPIRepoTeams(t *testing.T) {
 	session.MakeRequest(t, req, http.StatusForbidden)
 
 	// AddTeam with user2
-	user = unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session = loginUser(t, user.Name)
 	token = getTokenForLoggedInUser(t, session)
 	url = fmt.Sprintf("/api/v1/repos/%s/teams/%s?token=%s", publicOrgRepo.FullName(), "team1", token)
diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go
index 8de020d0748..b07416a5bff 100644
--- a/integrations/api_repo_test.go
+++ b/integrations/api_repo_test.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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/util"
@@ -22,7 +23,7 @@ import (
 
 func TestAPIUserReposNotLogin(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	req := NewRequestf(t, "GET", "/api/v1/users/%s/repos", user.Name)
 	resp := MakeRequest(t, req, http.StatusOK)
@@ -53,11 +54,11 @@ func TestAPISearchRepo(t *testing.T) {
 		assert.False(t, repo.Private)
 	}
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 15}).(*models.User)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 16}).(*models.User)
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 18}).(*models.User)
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 20}).(*models.User)
-	orgUser := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 17}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}).(*user_model.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 16}).(*user_model.User)
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 18}).(*user_model.User)
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}).(*user_model.User)
+	orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 17}).(*user_model.User)
 
 	oldAPIDefaultNum := setting.API.DefaultPagingNum
 	defer func() {
@@ -66,7 +67,7 @@ func TestAPISearchRepo(t *testing.T) {
 	setting.API.DefaultPagingNum = 10
 
 	// Map of expected results, where key is user for login
-	type expectedResults map[*models.User]struct {
+	type expectedResults map[*user_model.User]struct {
 		count           int
 		repoOwnerID     int64
 		repoName        string
@@ -246,13 +247,13 @@ func TestAPIViewRepo(t *testing.T) {
 
 func TestAPIOrgRepos(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
 	// User3 is an Org. Check their repos.
-	sourceOrg := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
+	sourceOrg := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
 
-	expectedResults := map[*models.User]struct {
+	expectedResults := map[*user_model.User]struct {
 		count           int
 		includesPrivate bool
 	}{
@@ -292,7 +293,7 @@ func TestAPIOrgRepos(t *testing.T) {
 
 func TestAPIGetRepoByIDUnauthorized(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 	req := NewRequestf(t, "GET", "/api/v1/repositories/2?token="+token)
@@ -316,7 +317,7 @@ func TestAPIRepoMigrate(t *testing.T) {
 
 	defer prepareTestEnv(t)()
 	for _, testCase := range testCases {
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}).(*user_model.User)
 		session := loginUser(t, user.Name)
 		token := getTokenForLoggedInUser(t, session)
 		req := NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate?token="+token, &api.MigrateRepoOptions{
@@ -361,7 +362,7 @@ func testAPIRepoMigrateConflict(t *testing.T, u *url.URL) {
 		defer util.RemoveAll(dstPath)
 		t.Run("CreateRepo", doAPICreateRepository(httpContext, false))
 
-		user, err := models.GetUserByName(httpContext.Username)
+		user, err := user_model.GetUserByName(httpContext.Username)
 		assert.NoError(t, err)
 		userID := user.ID
 
@@ -395,7 +396,7 @@ func TestAPIOrgRepoCreate(t *testing.T) {
 
 	defer prepareTestEnv(t)()
 	for _, testCase := range testCases {
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}).(*user_model.User)
 		session := loginUser(t, user.Name)
 		token := getTokenForLoggedInUser(t, session)
 		req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/org/%s/repos?token="+token, testCase.orgName), &api.CreateRepoOption{
@@ -463,7 +464,7 @@ func TestAPIRepoTransfer(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	//create repo to move
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 	repoName := "moveME"
@@ -480,7 +481,7 @@ func TestAPIRepoTransfer(t *testing.T) {
 
 	//start testing
 	for _, testCase := range testCases {
-		user = unittest.AssertExistsAndLoadBean(t, &models.User{ID: testCase.ctxUserID}).(*models.User)
+		user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: testCase.ctxUserID}).(*user_model.User)
 		repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: apiRepo.ID}).(*models.Repository)
 		session = loginUser(t, user.Name)
 		token = getTokenForLoggedInUser(t, session)
@@ -499,7 +500,7 @@ func TestAPIRepoTransfer(t *testing.T) {
 func TestAPIGenerateRepo(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 
@@ -535,7 +536,7 @@ func TestAPIGenerateRepo(t *testing.T) {
 
 func TestAPIRepoGetReviewers(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
@@ -549,7 +550,7 @@ func TestAPIRepoGetReviewers(t *testing.T) {
 
 func TestAPIRepoGetAssignees(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
diff --git a/integrations/api_repo_topic_test.go b/integrations/api_repo_topic_test.go
index 05f025d9461..572099b7d72 100644
--- a/integrations/api_repo_topic_test.go
+++ b/integrations/api_repo_topic_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -51,9 +52,9 @@ func TestAPITopicSearch(t *testing.T) {
 
 func TestAPIRepoTopic(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of repo2
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User) // owner of repo3
-	user4 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 4}).(*models.User) // write access to repo 3
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // owner of repo2
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) // owner of repo3
+	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) // write access to repo 3
 	repo2 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 2}).(*models.Repository)
 	repo3 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
 
diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go
index 26d6c884b49..da22d404797 100644
--- a/integrations/api_team_test.go
+++ b/integrations/api_team_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -23,7 +24,7 @@ func TestAPITeam(t *testing.T) {
 
 	teamUser := unittest.AssertExistsAndLoadBean(t, &models.TeamUser{}).(*models.TeamUser)
 	team := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamUser.TeamID}).(*models.Team)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: teamUser.UID}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}).(*user_model.User)
 
 	session := loginUser(t, user.Name)
 	token := getTokenForLoggedInUser(t, session)
@@ -37,7 +38,7 @@ func TestAPITeam(t *testing.T) {
 
 	// non team member user will not access the teams details
 	teamUser2 := unittest.AssertExistsAndLoadBean(t, &models.TeamUser{ID: 3}).(*models.TeamUser)
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: teamUser2.UID}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}).(*user_model.User)
 
 	session = loginUser(t, user2.Name)
 	token = getTokenForLoggedInUser(t, session)
@@ -48,11 +49,11 @@ func TestAPITeam(t *testing.T) {
 	_ = session.MakeRequest(t, req, http.StatusUnauthorized)
 
 	// Get an admin user able to create, update and delete teams.
-	user = unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	session = loginUser(t, user.Name)
 	token = getTokenForLoggedInUser(t, session)
 
-	org := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 6}).(*models.User)
+	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 6}).(*user_model.User)
 
 	// Create team.
 	teamToCreate := &api.CreateTeamOption{
@@ -139,8 +140,8 @@ type TeamSearchResults struct {
 func TestAPITeamSearch(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
-	org := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
 
 	var results TeamSearchResults
 
@@ -155,7 +156,7 @@ func TestAPITeamSearch(t *testing.T) {
 	assert.Equal(t, "test_team", results.Data[0].Name)
 
 	// no access if not organization member
-	user5 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
+	user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
 	session = loginUser(t, user5.Name)
 	csrf = GetCSRF(t, session, "/"+org.Name)
 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "team")
diff --git a/integrations/api_team_user_test.go b/integrations/api_team_user_test.go
index e7f4b0ee4c9..7263408bee5 100644
--- a/integrations/api_team_user_test.go
+++ b/integrations/api_team_user_test.go
@@ -9,8 +9,8 @@ import (
 	"testing"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -31,7 +31,7 @@ func TestAPITeamUser(t *testing.T) {
 	var user2 *api.User
 	DecodeJSON(t, resp, &user2)
 	user2.Created = user2.Created.In(time.Local)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}).(*user_model.User)
 
 	expectedUser := convert.ToUser(user, user)
 
diff --git a/integrations/api_token_test.go b/integrations/api_token_test.go
index 8314d5b636f..aca4768503c 100644
--- a/integrations/api_token_test.go
+++ b/integrations/api_token_test.go
@@ -10,13 +10,14 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
 // TestAPICreateAndDeleteToken tests that token that was just created can be deleted
 func TestAPICreateAndDeleteToken(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	req := NewRequestWithJSON(t, "POST", "/api/v1/users/user1/tokens", map[string]string{
 		"name": "test-key-1",
@@ -56,7 +57,7 @@ func TestAPICreateAndDeleteToken(t *testing.T) {
 // TestAPIDeleteMissingToken ensures that error is thrown when token not found
 func TestAPIDeleteMissingToken(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	req := NewRequestf(t, "DELETE", "/api/v1/users/user1/tokens/%d", unittest.NonexistentID)
 	req = AddBasicAuthHeader(req, user.Name)
diff --git a/integrations/api_user_orgs_test.go b/integrations/api_user_orgs_test.go
index 5bc5df118f8..09272726f66 100644
--- a/integrations/api_user_orgs_test.go
+++ b/integrations/api_user_orgs_test.go
@@ -9,8 +9,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -25,7 +25,7 @@ func TestUserOrgs(t *testing.T) {
 
 	orgs := getUserOrgs(t, adminUsername, normalUsername)
 
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user3"}).(*models.User)
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user3"}).(*user_model.User)
 
 	assert.Equal(t, []*api.Organization{
 		{
@@ -81,7 +81,7 @@ func TestMyOrgs(t *testing.T) {
 	resp := session.MakeRequest(t, req, http.StatusOK)
 	var orgs []*api.Organization
 	DecodeJSON(t, resp, &orgs)
-	user3 := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user3"}).(*models.User)
+	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user3"}).(*user_model.User)
 
 	assert.Equal(t, []*api.Organization{
 		{
diff --git a/integrations/api_user_search_test.go b/integrations/api_user_search_test.go
index 67dc2f1cac4..41f14cf944f 100644
--- a/integrations/api_user_search_test.go
+++ b/integrations/api_user_search_test.go
@@ -9,8 +9,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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"
 
@@ -49,10 +49,10 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) {
 	var results SearchResults
 	DecodeJSON(t, resp, &results)
 	assert.NotEmpty(t, results.Data)
-	var modelUser *models.User
+	var modelUser *user_model.User
 	for _, user := range results.Data {
 		assert.Contains(t, user.UserName, query)
-		modelUser = unittest.AssertExistsAndLoadBean(t, &models.User{ID: user.ID}).(*models.User)
+		modelUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID}).(*user_model.User)
 		if modelUser.KeepEmailPrivate {
 			assert.EqualValues(t, fmt.Sprintf("%s@%s", modelUser.LowerName, setting.Service.NoReplyAddress), user.Email)
 		} else {
diff --git a/integrations/change_default_branch_test.go b/integrations/change_default_branch_test.go
index d691e48e00a..3b79e5032d9 100644
--- a/integrations/change_default_branch_test.go
+++ b/integrations/change_default_branch_test.go
@@ -11,12 +11,13 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 func TestChangeDefaultBranch(t *testing.T) {
 	defer prepareTestEnv(t)()
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, owner.Name)
 	branchesURL := fmt.Sprintf("/%s/%s/settings/branches", owner.Name, repo.Name)
diff --git a/integrations/delete_user_test.go b/integrations/delete_user_test.go
index f8efab0a24f..385b1822bb0 100644
--- a/integrations/delete_user_test.go
+++ b/integrations/delete_user_test.go
@@ -15,7 +15,7 @@ import (
 )
 
 func assertUserDeleted(t *testing.T, userID int64) {
-	unittest.AssertNotExistsBean(t, &models.User{ID: userID})
+	unittest.AssertNotExistsBean(t, &user_model.User{ID: userID})
 	unittest.AssertNotExistsBean(t, &user_model.Follow{UserID: userID})
 	unittest.AssertNotExistsBean(t, &user_model.Follow{FollowID: userID})
 	unittest.AssertNotExistsBean(t, &models.Repository{OwnerID: userID})
@@ -38,7 +38,7 @@ func TestUserDeleteAccount(t *testing.T) {
 	session.MakeRequest(t, req, http.StatusFound)
 
 	assertUserDeleted(t, 8)
-	unittest.CheckConsistencyFor(t, &models.User{})
+	unittest.CheckConsistencyFor(t, &user_model.User{})
 }
 
 func TestUserDeleteAccountStillOwnRepos(t *testing.T) {
@@ -53,5 +53,5 @@ func TestUserDeleteAccountStillOwnRepos(t *testing.T) {
 	session.MakeRequest(t, req, http.StatusFound)
 
 	// user should not have been deleted, because the user still owns repos
-	unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2})
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
 }
diff --git a/integrations/empty_repo_test.go b/integrations/empty_repo_test.go
index cbab67ba1e2..70e7aff9aa2 100644
--- a/integrations/empty_repo_test.go
+++ b/integrations/empty_repo_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 func TestEmptyRepo(t *testing.T) {
@@ -21,7 +22,7 @@ func TestEmptyRepo(t *testing.T) {
 		"graph",
 	}
 	emptyRepo := unittest.AssertExistsAndLoadBean(t, &models.Repository{}, unittest.Cond("is_empty = ?", true)).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: emptyRepo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: emptyRepo.OwnerID}).(*user_model.User)
 	for _, subpath := range subpaths {
 		req := NewRequestf(t, "GET", "/%s/%s/%s", owner.Name, emptyRepo.Name, subpath)
 		MakeRequest(t, req, http.StatusNotFound)
diff --git a/integrations/eventsource_test.go b/integrations/eventsource_test.go
index 6c4c87b3d1f..277f5695a81 100644
--- a/integrations/eventsource_test.go
+++ b/integrations/eventsource_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/eventsource"
 	api "code.gitea.io/gitea/modules/structs"
 
@@ -51,7 +52,7 @@ func TestEventSourceManagerRun(t *testing.T) {
 		}
 	}
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	thread5 := unittest.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
 	assert.NoError(t, thread5.LoadAttributes())
diff --git a/integrations/git_test.go b/integrations/git_test.go
index cf84800eed8..71e3476abbf 100644
--- a/integrations/git_test.go
+++ b/integrations/git_test.go
@@ -19,6 +19,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/setting"
@@ -429,7 +430,7 @@ func doProtectBranch(ctx APITestContext, branch string, userToWhitelist string,
 			})
 			ctx.Session.MakeRequest(t, req, http.StatusFound)
 		} else {
-			user, err := models.GetUserByName(userToWhitelist)
+			user, err := user_model.GetUserByName(userToWhitelist)
 			assert.NoError(t, err)
 			// Change branch to protected
 			req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/%s", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame), url.PathEscape(branch)), map[string]string{
diff --git a/integrations/gpg_git_test.go b/integrations/gpg_git_test.go
index b136e822810..8478f53c0e9 100644
--- a/integrations/gpg_git_test.go
+++ b/integrations/gpg_git_test.go
@@ -11,8 +11,8 @@ import (
 	"os"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
@@ -61,7 +61,7 @@ func TestGPGGit(t *testing.T) {
 	setting.Repository.Signing.SigningKey = rootKeyID
 	setting.Repository.Signing.SigningName = "gitea"
 	setting.Repository.Signing.SigningEmail = "gitea@fake.local"
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{Name: username}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: username}).(*user_model.User)
 
 	setting.Repository.Signing.InitialCommit = []string{"never"}
 	setting.Repository.Signing.CRUDActions = []string{"never"}
@@ -326,7 +326,7 @@ func TestGPGGit(t *testing.T) {
 	}, false)
 }
 
-func crudActionCreateFile(t *testing.T, ctx APITestContext, user *models.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
+func crudActionCreateFile(t *testing.T, ctx APITestContext, user *user_model.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
 	return doAPICreateFile(ctx, path, &api.CreateFileOptions{
 		FileOptions: api.FileOptions{
 			BranchName:    from,
diff --git a/integrations/issue_test.go b/integrations/issue_test.go
index ae274749a27..69998b6f46e 100644
--- a/integrations/issue_test.go
+++ b/integrations/issue_test.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/indexer/issues"
 	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/setting"
@@ -61,7 +62,7 @@ func TestNoLoginViewIssues(t *testing.T) {
 func TestViewIssuesSortByType(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	session := loginUser(t, user.Name)
diff --git a/integrations/migrate_test.go b/integrations/migrate_test.go
index e22da7db907..9b59c85a4eb 100644
--- a/integrations/migrate_test.go
+++ b/integrations/migrate_test.go
@@ -8,8 +8,8 @@ import (
 	"os"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/migrations"
 
@@ -19,7 +19,7 @@ import (
 func TestMigrateLocalPath(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	adminUser := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
+	adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}).(*user_model.User)
 
 	old := setting.ImportLocalPaths
 	setting.ImportLocalPaths = true
diff --git a/integrations/mirror_pull_test.go b/integrations/mirror_pull_test.go
index aa0f89dd914..fb70fd01706 100644
--- a/integrations/mirror_pull_test.go
+++ b/integrations/mirror_pull_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/migration"
 	"code.gitea.io/gitea/modules/repository"
@@ -22,7 +23,7 @@ import (
 func TestMirrorPull(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repoPath := models.RepoPath(user.Name, repo.Name)
 
diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go
index 492e2c23eef..d9fa543768d 100644
--- a/integrations/mirror_push_test.go
+++ b/integrations/mirror_push_test.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
@@ -32,7 +33,7 @@ func testMirrorPush(t *testing.T, u *url.URL) {
 	setting.Migrations.AllowLocalNetworks = true
 	assert.NoError(t, migrations.Init())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	srcRepo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	mirrorRepo, err := repository.CreateRepository(user, user, models.CreateRepoOptions{
diff --git a/integrations/org_count_test.go b/integrations/org_count_test.go
index a45d1e81b59..b7d0da2f4bd 100644
--- a/integrations/org_count_test.go
+++ b/integrations/org_count_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -112,9 +113,9 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca
 	}
 
 	return func(t *testing.T) {
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name: username,
-		}).(*models.User)
+		}).(*user_model.User)
 
 		orgs, err := models.FindOrgs(models.FindOrgOptions{
 			UserID:         user.ID,
diff --git a/integrations/privateactivity_test.go b/integrations/privateactivity_test.go
index e45274ad43d..72890f611c6 100644
--- a/integrations/privateactivity_test.go
+++ b/integrations/privateactivity_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -26,7 +27,7 @@ const privateActivityTestOtherUser = "user4"
 
 func testPrivateActivityDoSomethingForActionEntries(t *testing.T) {
 	repoBefore := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repoBefore.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User)
 
 	session := loginUser(t, privateActivityTestUser)
 	token := getTokenForLoggedInUser(t, session)
diff --git a/integrations/pull_merge_test.go b/integrations/pull_merge_test.go
index 4d5699fa53c..2e219dcc918 100644
--- a/integrations/pull_merge_test.go
+++ b/integrations/pull_merge_test.go
@@ -18,6 +18,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
@@ -221,9 +222,9 @@ func TestCantMergeConflict(t *testing.T) {
 		session.MakeRequest(t, req, 201)
 
 		// Now this PR will be marked conflict - or at least a race will do - so drop down to pure code at this point...
-		user1 := unittest.AssertExistsAndLoadBean(t, &models.User{
+		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name: "user1",
-		}).(*models.User)
+		}).(*user_model.User)
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{
 			OwnerID: user1.ID,
 			Name:    "repo1",
@@ -258,9 +259,9 @@ func TestCantMergeUnrelated(t *testing.T) {
 
 		// Now we want to create a commit on a branch that is totally unrelated to our current head
 		// Drop down to pure code at this point
-		user1 := unittest.AssertExistsAndLoadBean(t, &models.User{
+		user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name: "user1",
-		}).(*models.User)
+		}).(*user_model.User)
 		repo1 := unittest.AssertExistsAndLoadBean(t, &models.Repository{
 			OwnerID: user1.ID,
 			Name:    "repo1",
diff --git a/integrations/pull_update_test.go b/integrations/pull_update_test.go
index d7ea676b79a..e5e19475caa 100644
--- a/integrations/pull_update_test.go
+++ b/integrations/pull_update_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	pull_service "code.gitea.io/gitea/services/pull"
 	repo_service "code.gitea.io/gitea/services/repository"
 	files_service "code.gitea.io/gitea/services/repository/files"
@@ -22,8 +23,8 @@ import (
 func TestAPIPullUpdate(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		//Create PR to test
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
-		org26 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 26}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+		org26 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26}).(*user_model.User)
 		pr := createOutdatedPR(t, user, org26)
 
 		//Test GetDiverging
@@ -50,8 +51,8 @@ func TestAPIPullUpdate(t *testing.T) {
 func TestAPIPullUpdateByRebase(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
 		//Create PR to test
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
-		org26 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 26}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+		org26 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 26}).(*user_model.User)
 		pr := createOutdatedPR(t, user, org26)
 
 		//Test GetDiverging
@@ -75,7 +76,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) {
 	})
 }
 
-func createOutdatedPR(t *testing.T, actor, forkOrg *models.User) *models.PullRequest {
+func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.PullRequest {
 	baseRepo, err := repo_service.CreateRepository(actor, actor, models.CreateRepoOptions{
 		Name:        "repo-pr-update",
 		Description: "repo-tmp-pr-update description",
diff --git a/integrations/repo_fork_test.go b/integrations/repo_fork_test.go
index 0f23aa5e872..27b62d26465 100644
--- a/integrations/repo_fork_test.go
+++ b/integrations/repo_fork_test.go
@@ -10,14 +10,14 @@ import (
 	"net/http/httptest"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName string) *httptest.ResponseRecorder {
-	forkOwner := unittest.AssertExistsAndLoadBean(t, &models.User{Name: forkOwnerName}).(*models.User)
+	forkOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: forkOwnerName}).(*user_model.User)
 
 	// Step0: check the existence of the to-fork repo
 	req := NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName)
diff --git a/integrations/repo_generate_test.go b/integrations/repo_generate_test.go
index 66819656f94..b70a4a47973 100644
--- a/integrations/repo_generate_test.go
+++ b/integrations/repo_generate_test.go
@@ -10,14 +10,14 @@ import (
 	"net/http/httptest"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func testRepoGenerate(t *testing.T, session *TestSession, templateOwnerName, templateRepoName, generateOwnerName, generateRepoName string) *httptest.ResponseRecorder {
-	generateOwner := unittest.AssertExistsAndLoadBean(t, &models.User{Name: generateOwnerName}).(*models.User)
+	generateOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: generateOwnerName}).(*user_model.User)
 
 	// Step0: check the existence of the generated repo
 	req := NewRequestf(t, "GET", "/%s/%s", generateOwnerName, generateRepoName)
diff --git a/integrations/repo_tag_test.go b/integrations/repo_tag_test.go
index 7bba055e2a5..823e7e6589e 100644
--- a/integrations/repo_tag_test.go
+++ b/integrations/repo_tag_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/release"
@@ -22,7 +23,7 @@ func TestCreateNewTagProtected(t *testing.T) {
 	defer prepareTestEnv(t)()
 
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	owner := unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	t.Run("API", func(t *testing.T) {
 		defer PrintCurrentTest(t)()
diff --git a/integrations/signin_test.go b/integrations/signin_test.go
index a920ed45dd3..3ea8866150c 100644
--- a/integrations/signin_test.go
+++ b/integrations/signin_test.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/unknwon/i18n"
@@ -34,7 +34,7 @@ func testLoginFailed(t *testing.T, username, password, message string) {
 func TestSignin(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	// add new user with user2's email
 	user.Name = "testuser"
diff --git a/integrations/signup_test.go b/integrations/signup_test.go
index 38aa069f318..93e384076ff 100644
--- a/integrations/signup_test.go
+++ b/integrations/signup_test.go
@@ -10,8 +10,8 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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"
@@ -54,7 +54,7 @@ func TestSignupAsRestricted(t *testing.T) {
 	req = NewRequest(t, "GET", "/restrictedUser")
 	MakeRequest(t, req, http.StatusOK)
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "restrictedUser"}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "restrictedUser"}).(*user_model.User)
 	assert.True(t, user2.IsRestricted)
 }
 
diff --git a/integrations/user_avatar_test.go b/integrations/user_avatar_test.go
index edc3a47314a..d1005bce078 100644
--- a/integrations/user_avatar_test.go
+++ b/integrations/user_avatar_test.go
@@ -13,8 +13,8 @@ import (
 	"net/url"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/avatar"
 
 	"github.com/stretchr/testify/assert"
@@ -22,7 +22,7 @@ import (
 
 func TestUserAvatar(t *testing.T) {
 	onGiteaRun(t, func(t *testing.T, u *url.URL) {
-		user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo3, is an org
+		user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // owner of the repo3, is an org
 
 		seed := user2.Email
 		if len(seed) == 0 {
@@ -72,7 +72,7 @@ func TestUserAvatar(t *testing.T) {
 
 		session.MakeRequest(t, req, http.StatusFound)
 
-		user2 = unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo3, is an org
+		user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) // owner of the repo3, is an org
 
 		req = NewRequest(t, "GET", user2.AvatarLinkWithSize(0))
 		_ = session.MakeRequest(t, req, http.StatusOK)
diff --git a/integrations/user_test.go b/integrations/user_test.go
index b70bf212b09..8552993217a 100644
--- a/integrations/user_test.go
+++ b/integrations/user_test.go
@@ -8,8 +8,8 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/test"
 
 	"github.com/stretchr/testify/assert"
@@ -35,8 +35,8 @@ func TestRenameUsername(t *testing.T) {
 	})
 	session.MakeRequest(t, req, http.StatusFound)
 
-	unittest.AssertExistsAndLoadBean(t, &models.User{Name: "newUsername"})
-	unittest.AssertNotExistsBean(t, &models.User{Name: "user2"})
+	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "newUsername"})
+	unittest.AssertNotExistsBean(t, &user_model.User{Name: "user2"})
 }
 
 func TestRenameInvalidUsername(t *testing.T) {
@@ -67,7 +67,7 @@ func TestRenameInvalidUsername(t *testing.T) {
 			i18n.Tr("en", "form.alpha_dash_dot_error"),
 		)
 
-		unittest.AssertNotExistsBean(t, &models.User{Name: invalidUsername})
+		unittest.AssertNotExistsBean(t, &user_model.User{Name: invalidUsername})
 	}
 }
 
@@ -113,7 +113,7 @@ func TestRenameReservedUsername(t *testing.T) {
 			i18n.Tr("en", "user.form.name_reserved", reservedUsername),
 		)
 
-		unittest.AssertNotExistsBean(t, &models.User{Name: reservedUsername})
+		unittest.AssertNotExistsBean(t, &user_model.User{Name: reservedUsername})
 	}
 }
 
diff --git a/integrations/xss_test.go b/integrations/xss_test.go
index 8128a54aa37..4c2e60e799b 100644
--- a/integrations/xss_test.go
+++ b/integrations/xss_test.go
@@ -8,15 +8,15 @@ import (
 	"net/http"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
 
 func TestXSSUserFullName(t *testing.T) {
 	defer prepareTestEnv(t)()
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	const fullName = `name & <script class="evil">alert('Oh no!');</script>`
 
 	session := loginUser(t, user.Name)
diff --git a/models/access.go b/models/access.go
index 7af88e1b4a1..01b37dd7aec 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 81e36ed2e02..a2fd05a033e 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 d790cd6678f..66fa78f2683 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 69e6aa73121..4f2024cfcf6 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 87bbcf3fb77..8ab88bb97e0 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 fc0da58e381..f5ed570ea24 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 474825820af..12aecdae84d 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 df34b93ec53..bba39fde876 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 ba669628b90..d16e7f5e99a 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 675247ed874..adaeedcc094 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 00000000000..9c9d18f1847
--- /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 00000000000..704a48ed1eb
--- /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 d0691c1c697..5ef96683e70 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 6b023a4cb2a..4be13072257 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 357dc2b964c..ce27a9237ed 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 6da65d36979..5eeeb69ef50 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 e5f4960b766..8f51c146aa0 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 1c6b79ec5f6..18240860211 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 710c15a9783..41dd1ccd535 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 922a5790be2..3e98f60ffdf 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 7aab8b93995..557d19e739d 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 996a3a8563e..527549171ec 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 b604f13bd5a..dd9a42b572b 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 8429596c821..417caccfaf8 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 3c587fc8f71..ef6aff9cbe2 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 4d3607b3e15..5ab4892bc7c 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 ab81ef0c684..9fef652b4e6 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 86ac98b733a..61215dedeff 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 2858b12dde6..9b36d8dfdf1 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 d0f807a56e6..aa3c92b2827 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 6eeb2a961a5..7c98f60b3a3 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 d0b9c660c21..66a932225ad 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 de4e13b4bb4..a85c8654645 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 2956e40f065..21d90a61189 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 933246544b7..fb34db4ed58 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 c01e4add7ac..458250ac586 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 eaec5d924e8..7754e90a864 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 ceb579d0955..0f578f101f1 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 7942b7e7853..a9e19438d6c 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 55d585a55cc..392bf91e194 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 83420f5a152..97efd8bb729 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 5bac406ad01..bf5c2593a3f 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 55483ce57bd..ceaff62be12 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 c40e545fa89..06f937c80d7 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 40539d2b781..da5e61ed0bc 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 943699290f2..15513ea1dd8 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 5c99a2b2f7b..87e59f5b117 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 db785586474..ef8a1e83b89 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 19fad25f99f..3b05f34c551 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 ea73d03d422..63e1b99af55 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 1bdffe2cb15..510a0dcfaae 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 b912dd83f26..450cf7dd551 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 805c63300b3..c24064c5b8b 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 6f314ca931f..fb08efa994e 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 1e1dd13c571..85ca0b3fc8d 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 57ef2102139..7c5b83ae5f2 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 028a3e5c3b6..269e8e06d20 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 c967cca3139..2281dbffdaf 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 69d2ceda5e1..f60024c7109 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 860c5c4813d..bf5c160297d 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 5986da7e77a..d3d4c7bbd71 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 6502d7959ca..11d1a0e1d09 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 060afc2139b..94dd238cb6f 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 511b446711d..ff86f61577e 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 a6304cf1aaf..96abb9037ab 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 180a85a2f5d..ce046eba7a1 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 f7a303b0c12..f30ba4748c6 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 5cc396eb76a..e6aaedaae65 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 3b5ac21b09c..9918a10d767 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 368fea598de..1bde5628c19 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 fb1b1086aa1..e225c945e95 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 00906c7a273..1c4943f6993 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 7b8b86df132..b9d156ceb18 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 21d5b7d4964..780eb2e15ce 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 c31379dcb47..cf439f6282f 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 55d8e403976..63c38f6886e 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 c0b15be21a0..58bc77132da 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 cb300c1a87f..9a2a0d5c472 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 9bff00cd8cc..19999572709 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 4126606e240..9816fcc7130 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}
-}
-
-// 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
+		if count < 0 {
+			// No common organization
+			return false
+		}
+
+		// they are in an organization together
+		return true
+	}
+	return false
 }
diff --git a/models/user_avatar.go b/models/user/avatar.go
similarity index 90%
rename from models/user_avatar.go
rename to models/user/avatar.go
index ae4cd1e5ec5..c881642b568 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 00000000000..25e0d8ea8a4
--- /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 925af0e8b29..55e5f9341cd 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 00000000000..5c37bb99617
--- /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 00000000000..f8ccee0b389
--- /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 00000000000..ac49852254c
--- /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 8c5fbf6d6ae..528ebb05402 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 bed6a188673..a91e24d3637 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 3e94a6f9b70..f331a0c16b8 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 2abbbfc6f33..7d2997648df 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 cfbd8496409..d3c7718d69a 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 aebdb4f48c2..ae39de3da42 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 beab48d45ad..f80696542e6 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.
diff --git a/modules/context/context.go b/modules/context/context.go
index 6fc4c7261ae..5a37f8b0bf2 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -20,8 +20,8 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	mc "code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/json"
@@ -62,7 +62,7 @@ type Context struct {
 
 	Link        string // current request URL
 	EscapedLink string
-	User        *models.User
+	User        *user_model.User
 	IsSigned    bool
 	IsBasicAuth bool
 
@@ -123,7 +123,7 @@ func (ctx *Context) IsUserRepoReaderAny() bool {
 
 // RedirectToUser redirect to a differently-named user
 func RedirectToUser(ctx *Context, userName string, redirectUserID int64) {
-	user, err := models.GetUserByID(redirectUserID)
+	user, err := user_model.GetUserByID(redirectUserID)
 	if err != nil {
 		ctx.ServerError("GetUserByID", err)
 		return
@@ -560,7 +560,7 @@ func GetContext(req *http.Request) *Context {
 }
 
 // GetContextUser returns context user
-func GetContextUser(req *http.Request) *models.User {
+func GetContextUser(req *http.Request) *user_model.User {
 	if apiContext, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
 		return apiContext.User
 	}
diff --git a/modules/context/org.go b/modules/context/org.go
index d4159a32a7a..2a3ebdd1e73 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -52,7 +52,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 	var err error
 	ctx.Org.Organization, err = models.GetOrgByName(orgName)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			redirectUserID, err := user_model.LookupUserRedirect(orgName)
 			if err == nil {
 				RedirectToUser(ctx, orgName, redirectUserID)
@@ -108,7 +108,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 		}
 	} else {
 		// Fake data.
-		ctx.Data["SignedUser"] = &models.User{}
+		ctx.Data["SignedUser"] = &user_model.User{}
 	}
 	if (requireMember && !ctx.Org.IsMember) ||
 		(requireOwner && !ctx.Org.IsOwner) {
diff --git a/modules/context/repo.go b/modules/context/repo.go
index 1f1a035267e..7feaad4ccd5 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	unit_model "code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -55,7 +56,7 @@ type Repository struct {
 	IsViewTag    bool
 	IsViewCommit bool
 	Repository   *models.Repository
-	Owner        *models.User
+	Owner        *user_model.User
 	Commit       *git.Commit
 	Tag          *git.Tag
 	GitRepo      *git.Repository
@@ -104,7 +105,7 @@ type CanCommitToBranchResults struct {
 
 // CanCommitToBranch returns true if repository is editable and user has proper access level
 //   and branch is not protected for push
-func (r *Repository) CanCommitToBranch(doer *models.User) (CanCommitToBranchResults, error) {
+func (r *Repository) CanCommitToBranch(doer *user_model.User) (CanCommitToBranchResults, error) {
 	protectedBranch, err := models.GetProtectedBranchBy(r.Repository.ID, r.BranchName)
 
 	if err != nil {
@@ -145,7 +146,7 @@ func (r *Repository) CanCommitToBranch(doer *models.User) (CanCommitToBranchResu
 }
 
 // CanUseTimetracker returns whether or not a user can use the timetracker.
-func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) bool {
+func (r *Repository) CanUseTimetracker(issue *models.Issue, user *user_model.User) bool {
 	// Checking for following:
 	// 1. Is timetracker enabled
 	// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
@@ -155,7 +156,7 @@ func (r *Repository) CanUseTimetracker(issue *models.Issue, user *models.User) b
 }
 
 // CanCreateIssueDependencies returns whether or not a user can create dependencies.
-func (r *Repository) CanCreateIssueDependencies(user *models.User, isPull bool) bool {
+func (r *Repository) CanCreateIssueDependencies(user *user_model.User, isPull bool) bool {
 	return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull)
 }
 
@@ -402,7 +403,7 @@ func RepoIDAssignment() func(ctx *Context) {
 // RepoAssignment returns a middleware to handle repository assignment
 func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 	var (
-		owner *models.User
+		owner *user_model.User
 		err   error
 	)
 
@@ -414,9 +415,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 	if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
 		owner = ctx.User
 	} else {
-		owner, err = models.GetUserByName(userName)
+		owner, err = user_model.GetUserByName(userName)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				if ctx.FormString("go-get") == "1" {
 					EarlyResponseForGoGetMeta(ctx)
 					return
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index 4229e6cc7d8..f08a62588ee 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -33,7 +33,7 @@ func ToEmail(email *user_model.EmailAddress) *api.Email {
 }
 
 // ToBranch convert a git.Commit and git.Branch to an api.Branch
-func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User, isRepoAdmin bool) (*api.Branch, error) {
+func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *user_model.User, isRepoAdmin bool) (*api.Branch, error) {
 	if bp == nil {
 		var hasPerm bool
 		var err error
@@ -83,15 +83,15 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
 
 // ToBranchProtection convert a ProtectedBranch to api.BranchProtection
 func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
-	pushWhitelistUsernames, err := models.GetUserNamesByIDs(bp.WhitelistUserIDs)
+	pushWhitelistUsernames, err := user_model.GetUserNamesByIDs(bp.WhitelistUserIDs)
 	if err != nil {
 		log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
 	}
-	mergeWhitelistUsernames, err := models.GetUserNamesByIDs(bp.MergeWhitelistUserIDs)
+	mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(bp.MergeWhitelistUserIDs)
 	if err != nil {
 		log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
 	}
-	approvalsWhitelistUsernames, err := models.GetUserNamesByIDs(bp.ApprovalsWhitelistUserIDs)
+	approvalsWhitelistUsernames, err := user_model.GetUserNamesByIDs(bp.ApprovalsWhitelistUserIDs)
 	if err != nil {
 		log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
 	}
@@ -276,7 +276,7 @@ func ToDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
 	}
 }
 
-// ToOrganization convert models.User to api.Organization
+// ToOrganization convert user_model.User to api.Organization
 func ToOrganization(org *models.Organization) *api.Organization {
 	return &api.Organization{
 		ID:                        org.ID,
@@ -355,12 +355,16 @@ func ToOAuth2Application(app *login.OAuth2Application) *api.OAuth2Application {
 
 // ToLFSLock convert a LFSLock to api.LFSLock
 func ToLFSLock(l *models.LFSLock) *api.LFSLock {
+	u, err := user_model.GetUserByID(l.OwnerID)
+	if err != nil {
+		return nil
+	}
 	return &api.LFSLock{
 		ID:       strconv.FormatInt(l.ID, 10),
 		Path:     l.Path,
 		LockedAt: l.Created.Round(time.Second),
 		Owner: &api.LFSLockOwner{
-			Name: l.Owner.DisplayName(),
+			Name: u.DisplayName(),
 		},
 	}
 }
diff --git a/modules/convert/git_commit.go b/modules/convert/git_commit.go
index 9905b51fe45..3148d880ff4 100644
--- a/modules/convert/git_commit.go
+++ b/modules/convert/git_commit.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
@@ -38,16 +39,16 @@ func ToCommitMeta(repo *models.Repository, tag *git.Tag) *api.CommitMeta {
 // ToPayloadCommit convert a git.Commit to api.PayloadCommit
 func ToPayloadCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
 	authorUsername := ""
-	if author, err := models.GetUserByEmail(c.Author.Email); err == nil {
+	if author, err := user_model.GetUserByEmail(c.Author.Email); err == nil {
 		authorUsername = author.Name
-	} else if !models.IsErrUserNotExist(err) {
+	} else if !user_model.IsErrUserNotExist(err) {
 		log.Error("GetUserByEmail: %v", err)
 	}
 
 	committerUsername := ""
-	if committer, err := models.GetUserByEmail(c.Committer.Email); err == nil {
+	if committer, err := user_model.GetUserByEmail(c.Committer.Email); err == nil {
 		committerUsername = committer.Name
-	} else if !models.IsErrUserNotExist(err) {
+	} else if !user_model.IsErrUserNotExist(err) {
 		log.Error("GetUserByEmail: %v", err)
 	}
 
@@ -71,16 +72,16 @@ func ToPayloadCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit
 }
 
 // ToCommit convert a git.Commit to api.Commit
-func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]*models.User) (*api.Commit, error) {
+func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]*user_model.User) (*api.Commit, error) {
 
 	var apiAuthor, apiCommitter *api.User
 
 	// Retrieve author and committer information
 
-	var cacheAuthor *models.User
+	var cacheAuthor *user_model.User
 	var ok bool
 	if userCache == nil {
-		cacheAuthor = (*models.User)(nil)
+		cacheAuthor = (*user_model.User)(nil)
 		ok = false
 	} else {
 		cacheAuthor, ok = userCache[commit.Author.Email]
@@ -89,8 +90,8 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 	if ok {
 		apiAuthor = ToUser(cacheAuthor, nil)
 	} else {
-		author, err := models.GetUserByEmail(commit.Author.Email)
-		if err != nil && !models.IsErrUserNotExist(err) {
+		author, err := user_model.GetUserByEmail(commit.Author.Email)
+		if err != nil && !user_model.IsErrUserNotExist(err) {
 			return nil, err
 		} else if err == nil {
 			apiAuthor = ToUser(author, nil)
@@ -100,9 +101,9 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 		}
 	}
 
-	var cacheCommitter *models.User
+	var cacheCommitter *user_model.User
 	if userCache == nil {
-		cacheCommitter = (*models.User)(nil)
+		cacheCommitter = (*user_model.User)(nil)
 		ok = false
 	} else {
 		cacheCommitter, ok = userCache[commit.Committer.Email]
@@ -111,8 +112,8 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
 	if ok {
 		apiCommitter = ToUser(cacheCommitter, nil)
 	} else {
-		committer, err := models.GetUserByEmail(commit.Committer.Email)
-		if err != nil && !models.IsErrUserNotExist(err) {
+		committer, err := user_model.GetUserByEmail(commit.Committer.Email)
+		if err != nil && !user_model.IsErrUserNotExist(err) {
 			return nil, err
 		} else if err == nil {
 			apiCommitter = ToUser(committer, nil)
diff --git a/modules/convert/issue.go b/modules/convert/issue.go
index 7363cfb8fb8..c67ff5427f9 100644
--- a/modules/convert/issue.go
+++ b/modules/convert/issue.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	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"
@@ -175,7 +176,7 @@ func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList {
 }
 
 // ToLabel converts Label to API format
-func ToLabel(label *models.Label, repo *models.Repository, org *models.User) *api.Label {
+func ToLabel(label *models.Label, repo *models.Repository, org *user_model.User) *api.Label {
 	result := &api.Label{
 		ID:          label.ID,
 		Name:        label.Name,
@@ -202,7 +203,7 @@ func ToLabel(label *models.Label, repo *models.Repository, org *models.User) *ap
 }
 
 // ToLabelList converts list of Label to API format
-func ToLabelList(labels []*models.Label, repo *models.Repository, org *models.User) []*api.Label {
+func ToLabelList(labels []*models.Label, repo *models.Repository, org *user_model.User) []*api.Label {
 	result := make([]*api.Label, len(labels))
 	for i := range labels {
 		result[i] = ToLabel(labels[i], repo, org)
diff --git a/modules/convert/pull.go b/modules/convert/pull.go
index 0f2b5af1b41..f892107f4b0 100644
--- a/modules/convert/pull.go
+++ b/modules/convert/pull.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
@@ -16,7 +17,7 @@ import (
 // ToAPIPullRequest assumes following fields have been assigned with valid values:
 // Required - Issue
 // Optional - Merger
-func ToAPIPullRequest(pr *models.PullRequest, doer *models.User) *api.PullRequest {
+func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRequest {
 	var (
 		baseBranch *git.Branch
 		headBranch *git.Branch
diff --git a/modules/convert/pull_review.go b/modules/convert/pull_review.go
index b461b27b5ab..b99d7bead6c 100644
--- a/modules/convert/pull_review.go
+++ b/modules/convert/pull_review.go
@@ -8,16 +8,17 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
 // ToPullReview convert a review to api format
-func ToPullReview(r *models.Review, doer *models.User) (*api.PullReview, error) {
+func ToPullReview(r *models.Review, doer *user_model.User) (*api.PullReview, error) {
 	if err := r.LoadAttributes(); err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			return nil, err
 		}
-		r.Reviewer = models.NewGhostUser()
+		r.Reviewer = user_model.NewGhostUser()
 	}
 
 	result := &api.PullReview{
@@ -53,7 +54,7 @@ func ToPullReview(r *models.Review, doer *models.User) (*api.PullReview, error)
 }
 
 // ToPullReviewList convert a list of review to it's api format
-func ToPullReviewList(rl []*models.Review, doer *models.User) ([]*api.PullReview, error) {
+func ToPullReviewList(rl []*models.Review, doer *user_model.User) ([]*api.PullReview, error) {
 	result := make([]*api.PullReview, 0, len(rl))
 	for i := range rl {
 		// show pending reviews only for the user who created them
@@ -70,12 +71,12 @@ func ToPullReviewList(rl []*models.Review, doer *models.User) ([]*api.PullReview
 }
 
 // ToPullReviewCommentList convert the CodeComments of an review to it's api format
-func ToPullReviewCommentList(review *models.Review, doer *models.User) ([]*api.PullReviewComment, error) {
+func ToPullReviewCommentList(review *models.Review, doer *user_model.User) ([]*api.PullReviewComment, error) {
 	if err := review.LoadAttributes(); err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			return nil, err
 		}
-		review.Reviewer = models.NewGhostUser()
+		review.Reviewer = user_model.NewGhostUser()
 	}
 
 	apiComments := make([]*api.PullReviewComment, 0, len(review.CodeComments))
diff --git a/modules/convert/status.go b/modules/convert/status.go
index bb2f964fe44..eb77c14dab3 100644
--- a/modules/convert/status.go
+++ b/modules/convert/status.go
@@ -6,6 +6,7 @@ package convert
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
@@ -23,7 +24,7 @@ func ToCommitStatus(status *models.CommitStatus) *api.CommitStatus {
 	}
 
 	if status.CreatorID != 0 {
-		creator, _ := models.GetUserByID(status.CreatorID)
+		creator, _ := user_model.GetUserByID(status.CreatorID)
 		apiStatus.Creator = ToUser(creator, nil)
 	}
 
diff --git a/modules/convert/user.go b/modules/convert/user.go
index 3f17ae4b4d5..ebe2f064605 100644
--- a/modules/convert/user.go
+++ b/modules/convert/user.go
@@ -6,12 +6,13 @@ package convert
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
-// ToUser convert models.User to api.User
+// ToUser convert user_model.User to api.User
 // if doer is set, private information is added if the doer has the permission to see it
-func ToUser(user, doer *models.User) *api.User {
+func ToUser(user, doer *user_model.User) *api.User {
 	if user == nil {
 		return nil
 	}
@@ -24,8 +25,8 @@ func ToUser(user, doer *models.User) *api.User {
 	return toUser(user, signed, authed)
 }
 
-// ToUsers convert list of models.User to list of api.User
-func ToUsers(doer *models.User, users []*models.User) []*api.User {
+// ToUsers convert list of user_model.User to list of api.User
+func ToUsers(doer *user_model.User, users []*user_model.User) []*api.User {
 	result := make([]*api.User, len(users))
 	for i := range users {
 		result[i] = ToUser(users[i], doer)
@@ -33,18 +34,18 @@ func ToUsers(doer *models.User, users []*models.User) []*api.User {
 	return result
 }
 
-// ToUserWithAccessMode convert models.User to api.User
+// ToUserWithAccessMode convert user_model.User to api.User
 // AccessMode is not none show add some more information
-func ToUserWithAccessMode(user *models.User, accessMode models.AccessMode) *api.User {
+func ToUserWithAccessMode(user *user_model.User, accessMode models.AccessMode) *api.User {
 	if user == nil {
 		return nil
 	}
 	return toUser(user, accessMode != models.AccessModeNone, false)
 }
 
-// toUser convert models.User to api.User
+// toUser convert user_model.User to api.User
 // signed shall only be set if requester is logged in. authed shall only be set if user is site admin or user himself
-func toUser(user *models.User, signed, authed bool) *api.User {
+func toUser(user *user_model.User, signed, authed bool) *api.User {
 	result := &api.User{
 		ID:          user.ID,
 		UserName:    user.Name,
@@ -81,7 +82,7 @@ func toUser(user *models.User, signed, authed bool) *api.User {
 }
 
 // User2UserSettings return UserSettings based on a user
-func User2UserSettings(user *models.User) api.UserSettings {
+func User2UserSettings(user *user_model.User) api.UserSettings {
 	return api.UserSettings{
 		FullName:      user.FullName,
 		Website:       user.Website,
diff --git a/modules/convert/user_test.go b/modules/convert/user_test.go
index ce174e30766..2ed962950ff 100644
--- a/modules/convert/user_test.go
+++ b/modules/convert/user_test.go
@@ -7,8 +7,8 @@ package convert
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -17,13 +17,13 @@ import (
 func TestUser_ToUser(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user1 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1, IsAdmin: true}).(*models.User)
+	user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, IsAdmin: true}).(*user_model.User)
 
 	apiUser := toUser(user1, true, true)
 	assert.True(t, apiUser.IsAdmin)
 	assert.Contains(t, apiUser.AvatarURL, "://")
 
-	user2 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2, IsAdmin: false}).(*models.User)
+	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2, IsAdmin: false}).(*user_model.User)
 
 	apiUser = toUser(user2, true, true)
 	assert.False(t, apiUser.IsAdmin)
@@ -32,7 +32,7 @@ func TestUser_ToUser(t *testing.T) {
 	assert.False(t, apiUser.IsAdmin)
 	assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility)
 
-	user31 := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}).(*models.User)
+	user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}).(*user_model.User)
 
 	apiUser = toUser(user31, true, true)
 	assert.False(t, apiUser.IsAdmin)
diff --git a/modules/doctor/misc.go b/modules/doctor/misc.go
index 2f748bcb718..1cf8024b983 100644
--- a/modules/doctor/misc.go
+++ b/modules/doctor/misc.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/repository"
@@ -134,7 +135,7 @@ func checkDaemonExport(logger log.Logger, autofix bool) error {
 		numRepos++
 
 		if owner, has := cache.Get(repo.OwnerID); has {
-			repo.Owner = owner.(*models.User)
+			repo.Owner = owner.(*user_model.User)
 		} else {
 			if err := repo.GetOwner(); err != nil {
 				return err
diff --git a/modules/gitgraph/graph_models.go b/modules/gitgraph/graph_models.go
index 95f761742ea..d7d198e01c7 100644
--- a/modules/gitgraph/graph_models.go
+++ b/modules/gitgraph/graph_models.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 )
@@ -91,7 +92,7 @@ func (graph *Graph) LoadAndProcessCommits(repository *models.Repository, gitRepo
 
 	var ok bool
 
-	emails := map[string]*models.User{}
+	emails := map[string]*user_model.User{}
 	keyMap := map[string]bool{}
 
 	for _, c := range graph.Commits {
@@ -106,7 +107,7 @@ func (graph *Graph) LoadAndProcessCommits(repository *models.Repository, gitRepo
 		if c.Commit.Author != nil {
 			email := c.Commit.Author.Email
 			if c.User, ok = emails[email]; !ok {
-				c.User, _ = models.GetUserByEmail(email)
+				c.User, _ = user_model.GetUserByEmail(email)
 				emails[email] = c.User
 			}
 		}
@@ -233,7 +234,7 @@ func newRefsFromRefNames(refNames []byte) []git.Reference {
 // Commit represents a commit at co-ordinate X, Y with the data
 type Commit struct {
 	Commit       *git.Commit
-	User         *models.User
+	User         *user_model.User
 	Verification *models.CommitVerification
 	Status       *models.CommitStatus
 	Flow         int64
diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go
index 4e133b4dd39..0b855460bd4 100644
--- a/modules/indexer/issues/indexer.go
+++ b/modules/indexer/issues/indexer.go
@@ -243,7 +243,7 @@ func populateIssueIndexer(ctx context.Context) {
 		}
 		repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
 			ListOptions: db.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize},
-			OrderBy:     models.SearchOrderByID,
+			OrderBy:     db.SearchOrderByID,
 			Private:     true,
 			Collaborate: util.OptionalBoolFalse,
 		})
diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go
index 772e7be1370..80f97ad9932 100644
--- a/modules/notification/action/action.go
+++ b/modules/notification/action/action.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification/base"
@@ -29,7 +30,7 @@ func NewNotifier() base.Notifier {
 	return &actionNotifier{}
 }
 
-func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	if err := issue.LoadPoster(); err != nil {
 		log.Error("issue.LoadPoster: %v", err)
 		return
@@ -54,7 +55,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.
 }
 
 // NotifyIssueChangeStatus notifies close or reopen issue to notifiers
-func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
+func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
 	// Compose comment action, could be plain comment, close or reopen issue/pull request.
 	// This object will be used to notify watchers in the end of function.
 	act := &models.Action{
@@ -87,8 +88,8 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *models.User, issue *model
 }
 
 // NotifyCreateIssueComment notifies comment on an issue to notifiers
-func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	act := &models.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
@@ -120,7 +121,7 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *models.User, repo *model
 	}
 }
 
-func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*models.User) {
+func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*user_model.User) {
 	if err := pull.LoadIssue(); err != nil {
 		log.Error("pull.LoadIssue: %v", err)
 		return
@@ -147,7 +148,7 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions
 	}
 }
 
-func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
+func (a *actionNotifier) NotifyRenameRepository(doer *user_model.User, repo *models.Repository, oldRepoName string) {
 	log.Trace("action.ChangeRepositoryName: %s/%s", doer.Name, repo.Name)
 
 	if err := models.NotifyWatchers(&models.Action{
@@ -163,7 +164,7 @@ func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.
 	}
 }
 
-func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
+func (a *actionNotifier) NotifyTransferRepository(doer *user_model.User, repo *models.Repository, oldOwnerName string) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
@@ -177,7 +178,7 @@ func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *model
 	}
 }
 
-func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (a *actionNotifier) NotifyCreateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
@@ -190,7 +191,7 @@ func (a *actionNotifier) NotifyCreateRepository(doer *models.User, u *models.Use
 	}
 }
 
-func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
+func (a *actionNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, repo *models.Repository) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
@@ -203,7 +204,7 @@ func (a *actionNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *
 	}
 }
 
-func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*models.User) {
+func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) {
 	if err := review.LoadReviewer(); err != nil {
 		log.Error("LoadReviewer '%d/%d': %v", review.ID, review.ReviewerID, err)
 		return
@@ -261,7 +262,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
 	}
 }
 
-func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: doer.ID,
 		ActUser:   doer,
@@ -275,7 +276,7 @@ func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mode
 	}
 }
 
-func (*actionNotifier) NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
+func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
 	reviewerName := review.Reviewer.Name
 	if len(review.OriginalAuthor) > 0 {
 		reviewerName = review.OriginalAuthor
@@ -295,7 +296,7 @@ func (*actionNotifier) NotifyPullRevieweDismiss(doer *models.User, review *model
 	}
 }
 
-func (a *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (a *actionNotifier) NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	data, err := json.Marshal(commits)
 	if err != nil {
 		log.Error("Marshal: %v", err)
@@ -328,7 +329,7 @@ func (a *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Rep
 	}
 }
 
-func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (a *actionNotifier) NotifyCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 	opType := models.ActionCommitRepo
 	if refType == "tag" {
 		// has sent same action in `NotifyPushCommits`, so skip it.
@@ -347,7 +348,7 @@ func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Reposit
 	}
 }
 
-func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (a *actionNotifier) NotifyDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 	opType := models.ActionDeleteBranch
 	if refType == "tag" {
 		// has sent same action in `NotifyPushCommits`, so skip it.
@@ -366,7 +367,7 @@ func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Reposit
 	}
 }
 
-func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (a *actionNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	data, err := json.Marshal(commits)
 	if err != nil {
 		log.Error("json.Marshal: %v", err)
@@ -387,7 +388,7 @@ func (a *actionNotifier) NotifySyncPushCommits(pusher *models.User, repo *models
 	}
 }
 
-func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (a *actionNotifier) NotifySyncCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: repo.OwnerID,
 		ActUser:   repo.MustOwner(),
@@ -401,7 +402,7 @@ func (a *actionNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Rep
 	}
 }
 
-func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (a *actionNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 	if err := models.NotifyWatchers(&models.Action{
 		ActUserID: repo.OwnerID,
 		ActUser:   repo.MustOwner(),
diff --git a/modules/notification/action/action_test.go b/modules/notification/action/action_test.go
index 448242bcdfd..2218bd46cb2 100644
--- a/modules/notification/action/action_test.go
+++ b/modules/notification/action/action_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -22,7 +23,7 @@ func TestMain(m *testing.M) {
 func TestRenameRepoAction(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{OwnerID: user.ID}).(*models.Repository)
 	repo.Owner = user
 
diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go
index 8f8aa659b45..24f6375a697 100644
--- a/modules/notification/base/notifier.go
+++ b/modules/notification/base/notifier.go
@@ -6,6 +6,7 @@ package base
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/repository"
 )
 
@@ -13,50 +14,50 @@ import (
 type Notifier interface {
 	Run()
 
-	NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository)
-	NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository)
-	NotifyDeleteRepository(doer *models.User, repo *models.Repository)
-	NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository)
-	NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string)
-	NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string)
+	NotifyCreateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository)
+	NotifyMigrateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository)
+	NotifyDeleteRepository(doer *user_model.User, repo *models.Repository)
+	NotifyForkRepository(doer *user_model.User, oldRepo, repo *models.Repository)
+	NotifyRenameRepository(doer *user_model.User, repo *models.Repository, oldRepoName string)
+	NotifyTransferRepository(doer *user_model.User, repo *models.Repository, oldOwnerName string)
 
-	NotifyNewIssue(issue *models.Issue, mentions []*models.User)
-	NotifyIssueChangeStatus(*models.User, *models.Issue, *models.Comment, bool)
-	NotifyIssueChangeMilestone(doer *models.User, issue *models.Issue, oldMilestoneID int64)
-	NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment)
-	NotifyPullReviewRequest(doer *models.User, issue *models.Issue, reviewer *models.User, isRequest bool, comment *models.Comment)
-	NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string)
-	NotifyIssueClearLabels(doer *models.User, issue *models.Issue)
-	NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string)
-	NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string)
-	NotifyIssueChangeLabels(doer *models.User, issue *models.Issue,
+	NotifyNewIssue(issue *models.Issue, mentions []*user_model.User)
+	NotifyIssueChangeStatus(*user_model.User, *models.Issue, *models.Comment, bool)
+	NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64)
+	NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment)
+	NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment)
+	NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string)
+	NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue)
+	NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string)
+	NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string)
+	NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue,
 		addedLabels []*models.Label, removedLabels []*models.Label)
 
-	NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User)
-	NotifyMergePullRequest(*models.PullRequest, *models.User)
-	NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest)
-	NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*models.User)
-	NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*models.User)
-	NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string)
-	NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment)
-	NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment)
+	NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User)
+	NotifyMergePullRequest(*models.PullRequest, *user_model.User)
+	NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest)
+	NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User)
+	NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User)
+	NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string)
+	NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment)
+	NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment)
 
-	NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-		issue *models.Issue, comment *models.Comment, mentions []*models.User)
-	NotifyUpdateComment(*models.User, *models.Comment, string)
-	NotifyDeleteComment(*models.User, *models.Comment)
+	NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+		issue *models.Issue, comment *models.Comment, mentions []*user_model.User)
+	NotifyUpdateComment(*user_model.User, *models.Comment, string)
+	NotifyDeleteComment(*user_model.User, *models.Comment)
 
 	NotifyNewRelease(rel *models.Release)
-	NotifyUpdateRelease(doer *models.User, rel *models.Release)
-	NotifyDeleteRelease(doer *models.User, rel *models.Release)
+	NotifyUpdateRelease(doer *user_model.User, rel *models.Release)
+	NotifyDeleteRelease(doer *user_model.User, rel *models.Release)
 
-	NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
-	NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string)
-	NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string)
+	NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
+	NotifyCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string)
+	NotifyDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string)
 
-	NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
-	NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string)
-	NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string)
+	NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
+	NotifySyncCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string)
+	NotifySyncDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string)
 
-	NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository)
+	NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *models.Repository)
 }
diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go
index 32fe259bca8..8a977e122bd 100644
--- a/modules/notification/base/null.go
+++ b/modules/notification/base/null.go
@@ -6,6 +6,7 @@ package base
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/repository"
 )
 
@@ -22,56 +23,56 @@ func (*NullNotifier) Run() {
 }
 
 // NotifyCreateIssueComment places a place holder function
-func (*NullNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (*NullNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 }
 
 // NotifyNewIssue places a place holder function
-func (*NullNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (*NullNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 }
 
 // NotifyIssueChangeStatus places a place holder function
-func (*NullNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
+func (*NullNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
 }
 
 // NotifyNewPullRequest places a place holder function
-func (*NullNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
+func (*NullNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
 }
 
 // NotifyPullRequestReview places a place holder function
-func (*NullNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*models.User) {
+func (*NullNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*user_model.User) {
 }
 
 // NotifyPullRequestCodeComment places a place holder function
-func (*NullNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*models.User) {
+func (*NullNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) {
 }
 
 // NotifyMergePullRequest places a place holder function
-func (*NullNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func (*NullNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 }
 
 // NotifyPullRequestSynchronized places a place holder function
-func (*NullNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
+func (*NullNotifier) NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) {
 }
 
 // NotifyPullRequestChangeTargetBranch places a place holder function
-func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) {
+func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) {
 }
 
 // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch
-func (*NullNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
+func (*NullNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) {
 }
 
 // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin
-func (*NullNotifier) NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
+func (*NullNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
 }
 
 // NotifyUpdateComment places a place holder function
-func (*NullNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
+func (*NullNotifier) NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) {
 }
 
 // NotifyDeleteComment places a place holder function
-func (*NullNotifier) NotifyDeleteComment(doer *models.User, c *models.Comment) {
+func (*NullNotifier) NotifyDeleteComment(doer *user_model.User, c *models.Comment) {
 }
 
 // NotifyNewRelease places a place holder function
@@ -79,94 +80,94 @@ func (*NullNotifier) NotifyNewRelease(rel *models.Release) {
 }
 
 // NotifyUpdateRelease places a place holder function
-func (*NullNotifier) NotifyUpdateRelease(doer *models.User, rel *models.Release) {
+func (*NullNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
 }
 
 // NotifyDeleteRelease places a place holder function
-func (*NullNotifier) NotifyDeleteRelease(doer *models.User, rel *models.Release) {
+func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
 }
 
 // NotifyIssueChangeMilestone places a place holder function
-func (*NullNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *models.Issue, oldMilestoneID int64) {
+func (*NullNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) {
 }
 
 // NotifyIssueChangeContent places a place holder function
-func (*NullNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
+func (*NullNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) {
 }
 
 // NotifyIssueChangeAssignee places a place holder function
-func (*NullNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+func (*NullNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
 }
 
 // NotifyPullReviewRequest places a place holder function
-func (*NullNotifier) NotifyPullReviewRequest(doer *models.User, issue *models.Issue, reviewer *models.User, isRequest bool, comment *models.Comment) {
+func (*NullNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) {
 }
 
 // NotifyIssueClearLabels places a place holder function
-func (*NullNotifier) NotifyIssueClearLabels(doer *models.User, issue *models.Issue) {
+func (*NullNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) {
 }
 
 // NotifyIssueChangeTitle places a place holder function
-func (*NullNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func (*NullNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 }
 
 // NotifyIssueChangeRef places a place holder function
-func (*NullNotifier) NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldTitle string) {
+func (*NullNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldTitle string) {
 }
 
 // NotifyIssueChangeLabels places a place holder function
-func (*NullNotifier) NotifyIssueChangeLabels(doer *models.User, issue *models.Issue,
+func (*NullNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue,
 	addedLabels []*models.Label, removedLabels []*models.Label) {
 }
 
 // NotifyCreateRepository places a place holder function
-func (*NullNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (*NullNotifier) NotifyCreateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 }
 
 // NotifyDeleteRepository places a place holder function
-func (*NullNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
+func (*NullNotifier) NotifyDeleteRepository(doer *user_model.User, repo *models.Repository) {
 }
 
 // NotifyForkRepository places a place holder function
-func (*NullNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
+func (*NullNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, repo *models.Repository) {
 }
 
 // NotifyMigrateRepository places a place holder function
-func (*NullNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (*NullNotifier) NotifyMigrateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 }
 
 // NotifyPushCommits notifies commits pushed to notifiers
-func (*NullNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (*NullNotifier) NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 }
 
 // NotifyCreateRef notifies branch or tag creation to notifiers
-func (*NullNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (*NullNotifier) NotifyCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 }
 
 // NotifyDeleteRef notifies branch or tag deletion to notifiers
-func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (*NullNotifier) NotifyDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 }
 
 // NotifyRenameRepository places a place holder function
-func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
+func (*NullNotifier) NotifyRenameRepository(doer *user_model.User, repo *models.Repository, oldRepoName string) {
 }
 
 // NotifyTransferRepository places a place holder function
-func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
+func (*NullNotifier) NotifyTransferRepository(doer *user_model.User, repo *models.Repository, oldOwnerName string) {
 }
 
 // NotifySyncPushCommits places a place holder function
-func (*NullNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (*NullNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 }
 
 // NotifySyncCreateRef places a place holder function
-func (*NullNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (*NullNotifier) NotifySyncCreateRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 }
 
 // NotifySyncDeleteRef places a place holder function
-func (*NullNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
+func (*NullNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *models.Repository, refType, refFullName string) {
 }
 
 // NotifyRepoPendingTransfer places a place holder function
-func (*NullNotifier) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
+func (*NullNotifier) NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *models.Repository) {
 }
diff --git a/modules/notification/indexer/indexer.go b/modules/notification/indexer/indexer.go
index 109eb1f62d8..03914db03b7 100644
--- a/modules/notification/indexer/indexer.go
+++ b/modules/notification/indexer/indexer.go
@@ -6,6 +6,7 @@ package indexer
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	code_indexer "code.gitea.io/gitea/modules/indexer/code"
 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
@@ -29,8 +30,8 @@ func NewNotifier() base.Notifier {
 	return &indexerNotifier{}
 }
 
-func (r *indexerNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (r *indexerNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	if comment.Type == models.CommentTypeComment {
 		if issue.Comments == nil {
 			if err := issue.LoadDiscussComments(); err != nil {
@@ -45,15 +46,15 @@ func (r *indexerNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode
 	}
 }
 
-func (r *indexerNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (r *indexerNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	issue_indexer.UpdateIssueIndexer(issue)
 }
 
-func (r *indexerNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
+func (r *indexerNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
 	issue_indexer.UpdateIssueIndexer(pr.Issue)
 }
 
-func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
+func (r *indexerNotifier) NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) {
 	if c.Type == models.CommentTypeComment {
 		var found bool
 		if c.Issue.Comments != nil {
@@ -77,7 +78,7 @@ func (r *indexerNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme
 	}
 }
 
-func (r *indexerNotifier) NotifyDeleteComment(doer *models.User, comment *models.Comment) {
+func (r *indexerNotifier) NotifyDeleteComment(doer *user_model.User, comment *models.Comment) {
 	if comment.Type == models.CommentTypeComment {
 		if err := comment.LoadIssue(); err != nil {
 			log.Error("LoadIssue: %v", err)
@@ -106,14 +107,14 @@ func (r *indexerNotifier) NotifyDeleteComment(doer *models.User, comment *models
 	}
 }
 
-func (r *indexerNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
+func (r *indexerNotifier) NotifyDeleteRepository(doer *user_model.User, repo *models.Repository) {
 	issue_indexer.DeleteRepoIssueIndexer(repo)
 	if setting.Indexer.RepoIndexerEnabled {
 		code_indexer.UpdateRepoIndexer(repo)
 	}
 }
 
-func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (r *indexerNotifier) NotifyMigrateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	issue_indexer.UpdateRepoIndexer(repo)
 	if setting.Indexer.RepoIndexerEnabled && !repo.IsEmpty {
 		code_indexer.UpdateRepoIndexer(repo)
@@ -123,7 +124,7 @@ func (r *indexerNotifier) NotifyMigrateRepository(doer *models.User, u *models.U
 	}
 }
 
-func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (r *indexerNotifier) NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	if setting.Indexer.RepoIndexerEnabled && opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
 		code_indexer.UpdateRepoIndexer(repo)
 	}
@@ -132,7 +133,7 @@ func (r *indexerNotifier) NotifyPushCommits(pusher *models.User, repo *models.Re
 	}
 }
 
-func (r *indexerNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (r *indexerNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	if setting.Indexer.RepoIndexerEnabled && opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
 		code_indexer.UpdateRepoIndexer(repo)
 	}
@@ -141,14 +142,14 @@ func (r *indexerNotifier) NotifySyncPushCommits(pusher *models.User, repo *model
 	}
 }
 
-func (r *indexerNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
+func (r *indexerNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) {
 	issue_indexer.UpdateIssueIndexer(issue)
 }
 
-func (r *indexerNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func (r *indexerNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 	issue_indexer.UpdateIssueIndexer(issue)
 }
 
-func (r *indexerNotifier) NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string) {
+func (r *indexerNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string) {
 	issue_indexer.UpdateIssueIndexer(issue)
 }
diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go
index 5bfb0b3ef8b..c1c9e961355 100644
--- a/modules/notification/mail/mail.go
+++ b/modules/notification/mail/mail.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification/base"
 	"code.gitea.io/gitea/services/mailer"
@@ -26,8 +27,8 @@ func NewNotifier() base.Notifier {
 	return &mailNotifier{}
 }
 
-func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	var act models.ActionType
 	if comment.Type == models.CommentTypeClose {
 		act = models.ActionCloseIssue
@@ -46,13 +47,13 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.
 	}
 }
 
-func (m *mailNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (m *mailNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue, mentions); err != nil {
 		log.Error("MailParticipants: %v", err)
 	}
 }
 
-func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
+func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
 	var actionType models.ActionType
 	if issue.IsPull {
 		if isClosed {
@@ -73,7 +74,7 @@ func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.
 	}
 }
 
-func (m *mailNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func (m *mailNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 	if err := issue.LoadPullRequest(); err != nil {
 		log.Error("issue.LoadPullRequest: %v", err)
 		return
@@ -85,13 +86,13 @@ func (m *mailNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.I
 	}
 }
 
-func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
+func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
 	if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil {
 		log.Error("MailParticipants: %v", err)
 	}
 }
 
-func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*models.User) {
+func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*user_model.User) {
 	var act models.ActionType
 	if comment.Type == models.CommentTypeClose {
 		act = models.ActionCloseIssue
@@ -105,32 +106,32 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models
 	}
 }
 
-func (m *mailNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*models.User) {
+func (m *mailNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) {
 	if err := mailer.MailMentionsComment(pr, comment, mentions); err != nil {
 		log.Error("MailMentionsComment: %v", err)
 	}
 }
 
-func (m *mailNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
 	// mail only sent to added assignees and not self-assignee
-	if !removed && doer.ID != assignee.ID && assignee.EmailNotifications() == models.EmailNotificationsEnabled {
+	if !removed && doer.ID != assignee.ID && assignee.EmailNotifications() == user_model.EmailNotificationsEnabled {
 		ct := fmt.Sprintf("Assigned #%d.", issue.Index)
-		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*models.User{assignee}); err != nil {
+		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{assignee}); err != nil {
 			log.Error("Error in SendIssueAssignedMail for issue[%d] to assignee[%d]: %v", issue.ID, assignee.ID, err)
 		}
 	}
 }
 
-func (m *mailNotifier) NotifyPullReviewRequest(doer *models.User, issue *models.Issue, reviewer *models.User, isRequest bool, comment *models.Comment) {
-	if isRequest && doer.ID != reviewer.ID && reviewer.EmailNotifications() == models.EmailNotificationsEnabled {
+func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) {
+	if isRequest && doer.ID != reviewer.ID && reviewer.EmailNotifications() == user_model.EmailNotificationsEnabled {
 		ct := fmt.Sprintf("Requested to review %s.", issue.HTMLURL())
-		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*models.User{reviewer}); err != nil {
+		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{reviewer}); err != nil {
 			log.Error("Error in SendIssueAssignedMail for issue[%d] to reviewer[%d]: %v", issue.ID, reviewer.ID, err)
 		}
 	}
 }
 
-func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 	if err := pr.LoadIssue(); err != nil {
 		log.Error("pr.LoadIssue: %v", err)
 		return
@@ -140,7 +141,7 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mode
 	}
 }
 
-func (m *mailNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
+func (m *mailNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) {
 	var err error
 	if err = comment.LoadIssue(); err != nil {
 		log.Error("comment.LoadIssue: %v", err)
@@ -164,7 +165,7 @@ func (m *mailNotifier) NotifyPullRequestPushCommits(doer *models.User, pr *model
 	m.NotifyCreateIssueComment(doer, comment.Issue.Repo, comment.Issue, comment, nil)
 }
 
-func (m *mailNotifier) NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
+func (m *mailNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
 	if err := mailer.MailParticipantsComment(comment, models.ActionPullReviewDismissed, review.Issue, nil); err != nil {
 		log.Error("MailParticipantsComment: %v", err)
 	}
@@ -183,7 +184,7 @@ func (m *mailNotifier) NotifyNewRelease(rel *models.Release) {
 	mailer.MailNewRelease(rel)
 }
 
-func (m *mailNotifier) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
+func (m *mailNotifier) NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *models.Repository) {
 	if err := mailer.SendRepoTransferNotifyMail(doer, newOwner, repo); err != nil {
 		log.Error("NotifyRepoPendingTransfer: %v", err)
 	}
diff --git a/modules/notification/notification.go b/modules/notification/notification.go
index b574f3ccda2..9bea38faf6e 100644
--- a/modules/notification/notification.go
+++ b/modules/notification/notification.go
@@ -6,6 +6,7 @@ package notification
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification/action"
 	"code.gitea.io/gitea/modules/notification/base"
 	"code.gitea.io/gitea/modules/notification/indexer"
@@ -38,92 +39,92 @@ func NewContext() {
 }
 
 // NotifyCreateIssueComment notifies issue comment related message to notifiers
-func NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyCreateIssueComment(doer, repo, issue, comment, mentions)
 	}
 }
 
 // NotifyNewIssue notifies new issue to notifiers
-func NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyNewIssue(issue, mentions)
 	}
 }
 
 // NotifyIssueChangeStatus notifies close or reopen issue to notifiers
-func NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
+func NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeStatus(doer, issue, actionComment, closeOrReopen)
 	}
 }
 
 // NotifyMergePullRequest notifies merge pull request to notifiers
-func NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyMergePullRequest(pr, doer)
 	}
 }
 
 // NotifyNewPullRequest notifies new pull request to notifiers
-func NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
+func NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyNewPullRequest(pr, mentions)
 	}
 }
 
 // NotifyPullRequestSynchronized notifies Synchronized pull request
-func NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
+func NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRequestSynchronized(doer, pr)
 	}
 }
 
 // NotifyPullRequestReview notifies new pull request review
-func NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*models.User) {
+func NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRequestReview(pr, review, comment, mentions)
 	}
 }
 
 // NotifyPullRequestCodeComment notifies new pull request code comment
-func NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*models.User) {
+func NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRequestCodeComment(pr, comment, mentions)
 	}
 }
 
 // NotifyPullRequestChangeTargetBranch notifies when a pull request's target branch was changed
-func NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) {
+func NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRequestChangeTargetBranch(doer, pr, oldBranch)
 	}
 }
 
 // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch
-func NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
+func NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRequestPushCommits(doer, pr, comment)
 	}
 }
 
 // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin
-func NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
+func NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullRevieweDismiss(doer, review, comment)
 	}
 }
 
 // NotifyUpdateComment notifies update comment to notifiers
-func NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
+func NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyUpdateComment(doer, c, oldContent)
 	}
 }
 
 // NotifyDeleteComment notifies delete comment to notifiers
-func NotifyDeleteComment(doer *models.User, c *models.Comment) {
+func NotifyDeleteComment(doer *user_model.User, c *models.Comment) {
 	for _, notifier := range notifiers {
 		notifier.NotifyDeleteComment(doer, c)
 	}
@@ -137,70 +138,70 @@ func NotifyNewRelease(rel *models.Release) {
 }
 
 // NotifyUpdateRelease notifies update release to notifiers
-func NotifyUpdateRelease(doer *models.User, rel *models.Release) {
+func NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
 	for _, notifier := range notifiers {
 		notifier.NotifyUpdateRelease(doer, rel)
 	}
 }
 
 // NotifyDeleteRelease notifies delete release to notifiers
-func NotifyDeleteRelease(doer *models.User, rel *models.Release) {
+func NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
 	for _, notifier := range notifiers {
 		notifier.NotifyDeleteRelease(doer, rel)
 	}
 }
 
 // NotifyIssueChangeMilestone notifies change milestone to notifiers
-func NotifyIssueChangeMilestone(doer *models.User, issue *models.Issue, oldMilestoneID int64) {
+func NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeMilestone(doer, issue, oldMilestoneID)
 	}
 }
 
 // NotifyIssueChangeContent notifies change content to notifiers
-func NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
+func NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeContent(doer, issue, oldContent)
 	}
 }
 
 // NotifyIssueChangeAssignee notifies change content to notifiers
-func NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+func NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeAssignee(doer, issue, assignee, removed, comment)
 	}
 }
 
 // NotifyPullReviewRequest notifies Request Review change
-func NotifyPullReviewRequest(doer *models.User, issue *models.Issue, reviewer *models.User, isRequest bool, comment *models.Comment) {
+func NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPullReviewRequest(doer, issue, reviewer, isRequest, comment)
 	}
 }
 
 // NotifyIssueClearLabels notifies clear labels to notifiers
-func NotifyIssueClearLabels(doer *models.User, issue *models.Issue) {
+func NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueClearLabels(doer, issue)
 	}
 }
 
 // NotifyIssueChangeTitle notifies change title to notifiers
-func NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeTitle(doer, issue, oldTitle)
 	}
 }
 
 // NotifyIssueChangeRef notifies change reference to notifiers
-func NotifyIssueChangeRef(doer *models.User, issue *models.Issue, oldRef string) {
+func NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeRef(doer, issue, oldRef)
 	}
 }
 
 // NotifyIssueChangeLabels notifies change labels to notifiers
-func NotifyIssueChangeLabels(doer *models.User, issue *models.Issue,
+func NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue,
 	addedLabels []*models.Label, removedLabels []*models.Label) {
 	for _, notifier := range notifiers {
 		notifier.NotifyIssueChangeLabels(doer, issue, addedLabels, removedLabels)
@@ -208,91 +209,91 @@ func NotifyIssueChangeLabels(doer *models.User, issue *models.Issue,
 }
 
 // NotifyCreateRepository notifies create repository to notifiers
-func NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func NotifyCreateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	for _, notifier := range notifiers {
 		notifier.NotifyCreateRepository(doer, u, repo)
 	}
 }
 
 // NotifyMigrateRepository notifies create repository to notifiers
-func NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func NotifyMigrateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	for _, notifier := range notifiers {
 		notifier.NotifyMigrateRepository(doer, u, repo)
 	}
 }
 
 // NotifyTransferRepository notifies create repository to notifiers
-func NotifyTransferRepository(doer *models.User, repo *models.Repository, newOwnerName string) {
+func NotifyTransferRepository(doer *user_model.User, repo *models.Repository, newOwnerName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyTransferRepository(doer, repo, newOwnerName)
 	}
 }
 
 // NotifyDeleteRepository notifies delete repository to notifiers
-func NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
+func NotifyDeleteRepository(doer *user_model.User, repo *models.Repository) {
 	for _, notifier := range notifiers {
 		notifier.NotifyDeleteRepository(doer, repo)
 	}
 }
 
 // NotifyForkRepository notifies fork repository to notifiers
-func NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
+func NotifyForkRepository(doer *user_model.User, oldRepo, repo *models.Repository) {
 	for _, notifier := range notifiers {
 		notifier.NotifyForkRepository(doer, oldRepo, repo)
 	}
 }
 
 // NotifyRenameRepository notifies repository renamed
-func NotifyRenameRepository(doer *models.User, repo *models.Repository, oldName string) {
+func NotifyRenameRepository(doer *user_model.User, repo *models.Repository, oldName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyRenameRepository(doer, repo, oldName)
 	}
 }
 
 // NotifyPushCommits notifies commits pushed to notifiers
-func NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	for _, notifier := range notifiers {
 		notifier.NotifyPushCommits(pusher, repo, opts, commits)
 	}
 }
 
 // NotifyCreateRef notifies branch or tag creation to notifiers
-func NotifyCreateRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func NotifyCreateRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyCreateRef(pusher, repo, refType, refFullName)
 	}
 }
 
 // NotifyDeleteRef notifies branch or tag deletion to notifiers
-func NotifyDeleteRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func NotifyDeleteRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifyDeleteRef(pusher, repo, refType, refFullName)
 	}
 }
 
 // NotifySyncPushCommits notifies commits pushed to notifiers
-func NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	for _, notifier := range notifiers {
 		notifier.NotifySyncPushCommits(pusher, repo, opts, commits)
 	}
 }
 
 // NotifySyncCreateRef notifies branch or tag creation to notifiers
-func NotifySyncCreateRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func NotifySyncCreateRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifySyncCreateRef(pusher, repo, refType, refFullName)
 	}
 }
 
 // NotifySyncDeleteRef notifies branch or tag deletion to notifiers
-func NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func NotifySyncDeleteRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	for _, notifier := range notifiers {
 		notifier.NotifySyncDeleteRef(pusher, repo, refType, refFullName)
 	}
 }
 
 // NotifyRepoPendingTransfer notifies creation of pending transfer to notifiers
-func NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
+func NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *models.Repository) {
 	for _, notifier := range notifiers {
 		notifier.NotifyRepoPendingTransfer(doer, newOwner, repo)
 	}
diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go
index f372d6759ce..04967fc5891 100644
--- a/modules/notification/ui/ui.go
+++ b/modules/notification/ui/ui.go
@@ -6,6 +6,7 @@ package ui
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification/base"
@@ -50,8 +51,8 @@ func (ns *notificationService) Run() {
 	graceful.GetManager().RunWithShutdownFns(ns.issueQueue.Run)
 }
 
-func (ns *notificationService) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (ns *notificationService) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	var opts = issueNotificationOpts{
 		IssueID:              issue.ID,
 		NotificationAuthorID: doer.ID,
@@ -73,7 +74,7 @@ func (ns *notificationService) NotifyCreateIssueComment(doer *models.User, repo
 	}
 }
 
-func (ns *notificationService) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (ns *notificationService) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	_ = ns.issueQueue.Push(issueNotificationOpts{
 		IssueID:              issue.ID,
 		NotificationAuthorID: issue.Poster.ID,
@@ -87,14 +88,14 @@ func (ns *notificationService) NotifyNewIssue(issue *models.Issue, mentions []*m
 	}
 }
 
-func (ns *notificationService) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
+func (ns *notificationService) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
 	_ = ns.issueQueue.Push(issueNotificationOpts{
 		IssueID:              issue.ID,
 		NotificationAuthorID: doer.ID,
 	})
 }
 
-func (ns *notificationService) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func (ns *notificationService) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 	if err := issue.LoadPullRequest(); err != nil {
 		log.Error("issue.LoadPullRequest: %v", err)
 		return
@@ -107,14 +108,14 @@ func (ns *notificationService) NotifyIssueChangeTitle(doer *models.User, issue *
 	}
 }
 
-func (ns *notificationService) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func (ns *notificationService) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 	_ = ns.issueQueue.Push(issueNotificationOpts{
 		IssueID:              pr.Issue.ID,
 		NotificationAuthorID: doer.ID,
 	})
 }
 
-func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) {
+func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) {
 	if err := pr.LoadIssue(); err != nil {
 		log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err)
 		return
@@ -149,7 +150,7 @@ func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, ment
 	}
 }
 
-func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, c *models.Comment, mentions []*models.User) {
+func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, c *models.Comment, mentions []*user_model.User) {
 	var opts = issueNotificationOpts{
 		IssueID:              pr.Issue.ID,
 		NotificationAuthorID: r.Reviewer.ID,
@@ -171,7 +172,7 @@ func (ns *notificationService) NotifyPullRequestReview(pr *models.PullRequest, r
 	}
 }
 
-func (ns *notificationService) NotifyPullRequestCodeComment(pr *models.PullRequest, c *models.Comment, mentions []*models.User) {
+func (ns *notificationService) NotifyPullRequestCodeComment(pr *models.PullRequest, c *models.Comment, mentions []*user_model.User) {
 	for _, mention := range mentions {
 		_ = ns.issueQueue.Push(issueNotificationOpts{
 			IssueID:              pr.Issue.ID,
@@ -182,7 +183,7 @@ func (ns *notificationService) NotifyPullRequestCodeComment(pr *models.PullReque
 	}
 }
 
-func (ns *notificationService) NotifyPullRequestPushCommits(doer *models.User, pr *models.PullRequest, comment *models.Comment) {
+func (ns *notificationService) NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) {
 	var opts = issueNotificationOpts{
 		IssueID:              pr.IssueID,
 		NotificationAuthorID: doer.ID,
@@ -191,7 +192,7 @@ func (ns *notificationService) NotifyPullRequestPushCommits(doer *models.User, p
 	_ = ns.issueQueue.Push(opts)
 }
 
-func (ns *notificationService) NotifyPullRevieweDismiss(doer *models.User, review *models.Review, comment *models.Comment) {
+func (ns *notificationService) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) {
 	var opts = issueNotificationOpts{
 		IssueID:              review.IssueID,
 		NotificationAuthorID: doer.ID,
@@ -200,7 +201,7 @@ func (ns *notificationService) NotifyPullRevieweDismiss(doer *models.User, revie
 	_ = ns.issueQueue.Push(opts)
 }
 
-func (ns *notificationService) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+func (ns *notificationService) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
 	if !removed {
 		var opts = issueNotificationOpts{
 			IssueID:              issue.ID,
@@ -216,7 +217,7 @@ func (ns *notificationService) NotifyIssueChangeAssignee(doer *models.User, issu
 	}
 }
 
-func (ns *notificationService) NotifyPullReviewRequest(doer *models.User, issue *models.Issue, reviewer *models.User, isRequest bool, comment *models.Comment) {
+func (ns *notificationService) NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) {
 	if isRequest {
 		var opts = issueNotificationOpts{
 			IssueID:              issue.ID,
@@ -232,7 +233,7 @@ func (ns *notificationService) NotifyPullReviewRequest(doer *models.User, issue
 	}
 }
 
-func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *models.User, repo *models.Repository) {
+func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *models.Repository) {
 	if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil {
 		log.Error("NotifyRepoPendingTransfer: %v", err)
 	}
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
index d5a947d9ac1..8fe456e1c96 100644
--- a/modules/notification/webhook/webhook.go
+++ b/modules/notification/webhook/webhook.go
@@ -7,6 +7,7 @@ package webhook
 import (
 	"code.gitea.io/gitea/models"
 	"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/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -31,7 +32,7 @@ func NewNotifier() base.Notifier {
 	return &webhookNotifier{}
 }
 
-func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *models.Issue) {
+func (m *webhookNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) {
 	if err := issue.LoadPoster(); err != nil {
 		log.Error("loadPoster: %v", err)
 		return
@@ -71,7 +72,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *model
 	}
 }
 
-func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) {
+func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, repo *models.Repository) {
 	oldMode, _ := models.AccessLevel(doer, oldRepo)
 	mode, _ := models.AccessLevel(doer, repo)
 
@@ -99,7 +100,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *models.User, oldRepo, repo
 	}
 }
 
-func (m *webhookNotifier) NotifyCreateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (m *webhookNotifier) NotifyCreateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	// Add to hook queue for created repo after session commit.
 	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 		Action:       api.HookRepoCreated,
@@ -111,7 +112,7 @@ func (m *webhookNotifier) NotifyCreateRepository(doer *models.User, u *models.Us
 	}
 }
 
-func (m *webhookNotifier) NotifyDeleteRepository(doer *models.User, repo *models.Repository) {
+func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *models.Repository) {
 	u := repo.MustOwner()
 
 	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
@@ -124,7 +125,7 @@ func (m *webhookNotifier) NotifyDeleteRepository(doer *models.User, repo *models
 	}
 }
 
-func (m *webhookNotifier) NotifyMigrateRepository(doer *models.User, u *models.User, repo *models.Repository) {
+func (m *webhookNotifier) NotifyMigrateRepository(doer *user_model.User, u *user_model.User, repo *models.Repository) {
 	// Add to hook queue for created repo after session commit.
 	if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
 		Action:       api.HookRepoCreated,
@@ -136,7 +137,7 @@ func (m *webhookNotifier) NotifyMigrateRepository(doer *models.User, u *models.U
 	}
 }
 
-func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) {
 	if issue.IsPull {
 		mode, _ := models.AccessLevelUnit(doer, issue.Repo, unit.TypePullRequests)
 
@@ -182,7 +183,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *mo
 	}
 }
 
-func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) {
+func (m *webhookNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) {
 	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
 	var err error
 	if issue.IsPull {
@@ -223,7 +224,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *models.User, issue *model
 	}
 }
 
-func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
+func (m *webhookNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) {
 	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
 	var err error
 	if issue.IsPull {
@@ -263,7 +264,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *models.User, issue *mode
 	}
 }
 
-func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models.User) {
+func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) {
 	if err := issue.LoadRepo(); err != nil {
 		log.Error("issue.LoadRepo: %v", err)
 		return
@@ -285,7 +286,7 @@ func (m *webhookNotifier) NotifyNewIssue(issue *models.Issue, mentions []*models
 	}
 }
 
-func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*models.User) {
+func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*user_model.User) {
 	if err := pull.LoadIssue(); err != nil {
 		log.Error("pull.LoadIssue: %v", err)
 		return
@@ -311,7 +312,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *models.PullRequest, mention
 	}
 }
 
-func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string) {
+func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) {
 	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
 	var err error
 	if issue.IsPull {
@@ -347,7 +348,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *models.User, issue *mod
 	}
 }
 
-func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comment, oldContent string) {
+func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) {
 	var err error
 
 	if err = c.LoadPoster(); err != nil {
@@ -400,8 +401,8 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *models.User, c *models.Comme
 	}
 }
 
-func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.Repository,
-	issue *models.Issue, comment *models.Comment, mentions []*models.User) {
+func (m *webhookNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *models.Repository,
+	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) {
 	mode, _ := models.AccessLevel(doer, repo)
 
 	var err error
@@ -430,7 +431,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(doer *models.User, repo *mode
 	}
 }
 
-func (m *webhookNotifier) NotifyDeleteComment(doer *models.User, comment *models.Comment) {
+func (m *webhookNotifier) NotifyDeleteComment(doer *user_model.User, comment *models.Comment) {
 	var err error
 
 	if err = comment.LoadPoster(); err != nil {
@@ -475,7 +476,7 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *models.User, comment *models
 
 }
 
-func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *models.Issue,
+func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue,
 	addedLabels []*models.Label, removedLabels []*models.Label) {
 	var err error
 
@@ -520,7 +521,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *models.User, issue *mode
 	}
 }
 
-func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *models.Issue, oldMilestoneID int64) {
+func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) {
 	var hookAction api.HookIssueAction
 	var err error
 	if issue.MilestoneID > 0 {
@@ -562,7 +563,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *models.User, issue *m
 	}
 }
 
-func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	apiPusher := convert.ToUser(pusher, nil)
 	apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
 	if err != nil {
@@ -585,7 +586,7 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *models.User, repo *models.Re
 	}
 }
 
-func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) {
+func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) {
 	// Reload pull request information.
 	if err := pr.LoadAttributes(); err != nil {
 		log.Error("LoadAttributes: %v", err)
@@ -623,7 +624,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *mod
 	}
 }
 
-func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User, pr *models.PullRequest, oldBranch string) {
+func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) {
 	issue := pr.Issue
 	if !issue.IsPull {
 		return
@@ -654,7 +655,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *models.User,
 	}
 }
 
-func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*models.User) {
+func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) {
 	var reviewHookType webhook.HookEventType
 
 	switch review.Type {
@@ -695,7 +696,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *models.PullRequest, review
 	}
 }
 
-func (m *webhookNotifier) NotifyCreateRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func (m *webhookNotifier) NotifyCreateRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	apiPusher := convert.ToUser(pusher, nil)
 	apiRepo := convert.ToRepo(repo, models.AccessModeNone)
 	refName := git.RefEndName(refFullName)
@@ -725,7 +726,7 @@ func (m *webhookNotifier) NotifyCreateRef(pusher *models.User, repo *models.Repo
 	}
 }
 
-func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *models.PullRequest) {
+func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) {
 	if err := pr.LoadIssue(); err != nil {
 		log.Error("pr.LoadIssue: %v", err)
 		return
@@ -746,7 +747,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *models.User, pr *m
 	}
 }
 
-func (m *webhookNotifier) NotifyDeleteRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func (m *webhookNotifier) NotifyDeleteRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	apiPusher := convert.ToUser(pusher, nil)
 	apiRepo := convert.ToRepo(repo, models.AccessModeNone)
 	refName := git.RefEndName(refFullName)
@@ -762,7 +763,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *models.User, repo *models.Repo
 	}
 }
 
-func sendReleaseHook(doer *models.User, rel *models.Release, action api.HookReleaseAction) {
+func sendReleaseHook(doer *user_model.User, rel *models.Release, action api.HookReleaseAction) {
 	if err := rel.LoadAttributes(); err != nil {
 		log.Error("LoadAttributes: %v", err)
 		return
@@ -783,15 +784,15 @@ func (m *webhookNotifier) NotifyNewRelease(rel *models.Release) {
 	sendReleaseHook(rel.Publisher, rel, api.HookReleasePublished)
 }
 
-func (m *webhookNotifier) NotifyUpdateRelease(doer *models.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
 	sendReleaseHook(doer, rel, api.HookReleaseUpdated)
 }
 
-func (m *webhookNotifier) NotifyDeleteRelease(doer *models.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
 	sendReleaseHook(doer, rel, api.HookReleaseDeleted)
 }
 
-func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
+func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *models.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
 	apiPusher := convert.ToUser(pusher, nil)
 	apiCommits, apiHeadCommit, err := commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
 	if err != nil {
@@ -814,10 +815,10 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *models.User, repo *model
 	}
 }
 
-func (m *webhookNotifier) NotifySyncCreateRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func (m *webhookNotifier) NotifySyncCreateRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	m.NotifyCreateRef(pusher, repo, refType, refFullName)
 }
 
-func (m *webhookNotifier) NotifySyncDeleteRef(pusher *models.User, repo *models.Repository, refType, refFullName string) {
+func (m *webhookNotifier) NotifySyncDeleteRef(pusher *user_model.User, repo *models.Repository, refType, refFullName string) {
 	m.NotifyDeleteRef(pusher, repo, refType, refFullName)
 }
diff --git a/modules/private/serv.go b/modules/private/serv.go
index c378dc6dc76..7e4805d60c2 100644
--- a/modules/private/serv.go
+++ b/modules/private/serv.go
@@ -11,6 +11,7 @@ import (
 	"net/url"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/setting"
 )
@@ -18,11 +19,11 @@ import (
 // KeyAndOwner is the response from ServNoCommand
 type KeyAndOwner struct {
 	Key   *models.PublicKey `json:"key"`
-	Owner *models.User      `json:"user"`
+	Owner *user_model.User  `json:"user"`
 }
 
 // ServNoCommand returns information about the provided key
-func ServNoCommand(ctx context.Context, keyID int64) (*models.PublicKey, *models.User, error) {
+func ServNoCommand(ctx context.Context, keyID int64) (*models.PublicKey, *user_model.User, error) {
 	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/serv/none/%d",
 		keyID)
 	resp, err := newInternalRequest(ctx, reqURL, "GET").Response()
diff --git a/modules/repofiles/commit_status.go b/modules/repofiles/commit_status.go
new file mode 100644
index 00000000000..2074b4b464c
--- /dev/null
+++ b/modules/repofiles/commit_status.go
@@ -0,0 +1,42 @@
+// 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 repofiles
+
+import (
+	"fmt"
+
+	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/git"
+)
+
+// CreateCommitStatus creates a new CommitStatus given a bunch of parameters
+// NOTE: All text-values will be trimmed from whitespaces.
+// Requires: Repo, Creator, SHA
+func CreateCommitStatus(repo *models.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error {
+	repoPath := repo.RepoPath()
+
+	// confirm that commit is exist
+	gitRepo, err := git.OpenRepository(repoPath)
+	if err != nil {
+		return fmt.Errorf("OpenRepository[%s]: %v", repoPath, err)
+	}
+	if _, err := gitRepo.GetCommit(sha); err != nil {
+		gitRepo.Close()
+		return fmt.Errorf("GetCommit[%s]: %v", sha, err)
+	}
+	gitRepo.Close()
+
+	if err := models.NewCommitStatus(models.NewCommitStatusOptions{
+		Repo:         repo,
+		Creator:      creator,
+		SHA:          sha,
+		CommitStatus: status,
+	}); err != nil {
+		return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %v", repo.ID, creator.ID, sha, err)
+	}
+
+	return nil
+}
diff --git a/modules/repository/commits.go b/modules/repository/commits.go
index a545ce952ba..8e727c95d0e 100644
--- a/modules/repository/commits.go
+++ b/modules/repository/commits.go
@@ -9,8 +9,8 @@ import (
 	"net/url"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/avatars"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
@@ -35,14 +35,14 @@ type PushCommits struct {
 	Len        int
 
 	avatars    map[string]string
-	emailUsers map[string]*models.User
+	emailUsers map[string]*user_model.User
 }
 
 // NewPushCommits creates a new PushCommits object.
 func NewPushCommits() *PushCommits {
 	return &PushCommits{
 		avatars:    make(map[string]string),
-		emailUsers: make(map[string]*models.User),
+		emailUsers: make(map[string]*user_model.User),
 	}
 }
 
@@ -52,7 +52,7 @@ func (pc *PushCommits) toAPIPayloadCommit(repoPath, repoLink string, commit *Pus
 	authorUsername := ""
 	author, ok := pc.emailUsers[commit.AuthorEmail]
 	if !ok {
-		author, err = models.GetUserByEmail(commit.AuthorEmail)
+		author, err = user_model.GetUserByEmail(commit.AuthorEmail)
 		if err == nil {
 			authorUsername = author.Name
 			pc.emailUsers[commit.AuthorEmail] = author
@@ -64,7 +64,7 @@ func (pc *PushCommits) toAPIPayloadCommit(repoPath, repoLink string, commit *Pus
 	committerUsername := ""
 	committer, ok := pc.emailUsers[commit.CommitterEmail]
 	if !ok {
-		committer, err = models.GetUserByEmail(commit.CommitterEmail)
+		committer, err = user_model.GetUserByEmail(commit.CommitterEmail)
 		if err == nil {
 			// TODO: check errors other than email not found.
 			committerUsername = committer.Name
@@ -107,7 +107,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(repoPath, repoLink string) ([]*api.Pa
 	var headCommit *api.PayloadCommit
 
 	if pc.emailUsers == nil {
-		pc.emailUsers = make(map[string]*models.User)
+		pc.emailUsers = make(map[string]*user_model.User)
 	}
 	for i, commit := range pc.Commits {
 		apiCommit, err := pc.toAPIPayloadCommit(repoPath, repoLink, commit)
@@ -146,10 +146,10 @@ func (pc *PushCommits) AvatarLink(email string) string {
 	u, ok := pc.emailUsers[email]
 	if !ok {
 		var err error
-		u, err = models.GetUserByEmail(email)
+		u, err = user_model.GetUserByEmail(email)
 		if err != nil {
 			pc.avatars[email] = avatars.GenerateEmailAvatarFastLink(email, size)
-			if !models.IsErrUserNotExist(err) {
+			if !user_model.IsErrUserNotExist(err) {
 				log.Error("GetUserByEmail: %v", err)
 				return ""
 			}
@@ -189,6 +189,6 @@ func GitToPushCommits(gitCommits []*git.Commit) *PushCommits {
 		CompareURL: "",
 		Len:        len(commits),
 		avatars:    make(map[string]string),
-		emailUsers: make(map[string]*models.User),
+		emailUsers: make(map[string]*user_model.User),
 	}
 }
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 64d92eeb2d3..850f7488bd0 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -18,7 +19,7 @@ import (
 )
 
 // CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (*models.Repository, error) {
+func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*models.Repository, error) {
 	if !doer.IsAdmin && !u.CanCreateRepo() {
 		return nil, models.ErrReachLimitOfRepo{
 			Limit: u.MaxRepoCreation,
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index 12fc7afeeb3..d7d32818726 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -11,6 +11,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -32,14 +33,14 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	}
 
 	// Get an admin user.
-	user, err := models.GetUserByID(1)
+	user, err := user_model.GetUserByID(1)
 	assert.NoError(t, err, "GetUserByID")
 
 	// Create org.
 	org := &models.Organization{
 		Name:       "All_repo",
 		IsActive:   true,
-		Type:       models.UserTypeOrganization,
+		Type:       user_model.UserTypeOrganization,
 		Visibility: structs.VisibleTypePublic,
 	}
 	assert.NoError(t, models.CreateOrganization(org, user), "CreateOrganization")
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index f6b76b14aff..61a8b0d1115 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	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/util"
@@ -239,7 +240,7 @@ func GenerateGitContent(ctx context.Context, templateRepo, generateRepo *models.
 }
 
 // GenerateRepository generates a repository from a template
-func GenerateRepository(ctx context.Context, doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
+func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
 	generateRepo := &models.Repository{
 		OwnerID:       owner.ID,
 		Owner:         owner,
diff --git a/modules/repository/init.go b/modules/repository/init.go
index 076dbf748e6..d7f31fabde3 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	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"
@@ -100,7 +101,7 @@ func prepareRepoCommit(ctx context.Context, repo *models.Repository, tmpDir, rep
 }
 
 // initRepoCommit temporarily changes with work directory.
-func initRepoCommit(tmpPath string, repo *models.Repository, u *models.User, defaultBranch string) (err error) {
+func initRepoCommit(tmpPath string, repo *models.Repository, u *user_model.User, defaultBranch string) (err error) {
 	commitTimeStr := time.Now().Format(time.RFC3339)
 
 	sig := u.NewGitSig()
@@ -197,7 +198,7 @@ func checkInitRepository(owner, name string) (err error) {
 }
 
 // InitRepository initializes README and .gitignore if needed.
-func initRepository(ctx context.Context, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
+func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
 	if err = checkInitRepository(repo.OwnerName, repo.Name); err != nil {
 		return err
 	}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index dd54a99cc9d..c7145658f80 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/log"
@@ -46,7 +47,7 @@ func WikiRemoteURL(remote string) string {
 }
 
 // MigrateRepositoryGitData starts migrating git related data after created migrating repository
-func MigrateRepositoryGitData(ctx context.Context, u *models.User,
+func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
 	repo *models.Repository, opts migration.MigrateOptions,
 	httpTransport *http.Transport,
 ) (*models.Repository, error) {
@@ -307,12 +308,12 @@ func PushUpdateAddTag(repo *models.Repository, gitRepo *git.Repository, tagName
 		sig = commit.Committer
 	}
 
-	var author *models.User
+	var author *user_model.User
 	var createdAt = time.Unix(1, 0)
 
 	if sig != nil {
-		author, err = models.GetUserByEmail(sig.Email)
-		if err != nil && !models.IsErrUserNotExist(err) {
+		author, err = user_model.GetUserByEmail(sig.Email)
+		if err != nil && !user_model.IsErrUserNotExist(err) {
 			return fmt.Errorf("GetUserByEmail: %v", err)
 		}
 		createdAt = sig.When
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index aec3596bd49..4eb8f347076 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -24,6 +24,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/avatars"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/emoji"
 	"code.gitea.io/gitea/modules/git"
@@ -554,7 +555,7 @@ func Avatar(item interface{}, others ...interface{}) template.HTML {
 	size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
 
 	switch t := item.(type) {
-	case *models.User:
+	case *user_model.User:
 		src := t.AvatarLinkWithSize(size * avatars.AvatarRenderedSizeFactor)
 		if src != "" {
 			return AvatarHTML(src, size, class, t.DisplayName())
diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go
index eb1a54d86f8..548e454c8ea 100644
--- a/modules/test/context_tests.go
+++ b/modules/test/context_tests.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -55,7 +56,7 @@ func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
 	ctx.Repo = &context.Repository{}
 	ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: repoID}).(*models.Repository)
 	var err error
-	ctx.Repo.Owner, err = models.GetUserByID(ctx.Repo.Repository.OwnerID)
+	ctx.Repo.Owner, err = user_model.GetUserByID(ctx.Repo.Repository.OwnerID)
 	assert.NoError(t, err)
 	ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
 	ctx.Repo.Permission, err = models.GetUserRepoPermission(ctx.Repo.Repository, ctx.User)
@@ -78,7 +79,7 @@ func LoadRepoCommit(t *testing.T, ctx *context.Context) {
 
 // LoadUser load a user into a test context.
 func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
-	ctx.User = unittest.AssertExistsAndLoadBean(t, &models.User{ID: userID}).(*models.User)
+	ctx.User = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
 }
 
 // LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go
index 184a2941a87..85262bc836b 100644
--- a/routers/api/v1/admin/adopt.go
+++ b/routers/api/v1/admin/adopt.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/routers/api/v1/utils"
@@ -79,9 +80,9 @@ func AdoptRepository(ctx *context.APIContext) {
 	ownerName := ctx.Params(":username")
 	repoName := ctx.Params(":reponame")
 
-	ctxUser, err := models.GetUserByName(ownerName)
+	ctxUser, err := user_model.GetUserByName(ownerName)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound()
 			return
 		}
@@ -141,9 +142,9 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
 	ownerName := ctx.Params(":username")
 	repoName := ctx.Params(":reponame")
 
-	ctxUser, err := models.GetUserByName(ownerName)
+	ctxUser, err := user_model.GetUserByName(ownerName)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound()
 			return
 		}
diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go
index 2633865d2c1..bdfe87fd4e4 100644
--- a/routers/api/v1/admin/org.go
+++ b/routers/api/v1/admin/org.go
@@ -9,6 +9,8 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -61,15 +63,15 @@ func CreateOrg(ctx *context.APIContext) {
 		Website:     form.Website,
 		Location:    form.Location,
 		IsActive:    true,
-		Type:        models.UserTypeOrganization,
+		Type:        user_model.UserTypeOrganization,
 		Visibility:  visibility,
 	}
 
 	if err := models.CreateOrganization(org, u); err != nil {
-		if models.IsErrUserAlreadyExist(err) ||
-			models.IsErrNameReserved(err) ||
-			models.IsErrNameCharsNotAllowed(err) ||
-			models.IsErrNamePatternNotAllowed(err) {
+		if user_model.IsErrUserAlreadyExist(err) ||
+			db.IsErrNameReserved(err) ||
+			db.IsErrNameCharsNotAllowed(err) ||
+			db.IsErrNamePatternNotAllowed(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
@@ -104,10 +106,10 @@ func GetAllOrgs(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+	users, maxResults, err := user_model.SearchUsers(&user_model.SearchUserOptions{
 		Actor:       ctx.User,
-		Type:        models.UserTypeOrganization,
-		OrderBy:     models.SearchOrderByAlphabetically,
+		Type:        user_model.UserTypeOrganization,
+		OrderBy:     db.SearchOrderByAlphabetically,
 		ListOptions: listOptions,
 		Visible:     []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate},
 	})
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 95060e7a1c8..e50abb59376 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -11,6 +11,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
@@ -25,7 +26,7 @@ import (
 	user_service "code.gitea.io/gitea/services/user"
 )
 
-func parseLoginSource(ctx *context.APIContext, u *models.User, sourceID int64, loginName string) {
+func parseLoginSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) {
 	if sourceID == 0 {
 		return
 	}
@@ -70,7 +71,7 @@ func CreateUser(ctx *context.APIContext) {
 	//     "$ref": "#/responses/validationError"
 	form := web.GetForm(ctx).(*api.CreateUserOption)
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:               form.Username,
 		FullName:           form.FullName,
 		Email:              form.Email,
@@ -102,20 +103,20 @@ func CreateUser(ctx *context.APIContext) {
 		return
 	}
 
-	var overwriteDefault *models.CreateUserOverwriteOptions
+	var overwriteDefault *user_model.CreateUserOverwriteOptions
 	if form.Visibility != "" {
-		overwriteDefault = &models.CreateUserOverwriteOptions{
+		overwriteDefault = &user_model.CreateUserOverwriteOptions{
 			Visibility: api.VisibilityModes[form.Visibility],
 		}
 	}
 
-	if err := models.CreateUser(u, overwriteDefault); err != nil {
-		if models.IsErrUserAlreadyExist(err) ||
+	if err := user_model.CreateUser(u, overwriteDefault); err != nil {
+		if user_model.IsErrUserAlreadyExist(err) ||
 			user_model.IsErrEmailAlreadyUsed(err) ||
-			models.IsErrNameReserved(err) ||
-			models.IsErrNameCharsNotAllowed(err) ||
+			db.IsErrNameReserved(err) ||
+			db.IsErrNameCharsNotAllowed(err) ||
 			user_model.IsErrEmailInvalid(err) ||
-			models.IsErrNamePatternNotAllowed(err) {
+			db.IsErrNamePatternNotAllowed(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "CreateUser", err)
@@ -183,7 +184,7 @@ func EditUser(ctx *context.APIContext) {
 			ctx.Error(http.StatusBadRequest, "PasswordPwned", errors.New("PasswordPwned"))
 			return
 		}
-		if u.Salt, err = models.GetUserSalt(); err != nil {
+		if u.Salt, err = user_model.GetUserSalt(); err != nil {
 			ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
 			return
 		}
@@ -246,7 +247,7 @@ func EditUser(ctx *context.APIContext) {
 		u.IsRestricted = *form.Restricted
 	}
 
-	if err := models.UpdateUser(u); err != nil {
+	if err := user_model.UpdateUser(u); err != nil {
 		if user_model.IsErrEmailAlreadyUsed(err) || user_model.IsErrEmailInvalid(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
@@ -409,10 +410,10 @@ func GetAllUsers(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+	users, maxResults, err := user_model.SearchUsers(&user_model.SearchUserOptions{
 		Actor:       ctx.User,
-		Type:        models.UserTypeIndividual,
-		OrderBy:     models.SearchOrderByAlphabetically,
+		Type:        user_model.UserTypeIndividual,
+		OrderBy:     db.SearchOrderByAlphabetically,
 		ListOptions: listOptions,
 	})
 	if err != nil {
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index ea653b96775..867803f8dd5 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -103,9 +103,9 @@ func sudo() func(ctx *context.APIContext) {
 
 		if len(sudo) > 0 {
 			if ctx.IsSigned && ctx.User.IsAdmin {
-				user, err := models.GetUserByName(sudo)
+				user, err := user_model.GetUserByName(sudo)
 				if err != nil {
-					if models.IsErrUserNotExist(err) {
+					if user_model.IsErrUserNotExist(err) {
 						ctx.NotFound()
 					} else {
 						ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -130,7 +130,7 @@ func repoAssignment() func(ctx *context.APIContext) {
 		repoName := ctx.Params("reponame")
 
 		var (
-			owner *models.User
+			owner *user_model.User
 			err   error
 		)
 
@@ -138,9 +138,9 @@ func repoAssignment() func(ctx *context.APIContext) {
 		if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
 			owner = ctx.User
 		} else {
-			owner, err = models.GetUserByName(userName)
+			owner, err = user_model.GetUserByName(userName)
 			if err != nil {
-				if models.IsErrUserNotExist(err) {
+				if user_model.IsErrUserNotExist(err) {
 					if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
 						context.RedirectToUser(ctx.Context, userName, redirectUserID)
 					} else if user_model.IsErrUserRedirectNotExist(err) {
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index 952e29fba9c..84065363f02 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -19,7 +20,7 @@ import (
 	"code.gitea.io/gitea/services/org"
 )
 
-func listUserOrgs(ctx *context.APIContext, u *models.User) {
+func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
 	listOptions := utils.GetListOptions(ctx)
 	showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == u.ID)
 
@@ -130,12 +131,12 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
-	var u *models.User
+	var u *user_model.User
 	if u = user.GetUserByParams(ctx); u == nil {
 		return
 	}
 
-	var o *models.User
+	var o *user_model.User
 	if o = user.GetUserByParamsName(ctx, ":org"); o == nil {
 		return
 	}
@@ -206,11 +207,11 @@ func GetAll(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	publicOrgs, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+	publicOrgs, maxResults, err := user_model.SearchUsers(&user_model.SearchUserOptions{
 		Actor:       ctx.User,
 		ListOptions: listOptions,
-		Type:        models.UserTypeOrganization,
-		OrderBy:     models.SearchOrderByAlphabetically,
+		Type:        user_model.UserTypeOrganization,
+		OrderBy:     db.SearchOrderByAlphabetically,
 		Visible:     vMode,
 	})
 	if err != nil {
@@ -266,15 +267,15 @@ func Create(ctx *context.APIContext) {
 		Website:                   form.Website,
 		Location:                  form.Location,
 		IsActive:                  true,
-		Type:                      models.UserTypeOrganization,
+		Type:                      user_model.UserTypeOrganization,
 		Visibility:                visibility,
 		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
 	}
 	if err := models.CreateOrganization(org, ctx.User); err != nil {
-		if models.IsErrUserAlreadyExist(err) ||
-			models.IsErrNameReserved(err) ||
-			models.IsErrNameCharsNotAllowed(err) ||
-			models.IsErrNamePatternNotAllowed(err) {
+		if user_model.IsErrUserAlreadyExist(err) ||
+			db.IsErrNameReserved(err) ||
+			db.IsErrNameCharsNotAllowed(err) ||
+			db.IsErrNamePatternNotAllowed(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
@@ -344,7 +345,7 @@ func Edit(ctx *context.APIContext) {
 	if form.RepoAdminChangeTeamAccess != nil {
 		org.RepoAdminChangeTeamAccess = *form.RepoAdminChangeTeamAccess
 	}
-	if err := models.UpdateUserCols(db.DefaultContext, org.AsUser(),
+	if err := user_model.UpdateUserCols(db.DefaultContext, org.AsUser(),
 		"full_name", "description", "website", "location",
 		"visibility", "repo_admin_change_team_access",
 	); err != nil {
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 04da9012396..70e5b3e2b87 100644
--- a/routers/api/v1/repo/branch.go
+++ b/routers/api/v1/repo/branch.go
@@ -11,6 +11,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -423,27 +424,27 @@ func CreateBranchProtection(ctx *context.APIContext) {
 		requiredApprovals = form.RequiredApprovals
 	}
 
-	whitelistUsers, err := models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
+	whitelistUsers, err := user_model.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 			return
 		}
 		ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 		return
 	}
-	mergeWhitelistUsers, err := models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
+	mergeWhitelistUsers, err := user_model.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 			return
 		}
 		ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
 		return
 	}
-	approvalsWhitelistUsers, err := models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
+	approvalsWhitelistUsers, err := user_model.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 			return
 		}
@@ -653,9 +654,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 
 	var whitelistUsers []int64
 	if form.PushWhitelistUsernames != nil {
-		whitelistUsers, err = models.GetUserIDsByNames(form.PushWhitelistUsernames, false)
+		whitelistUsers, err = user_model.GetUserIDsByNames(form.PushWhitelistUsernames, false)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 				return
 			}
@@ -667,9 +668,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 	}
 	var mergeWhitelistUsers []int64
 	if form.MergeWhitelistUsernames != nil {
-		mergeWhitelistUsers, err = models.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
+		mergeWhitelistUsers, err = user_model.GetUserIDsByNames(form.MergeWhitelistUsernames, false)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 				return
 			}
@@ -681,9 +682,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 	}
 	var approvalsWhitelistUsers []int64
 	if form.ApprovalsWhitelistUsernames != nil {
-		approvalsWhitelistUsers, err = models.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
+		approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(form.ApprovalsWhitelistUsernames, false)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
 				return
 			}
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index d636220f62c..fcc8f9a90fb 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -99,9 +100,9 @@ func IsCollaborator(ctx *context.APIContext) {
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 
-	user, err := models.GetUserByName(ctx.Params(":collaborator"))
+	user, err := user_model.GetUserByName(ctx.Params(":collaborator"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -155,9 +156,9 @@ func AddCollaborator(ctx *context.APIContext) {
 
 	form := web.GetForm(ctx).(*api.AddCollaboratorOption)
 
-	collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
+	collaborator, err := user_model.GetUserByName(ctx.Params(":collaborator"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -214,9 +215,9 @@ func DeleteCollaborator(ctx *context.APIContext) {
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 
-	collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
+	collaborator, err := user_model.GetUserByName(ctx.Params(":collaborator"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index 639100757bd..9907054b831 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -12,6 +12,7 @@ import (
 	"strconv"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -188,7 +189,7 @@ func GetAllCommits(ctx *context.APIContext) {
 		return
 	}
 
-	userCache := make(map[string]*models.User)
+	userCache := make(map[string]*user_model.User)
 
 	apiCommits := make([]*api.Commit, len(commits))
 	for i, commit := range commits {
diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go
index 45d3c815a71..528b8fdecc7 100644
--- a/routers/api/v1/repo/fork.go
+++ b/routers/api/v1/repo/fork.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -99,7 +100,7 @@ func CreateFork(ctx *context.APIContext) {
 
 	form := web.GetForm(ctx).(*api.CreateForkOption)
 	repo := ctx.Repo.Repository
-	var forker *models.User // user/org that will own the fork
+	var forker *user_model.User // user/org that will own the fork
 	if form.Organization == nil {
 		forker = ctx.User
 	} else {
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index a2454b86183..5137c7246ed 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/context"
 	"code.gitea.io/gitea/modules/convert"
 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
@@ -133,7 +134,7 @@ func SearchIssues(ctx *context.APIContext) {
 		Collaborate: util.OptionalBoolNone,
 		// This needs to be a column that is not nil in fixtures or
 		// MySQL will return different results when sorting by null in some cases
-		OrderBy: models.SearchOrderByAlphabetically,
+		OrderBy: db.SearchOrderByAlphabetically,
 		Actor:   ctx.User,
 	}
 	if ctx.IsSigned {
@@ -141,9 +142,9 @@ func SearchIssues(ctx *context.APIContext) {
 		opts.AllLimited = true
 	}
 	if ctx.FormString("owner") != "" {
-		owner, err := models.GetUserByName(ctx.FormString("owner"))
+		owner, err := user_model.GetUserByName(ctx.FormString("owner"))
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusBadRequest, "Owner not found", err)
 			} else {
 				ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -492,8 +493,8 @@ func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 {
 		return 0
 	}
 
-	user, err := models.GetUserByName(userName)
-	if models.IsErrUserNotExist(err) {
+	user, err := user_model.GetUserByName(userName)
+	if user_model.IsErrUserNotExist(err) {
 		ctx.NotFound(err)
 		return 0
 	}
@@ -604,7 +605,7 @@ func CreateIssue(ctx *context.APIContext) {
 		issue.MilestoneID = form.Milestone
 		assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
 			} else {
 				ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
@@ -614,7 +615,7 @@ func CreateIssue(ctx *context.APIContext) {
 
 		// Check if the passed assignees is assignable
 		for _, aID := range assigneeIDs {
-			assignee, err := models.GetUserByID(aID)
+			assignee, err := user_model.GetUserByID(aID)
 			if err != nil {
 				ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
 				return
diff --git a/routers/api/v1/repo/issue_subscription.go b/routers/api/v1/repo/issue_subscription.go
index 8acd378cc5e..09be3cc1931 100644
--- a/routers/api/v1/repo/issue_subscription.go
+++ b/routers/api/v1/repo/issue_subscription.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -114,9 +115,9 @@ func setIssueSubscription(ctx *context.APIContext, watch bool) {
 		return
 	}
 
-	user, err := models.GetUserByName(ctx.Params(":user"))
+	user, err := user_model.GetUserByName(ctx.Params(":user"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound()
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index f73e63547b0..00f8e77febd 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -92,8 +93,8 @@ func ListTrackedTimes(ctx *context.APIContext) {
 
 	qUser := ctx.FormTrim("user")
 	if qUser != "" {
-		user, err := models.GetUserByName(qUser)
-		if models.IsErrUserNotExist(err) {
+		user, err := user_model.GetUserByName(qUser)
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusNotFound, "User does not exist", err)
 		} else if err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -201,7 +202,7 @@ func AddTime(ctx *context.APIContext) {
 	if form.User != "" {
 		if (ctx.IsUserRepoAdmin() && ctx.User.Name != form.User) || ctx.User.IsAdmin {
 			//allow only RepoAdmin, Admin and User to add time
-			user, err = models.GetUserByName(form.User)
+			user, err = user_model.GetUserByName(form.User)
 			if err != nil {
 				ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
 			}
@@ -413,9 +414,9 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 		ctx.Error(http.StatusBadRequest, "", "time tracking disabled")
 		return
 	}
-	user, err := models.GetUserByName(ctx.Params(":timetrackingusername"))
+	user, err := user_model.GetUserByName(ctx.Params(":timetrackingusername"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound(err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -510,8 +511,8 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 	// Filters
 	qUser := ctx.FormTrim("user")
 	if qUser != "" {
-		user, err := models.GetUserByName(qUser)
-		if models.IsErrUserNotExist(err) {
+		user, err := user_model.GetUserByName(qUser)
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusNotFound, "User does not exist", err)
 		} else if err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 767972ee988..bc594419f70 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -12,6 +12,8 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/graceful"
@@ -54,18 +56,18 @@ func Migrate(ctx *context.APIContext) {
 
 	//get repoOwner
 	var (
-		repoOwner *models.User
+		repoOwner *user_model.User
 		err       error
 	)
 	if len(form.RepoOwner) != 0 {
-		repoOwner, err = models.GetUserByName(form.RepoOwner)
+		repoOwner, err = user_model.GetUserByName(form.RepoOwner)
 	} else if form.RepoOwnerID != 0 {
-		repoOwner, err = models.GetUserByID(form.RepoOwnerID)
+		repoOwner, err = user_model.GetUserByID(form.RepoOwnerID)
 	} else {
 		repoOwner = ctx.User
 	}
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetUser", err)
@@ -208,7 +210,7 @@ func Migrate(ctx *context.APIContext) {
 	ctx.JSON(http.StatusCreated, convert.ToRepo(repo, models.AccessModeAdmin))
 }
 
-func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
+func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, remoteAddr string, err error) {
 	switch {
 	case models.IsErrRepoAlreadyExist(err):
 		ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
@@ -220,12 +222,12 @@ func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteA
 		ctx.Error(http.StatusUnprocessableEntity, "", "Remote visit required two factors authentication.")
 	case models.IsErrReachLimitOfRepo(err):
 		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
-	case models.IsErrNameReserved(err):
-		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
-	case models.IsErrNameCharsNotAllowed(err):
-		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(models.ErrNameCharsNotAllowed).Name))
-	case models.IsErrNamePatternNotAllowed(err):
-		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern))
+	case db.IsErrNameReserved(err):
+		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' is reserved.", err.(db.ErrNameReserved).Name))
+	case db.IsErrNameCharsNotAllowed(err):
+		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The username '%s' contains invalid characters.", err.(db.ErrNameCharsNotAllowed).Name))
+	case db.IsErrNamePatternNotAllowed(err):
+		ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(db.ErrNamePatternNotAllowed).Pattern))
 	case models.IsErrInvalidCloneAddr(err):
 		ctx.Error(http.StatusUnprocessableEntity, "", err)
 	case base.IsErrNotSupported(err):
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index efe72e1502a..dd415a34af3 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -382,7 +383,7 @@ func CreatePullRequest(ctx *context.APIContext) {
 	// Get all assignee IDs
 	assigneeIDs, err := models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
 		} else {
 			ctx.Error(http.StatusInternalServerError, "AddAssigneeByName", err)
@@ -391,7 +392,7 @@ func CreatePullRequest(ctx *context.APIContext) {
 	}
 	// Check if the passed assignees is assignable
 	for _, aID := range assigneeIDs {
-		assignee, err := models.GetUserByID(aID)
+		assignee, err := user_model.GetUserByID(aID)
 		if err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
 			return
@@ -522,7 +523,7 @@ func EditPullRequest(ctx *context.APIContext) {
 	if ctx.Repo.CanWrite(unit.TypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
 		err = issue_service.UpdateAssignees(issue, form.Assignee, form.Assignees, ctx.User)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
 			} else {
 				ctx.Error(http.StatusInternalServerError, "UpdateAssignees", err)
@@ -900,7 +901,7 @@ func MergePullRequest(ctx *context.APIContext) {
 	ctx.Status(http.StatusOK)
 }
 
-func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
+func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*user_model.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
 	baseRepo := ctx.Repo.Repository
 
 	// Get compared branches information
@@ -913,7 +914,7 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 	baseBranch := form.Base
 
 	var (
-		headUser   *models.User
+		headUser   *user_model.User
 		headBranch string
 		isSameRepo bool
 		err        error
@@ -927,9 +928,9 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
 		headBranch = headInfos[0]
 
 	} else if len(headInfos) == 2 {
-		headUser, err = models.GetUserByName(headInfos[0])
+		headUser, err = user_model.GetUserByName(headInfos[0])
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.NotFound("GetUserByName")
 			} else {
 				ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
@@ -1209,7 +1210,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
 	totalNumberOfCommits := len(commits)
 	totalNumberOfPages := int(math.Ceil(float64(totalNumberOfCommits) / float64(listOptions.PageSize)))
 
-	userCache := make(map[string]*models.User)
+	userCache := make(map[string]*user_model.User)
 
 	start, end := listOptions.GetStartEnd()
 
diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index 55b5178305e..ec5f0456476 100644
--- a/routers/api/v1/repo/pull_review.go
+++ b/routers/api/v1/repo/pull_review.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -554,7 +555,7 @@ func prepareSingleReview(ctx *context.APIContext) (*models.Review, *models.PullR
 		return nil, nil, true
 	}
 
-	if err := review.LoadAttributes(); err != nil && !models.IsErrUserNotExist(err) {
+	if err := review.LoadAttributes(); err != nil && !user_model.IsErrUserNotExist(err) {
 		ctx.Error(http.StatusInternalServerError, "ReviewLoadAttributes", err)
 		return nil, nil, true
 	}
@@ -659,7 +660,7 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
 		return
 	}
 
-	reviewers := make([]*models.User, 0, len(opts.Reviewers))
+	reviewers := make([]*user_model.User, 0, len(opts.Reviewers))
 
 	permDoer, err := models.GetUserRepoPermission(pr.Issue.Repo, ctx.User)
 	if err != nil {
@@ -668,15 +669,15 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
 	}
 
 	for _, r := range opts.Reviewers {
-		var reviewer *models.User
+		var reviewer *user_model.User
 		if strings.Contains(r, "@") {
-			reviewer, err = models.GetUserByEmail(r)
+			reviewer, err = user_model.GetUserByEmail(r)
 		} else {
-			reviewer, err = models.GetUserByName(r)
+			reviewer, err = user_model.GetUserByName(r)
 		}
 
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.NotFound("UserNotExist", fmt.Sprintf("User '%s' not exist", r))
 				return
 			}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 872ce228713..a74e13d3abd 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -12,7 +12,9 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
 	unit_model "code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/git"
@@ -26,20 +28,20 @@ import (
 	repo_service "code.gitea.io/gitea/services/repository"
 )
 
-var searchOrderByMap = map[string]map[string]models.SearchOrderBy{
+var searchOrderByMap = map[string]map[string]db.SearchOrderBy{
 	"asc": {
-		"alpha":   models.SearchOrderByAlphabetically,
-		"created": models.SearchOrderByOldest,
-		"updated": models.SearchOrderByLeastUpdated,
-		"size":    models.SearchOrderBySize,
-		"id":      models.SearchOrderByID,
+		"alpha":   db.SearchOrderByAlphabetically,
+		"created": db.SearchOrderByOldest,
+		"updated": db.SearchOrderByLeastUpdated,
+		"size":    db.SearchOrderBySize,
+		"id":      db.SearchOrderByID,
 	},
 	"desc": {
-		"alpha":   models.SearchOrderByAlphabeticallyReverse,
-		"created": models.SearchOrderByNewest,
-		"updated": models.SearchOrderByRecentUpdated,
-		"size":    models.SearchOrderBySizeReverse,
-		"id":      models.SearchOrderByIDReverse,
+		"alpha":   db.SearchOrderByAlphabeticallyReverse,
+		"created": db.SearchOrderByNewest,
+		"updated": db.SearchOrderByRecentUpdated,
+		"size":    db.SearchOrderBySizeReverse,
+		"id":      db.SearchOrderByIDReverse,
 	},
 }
 
@@ -239,7 +241,7 @@ func Search(ctx *context.APIContext) {
 }
 
 // CreateUserRepo create a repository for a user
-func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) {
+func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.CreateRepoOption) {
 	if opt.AutoInit && opt.Readme == "" {
 		opt.Readme = "Default"
 	}
@@ -259,8 +261,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR
 	if err != nil {
 		if models.IsErrRepoAlreadyExist(err) {
 			ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
-		} else if models.IsErrNameReserved(err) ||
-			models.IsErrNamePatternNotAllowed(err) {
+		} else if db.IsErrNameReserved(err) ||
+			db.IsErrNamePatternNotAllowed(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
@@ -374,9 +376,9 @@ func Generate(ctx *context.APIContext) {
 	ctxUser := ctx.User
 	var err error
 	if form.Owner != ctxUser.Name {
-		ctxUser, err = models.GetUserByName(form.Owner)
+		ctxUser, err = user_model.GetUserByName(form.Owner)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.JSON(http.StatusNotFound, map[string]interface{}{
 					"error": "request owner `" + form.Owner + "` does not exist",
 				})
@@ -408,8 +410,8 @@ func Generate(ctx *context.APIContext) {
 	if err != nil {
 		if models.IsErrRepoAlreadyExist(err) {
 			ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
-		} else if models.IsErrNameReserved(err) ||
-			models.IsErrNamePatternNotAllowed(err) {
+		} else if db.IsErrNameReserved(err) ||
+			db.IsErrNamePatternNotAllowed(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
@@ -648,10 +650,10 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err
 			switch {
 			case models.IsErrRepoAlreadyExist(err):
 				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
-			case models.IsErrNameReserved(err):
+			case db.IsErrNameReserved(err):
 				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
-			case models.IsErrNamePatternNotAllowed(err):
-				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
+			case db.IsErrNamePatternNotAllowed(err):
+				ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(db.ErrNamePatternNotAllowed).Pattern), err)
 			default:
 				ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
 			}
diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go
index f16e2bb081f..b3913bcd347 100644
--- a/routers/api/v1/repo/transfer.go
+++ b/routers/api/v1/repo/transfer.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/log"
@@ -53,9 +54,9 @@ func Transfer(ctx *context.APIContext) {
 
 	opts := web.GetForm(ctx).(*api.TransferRepoOption)
 
-	newOwner, err := models.GetUserByName(opts.NewOwner)
+	newOwner, err := user_model.GetUserByName(opts.NewOwner)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
 			return
 		}
@@ -63,7 +64,7 @@ func Transfer(ctx *context.APIContext) {
 		return
 	}
 
-	if newOwner.Type == models.UserTypeOrganization {
+	if newOwner.Type == user_model.UserTypeOrganization {
 		if !ctx.User.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !models.OrgFromUser(newOwner).HasMemberWithUserID(ctx.User.ID) {
 			// The user shouldn't know about this organization
 			ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go
index edf3cea3ea0..1eacb89db2b 100644
--- a/routers/api/v1/user/follower.go
+++ b/routers/api/v1/user/follower.go
@@ -8,7 +8,6 @@ package user
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -16,7 +15,7 @@ import (
 	"code.gitea.io/gitea/routers/api/v1/utils"
 )
 
-func responseAPIUsers(ctx *context.APIContext, users []*models.User) {
+func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) {
 	apiUsers := make([]*api.User, len(users))
 	for i := range users {
 		apiUsers[i] = convert.ToUser(users[i], ctx.User)
@@ -24,8 +23,8 @@ func responseAPIUsers(ctx *context.APIContext, users []*models.User) {
 	ctx.JSON(http.StatusOK, &apiUsers)
 }
 
-func listUserFollowers(ctx *context.APIContext, u *models.User) {
-	users, err := models.GetUserFollowers(u, utils.GetListOptions(ctx))
+func listUserFollowers(ctx *context.APIContext, u *user_model.User) {
+	users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx))
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
 		return
@@ -90,8 +89,8 @@ func ListFollowers(ctx *context.APIContext) {
 	listUserFollowers(ctx, u)
 }
 
-func listUserFollowing(ctx *context.APIContext, u *models.User) {
-	users, err := models.GetUserFollowing(u, utils.GetListOptions(ctx))
+func listUserFollowing(ctx *context.APIContext, u *user_model.User) {
+	users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx))
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err)
 		return
@@ -156,7 +155,7 @@ func ListFollowing(ctx *context.APIContext) {
 	listUserFollowing(ctx, u)
 }
 
-func checkUserFollowing(ctx *context.APIContext, u *models.User, followID int64) {
+func checkUserFollowing(ctx *context.APIContext, u *user_model.User, followID int64) {
 	if user_model.IsFollowing(u.ID, followID) {
 		ctx.Status(http.StatusNoContent)
 	} else {
diff --git a/routers/api/v1/user/helper.go b/routers/api/v1/user/helper.go
index a7c2e614054..fab3ce2ae53 100644
--- a/routers/api/v1/user/helper.go
+++ b/routers/api/v1/user/helper.go
@@ -7,17 +7,16 @@ package user
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 )
 
 // GetUserByParamsName get user by name
-func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
+func GetUserByParamsName(ctx *context.APIContext, name string) *user_model.User {
 	username := ctx.Params(name)
-	user, err := models.GetUserByName(username)
+	user, err := user_model.GetUserByName(username)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil {
 				context.RedirectToUser(ctx.Context, username, redirectUserID)
 			} else {
@@ -32,6 +31,6 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *models.User {
 }
 
 // GetUserByParams returns user whose name is presented in URL (":username").
-func GetUserByParams(ctx *context.APIContext) *models.User {
+func GetUserByParams(ctx *context.APIContext) *user_model.User {
 	return GetUserByParamsName(ctx, ":username")
 }
diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go
index 36b1c17b895..719e1c0bdcb 100644
--- a/routers/api/v1/user/key.go
+++ b/routers/api/v1/user/key.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/setting"
@@ -18,7 +19,7 @@ import (
 )
 
 // appendPrivateInformation appends the owner and key type information to api.PublicKey
-func appendPrivateInformation(apiKey *api.PublicKey, key *models.PublicKey, defaultUser *models.User) (*api.PublicKey, error) {
+func appendPrivateInformation(apiKey *api.PublicKey, key *models.PublicKey, defaultUser *user_model.User) (*api.PublicKey, error) {
 	if key.Type == models.KeyTypeDeploy {
 		apiKey.KeyType = "deploy"
 	} else if key.Type == models.KeyTypeUser {
@@ -27,7 +28,7 @@ func appendPrivateInformation(apiKey *api.PublicKey, key *models.PublicKey, defa
 		if defaultUser.ID == key.OwnerID {
 			apiKey.Owner = convert.ToUser(defaultUser, defaultUser)
 		} else {
-			user, err := models.GetUserByID(key.OwnerID)
+			user, err := user_model.GetUserByID(key.OwnerID)
 			if err != nil {
 				return apiKey, err
 			}
@@ -44,7 +45,7 @@ func composePublicKeysAPILink() string {
 	return setting.AppURL + "api/v1/user/keys/"
 }
 
-func listPublicKeys(ctx *context.APIContext, user *models.User) {
+func listPublicKeys(ctx *context.APIContext, user *user_model.User) {
 	var keys []*models.PublicKey
 	var err error
 	var count int
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 3c9a17a0bef..0b0b522c15d 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -15,7 +16,7 @@ import (
 )
 
 // listUserRepos - List the repositories owned by the given user.
-func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
+func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
 	opts := utils.GetListOptions(ctx)
 
 	repos, count, err := models.GetUserRepositories(&models.SearchRepoOptions{
diff --git a/routers/api/v1/user/settings.go b/routers/api/v1/user/settings.go
index b4548e7443f..40bee566811 100644
--- a/routers/api/v1/user/settings.go
+++ b/routers/api/v1/user/settings.go
@@ -7,7 +7,7 @@ package user
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -74,7 +74,7 @@ func UpdateUserSettings(ctx *context.APIContext) {
 		ctx.User.KeepActivityPrivate = *form.HideActivity
 	}
 
-	if err := models.UpdateUser(ctx.User); err != nil {
+	if err := user_model.UpdateUser(ctx.User); err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index f067722bfa8..946ae738e94 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -18,7 +19,7 @@ import (
 
 // getStarredRepos returns the repos that the user with the specified userID has
 // starred
-func getStarredRepos(user *models.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) {
+func getStarredRepos(user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) {
 	starredRepos, err := models.GetStarredRepos(user.ID, private, listOptions)
 	if err != nil {
 		return nil, err
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 8c57b1f099f..bba7b7a5d14 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/routers/api/v1/utils"
@@ -54,11 +55,11 @@ func Search(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	users, maxResults, err := models.SearchUsers(&models.SearchUserOptions{
+	users, maxResults, err := user_model.SearchUsers(&user_model.SearchUserOptions{
 		Actor:       ctx.User,
 		Keyword:     ctx.FormTrim("q"),
 		UID:         ctx.FormInt64("uid"),
-		Type:        models.UserTypeIndividual,
+		Type:        user_model.UserTypeIndividual,
 		ListOptions: listOptions,
 	})
 	if err != nil {
@@ -105,7 +106,7 @@ func GetInfo(ctx *context.APIContext) {
 
 	if !models.IsUserVisibleToViewer(u, ctx.User) {
 		// fake ErrUserNotExist error message to not leak information about existence
-		ctx.NotFound("GetUserByName", models.ErrUserNotExist{Name: ctx.Params(":username")})
+		ctx.NotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.Params(":username")})
 		return
 	}
 	ctx.JSON(http.StatusOK, convert.ToUser(u, ctx.User))
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index 3c6f8b30704..54d5e74bc00 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -16,7 +17,7 @@ import (
 )
 
 // getWatchedRepos returns the repos that the user with the specified userID is watching
-func getWatchedRepos(user *models.User, private bool, listOptions db.ListOptions) ([]*api.Repository, int64, error) {
+func getWatchedRepos(user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, int64, error) {
 	watchedRepos, total, err := models.GetWatchedRepos(user.ID, private, listOptions)
 	if err != nil {
 		return nil, 0, err
diff --git a/routers/install/install.go b/routers/install/install.go
index 837467056dc..bd19ab5eb64 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -14,9 +14,9 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/migrations"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/generate"
@@ -72,7 +72,7 @@ func Init(next http.Handler) http.Handler {
 				"TmplLoadTimes": func() string {
 					return time.Since(startTime).String()
 				},
-				"PasswordHashAlgorithms": models.AvailableHashAlgorithms,
+				"PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
 			},
 		}
 		for _, lang := range translation.AllLangs() {
@@ -264,13 +264,13 @@ func SubmitInstall(ctx *context.Context) {
 	// Check admin user creation
 	if len(form.AdminName) > 0 {
 		// Ensure AdminName is valid
-		if err := models.IsUsableUsername(form.AdminName); err != nil {
+		if err := user_model.IsUsableUsername(form.AdminName); err != nil {
 			ctx.Data["Err_Admin"] = true
 			ctx.Data["Err_AdminName"] = true
-			if models.IsErrNameReserved(err) {
+			if db.IsErrNameReserved(err) {
 				ctx.RenderWithErr(ctx.Tr("install.err_admin_name_is_reserved"), tplInstall, form)
 				return
-			} else if models.IsErrNamePatternNotAllowed(err) {
+			} else if db.IsErrNamePatternNotAllowed(err) {
 				ctx.RenderWithErr(ctx.Tr("install.err_admin_name_pattern_not_allowed"), tplInstall, form)
 				return
 			}
@@ -416,15 +416,15 @@ func SubmitInstall(ctx *context.Context) {
 
 	// Create admin account
 	if len(form.AdminName) > 0 {
-		u := &models.User{
+		u := &user_model.User{
 			Name:     form.AdminName,
 			Email:    form.AdminEmail,
 			Passwd:   form.AdminPasswd,
 			IsAdmin:  true,
 			IsActive: true,
 		}
-		if err = models.CreateUser(u); err != nil {
-			if !models.IsErrUserAlreadyExist(err) {
+		if err = user_model.CreateUser(u); err != nil {
+			if !user_model.IsErrUserAlreadyExist(err) {
 				setting.InstallLock = false
 				ctx.Data["Err_AdminName"] = true
 				ctx.Data["Err_AdminEmail"] = true
@@ -432,7 +432,7 @@ func SubmitInstall(ctx *context.Context) {
 				return
 			}
 			log.Info("Admin account already exist")
-			u, _ = models.GetUserByName(u.Name)
+			u, _ = user_model.GetUserByName(u.Name)
 		}
 
 		days := 86400 * setting.LogInRememberDays
diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go
index 04c334b38f5..5ab21d525b9 100644
--- a/routers/private/hook_pre_receive.go
+++ b/routers/private/hook_pre_receive.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	gitea_context "code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -23,7 +24,7 @@ import (
 
 type preReceiveContext struct {
 	*gitea_context.PrivateContext
-	user *models.User
+	user *user_model.User
 	perm models.Permission
 
 	canCreatePullRequest        bool
@@ -41,7 +42,7 @@ type preReceiveContext struct {
 }
 
 // User gets or loads User
-func (ctx *preReceiveContext) User() *models.User {
+func (ctx *preReceiveContext) User() *user_model.User {
 	if ctx.user == nil {
 		ctx.user, ctx.perm = loadUserAndPermission(ctx.PrivateContext, ctx.opts.UserID)
 	}
@@ -449,8 +450,8 @@ func generateGitEnv(opts *private.HookOptions) (env []string) {
 	return env
 }
 
-func loadUserAndPermission(ctx *gitea_context.PrivateContext, id int64) (user *models.User, perm models.Permission) {
-	user, err := models.GetUserByID(id)
+func loadUserAndPermission(ctx *gitea_context.PrivateContext, id int64) (user *user_model.User, perm models.Permission) {
+	user, err := user_model.GetUserByID(id)
 	if err != nil {
 		log.Error("Unable to get User id %d Error: %v", id, err)
 		ctx.JSON(http.StatusInternalServerError, private.Response{
diff --git a/routers/private/mail.go b/routers/private/mail.go
index 6b9100159a9..5218cd34a1e 100644
--- a/routers/private/mail.go
+++ b/routers/private/mail.go
@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"strconv"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -44,7 +44,7 @@ func SendEmail(ctx *context.PrivateContext) {
 	var emails []string
 	if len(mail.To) > 0 {
 		for _, uname := range mail.To {
-			user, err := models.GetUserByName(uname)
+			user, err := user_model.GetUserByName(uname)
 			if err != nil {
 				err := fmt.Sprintf("Failed to get user information: %v", err)
 				log.Error(err)
@@ -59,7 +59,7 @@ func SendEmail(ctx *context.PrivateContext) {
 			}
 		}
 	} else {
-		err := models.IterateUser(func(user *models.User) error {
+		err := user_model.IterateUser(func(user *user_model.User) error {
 			if len(user.Email) > 0 {
 				emails = append(emails, user.Email)
 			}
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 329d80476a4..18057ededb5 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -48,9 +49,9 @@ func ServNoCommand(ctx *context.PrivateContext) {
 	results.Key = key
 
 	if key.Type == models.KeyTypeUser || key.Type == models.KeyTypePrincipal {
-		user, err := models.GetUserByID(key.OwnerID)
+		user, err := user_model.GetUserByID(key.OwnerID)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.JSON(http.StatusUnauthorized, private.Response{
 					Err: fmt.Sprintf("Cannot find owner with id: %d for key: %d", key.OwnerID, keyID),
 				})
@@ -105,7 +106,7 @@ func ServCommand(ctx *context.PrivateContext) {
 		results.RepoName = repoName[:len(repoName)-5]
 	}
 
-	owner, err := models.GetUserByName(results.OwnerName)
+	owner, err := user_model.GetUserByName(results.OwnerName)
 	if err != nil {
 		log.Error("Unable to get repository owner: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
 		ctx.JSON(http.StatusInternalServerError, private.ErrServCommand{
@@ -214,7 +215,7 @@ func ServCommand(ctx *context.PrivateContext) {
 	// So now we need to check if the key is a deploy key
 	// We'll keep hold of the deploy key here for permissions checking
 	var deployKey *models.DeployKey
-	var user *models.User
+	var user *user_model.User
 	if key.Type == models.KeyTypeDeploy {
 		results.IsDeployKey = true
 
@@ -248,9 +249,9 @@ func ServCommand(ctx *context.PrivateContext) {
 	} else {
 		// Get the user represented by the Key
 		var err error
-		user, err = models.GetUserByID(key.OwnerID)
+		user, err = user_model.GetUserByID(key.OwnerID)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.JSON(http.StatusUnauthorized, private.ErrServCommand{
 					Results: results,
 					Err:     fmt.Sprintf("Public Key: %d:%s owner %d does not exist.", key.ID, key.Name, key.OwnerID),
@@ -333,7 +334,7 @@ func ServCommand(ctx *context.PrivateContext) {
 
 	// We already know we aren't using a deploy key
 	if !repoExist {
-		owner, err := models.GetUserByName(ownerName)
+		owner, err := user_model.GetUserByName(ownerName)
 		if err != nil {
 			ctx.JSON(http.StatusInternalServerError, private.ErrServCommand{
 				Results: results,
diff --git a/routers/web/admin/orgs.go b/routers/web/admin/orgs.go
index df3118b60f2..3d440d83cb1 100644
--- a/routers/web/admin/orgs.go
+++ b/routers/web/admin/orgs.go
@@ -6,8 +6,8 @@
 package admin
 
 import (
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -25,9 +25,9 @@ func Organizations(ctx *context.Context) {
 	ctx.Data["PageIsAdmin"] = true
 	ctx.Data["PageIsAdminOrganizations"] = true
 
-	explore.RenderUserSearch(ctx, &models.SearchUserOptions{
+	explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
 		Actor: ctx.User,
-		Type:  models.UserTypeOrganization,
+		Type:  user_model.UserTypeOrganization,
 		ListOptions: db.ListOptions{
 			PageSize: setting.UI.Admin.OrgPagingNum,
 		},
diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go
index 432dd2f6ae6..69e522ef7e9 100644
--- a/routers/web/admin/repos.go
+++ b/routers/web/admin/repos.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -119,9 +120,9 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
 		return
 	}
 
-	ctxUser, err := models.GetUserByName(dirSplit[0])
+	ctxUser, err := user_model.GetUserByName(dirSplit[0])
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			log.Debug("User does not exist: %s", dirSplit[0])
 			ctx.Redirect(setting.AppSubURL + "/admin/repos")
 			return
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index 077cf02f15a..b92c5cf01a8 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -56,9 +56,9 @@ func Users(ctx *context.Context) {
 		"SortType":        sortType,
 	}
 
-	explore.RenderUserSearch(ctx, &models.SearchUserOptions{
+	explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
 		Actor: ctx.User,
-		Type:  models.UserTypeIndividual,
+		Type:  user_model.UserTypeIndividual,
 		ListOptions: db.ListOptions{
 			PageSize: setting.UI.Admin.UserPagingNum,
 		},
@@ -114,7 +114,7 @@ func NewUserPost(ctx *context.Context) {
 		return
 	}
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:      form.UserName,
 		Email:     form.Email,
 		Passwd:    form.Password,
@@ -156,9 +156,9 @@ func NewUserPost(ctx *context.Context) {
 		u.MustChangePassword = form.MustChangePassword
 	}
 
-	if err := models.CreateUser(u, &models.CreateUserOverwriteOptions{Visibility: form.Visibility}); err != nil {
+	if err := user_model.CreateUser(u, &user_model.CreateUserOverwriteOptions{Visibility: form.Visibility}); err != nil {
 		switch {
-		case models.IsErrUserAlreadyExist(err):
+		case user_model.IsErrUserAlreadyExist(err):
 			ctx.Data["Err_UserName"] = true
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplUserNew, &form)
 		case user_model.IsErrEmailAlreadyUsed(err):
@@ -167,15 +167,15 @@ func NewUserPost(ctx *context.Context) {
 		case user_model.IsErrEmailInvalid(err):
 			ctx.Data["Err_Email"] = true
 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplUserNew, &form)
-		case models.IsErrNameReserved(err):
+		case db.IsErrNameReserved(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(models.ErrNameReserved).Name), tplUserNew, &form)
-		case models.IsErrNamePatternNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tplUserNew, &form)
+		case db.IsErrNamePatternNotAllowed(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
-		case models.IsErrNameCharsNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplUserNew, &form)
+		case db.IsErrNameCharsNotAllowed(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
+			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tplUserNew, &form)
 		default:
 			ctx.ServerError("CreateUser", err)
 		}
@@ -192,8 +192,8 @@ func NewUserPost(ctx *context.Context) {
 	ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
 }
 
-func prepareUserInfo(ctx *context.Context) *models.User {
-	u, err := models.GetUserByID(ctx.ParamsInt64(":userid"))
+func prepareUserInfo(ctx *context.Context) *user_model.User {
+	u, err := user_model.GetUserByID(ctx.ParamsInt64(":userid"))
 	if err != nil {
 		ctx.ServerError("GetUserByID", err)
 		return nil
@@ -298,7 +298,7 @@ func EditUserPost(ctx *context.Context) {
 			ctx.RenderWithErr(errMsg, tplUserNew, &form)
 			return
 		}
-		if u.Salt, err = models.GetUserSalt(); err != nil {
+		if u.Salt, err = user_model.GetUserSalt(); err != nil {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
@@ -352,7 +352,7 @@ func EditUserPost(ctx *context.Context) {
 		u.ProhibitLogin = form.ProhibitLogin
 	}
 
-	if err := models.UpdateUser(u); err != nil {
+	if err := user_model.UpdateUser(u); err != nil {
 		if user_model.IsErrEmailAlreadyUsed(err) {
 			ctx.Data["Err_Email"] = true
 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplUserEdit, &form)
@@ -372,7 +372,7 @@ func EditUserPost(ctx *context.Context) {
 
 // DeleteUser response for deleting a user
 func DeleteUser(ctx *context.Context) {
-	u, err := models.GetUserByID(ctx.ParamsInt64(":userid"))
+	u, err := user_model.GetUserByID(ctx.ParamsInt64(":userid"))
 	if err != nil {
 		ctx.ServerError("GetUserByID", err)
 		return
diff --git a/routers/web/admin/users_test.go b/routers/web/admin/users_test.go
index 607ef2ea663..1849781bc47 100644
--- a/routers/web/admin/users_test.go
+++ b/routers/web/admin/users_test.go
@@ -7,8 +7,8 @@ package admin
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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/test"
@@ -23,10 +23,10 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx := test.MockContext(t, "admin/users/new")
 
-	u := unittest.AssertExistsAndLoadBean(t, &models.User{
+	u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		IsAdmin: true,
 		ID:      2,
-	}).(*models.User)
+	}).(*user_model.User)
 
 	ctx.User = u
 
@@ -48,7 +48,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
 
 	assert.NotEmpty(t, ctx.Flash.SuccessMsg)
 
-	u, err := models.GetUserByName(username)
+	u, err := user_model.GetUserByName(username)
 
 	assert.NoError(t, err)
 	assert.Equal(t, username, u.Name)
@@ -60,10 +60,10 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx := test.MockContext(t, "admin/users/new")
 
-	u := unittest.AssertExistsAndLoadBean(t, &models.User{
+	u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		IsAdmin: true,
 		ID:      2,
-	}).(*models.User)
+	}).(*user_model.User)
 
 	ctx.User = u
 
@@ -85,7 +85,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
 
 	assert.NotEmpty(t, ctx.Flash.SuccessMsg)
 
-	u, err := models.GetUserByName(username)
+	u, err := user_model.GetUserByName(username)
 
 	assert.NoError(t, err)
 	assert.Equal(t, username, u.Name)
@@ -97,10 +97,10 @@ func TestNewUserPost_InvalidEmail(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx := test.MockContext(t, "admin/users/new")
 
-	u := unittest.AssertExistsAndLoadBean(t, &models.User{
+	u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		IsAdmin: true,
 		ID:      2,
-	}).(*models.User)
+	}).(*user_model.User)
 
 	ctx.User = u
 
@@ -127,10 +127,10 @@ func TestNewUserPost_VisibilityDefaultPublic(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx := test.MockContext(t, "admin/users/new")
 
-	u := unittest.AssertExistsAndLoadBean(t, &models.User{
+	u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		IsAdmin: true,
 		ID:      2,
-	}).(*models.User)
+	}).(*user_model.User)
 
 	ctx.User = u
 
@@ -152,7 +152,7 @@ func TestNewUserPost_VisibilityDefaultPublic(t *testing.T) {
 
 	assert.NotEmpty(t, ctx.Flash.SuccessMsg)
 
-	u, err := models.GetUserByName(username)
+	u, err := user_model.GetUserByName(username)
 
 	assert.NoError(t, err)
 	assert.Equal(t, username, u.Name)
@@ -165,10 +165,10 @@ func TestNewUserPost_VisibilityPrivate(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	ctx := test.MockContext(t, "admin/users/new")
 
-	u := unittest.AssertExistsAndLoadBean(t, &models.User{
+	u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		IsAdmin: true,
 		ID:      2,
-	}).(*models.User)
+	}).(*user_model.User)
 
 	ctx.User = u
 
@@ -191,7 +191,7 @@ func TestNewUserPost_VisibilityPrivate(t *testing.T) {
 
 	assert.NotEmpty(t, ctx.Flash.SuccessMsg)
 
-	u, err := models.GetUserByName(username)
+	u, err := user_model.GetUserByName(username)
 
 	assert.NoError(t, err)
 	assert.Equal(t, username, u.Name)
diff --git a/routers/web/dev/template.go b/routers/web/dev/template.go
index de334c4f8b8..29d6033a7a1 100644
--- a/routers/web/dev/template.go
+++ b/routers/web/dev/template.go
@@ -7,7 +7,7 @@ package dev
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -16,7 +16,7 @@ import (
 
 // TemplatePreview render for previewing the indicated template
 func TemplatePreview(ctx *context.Context) {
-	ctx.Data["User"] = models.User{Name: "Unknown"}
+	ctx.Data["User"] = user_model.User{Name: "Unknown"}
 	ctx.Data["AppName"] = setting.AppName
 	ctx.Data["AppVer"] = setting.AppVer
 	ctx.Data["AppUrl"] = setting.AppURL
diff --git a/routers/web/explore/org.go b/routers/web/explore/org.go
index d005cfa5032..41c0a0c83cf 100644
--- a/routers/web/explore/org.go
+++ b/routers/web/explore/org.go
@@ -5,8 +5,8 @@
 package explore
 
 import (
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -31,9 +31,9 @@ func Organizations(ctx *context.Context) {
 		visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate)
 	}
 
-	RenderUserSearch(ctx, &models.SearchUserOptions{
+	RenderUserSearch(ctx, &user_model.SearchUserOptions{
 		Actor:       ctx.User,
-		Type:        models.UserTypeOrganization,
+		Type:        user_model.UserTypeOrganization,
 		ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
 		Visible:     visibleTypes,
 	}, tplExploreOrganizations)
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index 78035037e51..a81386b5fd7 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -39,38 +39,38 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
 		repos   []*models.Repository
 		count   int64
 		err     error
-		orderBy models.SearchOrderBy
+		orderBy db.SearchOrderBy
 	)
 
 	ctx.Data["SortType"] = ctx.FormString("sort")
 	switch ctx.FormString("sort") {
 	case "newest":
-		orderBy = models.SearchOrderByNewest
+		orderBy = db.SearchOrderByNewest
 	case "oldest":
-		orderBy = models.SearchOrderByOldest
+		orderBy = db.SearchOrderByOldest
 	case "recentupdate":
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	case "leastupdate":
-		orderBy = models.SearchOrderByLeastUpdated
+		orderBy = db.SearchOrderByLeastUpdated
 	case "reversealphabetically":
-		orderBy = models.SearchOrderByAlphabeticallyReverse
+		orderBy = db.SearchOrderByAlphabeticallyReverse
 	case "alphabetically":
-		orderBy = models.SearchOrderByAlphabetically
+		orderBy = db.SearchOrderByAlphabetically
 	case "reversesize":
-		orderBy = models.SearchOrderBySizeReverse
+		orderBy = db.SearchOrderBySizeReverse
 	case "size":
-		orderBy = models.SearchOrderBySize
+		orderBy = db.SearchOrderBySize
 	case "moststars":
-		orderBy = models.SearchOrderByStarsReverse
+		orderBy = db.SearchOrderByStarsReverse
 	case "feweststars":
-		orderBy = models.SearchOrderByStars
+		orderBy = db.SearchOrderByStars
 	case "mostforks":
-		orderBy = models.SearchOrderByForksReverse
+		orderBy = db.SearchOrderByForksReverse
 	case "fewestforks":
-		orderBy = models.SearchOrderByForks
+		orderBy = db.SearchOrderByForks
 	default:
 		ctx.Data["SortType"] = "recentupdate"
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	}
 
 	keyword := ctx.FormTrim("q")
diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go
index 1fe45ed5856..85603784479 100644
--- a/routers/web/explore/user.go
+++ b/routers/web/explore/user.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
@@ -34,17 +35,17 @@ func isKeywordValid(keyword string) bool {
 }
 
 // RenderUserSearch render user search page
-func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplName base.TplName) {
+func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName base.TplName) {
 	opts.Page = ctx.FormInt("page")
 	if opts.Page <= 1 {
 		opts.Page = 1
 	}
 
 	var (
-		users   []*models.User
+		users   []*user_model.User
 		count   int64
 		err     error
-		orderBy models.SearchOrderBy
+		orderBy db.SearchOrderBy
 	)
 
 	// we can not set orderBy to `models.SearchOrderByXxx`, because there may be a JOIN in the statement, different tables may have the same name columns
@@ -69,7 +70,7 @@ func RenderUserSearch(ctx *context.Context, opts *models.SearchUserOptions, tplN
 	opts.Keyword = ctx.FormTrim("q")
 	opts.OrderBy = orderBy
 	if len(opts.Keyword) == 0 || isKeywordValid(opts.Keyword) {
-		users, count, err = models.SearchUsers(opts)
+		users, count, err = user_model.SearchUsers(opts)
 		if err != nil {
 			ctx.ServerError("SearchUsers", err)
 			return
@@ -100,9 +101,9 @@ func Users(ctx *context.Context) {
 	ctx.Data["PageIsExploreUsers"] = true
 	ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
 
-	RenderUserSearch(ctx, &models.SearchUserOptions{
+	RenderUserSearch(ctx, &user_model.SearchUserOptions{
 		Actor:       ctx.User,
-		Type:        models.UserTypeIndividual,
+		Type:        user_model.UserTypeIndividual,
 		ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
 		IsActive:    util.OptionalBoolTrue,
 		Visible:     []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 8bd0cb7c29c..1a7f4ad24b2 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 
 	"github.com/gorilla/feeds"
@@ -22,7 +23,7 @@ func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*mode
 		return nil
 	}
 
-	userCache := map[int64]*models.User{options.RequestedUser.ID: options.RequestedUser}
+	userCache := map[int64]*user_model.User{options.RequestedUser.ID: options.RequestedUser}
 	if ctx.User != nil {
 		userCache[ctx.User.ID] = ctx.User
 	}
@@ -35,9 +36,9 @@ func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*mode
 	for _, act := range actions {
 		repoOwner, ok := userCache[act.Repo.OwnerID]
 		if !ok {
-			repoOwner, err = models.GetUserByID(act.Repo.OwnerID)
+			repoOwner, err = user_model.GetUserByID(act.Repo.OwnerID)
 			if err != nil {
-				if models.IsErrUserNotExist(err) {
+				if user_model.IsErrUserNotExist(err) {
 					continue
 				}
 				ctx.ServerError("GetUserByID", err)
@@ -51,7 +52,7 @@ func RetrieveFeeds(ctx *context.Context, options models.GetFeedsOptions) []*mode
 }
 
 // ShowUserFeed show user activity as RSS / Atom feed
-func ShowUserFeed(ctx *context.Context, ctxUser *models.User, formatType string) {
+func ShowUserFeed(ctx *context.Context, ctxUser *user_model.User, formatType string) {
 	actions := RetrieveFeeds(ctx, models.GetFeedsOptions{
 		RequestedUser:   ctxUser,
 		Actor:           ctx.User,
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index 6e812571352..0dc441fe84a 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -50,32 +50,32 @@ func Home(ctx *context.Context) {
 		ctx.Data["RenderedDescription"] = desc
 	}
 
-	var orderBy models.SearchOrderBy
+	var orderBy db.SearchOrderBy
 	ctx.Data["SortType"] = ctx.FormString("sort")
 	switch ctx.FormString("sort") {
 	case "newest":
-		orderBy = models.SearchOrderByNewest
+		orderBy = db.SearchOrderByNewest
 	case "oldest":
-		orderBy = models.SearchOrderByOldest
+		orderBy = db.SearchOrderByOldest
 	case "recentupdate":
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	case "leastupdate":
-		orderBy = models.SearchOrderByLeastUpdated
+		orderBy = db.SearchOrderByLeastUpdated
 	case "reversealphabetically":
-		orderBy = models.SearchOrderByAlphabeticallyReverse
+		orderBy = db.SearchOrderByAlphabeticallyReverse
 	case "alphabetically":
-		orderBy = models.SearchOrderByAlphabetically
+		orderBy = db.SearchOrderByAlphabetically
 	case "moststars":
-		orderBy = models.SearchOrderByStarsReverse
+		orderBy = db.SearchOrderByStarsReverse
 	case "feweststars":
-		orderBy = models.SearchOrderByStars
+		orderBy = db.SearchOrderByStars
 	case "mostforks":
-		orderBy = models.SearchOrderByForksReverse
+		orderBy = db.SearchOrderByForksReverse
 	case "fewestforks":
-		orderBy = models.SearchOrderByForks
+		orderBy = db.SearchOrderByForks
 	default:
 		ctx.Data["SortType"] = "recentupdate"
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	}
 
 	keyword := ctx.FormTrim("q")
diff --git a/routers/web/org/org.go b/routers/web/org/org.go
index 6722f8d104c..c66a0cd4c83 100644
--- a/routers/web/org/org.go
+++ b/routers/web/org/org.go
@@ -10,6 +10,8 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -52,7 +54,7 @@ func CreatePost(ctx *context.Context) {
 	org := &models.Organization{
 		Name:                      form.OrgName,
 		IsActive:                  true,
-		Type:                      models.UserTypeOrganization,
+		Type:                      user_model.UserTypeOrganization,
 		Visibility:                form.Visibility,
 		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
 	}
@@ -60,12 +62,12 @@ func CreatePost(ctx *context.Context) {
 	if err := models.CreateOrganization(org, ctx.User); err != nil {
 		ctx.Data["Err_OrgName"] = true
 		switch {
-		case models.IsErrUserAlreadyExist(err):
+		case user_model.IsErrUserAlreadyExist(err):
 			ctx.RenderWithErr(ctx.Tr("form.org_name_been_taken"), tplCreateOrg, &form)
-		case models.IsErrNameReserved(err):
-			ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(models.ErrNameReserved).Name), tplCreateOrg, &form)
-		case models.IsErrNamePatternNotAllowed(err):
-			ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplCreateOrg, &form)
+		case db.IsErrNameReserved(err):
+			ctx.RenderWithErr(ctx.Tr("org.form.name_reserved", err.(db.ErrNameReserved).Name), tplCreateOrg, &form)
+		case db.IsErrNamePatternNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("org.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplCreateOrg, &form)
 		case models.IsErrUserNotAllowedCreateOrg(err):
 			ctx.RenderWithErr(ctx.Tr("org.form.create_org_not_allowed"), tplCreateOrg, &form)
 		default:
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index 7a6f4fec60f..f0e1db14d08 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -61,7 +62,7 @@ func SettingsPost(ctx *context.Context) {
 
 	// Check if organization name has been changed.
 	if org.LowerName != strings.ToLower(form.Name) {
-		isExist, err := models.IsUserExist(org.ID, form.Name)
+		isExist, err := user_model.IsUserExist(org.ID, form.Name)
 		if err != nil {
 			ctx.ServerError("IsUserExist", err)
 			return
@@ -69,8 +70,8 @@ func SettingsPost(ctx *context.Context) {
 			ctx.Data["OrgName"] = true
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tplSettingsOptions, &form)
 			return
-		} else if err = models.ChangeUserName(org.AsUser(), form.Name); err != nil {
-			if err == models.ErrUserNameIllegal {
+		} else if err = user_model.ChangeUserName(org.AsUser(), form.Name); err != nil {
+			if err == user_model.ErrUserNameIllegal {
 				ctx.Data["OrgName"] = true
 				ctx.RenderWithErr(ctx.Tr("form.illegal_username"), tplSettingsOptions, &form)
 			} else {
@@ -101,7 +102,7 @@ func SettingsPost(ctx *context.Context) {
 	visibilityChanged := form.Visibility != org.Visibility
 	org.Visibility = form.Visibility
 
-	if err := models.UpdateUser(org.AsUser()); err != nil {
+	if err := user_model.UpdateUser(org.AsUser()); err != nil {
 		ctx.ServerError("UpdateUser", err)
 		return
 	}
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index ac6a42904a8..da0830aad9a 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	unit_model "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/context"
 	"code.gitea.io/gitea/modules/log"
@@ -114,10 +115,10 @@ func TeamsAction(ctx *context.Context) {
 			return
 		}
 		uname := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("uname")))
-		var u *models.User
-		u, err = models.GetUserByName(uname)
+		var u *user_model.User
+		u, err = user_model.GetUserByName(uname)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 				ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
 			} else {
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 2fd72d81ab1..5786aa1eb2d 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -12,6 +12,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
@@ -139,9 +140,9 @@ func RefBlame(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplBlame)
 }
 
-func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*models.UserCommit, map[string]string) {
+func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) {
 	// store commit data by SHA to look up avatar info etc
-	commitNames := make(map[string]*models.UserCommit)
+	commitNames := make(map[string]*user_model.UserCommit)
 	// previousCommits contains links from SHA to parent SHA,
 	// if parent also contains the current TreePath.
 	previousCommits := make(map[string]string)
@@ -195,14 +196,14 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
 	}
 
 	// populate commit email addresses to later look up avatars.
-	for _, c := range models.ValidateCommitsWithEmails(commits) {
+	for _, c := range user_model.ValidateCommitsWithEmails(commits) {
 		commitNames[c.ID.String()] = c
 	}
 
 	return commitNames, previousCommits
 }
 
-func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*models.UserCommit, previousCommits map[string]string) {
+func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit, previousCommits map[string]string) {
 	repoLink := ctx.Repo.RepoLink
 
 	language := ""
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index ecb5107a3d9..48386f7935f 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/context"
@@ -344,7 +345,7 @@ func Diff(ctx *context.Context) {
 
 	verification := models.ParseCommitWithSignature(commit)
 	ctx.Data["Verification"] = verification
-	ctx.Data["Author"] = models.ValidateCommitWithEmail(commit)
+	ctx.Data["Author"] = user_model.ValidateCommitWithEmail(commit)
 	ctx.Data["Parents"] = parents
 	ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
 
@@ -358,7 +359,7 @@ func Diff(ctx *context.Context) {
 	if err == nil {
 		ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message))
 		ctx.Data["NoteCommit"] = note.Commit
-		ctx.Data["NoteAuthor"] = models.ValidateCommitWithEmail(note.Commit)
+		ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(note.Commit)
 	}
 
 	ctx.Data["BranchName"], err = commit.GetBranchName()
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 706009820a4..865a734caf2 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -18,6 +18,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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/charset"
 	"code.gitea.io/gitea/modules/context"
@@ -163,7 +164,7 @@ func setCsvCompareContext(ctx *context.Context) {
 
 // CompareInfo represents the collected results from ParseCompareInfo
 type CompareInfo struct {
-	HeadUser         *models.User
+	HeadUser         *user_model.User
 	HeadRepo         *models.Repository
 	HeadGitRepo      *git.Repository
 	CompareInfo      *git.CompareInfo
@@ -237,9 +238,9 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 	} else if len(headInfos) == 2 {
 		headInfosSplit := strings.Split(headInfos[0], "/")
 		if len(headInfosSplit) == 1 {
-			ci.HeadUser, err = models.GetUserByName(headInfos[0])
+			ci.HeadUser, err = user_model.GetUserByName(headInfos[0])
 			if err != nil {
-				if models.IsErrUserNotExist(err) {
+				if user_model.IsErrUserNotExist(err) {
 					ctx.NotFound("GetUserByName", nil)
 				} else {
 					ctx.ServerError("GetUserByName", err)
@@ -262,7 +263,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
 				return nil
 			}
 			if err := ci.HeadRepo.GetOwner(); err != nil {
-				if models.IsErrUserNotExist(err) {
+				if user_model.IsErrUserNotExist(err) {
 					ctx.NotFound("GetUserByName", nil)
 				} else {
 					ctx.ServerError("GetUserByName", err)
diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go
index 0626d08a7cf..ce2d977518e 100644
--- a/routers/web/repo/http.go
+++ b/routers/web/repo/http.go
@@ -110,9 +110,9 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 		reponame = reponame[:len(reponame)-5]
 	}
 
-	owner, err := models.GetUserByName(username)
+	owner, err := user_model.GetUserByName(username)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil {
 				context.RedirectToUser(ctx, username, redirectUserID)
 			} else {
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index ac6240e282d..1ff126c7d16 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -20,6 +20,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/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -470,7 +471,7 @@ func retrieveProjects(ctx *context.Context, repo *models.Repository) {
 type repoReviewerSelection struct {
 	IsTeam    bool
 	Team      *models.Team
-	User      *models.User
+	User      *user_model.User
 	Review    *models.Review
 	CanChange bool
 	Checked   bool
@@ -503,7 +504,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *models.Repository, issue
 		reviewersResult     []*repoReviewerSelection
 		teamReviewersResult []*repoReviewerSelection
 		teamReviewers       []*models.Team
-		reviewers           []*models.User
+		reviewers           []*user_model.User
 	)
 
 	if canChooseReviewer {
@@ -575,7 +576,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *models.Repository, issue
 		for _, item := range pullReviews {
 			if item.Review.ReviewerID > 0 {
 				if err = item.Review.LoadReviewer(); err != nil {
-					if models.IsErrUserNotExist(err) {
+					if user_model.IsErrUserNotExist(err) {
 						continue
 					}
 					ctx.ServerError("LoadReviewer", err)
@@ -919,7 +920,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull
 
 		// Check if the passed assignees actually exists and is assignable
 		for _, aID := range assigneeIDs {
-			assignee, err := models.GetUserByID(aID)
+			assignee, err := user_model.GetUserByID(aID)
 			if err != nil {
 				ctx.ServerError("GetUserByID", err)
 				return nil, nil, 0, 0
@@ -1019,7 +1020,7 @@ func NewIssuePost(ctx *context.Context) {
 }
 
 // roleDescriptor returns the Role Decriptor for a comment in/with the given repo, poster and issue
-func roleDescriptor(repo *models.Repository, poster *models.User, issue *models.Issue) (models.RoleDescriptor, error) {
+func roleDescriptor(repo *models.Repository, poster *user_model.User, issue *models.Issue) (models.RoleDescriptor, error) {
 	perm, err := models.GetUserRepoPermission(repo, poster)
 	if err != nil {
 		return models.RoleDescriptorNone, err
@@ -1267,7 +1268,7 @@ func ViewIssue(ctx *context.Context) {
 		ok           bool
 		marked       = make(map[int64]models.RoleDescriptor)
 		comment      *models.Comment
-		participants = make([]*models.User, 1, 10)
+		participants = make([]*user_model.User, 1, 10)
 	)
 	if ctx.Repo.Repository.IsTimetrackerEnabled() {
 		if ctx.IsSigned {
@@ -1430,11 +1431,11 @@ func ViewIssue(ctx *context.Context) {
 				continue
 			}
 			if err = comment.Review.LoadAttributes(); err != nil {
-				if !models.IsErrUserNotExist(err) {
+				if !user_model.IsErrUserNotExist(err) {
 					ctx.ServerError("Review.LoadAttributes", err)
 					return
 				}
-				comment.Review.Reviewer = models.NewGhostUser()
+				comment.Review.Reviewer = user_model.NewGhostUser()
 			}
 			if err = comment.Review.LoadCodeComments(); err != nil {
 				ctx.ServerError("Review.LoadCodeComments", err)
@@ -1833,12 +1834,12 @@ func UpdateIssueAssignee(ctx *context.Context) {
 	for _, issue := range issues {
 		switch action {
 		case "clear":
-			if err := issue_service.DeleteNotPassedAssignee(issue, ctx.User, []*models.User{}); err != nil {
+			if err := issue_service.DeleteNotPassedAssignee(issue, ctx.User, []*user_model.User{}); err != nil {
 				ctx.ServerError("ClearAssignees", err)
 				return
 			}
 		default:
-			assignee, err := models.GetUserByID(assigneeID)
+			assignee, err := user_model.GetUserByID(assigneeID)
 			if err != nil {
 				ctx.ServerError("GetUserByID", err)
 				return
@@ -1949,9 +1950,9 @@ func UpdatePullReviewRequest(ctx *context.Context) {
 			continue
 		}
 
-		reviewer, err := models.GetUserByID(reviewID)
+		reviewer, err := user_model.GetUserByID(reviewID)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				log.Warn(
 					"UpdatePullReviewRequest: requested reviewer [%d] for %-v to %-v#%d is not exist: Error: %v",
 					reviewID, issue.Repo, issue.Index,
@@ -2452,7 +2453,7 @@ func ChangeCommentReaction(ctx *context.Context) {
 	})
 }
 
-func addParticipant(poster *models.User, participants []*models.User) []*models.User {
+func addParticipant(poster *user_model.User, participants []*user_model.User) []*user_model.User {
 	for _, part := range participants {
 		if poster.ID == part.ID {
 			return participants
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go
index b15c7628db6..b1bc06f08b4 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/lfs.go
@@ -216,9 +216,9 @@ func LFSLockFile(ctx *context.Context) {
 	}
 
 	_, err := models.CreateLFSLock(&models.LFSLock{
-		Repo:  ctx.Repo.Repository,
-		Path:  lockPath,
-		Owner: ctx.User,
+		Repo:    ctx.Repo.Repository,
+		Path:    lockPath,
+		OwnerID: ctx.User.ID,
 	})
 	if err != nil {
 		if models.IsErrLFSLockAlreadyExist(err) {
diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go
index 0dc6e1cb720..3ee9d1a3e98 100644
--- a/routers/web/repo/middlewares.go
+++ b/routers/web/repo/middlewares.go
@@ -7,8 +7,8 @@ package repo
 import (
 	"fmt"
 
-	"code.gitea.io/gitea/models"
 	admin_model "code.gitea.io/gitea/models/admin"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 )
@@ -56,7 +56,7 @@ func SetDiffViewStyle(ctx *context.Context) {
 	}
 
 	ctx.Data["IsSplitStyle"] = style == "split"
-	if err := models.UpdateUserDiffViewStyle(ctx.User, style); err != nil {
+	if err := user_model.UpdateUserDiffViewStyle(ctx.User, style); err != nil {
 		ctx.ServerError("ErrUpdateDiffViewStyle", err)
 	}
 }
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index 1940d896286..5eba4690d0e 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -11,6 +11,8 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/lfs"
@@ -66,7 +68,7 @@ func Migrate(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, base.TplName("repo/migrate/"+serviceType.Name()))
 }
 
-func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *forms.MigrateRepoForm) {
+func handleMigrateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl base.TplName, form *forms.MigrateRepoForm) {
 	if setting.Repository.DisableMigrations {
 		ctx.Error(http.StatusForbidden, "MigrateError: the site administrator has disabled migrations")
 		return
@@ -94,12 +96,12 @@ func handleMigrateError(ctx *context.Context, owner *models.User, err error, nam
 		default:
 			ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tpl, form)
 		}
-	case models.IsErrNameReserved(err):
+	case db.IsErrNameReserved(err):
 		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
-	case models.IsErrNamePatternNotAllowed(err):
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tpl, form)
+	case db.IsErrNamePatternNotAllowed(err):
 		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tpl, form)
 	default:
 		remoteAddr, _ := forms.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
 		err = util.NewStringURLSanitizedError(err, remoteAddr, true)
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 83e353833fc..06aa86206d2 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -20,6 +20,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/context"
 	"code.gitea.io/gitea/modules/git"
@@ -237,10 +238,10 @@ func ForkPost(ctx *context.Context) {
 		switch {
 		case models.IsErrRepoAlreadyExist(err):
 			ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
-		case models.IsErrNameReserved(err):
-			ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplFork, &form)
-		case models.IsErrNamePatternNotAllowed(err):
-			ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplFork, &form)
+		case db.IsErrNameReserved(err):
+			ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplFork, &form)
+		case db.IsErrNamePatternNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplFork, &form)
 		default:
 			ctx.ServerError("ForkPost", err)
 		}
@@ -1024,7 +1025,7 @@ func MergePullRequest(ctx *context.Context) {
 	ctx.Redirect(issue.Link())
 }
 
-func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
+func stopTimerIfAvailable(user *user_model.User, issue *models.Issue) error {
 
 	if models.StopwatchExists(user.ID, issue.ID) {
 		if err := models.CreateOrStopIssueStopwatch(user, issue); err != nil {
@@ -1185,9 +1186,9 @@ func TriggerTask(ctx *context.Context) {
 		return
 	}
 
-	pusher, err := models.GetUserByID(pusherID)
+	pusher, err := user_model.GetUserByID(pusherID)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Error(http.StatusNotFound)
 		} else {
 			ctx.ServerError("GetUserByID", err)
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 3f12ee72bcb..9bca4e68978 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/context"
 	"code.gitea.io/gitea/modules/log"
@@ -132,7 +133,7 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
 
 	// Temporary cache commits count of used branches to speed up.
 	countCache := make(map[string]int64)
-	cacheUsers := make(map[int64]*models.User)
+	cacheUsers := make(map[int64]*user_model.User)
 	if ctx.User != nil {
 		cacheUsers[ctx.User.ID] = ctx.User
 	}
@@ -140,10 +141,10 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
 
 	for _, r := range releases {
 		if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok {
-			r.Publisher, err = models.GetUserByID(r.PublisherID)
+			r.Publisher, err = user_model.GetUserByID(r.PublisherID)
 			if err != nil {
-				if models.IsErrUserNotExist(err) {
-					r.Publisher = models.NewGhostUser()
+				if user_model.IsErrUserNotExist(err) {
+					r.Publisher = user_model.NewGhostUser()
 				} else {
 					ctx.ServerError("GetUserByID", err)
 					return
@@ -207,10 +208,10 @@ func SingleRelease(ctx *context.Context) {
 		return
 	}
 
-	release.Publisher, err = models.GetUserByID(release.PublisherID)
+	release.Publisher, err = user_model.GetUserByID(release.PublisherID)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
-			release.Publisher = models.NewGhostUser()
+		if user_model.IsErrUserNotExist(err) {
+			release.Publisher = user_model.NewGhostUser()
 		} else {
 			ctx.ServerError("GetUserByID", err)
 			return
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 9463fc4c5f7..bacfa549b51 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -16,6 +16,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/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/graceful"
@@ -55,7 +56,7 @@ func MustBeAbleToUpload(ctx *context.Context) {
 	}
 }
 
-func checkContextUser(ctx *context.Context, uid int64) *models.User {
+func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
 	orgs, err := models.GetOrgsCanCreateRepoByUserID(ctx.User.ID)
 	if err != nil {
 		ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
@@ -79,8 +80,8 @@ func checkContextUser(ctx *context.Context, uid int64) *models.User {
 		return ctx.User
 	}
 
-	org, err := models.GetUserByID(uid)
-	if models.IsErrUserNotExist(err) {
+	org, err := user_model.GetUserByID(uid)
+	if user_model.IsErrUserNotExist(err) {
 		return ctx.User
 	}
 
@@ -158,7 +159,7 @@ func Create(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplCreate)
 }
 
-func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
+func handleCreateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl base.TplName, form interface{}) {
 	switch {
 	case models.IsErrReachLimitOfRepo(err):
 		ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
@@ -177,12 +178,12 @@ func handleCreateError(ctx *context.Context, owner *models.User, err error, name
 		default:
 			ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tpl, form)
 		}
-	case models.IsErrNameReserved(err):
+	case db.IsErrNameReserved(err):
 		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
-	case models.IsErrNamePatternNotAllowed(err):
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tpl, form)
+	case db.IsErrNamePatternNotAllowed(err):
 		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tpl, form)
 	default:
 		ctx.ServerError(name, err)
 	}
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 6753ce11722..20fa9ea7855 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	unit_model "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/context"
 	"code.gitea.io/gitea/modules/git"
@@ -94,8 +95,8 @@ func SettingsPost(ctx *context.Context) {
 				switch {
 				case models.IsErrRepoAlreadyExist(err):
 					ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form)
-				case models.IsErrNameReserved(err):
-					ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplSettingsOptions, &form)
+				case db.IsErrNameReserved(err):
+					ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(db.ErrNameReserved).Name), tplSettingsOptions, &form)
 				case models.IsErrRepoFilesAlreadyExist(err):
 					ctx.Data["Err_RepoName"] = true
 					switch {
@@ -108,8 +109,8 @@ func SettingsPost(ctx *context.Context) {
 					default:
 						ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tplSettingsOptions, form)
 					}
-				case models.IsErrNamePatternNotAllowed(err):
-					ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
+				case db.IsErrNamePatternNotAllowed(err):
+					ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
 				default:
 					ctx.ServerError("ChangeRepositoryName", err)
 				}
@@ -577,9 +578,9 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
-		newOwner, err := models.GetUserByName(ctx.FormString("new_owner_name"))
+		newOwner, err := user_model.GetUserByName(ctx.FormString("new_owner_name"))
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
 				return
 			}
@@ -587,7 +588,7 @@ func SettingsPost(ctx *context.Context) {
 			return
 		}
 
-		if newOwner.Type == models.UserTypeOrganization {
+		if newOwner.Type == user_model.UserTypeOrganization {
 			if !ctx.User.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !models.OrgFromUser(newOwner).HasMemberWithUserID(ctx.User.ID) {
 				// The user shouldn't know about this organization
 				ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
@@ -798,9 +799,9 @@ func CollaborationPost(ctx *context.Context) {
 		return
 	}
 
-	u, err := models.GetUserByName(name)
+	u, err := user_model.GetUserByName(name)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
 			ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
 		} else {
@@ -935,10 +936,10 @@ func DeleteTeam(ctx *context.Context) {
 }
 
 // parseOwnerAndRepo get repos by owner
-func parseOwnerAndRepo(ctx *context.Context) (*models.User, *models.Repository) {
-	owner, err := models.GetUserByName(ctx.Params(":username"))
+func parseOwnerAndRepo(ctx *context.Context) (*user_model.User, *models.Repository) {
+	owner, err := user_model.GetUserByName(ctx.Params(":username"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound("GetUserByName", err)
 		} else {
 			ctx.ServerError("GetUserByName", err)
diff --git a/routers/web/repo/settings_test.go b/routers/web/repo/settings_test.go
index 3e8ae2b07e9..8e53babe7c3 100644
--- a/routers/web/repo/settings_test.go
+++ b/routers/web/repo/settings_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/test"
@@ -105,9 +106,9 @@ func TestCollaborationPost(t *testing.T) {
 
 	ctx.Req.Form.Set("collaborator", "user4")
 
-	u := &models.User{
+	u := &user_model.User{
 		LowerName: "user2",
-		Type:      models.UserTypeIndividual,
+		Type:      user_model.UserTypeIndividual,
 	}
 
 	re := &models.Repository{
@@ -142,7 +143,7 @@ func TestCollaborationPost_InactiveUser(t *testing.T) {
 	ctx.Req.Form.Set("collaborator", "user9")
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			LowerName: "user2",
 		},
 	}
@@ -165,9 +166,9 @@ func TestCollaborationPost_AddCollaboratorTwice(t *testing.T) {
 
 	ctx.Req.Form.Set("collaborator", "user4")
 
-	u := &models.User{
+	u := &user_model.User{
 		LowerName: "user2",
-		Type:      models.UserTypeIndividual,
+		Type:      user_model.UserTypeIndividual,
 	}
 
 	re := &models.Repository{
@@ -207,7 +208,7 @@ func TestCollaborationPost_NonExistentUser(t *testing.T) {
 	ctx.Req.Form.Set("collaborator", "user34")
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			LowerName: "user2",
 		},
 	}
@@ -226,9 +227,9 @@ func TestAddTeamPost(t *testing.T) {
 
 	ctx.Req.Form.Set("team", "team11")
 
-	org := &models.User{
+	org := &user_model.User{
 		LowerName: "org26",
-		Type:      models.UserTypeOrganization,
+		Type:      user_model.UserTypeOrganization,
 	}
 
 	team := &models.Team{
@@ -243,7 +244,7 @@ func TestAddTeamPost(t *testing.T) {
 	}
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			ID:                        26,
 			LowerName:                 "org26",
 			RepoAdminChangeTeamAccess: true,
@@ -266,9 +267,9 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
 
 	ctx.Req.Form.Set("team", "team11")
 
-	org := &models.User{
+	org := &user_model.User{
 		LowerName: "org26",
-		Type:      models.UserTypeOrganization,
+		Type:      user_model.UserTypeOrganization,
 	}
 
 	team := &models.Team{
@@ -283,7 +284,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
 	}
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			ID:                        26,
 			LowerName:                 "org26",
 			RepoAdminChangeTeamAccess: false,
@@ -307,9 +308,9 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
 
 	ctx.Req.Form.Set("team", "team11")
 
-	org := &models.User{
+	org := &user_model.User{
 		LowerName: "org26",
-		Type:      models.UserTypeOrganization,
+		Type:      user_model.UserTypeOrganization,
 	}
 
 	team := &models.Team{
@@ -324,7 +325,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
 	}
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			ID:                        26,
 			LowerName:                 "org26",
 			RepoAdminChangeTeamAccess: true,
@@ -348,9 +349,9 @@ func TestAddTeamPost_NonExistentTeam(t *testing.T) {
 
 	ctx.Req.Form.Set("team", "team-non-existent")
 
-	org := &models.User{
+	org := &user_model.User{
 		LowerName: "org26",
-		Type:      models.UserTypeOrganization,
+		Type:      user_model.UserTypeOrganization,
 	}
 
 	re := &models.Repository{
@@ -360,7 +361,7 @@ func TestAddTeamPost_NonExistentTeam(t *testing.T) {
 	}
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			ID:                        26,
 			LowerName:                 "org26",
 			RepoAdminChangeTeamAccess: true,
@@ -381,9 +382,9 @@ func TestDeleteTeam(t *testing.T) {
 
 	ctx.Req.Form.Set("id", "2")
 
-	org := &models.User{
+	org := &user_model.User{
 		LowerName: "org3",
-		Type:      models.UserTypeOrganization,
+		Type:      user_model.UserTypeOrganization,
 	}
 
 	team := &models.Team{
@@ -398,7 +399,7 @@ func TestDeleteTeam(t *testing.T) {
 	}
 
 	repo := &context.Repository{
-		Owner: &models.User{
+		Owner: &user_model.User{
 			ID:                        3,
 			LowerName:                 "org3",
 			RepoAdminChangeTeamAccess: true,
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 1293882cc5f..33420aab3b9 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	unit_model "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/cache"
 	"code.gitea.io/gitea/modules/charset"
@@ -449,7 +450,12 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 		return
 	}
 	if lfsLock != nil {
-		ctx.Data["LFSLockOwner"] = lfsLock.Owner.DisplayName()
+		u, err := user_model.GetUserByID(lfsLock.OwnerID)
+		if err != nil {
+			ctx.ServerError("GetTreePathLock", err)
+			return
+		}
+		ctx.Data["LFSLockOwner"] = u.DisplayName()
 		ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked")
 	}
 
@@ -777,7 +783,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
 			return nil
 		}
 		ctx.Data["LatestCommitVerification"] = verification
-		ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
+		ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(latestCommit)
 	}
 
 	statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), db.ListOptions{})
@@ -893,7 +899,7 @@ func renderCode(ctx *context.Context) {
 }
 
 // RenderUserCards render a page show users according the input template
-func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOptions) ([]*models.User, error), tpl base.TplName) {
+func RenderUserCards(ctx *context.Context, total int, getter func(opts db.ListOptions) ([]*user_model.User, error), tpl base.TplName) {
 	page := ctx.FormInt("page")
 	if page <= 0 {
 		page = 1
@@ -928,7 +934,7 @@ func Stars(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.stargazers")
 	ctx.Data["CardsTitle"] = ctx.Tr("repo.stargazers")
 	ctx.Data["PageIsStargazers"] = true
-	RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*models.User, error) {
+	RenderUserCards(ctx, ctx.Repo.Repository.NumStars, func(opts db.ListOptions) ([]*user_model.User, error) {
 		return models.GetStargazers(ctx.Repo.Repository, opts)
 	}, tplWatchers)
 }
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index 4f6660926ee..28ca3e0de54 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -1149,7 +1150,7 @@ func TestWebhook(ctx *context.Context) {
 	// Grab latest commit or fake one if it's empty repository.
 	commit := ctx.Repo.Commit
 	if commit == nil {
-		ghost := models.NewGhostUser()
+		ghost := user_model.NewGhostUser()
 		commit = &git.Commit{
 			ID:            git.MustIDFromString(git.EmptySHA),
 			Author:        ghost.NewGitSig(),
diff --git a/routers/web/user/auth.go b/routers/web/user/auth.go
index c5164c4956e..02f5afd8bae 100644
--- a/routers/web/user/auth.go
+++ b/routers/web/user/auth.go
@@ -76,9 +76,9 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
 		}
 	}()
 
-	u, err := models.GetUserByName(uname)
+	u, err := user_model.GetUserByName(uname)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			return false, fmt.Errorf("GetUserByName: %v", err)
 		}
 		return false, nil
@@ -180,17 +180,17 @@ func SignInPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.SignInForm)
 	u, source, err := auth.UserSignIn(form.UserName, form.Password)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
 		} else if user_model.IsErrEmailAlreadyUsed(err) {
 			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSignIn, &form)
 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
-		} else if models.IsErrUserProhibitLogin(err) {
+		} else if user_model.IsErrUserProhibitLogin(err) {
 			log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
 			ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
 			ctx.HTML(http.StatusOK, "user/auth/prohibit_login")
-		} else if models.IsErrUserInactive(err) {
+		} else if user_model.IsErrUserInactive(err) {
 			if setting.Service.RegisterEmailConfirm {
 				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
 				ctx.HTML(http.StatusOK, TplActivate)
@@ -314,7 +314,7 @@ func TwoFactorPost(ctx *context.Context) {
 
 	if ok && twofa.LastUsedPasscode != form.Passcode {
 		remember := ctx.Session.Get("twofaRemember").(bool)
-		u, err := models.GetUserByID(id)
+		u, err := user_model.GetUserByID(id)
 		if err != nil {
 			ctx.ServerError("UserSignIn", err)
 			return
@@ -398,7 +398,7 @@ func TwoFactorScratchPost(ctx *context.Context) {
 		}
 
 		remember := ctx.Session.Get("twofaRemember").(bool)
-		u, err := models.GetUserByID(id)
+		u, err := user_model.GetUserByID(id)
 		if err != nil {
 			ctx.ServerError("UserSignIn", err)
 			return
@@ -495,7 +495,7 @@ func U2FSign(ctx *context.Context) {
 		newCounter, authErr := r.Authenticate(*signResp, *challenge, reg.Counter)
 		if authErr == nil {
 			reg.Counter = newCounter
-			user, err := models.GetUserByID(id)
+			user, err := user_model.GetUserByID(id)
 			if err != nil {
 				ctx.ServerError("UserSignIn", err)
 				return
@@ -531,11 +531,11 @@ func U2FSign(ctx *context.Context) {
 }
 
 // This handles the final part of the sign-in process of the user.
-func handleSignIn(ctx *context.Context, u *models.User, remember bool) {
+func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) {
 	handleSignInFull(ctx, u, remember, true)
 }
 
-func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyRedirect bool) string {
+func handleSignInFull(ctx *context.Context, u *user_model.User, remember bool, obeyRedirect bool) string {
 	if remember {
 		days := 86400 * setting.LogInRememberDays
 		ctx.SetCookie(setting.CookieUserName, u.Name, days)
@@ -565,7 +565,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 	// If the user does not have a locale set, we save the current one.
 	if len(u.Language) == 0 {
 		u.Language = ctx.Locale.Language()
-		if err := models.UpdateUserCols(db.DefaultContext, u, "language"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, u, "language"); err != nil {
 			log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
 			return setting.AppSubURL + "/"
 		}
@@ -582,7 +582,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
 
 	// Register last login
 	u.SetLastLogin()
-	if err := models.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil {
+	if err := user_model.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil {
 		ctx.ServerError("UpdateUserCols", err)
 		return setting.AppSubURL + "/"
 	}
@@ -680,7 +680,7 @@ func SignInOAuthCallback(ctx *context.Context) {
 				ctx.ServerError("CreateUser", err)
 				return
 			}
-			u = &models.User{
+			u = &user_model.User{
 				Name:        getUserName(&gothUser),
 				FullName:    gothUser.Name,
 				Email:       gothUser.Email,
@@ -725,7 +725,7 @@ func showLinkingLogin(ctx *context.Context, gothUser goth.User) {
 	ctx.Redirect(setting.AppSubURL + "/user/link_account")
 }
 
-func updateAvatarIfNeed(url string, u *models.User) {
+func updateAvatarIfNeed(url string, u *user_model.User) {
 	if setting.OAuth2Client.UpdateAvatar && len(url) > 0 {
 		resp, err := http.Get(url)
 		if err == nil {
@@ -743,7 +743,7 @@ func updateAvatarIfNeed(url string, u *models.User) {
 	}
 }
 
-func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *models.User, gothUser goth.User) {
+func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *user_model.User, gothUser goth.User) {
 	updateAvatarIfNeed(gothUser.AvatarURL, u)
 
 	needs2FA := false
@@ -774,7 +774,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *models.Us
 
 		// Register last login
 		u.SetLastLogin()
-		if err := models.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, u, "last_login_unix"); err != nil {
 			ctx.ServerError("UpdateUserCols", err)
 			return
 		}
@@ -817,7 +817,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *login.Source, u *models.Us
 
 // OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful
 // login the user
-func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, response http.ResponseWriter) (*models.User, goth.User, error) {
+func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) {
 	gothUser, err := loginSource.Cfg.(*oauth2.Source).Callback(request, response)
 	if err != nil {
 		if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") {
@@ -827,13 +827,13 @@ func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, r
 		return nil, goth.User{}, err
 	}
 
-	user := &models.User{
+	user := &user_model.User{
 		LoginName:   gothUser.UserID,
 		LoginType:   login.OAuth2,
 		LoginSource: loginSource.ID,
 	}
 
-	hasUser, err := models.GetUser(user)
+	hasUser, err := user_model.GetUser(user)
 	if err != nil {
 		return nil, goth.User{}, err
 	}
@@ -852,7 +852,7 @@ func oAuth2UserLoginCallback(loginSource *login.Source, request *http.Request, r
 		return nil, goth.User{}, err
 	}
 	if hasUser {
-		user, err = models.GetUserByID(externalLoginUser.UserID)
+		user, err = user_model.GetUserByID(externalLoginUser.UserID)
 		return user, gothUser, err
 	}
 
@@ -893,8 +893,8 @@ func LinkAccount(ctx *context.Context) {
 	ctx.Data["email"] = email
 
 	if len(email) != 0 {
-		u, err := models.GetUserByEmail(email)
-		if err != nil && !models.IsErrUserNotExist(err) {
+		u, err := user_model.GetUserByEmail(email)
+		if err != nil && !user_model.IsErrUserNotExist(err) {
 			ctx.ServerError("UserSignIn", err)
 			return
 		}
@@ -902,8 +902,8 @@ func LinkAccount(ctx *context.Context) {
 			ctx.Data["user_exists"] = true
 		}
 	} else if len(uname) != 0 {
-		u, err := models.GetUserByName(uname)
-		if err != nil && !models.IsErrUserNotExist(err) {
+		u, err := user_model.GetUserByName(uname)
+		if err != nil && !user_model.IsErrUserNotExist(err) {
 			ctx.ServerError("UserSignIn", err)
 			return
 		}
@@ -948,7 +948,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
 
 	u, _, err := auth.UserSignIn(signInForm.UserName, signInForm.Password)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Data["user_exists"] = true
 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm)
 		} else {
@@ -960,7 +960,7 @@ func LinkAccountPostSignIn(ctx *context.Context) {
 	linkAccount(ctx, u, gothUser.(goth.User), signInForm.Remember)
 }
 
-func linkAccount(ctx *context.Context, u *models.User, gothUser goth.User, remember bool) {
+func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, remember bool) {
 	updateAvatarIfNeed(gothUser.AvatarURL, u)
 
 	// If this user is enrolled in 2FA, we can't sign the user in just yet.
@@ -1081,7 +1081,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
 	}
 
 	if setting.Service.AllowOnlyExternalRegistration || !setting.Service.RequireExternalRegistrationPassword {
-		// In models.User an empty password is classed as not set, so we set form.Password to empty.
+		// In user_model.User an empty password is classed as not set, so we set form.Password to empty.
 		// Eventually the database should be changed to indicate "Second Factor"-enabled accounts
 		// (accounts that do not introduce the security vulnerabilities of a password).
 		// If a user decides to circumvent second-factor security, and purposefully create a password,
@@ -1105,7 +1105,7 @@ func LinkAccountPostRegister(ctx *context.Context) {
 		ctx.ServerError("CreateUser", err)
 	}
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:        form.UserName,
 		Email:       form.Email,
 		Passwd:      form.Password,
@@ -1249,7 +1249,7 @@ func SignUpPost(ctx *context.Context) {
 		return
 	}
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:         form.UserName,
 		Email:        form.Email,
 		Passwd:       form.Password,
@@ -1268,7 +1268,7 @@ func SignUpPost(ctx *context.Context) {
 
 // createAndHandleCreatedUser calls createUserInContext and
 // then handleUserCreated.
-func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *models.User, gothUser *goth.User, allowLink bool) bool {
+func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) bool {
 	if !createUserInContext(ctx, tpl, form, u, gothUser, allowLink) {
 		return false
 	}
@@ -1277,16 +1277,16 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form int
 
 // createUserInContext creates a user and handles errors within a given context.
 // Optionally a template can be specified.
-func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *models.User, gothUser *goth.User, allowLink bool) (ok bool) {
-	if err := models.CreateUser(u); err != nil {
-		if allowLink && (models.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
+func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, gothUser *goth.User, allowLink bool) (ok bool) {
+	if err := user_model.CreateUser(u); err != nil {
+		if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) {
 			if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto {
-				var user *models.User
-				user = &models.User{Name: u.Name}
-				hasUser, err := models.GetUser(user)
+				var user *user_model.User
+				user = &user_model.User{Name: u.Name}
+				hasUser, err := user_model.GetUser(user)
 				if !hasUser || err != nil {
-					user = &models.User{Email: u.Email}
-					hasUser, err = models.GetUser(user)
+					user = &user_model.User{Email: u.Email}
+					hasUser, err = user_model.GetUser(user)
 					if !hasUser || err != nil {
 						ctx.ServerError("UserLinkAccount", err)
 						return
@@ -1310,7 +1310,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{
 
 		// handle error with template
 		switch {
-		case models.IsErrUserAlreadyExist(err):
+		case user_model.IsErrUserAlreadyExist(err):
 			ctx.Data["Err_UserName"] = true
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), tpl, form)
 		case user_model.IsErrEmailAlreadyUsed(err):
@@ -1319,15 +1319,15 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{
 		case user_model.IsErrEmailInvalid(err):
 			ctx.Data["Err_Email"] = true
 			ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tpl, form)
-		case models.IsErrNameReserved(err):
+		case db.IsErrNameReserved(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
-		case models.IsErrNamePatternNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("user.form.name_reserved", err.(db.ErrNameReserved).Name), tpl, form)
+		case db.IsErrNamePatternNotAllowed(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
-		case models.IsErrNameCharsNotAllowed(err):
+			ctx.RenderWithErr(ctx.Tr("user.form.name_pattern_not_allowed", err.(db.ErrNamePatternNotAllowed).Pattern), tpl, form)
+		case db.IsErrNameCharsNotAllowed(err):
 			ctx.Data["Err_UserName"] = true
-			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(models.ErrNameCharsNotAllowed).Name), tpl, form)
+			ctx.RenderWithErr(ctx.Tr("user.form.name_chars_not_allowed", err.(db.ErrNameCharsNotAllowed).Name), tpl, form)
 		default:
 			ctx.ServerError("CreateUser", err)
 		}
@@ -1340,13 +1340,13 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{
 // handleUserCreated does additional steps after a new user is created.
 // It auto-sets admin for the only user, updates the optional external user and
 // sends a confirmation email if required.
-func handleUserCreated(ctx *context.Context, u *models.User, gothUser *goth.User) (ok bool) {
+func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.User) (ok bool) {
 	// Auto-set admin for the only user.
-	if models.CountUsers() == 1 {
+	if user_model.CountUsers() == 1 {
 		u.IsAdmin = true
 		u.IsActive = true
 		u.SetLastLogin()
-		if err := models.UpdateUserCols(db.DefaultContext, u, "is_admin", "is_active", "last_login_unix"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, u, "is_admin", "is_active", "last_login_unix"); err != nil {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
@@ -1406,7 +1406,7 @@ func Activate(ctx *context.Context) {
 		return
 	}
 
-	user := models.VerifyUserActiveCode(code)
+	user := user_model.VerifyUserActiveCode(code)
 	// if code is wrong
 	if user == nil {
 		ctx.Data["IsActivateFailed"] = true
@@ -1433,7 +1433,7 @@ func ActivatePost(ctx *context.Context) {
 		return
 	}
 
-	user := models.VerifyUserActiveCode(code)
+	user := user_model.VerifyUserActiveCode(code)
 	// if code is wrong
 	if user == nil {
 		ctx.Data["IsActivateFailed"] = true
@@ -1460,15 +1460,15 @@ func ActivatePost(ctx *context.Context) {
 	handleAccountActivation(ctx, user)
 }
 
-func handleAccountActivation(ctx *context.Context, user *models.User) {
+func handleAccountActivation(ctx *context.Context, user *user_model.User) {
 	user.IsActive = true
 	var err error
-	if user.Rands, err = models.GetUserSalt(); err != nil {
+	if user.Rands, err = user_model.GetUserSalt(); err != nil {
 		ctx.ServerError("UpdateUser", err)
 		return
 	}
-	if err := models.UpdateUserCols(db.DefaultContext, user, "is_active", "rands"); err != nil {
-		if models.IsErrUserNotExist(err) {
+	if err := user_model.UpdateUserCols(db.DefaultContext, user, "is_active", "rands"); err != nil {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound("UpdateUserCols", err)
 		} else {
 			ctx.ServerError("UpdateUser", err)
@@ -1512,7 +1512,7 @@ func ActivateEmail(ctx *context.Context) {
 		log.Trace("Email activated: %s", email.Email)
 		ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
 
-		if u, err := models.GetUserByID(email.UID); err != nil {
+		if u, err := user_model.GetUserByID(email.UID); err != nil {
 			log.Warn("GetUserByID: %d", email.UID)
 		} else {
 			// Allow user to validate more emails
@@ -1556,9 +1556,9 @@ func ForgotPasswdPost(ctx *context.Context) {
 	email := ctx.FormString("email")
 	ctx.Data["Email"] = email
 
-	u, err := models.GetUserByEmail(email)
+	u, err := user_model.GetUserByEmail(email)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language())
 			ctx.Data["IsResetSent"] = true
 			ctx.HTML(http.StatusOK, tplForgotPassword)
@@ -1592,7 +1592,7 @@ func ForgotPasswdPost(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplForgotPassword)
 }
 
-func commonResetPassword(ctx *context.Context) (*models.User, *login.TwoFactor) {
+func commonResetPassword(ctx *context.Context) (*user_model.User, *login.TwoFactor) {
 	code := ctx.FormString("code")
 
 	ctx.Data["Title"] = ctx.Tr("auth.reset_password")
@@ -1608,7 +1608,7 @@ func commonResetPassword(ctx *context.Context) (*models.User, *login.TwoFactor)
 	}
 
 	// Fail early, don't frustrate the user
-	u := models.VerifyUserActiveCode(code)
+	u := user_model.VerifyUserActiveCode(code)
 	if u == nil {
 		ctx.Flash.Error(ctx.Tr("auth.invalid_code"))
 		return nil, nil
@@ -1718,7 +1718,7 @@ func ResetPasswdPost(ctx *context.Context) {
 		}
 	}
 	var err error
-	if u.Rands, err = models.GetUserSalt(); err != nil {
+	if u.Rands, err = user_model.GetUserSalt(); err != nil {
 		ctx.ServerError("UpdateUser", err)
 		return
 	}
@@ -1727,7 +1727,7 @@ func ResetPasswdPost(ctx *context.Context) {
 		return
 	}
 	u.MustChangePassword = false
-	if err := models.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "rands", "salt"); err != nil {
+	if err := user_model.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "rands", "salt"); err != nil {
 		ctx.ServerError("UpdateUser", err)
 		return
 	}
@@ -1803,7 +1803,7 @@ func MustChangePasswordPost(ctx *context.Context) {
 
 	u.MustChangePassword = false
 
-	if err := models.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "salt"); err != nil {
+	if err := user_model.UpdateUserCols(db.DefaultContext, u, "must_change_password", "passwd", "passwd_hash_algo", "salt"); err != nil {
 		ctx.ServerError("UpdateUser", err)
 		return
 	}
diff --git a/routers/web/user/auth_openid.go b/routers/web/user/auth_openid.go
index 4724a7b431f..68e166d12c7 100644
--- a/routers/web/user/auth_openid.go
+++ b/routers/web/user/auth_openid.go
@@ -9,7 +9,6 @@ import (
 	"net/http"
 	"net/url"
 
-	"code.gitea.io/gitea/models"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/auth/openid"
 	"code.gitea.io/gitea/modules/base"
@@ -163,9 +162,9 @@ func signInOpenIDVerify(ctx *context.Context) {
 	/* Now we should seek for the user and log him in, or prompt
 	 * to register if not found */
 
-	u, err := models.GetUserByOpenID(id)
+	u, err := user_model.GetUserByOpenID(id)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			ctx.RenderWithErr(err.Error(), tplSignInOpenID, &forms.SignInOpenIDForm{
 				Openid: id,
 			})
@@ -203,9 +202,9 @@ func signInOpenIDVerify(ctx *context.Context) {
 	log.Trace("User has email=" + email + " and nickname=" + nickname)
 
 	if email != "" {
-		u, err = models.GetUserByEmail(email)
+		u, err = user_model.GetUserByEmail(email)
 		if err != nil {
-			if !models.IsErrUserNotExist(err) {
+			if !user_model.IsErrUserNotExist(err) {
 				ctx.RenderWithErr(err.Error(), tplSignInOpenID, &forms.SignInOpenIDForm{
 					Openid: id,
 				})
@@ -219,9 +218,9 @@ func signInOpenIDVerify(ctx *context.Context) {
 	}
 
 	if u == nil && nickname != "" {
-		u, _ = models.GetUserByName(nickname)
+		u, _ = user_model.GetUserByName(nickname)
 		if err != nil {
-			if !models.IsErrUserNotExist(err) {
+			if !user_model.IsErrUserNotExist(err) {
 				ctx.RenderWithErr(err.Error(), tplSignInOpenID, &forms.SignInOpenIDForm{
 					Openid: id,
 				})
@@ -294,7 +293,7 @@ func ConnectOpenIDPost(ctx *context.Context) {
 
 	u, _, err := auth.UserSignIn(form.UserName, form.Password)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form)
 		} else {
 			ctx.ServerError("ConnectOpenIDPost", err)
@@ -419,7 +418,7 @@ func RegisterOpenIDPost(ctx *context.Context) {
 		return
 	}
 
-	u := &models.User{
+	u := &user_model.User{
 		Name:     form.UserName,
 		Email:    form.Email,
 		Passwd:   password,
diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go
index f39bcc36d34..c8bca9dc2c1 100644
--- a/routers/web/user/avatar.go
+++ b/routers/web/user/avatar.go
@@ -8,8 +8,8 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/avatars"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/httpcache"
 )
@@ -27,15 +27,15 @@ func AvatarByUserName(ctx *context.Context) {
 	userName := ctx.Params(":username")
 	size := int(ctx.ParamsInt64(":size"))
 
-	var user *models.User
+	var user *user_model.User
 	if strings.ToLower(userName) != "ghost" {
 		var err error
-		if user, err = models.GetUserByName(userName); err != nil {
+		if user, err = user_model.GetUserByName(userName); err != nil {
 			ctx.ServerError("Invalid user: "+userName, err)
 			return
 		}
 	} else {
-		user = models.NewGhostUser()
+		user = user_model.NewGhostUser()
 	}
 
 	cacheableRedirect(ctx, user.AvatarLinkWithSize(size))
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 9efbe755d90..ca2705a42fd 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/context"
 	issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
@@ -43,7 +44,7 @@ const (
 )
 
 // getDashboardContextUser finds out which context user dashboard is being viewed as .
-func getDashboardContextUser(ctx *context.Context) *models.User {
+func getDashboardContextUser(ctx *context.Context) *user_model.User {
 	ctxUser := ctx.User
 	orgName := ctx.Params(":org")
 	if len(orgName) > 0 {
@@ -727,7 +728,7 @@ func getRepoIDs(reposQuery string) []int64 {
 	return repoIDs
 }
 
-func getActiveUserRepoIDs(ctxUser *models.User, team *models.Team, unitType unit.Type) ([]int64, error) {
+func getActiveUserRepoIDs(ctxUser *user_model.User, team *models.Team, unitType unit.Type) ([]int64, error) {
 	var userRepoIDs []int64
 	var err error
 
@@ -737,7 +738,7 @@ func getActiveUserRepoIDs(ctxUser *models.User, team *models.Team, unitType unit
 			return nil, fmt.Errorf("orgRepoIds: %v", err)
 		}
 	} else {
-		userRepoIDs, err = ctxUser.GetActiveAccessRepoIDs(unitType)
+		userRepoIDs, err = models.GetActiveAccessRepoIDs(ctxUser, unitType)
 		if err != nil {
 			return nil, fmt.Errorf("ctxUser.GetAccessRepoIDs: %v", err)
 		}
@@ -752,7 +753,7 @@ func getActiveUserRepoIDs(ctxUser *models.User, team *models.Team, unitType unit
 
 // getActiveTeamOrOrgRepoIds gets RepoIDs for ctxUser as Organization.
 // Should be called if and only if ctxUser.IsOrganization == true.
-func getActiveTeamOrOrgRepoIds(ctxUser *models.User, team *models.Team, unitType unit.Type) ([]int64, error) {
+func getActiveTeamOrOrgRepoIds(ctxUser *user_model.User, team *models.Team, unitType unit.Type) ([]int64, error) {
 	var orgRepoIDs []int64
 	var err error
 	var env models.AccessibleReposEnvironment
@@ -777,7 +778,7 @@ func getActiveTeamOrOrgRepoIds(ctxUser *models.User, team *models.Team, unitType
 	return orgRepoIDs, nil
 }
 
-func issueIDsFromSearch(ctxUser *models.User, keyword string, opts *models.IssuesOptions) ([]int64, error) {
+func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.IssuesOptions) ([]int64, error) {
 	if len(keyword) == 0 {
 		return []int64{}, nil
 	}
@@ -794,7 +795,7 @@ func issueIDsFromSearch(ctxUser *models.User, keyword string, opts *models.Issue
 	return issueIDsFromSearch, nil
 }
 
-func repoIDMap(ctxUser *models.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*models.Repository, error) {
+func repoIDMap(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*models.Repository, error) {
 	repoByID := make(map[int64]*models.Repository, len(issueCountByRepo))
 	for id := range issueCountByRepo {
 		if id <= 0 {
@@ -880,9 +881,9 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
 
 // Email2User show user page via email
 func Email2User(ctx *context.Context) {
-	u, err := models.GetUserByEmail(ctx.FormString("email"))
+	u, err := user_model.GetUserByEmail(ctx.FormString("email"))
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			ctx.NotFound("GetUserByEmail", err)
 		} else {
 			ctx.ServerError("GetUserByEmail", err)
diff --git a/routers/web/user/oauth.go b/routers/web/user/oauth.go
index 7b1854c5210..d3baeaedc40 100644
--- a/routers/web/user/oauth.go
+++ b/routers/web/user/oauth.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/json"
@@ -170,9 +171,9 @@ func newAccessTokenResponse(grant *login.OAuth2Grant, serverKey, clientKey oauth
 				ErrorDescription: "cannot find application",
 			}
 		}
-		user, err := models.GetUserByID(grant.UserID)
+		user, err := user_model.GetUserByID(grant.UserID)
 		if err != nil {
-			if models.IsErrUserNotExist(err) {
+			if user_model.IsErrUserNotExist(err) {
 				return nil, &AccessTokenError{
 					ErrorCode:        AccessTokenErrorCodeInvalidRequest,
 					ErrorDescription: "cannot find user",
@@ -274,7 +275,7 @@ func InfoOAuth(ctx *context.Context) {
 
 // returns a list of "org" and "org:team" strings,
 // that the given user is a part of.
-func getOAuthGroupsForUser(user *models.User) ([]string, error) {
+func getOAuthGroupsForUser(user *user_model.User) ([]string, error) {
 	orgs, err := models.GetUserOrgsList(user)
 	if err != nil {
 		return nil, fmt.Errorf("GetUserOrgList: %v", err)
@@ -359,7 +360,7 @@ func AuthorizeOAuth(ctx *context.Context) {
 		return
 	}
 
-	user, err := models.GetUserByID(app.UID)
+	user, err := user_model.GetUserByID(app.UID)
 	if err != nil {
 		ctx.ServerError("GetUserByID", err)
 		return
diff --git a/routers/web/user/oauth_test.go b/routers/web/user/oauth_test.go
index dfdaa9a1ed0..e26a42b2fcf 100644
--- a/routers/web/user/oauth_test.go
+++ b/routers/web/user/oauth_test.go
@@ -7,9 +7,9 @@ package user
 import (
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/services/auth/source/oauth2"
 
 	"github.com/golang-jwt/jwt"
@@ -58,7 +58,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) {
 	assert.Empty(t, oidcToken.Email)
 	assert.False(t, oidcToken.EmailVerified)
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
 	grants, err = login.GetOAuth2GrantsByUserID(user.ID)
 	assert.NoError(t, err)
 	assert.Len(t, grants, 1)
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 2594ba3013b..32d2bdde838 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -24,10 +24,10 @@ import (
 )
 
 // GetUserByName get user by name
-func GetUserByName(ctx *context.Context, name string) *models.User {
-	user, err := models.GetUserByName(name)
+func GetUserByName(ctx *context.Context, name string) *user_model.User {
+	user, err := user_model.GetUserByName(name)
 	if err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			if redirectUserID, err := user_model.LookupUserRedirect(name); err == nil {
 				context.RedirectToUser(ctx, name, redirectUserID)
 			} else {
@@ -42,7 +42,7 @@ func GetUserByName(ctx *context.Context, name string) *models.User {
 }
 
 // GetUserByParams returns user whose name is presented in URL paramenter.
-func GetUserByParams(ctx *context.Context) *models.User {
+func GetUserByParams(ctx *context.Context) *user_model.User {
 	return GetUserByName(ctx, ctx.Params(":username"))
 }
 
@@ -199,41 +199,41 @@ func Profile(ctx *context.Context) {
 		repos   []*models.Repository
 		count   int64
 		total   int
-		orderBy models.SearchOrderBy
+		orderBy db.SearchOrderBy
 	)
 
 	ctx.Data["SortType"] = ctx.FormString("sort")
 	switch ctx.FormString("sort") {
 	case "newest":
-		orderBy = models.SearchOrderByNewest
+		orderBy = db.SearchOrderByNewest
 	case "oldest":
-		orderBy = models.SearchOrderByOldest
+		orderBy = db.SearchOrderByOldest
 	case "recentupdate":
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	case "leastupdate":
-		orderBy = models.SearchOrderByLeastUpdated
+		orderBy = db.SearchOrderByLeastUpdated
 	case "reversealphabetically":
-		orderBy = models.SearchOrderByAlphabeticallyReverse
+		orderBy = db.SearchOrderByAlphabeticallyReverse
 	case "alphabetically":
-		orderBy = models.SearchOrderByAlphabetically
+		orderBy = db.SearchOrderByAlphabetically
 	case "moststars":
-		orderBy = models.SearchOrderByStarsReverse
+		orderBy = db.SearchOrderByStarsReverse
 	case "feweststars":
-		orderBy = models.SearchOrderByStars
+		orderBy = db.SearchOrderByStars
 	case "mostforks":
-		orderBy = models.SearchOrderByForksReverse
+		orderBy = db.SearchOrderByForksReverse
 	case "fewestforks":
-		orderBy = models.SearchOrderByForks
+		orderBy = db.SearchOrderByForks
 	default:
 		ctx.Data["SortType"] = "recentupdate"
-		orderBy = models.SearchOrderByRecentUpdated
+		orderBy = db.SearchOrderByRecentUpdated
 	}
 
 	keyword := ctx.FormTrim("q")
 	ctx.Data["Keyword"] = keyword
 	switch tab {
 	case "followers":
-		items, err := models.GetUserFollowers(ctxUser, db.ListOptions{
+		items, err := user_model.GetUserFollowers(ctxUser, db.ListOptions{
 			PageSize: setting.UI.User.RepoPagingNum,
 			Page:     page,
 		})
@@ -245,7 +245,7 @@ func Profile(ctx *context.Context) {
 
 		total = ctxUser.NumFollowers
 	case "following":
-		items, err := models.GetUserFollowing(ctxUser, db.ListOptions{
+		items, err := user_model.GetUserFollowing(ctxUser, db.ListOptions{
 			PageSize: setting.UI.User.RepoPagingNum,
 			Page:     page,
 		})
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index 603786697aa..3b51fdb7775 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -75,7 +75,7 @@ func AccountPost(ctx *context.Context) {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
-		if err := models.UpdateUserCols(db.DefaultContext, ctx.User, "salt", "passwd_hash_algo", "passwd"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, ctx.User, "salt", "passwd_hash_algo", "passwd"); err != nil {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
@@ -152,14 +152,14 @@ func EmailPost(ctx *context.Context) {
 	// Set Email Notification Preference
 	if ctx.FormString("_method") == "NOTIFICATION" {
 		preference := ctx.FormString("preference")
-		if !(preference == models.EmailNotificationsEnabled ||
-			preference == models.EmailNotificationsOnMention ||
-			preference == models.EmailNotificationsDisabled) {
+		if !(preference == user_model.EmailNotificationsEnabled ||
+			preference == user_model.EmailNotificationsOnMention ||
+			preference == user_model.EmailNotificationsDisabled) {
 			log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.User.Name)
 			ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
 			return
 		}
-		if err := models.SetEmailNotifications(ctx.User, preference); err != nil {
+		if err := user_model.SetEmailNotifications(ctx.User, preference); err != nil {
 			log.Error("Set Email Notifications failed: %v", err)
 			ctx.ServerError("SetEmailNotifications", err)
 			return
@@ -233,7 +233,7 @@ func DeleteAccount(ctx *context.Context) {
 	ctx.Data["PageIsSettingsAccount"] = true
 
 	if _, _, err := auth.UserSignIn(ctx.User.Name, ctx.FormString("password")); err != nil {
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			loadAccountData(ctx)
 
 			ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil)
diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go
index 948cc1e9366..56ab6ec9994 100644
--- a/routers/web/user/setting/adopt.go
+++ b/routers/web/user/setting/adopt.go
@@ -8,6 +8,7 @@ import (
 	"path/filepath"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
@@ -27,7 +28,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
 	action := ctx.FormString("action")
 
 	ctxUser := ctx.User
-	root := models.UserPath(ctxUser.LowerName)
+	root := user_model.UserPath(ctxUser.LowerName)
 
 	// check not a repo
 	has, err := models.IsRepositoryExist(ctxUser, dir)
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index 1e515f2fd31..0475398a9a8 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -49,7 +49,7 @@ func Profile(ctx *context.Context) {
 }
 
 // HandleUsernameChange handle username changes from user settings and admin interface
-func HandleUsernameChange(ctx *context.Context, user *models.User, newName string) error {
+func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName string) error {
 	// Non-local users are not allowed to change their username.
 	if !user.IsLocal() {
 		ctx.Flash.Error(ctx.Tr("form.username_change_not_local_user"))
@@ -58,17 +58,17 @@ func HandleUsernameChange(ctx *context.Context, user *models.User, newName strin
 
 	// Check if user name has been changed
 	if user.LowerName != strings.ToLower(newName) {
-		if err := models.ChangeUserName(user, newName); err != nil {
+		if err := user_model.ChangeUserName(user, newName); err != nil {
 			switch {
-			case models.IsErrUserAlreadyExist(err):
+			case user_model.IsErrUserAlreadyExist(err):
 				ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
 			case user_model.IsErrEmailAlreadyUsed(err):
 				ctx.Flash.Error(ctx.Tr("form.email_been_used"))
-			case models.IsErrNameReserved(err):
+			case db.IsErrNameReserved(err):
 				ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
-			case models.IsErrNamePatternNotAllowed(err):
+			case db.IsErrNamePatternNotAllowed(err):
 				ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
-			case models.IsErrNameCharsNotAllowed(err):
+			case db.IsErrNameCharsNotAllowed(err):
 				ctx.Flash.Error(ctx.Tr("user.form.name_chars_not_allowed", newName))
 			default:
 				ctx.ServerError("ChangeUserName", err)
@@ -121,7 +121,7 @@ func ProfilePost(ctx *context.Context) {
 	ctx.User.Description = form.Description
 	ctx.User.KeepActivityPrivate = form.KeepActivityPrivate
 	ctx.User.Visibility = form.Visibility
-	if err := models.UpdateUserSetting(ctx.User); err != nil {
+	if err := user_model.UpdateUserSetting(ctx.User); err != nil {
 		if _, ok := err.(user_model.ErrEmailAlreadyUsed); ok {
 			ctx.Flash.Error(ctx.Tr("form.email_been_used"))
 			ctx.Redirect(setting.AppSubURL + "/user/settings")
@@ -141,7 +141,7 @@ func ProfilePost(ctx *context.Context) {
 
 // UpdateAvatarSetting update user's avatar
 // FIXME: limit size.
-func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *models.User) error {
+func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *user_model.User) error {
 	ctxUser.UseCustomAvatar = form.Source == forms.AvatarLocal
 	if len(form.Gravatar) > 0 {
 		if form.Avatar != nil {
@@ -178,12 +178,12 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *
 	} else if ctxUser.UseCustomAvatar && ctxUser.Avatar == "" {
 		// No avatar is uploaded but setting has been changed to enable,
 		// generate a random one when needed.
-		if err := models.GenerateRandomAvatar(ctxUser); err != nil {
+		if err := user_model.GenerateRandomAvatar(ctxUser); err != nil {
 			log.Error("GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
 		}
 	}
 
-	if err := models.UpdateUserCols(db.DefaultContext, ctxUser, "avatar", "avatar_email", "use_custom_avatar"); err != nil {
+	if err := user_model.UpdateUserCols(db.DefaultContext, ctxUser, "avatar", "avatar_email", "use_custom_avatar"); err != nil {
 		return fmt.Errorf("UpdateUser: %v", err)
 	}
 
@@ -273,7 +273,7 @@ func Repos(ctx *context.Context) {
 		repoNames := make([]string, 0, setting.UI.Admin.UserPagingNum)
 		repos := map[string]*models.Repository{}
 		// We're going to iterate by pagesize.
-		root := models.UserPath(ctxUser.Name)
+		root := user_model.UserPath(ctxUser.Name)
 		if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
 			if err != nil {
 				if os.IsNotExist(err) {
@@ -377,7 +377,7 @@ func UpdateUIThemePost(ctx *context.Context) {
 		return
 	}
 
-	if err := models.UpdateUserTheme(ctx.User, form.Theme); err != nil {
+	if err := user_model.UpdateUserTheme(ctx.User, form.Theme); err != nil {
 		ctx.Flash.Error(ctx.Tr("settings.theme_update_error"))
 		ctx.Redirect(setting.AppSubURL + "/user/settings/appearance")
 		return
@@ -403,7 +403,7 @@ func UpdateUserLang(ctx *context.Context) {
 		ctx.User.Language = form.Language
 	}
 
-	if err := models.UpdateUserSetting(ctx.User); err != nil {
+	if err := user_model.UpdateUserSetting(ctx.User); err != nil {
 		ctx.ServerError("UpdateUserSetting", err)
 		return
 	}
diff --git a/services/agit/agit.go b/services/agit/agit.go
index f32ad371d55..beb21940b0c 100644
--- a/services/agit/agit.go
+++ b/services/agit/agit.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -124,7 +125,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat
 				description = opts.GitPushOptions["description"]
 			}
 
-			pusher, err := models.GetUserByID(opts.UserID)
+			pusher, err := user_model.GetUserByID(opts.UserID)
 			if err != nil {
 				log.Error("Failed to get user. Error: %v", err)
 				ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
@@ -232,7 +233,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat
 		}
 
 		pull_service.AddToTaskQueue(pr)
-		pusher, err := models.GetUserByID(opts.UserID)
+		pusher, err := user_model.GetUserByID(opts.UserID)
 		if err != nil {
 			log.Error("Failed to get user. Error: %v", err)
 			ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
@@ -268,7 +269,7 @@ func ProcRecive(ctx *context.PrivateContext, opts *private.HookOptions) []privat
 }
 
 // UserNameChanged hanle user name change for agit flow pull
-func UserNameChanged(user *models.User, newName string) error {
+func UserNameChanged(user *user_model.User, newName string) error {
 	pulls, err := models.GetAllUnmergedAgitPullRequestByPoster(user.ID)
 	if err != nil {
 		return err
diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go
index a992fbf151d..9ebd0d29ab4 100644
--- a/services/attachment/attachment_test.go
+++ b/services/attachment/attachment_test.go
@@ -9,9 +9,9 @@ import (
 	"path/filepath"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -23,7 +23,7 @@ func TestMain(m *testing.M) {
 func TestUploadAttachment(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	fPath := "./attachment_test.go"
 	f, err := os.Open(fPath)
diff --git a/services/auth/auth.go b/services/auth/auth.go
index 3e48e150479..e53691221f5 100644
--- a/services/auth/auth.go
+++ b/services/auth/auth.go
@@ -12,8 +12,8 @@ import (
 	"regexp"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -105,7 +105,7 @@ func isGitRawReleaseOrLFSPath(req *http.Request) bool {
 }
 
 // handleSignIn clears existing session variables and stores new ones for the specified user object
-func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *models.User) {
+func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) {
 	_ = sess.Delete("openid_verified_uri")
 	_ = sess.Delete("openid_signin_remember")
 	_ = sess.Delete("openid_determined_email")
@@ -128,7 +128,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
 	if len(user.Language) == 0 {
 		lc := middleware.Locale(resp, req)
 		user.Language = lc.Language()
-		if err := models.UpdateUserCols(db.DefaultContext, user, "language"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, user, "language"); err != nil {
 			log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", user.ID, user.Language))
 			return
 		}
diff --git a/services/auth/basic.go b/services/auth/basic.go
index 9cfbd0f644c..e2448eeca07 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	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/setting"
@@ -41,7 +42,7 @@ func (b *Basic) Name() string {
 // "Authorization" header of the request and returns the corresponding user object for that
 // name/token on successful validation.
 // Returns nil if header is empty or validation fails.
-func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	// Basic authentication should only fire on API, Download or on Git or LFSPaths
 	if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
 		return nil
@@ -75,7 +76,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 	if uid != 0 {
 		log.Trace("Basic Authorization: Valid OAuthAccessToken for user[%d]", uid)
 
-		u, err := models.GetUserByID(uid)
+		u, err := user_model.GetUserByID(uid)
 		if err != nil {
 			log.Error("GetUserByID:  %v", err)
 			return nil
@@ -88,7 +89,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 	token, err := models.GetAccessTokenBySHA(authToken)
 	if err == nil {
 		log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid)
-		u, err := models.GetUserByID(token.UID)
+		u, err := user_model.GetUserByID(token.UID)
 		if err != nil {
 			log.Error("GetUserByID:  %v", err)
 			return nil
@@ -112,7 +113,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
 	log.Trace("Basic Authorization: Attempting SignIn for %s", uname)
 	u, source, err := UserSignIn(uname, passwd)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			log.Error("UserSignIn: %v", err)
 		}
 		return nil
diff --git a/services/auth/group.go b/services/auth/group.go
index c396ae046be..bf047338bbd 100644
--- a/services/auth/group.go
+++ b/services/auth/group.go
@@ -7,8 +7,8 @@ package auth
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 // Ensure the struct implements the interface.
@@ -60,7 +60,7 @@ func (b *Group) Free() error {
 }
 
 // Verify extracts and validates
-func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	if !db.HasEngine {
 		return nil
 	}
diff --git a/services/auth/interface.go b/services/auth/interface.go
index a198fbe5b84..a05ece2078d 100644
--- a/services/auth/interface.go
+++ b/services/auth/interface.go
@@ -8,7 +8,7 @@ import (
 	"context"
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/session"
 	"code.gitea.io/gitea/modules/web/middleware"
 )
@@ -26,7 +26,7 @@ type Method interface {
 	// or a new user object (with id = 0) populated with the information that was found
 	// in the authentication data (username or email).
 	// Returns nil if verification fails.
-	Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User
+	Verify(http *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User
 }
 
 // Initializable represents a structure that requires initialization
@@ -51,7 +51,7 @@ type Freeable interface {
 
 // PasswordAuthenticator represents a source of authentication
 type PasswordAuthenticator interface {
-	Authenticate(user *models.User, login, password string) (*models.User, error)
+	Authenticate(user *user_model.User, login, password string) (*user_model.User, error)
 }
 
 // LocalTwoFASkipper represents a source of authentication that can skip local 2fa
diff --git a/services/auth/login_source.go b/services/auth/login_source.go
index 723dd2b1a5d..edce14cd8b3 100644
--- a/services/auth/login_source.go
+++ b/services/auth/login_source.go
@@ -8,11 +8,12 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 // DeleteLoginSource deletes a LoginSource record in DB.
 func DeleteLoginSource(source *login.Source) error {
-	count, err := db.GetEngine(db.DefaultContext).Count(&models.User{LoginSource: source.ID})
+	count, err := db.GetEngine(db.DefaultContext).Count(&user_model.User{LoginSource: source.ID})
 	if err != nil {
 		return err
 	} else if count > 0 {
diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go
index 9b342f3458f..74dc5eaaf68 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -110,7 +111,7 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
 // or the "Authorization" header and returns the corresponding user object for that ID.
 // If verification is successful returns an existing user object.
 // Returns nil if verification fails.
-func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	if !db.HasEngine {
 		return nil
 	}
@@ -125,9 +126,9 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor
 	}
 	log.Trace("OAuth2 Authorization: Found token for user[%d]", id)
 
-	user, err := models.GetUserByID(id)
+	user, err := user_model.GetUserByID(id)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			log.Error("GetUserByName: %v", err)
 		}
 		return nil
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index 7cd24c302d2..3e44d8b8639 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -9,7 +9,7 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	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/web/middleware"
@@ -56,16 +56,16 @@ func (r *ReverseProxy) Name() string {
 // If a username is available in the "setting.ReverseProxyAuthUser" header an existing
 // user object is returned (populated with username or email found in header).
 // Returns nil if header is empty.
-func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	username := r.getUserName(req)
 	if len(username) == 0 {
 		return nil
 	}
 	log.Trace("ReverseProxy Authorization: Found username: %s", username)
 
-	user, err := models.GetUserByName(username)
+	user, err := user_model.GetUserByName(username)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) || !r.isAutoRegisterAllowed() {
+		if !user_model.IsErrUserNotExist(err) || !r.isAutoRegisterAllowed() {
 			log.Error("GetUserByName: %v", err)
 			return nil
 		}
@@ -91,7 +91,7 @@ func (r *ReverseProxy) isAutoRegisterAllowed() bool {
 
 // newUser creates a new user object for the purpose of automatic registration
 // and populates its name and email with the information present in request headers.
-func (r *ReverseProxy) newUser(req *http.Request) *models.User {
+func (r *ReverseProxy) newUser(req *http.Request) *user_model.User {
 	username := r.getUserName(req)
 	if len(username) == 0 {
 		return nil
@@ -105,12 +105,12 @@ func (r *ReverseProxy) newUser(req *http.Request) *models.User {
 		}
 	}
 
-	user := &models.User{
+	user := &user_model.User{
 		Name:     username,
 		Email:    email,
 		IsActive: true,
 	}
-	if err := models.CreateUser(user); err != nil {
+	if err := user_model.CreateUser(user); err != nil {
 		// FIXME: should I create a system notice?
 		log.Error("CreateUser: %v", err)
 		return nil
diff --git a/services/auth/session.go b/services/auth/session.go
index 9a6e2d95d0d..256598d1000 100644
--- a/services/auth/session.go
+++ b/services/auth/session.go
@@ -7,7 +7,7 @@ package auth
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 )
 
@@ -30,7 +30,7 @@ func (s *Session) Name() string {
 // Verify checks if there is a user uid stored in the session and returns the user
 // object for that uid.
 // Returns nil if there is no user uid stored in the session.
-func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	user := SessionUser(sess)
 	if user != nil {
 		return user
@@ -39,7 +39,7 @@ func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataSto
 }
 
 // SessionUser returns the user object corresponding to the "uid" session variable.
-func SessionUser(sess SessionStore) *models.User {
+func SessionUser(sess SessionStore) *user_model.User {
 	// Get user ID
 	uid := sess.Get("uid")
 	if uid == nil {
@@ -53,9 +53,9 @@ func SessionUser(sess SessionStore) *models.User {
 	}
 
 	// Get user object
-	user, err := models.GetUserByID(id)
+	user, err := user_model.GetUserByID(id)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			log.Error("GetUserById: %v", err)
 		}
 		return nil
diff --git a/services/auth/signin.go b/services/auth/signin.go
index 504214f9f16..5f75000d983 100644
--- a/services/auth/signin.go
+++ b/services/auth/signin.go
@@ -7,25 +7,24 @@ package auth
 import (
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 
-	_ "code.gitea.io/gitea/services/auth/source/db" // register the sources (and below)
-	_ "code.gitea.io/gitea/services/auth/source/ldap"
-	_ "code.gitea.io/gitea/services/auth/source/oauth2"
-	_ "code.gitea.io/gitea/services/auth/source/pam"
-	_ "code.gitea.io/gitea/services/auth/source/smtp"
-	_ "code.gitea.io/gitea/services/auth/source/sspi"
+	_ "code.gitea.io/gitea/services/auth/source/db"   // register the sources (and below)
+	_ "code.gitea.io/gitea/services/auth/source/ldap" // register the ldap source
+	"code.gitea.io/gitea/services/auth/source/oauth2"
+	_ "code.gitea.io/gitea/services/auth/source/pam" // register the pam source
+	"code.gitea.io/gitea/services/auth/source/smtp"
+	_ "code.gitea.io/gitea/services/auth/source/sspi" // register the sspi source
 )
 
 // UserSignIn validates user name and password.
-func UserSignIn(username, password string) (*models.User, *login.Source, error) {
-	var user *models.User
+func UserSignIn(username, password string) (*user_model.User, *login.Source, error) {
+	var user *user_model.User
 	if strings.Contains(username, "@") {
-		user = &models.User{Email: strings.ToLower(strings.TrimSpace(username))}
+		user = &user_model.User{Email: strings.ToLower(strings.TrimSpace(username))}
 		// check same email
 		cnt, err := db.Count(user)
 		if err != nil {
@@ -39,13 +38,13 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error)
 	} else {
 		trimmedUsername := strings.TrimSpace(username)
 		if len(trimmedUsername) == 0 {
-			return nil, nil, models.ErrUserNotExist{Name: username}
+			return nil, nil, user_model.ErrUserNotExist{Name: username}
 		}
 
-		user = &models.User{LowerName: strings.ToLower(trimmedUsername)}
+		user = &user_model.User{LowerName: strings.ToLower(trimmedUsername)}
 	}
 
-	hasUser, err := models.GetUser(user)
+	hasUser, err := user_model.GetUser(user)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -57,12 +56,12 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error)
 		}
 
 		if !source.IsActive {
-			return nil, nil, models.ErrLoginSourceNotActived
+			return nil, nil, oauth2.ErrLoginSourceNotActived
 		}
 
 		authenticator, ok := source.Cfg.(PasswordAuthenticator)
 		if !ok {
-			return nil, nil, models.ErrUnsupportedLoginType
+			return nil, nil, smtp.ErrUnsupportedLoginType
 		}
 
 		user, err := authenticator.Authenticate(user, username, password)
@@ -73,7 +72,7 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error)
 		// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
 		// user could be hint to resend confirm email.
 		if user.ProhibitLogin {
-			return nil, nil, models.ErrUserProhibitLogin{UID: user.ID, Name: user.Name}
+			return nil, nil, user_model.ErrUserProhibitLogin{UID: user.ID, Name: user.Name}
 		}
 
 		return user, source, nil
@@ -101,15 +100,15 @@ func UserSignIn(username, password string) (*models.User, *login.Source, error)
 			if !authUser.ProhibitLogin {
 				return authUser, source, nil
 			}
-			err = models.ErrUserProhibitLogin{UID: authUser.ID, Name: authUser.Name}
+			err = user_model.ErrUserProhibitLogin{UID: authUser.ID, Name: authUser.Name}
 		}
 
-		if models.IsErrUserNotExist(err) {
+		if user_model.IsErrUserNotExist(err) {
 			log.Debug("Failed to login '%s' via '%s': %v", username, source.Name, err)
 		} else {
 			log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
 		}
 	}
 
-	return nil, nil, models.ErrUserNotExist{Name: username}
+	return nil, nil, user_model.ErrUserNotExist{Name: username}
 }
diff --git a/services/auth/source/db/authenticate.go b/services/auth/source/db/authenticate.go
index af7b719a634..e0e439c2fe8 100644
--- a/services/auth/source/db/authenticate.go
+++ b/services/auth/source/db/authenticate.go
@@ -5,19 +5,19 @@
 package db
 
 import (
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 )
 
 // Authenticate authenticates the provided user against the DB
-func Authenticate(user *models.User, login, password string) (*models.User, error) {
+func Authenticate(user *user_model.User, login, password string) (*user_model.User, error) {
 	if user == nil {
-		return nil, models.ErrUserNotExist{Name: login}
+		return nil, user_model.ErrUserNotExist{Name: login}
 	}
 
 	if !user.IsPasswordSet() || !user.ValidatePassword(password) {
-		return nil, models.ErrUserNotExist{UID: user.ID, Name: user.Name}
+		return nil, user_model.ErrUserNotExist{UID: user.ID, Name: user.Name}
 	}
 
 	// Update password hash if server password hash algorithm have changed
@@ -25,7 +25,7 @@ func Authenticate(user *models.User, login, password string) (*models.User, erro
 		if err := user.SetPassword(password); err != nil {
 			return nil, err
 		}
-		if err := models.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
+		if err := user_model.UpdateUserCols(db.DefaultContext, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
 			return nil, err
 		}
 	}
@@ -33,7 +33,7 @@ func Authenticate(user *models.User, login, password string) (*models.User, erro
 	// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
 	// user could be hint to resend confirm email.
 	if user.ProhibitLogin {
-		return nil, models.ErrUserProhibitLogin{
+		return nil, user_model.ErrUserProhibitLogin{
 			UID:  user.ID,
 			Name: user.Name,
 		}
diff --git a/services/auth/source/db/source.go b/services/auth/source/db/source.go
index 2fedff3a7ea..5ae2107a3b7 100644
--- a/services/auth/source/db/source.go
+++ b/services/auth/source/db/source.go
@@ -5,8 +5,8 @@
 package db
 
 import (
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 )
 
 // Source is a password authentication service
@@ -24,7 +24,7 @@ func (source *Source) ToDB() ([]byte, error) {
 
 // Authenticate queries if login/password is valid against the PAM,
 // and create a local user if success when enabled.
-func (source *Source) Authenticate(user *models.User, login, password string) (*models.User, error) {
+func (source *Source) Authenticate(user *user_model.User, login, password string) (*user_model.User, error) {
 	return Authenticate(user, login, password)
 }
 
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 99a99801a4b..9bb07d244fa 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -11,27 +11,28 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/services/mailer"
 	user_service "code.gitea.io/gitea/services/user"
 )
 
 // Authenticate queries if login/password is valid against the LDAP directory pool,
 // and create a local user if success when enabled.
-func (source *Source) Authenticate(user *models.User, userName, password string) (*models.User, error) {
+func (source *Source) Authenticate(user *user_model.User, userName, password string) (*user_model.User, error) {
 	sr := source.SearchEntry(userName, password, source.loginSource.Type == login.DLDAP)
 	if sr == nil {
 		// User not in LDAP, do nothing
-		return nil, models.ErrUserNotExist{Name: userName}
+		return nil, user_model.ErrUserNotExist{Name: userName}
 	}
 
 	isAttributeSSHPublicKeySet := len(strings.TrimSpace(source.AttributeSSHPublicKey)) > 0
 
 	// Update User admin flag if exist
-	if isExist, err := models.IsUserExist(0, sr.Username); err != nil {
+	if isExist, err := user_model.IsUserExist(0, sr.Username); err != nil {
 		return nil, err
 	} else if isExist {
 		if user == nil {
-			user, err = models.GetUserByName(sr.Username)
+			user, err = user_model.GetUserByName(sr.Username)
 			if err != nil {
 				return nil, err
 			}
@@ -49,7 +50,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 				cols = append(cols, "is_restricted")
 			}
 			if len(cols) > 0 {
-				err = models.UpdateUserCols(db.DefaultContext, user, cols...)
+				err = user_model.UpdateUserCols(db.DefaultContext, user, cols...)
 				if err != nil {
 					return nil, err
 				}
@@ -74,7 +75,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		sr.Mail = fmt.Sprintf("%s@localhost", sr.Username)
 	}
 
-	user = &models.User{
+	user = &user_model.User{
 		LowerName:    strings.ToLower(sr.Username),
 		Name:         sr.Username,
 		FullName:     composeFullName(sr.Name, sr.Surname, sr.Username),
@@ -87,7 +88,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		IsRestricted: sr.IsRestricted,
 	}
 
-	err := models.CreateUser(user)
+	err := user_model.CreateUser(user)
 	if err != nil {
 		return user, err
 	}
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index 89f84ae20cf..9c504476c0a 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	user_service "code.gitea.io/gitea/services/user"
 )
@@ -25,7 +26,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 	var sshKeysNeedUpdate bool
 
 	// Find all users with this login type - FIXME: Should this be an iterator?
-	users, err := models.GetUsersBySource(source.loginSource)
+	users, err := user_model.GetUsersBySource(source.loginSource)
 	if err != nil {
 		log.Error("SyncExternalUsers: %v", err)
 		return err
@@ -83,7 +84,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 			su.Mail = fmt.Sprintf("%s@localhost", su.Username)
 		}
 
-		var usr *models.User
+		var usr *user_model.User
 		for userPos < len(users) && users[userPos].LowerName < su.LowerName {
 			userPos++
 		}
@@ -97,7 +98,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 		if usr == nil {
 			log.Trace("SyncExternalUsers[%s]: Creating user %s", source.loginSource.Name, su.Username)
 
-			usr = &models.User{
+			usr = &user_model.User{
 				LowerName:    su.LowerName,
 				Name:         su.Username,
 				FullName:     fullName,
@@ -110,7 +111,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 				IsActive:     true,
 			}
 
-			err = models.CreateUser(usr)
+			err = user_model.CreateUser(usr)
 
 			if err != nil {
 				log.Error("SyncExternalUsers[%s]: Error creating user %s: %v", source.loginSource.Name, su.Username, err)
@@ -153,7 +154,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 				}
 				usr.IsActive = true
 
-				err = models.UpdateUserCols(db.DefaultContext, usr, "full_name", "email", "is_admin", "is_restricted", "is_active")
+				err = user_model.UpdateUserCols(db.DefaultContext, usr, "full_name", "email", "is_admin", "is_restricted", "is_active")
 				if err != nil {
 					log.Error("SyncExternalUsers[%s]: Error updating user %s: %v", source.loginSource.Name, usr.Name, err)
 				}
@@ -194,7 +195,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 				log.Trace("SyncExternalUsers[%s]: Deactivating user %s", source.loginSource.Name, usr.Name)
 
 				usr.IsActive = false
-				err = models.UpdateUserCols(db.DefaultContext, usr, "is_active")
+				err = user_model.UpdateUserCols(db.DefaultContext, usr, "is_active")
 				if err != nil {
 					log.Error("SyncExternalUsers[%s]: Error deactivating user %s: %v", source.loginSource.Name, usr.Name, err)
 				}
diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go
index 0fd57a8dbd5..18879e917bb 100644
--- a/services/auth/source/oauth2/providers.go
+++ b/services/auth/source/oauth2/providers.go
@@ -5,10 +5,10 @@
 package oauth2
 
 import (
+	"errors"
 	"net/url"
 	"sort"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -139,6 +139,11 @@ func ClearProviders() {
 	goth.ClearProviders()
 }
 
+var (
+	// ErrLoginSourceNotActived login source is not actived error
+	ErrLoginSourceNotActived = errors.New("Login source is not actived")
+)
+
 // used to create different types of goth providers
 func createProvider(providerName string, source *Source) (goth.Provider, error) {
 	callbackURL := setting.AppURL + "user/oauth2/" + url.PathEscape(providerName) + "/callback"
@@ -148,7 +153,7 @@ func createProvider(providerName string, source *Source) (goth.Provider, error)
 
 	p, ok := gothProviders[source.Provider]
 	if !ok {
-		return nil, models.ErrLoginSourceNotActived
+		return nil, ErrLoginSourceNotActived
 	}
 
 	provider, err = p.CreateGothProvider(providerName, callbackURL, source)
diff --git a/services/auth/source/oauth2/source_authenticate.go b/services/auth/source/oauth2/source_authenticate.go
index be2ff05356c..fdc18411a75 100644
--- a/services/auth/source/oauth2/source_authenticate.go
+++ b/services/auth/source/oauth2/source_authenticate.go
@@ -5,12 +5,12 @@
 package oauth2
 
 import (
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/services/auth/source/db"
 )
 
 // Authenticate falls back to the db authenticator
-func (source *Source) Authenticate(user *models.User, login, password string) (*models.User, error) {
+func (source *Source) Authenticate(user *user_model.User, login, password string) (*user_model.User, error) {
 	return db.Authenticate(user, login, password)
 }
 
diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go
index 6998241ad72..8553653ea06 100644
--- a/services/auth/source/pam/source_authenticate.go
+++ b/services/auth/source/pam/source_authenticate.go
@@ -8,7 +8,6 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/auth/pam"
@@ -20,11 +19,11 @@ import (
 
 // Authenticate queries if login/password is valid against the PAM,
 // and create a local user if success when enabled.
-func (source *Source) Authenticate(user *models.User, userName, password string) (*models.User, error) {
+func (source *Source) Authenticate(user *user_model.User, userName, password string) (*user_model.User, error) {
 	pamLogin, err := pam.Auth(source.ServiceName, userName, password)
 	if err != nil {
 		if strings.Contains(err.Error(), "Authentication failure") {
-			return nil, models.ErrUserNotExist{Name: userName}
+			return nil, user_model.ErrUserNotExist{Name: userName}
 		}
 		return nil, err
 	}
@@ -51,7 +50,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		}
 	}
 
-	user = &models.User{
+	user = &user_model.User{
 		LowerName:   strings.ToLower(username),
 		Name:        username,
 		Email:       email,
@@ -62,7 +61,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		IsActive:    true,
 	}
 
-	if err := models.CreateUser(user); err != nil {
+	if err := user_model.CreateUser(user); err != nil {
 		return user, err
 	}
 
diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go
index d797982da18..c5bd09b0a72 100644
--- a/services/auth/source/smtp/auth.go
+++ b/services/auth/source/smtp/auth.go
@@ -6,13 +6,12 @@ package smtp
 
 import (
 	"crypto/tls"
+	"errors"
 	"fmt"
 	"net"
 	"net/smtp"
 	"os"
 	"strconv"
-
-	"code.gitea.io/gitea/models"
 )
 
 //   _________   __________________________
@@ -52,6 +51,11 @@ const (
 // Authenticators contains available SMTP authentication type names.
 var Authenticators = []string{PlainAuthentication, LoginAuthentication, CRAMMD5Authentication}
 
+var (
+	// ErrUnsupportedLoginType login source is unknown error
+	ErrUnsupportedLoginType = errors.New("Login source is unknown")
+)
+
 // Authenticate performs an SMTP authentication.
 func Authenticate(a smtp.Auth, source *Source) error {
 	tlsConfig := &tls.Config{
@@ -101,5 +105,5 @@ func Authenticate(a smtp.Auth, source *Source) error {
 		return client.Auth(a)
 	}
 
-	return models.ErrUnsupportedLoginType
+	return ErrUnsupportedLoginType
 }
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index f51c884c3a4..c32d638b54f 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -10,22 +10,22 @@ import (
 	"net/textproto"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/mailer"
 )
 
 // Authenticate queries if the provided login/password is authenticates against the SMTP server
 // Users will be autoregistered as required
-func (source *Source) Authenticate(user *models.User, userName, password string) (*models.User, error) {
+func (source *Source) Authenticate(user *user_model.User, userName, password string) (*user_model.User, error) {
 	// Verify allowed domains.
 	if len(source.AllowedDomains) > 0 {
 		idx := strings.Index(userName, "@")
 		if idx == -1 {
-			return nil, models.ErrUserNotExist{Name: userName}
+			return nil, user_model.ErrUserNotExist{Name: userName}
 		} else if !util.IsStringInSlice(userName[idx+1:], strings.Split(source.AllowedDomains, ","), true) {
-			return nil, models.ErrUserNotExist{Name: userName}
+			return nil, user_model.ErrUserNotExist{Name: userName}
 		}
 	}
 
@@ -47,11 +47,11 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		tperr, ok := err.(*textproto.Error)
 		if (ok && tperr.Code == 535) ||
 			strings.Contains(err.Error(), "Username and Password not accepted") {
-			return nil, models.ErrUserNotExist{Name: userName}
+			return nil, user_model.ErrUserNotExist{Name: userName}
 		}
 		if (ok && tperr.Code == 534) ||
 			strings.Contains(err.Error(), "Application-specific password required") {
-			return nil, models.ErrUserNotExist{Name: userName}
+			return nil, user_model.ErrUserNotExist{Name: userName}
 		}
 		return nil, err
 	}
@@ -66,7 +66,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		username = userName[:idx]
 	}
 
-	user = &models.User{
+	user = &user_model.User{
 		LowerName:   strings.ToLower(username),
 		Name:        strings.ToLower(username),
 		Email:       userName,
@@ -77,7 +77,7 @@ func (source *Source) Authenticate(user *models.User, userName, password string)
 		IsActive:    true,
 	}
 
-	if err := models.CreateUser(user); err != nil {
+	if err := user_model.CreateUser(user); err != nil {
 		return user, err
 	}
 
diff --git a/services/auth/sspi_windows.go b/services/auth/sspi_windows.go
index 821a3df459c..19f23491224 100644
--- a/services/auth/sspi_windows.go
+++ b/services/auth/sspi_windows.go
@@ -9,9 +9,9 @@ import (
 	"net/http"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/avatars"
 	"code.gitea.io/gitea/models/login"
+	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/setting"
@@ -83,7 +83,7 @@ func (s *SSPI) Free() error {
 // If authentication is successful, returns the corresponding user object.
 // If negotiation should continue or authentication fails, immediately returns a 401 HTTP
 // response code, as required by the SPNEGO protocol.
-func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *models.User {
+func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
 	if !s.shouldAuthenticate(req) {
 		return nil
 	}
@@ -126,9 +126,9 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
 	}
 	log.Info("Authenticated as %s\n", username)
 
-	user, err := models.GetUserByName(username)
+	user, err := user_model.GetUserByName(username)
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			log.Error("GetUserByName: %v", err)
 			return nil
 		}
@@ -184,9 +184,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
 
 // newUser creates a new user object for the purpose of automatic registration
 // and populates its name and email with the information present in request headers.
-func (s *SSPI) newUser(username string, cfg *sspi.Source) (*models.User, error) {
+func (s *SSPI) newUser(username string, cfg *sspi.Source) (*user_model.User, error) {
 	email := gouuid.New().String() + "@localhost.localdomain"
-	user := &models.User{
+	user := &user_model.User{
 		Name:                         username,
 		Email:                        email,
 		KeepEmailPrivate:             true,
@@ -195,9 +195,9 @@ func (s *SSPI) newUser(username string, cfg *sspi.Source) (*models.User, error)
 		Language:                     cfg.DefaultLanguage,
 		UseCustomAvatar:              true,
 		Avatar:                       avatars.DefaultAvatarLink(),
-		EmailNotificationsPreference: models.EmailNotificationsDisabled,
+		EmailNotificationsPreference: user_model.EmailNotificationsDisabled,
 	}
-	if err := models.CreateUser(user); err != nil {
+	if err := user_model.CreateUser(user); err != nil {
 		return nil, err
 	}
 
diff --git a/services/comments/comments.go b/services/comments/comments.go
index 2477f2d2b0e..6e963012753 100644
--- a/services/comments/comments.go
+++ b/services/comments/comments.go
@@ -8,12 +8,13 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/issues"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/timeutil"
 )
 
 // CreateIssueComment creates a plain issue comment.
-func CreateIssueComment(doer *models.User, repo *models.Repository, issue *models.Issue, content string, attachments []string) (*models.Comment, error) {
+func CreateIssueComment(doer *user_model.User, repo *models.Repository, issue *models.Issue, content string, attachments []string) (*models.Comment, error) {
 	comment, err := models.CreateComment(&models.CreateCommentOptions{
 		Type:        models.CommentTypeComment,
 		Doer:        doer,
@@ -37,7 +38,7 @@ func CreateIssueComment(doer *models.User, repo *models.Repository, issue *model
 }
 
 // UpdateComment updates information of comment.
-func UpdateComment(c *models.Comment, doer *models.User, oldContent string) error {
+func UpdateComment(c *models.Comment, doer *user_model.User, oldContent string) error {
 	var needsContentHistory = c.Content != oldContent &&
 		(c.Type == models.CommentTypeComment || c.Type == models.CommentTypeReview || c.Type == models.CommentTypeCode)
 	if needsContentHistory {
@@ -70,7 +71,7 @@ func UpdateComment(c *models.Comment, doer *models.User, oldContent string) erro
 }
 
 // DeleteComment deletes the comment
-func DeleteComment(doer *models.User, comment *models.Comment) error {
+func DeleteComment(doer *user_model.User, comment *models.Comment) error {
 	if err := models.DeleteComment(comment); err != nil {
 		return err
 	}
diff --git a/services/cron/setting.go b/services/cron/setting.go
index a0393e23dc8..3d9495764d2 100644
--- a/services/cron/setting.go
+++ b/services/cron/setting.go
@@ -7,7 +7,7 @@ package cron
 import (
 	"time"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/unknwon/i18n"
 )
@@ -17,7 +17,7 @@ type Config interface {
 	IsEnabled() bool
 	DoRunAtStart() bool
 	GetSchedule() string
-	FormatMessage(name, status string, doer *models.User, args ...interface{}) string
+	FormatMessage(name, status string, doer *user_model.User, args ...interface{}) string
 	DoNoticeOnSuccess() bool
 }
 
@@ -70,7 +70,7 @@ func (b *BaseConfig) DoNoticeOnSuccess() bool {
 }
 
 // FormatMessage returns a message for the task
-func (b *BaseConfig) FormatMessage(name, status string, doer *models.User, args ...interface{}) string {
+func (b *BaseConfig) FormatMessage(name, status string, doer *user_model.User, args ...interface{}) string {
 	realArgs := make([]interface{}, 0, len(args)+2)
 	realArgs = append(realArgs, i18n.Tr("en-US", "admin.dashboard."+name))
 	if doer == nil {
diff --git a/services/cron/tasks.go b/services/cron/tasks.go
index 732eead9309..75bb4993c02 100644
--- a/services/cron/tasks.go
+++ b/services/cron/tasks.go
@@ -10,9 +10,9 @@ import (
 	"reflect"
 	"sync"
 
-	"code.gitea.io/gitea/models"
 	admin_model "code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/process"
@@ -29,7 +29,7 @@ type Task struct {
 	lock      sync.Mutex
 	Name      string
 	config    Config
-	fun       func(context.Context, *models.User, Config) error
+	fun       func(context.Context, *user_model.User, Config) error
 	ExecTimes int64
 }
 
@@ -55,7 +55,7 @@ func (t *Task) GetConfig() Config {
 
 // Run will run the task incrementing the cron counter with no user defined
 func (t *Task) Run() {
-	t.RunWithUser(&models.User{
+	t.RunWithUser(&user_model.User{
 		ID:        -1,
 		Name:      "(Cron)",
 		LowerName: "(cron)",
@@ -63,7 +63,7 @@ func (t *Task) Run() {
 }
 
 // RunWithUser will run the task incrementing the cron counter at the time with User
-func (t *Task) RunWithUser(doer *models.User, config Config) {
+func (t *Task) RunWithUser(doer *user_model.User, config Config) {
 	if !taskStatusTable.StartIfNotRunning(t.Name) {
 		return
 	}
@@ -118,7 +118,7 @@ func GetTask(name string) *Task {
 }
 
 // RegisterTask allows a task to be registered with the cron service
-func RegisterTask(name string, config Config, fun func(context.Context, *models.User, Config) error) error {
+func RegisterTask(name string, config Config, fun func(context.Context, *user_model.User, Config) error) error {
 	log.Debug("Registering task: %s", name)
 	_, err := setting.GetCronSettings(name, config)
 	if err != nil {
@@ -163,7 +163,7 @@ func RegisterTask(name string, config Config, fun func(context.Context, *models.
 }
 
 // RegisterTaskFatal will register a task but if there is an error log.Fatal
-func RegisterTaskFatal(name string, config Config, fun func(context.Context, *models.User, Config) error) {
+func RegisterTaskFatal(name string, config Config, fun func(context.Context, *user_model.User, Config) error) {
 	if err := RegisterTask(name, config, fun); err != nil {
 		log.Fatal("Unable to register cron task %s Error: %v", name, err)
 	}
diff --git a/services/cron/tasks_basic.go b/services/cron/tasks_basic.go
index 4832ca98a73..814f6eae4e5 100644
--- a/services/cron/tasks_basic.go
+++ b/services/cron/tasks_basic.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/auth"
@@ -33,7 +34,7 @@ func registerUpdateMirrorTask() {
 		},
 		PullLimit: 50,
 		PushLimit: 50,
-	}, func(ctx context.Context, _ *models.User, cfg Config) error {
+	}, func(ctx context.Context, _ *user_model.User, cfg Config) error {
 		umtc := cfg.(*UpdateMirrorTaskConfig)
 		return mirror_service.Update(ctx, umtc.PullLimit, umtc.PushLimit)
 	})
@@ -53,7 +54,7 @@ func registerRepoHealthCheck() {
 		},
 		Timeout: 60 * time.Second,
 		Args:    []string{},
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		rhcConfig := config.(*RepoHealthCheckConfig)
 		return repository_service.GitFsck(ctx, rhcConfig.Timeout, rhcConfig.Args)
 	})
@@ -64,7 +65,7 @@ func registerCheckRepoStats() {
 		Enabled:    true,
 		RunAtStart: true,
 		Schedule:   "@midnight",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return models.CheckRepoStats(ctx)
 	})
 }
@@ -77,7 +78,7 @@ func registerArchiveCleanup() {
 			Schedule:   "@midnight",
 		},
 		OlderThan: 24 * time.Hour,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		acConfig := config.(*OlderThanConfig)
 		return models.DeleteOldRepositoryArchives(ctx, acConfig.OlderThan)
 	})
@@ -91,7 +92,7 @@ func registerSyncExternalUsers() {
 			Schedule:   "@midnight",
 		},
 		UpdateExisting: true,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		realConfig := config.(*UpdateExistingConfig)
 		return auth.SyncExternalUsers(ctx, realConfig.UpdateExisting)
 	})
@@ -105,7 +106,7 @@ func registerDeletedBranchesCleanup() {
 			Schedule:   "@midnight",
 		},
 		OlderThan: 24 * time.Hour,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		realConfig := config.(*OlderThanConfig)
 		models.RemoveOldDeletedBranches(ctx, realConfig.OlderThan)
 		return nil
@@ -117,7 +118,7 @@ func registerUpdateMigrationPosterID() {
 		Enabled:    true,
 		RunAtStart: true,
 		Schedule:   "@midnight",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return migrations.UpdateMigrationPosterID(ctx)
 	})
 }
@@ -132,7 +133,7 @@ func registerCleanupHookTaskTable() {
 		CleanupType:  "OlderThan",
 		OlderThan:    168 * time.Hour,
 		NumberToKeep: 10,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		realConfig := config.(*CleanupHookTaskConfig)
 		return webhook.CleanupHookTaskTable(ctx, webhook.ToHookTaskCleanupType(realConfig.CleanupType), realConfig.OlderThan, realConfig.NumberToKeep)
 	})
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index 95293b1c1d0..26dbe548a7e 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -9,6 +9,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/updatechecker"
 	repo_service "code.gitea.io/gitea/services/repository"
@@ -23,7 +24,7 @@ func registerDeleteInactiveUsers() {
 			Schedule:   "@annually",
 		},
 		OlderThan: 0 * time.Second,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		olderThanConfig := config.(*OlderThanConfig)
 		return user_service.DeleteInactiveUsers(ctx, olderThanConfig.OlderThan)
 	})
@@ -34,7 +35,7 @@ func registerDeleteRepositoryArchives() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@annually",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return repo_service.DeleteRepositoryArchives(ctx)
 	})
 }
@@ -53,7 +54,7 @@ func registerGarbageCollectRepositories() {
 		},
 		Timeout: time.Duration(setting.Git.Timeout.GC) * time.Second,
 		Args:    setting.Git.GCArgs,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		rhcConfig := config.(*RepoHealthCheckConfig)
 		return repo_service.GitGcRepos(ctx, rhcConfig.Timeout, rhcConfig.Args...)
 	})
@@ -64,7 +65,7 @@ func registerRewriteAllPublicKeys() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(_ context.Context, _ *models.User, _ Config) error {
+	}, func(_ context.Context, _ *user_model.User, _ Config) error {
 		return models.RewriteAllPublicKeys()
 	})
 }
@@ -74,7 +75,7 @@ func registerRewriteAllPrincipalKeys() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(_ context.Context, _ *models.User, _ Config) error {
+	}, func(_ context.Context, _ *user_model.User, _ Config) error {
 		return models.RewriteAllPrincipalKeys()
 	})
 }
@@ -84,7 +85,7 @@ func registerRepositoryUpdateHook() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return repo_service.SyncRepositoryHooks(ctx)
 	})
 }
@@ -94,7 +95,7 @@ func registerReinitMissingRepositories() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return repo_service.ReinitMissingRepositories(ctx)
 	})
 }
@@ -104,7 +105,7 @@ func registerDeleteMissingRepositories() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(ctx context.Context, user *models.User, _ Config) error {
+	}, func(ctx context.Context, user *user_model.User, _ Config) error {
 		return repo_service.DeleteMissingRepositories(ctx, user)
 	})
 }
@@ -114,7 +115,7 @@ func registerRemoveRandomAvatars() {
 		Enabled:    false,
 		RunAtStart: false,
 		Schedule:   "@every 72h",
-	}, func(ctx context.Context, _ *models.User, _ Config) error {
+	}, func(ctx context.Context, _ *user_model.User, _ Config) error {
 		return models.RemoveRandomAvatars(ctx)
 	})
 }
@@ -127,7 +128,7 @@ func registerDeleteOldActions() {
 			Schedule:   "@every 168h",
 		},
 		OlderThan: 365 * 24 * time.Hour,
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		olderThanConfig := config.(*OlderThanConfig)
 		return models.DeleteOldActions(olderThanConfig.OlderThan)
 	})
@@ -145,7 +146,7 @@ func registerUpdateGiteaChecker() {
 			Schedule:   "@every 168h",
 		},
 		HTTPEndpoint: "https://dl.gitea.io/gitea/version.json",
-	}, func(ctx context.Context, _ *models.User, config Config) error {
+	}, func(ctx context.Context, _ *user_model.User, config Config) error {
 		updateCheckerConfig := config.(*UpdateCheckerConfig)
 		return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint)
 	})
diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go
index e43b3ca7c5f..c11499d7651 100644
--- a/services/externalaccount/user.go
+++ b/services/externalaccount/user.go
@@ -9,13 +9,14 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/login"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/markbates/goth"
 )
 
 // LinkAccountToUser link the gothUser to the user
-func LinkAccountToUser(user *models.User, gothUser goth.User) error {
+func LinkAccountToUser(user *user_model.User, gothUser goth.User) error {
 	loginSource, err := login.GetActiveOAuth2LoginSourceByName(gothUser.Provider)
 	if err != nil {
 		return err
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index c303de0a015..44c1727719d 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -23,6 +23,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/analyze"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/git"
@@ -666,7 +667,7 @@ type Diff struct {
 }
 
 // LoadComments loads comments into each line
-func (diff *Diff) LoadComments(issue *models.Issue, currentUser *models.User) error {
+func (diff *Diff) LoadComments(issue *models.Issue, currentUser *user_model.User) error {
 	allComments, err := models.FetchCodeComments(issue, currentUser)
 	if err != nil {
 		return err
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index 7d63beffebc..21afdb4cac4 100644
--- a/services/gitdiff/gitdiff_test.go
+++ b/services/gitdiff/gitdiff_test.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/highlight"
 	"code.gitea.io/gitea/modules/json"
@@ -667,7 +668,7 @@ func TestDiff_LoadComments(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue)
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 	diff := setupDefaultDiff()
 	assert.NoError(t, diff.LoadComments(issue, user))
 	assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 2)
diff --git a/services/issue/assignee.go b/services/issue/assignee.go
index 290a0041ff2..62d274a65f8 100644
--- a/services/issue/assignee.go
+++ b/services/issue/assignee.go
@@ -7,12 +7,13 @@ package issue
 import (
 	"code.gitea.io/gitea/models"
 	"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/notification"
 )
 
 // DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
-func DeleteNotPassedAssignee(issue *models.Issue, doer *models.User, assignees []*models.User) (err error) {
+func DeleteNotPassedAssignee(issue *models.Issue, doer *user_model.User, assignees []*user_model.User) (err error) {
 	var found bool
 
 	for _, assignee := range issue.Assignees {
@@ -37,13 +38,13 @@ func DeleteNotPassedAssignee(issue *models.Issue, doer *models.User, assignees [
 }
 
 // ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
-func ToggleAssignee(issue *models.Issue, doer *models.User, assigneeID int64) (removed bool, comment *models.Comment, err error) {
+func ToggleAssignee(issue *models.Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *models.Comment, err error) {
 	removed, comment, err = issue.ToggleAssignee(doer, assigneeID)
 	if err != nil {
 		return
 	}
 
-	assignee, err1 := models.GetUserByID(assigneeID)
+	assignee, err1 := user_model.GetUserByID(assigneeID)
 	if err1 != nil {
 		err = err1
 		return
@@ -55,7 +56,7 @@ func ToggleAssignee(issue *models.Issue, doer *models.User, assigneeID int64) (r
 }
 
 // ReviewRequest add or remove a review request from a user for this PR, and make comment for it.
-func ReviewRequest(issue *models.Issue, doer *models.User, reviewer *models.User, isAdd bool) (comment *models.Comment, err error) {
+func ReviewRequest(issue *models.Issue, doer *user_model.User, reviewer *user_model.User, isAdd bool) (comment *models.Comment, err error) {
 	if isAdd {
 		comment, err = models.AddReviewRequest(issue, reviewer, doer)
 	} else {
@@ -74,7 +75,7 @@ func ReviewRequest(issue *models.Issue, doer *models.User, reviewer *models.User
 }
 
 // IsValidReviewRequest Check permission for ReviewRequest
-func IsValidReviewRequest(reviewer, doer *models.User, isAdd bool, issue *models.Issue, permDoer *models.Permission) error {
+func IsValidReviewRequest(reviewer, doer *user_model.User, isAdd bool, issue *models.Issue, permDoer *models.Permission) error {
 	if reviewer.IsOrganization() {
 		return models.ErrNotValidReviewRequest{
 			Reason: "Organization can't be added as reviewer",
@@ -172,7 +173,7 @@ func IsValidReviewRequest(reviewer, doer *models.User, isAdd bool, issue *models
 }
 
 // IsValidTeamReviewRequest Check permission for ReviewRequest Team
-func IsValidTeamReviewRequest(reviewer *models.Team, doer *models.User, isAdd bool, issue *models.Issue) error {
+func IsValidTeamReviewRequest(reviewer *models.Team, doer *user_model.User, isAdd bool, issue *models.Issue) error {
 	if doer.IsOrganization() {
 		return models.ErrNotValidReviewRequest{
 			Reason: "Organization can't be doer to add reviewer",
@@ -227,7 +228,7 @@ func IsValidTeamReviewRequest(reviewer *models.Team, doer *models.User, isAdd bo
 }
 
 // TeamReviewRequest add or remove a review request from a team for this PR, and make comment for it.
-func TeamReviewRequest(issue *models.Issue, doer *models.User, reviewer *models.Team, isAdd bool) (comment *models.Comment, err error) {
+func TeamReviewRequest(issue *models.Issue, doer *user_model.User, reviewer *models.Team, isAdd bool) (comment *models.Comment, err error) {
 	if isAdd {
 		comment, err = models.AddTeamReviewRequest(issue, reviewer, doer)
 	} else {
diff --git a/services/issue/assignee_test.go b/services/issue/assignee_test.go
index bc2721ebd41..d3d7ad74f88 100644
--- a/services/issue/assignee_test.go
+++ b/services/issue/assignee_test.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -20,7 +21,7 @@ func TestDeleteNotPassedAssignee(t *testing.T) {
 	issue, err := models.GetIssueWithAttrsByID(1)
 	assert.NoError(t, err)
 
-	user1, err := models.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)
 
 	// Check if he got removed
@@ -29,7 +30,7 @@ func TestDeleteNotPassedAssignee(t *testing.T) {
 	assert.True(t, isAssigned)
 
 	// Clean everyone
-	err = DeleteNotPassedAssignee(issue, user1, []*models.User{})
+	err = DeleteNotPassedAssignee(issue, user1, []*user_model.User{})
 	assert.NoError(t, err)
 
 	// Check they're gone
diff --git a/services/issue/commit.go b/services/issue/commit.go
index 401084639d6..3e2f6c471b4 100644
--- a/services/issue/commit.go
+++ b/services/issue/commit.go
@@ -14,6 +14,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/repository"
 )
@@ -72,7 +73,7 @@ func timeLogToAmount(str string) int64 {
 	return a
 }
 
-func issueAddTime(issue *models.Issue, doer *models.User, time time.Time, timeLog string) error {
+func issueAddTime(issue *models.Issue, doer *user_model.User, time time.Time, timeLog string) error {
 	amount := timeLogToAmount(timeLog)
 	if amount == 0 {
 		return nil
@@ -96,7 +97,7 @@ func getIssueFromRef(repo *models.Repository, index int64) (*models.Issue, error
 }
 
 // UpdateIssuesCommit checks if issues are manipulated by commit message.
-func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*repository.PushCommit, branchName string) error {
+func UpdateIssuesCommit(doer *user_model.User, repo *models.Repository, commits []*repository.PushCommit, branchName string) error {
 	// Commits are appended in the reverse order.
 	for i := len(commits) - 1; i >= 0; i-- {
 		c := commits[i]
diff --git a/services/issue/commit_test.go b/services/issue/commit_test.go
index 3f8c5f3b42e..1addbd080f8 100644
--- a/services/issue/commit_test.go
+++ b/services/issue/commit_test.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/repository"
 	"code.gitea.io/gitea/modules/setting"
 
@@ -44,7 +45,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
 		},
 	}
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repo.Owner = user
 
@@ -130,7 +131,7 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
 		},
 	}
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repo.Owner = user
 
@@ -144,7 +145,7 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
 
 func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	// Test that push to a non-default branch closes an issue.
 	pushCommits := []*repository.PushCommit{
@@ -178,7 +179,7 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
 
 func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	// Test that a push to default branch closes issue in another repo
 	// If the user also has push permissions to that repo
@@ -213,7 +214,7 @@ func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
 
 func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 
 	// Test that a push to default branch closes issue in another repo
 	// If the user also has push permissions to that repo
@@ -248,7 +249,7 @@ func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
 
 func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 10}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}).(*user_model.User)
 
 	// Test that a push with close reference *can not* close issue
 	// If the committer doesn't have push rights in that repo
diff --git a/services/issue/content.go b/services/issue/content.go
index 1081e30b5d8..6d7883090b0 100644
--- a/services/issue/content.go
+++ b/services/issue/content.go
@@ -6,11 +6,12 @@ package issue
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 )
 
 // ChangeContent changes issue content, as the given user.
-func ChangeContent(issue *models.Issue, doer *models.User, content string) (err error) {
+func ChangeContent(issue *models.Issue, doer *user_model.User, content string) (err error) {
 	oldContent := issue.Content
 
 	if err := issue.ChangeContent(doer, content); err != nil {
diff --git a/services/issue/issue.go b/services/issue/issue.go
index e3571bd396f..cb34a9eacdb 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -7,6 +7,7 @@ package issue
 import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/util"
@@ -41,7 +42,7 @@ func NewIssue(repo *models.Repository, issue *models.Issue, labelIDs []int64, uu
 }
 
 // ChangeTitle changes the title of this issue, as the given user.
-func ChangeTitle(issue *models.Issue, doer *models.User, title string) (err error) {
+func ChangeTitle(issue *models.Issue, doer *user_model.User, title string) (err error) {
 	oldTitle := issue.Title
 	issue.Title = title
 
@@ -55,7 +56,7 @@ func ChangeTitle(issue *models.Issue, doer *models.User, title string) (err erro
 }
 
 // ChangeIssueRef changes the branch of this issue, as the given user.
-func ChangeIssueRef(issue *models.Issue, doer *models.User, ref string) error {
+func ChangeIssueRef(issue *models.Issue, doer *user_model.User, ref string) error {
 	oldRef := issue.Ref
 	issue.Ref = ref
 
@@ -74,8 +75,8 @@ func ChangeIssueRef(issue *models.Issue, doer *models.User, ref string) error {
 // "assignees" (array): Logins for Users to assign to this issue.
 // Pass one or more user logins to replace the set of assignees on this Issue.
 // Send an empty array ([]) to clear all assignees from the Issue.
-func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees []string, doer *models.User) (err error) {
-	var allNewAssignees []*models.User
+func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees []string, doer *user_model.User) (err error) {
+	var allNewAssignees []*user_model.User
 
 	// Keep the old assignee thingy for compatibility reasons
 	if oneAssignee != "" {
@@ -95,7 +96,7 @@ func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees
 
 	// Loop through all assignees to add them
 	for _, assigneeName := range multipleAssignees {
-		assignee, err := models.GetUserByName(assigneeName)
+		assignee, err := user_model.GetUserByName(assigneeName)
 		if err != nil {
 			return err
 		}
@@ -125,8 +126,8 @@ func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees
 
 // AddAssigneeIfNotAssigned adds an assignee only if he isn't already assigned to the issue.
 // Also checks for access of assigned user
-func AddAssigneeIfNotAssigned(issue *models.Issue, doer *models.User, assigneeID int64) (err error) {
-	assignee, err := models.GetUserByID(assigneeID)
+func AddAssigneeIfNotAssigned(issue *models.Issue, doer *user_model.User, assigneeID int64) (err error) {
+	assignee, err := user_model.GetUserByID(assigneeID)
 	if err != nil {
 		return err
 	}
diff --git a/services/issue/label.go b/services/issue/label.go
index c8ef9e95366..96df7ed345b 100644
--- a/services/issue/label.go
+++ b/services/issue/label.go
@@ -6,11 +6,12 @@ package issue
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 )
 
 // ClearLabels clears all of an issue's labels
-func ClearLabels(issue *models.Issue, doer *models.User) (err error) {
+func ClearLabels(issue *models.Issue, doer *user_model.User) (err error) {
 	if err = issue.ClearLabels(doer); err != nil {
 		return
 	}
@@ -21,7 +22,7 @@ func ClearLabels(issue *models.Issue, doer *models.User) (err error) {
 }
 
 // AddLabel adds a new label to the issue.
-func AddLabel(issue *models.Issue, doer *models.User, label *models.Label) error {
+func AddLabel(issue *models.Issue, doer *user_model.User, label *models.Label) error {
 	if err := models.NewIssueLabel(issue, label, doer); err != nil {
 		return err
 	}
@@ -31,7 +32,7 @@ func AddLabel(issue *models.Issue, doer *models.User, label *models.Label) error
 }
 
 // AddLabels adds a list of new labels to the issue.
-func AddLabels(issue *models.Issue, doer *models.User, labels []*models.Label) error {
+func AddLabels(issue *models.Issue, doer *user_model.User, labels []*models.Label) error {
 	if err := models.NewIssueLabels(issue, labels, doer); err != nil {
 		return err
 	}
@@ -41,7 +42,7 @@ func AddLabels(issue *models.Issue, doer *models.User, labels []*models.Label) e
 }
 
 // RemoveLabel removes a label from issue by given ID.
-func RemoveLabel(issue *models.Issue, doer *models.User, label *models.Label) error {
+func RemoveLabel(issue *models.Issue, doer *user_model.User, label *models.Label) error {
 	if err := issue.LoadRepo(); err != nil {
 		return err
 	}
@@ -66,7 +67,7 @@ func RemoveLabel(issue *models.Issue, doer *models.User, label *models.Label) er
 }
 
 // ReplaceLabels removes all current labels and add new labels to the issue.
-func ReplaceLabels(issue *models.Issue, doer *models.User, labels []*models.Label) error {
+func ReplaceLabels(issue *models.Issue, doer *user_model.User, labels []*models.Label) error {
 	old, err := models.GetLabelsByIssueID(issue.ID)
 	if err != nil {
 		return err
diff --git a/services/issue/label_test.go b/services/issue/label_test.go
index fdc2c4ffb64..fc94749bdbd 100644
--- a/services/issue/label_test.go
+++ b/services/issue/label_test.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -31,7 +32,7 @@ func TestIssue_AddLabels(t *testing.T) {
 		for i, labelID := range test.labelIDs {
 			labels[i] = unittest.AssertExistsAndLoadBean(t, &models.Label{ID: labelID}).(*models.Label)
 		}
-		doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: test.doerID}).(*models.User)
+		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
 		assert.NoError(t, AddLabels(issue, doer, labels))
 		for _, labelID := range test.labelIDs {
 			unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: test.issueID, LabelID: labelID})
@@ -54,7 +55,7 @@ func TestIssue_AddLabel(t *testing.T) {
 		assert.NoError(t, unittest.PrepareTestDatabase())
 		issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: test.issueID}).(*models.Issue)
 		label := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: test.labelID}).(*models.Label)
-		doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: test.doerID}).(*models.User)
+		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
 		assert.NoError(t, AddLabel(issue, doer, label))
 		unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: test.issueID, LabelID: test.labelID})
 	}
diff --git a/services/issue/milestone.go b/services/issue/milestone.go
index 6fe527f58cd..999da50844f 100644
--- a/services/issue/milestone.go
+++ b/services/issue/milestone.go
@@ -6,11 +6,12 @@ package issue
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 )
 
 // ChangeMilestoneAssign changes assignment of milestone for issue.
-func ChangeMilestoneAssign(issue *models.Issue, doer *models.User, oldMilestoneID int64) (err error) {
+func ChangeMilestoneAssign(issue *models.Issue, doer *user_model.User, oldMilestoneID int64) (err error) {
 	if err = models.ChangeMilestoneAssign(issue, doer, oldMilestoneID); err != nil {
 		return
 	}
diff --git a/services/issue/status.go b/services/issue/status.go
index 0a18169a27a..5b8d21274d6 100644
--- a/services/issue/status.go
+++ b/services/issue/status.go
@@ -7,11 +7,12 @@ package issue
 import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 )
 
 // ChangeStatus changes issue status to open or closed.
-func ChangeStatus(issue *models.Issue, doer *models.User, closed bool) error {
+func ChangeStatus(issue *models.Issue, doer *user_model.User, closed bool) error {
 	comment, err := issue.ChangeStatus(doer, closed)
 	if err != nil {
 		// Don't return an error when dependencies are open as this would let the push fail
diff --git a/services/lfs/locks.go b/services/lfs/locks.go
index ac72fff6af7..11cf5c16b99 100644
--- a/services/lfs/locks.go
+++ b/services/lfs/locks.go
@@ -168,9 +168,9 @@ func PostLockHandler(ctx *context.Context) {
 	}
 
 	lock, err := models.CreateLFSLock(&models.LFSLock{
-		Repo:  repository,
-		Path:  req.Path,
-		Owner: ctx.User,
+		Repo:    repository,
+		Path:    req.Path,
+		OwnerID: ctx.User.ID,
 	})
 	if err != nil {
 		if models.IsErrLFSLockAlreadyExist(err) {
@@ -249,7 +249,7 @@ func VerifyLockHandler(ctx *context.Context) {
 	lockOursListAPI := make([]*api.LFSLock, 0, len(lockList))
 	lockTheirsListAPI := make([]*api.LFSLock, 0, len(lockList))
 	for _, l := range lockList {
-		if l.Owner.ID == ctx.User.ID {
+		if l.OwnerID == ctx.User.ID {
 			lockOursListAPI = append(lockOursListAPI, convert.ToLFSLock(l))
 		} else {
 			lockTheirsListAPI = append(lockTheirsListAPI, convert.ToLFSLock(l))
diff --git a/services/lfs/server.go b/services/lfs/server.go
index 78876588165..28756e4f190 100644
--- a/services/lfs/server.go
+++ b/services/lfs/server.go
@@ -20,6 +20,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/json"
 	lfs_module "code.gitea.io/gitea/modules/lfs"
@@ -506,7 +507,7 @@ func authenticate(ctx *context.Context, repository *models.Repository, authoriza
 	return true
 }
 
-func handleLFSToken(tokenSHA string, target *models.Repository, mode models.AccessMode) (*models.User, error) {
+func handleLFSToken(tokenSHA string, target *models.Repository, mode models.AccessMode) (*user_model.User, error) {
 	if !strings.Contains(tokenSHA, ".") {
 		return nil, nil
 	}
@@ -533,7 +534,7 @@ func handleLFSToken(tokenSHA string, target *models.Repository, mode models.Acce
 		return nil, fmt.Errorf("invalid token claim")
 	}
 
-	u, err := models.GetUserByID(claims.UserID)
+	u, err := user_model.GetUserByID(claims.UserID)
 	if err != nil {
 		log.Error("Unable to GetUserById[%d]: Error: %v", claims.UserID, err)
 		return nil, err
@@ -541,7 +542,7 @@ func handleLFSToken(tokenSHA string, target *models.Repository, mode models.Acce
 	return u, nil
 }
 
-func parseToken(authorization string, target *models.Repository, mode models.AccessMode) (*models.User, error) {
+func parseToken(authorization string, target *models.Repository, mode models.AccessMode) (*user_model.User, error) {
 	if authorization == "" {
 		return nil, fmt.Errorf("no token")
 	}
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index b579bf383a9..a9978be8b19 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -66,7 +66,7 @@ func SendTestMail(email string) error {
 }
 
 // sendUserMail sends a mail to the user
-func sendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) {
+func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) {
 	locale := translation.NewLocale(language)
 	data := map[string]interface{}{
 		"DisplayName":       u.DisplayName(),
@@ -94,7 +94,7 @@ func sendUserMail(language string, u *models.User, tpl base.TplName, code, subje
 }
 
 // SendActivateAccountMail sends an activation mail to the user (new user registration)
-func SendActivateAccountMail(locale translation.Locale, u *models.User) {
+func SendActivateAccountMail(locale translation.Locale, u *user_model.User) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -103,7 +103,7 @@ func SendActivateAccountMail(locale translation.Locale, u *models.User) {
 }
 
 // SendResetPasswordMail sends a password reset mail to the user
-func SendResetPasswordMail(u *models.User) {
+func SendResetPasswordMail(u *user_model.User) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -113,7 +113,7 @@ func SendResetPasswordMail(u *models.User) {
 }
 
 // SendActivateEmailMail sends confirmation email to confirm new email address
-func SendActivateEmailMail(u *models.User, email *user_model.EmailAddress) {
+func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -145,7 +145,7 @@ func SendActivateEmailMail(u *models.User, email *user_model.EmailAddress) {
 }
 
 // SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
-func SendRegisterNotifyMail(u *models.User) {
+func SendRegisterNotifyMail(u *user_model.User) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -176,7 +176,7 @@ func SendRegisterNotifyMail(u *models.User) {
 }
 
 // SendCollaboratorMail sends mail notification to new collaborator.
-func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
+func SendCollaboratorMail(u, doer *user_model.User, repo *models.Repository) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return
@@ -209,7 +209,7 @@ func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
 	SendAsync(msg)
 }
 
-func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipients []*models.User, fromMention bool, info string) ([]*Message, error) {
+func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipients []*user_model.User, fromMention bool, info string) ([]*Message, error) {
 	var (
 		subject string
 		link    string
@@ -337,7 +337,7 @@ func createReference(issue *models.Issue, comment *models.Comment) string {
 	return fmt.Sprintf("%s/%s/%d%s@%s", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain)
 }
 
-func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *models.User) map[string]string {
+func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string {
 	repo := ctx.Issue.Repo
 
 	return map[string]string{
@@ -381,7 +381,7 @@ func sanitizeSubject(subject string) string {
 }
 
 // SendIssueAssignedMail composes and sends issue assigned email
-func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, recipients []*models.User) error {
+func SendIssueAssignedMail(issue *models.Issue, doer *user_model.User, content string, comment *models.Comment, recipients []*user_model.User) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
@@ -392,7 +392,7 @@ func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content strin
 		return err
 	}
 
-	langMap := make(map[string][]*models.User)
+	langMap := make(map[string][]*user_model.User)
 	for _, user := range recipients {
 		langMap[user.Language] = append(langMap[user.Language], user)
 	}
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index eef71557e7a..3662164092c 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -6,12 +6,13 @@ package mailer
 
 import (
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 )
 
 // MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
-func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*models.User) error {
+func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*user_model.User) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
@@ -35,7 +36,7 @@ func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue
 }
 
 // MailMentionsComment sends email to users mentioned in a code comment
-func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*models.User) (err error) {
+func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*user_model.User) (err error) {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 6e631627136..1fbe68e0222 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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"
 )
@@ -19,7 +20,7 @@ func fallbackMailSubject(issue *models.Issue) string {
 
 type mailCommentContext struct {
 	Issue      *models.Issue
-	Doer       *models.User
+	Doer       *user_model.User
 	ActionType models.ActionType
 	Content    string
 	Comment    *models.Comment
@@ -34,7 +35,7 @@ const (
 // This function sends two list of emails:
 // 1. Repository watchers (except for WIP pull requests) and users who are participated in comments.
 // 2. Users who are not in 1. but get mentioned in current issue/comment.
-func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error {
+func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_model.User) error {
 
 	// Required by the mail composer; make sure to load these before calling the async function
 	if err := ctx.Issue.LoadRepo(); err != nil {
@@ -103,7 +104,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.
 		visited[i] = true
 	}
 
-	unfilteredUsers, err := models.GetMaileableUsersByIDs(unfiltered, false)
+	unfilteredUsers, err := user_model.GetMaileableUsersByIDs(unfiltered, false)
 	if err != nil {
 		return err
 	}
@@ -114,18 +115,18 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.
 	return nil
 }
 
-func mailIssueCommentBatch(ctx *mailCommentContext, users []*models.User, visited map[int64]bool, fromMention bool) error {
+func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited map[int64]bool, fromMention bool) error {
 	checkUnit := unit.TypeIssues
 	if ctx.Issue.IsPull {
 		checkUnit = unit.TypePullRequests
 	}
 
-	langMap := make(map[string][]*models.User)
+	langMap := make(map[string][]*user_model.User)
 	for _, user := range users {
 		// At this point we exclude:
 		// user that don't have all mails enabled or users only get mail on mention and this is one ...
-		if !(user.EmailNotificationsPreference == models.EmailNotificationsEnabled ||
-			fromMention && user.EmailNotificationsPreference == models.EmailNotificationsOnMention) {
+		if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled ||
+			fromMention && user.EmailNotificationsPreference == user_model.EmailNotificationsOnMention) {
 			continue
 		}
 
@@ -164,7 +165,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*models.User, visite
 
 // MailParticipants sends new issue thread created emails to repository watchers
 // and mentioned people.
-func MailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) error {
+func MailParticipants(issue *models.Issue, doer *user_model.User, opType models.ActionType, mentions []*user_model.User) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index a6fc28a5ca4..72476fe8be0 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -8,6 +8,7 @@ import (
 	"bytes"
 
 	"code.gitea.io/gitea/models"
+	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/markup"
@@ -34,9 +35,9 @@ func MailNewRelease(rel *models.Release) {
 		return
 	}
 
-	recipients, err := models.GetMaileableUsersByIDs(watcherIDList, false)
+	recipients, err := user_model.GetMaileableUsersByIDs(watcherIDList, false)
 	if err != nil {
-		log.Error("models.GetMaileableUsersByIDs: %v", err)
+		log.Error("user_model.GetMaileableUsersByIDs: %v", err)
 		return
 	}
 
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index ef85f4aa543..8a2eeac0871 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -9,13 +9,14 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/templates"
 	"code.gitea.io/gitea/modules/translation"
 )
 
 // SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
-func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error {
+func SendRepoTransferNotifyMail(doer, newOwner *user_model.User, repo *models.Repository) error {
 	if setting.MailService == nil {
 		// No mail service configured
 		return nil
@@ -45,7 +46,7 @@ func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Reposi
 }
 
 // sendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created for each language
-func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *models.User, emails []string, repo *models.Repository) error {
+func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.User, emails []string, repo *models.Repository) error {
 	var (
 		locale  = translation.NewLocale(lang)
 		content bytes.Buffer
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 94ff5a65ae9..8e8cd982312 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"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"
@@ -40,7 +41,7 @@ const bodyTpl = `
 </html>
 `
 
-func prepareMailerTest(t *testing.T) (doer *models.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) {
+func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *models.Repository, issue *models.Issue, comment *models.Comment) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 	var mailService = setting.Mailer{
 		From: "test@gitea.com",
@@ -49,7 +50,7 @@ func prepareMailerTest(t *testing.T) (doer *models.User, repo *models.Repository
 	setting.MailService = &mailService
 	setting.Domain = "localhost"
 
-	doer = unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo = unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1, Owner: doer}).(*models.Repository)
 	issue = unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1, Repo: repo, Poster: doer}).(*models.Issue)
 	assert.NoError(t, issue.LoadRepo())
@@ -64,7 +65,7 @@ func TestComposeIssueCommentMessage(t *testing.T) {
 	btpl := template.Must(template.New("issue/comment").Parse(bodyTpl))
 	InitMailRender(stpl, btpl)
 
-	recipients := []*models.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
+	recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
 	msgs, err := composeIssueCommentMessages(&mailCommentContext{Issue: issue, Doer: doer, ActionType: models.ActionCommentIssue,
 		Content: "test body", Comment: comment}, "en-US", recipients, false, "issue comment")
 	assert.NoError(t, err)
@@ -91,7 +92,7 @@ func TestComposeIssueMessage(t *testing.T) {
 	btpl := template.Must(template.New("issue/new").Parse(bodyTpl))
 	InitMailRender(stpl, btpl)
 
-	recipients := []*models.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
+	recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
 	msgs, err := composeIssueCommentMessages(&mailCommentContext{Issue: issue, Doer: doer, ActionType: models.ActionCreateIssue,
 		Content: "test body"}, "en-US", recipients, false, "issue create")
 	assert.NoError(t, err)
@@ -113,7 +114,7 @@ func TestComposeIssueMessage(t *testing.T) {
 
 func TestTemplateSelection(t *testing.T) {
 	doer, repo, issue, comment := prepareMailerTest(t)
-	recipients := []*models.User{{Name: "Test", Email: "test@gitea.com"}}
+	recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
 
 	stpl := texttmpl.Must(texttmpl.New("issue/default").Parse("issue/default/subject"))
 	texttmpl.Must(stpl.New("issue/new").Parse("issue/new/subject"))
@@ -159,14 +160,14 @@ func TestTemplateServices(t *testing.T) {
 	doer, _, issue, comment := prepareMailerTest(t)
 	assert.NoError(t, issue.LoadRepo())
 
-	expect := func(t *testing.T, issue *models.Issue, comment *models.Comment, doer *models.User,
+	expect := func(t *testing.T, issue *models.Issue, comment *models.Comment, doer *user_model.User,
 		actionType models.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string) {
 
 		stpl := texttmpl.Must(texttmpl.New("issue/default").Parse(tplSubject))
 		btpl := template.Must(template.New("issue/default").Parse(tplBody))
 		InitMailRender(stpl, btpl)
 
-		recipients := []*models.User{{Name: "Test", Email: "test@gitea.com"}}
+		recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
 		msg := testComposeIssueCommentMessage(t, &mailCommentContext{Issue: issue, Doer: doer, ActionType: actionType,
 			Content: "test body", Comment: comment}, recipients, fromMention, "TestTemplateServices")
 
@@ -198,7 +199,7 @@ func TestTemplateServices(t *testing.T) {
 		"//Re: //")
 }
 
-func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recipients []*models.User, fromMention bool, info string) *Message {
+func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, recipients []*user_model.User, fromMention bool, info string) *Message {
 	msgs, err := composeIssueCommentMessages(ctx, "en-US", recipients, fromMention, info)
 	assert.NoError(t, err)
 	assert.Len(t, msgs, 1)
@@ -209,7 +210,7 @@ func TestGenerateAdditionalHeaders(t *testing.T) {
 	doer, _, issue, _ := prepareMailerTest(t)
 
 	ctx := &mailCommentContext{Issue: issue, Doer: doer}
-	recipient := &models.User{Name: "Test", Email: "test@gitea.com"}
+	recipient := &user_model.User{Name: "Test", Email: "test@gitea.com"}
 
 	headers := generateAdditionalHeaders(ctx, "dummy-reason", recipient)
 
diff --git a/services/migrations/dump.go b/services/migrations/dump.go
index 6e3596230fd..afff49a0597 100644
--- a/services/migrations/dump.go
+++ b/services/migrations/dump.go
@@ -17,7 +17,7 @@ import (
 	"strings"
 	"time"
 
-	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	base "code.gitea.io/gitea/modules/migration"
@@ -607,7 +607,7 @@ func updateOptionsUnits(opts *base.MigrateOptions, units []string) {
 
 // RestoreRepository restore a repository from the disk directory
 func RestoreRepository(ctx context.Context, baseDir string, ownerName, repoName string, units []string) error {
-	doer, err := models.GetAdminUser()
+	doer, err := user_model.GetAdminUser()
 	if err != nil {
 		return err
 	}
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index d28c83a64f5..3bc8992c3a6 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -18,6 +18,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/git"
 	"code.gitea.io/gitea/modules/log"
 	base "code.gitea.io/gitea/modules/migration"
@@ -39,7 +40,7 @@ var (
 // GiteaLocalUploader implements an Uploader to gitea sites
 type GiteaLocalUploader struct {
 	ctx            context.Context
-	doer           *models.User
+	doer           *user_model.User
 	repoOwner      string
 	repoName       string
 	repo           *models.Repository
@@ -54,7 +55,7 @@ type GiteaLocalUploader struct {
 }
 
 // NewGiteaLocalUploader creates an gitea Uploader via gitea API v1
-func NewGiteaLocalUploader(ctx context.Context, doer *models.User, repoOwner, repoName string) *GiteaLocalUploader {
+func NewGiteaLocalUploader(ctx context.Context, doer *user_model.User, repoOwner, repoName string) *GiteaLocalUploader {
 	return &GiteaLocalUploader{
 		ctx:         ctx,
 		doer:        doer,
@@ -87,7 +88,7 @@ func (g *GiteaLocalUploader) MaxBatchInsertSize(tp string) int {
 
 // CreateRepo creates a repository
 func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.MigrateOptions) error {
-	owner, err := models.GetUserByName(g.repoOwner)
+	owner, err := user_model.GetUserByName(g.repoOwner)
 	if err != nil {
 		return err
 	}
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index 9b04004cd73..45876dd9603 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/graceful"
 	base "code.gitea.io/gitea/modules/migration"
 	"code.gitea.io/gitea/modules/structs"
@@ -27,7 +28,7 @@ func TestGiteaUploadRepo(t *testing.T) {
 
 	unittest.PrepareTestEnv(t)
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
 
 	var (
 		downloader = NewGithubDownloaderV3(context.Background(), "https://github.com", "", "", "", "go-xorm", "builder")
diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go
index 3e805f0b71e..02b167b27c4 100644
--- a/services/migrations/migrate.go
+++ b/services/migrations/migrate.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	admin_model "code.gitea.io/gitea/models/admin"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/hostmatcher"
 	"code.gitea.io/gitea/modules/log"
 	base "code.gitea.io/gitea/modules/migration"
@@ -38,7 +39,7 @@ func RegisterDownloaderFactory(factory base.DownloaderFactory) {
 }
 
 // IsMigrateURLAllowed checks if an URL is allowed to be migrated from
-func IsMigrateURLAllowed(remoteURL string, doer *models.User) error {
+func IsMigrateURLAllowed(remoteURL string, doer *user_model.User) error {
 	// Remote address can be HTTP/HTTPS/Git URL or local path.
 	u, err := url.Parse(remoteURL)
 	if err != nil {
@@ -105,7 +106,7 @@ func IsMigrateURLAllowed(remoteURL string, doer *models.User) error {
 }
 
 // MigrateRepository migrate repository according MigrateOptions
-func MigrateRepository(ctx context.Context, doer *models.User, ownerName string, opts base.MigrateOptions, messenger base.Messenger) (*models.Repository, error) {
+func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName string, opts base.MigrateOptions, messenger base.Messenger) (*models.Repository, error) {
 	err := IsMigrateURLAllowed(opts.CloneAddr, doer)
 	if err != nil {
 		return nil, err
diff --git a/services/migrations/migrate_test.go b/services/migrations/migrate_test.go
index e2363242a20..d09c184d91e 100644
--- a/services/migrations/migrate_test.go
+++ b/services/migrations/migrate_test.go
@@ -8,8 +8,8 @@ import (
 	"path/filepath"
 	"testing"
 
-	"code.gitea.io/gitea/models"
 	"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"
@@ -18,8 +18,8 @@ import (
 func TestMigrateWhiteBlocklist(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	adminUser := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user1"}).(*models.User)
-	nonAdminUser := unittest.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
+	adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}).(*user_model.User)
+	nonAdminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}).(*user_model.User)
 
 	setting.Migrations.AllowedDomains = "github.com"
 	setting.Migrations.AllowLocalNetworks = false
diff --git a/services/org/org.go b/services/org/org.go
index c2b21d10ac9..834c6e5abac 100644
--- a/services/org/org.go
+++ b/services/org/org.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/storage"
 	"code.gitea.io/gitea/modules/util"
 )
@@ -40,7 +41,7 @@ func DeleteOrganization(org *models.Organization) error {
 	// FIXME: system notice
 	// Note: There are something just cannot be roll back,
 	//	so just keep error logs of those operations.
-	path := models.UserPath(org.Name)
+	path := user_model.UserPath(org.Name)
 
 	if err := util.RemoveAll(path); err != nil {
 		return fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
diff --git a/services/org/org_test.go b/services/org/org_test.go
index 3c620c055be..aaa2756bb5c 100644
--- a/services/org/org_test.go
+++ b/services/org/org_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -33,5 +34,5 @@ func TestDeleteOrganization(t *testing.T) {
 
 	user := unittest.AssertExistsAndLoadBean(t, &models.Organization{ID: 5}).(*models.Organization)
 	assert.Error(t, DeleteOrganization(user))
-	unittest.CheckConsistencyFor(t, &models.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &models.Team{})
 }
diff --git a/services/pull/check.go b/services/pull/check.go
index 1fb167ac219..03d4a39919f 100644
--- a/services/pull/check.go
+++ b/services/pull/check.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
@@ -159,7 +160,7 @@ func manuallyMerged(pr *models.PullRequest) bool {
 		pr.MergedCommitID = commit.ID.String()
 		pr.MergedUnix = timeutil.TimeStamp(commit.Author.When.Unix())
 		pr.Status = models.PullRequestStatusManuallyMerged
-		merger, _ := models.GetUserByEmail(commit.Author.Email)
+		merger, _ := user_model.GetUserByEmail(commit.Author.Email)
 
 		// When the commit author is unknown set the BaseRepo owner as merger
 		if merger == nil {
diff --git a/services/pull/merge.go b/services/pull/merge.go
index e4ed4e38bd9..f94aa36128d 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -17,6 +17,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unit"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
@@ -30,7 +31,7 @@ import (
 // Merge merges pull request to base repository.
 // Caller should check PR is ready to be merged (review and status checks)
 // FIXME: add repoWorkingPull make sure two merges does not happen at same time.
-func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) {
+func Merge(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) {
 	if err = pr.LoadHeadRepo(); err != nil {
 		log.Error("LoadHeadRepo: %v", err)
 		return fmt.Errorf("LoadHeadRepo: %v", err)
@@ -110,7 +111,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 }
 
 // rawMerge perform the merge operation without changing any pull information in database
-func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.MergeStyle, message string) (string, error) {
+func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle models.MergeStyle, message string) (string, error) {
 	err := git.LoadGitVersion()
 	if err != nil {
 		log.Error("git.LoadGitVersion: %v", err)
@@ -396,10 +397,10 @@ func rawMerge(pr *models.PullRequest, doer *models.User, mergeStyle models.Merge
 		}
 	}
 
-	var headUser *models.User
+	var headUser *user_model.User
 	err = pr.HeadRepo.GetOwner()
 	if err != nil {
-		if !models.IsErrUserNotExist(err) {
+		if !user_model.IsErrUserNotExist(err) {
 			log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err)
 			return "", err
 		}
@@ -541,7 +542,7 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
 }
 
 // IsSignedIfRequired check if merge will be signed if required
-func IsSignedIfRequired(pr *models.PullRequest, doer *models.User) (bool, error) {
+func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, error) {
 	if err := pr.LoadProtectedBranch(); err != nil {
 		return false, err
 	}
@@ -556,7 +557,7 @@ func IsSignedIfRequired(pr *models.PullRequest, doer *models.User) (bool, error)
 }
 
 // IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
-func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *models.User) (bool, error) {
+func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *user_model.User) (bool, error) {
 	if user == nil {
 		return false, nil
 	}
@@ -632,7 +633,7 @@ func CheckPRReadyToMerge(pr *models.PullRequest, skipProtectedFilesCheck bool) (
 }
 
 // MergedManually mark pr as merged manually
-func MergedManually(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, commitID string) (err error) {
+func MergedManually(pr *models.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, commitID string) (err error) {
 	prUnit, err := pr.BaseRepo.GetUnit(unit.TypePullRequests)
 	if err != nil {
 		return
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 5f6198a3931..339fb1e22d1 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -15,6 +15,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
@@ -113,7 +114,7 @@ func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int6
 }
 
 // ChangeTargetBranch changes the target branch of this pull request, as the given user.
-func ChangeTargetBranch(pr *models.PullRequest, doer *models.User, targetBranch string) (err error) {
+func ChangeTargetBranch(pr *models.PullRequest, doer *user_model.User, targetBranch string) (err error) {
 	// Current target branch is already the same
 	if pr.BaseBranch == targetBranch {
 		return nil
@@ -209,7 +210,7 @@ func ChangeTargetBranch(pr *models.PullRequest, doer *models.User, targetBranch
 	return nil
 }
 
-func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *models.User, branch string) error {
+func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *user_model.User, branch string) error {
 	repo, err := models.GetRepositoryByID(repoID)
 	if err != nil {
 		return fmt.Errorf("GetRepositoryByID: %v", err)
@@ -231,7 +232,7 @@ func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *m
 
 // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
 // and generate new patch for testing as needed.
-func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
+func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) {
 	log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
 	graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) {
 		// There is no sensible way to shut this down ":-("
@@ -494,7 +495,7 @@ func (errs errlist) Error() string {
 }
 
 // CloseBranchPulls close all the pull requests who's head branch is the branch
-func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {
+func CloseBranchPulls(doer *user_model.User, repoID int64, branch string) error {
 	prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
 	if err != nil {
 		return err
@@ -523,7 +524,7 @@ func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {
 }
 
 // CloseRepoBranchesPulls close all pull requests which head branches are in the given repository, but only whose base repo is not in the given repository
-func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error {
+func CloseRepoBranchesPulls(doer *user_model.User, repo *models.Repository) error {
 	branches, _, err := git.GetBranchesByPath(repo.RepoPath(), 0, 0)
 	if err != nil {
 		return err
diff --git a/services/pull/review.go b/services/pull/review.go
index 081b17cd83e..31c2e99782c 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
@@ -20,7 +21,7 @@ import (
 )
 
 // CreateCodeComment creates a comment on the code line
-func CreateCodeComment(doer *models.User, gitRepo *git.Repository, issue *models.Issue, line int64, content string, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) {
+func CreateCodeComment(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, line int64, content string, treePath string, isReview bool, replyReviewID int64, latestCommitID string) (*models.Comment, error) {
 
 	var (
 		existsReview bool
@@ -114,7 +115,7 @@ func CreateCodeComment(doer *models.User, gitRepo *git.Repository, issue *models
 var notEnoughLines = regexp.MustCompile(`exit status 128 - fatal: file .* has only \d+ lines?`)
 
 // createCodeComment creates a plain code comment at the specified line / path
-func createCodeComment(doer *models.User, repo *models.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) {
+func createCodeComment(doer *user_model.User, repo *models.Repository, issue *models.Issue, content, treePath string, line, reviewID int64) (*models.Comment, error) {
 	var commitID, patch string
 	if err := issue.LoadPullRequest(); err != nil {
 		return nil, fmt.Errorf("GetPullRequestByIssueID: %v", err)
@@ -216,7 +217,7 @@ func createCodeComment(doer *models.User, repo *models.Repository, issue *models
 }
 
 // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
-func SubmitReview(doer *models.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) {
+func SubmitReview(doer *user_model.User, gitRepo *git.Repository, issue *models.Issue, reviewType models.ReviewType, content, commitID string, attachmentUUIDs []string) (*models.Review, *models.Comment, error) {
 	pr, err := issue.GetPullRequest()
 	if err != nil {
 		return nil, nil, err
@@ -270,7 +271,7 @@ func SubmitReview(doer *models.User, gitRepo *git.Repository, issue *models.Issu
 }
 
 // DismissReview dismissing stale review by repo admin
-func DismissReview(reviewID int64, message string, doer *models.User, isDismiss bool) (comment *models.Comment, err error) {
+func DismissReview(reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *models.Comment, err error) {
 	review, err := models.GetReviewByID(reviewID)
 	if err != nil {
 		return
diff --git a/services/pull/update.go b/services/pull/update.go
index 4b4e67797ef..ef6e84598e5 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -8,12 +8,13 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 )
 
 // Update updates pull request with base branch.
-func Update(pull *models.PullRequest, doer *models.User, message string, rebase bool) error {
+func Update(pull *models.PullRequest, doer *user_model.User, message string, rebase bool) error {
 	var (
 		pr    *models.PullRequest
 		style models.MergeStyle
@@ -67,7 +68,7 @@ func Update(pull *models.PullRequest, doer *models.User, message string, rebase
 }
 
 // IsUserAllowedToUpdate check if user is allowed to update PR with given permissions and branch protections
-func IsUserAllowedToUpdate(pull *models.PullRequest, user *models.User) (mergeAllowed, rebaseAllowed bool, err error) {
+func IsUserAllowedToUpdate(pull *models.PullRequest, user *user_model.User) (mergeAllowed, rebaseAllowed bool, err error) {
 	if pull.Flow == models.PullRequestFlowAGit {
 		return false, false, nil
 	}
diff --git a/services/release/release.go b/services/release/release.go
index 30274f93efa..ec1494d4260 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -12,6 +12,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
@@ -93,7 +94,7 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
 		}
 
 		if rel.PublisherID <= 0 {
-			u, err := models.GetUserByEmail(commit.Author.Email)
+			u, err := user_model.GetUserByEmail(commit.Author.Email)
 			if err == nil {
 				rel.PublisherID = u.ID
 			}
@@ -136,7 +137,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs
 }
 
 // CreateNewTag creates a new repository tag
-func CreateNewTag(doer *models.User, repo *models.Repository, commit, tagName, msg string) error {
+func CreateNewTag(doer *user_model.User, repo *models.Repository, commit, tagName, msg string) error {
 	isExist, err := models.IsReleaseExist(repo.ID, tagName)
 	if err != nil {
 		return err
@@ -179,7 +180,7 @@ func CreateNewTag(doer *models.User, repo *models.Repository, commit, tagName, m
 // addAttachmentUUIDs accept a slice of new created attachments' uuids which will be reassigned release_id as the created release
 // delAttachmentUUIDs accept a slice of attachments' uuids which will be deleted from the release
 // editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments.
-func UpdateRelease(doer *models.User, gitRepo *git.Repository, rel *models.Release,
+func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.Release,
 	addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string) (err error) {
 	if rel.ID == 0 {
 		return errors.New("UpdateRelease only accepts an exist release")
@@ -278,7 +279,7 @@ func UpdateRelease(doer *models.User, gitRepo *git.Repository, rel *models.Relea
 }
 
 // DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
-func DeleteReleaseByID(id int64, doer *models.User, delTag bool) error {
+func DeleteReleaseByID(id int64, doer *user_model.User, delTag bool) error {
 	rel, err := models.GetReleaseByID(id)
 	if err != nil {
 		return fmt.Errorf("GetReleaseByID: %v", err)
diff --git a/services/release/release_test.go b/services/release/release_test.go
index 92eb128f71d..29ff78ca3a7 100644
--- a/services/release/release_test.go
+++ b/services/release/release_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/services/attachment"
 
@@ -26,7 +27,7 @@ func TestMain(m *testing.M) {
 func TestRelease_Create(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repoPath := models.RepoPath(user.Name, repo.Name)
 
@@ -130,7 +131,7 @@ func TestRelease_Create(t *testing.T) {
 func TestRelease_Update(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repoPath := models.RepoPath(user.Name, repo.Name)
 
@@ -272,7 +273,7 @@ func TestRelease_Update(t *testing.T) {
 func TestRelease_createTag(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repoPath := models.RepoPath(user.Name, repo.Name)
 
@@ -354,7 +355,7 @@ func TestRelease_createTag(t *testing.T) {
 
 func TestCreateNewTag(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 
 	assert.NoError(t, CreateNewTag(user, repo, "master", "v2.0",
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index d48411fbb42..3f4045a7783 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
@@ -24,7 +25,7 @@ import (
 )
 
 // AdoptRepository adopts pre-existing repository files for the user/organization.
-func AdoptRepository(doer, u *models.User, opts models.CreateRepoOptions) (*models.Repository, error) {
+func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*models.Repository, error) {
 	if !doer.IsAdmin && !u.CanCreateRepo() {
 		return nil, models.ErrReachLimitOfRepo{
 			Limit: u.MaxRepoCreation,
@@ -98,7 +99,7 @@ func AdoptRepository(doer, u *models.User, opts models.CreateRepoOptions) (*mode
 	return repo, nil
 }
 
-func adoptRepository(ctx context.Context, repoPath string, u *models.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
+func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *models.Repository, opts models.CreateRepoOptions) (err error) {
 	isExist, err := util.IsExist(repoPath)
 	if err != nil {
 		log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
@@ -185,7 +186,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *models.User, repo
 }
 
 // DeleteUnadoptedRepository deletes unadopted repository files from the filesystem
-func DeleteUnadoptedRepository(doer, u *models.User, repoName string) error {
+func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error {
 	if err := models.IsUsableRepoName(repoName); err != nil {
 		return err
 	}
@@ -240,7 +241,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
 	repoNamesToCheck := make([]string, 0, opts.PageSize)
 
 	repoNames := make([]string, 0, opts.PageSize)
-	var ctxUser *models.User
+	var ctxUser *user_model.User
 
 	count := 0
 
@@ -293,9 +294,9 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
 				return filepath.SkipDir
 			}
 
-			ctxUser, err = models.GetUserByName(info.Name())
+			ctxUser, err = user_model.GetUserByName(info.Name())
 			if err != nil {
-				if models.IsErrUserNotExist(err) {
+				if user_model.IsErrUserNotExist(err) {
 					log.Debug("Missing user: %s", info.Name())
 					return filepath.SkipDir
 				}
diff --git a/services/repository/branch.go b/services/repository/branch.go
index f94a9afcb67..92e662f3d6d 100644
--- a/services/repository/branch.go
+++ b/services/repository/branch.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	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/notification"
@@ -17,7 +18,7 @@ import (
 )
 
 // CreateNewBranch creates a new repository branch
-func CreateNewBranch(doer *models.User, repo *models.Repository, oldBranchName, branchName string) (err error) {
+func CreateNewBranch(doer *user_model.User, repo *models.Repository, oldBranchName, branchName string) (err error) {
 	// Check if branch name can be used
 	if err := checkBranchName(repo, branchName); err != nil {
 		return err
@@ -99,7 +100,7 @@ func checkBranchName(repo *models.Repository, name string) error {
 }
 
 // CreateNewBranchFromCommit creates a new repository branch
-func CreateNewBranchFromCommit(doer *models.User, repo *models.Repository, commit, branchName string) (err error) {
+func CreateNewBranchFromCommit(doer *user_model.User, repo *models.Repository, commit, branchName string) (err error) {
 	// Check if branch name can be used
 	if err := checkBranchName(repo, branchName); err != nil {
 		return err
@@ -120,7 +121,7 @@ func CreateNewBranchFromCommit(doer *models.User, repo *models.Repository, commi
 }
 
 // RenameBranch rename a branch
-func RenameBranch(repo *models.Repository, doer *models.User, gitRepo *git.Repository, from, to string) (string, error) {
+func RenameBranch(repo *models.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) {
 	if from == to {
 		return "target_exist", nil
 	}
@@ -164,7 +165,7 @@ var (
 )
 
 // DeleteBranch delete branch
-func DeleteBranch(doer *models.User, repo *models.Repository, gitRepo *git.Repository, branchName string) error {
+func DeleteBranch(doer *user_model.User, repo *models.Repository, gitRepo *git.Repository, branchName string) error {
 	if branchName == repo.DefaultBranch {
 		return ErrBranchIsDefault
 	}
diff --git a/services/repository/check.go b/services/repository/check.go
index 36bd4e2e0de..ab7539e5555 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/models"
 	admin_model "code.gitea.io/gitea/models/admin"
 	"code.gitea.io/gitea/models/db"
+	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/util"
@@ -149,7 +150,7 @@ func gatherMissingRepoRecords(ctx context.Context) ([]*models.Repository, error)
 }
 
 // DeleteMissingRepositories deletes all repository records that lost Git files.
-func DeleteMissingRepositories(ctx context.Context, doer *models.User) error {
+func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error {
 	repos, err := gatherMissingRepoRecords(ctx)
 	if err != nil {
 		return err
diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go
index ebae0971123..8f14ed89263 100644
--- a/services/repository/files/commit.go
+++ b/services/repository/files/commit.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/structs"
 )
@@ -15,7 +16,7 @@ import (
 // CreateCommitStatus creates a new CommitStatus given a bunch of parameters
 // NOTE: All text-values will be trimmed from whitespaces.
 // Requires: Repo, Creator, SHA
-func CreateCommitStatus(repo *models.Repository, creator *models.User, sha string, status *models.CommitStatus) error {
+func CreateCommitStatus(repo *models.Repository, creator *user_model.User, sha string, status *models.CommitStatus) error {
 	repoPath := repo.RepoPath()
 
 	// confirm that commit is exist
diff --git a/services/repository/files/delete.go b/services/repository/files/delete.go
index f8d7f62c142..f7b342db3f7 100644
--- a/services/repository/files/delete.go
+++ b/services/repository/files/delete.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 	repo_service "code.gitea.io/gitea/services/repository"
@@ -29,7 +30,7 @@ type DeleteRepoFileOptions struct {
 }
 
 // DeleteRepoFile deletes a file in the given repository
-func DeleteRepoFile(repo *models.Repository, doer *models.User, opts *DeleteRepoFileOptions) (*api.FileResponse, error) {
+func DeleteRepoFile(repo *models.Repository, doer *user_model.User, opts *DeleteRepoFileOptions) (*api.FileResponse, error) {
 	// If no branch name is set, assume the repo's default branch
 	if opts.OldBranch == "" {
 		opts.OldBranch = repo.DefaultBranch
diff --git a/services/repository/files/file.go b/services/repository/files/file.go
index ad445bb4ae2..8de60c4cb85 100644
--- a/services/repository/files/file.go
+++ b/services/repository/files/file.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	api "code.gitea.io/gitea/modules/structs"
 )
@@ -81,7 +82,7 @@ func GetFileCommitResponse(repo *models.Repository, commit *git.Commit) (*api.Fi
 }
 
 // GetAuthorAndCommitterUsers Gets the author and committer user objects from the IdentityOptions
-func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models.User) (authorUser, committerUser *models.User) {
+func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *user_model.User) (authorUser, committerUser *user_model.User) {
 	// Committer and author are optional. If they are not the doer (not same email address)
 	// then we use bogus User objects for them to store their FullName and Email.
 	// If only one of the two are provided, we set both of them to it.
@@ -93,7 +94,7 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models
 				committerUser.FullName = committer.Name
 			}
 		} else {
-			committerUser = &models.User{
+			committerUser = &user_model.User{
 				FullName: committer.Name,
 				Email:    committer.Email,
 			}
@@ -106,7 +107,7 @@ func GetAuthorAndCommitterUsers(author, committer *IdentityOptions, doer *models
 				authorUser.FullName = author.Name
 			}
 		} else {
-			authorUser = &models.User{
+			authorUser = &user_model.User{
 				FullName: author.Name,
 				Email:    author.Email,
 			}
diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go
index 2a6d6c3ab62..0b6bea63795 100644
--- a/services/repository/files/temp_repo.go
+++ b/services/repository/files/temp_repo.go
@@ -15,6 +15,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	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"
@@ -185,12 +186,12 @@ func (t *TemporaryUploadRepository) GetLastCommitByRef(ref string) (string, erro
 }
 
 // CommitTree creates a commit from a given tree for the user with provided message
-func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, treeHash string, message string, signoff bool) (string, error) {
+func (t *TemporaryUploadRepository) CommitTree(author, committer *user_model.User, treeHash string, message string, signoff bool) (string, error) {
 	return t.CommitTreeWithDate(author, committer, treeHash, message, signoff, time.Now(), time.Now())
 }
 
 // CommitTreeWithDate creates a commit from a given tree for the user with provided message
-func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models.User, treeHash string, message string, signoff bool, authorDate, committerDate time.Time) (string, error) {
+func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *user_model.User, treeHash string, message string, signoff bool, authorDate, committerDate time.Time) (string, error) {
 	authorSig := author.NewGitSig()
 	committerSig := committer.NewGitSig()
 
@@ -260,7 +261,7 @@ func (t *TemporaryUploadRepository) CommitTreeWithDate(author, committer *models
 }
 
 // Push the provided commitHash to the repository branch by the provided user
-func (t *TemporaryUploadRepository) Push(doer *models.User, commitHash string, branch string) error {
+func (t *TemporaryUploadRepository) Push(doer *user_model.User, commitHash string, branch string) error {
 	// Because calls hooks we need to pass in the environment
 	env := models.PushingEnvironment(doer, t.repo)
 	if err := git.Push(t.basePath, git.PushOptions{
diff --git a/services/repository/files/update.go b/services/repository/files/update.go
index 5d6c3da6707..eeb98f36302 100644
--- a/services/repository/files/update.go
+++ b/services/repository/files/update.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/lfs"
@@ -122,7 +123,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string
 }
 
 // CreateOrUpdateRepoFile adds or updates a file in the given repository
-func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) {
+func CreateOrUpdateRepoFile(repo *models.Repository, doer *user_model.User, opts *UpdateRepoFileOptions) (*structs.FileResponse, error) {
 	// If no branch name is set, assume default branch
 	if opts.OldBranch == "" {
 		opts.OldBranch = repo.DefaultBranch
@@ -439,7 +440,7 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 }
 
 // VerifyBranchProtection verify the branch protection for modifying the given treePath on the given branch
-func VerifyBranchProtection(repo *models.Repository, doer *models.User, branchName string, treePath string) error {
+func VerifyBranchProtection(repo *models.Repository, doer *user_model.User, branchName string, treePath string) error {
 	protectedBranch, err := repo.GetBranchProtection(branchName)
 	if err != nil {
 		return err
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index 98d01506d53..7a0ea31085e 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -11,6 +11,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/lfs"
 	"code.gitea.io/gitea/modules/setting"
@@ -47,7 +48,7 @@ func cleanUpAfterFailure(infos *[]uploadInfo, t *TemporaryUploadRepository, orig
 }
 
 // UploadRepoFiles uploads files to the given repository
-func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRepoFileOptions) error {
+func UploadRepoFiles(repo *models.Repository, doer *user_model.User, opts *UploadRepoFileOptions) error {
 	if len(opts.Files) == 0 {
 		return nil
 	}
@@ -67,7 +68,11 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 			return err
 		}
 		if lfsLock != nil && lfsLock.OwnerID != doer.ID {
-			return models.ErrLFSFileLocked{RepoID: repo.ID, Path: filepath, UserName: lfsLock.Owner.Name}
+			u, err := user_model.GetUserByID(lfsLock.OwnerID)
+			if err != nil {
+				return err
+			}
+			return models.ErrLFSFileLocked{RepoID: repo.ID, Path: filepath, UserName: u.Name}
 		}
 
 		names[i] = upload.Name
diff --git a/services/repository/fork.go b/services/repository/fork.go
index f052d18a759..3fc08b4986f 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
@@ -21,7 +22,7 @@ import (
 )
 
 // ForkRepository forks a repository
-func ForkRepository(doer, owner *models.User, opts models.ForkRepoOptions) (_ *models.Repository, err error) {
+func ForkRepository(doer, owner *user_model.User, opts models.ForkRepoOptions) (_ *models.Repository, err error) {
 	forkedRepo, err := opts.BaseRepo.GetUserFork(owner.ID)
 	if err != nil {
 		return nil, err
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 1280a3d84da..befb6061356 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -17,7 +18,7 @@ func TestForkRepository(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	// user 13 has already forked repo10
-	user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User)
+	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 13}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)
 
 	fork, err := ForkRepository(user, user, models.ForkRepoOptions{
diff --git a/services/repository/generate.go b/services/repository/generate.go
index fe38723dea3..031648666d9 100644
--- a/services/repository/generate.go
+++ b/services/repository/generate.go
@@ -9,13 +9,14 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
 	repo_module "code.gitea.io/gitea/modules/repository"
 )
 
 // GenerateRepository generates a repository from a template
-func GenerateRepository(doer, owner *models.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
+func GenerateRepository(doer, owner *user_model.User, templateRepo *models.Repository, opts models.GenerateRepoOptions) (_ *models.Repository, err error) {
 	if !doer.IsAdmin && !owner.CanCreateRepo() {
 		return nil, models.ErrReachLimitOfRepo{
 			Limit: owner.MaxRepoCreation,
diff --git a/services/repository/push.go b/services/repository/push.go
index 97554c64900..7b439a41f48 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/cache"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/graceful"
@@ -93,7 +94,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 
 	addTags := make([]string, 0, len(optsList))
 	delTags := make([]string, 0, len(optsList))
-	var pusher *models.User
+	var pusher *user_model.User
 
 	for _, opts := range optsList {
 		if opts.IsNewRef() && opts.IsDelRef() {
@@ -102,7 +103,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 		if opts.IsTag() { // If is tag reference
 			if pusher == nil || pusher.ID != opts.PusherID {
 				var err error
-				if pusher, err = models.GetUserByID(opts.PusherID); err != nil {
+				if pusher, err = user_model.GetUserByID(opts.PusherID); err != nil {
 					return err
 				}
 			}
@@ -133,7 +134,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
 		} else if opts.IsBranch() { // If is branch reference
 			if pusher == nil || pusher.ID != opts.PusherID {
 				var err error
-				if pusher, err = models.GetUserByID(opts.PusherID); err != nil {
+				if pusher, err = user_model.GetUserByID(opts.PusherID); err != nil {
 					return err
 				}
 			}
@@ -276,7 +277,7 @@ func pushUpdateAddTags(ctx context.Context, repo *models.Repository, gitRepo *gi
 
 	newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap))
 
-	emailToUser := make(map[string]*models.User)
+	emailToUser := make(map[string]*user_model.User)
 
 	for i, lowerTag := range lowerTags {
 		tag, err := gitRepo.GetTag(tags[i])
@@ -295,15 +296,15 @@ func pushUpdateAddTags(ctx context.Context, repo *models.Repository, gitRepo *gi
 		if sig == nil {
 			sig = commit.Committer
 		}
-		var author *models.User
+		var author *user_model.User
 		var createdAt = time.Unix(1, 0)
 
 		if sig != nil {
 			var ok bool
 			author, ok = emailToUser[sig.Email]
 			if !ok {
-				author, err = models.GetUserByEmailContext(ctx, sig.Email)
-				if err != nil && !models.IsErrUserNotExist(err) {
+				author, err = user_model.GetUserByEmailContext(ctx, sig.Email)
+				if err != nil && !user_model.IsErrUserNotExist(err) {
 					return fmt.Errorf("GetUserByEmail: %v", err)
 				}
 				if author != nil {
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 1d390247f1a..4929f9f446f 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification"
 	repo_module "code.gitea.io/gitea/modules/repository"
@@ -16,7 +17,7 @@ import (
 )
 
 // CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) (*models.Repository, error) {
+func CreateRepository(doer, owner *user_model.User, opts models.CreateRepoOptions) (*models.Repository, error) {
 	repo, err := repo_module.CreateRepository(doer, owner, opts)
 	if err != nil {
 		// No need to rollback here we should do this in CreateRepository...
@@ -29,7 +30,7 @@ func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) (
 }
 
 // DeleteRepository deletes a repository for a user or organization.
-func DeleteRepository(doer *models.User, repo *models.Repository) error {
+func DeleteRepository(doer *user_model.User, repo *models.Repository) error {
 	if err := pull_service.CloseRepoBranchesPulls(doer, repo); err != nil {
 		log.Error("CloseRepoBranchesPulls failed: %v", err)
 	}
@@ -42,7 +43,7 @@ func DeleteRepository(doer *models.User, repo *models.Repository) error {
 }
 
 // PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace
-func PushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repository, error) {
+func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*models.Repository, error) {
 	if !authUser.IsAdmin {
 		if owner.IsOrganization() {
 			if ok, err := models.CanCreateOrgRepo(owner.ID, authUser.ID); err != nil {
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index bb323c1c0a2..e6b73fc9e08 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/sync"
 )
@@ -16,7 +17,7 @@ import (
 var repoWorkingPool = sync.NewExclusivePool()
 
 // TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, teams []*models.Team) error {
+func TransferOwnership(doer, newOwner *user_model.User, repo *models.Repository, teams []*models.Team) error {
 	if err := repo.GetOwner(); err != nil {
 		return err
 	}
@@ -52,7 +53,7 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea
 }
 
 // ChangeRepositoryName changes all corresponding setting from old repository name to new one.
-func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error {
+func ChangeRepositoryName(doer *user_model.User, repo *models.Repository, newRepoName string) error {
 	oldRepoName := repo.Name
 
 	// Change repository directory name. We must lock the local copy of the
@@ -73,7 +74,7 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam
 
 // StartRepositoryTransfer transfer a repo from one owner to a new one.
 // it make repository into pending transfer state, if doer can not create repo for new owner.
-func StartRepositoryTransfer(doer, newOwner *models.User, repo *models.Repository, teams []*models.Team) error {
+func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *models.Repository, teams []*models.Team) error {
 	if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil {
 		return err
 	}
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index 09c9829216a..866e49c8667 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/notification/action"
 	"code.gitea.io/gitea/modules/util"
@@ -30,9 +31,9 @@ func TestTransferOwnership(t *testing.T) {
 
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
-	repo.Owner = unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 	assert.NoError(t, TransferOwnership(doer, doer, repo, nil))
 
 	transferredRepo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
@@ -51,16 +52,16 @@ func TestTransferOwnership(t *testing.T) {
 		Content:   "user3/repo3",
 	})
 
-	unittest.CheckConsistencyFor(t, &models.Repository{}, &models.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &models.Repository{}, &user_model.User{}, &models.Team{})
 }
 
 func TestStartRepositoryTransferSetPermission(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
-	recipient := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 5}).(*models.User)
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+	recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 3}).(*models.Repository)
-	repo.Owner = unittest.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
+	repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
 
 	hasAccess, err := models.HasAccess(recipient.ID, repo)
 	assert.NoError(t, err)
@@ -72,5 +73,5 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) {
 	assert.NoError(t, err)
 	assert.True(t, hasAccess)
 
-	unittest.CheckConsistencyFor(t, &models.Repository{}, &models.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &models.Repository{}, &user_model.User{}, &models.Team{})
 }
diff --git a/services/task/migrate.go b/services/task/migrate.go
index 100aac1967e..46ea80b0028 100644
--- a/services/task/migrate.go
+++ b/services/task/migrate.go
@@ -11,6 +11,8 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -23,16 +25,16 @@ import (
 	"code.gitea.io/gitea/services/migrations"
 )
 
-func handleCreateError(owner *models.User, err error) error {
+func handleCreateError(owner *user_model.User, err error) error {
 	switch {
 	case models.IsErrReachLimitOfRepo(err):
 		return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit())
 	case models.IsErrRepoAlreadyExist(err):
 		return errors.New("The repository name is already used")
-	case models.IsErrNameReserved(err):
-		return fmt.Errorf("The repository name '%s' is reserved", err.(models.ErrNameReserved).Name)
-	case models.IsErrNamePatternNotAllowed(err):
-		return fmt.Errorf("The pattern '%s' is not allowed in a repository name", err.(models.ErrNamePatternNotAllowed).Pattern)
+	case db.IsErrNameReserved(err):
+		return fmt.Errorf("The repository name '%s' is reserved", err.(db.ErrNameReserved).Name)
+	case db.IsErrNamePatternNotAllowed(err):
+		return fmt.Errorf("The pattern '%s' is not allowed in a repository name", err.(db.ErrNamePatternNotAllowed).Pattern)
 	default:
 		return err
 	}
diff --git a/services/task/task.go b/services/task/task.go
index f538b36efc0..350b8206b26 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/json"
 	"code.gitea.io/gitea/modules/log"
@@ -57,7 +58,7 @@ func handle(data ...queue.Data) {
 }
 
 // MigrateRepository add migration repository to task
-func MigrateRepository(doer, u *models.User, opts base.MigrateOptions) error {
+func MigrateRepository(doer, u *user_model.User, opts base.MigrateOptions) error {
 	task, err := CreateMigrateTask(doer, u, opts)
 	if err != nil {
 		return err
@@ -67,7 +68,7 @@ func MigrateRepository(doer, u *models.User, opts base.MigrateOptions) error {
 }
 
 // CreateMigrateTask creates a migrate task
-func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.Task, error) {
+func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*models.Task, error) {
 	// encrypt credentials for persistence
 	var err error
 	opts.CloneAddrEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.CloneAddr)
diff --git a/services/user/user.go b/services/user/user.go
index 0578f70b27f..6df9eec1316 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -25,7 +25,7 @@ import (
 // DeleteUser completely and permanently deletes everything of a user,
 // but issues/comments/pulls will be kept and shown as someone has been deleted,
 // unless the user is younger than USER_DELETE_WITH_COMMENTS_MAX_DAYS.
-func DeleteUser(u *models.User) error {
+func DeleteUser(u *user_model.User) error {
 	if u.IsOrganization() {
 		return fmt.Errorf("%s is an organization not a user", u.Name)
 	}
@@ -72,7 +72,7 @@ func DeleteUser(u *models.User) error {
 
 	// Note: There are something just cannot be roll back,
 	//	so just keep error logs of those operations.
-	path := models.UserPath(u.Name)
+	path := user_model.UserPath(u.Name)
 	if err := util.RemoveAll(path); err != nil {
 		err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
 		_ = admin_model.CreateNotice(db.DefaultContext, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
@@ -93,7 +93,7 @@ func DeleteUser(u *models.User) error {
 
 // DeleteInactiveUsers deletes all inactive users and email addresses.
 func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
-	users, err := models.GetInactiveUsers(ctx, olderThan)
+	users, err := user_model.GetInactiveUsers(ctx, olderThan)
 	if err != nil {
 		return err
 	}
@@ -118,7 +118,7 @@ func DeleteInactiveUsers(ctx context.Context, olderThan time.Duration) error {
 }
 
 // UploadAvatar saves custom avatar for user.
-func UploadAvatar(u *models.User, data []byte) error {
+func UploadAvatar(u *user_model.User, data []byte) error {
 	m, err := avatar.Prepare(data)
 	if err != nil {
 		return err
@@ -136,7 +136,7 @@ func UploadAvatar(u *models.User, data []byte) error {
 	// Otherwise, if any of the users delete his avatar
 	// Other users will lose their avatars too.
 	u.Avatar = fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
-	if err = models.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
+	if err = user_model.UpdateUserCols(ctx, u, "use_custom_avatar", "avatar"); err != nil {
 		return fmt.Errorf("updateUser: %v", err)
 	}
 
@@ -153,7 +153,7 @@ func UploadAvatar(u *models.User, data []byte) error {
 }
 
 // DeleteAvatar deletes the user's custom avatar.
-func DeleteAvatar(u *models.User) error {
+func DeleteAvatar(u *user_model.User) error {
 	aPath := u.CustomAvatarRelativePath()
 	log.Trace("DeleteAvatar[%d]: %s", u.ID, aPath)
 	if len(u.Avatar) > 0 {
diff --git a/services/user/user_test.go b/services/user/user_test.go
index 9162273fae0..e18b760308d 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -11,6 +11,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"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"
@@ -23,7 +24,7 @@ func TestMain(m *testing.M) {
 func TestDeleteUser(t *testing.T) {
 	test := func(userID int64) {
 		assert.NoError(t, unittest.PrepareTestDatabase())
-		user := unittest.AssertExistsAndLoadBean(t, &models.User{ID: userID}).(*models.User)
+		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
 
 		ownedRepos := make([]*models.Repository, 0, 10)
 		assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&ownedRepos, &models.Repository{OwnerID: userID}))
@@ -43,20 +44,20 @@ func TestDeleteUser(t *testing.T) {
 			}
 		}
 		assert.NoError(t, DeleteUser(user))
-		unittest.AssertNotExistsBean(t, &models.User{ID: userID})
-		unittest.CheckConsistencyFor(t, &models.User{}, &models.Repository{})
+		unittest.AssertNotExistsBean(t, &user_model.User{ID: userID})
+		unittest.CheckConsistencyFor(t, &user_model.User{}, &models.Repository{})
 	}
 	test(2)
 	test(4)
 	test(8)
 	test(11)
 
-	org := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 3}).(*models.User)
+	org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
 	assert.Error(t, DeleteUser(org))
 }
 
 func TestCreateUser(t *testing.T) {
-	user := &models.User{
+	user := &user_model.User{
 		Name:               "GiteaBot",
 		Email:              "GiteaBot@gitea.io",
 		Passwd:             ";p['////..-++']",
@@ -65,7 +66,7 @@ func TestCreateUser(t *testing.T) {
 		MustChangePassword: false,
 	}
 
-	assert.NoError(t, models.CreateUser(user))
+	assert.NoError(t, user_model.CreateUser(user))
 
 	assert.NoError(t, DeleteUser(user))
 }
@@ -77,11 +78,11 @@ func TestCreateUser_Issue5882(t *testing.T) {
 	passwd := ".//.;1;;//.,-=_"
 
 	tt := []struct {
-		user               *models.User
+		user               *user_model.User
 		disableOrgCreation bool
 	}{
-		{&models.User{Name: "GiteaBot", Email: "GiteaBot@gitea.io", Passwd: passwd, MustChangePassword: false}, false},
-		{&models.User{Name: "GiteaBot2", Email: "GiteaBot2@gitea.io", Passwd: passwd, MustChangePassword: false}, true},
+		{&user_model.User{Name: "GiteaBot", Email: "GiteaBot@gitea.io", Passwd: passwd, MustChangePassword: false}, false},
+		{&user_model.User{Name: "GiteaBot2", Email: "GiteaBot2@gitea.io", Passwd: passwd, MustChangePassword: false}, true},
 	}
 
 	setting.Service.DefaultAllowCreateOrganization = true
@@ -89,9 +90,9 @@ func TestCreateUser_Issue5882(t *testing.T) {
 	for _, v := range tt {
 		setting.Admin.DisableRegularOrgCreation = v.disableOrgCreation
 
-		assert.NoError(t, models.CreateUser(v.user))
+		assert.NoError(t, user_model.CreateUser(v.user))
 
-		u, err := models.GetUserByEmail(v.user.Email)
+		u, err := user_model.GetUserByEmail(v.user.Email)
 		assert.NoError(t, err)
 
 		assert.Equal(t, !u.AllowCreateOrganization, v.disableOrgCreation)
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 4c8d7dbc53c..8b234446733 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -15,6 +15,7 @@ import (
 	admin_model "code.gitea.io/gitea/models/admin"
 	"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/git"
 	"code.gitea.io/gitea/modules/log"
 	repo_module "code.gitea.io/gitea/modules/repository"
@@ -116,7 +117,7 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string
 }
 
 // updateWikiPage adds a new page to the repository wiki.
-func updateWikiPage(doer *models.User, repo *models.Repository, oldWikiName, newWikiName, content, message string, isNew bool) (err error) {
+func updateWikiPage(doer *user_model.User, repo *models.Repository, oldWikiName, newWikiName, content, message string, isNew bool) (err error) {
 	if err = nameAllowed(newWikiName); err != nil {
 		return err
 	}
@@ -264,18 +265,18 @@ func updateWikiPage(doer *models.User, repo *models.Repository, oldWikiName, new
 }
 
 // AddWikiPage adds a new wiki page with a given wikiPath.
-func AddWikiPage(doer *models.User, repo *models.Repository, wikiName, content, message string) error {
+func AddWikiPage(doer *user_model.User, repo *models.Repository, wikiName, content, message string) error {
 	return updateWikiPage(doer, repo, "", wikiName, content, message, true)
 }
 
 // EditWikiPage updates a wiki page identified by its wikiPath,
 // optionally also changing wikiPath.
-func EditWikiPage(doer *models.User, repo *models.Repository, oldWikiName, newWikiName, content, message string) error {
+func EditWikiPage(doer *user_model.User, repo *models.Repository, oldWikiName, newWikiName, content, message string) error {
 	return updateWikiPage(doer, repo, oldWikiName, newWikiName, content, message, false)
 }
 
 // DeleteWikiPage deletes a wiki page identified by its path.
-func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string) (err error) {
+func DeleteWikiPage(doer *user_model.User, repo *models.Repository, wikiName string) (err error) {
 	wikiWorkingPool.CheckIn(fmt.Sprint(repo.ID))
 	defer wikiWorkingPool.CheckOut(fmt.Sprint(repo.ID))
 
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index ee548d23158..5b42dd590f7 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -11,6 +11,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/unittest"
+	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/util"
 
@@ -126,7 +127,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
 	const wikiContent = "This is the wiki content"
 	const commitMsg = "Commit message"
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	for _, wikiName := range []string{
 		"Another page",
 		"Here's a <tag> and a/slash",
@@ -171,7 +172,7 @@ func TestRepository_EditWikiPage(t *testing.T) {
 	const newWikiContent = "This is the new content"
 	const commitMsg = "Commit message"
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	for _, newWikiName := range []string{
 		"Home", // same name as before
 		"New home",
@@ -201,7 +202,7 @@ func TestRepository_EditWikiPage(t *testing.T) {
 func TestRepository_DeleteWikiPage(t *testing.T) {
 	unittest.PrepareTestEnv(t)
 	repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
-	doer := unittest.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	assert.NoError(t, DeleteWikiPage(doer, repo, "Home"))
 
 	// Now need to show that the page has been added: