diff --git a/integrations/api_repo_teams_test.go b/integrations/api_repo_teams_test.go
index a3baeba63c..efd6ddb457 100644
--- a/integrations/api_repo_teams_test.go
+++ b/integrations/api_repo_teams_test.go
@@ -37,7 +37,7 @@ func TestAPIRepoTeams(t *testing.T) {
 	DecodeJSON(t, res, &teams)
 	if assert.Len(t, teams, 2) {
 		assert.EqualValues(t, "Owners", teams[0].Name)
-		assert.False(t, teams[0].CanCreateOrgRepo)
+		assert.True(t, teams[0].CanCreateOrgRepo)
 		assert.True(t, util.IsEqualSlice(unit.AllUnitKeyNames(), teams[0].Units), fmt.Sprintf("%v == %v", unit.AllUnitKeyNames(), teams[0].Units))
 		assert.EqualValues(t, "owner", teams[0].Permission)
 
diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go
index a622c63145..7f6d1b81d4 100644
--- a/integrations/api_team_test.go
+++ b/integrations/api_team_test.go
@@ -10,7 +10,7 @@ import (
 	"sort"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -23,8 +23,8 @@ import (
 func TestAPITeam(t *testing.T) {
 	defer prepareTestEnv(t)()
 
-	teamUser := unittest.AssertExistsAndLoadBean(t, &models.TeamUser{}).(*models.TeamUser)
-	team := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamUser.TeamID}).(*models.Team)
+	teamUser := unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{}).(*organization.TeamUser)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamUser.TeamID}).(*organization.Team)
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser.UID}).(*user_model.User)
 
 	session := loginUser(t, user.Name)
@@ -38,7 +38,7 @@ func TestAPITeam(t *testing.T) {
 	assert.Equal(t, team.Name, apiTeam.Name)
 
 	// non team member user will not access the teams details
-	teamUser2 := unittest.AssertExistsAndLoadBean(t, &models.TeamUser{ID: 3}).(*models.TeamUser)
+	teamUser2 := unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{ID: 3}).(*organization.TeamUser)
 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: teamUser2.UID}).(*user_model.User)
 
 	session = loginUser(t, user2.Name)
@@ -107,7 +107,7 @@ func TestAPITeam(t *testing.T) {
 		teamToEdit.Permission, unit.AllUnitKeyNames(), nil)
 
 	// Read team.
-	teamRead := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
+	teamRead := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
 	assert.NoError(t, teamRead.GetUnits())
 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
 	resp = session.MakeRequest(t, req, http.StatusOK)
@@ -119,7 +119,7 @@ func TestAPITeam(t *testing.T) {
 	// Delete team.
 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
 	session.MakeRequest(t, req, http.StatusNoContent)
-	unittest.AssertNotExistsBean(t, &models.Team{ID: teamID})
+	unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
 
 	// create team again via UnitsMap
 	// Create team.
@@ -173,7 +173,7 @@ func TestAPITeam(t *testing.T) {
 		"read", nil, teamToEdit.UnitsMap)
 
 	// Read team.
-	teamRead = unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
+	teamRead = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
 	resp = session.MakeRequest(t, req, http.StatusOK)
 	apiTeam = api.Team{}
@@ -185,7 +185,7 @@ func TestAPITeam(t *testing.T) {
 	// Delete team.
 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
 	session.MakeRequest(t, req, http.StatusNoContent)
-	unittest.AssertNotExistsBean(t, &models.Team{ID: teamID})
+	unittest.AssertNotExistsBean(t, &organization.Team{ID: teamID})
 }
 
 func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) {
@@ -206,7 +206,7 @@ func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string
 }
 
 func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) {
-	team := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: id}).(*models.Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: id}).(*organization.Team)
 	assert.NoError(t, team.GetUnits(), "GetUnits")
 	checkTeamResponse(t, convert.ToTeam(team), name, description, includesAllRepositories, permission, units, unitsMap)
 }
diff --git a/integrations/auth_ldap_test.go b/integrations/auth_ldap_test.go
index 234e7c3ee9..93334aec00 100644
--- a/integrations/auth_ldap_test.go
+++ b/integrations/auth_ldap_test.go
@@ -12,6 +12,8 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/services/auth"
@@ -317,37 +319,37 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) {
 	}
 	defer prepareTestEnv(t)()
 	addAuthSourceLDAP(t, "", "on", `{"cn=ship_crew,ou=people,dc=planetexpress,dc=com":{"org26": ["team11"]},"cn=admin_staff,ou=people,dc=planetexpress,dc=com": {"non-existent": ["non-existent"]}}`)
-	org, err := models.GetOrgByName("org26")
+	org, err := organization.GetOrgByName("org26")
 	assert.NoError(t, err)
-	team, err := models.GetTeam(org.ID, "team11")
+	team, err := organization.GetTeam(org.ID, "team11")
 	assert.NoError(t, err)
 	auth.SyncExternalUsers(context.Background(), true)
 	for _, gitLDAPUser := range gitLDAPUsers {
 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 			Name: gitLDAPUser.UserName,
 		}).(*user_model.User)
-		usersOrgs, err := models.FindOrgs(models.FindOrgOptions{
+		usersOrgs, err := organization.FindOrgs(organization.FindOrgOptions{
 			UserID:         user.ID,
 			IncludePrivate: true,
 		})
 		assert.NoError(t, err)
-		allOrgTeams, err := models.GetUserOrgTeams(org.ID, user.ID)
+		allOrgTeams, err := organization.GetUserOrgTeams(db.DefaultContext, org.ID, user.ID)
 		assert.NoError(t, err)
 		if user.Name == "fry" || user.Name == "leela" || user.Name == "bender" {
 			// assert members of LDAP group "cn=ship_crew" are added to mapped teams
 			assert.Equal(t, len(usersOrgs), 1, "User [%s] should be member of one organization", user.Name)
 			assert.Equal(t, usersOrgs[0].Name, "org26", "Membership should be added to the right organization")
-			isMember, err := models.IsTeamMember(usersOrgs[0].ID, team.ID, user.ID)
+			isMember, err := organization.IsTeamMember(db.DefaultContext, usersOrgs[0].ID, team.ID, user.ID)
 			assert.NoError(t, err)
 			assert.True(t, isMember, "Membership should be added to the right team")
-			err = team.RemoveMember(user.ID)
+			err = models.RemoveTeamMember(team, user.ID)
 			assert.NoError(t, err)
-			err = usersOrgs[0].RemoveMember(user.ID)
+			err = models.RemoveOrgUser(usersOrgs[0].ID, user.ID)
 			assert.NoError(t, err)
 		} else {
 			// assert members of LDAP group "cn=admin_staff" keep initial team membership since mapped team does not exist
 			assert.Empty(t, usersOrgs, "User should be member of no organization")
-			isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID)
+			isMember, err := organization.IsTeamMember(db.DefaultContext, org.ID, team.ID, user.ID)
 			assert.NoError(t, err)
 			assert.False(t, isMember, "User should no be added to this team")
 			assert.Empty(t, allOrgTeams, "User should not be added to any team")
@@ -362,30 +364,30 @@ func TestLDAPGroupTeamSyncRemoveMember(t *testing.T) {
 	}
 	defer prepareTestEnv(t)()
 	addAuthSourceLDAP(t, "", "on", `{"cn=dispatch,ou=people,dc=planetexpress,dc=com": {"org26": ["team11"]}}`)
-	org, err := models.GetOrgByName("org26")
+	org, err := organization.GetOrgByName("org26")
 	assert.NoError(t, err)
-	team, err := models.GetTeam(org.ID, "team11")
+	team, err := organization.GetTeam(org.ID, "team11")
 	assert.NoError(t, err)
 	loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password)
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{
 		Name: gitLDAPUsers[0].UserName,
 	}).(*user_model.User)
-	err = org.AddMember(user.ID)
+	err = organization.AddOrgUser(org.ID, user.ID)
 	assert.NoError(t, err)
-	err = team.AddMember(user.ID)
+	err = models.AddTeamMember(team, user.ID)
 	assert.NoError(t, err)
-	isMember, err := models.IsOrganizationMember(org.ID, user.ID)
+	isMember, err := organization.IsOrganizationMember(db.DefaultContext, org.ID, user.ID)
 	assert.NoError(t, err)
 	assert.True(t, isMember, "User should be member of this organization")
-	isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID)
+	isMember, err = organization.IsTeamMember(db.DefaultContext, org.ID, team.ID, user.ID)
 	assert.NoError(t, err)
 	assert.True(t, isMember, "User should be member of this team")
 	// assert team member "professor" gets removed from org26 team11
 	loginUserWithPassword(t, gitLDAPUsers[0].UserName, gitLDAPUsers[0].Password)
-	isMember, err = models.IsOrganizationMember(org.ID, user.ID)
+	isMember, err = organization.IsOrganizationMember(db.DefaultContext, org.ID, user.ID)
 	assert.NoError(t, err)
 	assert.False(t, isMember, "User membership should have been removed from organization")
-	isMember, err = models.IsTeamMember(org.ID, team.ID, user.ID)
+	isMember, err = organization.IsTeamMember(db.DefaultContext, org.ID, team.ID, user.ID)
 	assert.NoError(t, err)
 	assert.False(t, isMember, "User membership should have been removed from team")
 }
diff --git a/integrations/delete_user_test.go b/integrations/delete_user_test.go
index f24d75065f..4b67c05951 100644
--- a/integrations/delete_user_test.go
+++ b/integrations/delete_user_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -21,9 +22,9 @@ func assertUserDeleted(t *testing.T, userID int64) {
 	unittest.AssertNotExistsBean(t, &user_model.Follow{FollowID: userID})
 	unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerID: userID})
 	unittest.AssertNotExistsBean(t, &models.Access{UserID: userID})
-	unittest.AssertNotExistsBean(t, &models.OrgUser{UID: userID})
+	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: userID})
 	unittest.AssertNotExistsBean(t, &models.IssueUser{UID: userID})
-	unittest.AssertNotExistsBean(t, &models.TeamUser{UID: userID})
+	unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID})
 	unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID})
 }
 
diff --git a/integrations/org_count_test.go b/integrations/org_count_test.go
index b7d0da2f4b..eca51eb0f6 100644
--- a/integrations/org_count_test.go
+++ b/integrations/org_count_test.go
@@ -9,7 +9,7 @@ import (
 	"strings"
 	"testing"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
@@ -117,7 +117,7 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca
 			Name: username,
 		}).(*user_model.User)
 
-		orgs, err := models.FindOrgs(models.FindOrgOptions{
+		orgs, err := organization.FindOrgs(organization.FindOrgOptions{
 			UserID:         user.ID,
 			IncludePrivate: true,
 		})
diff --git a/models/access.go b/models/access.go
index 48b65c2c0f..d49d3430dc 100644
--- a/models/access.go
+++ b/models/access.go
@@ -10,6 +10,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -149,7 +150,7 @@ func recalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, i
 		return fmt.Errorf("refreshCollaboratorAccesses: %v", err)
 	}
 
-	teams, err := OrgFromUser(repo.Owner).loadTeams(e)
+	teams, err := organization.FindOrgTeams(ctx, repo.Owner.ID)
 	if err != nil {
 		return err
 	}
@@ -163,11 +164,11 @@ func recalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, i
 		// have relations with repository.
 		if t.IsOwnerTeam() {
 			t.AccessMode = perm.AccessModeOwner
-		} else if !t.hasRepository(e, repo.ID) {
+		} else if !hasRepository(ctx, t, repo.ID) {
 			continue
 		}
 
-		if err = t.getMembers(e); err != nil {
+		if err = t.GetMembersCtx(ctx); err != nil {
 			return fmt.Errorf("getMembers '%d': %v", t.ID, err)
 		}
 		for _, m := range t.Members {
@@ -198,7 +199,7 @@ func recalculateUserAccess(ctx context.Context, repo *repo_model.Repository, uid
 	if err = repo.GetOwner(ctx); err != nil {
 		return err
 	} else if repo.Owner.IsOrganization() {
-		var teams []Team
+		var teams []organization.Team
 		if err := e.Join("INNER", "team_repo", "team_repo.team_id = team.id").
 			Join("INNER", "team_user", "team_user.team_id = team.id").
 			Where("team.org_id = ?", repo.OwnerID).
diff --git a/models/access_test.go b/models/access_test.go
index 43e61e812b..7533381dca 100644
--- a/models/access_test.go
+++ b/models/access_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -127,7 +128,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)
+	team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5}).(*organization.Team)
 	user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}).(*user_model.User)
 
 	has, err := db.GetEngine(db.DefaultContext).Get(&Access{UserID: 29, RepoID: 23})
diff --git a/models/action.go b/models/action.go
index e9f5138b40..d3231641d9 100644
--- a/models/action.go
+++ b/models/action.go
@@ -15,6 +15,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -318,7 +319,7 @@ func (a *Action) GetIssueContent() string {
 type GetFeedsOptions struct {
 	db.ListOptions
 	RequestedUser   *user_model.User       // the user we want activity for
-	RequestedTeam   *Team                  // the team we want activity for
+	RequestedTeam   *organization.Team     // the team we want activity for
 	RequestedRepo   *repo_model.Repository // the repo we want activity for
 	Actor           *user_model.User       // the user viewing the activity
 	IncludePrivate  bool                   // include private actions
@@ -399,7 +400,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
 	}
 
 	if opts.RequestedTeam != nil {
-		env := OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(opts.RequestedTeam)
+		env := organization.OrgFromUser(opts.RequestedUser).AccessibleTeamReposEnv(opts.RequestedTeam)
 		teamRepoIDs, err := env.RepoIDs(1, opts.RequestedUser.NumRepos)
 		if err != nil {
 			return nil, fmt.Errorf("GetTeamRepositories: %v", err)
diff --git a/models/branches.go b/models/branches.go
index e6d8b7441a..8156fd0908 100644
--- a/models/branches.go
+++ b/models/branches.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -94,7 +95,7 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool {
 		return false
 	}
 
-	in, err := IsUserInTeams(userID, protectBranch.WhitelistTeamIDs)
+	in, err := organization.IsUserInTeams(db.DefaultContext, userID, protectBranch.WhitelistTeamIDs)
 	if err != nil {
 		log.Error("IsUserInTeams: %v", err)
 		return false
@@ -117,7 +118,7 @@ func IsUserMergeWhitelisted(protectBranch *ProtectedBranch, userID int64, permis
 		return false
 	}
 
-	in, err := IsUserInTeams(userID, protectBranch.MergeWhitelistTeamIDs)
+	in, err := organization.IsUserInTeams(db.DefaultContext, userID, protectBranch.MergeWhitelistTeamIDs)
 	if err != nil {
 		log.Error("IsUserInTeams: %v", err)
 		return false
@@ -149,7 +150,7 @@ func isUserOfficialReviewer(ctx context.Context, protectBranch *ProtectedBranch,
 		return true, nil
 	}
 
-	inTeam, err := isUserInTeams(db.GetEngine(ctx), user.ID, protectBranch.ApprovalsWhitelistTeamIDs)
+	inTeam, err := organization.IsUserInTeams(ctx, user.ID, protectBranch.ApprovalsWhitelistTeamIDs)
 	if err != nil {
 		return false, err
 	}
@@ -471,7 +472,7 @@ func updateTeamWhitelist(repo *repo_model.Repository, currentWhitelist, newWhite
 		return currentWhitelist, nil
 	}
 
-	teams, err := GetTeamsWithAccessToRepo(repo.OwnerID, repo.ID, perm.AccessModeRead)
+	teams, err := organization.GetTeamsWithAccessToRepo(repo.OwnerID, repo.ID, perm.AccessModeRead)
 	if err != nil {
 		return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v", repo.OwnerID, repo.ID, err)
 	}
diff --git a/models/error.go b/models/error.go
index f0e8751d75..3c4d219775 100644
--- a/models/error.go
+++ b/models/error.go
@@ -58,19 +58,6 @@ func (err ErrUserHasOrgs) Error() string {
 	return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
 }
 
-// ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
-type ErrUserNotAllowedCreateOrg struct{}
-
-// IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
-func IsErrUserNotAllowedCreateOrg(err error) bool {
-	_, ok := err.(ErrUserNotAllowedCreateOrg)
-	return ok
-}
-
-func (err ErrUserNotAllowedCreateOrg) Error() string {
-	return "user is not allowed to create organizations"
-}
-
 //  __      __.__ __   .__
 // /  \    /  \__|  | _|__|
 // \   \/\/   /  |  |/ /  |
@@ -158,44 +145,6 @@ func (err ErrAccessTokenEmpty) Error() string {
 	return "access token is empty"
 }
 
-// ________                            .__                __  .__
-// \_____  \_______  _________    ____ |__|____________ _/  |_|__| ____   ____
-//  /   |   \_  __ \/ ___\__  \  /    \|  \___   /\__  \\   __\  |/  _ \ /    \
-// /    |    \  | \/ /_/  > __ \|   |  \  |/    /  / __ \|  | |  (  <_> )   |  \
-// \_______  /__|  \___  (____  /___|  /__/_____ \(____  /__| |__|\____/|___|  /
-//         \/     /_____/     \/     \/         \/     \/                    \/
-
-// ErrOrgNotExist represents a "OrgNotExist" kind of error.
-type ErrOrgNotExist struct {
-	ID   int64
-	Name string
-}
-
-// IsErrOrgNotExist checks if an error is a ErrOrgNotExist.
-func IsErrOrgNotExist(err error) bool {
-	_, ok := err.(ErrOrgNotExist)
-	return ok
-}
-
-func (err ErrOrgNotExist) Error() string {
-	return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
-}
-
-// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
-type ErrLastOrgOwner struct {
-	UID int64
-}
-
-// IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
-func IsErrLastOrgOwner(err error) bool {
-	_, ok := err.(ErrLastOrgOwner)
-	return ok
-}
-
-func (err ErrLastOrgOwner) Error() string {
-	return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
-}
-
 //.____   ____________________
 //|    |  \_   _____/   _____/
 //|    |   |    __) \_____  \
@@ -1195,46 +1144,6 @@ func (err ErrMilestoneNotExist) Error() string {
 	return fmt.Sprintf("milestone does not exist [id: %d, repo_id: %d]", err.ID, err.RepoID)
 }
 
-// ___________
-// \__    ___/___ _____    _____
-//   |    |_/ __ \\__  \  /     \
-//   |    |\  ___/ / __ \|  Y Y  \
-//   |____| \___  >____  /__|_|  /
-//              \/     \/      \/
-
-// ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error.
-type ErrTeamAlreadyExist struct {
-	OrgID int64
-	Name  string
-}
-
-// IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist.
-func IsErrTeamAlreadyExist(err error) bool {
-	_, ok := err.(ErrTeamAlreadyExist)
-	return ok
-}
-
-func (err ErrTeamAlreadyExist) Error() string {
-	return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
-}
-
-// ErrTeamNotExist represents a "TeamNotExist" error
-type ErrTeamNotExist struct {
-	OrgID  int64
-	TeamID int64
-	Name   string
-}
-
-// IsErrTeamNotExist checks if an error is a ErrTeamNotExist.
-func IsErrTeamNotExist(err error) bool {
-	_, ok := err.(ErrTeamNotExist)
-	return ok
-}
-
-func (err ErrTeamNotExist) Error() string {
-	return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
-}
-
 //  ____ ___        .__                    .___
 // |    |   \______ |  |   _________     __| _/
 // |    |   /\____ \|  |  /  _ \__  \   / __ |
diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml
index 9a8b0aff76..f6dfd1e9d0 100644
--- a/models/fixtures/team.yml
+++ b/models/fixtures/team.yml
@@ -6,6 +6,7 @@
   authorize: 4 # owner
   num_repos: 3
   num_members: 1
+  can_create_org_repo: true
 
 -
   id: 2
@@ -15,6 +16,7 @@
   authorize: 2 # write
   num_repos: 1
   num_members: 2
+  can_create_org_repo: false
 
 -
   id: 3
@@ -24,6 +26,7 @@
   authorize: 4 # owner
   num_repos: 0
   num_members: 1
+  can_create_org_repo: true
 
 -
   id: 4
@@ -33,6 +36,7 @@
   authorize: 4 # owner
   num_repos: 0
   num_members: 1
+  can_create_org_repo: true
 
 -
   id: 5
@@ -42,6 +46,7 @@
   authorize: 4 # owner
   num_repos: 2
   num_members: 2
+  can_create_org_repo: true
 
 -
   id: 6
@@ -51,6 +56,7 @@
   authorize: 4 # owner
   num_repos: 2
   num_members: 1
+  can_create_org_repo: true
 
 -
   id: 7
@@ -60,6 +66,7 @@
   authorize: 2 # write
   num_repos: 1
   num_members: 1
+  can_create_org_repo: false
 
 -
   id: 8
@@ -69,6 +76,7 @@
   authorize: 2 # write
   num_repos: 1
   num_members: 1
+  can_create_org_repo: false
 
 -
   id: 9
@@ -78,6 +86,7 @@
   authorize: 1 # read
   num_repos: 1
   num_members: 2
+  can_create_org_repo: false
 
 -
   id: 10
@@ -87,6 +96,7 @@
   authorize: 1 # owner
   num_repos: 0
   num_members: 1
+  can_create_org_repo: false
 
 -
   id: 11
@@ -96,6 +106,7 @@
   authorize: 1 # read
   num_repos: 0
   num_members: 0
+  can_create_org_repo: false
 
 -
   id: 12
diff --git a/models/issue.go b/models/issue.go
index 79771ce15c..31f7a0edb0 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/foreignreference"
 	"code.gitea.io/gitea/models/issues"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -1238,9 +1239,9 @@ type IssuesOptions struct {
 	// prioritize issues from this repo
 	PriorityRepoID int64
 	IsArchived     util.OptionalBool
-	Org            *Organization    // issues permission scope
-	Team           *Team            // issues permission scope
-	User           *user_model.User // issues permission scope
+	Org            *organization.Organization // issues permission scope
+	Team           *organization.Team         // issues permission scope
+	User           *user_model.User           // issues permission scope
 }
 
 // sortIssuesSession sort an issues-related session based on the provided
@@ -1401,7 +1402,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
 }
 
 // issuePullAccessibleRepoCond userID must not be zero, this condition require join repository table
-func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *Organization, team *Team, isPull bool) builder.Cond {
+func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organization.Organization, team *organization.Team, isPull bool) builder.Cond {
 	cond := builder.NewCond()
 	unitType := unit.TypeIssues
 	if isPull {
@@ -1749,8 +1750,8 @@ type UserIssueStatsOptions struct {
 	IsArchived util.OptionalBool
 	LabelIDs   []int64
 	RepoCond   builder.Cond
-	Org        *Organization
-	Team       *Team
+	Org        *organization.Organization
+	Team       *organization.Team
 }
 
 // GetUserIssueStats returns issue statistic information for dashboard by given conditions.
@@ -2296,7 +2297,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *user_
 	}
 
 	if issue.Repo.Owner.IsOrganization() && len(mentionTeams) > 0 {
-		teams := make([]*Team, 0, len(mentionTeams))
+		teams := make([]*organization.Team, 0, len(mentionTeams))
 		if err := db.GetEngine(ctx).
 			Join("INNER", "team_repo", "team_repo.team_id = team.id").
 			Where("team_repo.repo_id=?", issue.Repo.ID).
@@ -2316,7 +2317,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *user_
 					resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
 					continue
 				}
-				has, err := db.GetEngine(ctx).Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
+				has, err := db.GetEngine(ctx).Get(&organization.TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
 				if err != nil {
 					return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
 				}
diff --git a/models/issue_comment.go b/models/issue_comment.go
index 8390d90f8a..7927b0037b 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -16,6 +16,7 @@ import (
 
 	"code.gitea.io/gitea/models/db"
 	"code.gitea.io/gitea/models/issues"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
@@ -213,9 +214,9 @@ type Comment struct {
 	Time             *TrackedTime `xorm:"-"`
 	AssigneeID       int64
 	RemovedAssignee  bool
-	Assignee         *user_model.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     *organization.Team `xorm:"-"`
 	ResolveDoerID    int64
 	ResolveDoer      *user_model.User `xorm:"-"`
 	OldTitle         string
@@ -581,8 +582,8 @@ func (c *Comment) LoadAssigneeUserAndTeam() error {
 		}
 
 		if c.Issue.Repo.Owner.IsOrganization() {
-			c.AssigneeTeam, err = GetTeamByID(c.AssigneeTeamID)
-			if err != nil && !IsErrTeamNotExist(err) {
+			c.AssigneeTeam, err = organization.GetTeamByID(c.AssigneeTeamID)
+			if err != nil && !organization.IsErrTeamNotExist(err) {
 				return err
 			}
 		}
diff --git a/models/main_test.go b/models/main_test.go
index 8d5291a8aa..08cb2386c0 100644
--- a/models/main_test.go
+++ b/models/main_test.go
@@ -7,6 +7,7 @@ package models
 import (
 	"testing"
 
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -30,7 +31,7 @@ func TestFixturesAreConsistent(t *testing.T) {
 		&PullRequest{},
 		&Milestone{},
 		&Label{},
-		&Team{},
+		&organization.Team{},
 		&Action{})
 }
 
diff --git a/models/notification.go b/models/notification.go
index b53d236e43..b5217777bf 100644
--- a/models/notification.go
+++ b/models/notification.go
@@ -11,6 +11,7 @@ import (
 	"strconv"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -151,7 +152,7 @@ func CreateRepoTransferNotification(doer, newOwner *user_model.User, repo *repo_
 	var notify []*Notification
 
 	if newOwner.IsOrganization() {
-		users, err := getUsersWhoCanCreateOrgRepo(db.GetEngine(ctx), newOwner.ID)
+		users, err := organization.GetUsersWhoCanCreateOrgRepo(ctx, newOwner.ID)
 		if err != nil || len(users) == 0 {
 			return err
 		}
diff --git a/models/org.go b/models/org.go
index 70b3fe27b9..1e5b403f12 100644
--- a/models/org.go
+++ b/models/org.go
@@ -11,443 +11,15 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/perm"
+	"code.gitea.io/gitea/models/organization"
 	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/log"
-	"code.gitea.io/gitea/modules/setting"
-	"code.gitea.io/gitea/modules/structs"
 
 	"xorm.io/builder"
 )
 
-// Organization represents an organization
-type Organization user_model.User
-
-// OrgFromUser converts user to organization
-func OrgFromUser(user *user_model.User) *Organization {
-	return (*Organization)(user)
-}
-
-// TableName represents the real table name of Organization
-func (Organization) TableName() string {
-	return "user"
-}
-
-// IsOwnedBy returns true if given user is in the owner team.
-func (org *Organization) IsOwnedBy(uid int64) (bool, error) {
-	return IsOrganizationOwner(org.ID, uid)
-}
-
-// IsOrgMember returns true if given user is member of organization.
-func (org *Organization) IsOrgMember(uid int64) (bool, error) {
-	return IsOrganizationMember(org.ID, uid)
-}
-
-// CanCreateOrgRepo returns true if given user can create repo in organization
-func (org *Organization) CanCreateOrgRepo(uid int64) (bool, error) {
-	return CanCreateOrgRepo(org.ID, uid)
-}
-
-func (org *Organization) getTeam(e db.Engine, name string) (*Team, error) {
-	return getTeam(e, org.ID, name)
-}
-
-// GetTeam returns named team of organization.
-func (org *Organization) GetTeam(name string) (*Team, error) {
-	return org.getTeam(db.GetEngine(db.DefaultContext), name)
-}
-
-func (org *Organization) getOwnerTeam(e db.Engine) (*Team, error) {
-	return org.getTeam(e, ownerTeamName)
-}
-
-// GetOwnerTeam returns owner team of organization.
-func (org *Organization) GetOwnerTeam() (*Team, error) {
-	return org.getOwnerTeam(db.GetEngine(db.DefaultContext))
-}
-
-func (org *Organization) loadTeams(e db.Engine) ([]*Team, error) {
-	var teams []*Team
-	return teams, e.
-		Where("org_id=?", org.ID).
-		OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
-		Find(&teams)
-}
-
-// LoadTeams load teams if not loaded.
-func (org *Organization) LoadTeams() ([]*Team, error) {
-	return org.loadTeams(db.GetEngine(db.DefaultContext))
-}
-
-// GetMembers returns all members of organization.
-func (org *Organization) GetMembers() (user_model.UserList, map[int64]bool, error) {
-	return FindOrgMembers(&FindOrgMembersOpts{
-		OrgID: org.ID,
-	})
-}
-
-// HasMemberWithUserID returns true if user with userID is part of the u organisation.
-func (org *Organization) HasMemberWithUserID(userID int64) bool {
-	return org.hasMemberWithUserID(db.GetEngine(db.DefaultContext), userID)
-}
-
-func (org *Organization) hasMemberWithUserID(e db.Engine, userID int64) bool {
-	isMember, err := isOrganizationMember(e, org.ID, userID)
-	if err != nil {
-		log.Error("IsOrganizationMember: %v", err)
-		return false
-	}
-	return isMember
-}
-
-// AvatarLink returns the full avatar link with http host
-func (org *Organization) AvatarLink() string {
-	return org.AsUser().AvatarLink()
-}
-
-// HTMLURL returns the organization's full link.
-func (org *Organization) HTMLURL() string {
-	return org.AsUser().HTMLURL()
-}
-
-// OrganisationLink returns the organization sub page link.
-func (org *Organization) OrganisationLink() string {
-	return org.AsUser().OrganisationLink()
-}
-
-// ShortName ellipses username to length
-func (org *Organization) ShortName(length int) string {
-	return org.AsUser().ShortName(length)
-}
-
-// HomeLink returns the user or organization home page link.
-func (org *Organization) HomeLink() string {
-	return org.AsUser().HomeLink()
-}
-
-// 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 (org *Organization) CanCreateRepo() bool {
-	return org.AsUser().CanCreateRepo()
-}
-
-// FindOrgMembersOpts represensts find org members conditions
-type FindOrgMembersOpts struct {
-	db.ListOptions
-	OrgID      int64
-	PublicOnly bool
-}
-
-// CountOrgMembers counts the organization's members
-func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) {
-	sess := db.GetEngine(db.DefaultContext).Where("org_id=?", opts.OrgID)
-	if opts.PublicOnly {
-		sess.And("is_public = ?", true)
-	}
-	return sess.Count(new(OrgUser))
-}
-
-// FindOrgMembers loads organization members according conditions
-func FindOrgMembers(opts *FindOrgMembersOpts) (user_model.UserList, map[int64]bool, error) {
-	ous, err := GetOrgUsersByOrgID(opts)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	ids := make([]int64, len(ous))
-	idsIsPublic := make(map[int64]bool, len(ous))
-	for i, ou := range ous {
-		ids[i] = ou.UID
-		idsIsPublic[ou.UID] = ou.IsPublic
-	}
-
-	users, err := user_model.GetUsersByIDs(ids)
-	if err != nil {
-		return nil, nil, err
-	}
-	return users, idsIsPublic, nil
-}
-
-// AddMember adds new member to organization.
-func (org *Organization) AddMember(uid int64) error {
-	return AddOrgUser(org.ID, uid)
-}
-
-// RemoveMember removes member from organization.
-func (org *Organization) RemoveMember(uid int64) error {
-	return RemoveOrgUser(org.ID, uid)
-}
-
-func (org *Organization) removeOrgRepo(e db.Engine, repoID int64) error {
-	return removeOrgRepo(e, org.ID, repoID)
-}
-
-// RemoveOrgRepo removes all team-repository relations of organization.
-func (org *Organization) RemoveOrgRepo(repoID int64) error {
-	return org.removeOrgRepo(db.GetEngine(db.DefaultContext), repoID)
-}
-
-// AsUser returns the org as user object
-func (org *Organization) AsUser() *user_model.User {
-	return (*user_model.User)(org)
-}
-
-// DisplayName returns full name if it's not empty,
-// returns username otherwise.
-func (org *Organization) DisplayName() string {
-	return org.AsUser().DisplayName()
-}
-
-// CustomAvatarRelativePath returns user custom avatar relative path.
-func (org *Organization) CustomAvatarRelativePath() string {
-	return org.Avatar
-}
-
-// CreateOrganization creates record of a new organization.
-func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
-	if !owner.CanCreateOrganization() {
-		return ErrUserNotAllowedCreateOrg{}
-	}
-
-	if err = user_model.IsUsableUsername(org.Name); err != nil {
-		return err
-	}
-
-	isExist, err := user_model.IsUserExist(0, org.Name)
-	if err != nil {
-		return err
-	} else if isExist {
-		return user_model.ErrUserAlreadyExist{Name: org.Name}
-	}
-
-	org.LowerName = strings.ToLower(org.Name)
-	if org.Rands, err = user_model.GetUserSalt(); err != nil {
-		return err
-	}
-	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 = user_model.UserTypeOrganization
-
-	ctx, committer, err := db.TxContext()
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
-		return err
-	}
-
-	if err = db.Insert(ctx, org); err != nil {
-		return fmt.Errorf("insert organization: %v", err)
-	}
-	if err = user_model.GenerateRandomAvatarCtx(ctx, org.AsUser()); err != nil {
-		return fmt.Errorf("generate random avatar: %v", err)
-	}
-
-	// Add initial creator to organization and owner team.
-	if err = db.Insert(ctx, &OrgUser{
-		UID:   owner.ID,
-		OrgID: org.ID,
-	}); err != nil {
-		return fmt.Errorf("insert org-user relation: %v", err)
-	}
-
-	// Create default owner team.
-	t := &Team{
-		OrgID:                   org.ID,
-		LowerName:               strings.ToLower(ownerTeamName),
-		Name:                    ownerTeamName,
-		AccessMode:              perm.AccessModeOwner,
-		NumMembers:              1,
-		IncludesAllRepositories: true,
-		CanCreateOrgRepo:        true,
-	}
-	if err = db.Insert(ctx, t); err != nil {
-		return fmt.Errorf("insert owner team: %v", err)
-	}
-
-	// insert units for team
-	units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
-	for _, tp := range unit.AllRepoUnitTypes {
-		units = append(units, TeamUnit{
-			OrgID:  org.ID,
-			TeamID: t.ID,
-			Type:   tp,
-		})
-	}
-
-	if err = db.Insert(ctx, &units); err != nil {
-		return err
-	}
-
-	if err = db.Insert(ctx, &TeamUser{
-		UID:    owner.ID,
-		OrgID:  org.ID,
-		TeamID: t.ID,
-	}); err != nil {
-		return fmt.Errorf("insert team-user relation: %v", err)
-	}
-
-	return committer.Commit()
-}
-
-// GetOrgByName returns organization by given name.
-func GetOrgByName(name string) (*Organization, error) {
-	if len(name) == 0 {
-		return nil, ErrOrgNotExist{0, name}
-	}
-	u := &Organization{
-		LowerName: strings.ToLower(name),
-		Type:      user_model.UserTypeOrganization,
-	}
-	has, err := db.GetEngine(db.DefaultContext).Get(u)
-	if err != nil {
-		return nil, err
-	} else if !has {
-		return nil, ErrOrgNotExist{0, name}
-	}
-	return u, nil
-}
-
-// CountOrganizations returns number of organizations.
-func CountOrganizations() int64 {
-	count, _ := db.GetEngine(db.DefaultContext).
-		Where("type=1").
-		Count(new(Organization))
-	return count
-}
-
-// DeleteOrganization deletes models associated to an organization.
-func DeleteOrganization(ctx context.Context, org *Organization) error {
-	if org.Type != user_model.UserTypeOrganization {
-		return fmt.Errorf("%s is a user not an organization", org.Name)
-	}
-
-	if err := db.DeleteBeans(ctx,
-		&Team{OrgID: org.ID},
-		&OrgUser{OrgID: org.ID},
-		&TeamUser{OrgID: org.ID},
-		&TeamUnit{OrgID: org.ID},
-	); err != nil {
-		return fmt.Errorf("deleteBeans: %v", err)
-	}
-
-	if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
-		return fmt.Errorf("Delete: %v", err)
-	}
-
-	return nil
-}
-
-// ________                ____ ___
-// \_____  \_______  ____ |    |   \______ ___________
-//  /   |   \_  __ \/ ___\|    |   /  ___// __ \_  __ \
-// /    |    \  | \/ /_/  >    |  /\___ \\  ___/|  | \/
-// \_______  /__|  \___  /|______//____  >\___  >__|
-//         \/     /_____/              \/     \/
-
-// OrgUser represents an organization-user relation.
-type OrgUser struct {
-	ID       int64 `xorm:"pk autoincr"`
-	UID      int64 `xorm:"INDEX UNIQUE(s)"`
-	OrgID    int64 `xorm:"INDEX UNIQUE(s)"`
-	IsPublic bool  `xorm:"INDEX"`
-}
-
-func init() {
-	db.RegisterModel(new(OrgUser))
-}
-
-func isOrganizationOwner(e db.Engine, orgID, uid int64) (bool, error) {
-	ownerTeam, err := getOwnerTeam(e, orgID)
-	if err != nil {
-		if IsErrTeamNotExist(err) {
-			log.Error("Organization does not have owner team: %d", orgID)
-			return false, nil
-		}
-		return false, err
-	}
-	return isTeamMember(e, orgID, ownerTeam.ID, uid)
-}
-
-// IsOrganizationOwner returns true if given user is in the owner team.
-func IsOrganizationOwner(orgID, uid int64) (bool, error) {
-	return isOrganizationOwner(db.GetEngine(db.DefaultContext), orgID, uid)
-}
-
-// IsOrganizationMember returns true if given user is member of organization.
-func IsOrganizationMember(orgID, uid int64) (bool, error) {
-	return isOrganizationMember(db.GetEngine(db.DefaultContext), orgID, uid)
-}
-
-func isOrganizationMember(e db.Engine, orgID, uid int64) (bool, error) {
-	return e.
-		Where("uid=?", uid).
-		And("org_id=?", orgID).
-		Table("org_user").
-		Exist()
-}
-
-// IsPublicMembership returns true if given user public his/her membership.
-func IsPublicMembership(orgID, uid int64) (bool, error) {
-	return db.GetEngine(db.DefaultContext).
-		Where("uid=?", uid).
-		And("org_id=?", orgID).
-		And("is_public=?", true).
-		Table("org_user").
-		Exist()
-}
-
-// CanCreateOrgRepo returns true if user can create repo in organization
-func CanCreateOrgRepo(orgID, uid int64) (bool, error) {
-	if owner, err := IsOrganizationOwner(orgID, uid); owner || err != nil {
-		return owner, err
-	}
-	return db.GetEngine(db.DefaultContext).
-		Where(builder.Eq{"team.can_create_org_repo": true}).
-		Join("INNER", "team_user", "team_user.team_id = team.id").
-		And("team_user.uid = ?", uid).
-		And("team_user.org_id = ?", orgID).
-		Exist(new(Team))
-}
-
-// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
-func (org *Organization) GetOrgUserMaxAuthorizeLevel(uid int64) (perm.AccessMode, error) {
-	var authorize perm.AccessMode
-	_, err := db.GetEngine(db.DefaultContext).
-		Select("max(team.authorize)").
-		Table("team").
-		Join("INNER", "team_user", "team_user.team_id = team.id").
-		Where("team_user.uid = ?", uid).
-		And("team_user.org_id = ?", org.ID).
-		Get(&authorize)
-	return authorize, err
-}
-
-// GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization
-func GetUsersWhoCanCreateOrgRepo(orgID int64) ([]*user_model.User, error) {
-	return getUsersWhoCanCreateOrgRepo(db.GetEngine(db.DefaultContext), orgID)
-}
-
-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").
-		Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
-		And("team_user.org_id = ?", orgID).Asc("`user`.name").Find(&users)
-}
-
 // MinimalOrg represents a simple orgnization with only needed columns
-type MinimalOrg = Organization
+type MinimalOrg = organization.Organization
 
 // GetUserOrgsList returns one user's all orgs list
 func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
@@ -486,8 +58,8 @@ func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
 		GroupBy(groupByStr)
 
 	type OrgCount struct {
-		Organization `xorm:"extends"`
-		OrgCount     int
+		organization.Organization `xorm:"extends"`
+		OrgCount                  int
 	}
 
 	orgCounts := make([]*OrgCount, 0, 10)
@@ -507,239 +79,8 @@ func GetUserOrgsList(user *user_model.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
-	UserID         int64
-	IncludePrivate bool
-}
-
-func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
-	cond := builder.Eq{"uid": userID}
-	if !includePrivate {
-		cond["is_public"] = true
-	}
-	return builder.Select("org_id").From("org_user").Where(cond)
-}
-
-func (opts FindOrgOptions) toConds() builder.Cond {
-	cond := builder.NewCond()
-	if opts.UserID > 0 {
-		cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
-	}
-	if !opts.IncludePrivate {
-		cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
-	}
-	return cond
-}
-
-// FindOrgs returns a list of organizations according given conditions
-func FindOrgs(opts FindOrgOptions) ([]*Organization, error) {
-	orgs := make([]*Organization, 0, 10)
-	sess := db.GetEngine(db.DefaultContext).
-		Where(opts.toConds()).
-		Asc("`user`.name")
-	if opts.Page > 0 && opts.PageSize > 0 {
-		sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1))
-	}
-	return orgs, sess.Find(&orgs)
-}
-
-// CountOrgs returns total count organizations according options
-func CountOrgs(opts FindOrgOptions) (int64, error) {
-	return db.GetEngine(db.DefaultContext).
-		Where(opts.toConds()).
-		Count(new(user_model.User))
-}
-
-func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*Organization, error) {
-	orgs := make([]*Organization, 0, 10)
-	return orgs, sess.
-		Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id").
-		Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
-		Where("`team_user`.uid=?", userID).
-		And("`team`.authorize=?", perm.AccessModeOwner).
-		Asc("`user`.name").
-		Find(&orgs)
-}
-
-// HasOrgOrUserVisible tells if the given user can see the given org or user
-func HasOrgOrUserVisible(org, user *user_model.User) bool {
-	return hasOrgOrUserVisible(db.GetEngine(db.DefaultContext), org, user)
-}
-
-func hasOrgOrUserVisible(e db.Engine, orgOrUser, user *user_model.User) bool {
-	// Not SignedUser
-	if user == nil {
-		return orgOrUser.Visibility == structs.VisibleTypePublic
-	}
-
-	if user.IsAdmin || orgOrUser.ID == user.ID {
-		return true
-	}
-
-	if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(e, user.ID) {
-		return false
-	}
-	return true
-}
-
-// HasOrgsVisible tells if the given user can see at least one of the orgs provided
-func HasOrgsVisible(orgs []*Organization, user *user_model.User) bool {
-	if len(orgs) == 0 {
-		return false
-	}
-
-	for _, org := range orgs {
-		if HasOrgOrUserVisible(org.AsUser(), user) {
-			return true
-		}
-	}
-	return false
-}
-
-// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
-func GetOwnedOrgsByUserID(userID int64) ([]*Organization, error) {
-	return getOwnedOrgsByUserID(db.GetEngine(db.DefaultContext), userID)
-}
-
-// GetOwnedOrgsByUserIDDesc returns a list of organizations are owned by
-// given user ID, ordered descending by the given condition.
-func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*Organization, error) {
-	return getOwnedOrgsByUserID(db.GetEngine(db.DefaultContext).Desc(desc), userID)
-}
-
-// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
-// are allowed to create repos.
-func GetOrgsCanCreateRepoByUserID(userID int64) ([]*Organization, error) {
-	orgs := make([]*Organization, 0, 10)
-
-	return orgs, db.GetEngine(db.DefaultContext).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
-		Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
-		Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
-		Where(builder.Eq{"`team_user`.uid": userID}).
-		And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
-		Asc("`user`.name").
-		Find(&orgs)
-}
-
-// GetOrgUsersByUserID returns all organization-user relations by user ID.
-func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) {
-	ous := make([]*OrgUser, 0, 10)
-	sess := db.GetEngine(db.DefaultContext).
-		Join("LEFT", "`user`", "`org_user`.org_id=`user`.id").
-		Where("`org_user`.uid=?", uid)
-	if !opts.All {
-		// Only show public organizations
-		sess.And("is_public=?", true)
-	}
-
-	if opts.PageSize != 0 {
-		sess = db.SetSessionPagination(sess, opts)
-	}
-
-	err := sess.
-		Asc("`user`.name").
-		Find(&ous)
-	return ous, err
-}
-
-// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
-func GetOrgUsersByOrgID(opts *FindOrgMembersOpts) ([]*OrgUser, error) {
-	return getOrgUsersByOrgID(db.GetEngine(db.DefaultContext), opts)
-}
-
-func getOrgUsersByOrgID(e db.Engine, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
-	sess := e.Where("org_id=?", opts.OrgID)
-	if opts.PublicOnly {
-		sess.And("is_public = ?", true)
-	}
-	if opts.ListOptions.PageSize > 0 {
-		sess = db.SetSessionPagination(sess, opts)
-
-		ous := make([]*OrgUser, 0, opts.PageSize)
-		return ous, sess.Find(&ous)
-	}
-
-	var ous []*OrgUser
-	return ous, sess.Find(&ous)
-}
-
-// ChangeOrgUserStatus changes public or private membership status.
-func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
-	ou := new(OrgUser)
-	has, err := db.GetEngine(db.DefaultContext).
-		Where("uid=?", uid).
-		And("org_id=?", orgID).
-		Get(ou)
-	if err != nil {
-		return err
-	} else if !has {
-		return nil
-	}
-
-	ou.IsPublic = public
-	_, err = db.GetEngine(db.DefaultContext).ID(ou.ID).Cols("is_public").Update(ou)
-	return err
-}
-
-// AddOrgUser adds new user to given organization.
-func AddOrgUser(orgID, uid int64) error {
-	isAlreadyMember, err := IsOrganizationMember(orgID, uid)
-	if err != nil || isAlreadyMember {
-		return err
-	}
-
-	ctx, committer, err := db.TxContext()
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	ou := &OrgUser{
-		UID:      uid,
-		OrgID:    orgID,
-		IsPublic: setting.Service.DefaultOrgMemberVisible,
-	}
-
-	if err := db.Insert(ctx, ou); err != nil {
-		return err
-	} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
-		return err
-	}
-
-	return committer.Commit()
-}
-
-// GetOrgByIDCtx returns the user object by given ID if exists.
-func GetOrgByIDCtx(ctx context.Context, id int64) (*Organization, error) {
-	u := new(Organization)
-	has, err := db.GetEngine(ctx).ID(id).Get(u)
-	if err != nil {
-		return nil, err
-	} else if !has {
-		return nil, user_model.ErrUserNotExist{
-			UID:   id,
-			Name:  "",
-			KeyID: 0,
-		}
-	}
-	return u, nil
-}
-
-// GetOrgByID returns the user object by given ID if exists.
-func GetOrgByID(id int64) (*Organization, error) {
-	return GetOrgByIDCtx(db.DefaultContext, id)
-}
-
 func removeOrgUser(ctx context.Context, orgID, userID int64) error {
-	ou := new(OrgUser)
+	ou := new(organization.OrgUser)
 
 	sess := db.GetEngine(ctx)
 
@@ -753,25 +94,25 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
 		return nil
 	}
 
-	org, err := GetOrgByIDCtx(ctx, orgID)
+	org, err := organization.GetOrgByIDCtx(ctx, orgID)
 	if err != nil {
 		return fmt.Errorf("GetUserByID [%d]: %v", orgID, err)
 	}
 
 	// Check if the user to delete is the last member in owner team.
-	if isOwner, err := isOrganizationOwner(sess, orgID, userID); err != nil {
+	if isOwner, err := organization.IsOrganizationOwner(ctx, orgID, userID); err != nil {
 		return err
 	} else if isOwner {
-		t, err := org.getOwnerTeam(sess)
+		t, err := organization.GetOwnerTeam(ctx, org.ID)
 		if err != nil {
 			return err
 		}
 		if t.NumMembers == 1 {
-			if err := t.getMembers(sess); err != nil {
+			if err := t.GetMembersCtx(ctx); err != nil {
 				return err
 			}
 			if t.Members[0].ID == userID {
-				return ErrLastOrgOwner{UID: userID}
+				return organization.ErrLastOrgOwner{UID: userID}
 			}
 		}
 	}
@@ -783,7 +124,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
 	}
 
 	// Delete all repository accesses and unwatch them.
-	env, err := org.accessibleReposEnv(sess, userID)
+	env, err := organization.AccessibleReposEnv(ctx, org, userID)
 	if err != nil {
 		return fmt.Errorf("AccessibleReposEnv: %v", err)
 	}
@@ -807,7 +148,7 @@ func removeOrgUser(ctx context.Context, orgID, userID int64) error {
 	}
 
 	// Delete member in his/her teams.
-	teams, err := getUserOrgTeams(sess, org.ID, userID)
+	teams, err := organization.GetUserOrgTeams(ctx, org.ID, userID)
 	if err != nil {
 		return err
 	}
@@ -832,233 +173,3 @@ func RemoveOrgUser(orgID, userID int64) error {
 	}
 	return committer.Commit()
 }
-
-func removeOrgRepo(e db.Engine, orgID, repoID int64) error {
-	teamRepos := make([]*TeamRepo, 0, 10)
-	if err := e.Find(&teamRepos, &TeamRepo{OrgID: orgID, RepoID: repoID}); err != nil {
-		return err
-	}
-
-	if len(teamRepos) == 0 {
-		return nil
-	}
-
-	if _, err := e.Delete(&TeamRepo{
-		OrgID:  orgID,
-		RepoID: repoID,
-	}); err != nil {
-		return err
-	}
-
-	teamIDs := make([]int64, len(teamRepos))
-	for i, teamRepo := range teamRepos {
-		teamIDs[i] = teamRepo.TeamID
-	}
-
-	_, err := e.Decr("num_repos").In("id", teamIDs).Update(new(Team))
-	return err
-}
-
-func (org *Organization) getUserTeams(e db.Engine, userID int64, cols ...string) ([]*Team, error) {
-	teams := make([]*Team, 0, org.NumTeams)
-	return teams, e.
-		Where("`team_user`.org_id = ?", org.ID).
-		Join("INNER", "team_user", "`team_user`.team_id = team.id").
-		Join("INNER", "`user`", "`user`.id=team_user.uid").
-		And("`team_user`.uid = ?", userID).
-		Asc("`user`.name").
-		Cols(cols...).
-		Find(&teams)
-}
-
-func (org *Organization) getUserTeamIDs(e db.Engine, userID int64) ([]int64, error) {
-	teamIDs := make([]int64, 0, org.NumTeams)
-	return teamIDs, e.
-		Table("team").
-		Cols("team.id").
-		Where("`team_user`.org_id = ?", org.ID).
-		Join("INNER", "team_user", "`team_user`.team_id = team.id").
-		And("`team_user`.uid = ?", userID).
-		Find(&teamIDs)
-}
-
-// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
-func (org *Organization) TeamsWithAccessToRepo(repoID int64, mode perm.AccessMode) ([]*Team, error) {
-	return GetTeamsWithAccessToRepo(org.ID, repoID, mode)
-}
-
-// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
-func (org *Organization) GetUserTeamIDs(userID int64) ([]int64, error) {
-	return org.getUserTeamIDs(db.GetEngine(db.DefaultContext), userID)
-}
-
-// GetUserTeams returns all teams that belong to user,
-// and that the user has joined.
-func (org *Organization) GetUserTeams(userID int64) ([]*Team, error) {
-	return org.getUserTeams(db.GetEngine(db.DefaultContext), userID)
-}
-
-// AccessibleReposEnvironment operations involving the repositories that are
-// accessible to a particular user
-type AccessibleReposEnvironment interface {
-	CountRepos() (int64, error)
-	RepoIDs(page, pageSize int) ([]int64, error)
-	Repos(page, pageSize int) ([]*repo_model.Repository, error)
-	MirrorRepos() ([]*repo_model.Repository, error)
-	AddKeyword(keyword string)
-	SetSort(db.SearchOrderBy)
-}
-
-type accessibleReposEnv struct {
-	org     *Organization
-	user    *user_model.User
-	team    *Team
-	teamIDs []int64
-	e       db.Engine
-	keyword string
-	orderBy db.SearchOrderBy
-}
-
-// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
-// that are accessible to the specified user.
-func (org *Organization) AccessibleReposEnv(userID int64) (AccessibleReposEnvironment, error) {
-	return org.accessibleReposEnv(db.GetEngine(db.DefaultContext), userID)
-}
-
-func (org *Organization) accessibleReposEnv(e db.Engine, userID int64) (AccessibleReposEnvironment, error) {
-	var user *user_model.User
-
-	if userID > 0 {
-		u, err := user_model.GetUserByIDEngine(e, userID)
-		if err != nil {
-			return nil, err
-		}
-		user = u
-	}
-
-	teamIDs, err := org.getUserTeamIDs(e, userID)
-	if err != nil {
-		return nil, err
-	}
-	return &accessibleReposEnv{
-		org:     org,
-		user:    user,
-		teamIDs: teamIDs,
-		e:       e,
-		orderBy: db.SearchOrderByRecentUpdated,
-	}, nil
-}
-
-// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
-// that are accessible to the specified team.
-func (org *Organization) AccessibleTeamReposEnv(team *Team) AccessibleReposEnvironment {
-	return &accessibleReposEnv{
-		org:     org,
-		team:    team,
-		e:       db.GetEngine(db.DefaultContext),
-		orderBy: db.SearchOrderByRecentUpdated,
-	}
-}
-
-func (env *accessibleReposEnv) cond() builder.Cond {
-	cond := builder.NewCond()
-	if env.team != nil {
-		cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
-	} else {
-		if env.user == nil || !env.user.IsRestricted {
-			cond = cond.Or(builder.Eq{
-				"`repository`.owner_id":   env.org.ID,
-				"`repository`.is_private": false,
-			})
-		}
-		if len(env.teamIDs) > 0 {
-			cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
-		}
-	}
-	if env.keyword != "" {
-		cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
-	}
-	return cond
-}
-
-func (env *accessibleReposEnv) CountRepos() (int64, error) {
-	repoCount, err := env.e.
-		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
-		Where(env.cond()).
-		Distinct("`repository`.id").
-		Count(&repo_model.Repository{})
-	if err != nil {
-		return 0, fmt.Errorf("count user repositories in organization: %v", err)
-	}
-	return repoCount, nil
-}
-
-func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
-	if page <= 0 {
-		page = 1
-	}
-
-	repoIDs := make([]int64, 0, pageSize)
-	return repoIDs, env.e.
-		Table("repository").
-		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
-		Where(env.cond()).
-		GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
-		OrderBy(string(env.orderBy)).
-		Limit(pageSize, (page-1)*pageSize).
-		Cols("`repository`.id").
-		Find(&repoIDs)
-}
-
-func (env *accessibleReposEnv) Repos(page, pageSize int) ([]*repo_model.Repository, error) {
-	repoIDs, err := env.RepoIDs(page, pageSize)
-	if err != nil {
-		return nil, fmt.Errorf("GetUserRepositoryIDs: %v", err)
-	}
-
-	repos := make([]*repo_model.Repository, 0, len(repoIDs))
-	if len(repoIDs) == 0 {
-		return repos, nil
-	}
-
-	return repos, env.e.
-		In("`repository`.id", repoIDs).
-		OrderBy(string(env.orderBy)).
-		Find(&repos)
-}
-
-func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
-	repoIDs := make([]int64, 0, 10)
-	return repoIDs, env.e.
-		Table("repository").
-		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
-		Where(env.cond()).
-		GroupBy("`repository`.id, `repository`.updated_unix").
-		OrderBy(string(env.orderBy)).
-		Cols("`repository`.id").
-		Find(&repoIDs)
-}
-
-func (env *accessibleReposEnv) MirrorRepos() ([]*repo_model.Repository, error) {
-	repoIDs, err := env.MirrorRepoIDs()
-	if err != nil {
-		return nil, fmt.Errorf("MirrorRepoIDs: %v", err)
-	}
-
-	repos := make([]*repo_model.Repository, 0, len(repoIDs))
-	if len(repoIDs) == 0 {
-		return repos, nil
-	}
-
-	return repos, env.e.
-		In("`repository`.id", repoIDs).
-		Find(&repos)
-}
-
-func (env *accessibleReposEnv) AddKeyword(keyword string) {
-	env.keyword = keyword
-}
-
-func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
-	env.orderBy = orderBy
-}
diff --git a/models/org_team.go b/models/org_team.go
index faee23f4f8..cf810cad33 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -9,288 +9,24 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"sort"
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
-	"code.gitea.io/gitea/models/perm"
+	"code.gitea.io/gitea/models/organization"
 	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/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 
 	"xorm.io/builder"
 )
 
-const ownerTeamName = "Owners"
-
-// Team represents a organization team.
-type Team struct {
-	ID                      int64 `xorm:"pk autoincr"`
-	OrgID                   int64 `xorm:"INDEX"`
-	LowerName               string
-	Name                    string
-	Description             string
-	AccessMode              perm.AccessMode          `xorm:"'authorize'"`
-	Repos                   []*repo_model.Repository `xorm:"-"`
-	Members                 []*user_model.User       `xorm:"-"`
-	NumRepos                int
-	NumMembers              int
-	Units                   []*TeamUnit `xorm:"-"`
-	IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"`
-	CanCreateOrgRepo        bool        `xorm:"NOT NULL DEFAULT false"`
-}
-
-func init() {
-	db.RegisterModel(new(Team))
-	db.RegisterModel(new(TeamUser))
-	db.RegisterModel(new(TeamRepo))
-	db.RegisterModel(new(TeamUnit))
-}
-
-// SearchOrgTeamOptions holds the search options
-type SearchOrgTeamOptions struct {
-	db.ListOptions
-	Keyword     string
-	OrgID       int64
-	IncludeDesc bool
-}
-
-// GetUserTeamOptions holds the search options.
-type GetUserTeamOptions struct {
-	db.ListOptions
-	UserID int64
-}
-
-// SearchMembersOptions holds the search options
-type SearchMembersOptions struct {
-	db.ListOptions
-}
-
-// GetUserTeams search for org teams. Caller is responsible to check permissions.
-func GetUserTeams(opts *GetUserTeamOptions) ([]*Team, int64, error) {
-	if opts.Page <= 0 {
-		opts.Page = 1
-	}
-	if opts.PageSize == 0 {
-		// Default limit
-		opts.PageSize = 10
-	}
-
-	sess := db.GetEngine(db.DefaultContext)
-
-	sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id").
-		And("team_user.uid=?", opts.UserID)
-
-	count, err := sess.
-		Count(new(Team))
-	if err != nil {
-		return nil, 0, err
-	}
-
-	if opts.PageSize == -1 {
-		opts.PageSize = int(count)
-	} else {
-		sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
-	}
-
-	sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id").
-		And("team_user.uid=?", opts.UserID)
-
-	teams := make([]*Team, 0, opts.PageSize)
-	if err = sess.
-		OrderBy("lower_name").
-		Find(&teams); err != nil {
-		return nil, 0, err
-	}
-
-	return teams, count, nil
-}
-
-// SearchOrgTeams search for org teams. Caller is responsible to check permissions.
-func SearchOrgTeams(opts *SearchOrgTeamOptions) ([]*Team, int64, error) {
-	if opts.Page <= 0 {
-		opts.Page = 1
-	}
-	if opts.PageSize == 0 {
-		// Default limit
-		opts.PageSize = 10
-	}
-
-	cond := builder.NewCond()
-
-	if len(opts.Keyword) > 0 {
-		lowerKeyword := strings.ToLower(opts.Keyword)
-		var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword}
-		if opts.IncludeDesc {
-			keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword})
-		}
-		cond = cond.And(keywordCond)
-	}
-
-	cond = cond.And(builder.Eq{"org_id": opts.OrgID})
-
-	sess := db.GetEngine(db.DefaultContext)
-
-	count, err := sess.
-		Where(cond).
-		Count(new(Team))
-	if err != nil {
-		return nil, 0, err
-	}
-
-	sess = sess.Where(cond)
-	if opts.PageSize == -1 {
-		opts.PageSize = int(count)
-	} else {
-		sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
-	}
-
-	teams := make([]*Team, 0, opts.PageSize)
-	if err = sess.
-		OrderBy("lower_name").
-		Find(&teams); err != nil {
-		return nil, 0, err
-	}
-
-	return teams, count, nil
-}
-
-// ColorFormat provides a basic color format for a Team
-func (t *Team) ColorFormat(s fmt.State) {
-	if t == nil {
-		log.ColorFprintf(s, "%d:%s (OrgID: %d) %-v",
-			log.NewColoredIDValue(0),
-			"<nil>",
-			log.NewColoredIDValue(0),
-			0)
-		return
-	}
-	log.ColorFprintf(s, "%d:%s (OrgID: %d) %-v",
-		log.NewColoredIDValue(t.ID),
-		t.Name,
-		log.NewColoredIDValue(t.OrgID),
-		t.AccessMode)
-}
-
-// GetUnits return a list of available units for a team
-func (t *Team) GetUnits() error {
-	return t.getUnits(db.GetEngine(db.DefaultContext))
-}
-
-func (t *Team) getUnits(e db.Engine) (err error) {
-	if t.Units != nil {
-		return nil
-	}
-
-	t.Units, err = getUnitsByTeamID(e, t.ID)
-	return err
-}
-
-// GetUnitNames returns the team units names
-func (t *Team) GetUnitNames() (res []string) {
-	if t.AccessMode >= perm.AccessModeAdmin {
-		return unit.AllUnitKeyNames()
-	}
-
-	for _, u := range t.Units {
-		res = append(res, unit.Units[u.Type].NameKey)
-	}
-	return
-}
-
-// GetUnitsMap returns the team units permissions
-func (t *Team) GetUnitsMap() map[string]string {
-	m := make(map[string]string)
-	if t.AccessMode >= perm.AccessModeAdmin {
-		for _, u := range unit.Units {
-			m[u.NameKey] = t.AccessMode.String()
-		}
-	} else {
-		for _, u := range t.Units {
-			m[u.Unit().NameKey] = u.AccessMode.String()
-		}
-	}
-	return m
-}
-
-// IsOwnerTeam returns true if team is owner team.
-func (t *Team) IsOwnerTeam() bool {
-	return t.Name == ownerTeamName
-}
-
-// IsMember returns true if given user is a member of team.
-func (t *Team) IsMember(userID int64) bool {
-	isMember, err := IsTeamMember(t.OrgID, t.ID, userID)
-	if err != nil {
-		log.Error("IsMember: %v", err)
-		return false
-	}
-	return isMember
-}
-
-func (t *Team) getRepositories(e db.Engine) error {
-	if t.Repos != nil {
-		return nil
-	}
-	return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id").
-		Where("team_repo.team_id=?", t.ID).
-		OrderBy("repository.name").
-		Find(&t.Repos)
-}
-
-// GetRepositories returns paginated repositories in team of organization.
-func (t *Team) GetRepositories(opts *SearchOrgTeamOptions) error {
-	if opts.Page == 0 {
-		return t.getRepositories(db.GetEngine(db.DefaultContext))
-	}
-
-	return t.getRepositories(db.GetPaginatedSession(opts))
-}
-
-func (t *Team) getMembers(e db.Engine) (err error) {
-	t.Members, err = getTeamMembers(e, t.ID)
-	return err
-}
-
-// GetMembers returns paginated members in team of organization.
-func (t *Team) GetMembers(opts *SearchMembersOptions) (err error) {
-	if opts.Page == 0 {
-		return t.getMembers(db.GetEngine(db.DefaultContext))
-	}
-
-	return t.getMembers(db.GetPaginatedSession(opts))
-}
-
-// AddMember adds new membership of the team to the organization,
-// the user will have membership to the organization automatically when needed.
-func (t *Team) AddMember(userID int64) error {
-	return AddTeamMember(t, userID)
-}
-
-// RemoveMember removes member from team of organization.
-func (t *Team) RemoveMember(userID int64) error {
-	return RemoveTeamMember(t, userID)
-}
-
-func (t *Team) hasRepository(e db.Engine, repoID int64) bool {
-	return hasTeamRepo(e, t.OrgID, t.ID, repoID)
-}
-
-// HasRepository returns true if given repository belong to team.
-func (t *Team) HasRepository(repoID int64) bool {
-	return t.hasRepository(db.GetEngine(db.DefaultContext), repoID)
-}
-
-func (t *Team) addRepository(ctx context.Context, repo *repo_model.Repository) (err error) {
-	e := db.GetEngine(ctx)
-	if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
+func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
+	if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
 		return err
 	}
 
-	if _, err = e.Incr("num_repos").ID(t.ID).Update(new(Team)); err != nil {
+	if _, err = db.GetEngine(ctx).Incr("num_repos").ID(t.ID).Update(new(organization.Team)); err != nil {
 		return fmt.Errorf("update team: %v", err)
 	}
 
@@ -302,7 +38,7 @@ func (t *Team) addRepository(ctx context.Context, repo *repo_model.Repository) (
 
 	// Make all team members watch this repo if enabled in global settings
 	if setting.Service.AutoWatchNewRepos {
-		if err = t.getMembers(e); err != nil {
+		if err = t.GetMembersCtx(ctx); err != nil {
 			return fmt.Errorf("getMembers: %v", err)
 		}
 		for _, u := range t.Members {
@@ -317,7 +53,7 @@ func (t *Team) addRepository(ctx context.Context, repo *repo_model.Repository) (
 
 // addAllRepositories adds all repositories to the team.
 // If the team already has some repositories they will be left unchanged.
-func (t *Team) addAllRepositories(ctx context.Context) error {
+func addAllRepositories(ctx context.Context, t *organization.Team) error {
 	var orgRepos []repo_model.Repository
 	e := db.GetEngine(ctx)
 	if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil {
@@ -325,8 +61,8 @@ func (t *Team) addAllRepositories(ctx context.Context) error {
 	}
 
 	for _, repo := range orgRepos {
-		if !t.hasRepository(e, repo.ID) {
-			if err := t.addRepository(ctx, &repo); err != nil {
+		if !hasRepository(ctx, t, repo.ID) {
+			if err := addRepository(ctx, t, &repo); err != nil {
 				return fmt.Errorf("addRepository: %v", err)
 			}
 		}
@@ -336,14 +72,14 @@ func (t *Team) addAllRepositories(ctx context.Context) error {
 }
 
 // AddAllRepositories adds all repositories to the team
-func (t *Team) AddAllRepositories() (err error) {
+func AddAllRepositories(t *organization.Team) (err error) {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return err
 	}
 	defer committer.Close()
 
-	if err = t.addAllRepositories(ctx); err != nil {
+	if err = addAllRepositories(ctx, t); err != nil {
 		return err
 	}
 
@@ -351,10 +87,10 @@ func (t *Team) AddAllRepositories() (err error) {
 }
 
 // AddRepository adds new repository to team of organization.
-func (t *Team) AddRepository(repo *repo_model.Repository) (err error) {
+func AddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
 	if repo.OwnerID != t.OrgID {
 		return errors.New("Repository does not belong to organization")
-	} else if t.HasRepository(repo.ID) {
+	} else if HasRepository(t, repo.ID) {
 		return nil
 	}
 
@@ -364,15 +100,20 @@ func (t *Team) AddRepository(repo *repo_model.Repository) (err error) {
 	}
 	defer committer.Close()
 
-	if err = t.addRepository(ctx, repo); err != nil {
+	if err = addRepository(ctx, t, repo); err != nil {
 		return err
 	}
 
 	return committer.Commit()
 }
 
+// HasRepository returns true if given repository belong to team.
+func HasRepository(t *organization.Team, repoID int64) bool {
+	return hasRepository(db.DefaultContext, t, repoID)
+}
+
 // RemoveAllRepositories removes all repositories from team and recalculates access
-func (t *Team) RemoveAllRepositories() (err error) {
+func RemoveAllRepositories(t *organization.Team) (err error) {
 	if t.IncludesAllRepositories {
 		return nil
 	}
@@ -383,7 +124,7 @@ func (t *Team) RemoveAllRepositories() (err error) {
 	}
 	defer committer.Close()
 
-	if err = t.removeAllRepositories(ctx); err != nil {
+	if err = removeAllRepositories(ctx, t); err != nil {
 		return err
 	}
 
@@ -392,7 +133,7 @@ func (t *Team) RemoveAllRepositories() (err error) {
 
 // removeAllRepositories removes all repositories from team and recalculates access
 // Note: Shall not be called if team includes all repositories
-func (t *Team) removeAllRepositories(ctx context.Context) (err error) {
+func removeAllRepositories(ctx context.Context, t *organization.Team) (err error) {
 	e := db.GetEngine(ctx)
 	// Delete all accesses.
 	for _, repo := range t.Repos {
@@ -423,7 +164,7 @@ func (t *Team) removeAllRepositories(ctx context.Context) (err error) {
 	// Delete team-repo
 	if _, err := e.
 		Where("team_id=?", t.ID).
-		Delete(new(TeamRepo)); err != nil {
+		Delete(new(organization.TeamRepo)); err != nil {
 		return err
 	}
 
@@ -435,11 +176,15 @@ func (t *Team) removeAllRepositories(ctx context.Context) (err error) {
 	return nil
 }
 
+func hasRepository(ctx context.Context, t *organization.Team, repoID int64) bool {
+	return organization.HasTeamRepo(ctx, t.OrgID, t.ID, repoID)
+}
+
 // removeRepository removes a repository from a team and recalculates access
 // Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
-func (t *Team) removeRepository(ctx context.Context, repo *repo_model.Repository, recalculate bool) (err error) {
+func removeRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository, recalculate bool) (err error) {
 	e := db.GetEngine(ctx)
-	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
+	if err = organization.RemoveTeamRepo(ctx, t.ID, repo.ID); err != nil {
 		return err
 	}
 
@@ -455,7 +200,7 @@ func (t *Team) removeRepository(ctx context.Context, repo *repo_model.Repository
 		}
 	}
 
-	teamUsers, err := getTeamUsersByTeamID(e, t.ID)
+	teamUsers, err := organization.GetTeamUsersByTeamID(ctx, t.ID)
 	if err != nil {
 		return fmt.Errorf("getTeamUsersByTeamID: %v", err)
 	}
@@ -482,8 +227,8 @@ func (t *Team) removeRepository(ctx context.Context, repo *repo_model.Repository
 
 // RemoveRepository removes repository from team of organization.
 // If the team shall include all repositories the request is ignored.
-func (t *Team) RemoveRepository(repoID int64) error {
-	if !t.HasRepository(repoID) {
+func RemoveRepository(t *organization.Team, repoID int64) error {
+	if !HasRepository(t, repoID) {
 		return nil
 	}
 
@@ -502,58 +247,21 @@ func (t *Team) RemoveRepository(repoID int64) error {
 	}
 	defer committer.Close()
 
-	if err = t.removeRepository(ctx, repo, true); err != nil {
+	if err = removeRepository(ctx, t, repo, true); err != nil {
 		return err
 	}
 
 	return committer.Commit()
 }
 
-// UnitEnabled returns if the team has the given unit type enabled
-func (t *Team) UnitEnabled(tp unit.Type) bool {
-	return t.unitEnabled(db.GetEngine(db.DefaultContext), tp)
-}
-
-func (t *Team) unitEnabled(e db.Engine, tp unit.Type) bool {
-	return t.unitAccessMode(e, tp) > perm.AccessModeNone
-}
-
-// UnitAccessMode returns if the team has the given unit type enabled
-func (t *Team) UnitAccessMode(tp unit.Type) perm.AccessMode {
-	return t.unitAccessMode(db.GetEngine(db.DefaultContext), tp)
-}
-
-func (t *Team) unitAccessMode(e db.Engine, tp unit.Type) perm.AccessMode {
-	if err := t.getUnits(e); err != nil {
-		log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
-	}
-
-	for _, unit := range t.Units {
-		if unit.Type == tp {
-			return unit.AccessMode
-		}
-	}
-	return perm.AccessModeNone
-}
-
-// IsUsableTeamName tests if a name could be as team name
-func IsUsableTeamName(name string) error {
-	switch name {
-	case "new":
-		return db.ErrNameReserved{Name: name}
-	default:
-		return nil
-	}
-}
-
 // NewTeam creates a record of new team.
 // It's caller's responsibility to assign organization ID.
-func NewTeam(t *Team) (err error) {
+func NewTeam(t *organization.Team) (err error) {
 	if len(t.Name) == 0 {
 		return errors.New("empty team name")
 	}
 
-	if err = IsUsableTeamName(t.Name); err != nil {
+	if err = organization.IsUsableTeamName(t.Name); err != nil {
 		return err
 	}
 
@@ -562,19 +270,19 @@ func NewTeam(t *Team) (err error) {
 		return err
 	}
 	if !has {
-		return ErrOrgNotExist{t.OrgID, ""}
+		return organization.ErrOrgNotExist{ID: t.OrgID}
 	}
 
 	t.LowerName = strings.ToLower(t.Name)
 	has, err = db.GetEngine(db.DefaultContext).
 		Where("org_id=?", t.OrgID).
 		And("lower_name=?", t.LowerName).
-		Get(new(Team))
+		Get(new(organization.Team))
 	if err != nil {
 		return err
 	}
 	if has {
-		return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
+		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
 	}
 
 	ctx, committer, err := db.TxContext()
@@ -599,7 +307,7 @@ func NewTeam(t *Team) (err error) {
 
 	// Add all repositories to the team if it has access to all of them.
 	if t.IncludesAllRepositories {
-		err = t.addAllRepositories(ctx)
+		err = addAllRepositories(ctx, t)
 		if err != nil {
 			return fmt.Errorf("addAllRepositories: %v", err)
 		}
@@ -612,81 +320,8 @@ func NewTeam(t *Team) (err error) {
 	return committer.Commit()
 }
 
-func getTeam(e db.Engine, orgID int64, name string) (*Team, error) {
-	t := &Team{
-		OrgID:     orgID,
-		LowerName: strings.ToLower(name),
-	}
-	has, err := e.Get(t)
-	if err != nil {
-		return nil, err
-	} else if !has {
-		return nil, ErrTeamNotExist{orgID, 0, name}
-	}
-	return t, nil
-}
-
-// GetTeam returns team by given team name and organization.
-func GetTeam(orgID int64, name string) (*Team, error) {
-	return getTeam(db.GetEngine(db.DefaultContext), orgID, name)
-}
-
-// GetTeamIDsByNames returns a slice of team ids corresponds to names.
-func GetTeamIDsByNames(orgID int64, names []string, ignoreNonExistent bool) ([]int64, error) {
-	ids := make([]int64, 0, len(names))
-	for _, name := range names {
-		u, err := GetTeam(orgID, name)
-		if err != nil {
-			if ignoreNonExistent {
-				continue
-			} else {
-				return nil, err
-			}
-		}
-		ids = append(ids, u.ID)
-	}
-	return ids, nil
-}
-
-// getOwnerTeam returns team by given team name and organization.
-func getOwnerTeam(e db.Engine, orgID int64) (*Team, error) {
-	return getTeam(e, orgID, ownerTeamName)
-}
-
-func getTeamByID(e db.Engine, teamID int64) (*Team, error) {
-	t := new(Team)
-	has, err := e.ID(teamID).Get(t)
-	if err != nil {
-		return nil, err
-	} else if !has {
-		return nil, ErrTeamNotExist{0, teamID, ""}
-	}
-	return t, nil
-}
-
-// GetTeamByID returns team by given ID.
-func GetTeamByID(teamID int64) (*Team, error) {
-	return getTeamByID(db.GetEngine(db.DefaultContext), teamID)
-}
-
-// GetTeamNamesByID returns team's lower name from a list of team ids.
-func GetTeamNamesByID(teamIDs []int64) ([]string, error) {
-	if len(teamIDs) == 0 {
-		return []string{}, nil
-	}
-
-	var teamNames []string
-	err := db.GetEngine(db.DefaultContext).Table("team").
-		Select("lower_name").
-		In("id", teamIDs).
-		Asc("name").
-		Find(&teamNames)
-
-	return teamNames, err
-}
-
 // UpdateTeam updates information of team.
-func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
+func UpdateTeam(t *organization.Team, authChanged, includeAllChanged bool) (err error) {
 	if len(t.Name) == 0 {
 		return errors.New("empty team name")
 	}
@@ -707,11 +342,11 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 		Where("org_id=?", t.OrgID).
 		And("lower_name=?", t.LowerName).
 		And("id!=?", t.ID).
-		Get(new(Team))
+		Get(new(organization.Team))
 	if err != nil {
 		return err
 	} else if has {
-		return ErrTeamAlreadyExist{t.OrgID, t.LowerName}
+		return organization.ErrTeamAlreadyExist{OrgID: t.OrgID, Name: t.LowerName}
 	}
 
 	if _, err = sess.ID(t.ID).Cols("name", "lower_name", "description",
@@ -727,7 +362,7 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 		// Delete team-unit.
 		if _, err := sess.
 			Where("team_id=?", t.ID).
-			Delete(new(TeamUnit)); err != nil {
+			Delete(new(organization.TeamUnit)); err != nil {
 			return err
 		}
 		if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
@@ -737,7 +372,7 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 
 	// Update access for team members if needed.
 	if authChanged {
-		if err = t.getRepositories(sess); err != nil {
+		if err = t.GetRepositoriesCtx(ctx); err != nil {
 			return fmt.Errorf("getRepositories: %v", err)
 		}
 
@@ -750,7 +385,7 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 
 	// Add all repositories to the team if it has access to all of them.
 	if includeAllChanged && t.IncludesAllRepositories {
-		err = t.addAllRepositories(ctx)
+		err = addAllRepositories(ctx, t)
 		if err != nil {
 			return fmt.Errorf("addAllRepositories: %v", err)
 		}
@@ -761,11 +396,7 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 
 // DeleteTeam deletes given team.
 // It's caller's responsibility to assign organization ID.
-func DeleteTeam(t *Team) error {
-	if err := t.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
-		return err
-	}
-
+func DeleteTeam(t *organization.Team) error {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return err
@@ -773,7 +404,11 @@ func DeleteTeam(t *Team) error {
 	defer committer.Close()
 	sess := db.GetEngine(ctx)
 
-	if err := t.getMembers(sess); err != nil {
+	if err := t.GetRepositoriesCtx(ctx); err != nil {
+		return err
+	}
+
+	if err := t.GetMembersCtx(ctx); err != nil {
 		return err
 	}
 
@@ -813,7 +448,7 @@ func DeleteTeam(t *Team) error {
 	}
 
 	if !t.IncludesAllRepositories {
-		if err := t.removeAllRepositories(ctx); err != nil {
+		if err := removeAllRepositories(ctx, t); err != nil {
 			return err
 		}
 	}
@@ -822,19 +457,19 @@ func DeleteTeam(t *Team) error {
 	if _, err := sess.
 		Where("org_id=?", t.OrgID).
 		Where("team_id=?", t.ID).
-		Delete(new(TeamUser)); err != nil {
+		Delete(new(organization.TeamUser)); err != nil {
 		return err
 	}
 
 	// Delete team-unit.
 	if _, err := sess.
 		Where("team_id=?", t.ID).
-		Delete(new(TeamUnit)); err != nil {
+		Delete(new(organization.TeamUnit)); err != nil {
 		return err
 	}
 
 	// Delete team.
-	if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil {
+	if _, err := sess.ID(t.ID).Delete(new(organization.Team)); err != nil {
 		return err
 	}
 	// Update organization number of teams.
@@ -845,103 +480,15 @@ func DeleteTeam(t *Team) error {
 	return committer.Commit()
 }
 
-// ___________                    ____ ___
-// \__    ___/___ _____    _____ |    |   \______ ___________
-//   |    |_/ __ \\__  \  /     \|    |   /  ___// __ \_  __ \
-//   |    |\  ___/ / __ \|  Y Y  \    |  /\___ \\  ___/|  | \/
-//   |____| \___  >____  /__|_|  /______//____  >\___  >__|
-//              \/     \/      \/             \/     \/
-
-// TeamUser represents an team-user relation.
-type TeamUser struct {
-	ID     int64 `xorm:"pk autoincr"`
-	OrgID  int64 `xorm:"INDEX"`
-	TeamID int64 `xorm:"UNIQUE(s)"`
-	UID    int64 `xorm:"UNIQUE(s)"`
-}
-
-func isTeamMember(e db.Engine, orgID, teamID, userID int64) (bool, error) {
-	return e.
-		Where("org_id=?", orgID).
-		And("team_id=?", teamID).
-		And("uid=?", userID).
-		Table("team_user").
-		Exist()
-}
-
-// IsTeamMember returns true if given user is a member of team.
-func IsTeamMember(orgID, teamID, userID int64) (bool, error) {
-	return isTeamMember(db.GetEngine(db.DefaultContext), orgID, teamID, userID)
-}
-
-func getTeamUsersByTeamID(e db.Engine, teamID int64) ([]*TeamUser, error) {
-	teamUsers := make([]*TeamUser, 0, 10)
-	return teamUsers, e.
-		Where("team_id=?", teamID).
-		Find(&teamUsers)
-}
-
-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_model.User, len(teamUsers))
-	for i, teamUser := range teamUsers {
-		member, err := user_model.GetUserByIDEngine(e, teamUser.UID)
-		if err != nil {
-			return nil, fmt.Errorf("get user '%d': %v", teamUser.UID, err)
-		}
-		members[i] = member
-	}
-	sort.Slice(members, func(i, j int) bool {
-		return members[i].DisplayName() < members[j].DisplayName()
-	})
-	return members, nil
-}
-
-// GetTeamMembers returns all members in given team of organization.
-func GetTeamMembers(teamID int64) ([]*user_model.User, error) {
-	return getTeamMembers(db.GetEngine(db.DefaultContext), teamID)
-}
-
-func getUserOrgTeams(e db.Engine, orgID, userID int64) (teams []*Team, err error) {
-	return teams, e.
-		Join("INNER", "team_user", "team_user.team_id = team.id").
-		Where("team.org_id = ?", orgID).
-		And("team_user.uid=?", userID).
-		Find(&teams)
-}
-
-func getUserRepoTeams(e db.Engine, orgID, userID, repoID int64) (teams []*Team, err error) {
-	return teams, e.
-		Join("INNER", "team_user", "team_user.team_id = team.id").
-		Join("INNER", "team_repo", "team_repo.team_id = team.id").
-		Where("team.org_id = ?", orgID).
-		And("team_user.uid=?", userID).
-		And("team_repo.repo_id=?", repoID).
-		Find(&teams)
-}
-
-// GetUserOrgTeams returns all teams that user belongs to in given organization.
-func GetUserOrgTeams(orgID, userID int64) ([]*Team, error) {
-	return getUserOrgTeams(db.GetEngine(db.DefaultContext), orgID, userID)
-}
-
 // AddTeamMember adds new membership of given team to given organization,
 // the user will have membership to given organization automatically when needed.
-func AddTeamMember(team *Team, userID int64) error {
-	isAlreadyMember, err := IsTeamMember(team.OrgID, team.ID, userID)
+func AddTeamMember(team *organization.Team, userID int64) error {
+	isAlreadyMember, err := organization.IsTeamMember(db.DefaultContext, team.OrgID, team.ID, userID)
 	if err != nil || isAlreadyMember {
 		return err
 	}
 
-	if err := AddOrgUser(team.OrgID, userID); err != nil {
-		return err
-	}
-
-	// Get team and its repositories.
-	if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
+	if err := organization.AddOrgUser(team.OrgID, userID); err != nil {
 		return err
 	}
 
@@ -951,15 +498,20 @@ func AddTeamMember(team *Team, userID int64) error {
 	}
 	defer committer.Close()
 
+	// Get team and its repositories.
+	if err := team.GetRepositoriesCtx(ctx); err != nil {
+		return err
+	}
+
 	sess := db.GetEngine(ctx)
 
-	if err := db.Insert(ctx, &TeamUser{
+	if err := db.Insert(ctx, &organization.TeamUser{
 		UID:    userID,
 		OrgID:  team.OrgID,
 		TeamID: team.ID,
 	}); err != nil {
 		return err
-	} else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(Team)); err != nil {
+	} else if _, err := sess.Incr("num_members").ID(team.ID).Update(new(organization.Team)); err != nil {
 		return err
 	}
 
@@ -980,25 +532,25 @@ func AddTeamMember(team *Team, userID int64) error {
 	return committer.Commit()
 }
 
-func removeTeamMember(ctx context.Context, team *Team, userID int64) error {
+func removeTeamMember(ctx context.Context, team *organization.Team, userID int64) error {
 	e := db.GetEngine(ctx)
-	isMember, err := isTeamMember(e, team.OrgID, team.ID, userID)
+	isMember, err := organization.IsTeamMember(ctx, team.OrgID, team.ID, userID)
 	if err != nil || !isMember {
 		return err
 	}
 
 	// Check if the user to delete is the last member in owner team.
 	if team.IsOwnerTeam() && team.NumMembers == 1 {
-		return ErrLastOrgOwner{UID: userID}
+		return organization.ErrLastOrgOwner{UID: userID}
 	}
 
 	team.NumMembers--
 
-	if err := team.getRepositories(e); err != nil {
+	if err := team.GetRepositoriesCtx(ctx); err != nil {
 		return err
 	}
 
-	if _, err := e.Delete(&TeamUser{
+	if _, err := e.Delete(&organization.TeamUser{
 		UID:    userID,
 		OrgID:  team.OrgID,
 		TeamID: team.ID,
@@ -1029,7 +581,7 @@ func removeTeamMember(ctx context.Context, team *Team, userID int64) error {
 	}
 
 	// Check if the user is a member of any team in the organization.
-	if count, err := e.Count(&TeamUser{
+	if count, err := e.Count(&organization.TeamUser{
 		UID:   userID,
 		OrgID: team.OrgID,
 	}); err != nil {
@@ -1042,7 +594,7 @@ func removeTeamMember(ctx context.Context, team *Team, userID int64) error {
 }
 
 // RemoveTeamMember removes member from given team of given organization.
-func RemoveTeamMember(team *Team, userID int64) error {
+func RemoveTeamMember(team *organization.Team, userID int64) error {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return err
@@ -1053,125 +605,3 @@ func RemoveTeamMember(team *Team, userID int64) error {
 	}
 	return committer.Commit()
 }
-
-// IsUserInTeams returns if a user in some teams
-func IsUserInTeams(userID int64, teamIDs []int64) (bool, error) {
-	return isUserInTeams(db.GetEngine(db.DefaultContext), userID, teamIDs)
-}
-
-func isUserInTeams(e db.Engine, userID int64, teamIDs []int64) (bool, error) {
-	return e.Where("uid=?", userID).In("team_id", teamIDs).Exist(new(TeamUser))
-}
-
-// UsersInTeamsCount counts the number of users which are in userIDs and teamIDs
-func UsersInTeamsCount(userIDs, teamIDs []int64) (int64, error) {
-	var ids []int64
-	if err := db.GetEngine(db.DefaultContext).In("uid", userIDs).In("team_id", teamIDs).
-		Table("team_user").
-		Cols("uid").GroupBy("uid").Find(&ids); err != nil {
-		return 0, err
-	}
-	return int64(len(ids)), nil
-}
-
-// ___________                  __________
-// \__    ___/___ _____    _____\______   \ ____ ______   ____
-//   |    |_/ __ \\__  \  /     \|       _// __ \\____ \ /  _ \
-//   |    |\  ___/ / __ \|  Y Y  \    |   \  ___/|  |_> >  <_> )
-//   |____| \___  >____  /__|_|  /____|_  /\___  >   __/ \____/
-//              \/     \/      \/       \/     \/|__|
-
-// TeamRepo represents an team-repository relation.
-type TeamRepo struct {
-	ID     int64 `xorm:"pk autoincr"`
-	OrgID  int64 `xorm:"INDEX"`
-	TeamID int64 `xorm:"UNIQUE(s)"`
-	RepoID int64 `xorm:"UNIQUE(s)"`
-}
-
-func hasTeamRepo(e db.Engine, orgID, teamID, repoID int64) bool {
-	has, _ := e.
-		Where("org_id=?", orgID).
-		And("team_id=?", teamID).
-		And("repo_id=?", repoID).
-		Get(new(TeamRepo))
-	return has
-}
-
-// HasTeamRepo returns true if given repository belongs to team.
-func HasTeamRepo(orgID, teamID, repoID int64) bool {
-	return hasTeamRepo(db.GetEngine(db.DefaultContext), orgID, teamID, repoID)
-}
-
-func addTeamRepo(e db.Engine, orgID, teamID, repoID int64) error {
-	_, err := e.InsertOne(&TeamRepo{
-		OrgID:  orgID,
-		TeamID: teamID,
-		RepoID: repoID,
-	})
-	return err
-}
-
-func removeTeamRepo(e db.Engine, teamID, repoID int64) error {
-	_, err := e.Delete(&TeamRepo{
-		TeamID: teamID,
-		RepoID: repoID,
-	})
-	return err
-}
-
-// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
-func GetTeamsWithAccessToRepo(orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) {
-	teams := make([]*Team, 0, 5)
-	return teams, db.GetEngine(db.DefaultContext).Where("team.authorize >= ?", mode).
-		Join("INNER", "team_repo", "team_repo.team_id = team.id").
-		And("team_repo.org_id = ?", orgID).
-		And("team_repo.repo_id = ?", repoID).
-		Find(&teams)
-}
-
-// ___________                    ____ ___      .__  __
-// \__    ___/___ _____    _____ |    |   \____ |__|/  |_
-//   |    |_/ __ \\__  \  /     \|    |   /    \|  \   __\
-//   |    |\  ___/ / __ \|  Y Y  \    |  /   |  \  ||  |
-//   |____| \___  >____  /__|_|  /______/|___|  /__||__|
-//              \/     \/      \/             \/
-
-// TeamUnit describes all units of a repository
-type TeamUnit struct {
-	ID         int64     `xorm:"pk autoincr"`
-	OrgID      int64     `xorm:"INDEX"`
-	TeamID     int64     `xorm:"UNIQUE(s)"`
-	Type       unit.Type `xorm:"UNIQUE(s)"`
-	AccessMode perm.AccessMode
-}
-
-// Unit returns Unit
-func (t *TeamUnit) Unit() unit.Unit {
-	return unit.Units[t.Type]
-}
-
-func getUnitsByTeamID(e db.Engine, teamID int64) (units []*TeamUnit, err error) {
-	return units, e.Where("team_id = ?", teamID).Find(&units)
-}
-
-// UpdateTeamUnits updates a teams's units
-func UpdateTeamUnits(team *Team, units []TeamUnit) (err error) {
-	ctx, committer, err := db.TxContext()
-	if err != nil {
-		return err
-	}
-	defer committer.Close()
-
-	if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
-		return err
-	}
-
-	if len(units) > 0 {
-		if err = db.Insert(ctx, units); err != nil {
-			return err
-		}
-	}
-
-	return committer.Commit()
-}
diff --git a/models/org_team_test.go b/models/org_team_test.go
index cf3a797991..e125f3c65b 100644
--- a/models/org_team_test.go
+++ b/models/org_team_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -17,68 +18,14 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestTeam_IsOwnerTeam(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
-	assert.True(t, team.IsOwnerTeam())
-
-	team = unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
-	assert.False(t, team.IsOwnerTeam())
-}
-
-func TestTeam_IsMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
-	assert.True(t, team.IsMember(2))
-	assert.False(t, team.IsMember(4))
-	assert.False(t, team.IsMember(unittest.NonexistentID))
-
-	team = unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
-	assert.True(t, team.IsMember(2))
-	assert.True(t, team.IsMember(4))
-	assert.False(t, team.IsMember(unittest.NonexistentID))
-}
-
-func TestTeam_GetRepositories(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	test := func(teamID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.NoError(t, team.GetRepositories(&SearchOrgTeamOptions{}))
-		assert.Len(t, team.Repos, team.NumRepos)
-		for _, repo := range team.Repos {
-			unittest.AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repo.ID})
-		}
-	}
-	test(1)
-	test(3)
-}
-
-func TestTeam_GetMembers(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	test := func(teamID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.NoError(t, team.GetMembers(&SearchMembersOptions{}))
-		assert.Len(t, team.Members, team.NumMembers)
-		for _, member := range team.Members {
-			unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID})
-		}
-	}
-	test(1)
-	test(3)
-}
-
 func TestTeam_AddMember(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	test := func(teamID, userID int64) {
-		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_model.User{ID: team.OrgID})
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+		assert.NoError(t, AddTeamMember(team, userID))
+		unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &user_model.User{ID: team.OrgID})
 	}
 	test(1, 2)
 	test(1, 4)
@@ -89,27 +36,27 @@ func TestTeam_RemoveMember(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	testSuccess := func(teamID, userID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.NoError(t, team.RemoveMember(userID))
-		unittest.AssertNotExistsBean(t, &TeamUser{UID: userID, TeamID: teamID})
-		unittest.CheckConsistencyFor(t, &Team{ID: teamID})
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+		assert.NoError(t, RemoveTeamMember(team, userID))
+		unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID})
 	}
 	testSuccess(1, 4)
 	testSuccess(2, 2)
 	testSuccess(3, 2)
 	testSuccess(3, unittest.NonexistentID)
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
-	err := team.RemoveMember(2)
-	assert.True(t, IsErrLastOrgOwner(err))
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
+	err := RemoveTeamMember(team, 2)
+	assert.True(t, organization.IsErrLastOrgOwner(err))
 }
 
 func TestTeam_HasRepository(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	test := func(teamID, repoID int64, expected bool) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.Equal(t, expected, team.HasRepository(repoID))
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+		assert.Equal(t, expected, HasRepository(team, repoID))
 	}
 	test(1, 1, false)
 	test(1, 3, true)
@@ -124,29 +71,29 @@ func TestTeam_AddRepository(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	testSuccess := func(teamID, repoID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
-		assert.NoError(t, team.AddRepository(repo))
-		unittest.AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repoID})
-		unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &repo_model.Repository{ID: repoID})
+		assert.NoError(t, AddRepository(team, repo))
+		unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
 	}
 	testSuccess(2, 3)
 	testSuccess(2, 5)
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
-	assert.Error(t, team.AddRepository(repo))
-	unittest.CheckConsistencyFor(t, &Team{ID: 1}, &repo_model.Repository{ID: 1})
+	assert.Error(t, AddRepository(team, repo))
+	unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
 }
 
 func TestTeam_RemoveRepository(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	testSuccess := func(teamID, repoID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.NoError(t, team.RemoveRepository(repoID))
-		unittest.AssertNotExistsBean(t, &TeamRepo{TeamID: teamID, RepoID: repoID})
-		unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &repo_model.Repository{ID: repoID})
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+		assert.NoError(t, RemoveRepository(team, repoID))
+		unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
 	}
 	testSuccess(2, 3)
 	testSuccess(2, 5)
@@ -154,97 +101,62 @@ func TestTeam_RemoveRepository(t *testing.T) {
 }
 
 func TestIsUsableTeamName(t *testing.T) {
-	assert.NoError(t, IsUsableTeamName("usable"))
-	assert.True(t, db.IsErrNameReserved(IsUsableTeamName("new")))
+	assert.NoError(t, organization.IsUsableTeamName("usable"))
+	assert.True(t, db.IsErrNameReserved(organization.IsUsableTeamName("new")))
 }
 
 func TestNewTeam(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	const teamName = "newTeamName"
-	team := &Team{Name: teamName, OrgID: 3}
+	team := &organization.Team{Name: teamName, OrgID: 3}
 	assert.NoError(t, NewTeam(team))
-	unittest.AssertExistsAndLoadBean(t, &Team{Name: teamName})
-	unittest.CheckConsistencyFor(t, &Team{}, &user_model.User{ID: team.OrgID})
-}
-
-func TestGetTeam(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testSuccess := func(orgID int64, name string) {
-		team, err := GetTeam(orgID, name)
-		assert.NoError(t, err)
-		assert.EqualValues(t, orgID, team.OrgID)
-		assert.Equal(t, name, team.Name)
-	}
-	testSuccess(3, "Owners")
-	testSuccess(3, "team1")
-
-	_, err := GetTeam(3, "nonexistent")
-	assert.Error(t, err)
-	_, err = GetTeam(unittest.NonexistentID, "Owners")
-	assert.Error(t, err)
-}
-
-func TestGetTeamByID(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testSuccess := func(teamID int64) {
-		team, err := GetTeamByID(teamID)
-		assert.NoError(t, err)
-		assert.EqualValues(t, teamID, team.ID)
-	}
-	testSuccess(1)
-	testSuccess(2)
-	testSuccess(3)
-	testSuccess(4)
-
-	_, err := GetTeamByID(unittest.NonexistentID)
-	assert.Error(t, err)
+	unittest.AssertExistsAndLoadBean(t, &organization.Team{Name: teamName})
+	unittest.CheckConsistencyFor(t, &organization.Team{}, &user_model.User{ID: team.OrgID})
 }
 
 func TestUpdateTeam(t *testing.T) {
 	// successful update
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
 	team.LowerName = "newname"
 	team.Name = "newName"
 	team.Description = strings.Repeat("A long description!", 100)
 	team.AccessMode = perm.AccessModeAdmin
 	assert.NoError(t, UpdateTeam(team, true, false))
 
-	team = unittest.AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team)
+	team = unittest.AssertExistsAndLoadBean(t, &organization.Team{Name: "newName"}).(*organization.Team)
 	assert.True(t, strings.HasPrefix(team.Description, "A long description!"))
 
 	access := unittest.AssertExistsAndLoadBean(t, &Access{UserID: 4, RepoID: 3}).(*Access)
 	assert.EqualValues(t, perm.AccessModeAdmin, access.Mode)
 
-	unittest.CheckConsistencyFor(t, &Team{ID: team.ID})
+	unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID})
 }
 
 func TestUpdateTeam2(t *testing.T) {
 	// update to already-existing team
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
 	team.LowerName = "owners"
 	team.Name = "Owners"
 	team.Description = strings.Repeat("A long description!", 100)
 	err := UpdateTeam(team, true, false)
-	assert.True(t, IsErrTeamAlreadyExist(err))
+	assert.True(t, organization.IsErrTeamAlreadyExist(err))
 
-	unittest.CheckConsistencyFor(t, &Team{ID: team.ID})
+	unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID})
 }
 
 func TestDeleteTeam(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
 	assert.NoError(t, DeleteTeam(team))
-	unittest.AssertNotExistsBean(t, &Team{ID: team.ID})
-	unittest.AssertNotExistsBean(t, &TeamRepo{TeamID: team.ID})
-	unittest.AssertNotExistsBean(t, &TeamUser{TeamID: team.ID})
+	unittest.AssertNotExistsBean(t, &organization.Team{ID: team.ID})
+	unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: team.ID})
+	unittest.AssertNotExistsBean(t, &organization.TeamUser{TeamID: team.ID})
 
 	// check that team members don't have "leftover" access to repos
 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
@@ -254,78 +166,14 @@ func TestDeleteTeam(t *testing.T) {
 	assert.True(t, accessMode < perm.AccessModeWrite)
 }
 
-func TestIsTeamMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(orgID, teamID, userID int64, expected bool) {
-		isMember, err := IsTeamMember(orgID, teamID, userID)
-		assert.NoError(t, err)
-		assert.Equal(t, expected, isMember)
-	}
-
-	test(3, 1, 2, true)
-	test(3, 1, 4, false)
-	test(3, 1, unittest.NonexistentID, false)
-
-	test(3, 2, 2, true)
-	test(3, 2, 4, true)
-
-	test(3, unittest.NonexistentID, unittest.NonexistentID, false)
-	test(unittest.NonexistentID, unittest.NonexistentID, unittest.NonexistentID, false)
-}
-
-func TestGetTeamMembers(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	test := func(teamID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		members, err := GetTeamMembers(teamID)
-		assert.NoError(t, err)
-		assert.Len(t, members, team.NumMembers)
-		for _, member := range members {
-			unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID})
-		}
-	}
-	test(1)
-	test(3)
-}
-
-func TestGetUserTeams(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(userID int64) {
-		teams, _, err := GetUserTeams(&GetUserTeamOptions{UserID: userID})
-		assert.NoError(t, err)
-		for _, team := range teams {
-			unittest.AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})
-		}
-	}
-	test(2)
-	test(5)
-	test(unittest.NonexistentID)
-}
-
-func TestGetUserOrgTeams(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(orgID, userID int64) {
-		teams, err := GetUserOrgTeams(orgID, userID)
-		assert.NoError(t, err)
-		for _, team := range teams {
-			assert.EqualValues(t, orgID, team.OrgID)
-			unittest.AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})
-		}
-	}
-	test(3, 2)
-	test(3, 4)
-	test(3, unittest.NonexistentID)
-}
-
 func TestAddTeamMember(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	test := func(teamID, userID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
 		assert.NoError(t, AddTeamMember(team, userID))
-		unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: userID, TeamID: teamID})
-		unittest.CheckConsistencyFor(t, &Team{ID: teamID}, &user_model.User{ID: team.OrgID})
+		unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &user_model.User{ID: team.OrgID})
 	}
 	test(1, 2)
 	test(1, 4)
@@ -336,47 +184,17 @@ func TestRemoveTeamMember(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	testSuccess := func(teamID, userID int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
 		assert.NoError(t, RemoveTeamMember(team, userID))
-		unittest.AssertNotExistsBean(t, &TeamUser{UID: userID, TeamID: teamID})
-		unittest.CheckConsistencyFor(t, &Team{ID: teamID})
+		unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
+		unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID})
 	}
 	testSuccess(1, 4)
 	testSuccess(2, 2)
 	testSuccess(3, 2)
 	testSuccess(3, unittest.NonexistentID)
 
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
 	err := RemoveTeamMember(team, 2)
-	assert.True(t, IsErrLastOrgOwner(err))
-}
-
-func TestHasTeamRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	test := func(teamID, repoID int64, expected bool) {
-		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
-		assert.Equal(t, expected, HasTeamRepo(team.OrgID, teamID, repoID))
-	}
-	test(1, 1, false)
-	test(1, 3, true)
-	test(1, 5, true)
-	test(1, unittest.NonexistentID, false)
-
-	test(2, 3, true)
-	test(2, 5, false)
-}
-
-func TestUsersInTeamsCount(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	test := func(teamIDs, userIDs []int64, expected int64) {
-		count, err := UsersInTeamsCount(teamIDs, userIDs)
-		assert.NoError(t, err)
-		assert.Equal(t, expected, count)
-	}
-
-	test([]int64{2}, []int64{1, 2, 3, 4}, 1)          // only userid 2
-	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
-	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
+	assert.True(t, organization.IsErrLastOrgOwner(err))
 }
diff --git a/models/org_test.go b/models/org_test.go
index ec324cb71a..4d8831858c 100644
--- a/models/org_test.go
+++ b/models/org_test.go
@@ -8,176 +8,81 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	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/setting"
-	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestUser_IsOwnedBy(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	for _, testCase := range []struct {
-		OrgID         int64
-		UserID        int64
-		ExpectedOwner bool
-	}{
-		{3, 2, true},
-		{3, 1, false},
-		{3, 3, false},
-		{3, 4, false},
-		{2, 2, false}, // user2 is not an organization
-		{2, 3, false},
-	} {
-		org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: testCase.OrgID}).(*Organization)
-		isOwner, err := org.IsOwnedBy(testCase.UserID)
-		assert.NoError(t, err)
-		assert.Equal(t, testCase.ExpectedOwner, isOwner)
-	}
-}
-
-func TestUser_IsOrgMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	for _, testCase := range []struct {
-		OrgID          int64
-		UserID         int64
-		ExpectedMember bool
-	}{
-		{3, 2, true},
-		{3, 4, true},
-		{3, 1, false},
-		{3, 3, false},
-		{2, 2, false}, // user2 is not an organization
-		{2, 3, false},
-	} {
-		org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: testCase.OrgID}).(*Organization)
-		isMember, err := org.IsOrgMember(testCase.UserID)
-		assert.NoError(t, err)
-		assert.Equal(t, testCase.ExpectedMember, isMember)
-	}
-}
-
-func TestUser_GetTeam(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	team, err := org.GetTeam("team1")
-	assert.NoError(t, err)
-	assert.Equal(t, org.ID, team.OrgID)
-	assert.Equal(t, "team1", team.LowerName)
-
-	_, err = org.GetTeam("does not exist")
-	assert.True(t, IsErrTeamNotExist(err))
-
-	nonOrg := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 2}).(*Organization)
-	_, err = nonOrg.GetTeam("team")
-	assert.True(t, IsErrTeamNotExist(err))
-}
-
-func TestUser_GetOwnerTeam(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	team, err := org.GetOwnerTeam()
-	assert.NoError(t, err)
-	assert.Equal(t, org.ID, team.OrgID)
-
-	nonOrg := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 2}).(*Organization)
-	_, err = nonOrg.GetOwnerTeam()
-	assert.True(t, IsErrTeamNotExist(err))
-}
-
-func TestUser_GetTeams(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	teams, err := org.LoadTeams()
-	assert.NoError(t, err)
-	if assert.Len(t, teams, 4) {
-		assert.Equal(t, int64(1), teams[0].ID)
-		assert.Equal(t, int64(2), teams[1].ID)
-		assert.Equal(t, int64(12), teams[2].ID)
-		assert.Equal(t, int64(7), teams[3].ID)
-	}
-}
-
-func TestUser_GetMembers(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	members, _, err := org.GetMembers()
-	assert.NoError(t, err)
-	if assert.Len(t, members, 3) {
-		assert.Equal(t, int64(2), members[0].ID)
-		assert.Equal(t, int64(28), members[1].ID)
-		assert.Equal(t, int64(4), members[2].ID)
-	}
-}
-
-func TestUser_AddMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-
-	// add a user that is not a member
-	unittest.AssertNotExistsBean(t, &OrgUser{UID: 5, OrgID: 3})
-	prevNumMembers := org.NumMembers
-	assert.NoError(t, org.AddMember(5))
-	unittest.AssertExistsAndLoadBean(t, &OrgUser{UID: 5, OrgID: 3})
-	org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	assert.Equal(t, prevNumMembers+1, org.NumMembers)
-
-	// add a user that is already a member
-	unittest.AssertExistsAndLoadBean(t, &OrgUser{UID: 4, OrgID: 3})
-	prevNumMembers = org.NumMembers
-	assert.NoError(t, org.AddMember(4))
-	unittest.AssertExistsAndLoadBean(t, &OrgUser{UID: 4, OrgID: 3})
-	org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	assert.Equal(t, prevNumMembers, org.NumMembers)
-
-	unittest.CheckConsistencyFor(t, &user_model.User{})
-}
-
 func TestUser_RemoveMember(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
 
 	// remove a user that is a member
-	unittest.AssertExistsAndLoadBean(t, &OrgUser{UID: 4, OrgID: 3})
+	unittest.AssertExistsAndLoadBean(t, &organization.OrgUser{UID: 4, OrgID: 3})
 	prevNumMembers := org.NumMembers
-	assert.NoError(t, org.RemoveMember(4))
-	unittest.AssertNotExistsBean(t, &OrgUser{UID: 4, OrgID: 3})
-	org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	assert.NoError(t, RemoveOrgUser(org.ID, 4))
+	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 4, OrgID: 3})
+	org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
 	assert.Equal(t, prevNumMembers-1, org.NumMembers)
 
 	// remove a user that is not a member
-	unittest.AssertNotExistsBean(t, &OrgUser{UID: 5, OrgID: 3})
+	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 5, OrgID: 3})
 	prevNumMembers = org.NumMembers
-	assert.NoError(t, org.RemoveMember(5))
-	unittest.AssertNotExistsBean(t, &OrgUser{UID: 5, OrgID: 3})
-	org = unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	assert.NoError(t, RemoveOrgUser(org.ID, 5))
+	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 5, OrgID: 3})
+	org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
 	assert.Equal(t, prevNumMembers, org.NumMembers)
 
-	unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
+}
+
+func TestRemoveOrgUser(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	testSuccess := func(orgID, userID int64) {
+		org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+		expectedNumMembers := org.NumMembers
+		if unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
+			expectedNumMembers--
+		}
+		assert.NoError(t, RemoveOrgUser(orgID, userID))
+		unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: orgID, UID: userID})
+		org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+		assert.EqualValues(t, expectedNumMembers, org.NumMembers)
+	}
+	testSuccess(3, 4)
+	testSuccess(3, 4)
+
+	err := RemoveOrgUser(7, 5)
+	assert.Error(t, err)
+	assert.True(t, organization.IsErrLastOrgOwner(err))
+	unittest.AssertExistsAndLoadBean(t, &organization.OrgUser{OrgID: 7, UID: 5})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
 }
 
 func TestUser_RemoveOrgRepo(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: org.ID}).(*repo_model.Repository)
 
 	// remove a repo that does belong to org
-	unittest.AssertExistsAndLoadBean(t, &TeamRepo{RepoID: repo.ID, OrgID: org.ID})
-	assert.NoError(t, org.RemoveOrgRepo(repo.ID))
-	unittest.AssertNotExistsBean(t, &TeamRepo{RepoID: repo.ID, OrgID: org.ID})
+	unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{RepoID: repo.ID, OrgID: org.ID})
+	assert.NoError(t, organization.RemoveOrgRepo(db.DefaultContext, org.ID, repo.ID))
+	unittest.AssertNotExistsBean(t, &organization.TeamRepo{RepoID: repo.ID, OrgID: org.ID})
 	unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID}) // repo should still exist
 
 	// remove a repo that does not belong to org
-	assert.NoError(t, org.RemoveOrgRepo(repo.ID))
-	unittest.AssertNotExistsBean(t, &TeamRepo{RepoID: repo.ID, OrgID: org.ID})
+	assert.NoError(t, organization.RemoveOrgRepo(db.DefaultContext, org.ID, repo.ID))
+	unittest.AssertNotExistsBean(t, &organization.TeamRepo{RepoID: repo.ID, OrgID: org.ID})
 
-	assert.NoError(t, org.RemoveOrgRepo(unittest.NonexistentID))
+	assert.NoError(t, organization.RemoveOrgRepo(db.DefaultContext, org.ID, unittest.NonexistentID))
 
 	unittest.CheckConsistencyFor(t,
 		&user_model.User{ID: org.ID},
-		&Team{OrgID: org.ID},
+		&organization.Team{OrgID: org.ID},
 		&repo_model.Repository{ID: repo.ID})
 }
 
@@ -187,18 +92,18 @@ func TestCreateOrganization(t *testing.T) {
 
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
 	const newOrgName = "neworg"
-	org := &Organization{
+	org := &organization.Organization{
 		Name: newOrgName,
 	}
 
 	unittest.AssertNotExistsBean(t, &user_model.User{Name: newOrgName, Type: user_model.UserTypeOrganization})
-	assert.NoError(t, CreateOrganization(org, owner))
+	assert.NoError(t, organization.CreateOrganization(org, owner))
 	org = unittest.AssertExistsAndLoadBean(t,
-		&Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}).(*Organization)
+		&organization.Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}).(*organization.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_model.User{}, &Team{})
+		&organization.Team{Name: organization.OwnerTeamName, OrgID: org.ID}).(*organization.Team)
+	unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: owner.ID, TeamID: ownerTeam.ID})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
 }
 
 func TestCreateOrganization2(t *testing.T) {
@@ -207,16 +112,16 @@ func TestCreateOrganization2(t *testing.T) {
 
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
 	const newOrgName = "neworg"
-	org := &Organization{
+	org := &organization.Organization{
 		Name: newOrgName,
 	}
 
-	unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: user_model.UserTypeOrganization})
-	err := CreateOrganization(org, owner)
+	unittest.AssertNotExistsBean(t, &organization.Organization{Name: newOrgName, Type: user_model.UserTypeOrganization})
+	err := organization.CreateOrganization(org, owner)
 	assert.Error(t, err)
-	assert.True(t, IsErrUserNotAllowedCreateOrg(err))
-	unittest.AssertNotExistsBean(t, &Organization{Name: newOrgName, Type: user_model.UserTypeOrganization})
-	unittest.CheckConsistencyFor(t, &Organization{}, &Team{})
+	assert.True(t, organization.IsErrUserNotAllowedCreateOrg(err))
+	unittest.AssertNotExistsBean(t, &organization.Organization{Name: newOrgName, Type: user_model.UserTypeOrganization})
+	unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
 }
 
 func TestCreateOrganization3(t *testing.T) {
@@ -224,12 +129,12 @@ func TestCreateOrganization3(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
-	org := &Organization{Name: "user3"}                                   // should already exist
+	org := &organization.Organization{Name: "user3"}                      // should already exist
 	unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: org.Name}) // sanity check
-	err := CreateOrganization(org, owner)
+	err := organization.CreateOrganization(org, owner)
 	assert.Error(t, err)
 	assert.True(t, user_model.IsErrUserAlreadyExist(err))
-	unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
 }
 
 func TestCreateOrganization4(t *testing.T) {
@@ -237,210 +142,10 @@ func TestCreateOrganization4(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
-	err := CreateOrganization(&Organization{Name: "assets"}, owner)
+	err := organization.CreateOrganization(&organization.Organization{Name: "assets"}, owner)
 	assert.Error(t, err)
 	assert.True(t, db.IsErrNameReserved(err))
-	unittest.CheckConsistencyFor(t, &Organization{}, &Team{})
-}
-
-func TestGetOrgByName(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	org, err := GetOrgByName("user3")
-	assert.NoError(t, err)
-	assert.EqualValues(t, 3, org.ID)
-	assert.Equal(t, "user3", org.Name)
-
-	_, err = GetOrgByName("user2") // user2 is an individual
-	assert.True(t, IsErrOrgNotExist(err))
-
-	_, err = GetOrgByName("") // corner case
-	assert.True(t, IsErrOrgNotExist(err))
-}
-
-func TestCountOrganizations(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&user_model.User{})
-	assert.NoError(t, err)
-	assert.Equal(t, expected, CountOrganizations())
-}
-
-func TestIsOrganizationOwner(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(orgID, userID int64, expected bool) {
-		isOwner, err := IsOrganizationOwner(orgID, userID)
-		assert.NoError(t, err)
-		assert.EqualValues(t, expected, isOwner)
-	}
-	test(3, 2, true)
-	test(3, 3, false)
-	test(6, 5, true)
-	test(6, 4, false)
-	test(unittest.NonexistentID, unittest.NonexistentID, false)
-}
-
-func TestIsOrganizationMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(orgID, userID int64, expected bool) {
-		isMember, err := IsOrganizationMember(orgID, userID)
-		assert.NoError(t, err)
-		assert.EqualValues(t, expected, isMember)
-	}
-	test(3, 2, true)
-	test(3, 3, false)
-	test(3, 4, true)
-	test(6, 5, true)
-	test(6, 4, false)
-	test(unittest.NonexistentID, unittest.NonexistentID, false)
-}
-
-func TestIsPublicMembership(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	test := func(orgID, userID int64, expected bool) {
-		isMember, err := IsPublicMembership(orgID, userID)
-		assert.NoError(t, err)
-		assert.EqualValues(t, expected, isMember)
-	}
-	test(3, 2, true)
-	test(3, 3, false)
-	test(3, 4, false)
-	test(6, 5, true)
-	test(6, 4, false)
-	test(unittest.NonexistentID, unittest.NonexistentID, false)
-}
-
-func TestFindOrgs(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	orgs, err := FindOrgs(FindOrgOptions{
-		UserID:         4,
-		IncludePrivate: true,
-	})
-	assert.NoError(t, err)
-	if assert.Len(t, orgs, 1) {
-		assert.EqualValues(t, 3, orgs[0].ID)
-	}
-
-	orgs, err = FindOrgs(FindOrgOptions{
-		UserID:         4,
-		IncludePrivate: false,
-	})
-	assert.NoError(t, err)
-	assert.Len(t, orgs, 0)
-
-	total, err := CountOrgs(FindOrgOptions{
-		UserID:         4,
-		IncludePrivate: true,
-	})
-	assert.NoError(t, err)
-	assert.EqualValues(t, 1, total)
-}
-
-func TestGetOwnedOrgsByUserID(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	orgs, err := GetOwnedOrgsByUserID(2)
-	assert.NoError(t, err)
-	if assert.Len(t, orgs, 1) {
-		assert.EqualValues(t, 3, orgs[0].ID)
-	}
-
-	orgs, err = GetOwnedOrgsByUserID(4)
-	assert.NoError(t, err)
-	assert.Len(t, orgs, 0)
-}
-
-func TestGetOwnedOrgsByUserIDDesc(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	orgs, err := GetOwnedOrgsByUserIDDesc(5, "id")
-	assert.NoError(t, err)
-	if assert.Len(t, orgs, 2) {
-		assert.EqualValues(t, 7, orgs[0].ID)
-		assert.EqualValues(t, 6, orgs[1].ID)
-	}
-
-	orgs, err = GetOwnedOrgsByUserIDDesc(4, "id")
-	assert.NoError(t, err)
-	assert.Len(t, orgs, 0)
-}
-
-func TestGetOrgUsersByUserID(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	orgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: true})
-	assert.NoError(t, err)
-	if assert.Len(t, orgUsers, 2) {
-		assert.Equal(t, OrgUser{
-			ID:       orgUsers[0].ID,
-			OrgID:    6,
-			UID:      5,
-			IsPublic: true,
-		}, *orgUsers[0])
-		assert.Equal(t, OrgUser{
-			ID:       orgUsers[1].ID,
-			OrgID:    7,
-			UID:      5,
-			IsPublic: false,
-		}, *orgUsers[1])
-	}
-
-	publicOrgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: false})
-	assert.NoError(t, err)
-	assert.Len(t, publicOrgUsers, 1)
-	assert.Equal(t, *orgUsers[0], *publicOrgUsers[0])
-
-	orgUsers, err = GetOrgUsersByUserID(1, &SearchOrganizationsOptions{All: true})
-	assert.NoError(t, err)
-	assert.Len(t, orgUsers, 0)
-}
-
-func TestGetOrgUsersByOrgID(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	orgUsers, err := GetOrgUsersByOrgID(&FindOrgMembersOpts{
-		ListOptions: db.ListOptions{},
-		OrgID:       3,
-		PublicOnly:  false,
-	})
-	assert.NoError(t, err)
-	if assert.Len(t, orgUsers, 3) {
-		assert.Equal(t, OrgUser{
-			ID:       orgUsers[0].ID,
-			OrgID:    3,
-			UID:      2,
-			IsPublic: true,
-		}, *orgUsers[0])
-		assert.Equal(t, OrgUser{
-			ID:       orgUsers[1].ID,
-			OrgID:    3,
-			UID:      4,
-			IsPublic: false,
-		}, *orgUsers[1])
-	}
-
-	orgUsers, err = GetOrgUsersByOrgID(&FindOrgMembersOpts{
-		ListOptions: db.ListOptions{},
-		OrgID:       unittest.NonexistentID,
-		PublicOnly:  false,
-	})
-	assert.NoError(t, err)
-	assert.Len(t, orgUsers, 0)
-}
-
-func TestChangeOrgUserStatus(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	testSuccess := func(orgID, userID int64, public bool) {
-		assert.NoError(t, ChangeOrgUserStatus(orgID, userID, public))
-		orgUser := unittest.AssertExistsAndLoadBean(t, &OrgUser{OrgID: orgID, UID: userID}).(*OrgUser)
-		assert.Equal(t, public, orgUser.IsPublic)
-	}
-
-	testSuccess(3, 2, false)
-	testSuccess(3, 2, false)
-	testSuccess(3, 4, true)
-	assert.NoError(t, ChangeOrgUserStatus(unittest.NonexistentID, unittest.NonexistentID, true))
+	unittest.CheckConsistencyFor(t, &organization.Organization{}, &organization.Team{})
 }
 
 func TestAddOrgUser(t *testing.T) {
@@ -448,11 +153,11 @@ func TestAddOrgUser(t *testing.T) {
 	testSuccess := func(orgID, userID int64, isPublic bool) {
 		org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
 		expectedNumMembers := org.NumMembers
-		if !unittest.BeanExists(t, &OrgUser{OrgID: orgID, UID: userID}) {
+		if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
 			expectedNumMembers++
 		}
-		assert.NoError(t, AddOrgUser(orgID, userID))
-		ou := &OrgUser{OrgID: orgID, UID: userID}
+		assert.NoError(t, organization.AddOrgUser(orgID, userID))
+		ou := &organization.OrgUser{OrgID: orgID, UID: userID}
 		unittest.AssertExistsAndLoadBean(t, ou)
 		assert.Equal(t, isPublic, ou.IsPublic)
 		org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
@@ -467,194 +172,5 @@ func TestAddOrgUser(t *testing.T) {
 	setting.Service.DefaultOrgMemberVisible = true
 	testSuccess(6, 3, true)
 
-	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_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_model.User{ID: orgID}).(*user_model.User)
-		assert.EqualValues(t, expectedNumMembers, org.NumMembers)
-	}
-	testSuccess(3, 4)
-	testSuccess(3, 4)
-
-	err := RemoveOrgUser(7, 5)
-	assert.Error(t, err)
-	assert.True(t, IsErrLastOrgOwner(err))
-	unittest.AssertExistsAndLoadBean(t, &OrgUser{OrgID: 7, UID: 5})
-	unittest.CheckConsistencyFor(t, &user_model.User{}, &Team{})
-}
-
-func TestUser_GetUserTeamIDs(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	testSuccess := func(userID int64, expected []int64) {
-		teamIDs, err := org.GetUserTeamIDs(userID)
-		assert.NoError(t, err)
-		assert.Equal(t, expected, teamIDs)
-	}
-	testSuccess(2, []int64{1, 2})
-	testSuccess(4, []int64{2})
-	testSuccess(unittest.NonexistentID, []int64{})
-}
-
-func TestAccessibleReposEnv_CountRepos(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	testSuccess := func(userID, expectedCount int64) {
-		env, err := org.AccessibleReposEnv(userID)
-		assert.NoError(t, err)
-		count, err := env.CountRepos()
-		assert.NoError(t, err)
-		assert.EqualValues(t, expectedCount, count)
-	}
-	testSuccess(2, 3)
-	testSuccess(4, 2)
-}
-
-func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	testSuccess := func(userID, _, pageSize int64, expectedRepoIDs []int64) {
-		env, err := org.AccessibleReposEnv(userID)
-		assert.NoError(t, err)
-		repoIDs, err := env.RepoIDs(1, 100)
-		assert.NoError(t, err)
-		assert.Equal(t, expectedRepoIDs, repoIDs)
-	}
-	testSuccess(2, 1, 100, []int64{3, 5, 32})
-	testSuccess(4, 0, 100, []int64{3, 32})
-}
-
-func TestAccessibleReposEnv_Repos(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	testSuccess := func(userID int64, expectedRepoIDs []int64) {
-		env, err := org.AccessibleReposEnv(userID)
-		assert.NoError(t, err)
-		repos, err := env.Repos(1, 100)
-		assert.NoError(t, err)
-		expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
-		for i, repoID := range expectedRepoIDs {
-			expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
-				&repo_model.Repository{ID: repoID}).(*repo_model.Repository)
-		}
-		assert.Equal(t, expectedRepos, repos)
-	}
-	testSuccess(2, []int64{3, 5, 32})
-	testSuccess(4, []int64{3, 32})
-}
-
-func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
-	testSuccess := func(userID int64, expectedRepoIDs []int64) {
-		env, err := org.AccessibleReposEnv(userID)
-		assert.NoError(t, err)
-		repos, err := env.MirrorRepos()
-		assert.NoError(t, err)
-		expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
-		for i, repoID := range expectedRepoIDs {
-			expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
-				&repo_model.Repository{ID: repoID}).(*repo_model.Repository)
-		}
-		assert.Equal(t, expectedRepos, repos)
-	}
-	testSuccess(2, []int64{5})
-	testSuccess(4, []int64{})
-}
-
-func TestHasOrgVisibleTypePublic(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	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{
-		Name:       newOrgName,
-		Visibility: structs.VisibleTypePublic,
-	}
-
-	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: user_model.UserTypeOrganization}).(*Organization)
-	test1 := HasOrgOrUserVisible(org.AsUser(), owner)
-	test2 := HasOrgOrUserVisible(org.AsUser(), user3)
-	test3 := HasOrgOrUserVisible(org.AsUser(), nil)
-	assert.True(t, test1) // owner of org
-	assert.True(t, test2) // user not a part of org
-	assert.True(t, test3) // logged out user
-}
-
-func TestHasOrgVisibleTypeLimited(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	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{
-		Name:       newOrgName,
-		Visibility: structs.VisibleTypeLimited,
-	}
-
-	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: user_model.UserTypeOrganization}).(*Organization)
-	test1 := HasOrgOrUserVisible(org.AsUser(), owner)
-	test2 := HasOrgOrUserVisible(org.AsUser(), user3)
-	test3 := HasOrgOrUserVisible(org.AsUser(), nil)
-	assert.True(t, test1)  // owner of org
-	assert.True(t, test2)  // user not a part of org
-	assert.False(t, test3) // logged out user
-}
-
-func TestHasOrgVisibleTypePrivate(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-	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{
-		Name:       newOrgName,
-		Visibility: structs.VisibleTypePrivate,
-	}
-
-	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: user_model.UserTypeOrganization}).(*Organization)
-	test1 := HasOrgOrUserVisible(org.AsUser(), owner)
-	test2 := HasOrgOrUserVisible(org.AsUser(), user3)
-	test3 := HasOrgOrUserVisible(org.AsUser(), nil)
-	assert.True(t, test1)  // owner of org
-	assert.False(t, test2) // user not a part of org
-	assert.False(t, test3) // logged out user
-}
-
-func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	users, err := GetUsersWhoCanCreateOrgRepo(3)
-	assert.NoError(t, err)
-	assert.Len(t, users, 2)
-	var ids []int64
-	for i := range users {
-		ids = append(ids, users[i].ID)
-	}
-	assert.ElementsMatch(t, ids, []int64{2, 28})
-
-	users, err = GetUsersWhoCanCreateOrgRepo(7)
-	assert.NoError(t, err)
-	assert.Len(t, users, 1)
-	assert.EqualValues(t, 5, users[0].ID)
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
 }
diff --git a/models/organization/main_test.go b/models/organization/main_test.go
new file mode 100644
index 0000000000..a2cb911021
--- /dev/null
+++ b/models/organization/main_test.go
@@ -0,0 +1,24 @@
+// 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 organization
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models/unittest"
+)
+
+func TestMain(m *testing.M) {
+	unittest.MainTest(m, filepath.Join("..", ".."),
+		"user.yml",
+		"org_user.yml",
+		"team.yml",
+		"team_repo.yml",
+		"team_unit.yml",
+		"team_user.yml",
+		"repository.yml",
+	)
+}
diff --git a/models/organization/org.go b/models/organization/org.go
new file mode 100644
index 0000000000..9e71bbe80d
--- /dev/null
+++ b/models/organization/org.go
@@ -0,0 +1,859 @@
+// 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 organization
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
+	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/log"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
+
+	"xorm.io/builder"
+)
+
+// ________                            .__                __  .__
+// \_____  \_______  _________    ____ |__|____________ _/  |_|__| ____   ____
+//  /   |   \_  __ \/ ___\__  \  /    \|  \___   /\__  \\   __\  |/  _ \ /    \
+// /    |    \  | \/ /_/  > __ \|   |  \  |/    /  / __ \|  | |  (  <_> )   |  \
+// \_______  /__|  \___  (____  /___|  /__/_____ \(____  /__| |__|\____/|___|  /
+//         \/     /_____/     \/     \/         \/     \/                    \/
+
+// ErrOrgNotExist represents a "OrgNotExist" kind of error.
+type ErrOrgNotExist struct {
+	ID   int64
+	Name string
+}
+
+// IsErrOrgNotExist checks if an error is a ErrOrgNotExist.
+func IsErrOrgNotExist(err error) bool {
+	_, ok := err.(ErrOrgNotExist)
+	return ok
+}
+
+func (err ErrOrgNotExist) Error() string {
+	return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
+}
+
+// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
+type ErrLastOrgOwner struct {
+	UID int64
+}
+
+// IsErrLastOrgOwner checks if an error is a ErrLastOrgOwner.
+func IsErrLastOrgOwner(err error) bool {
+	_, ok := err.(ErrLastOrgOwner)
+	return ok
+}
+
+func (err ErrLastOrgOwner) Error() string {
+	return fmt.Sprintf("user is the last member of owner team [uid: %d]", err.UID)
+}
+
+// ErrUserNotAllowedCreateOrg represents a "UserNotAllowedCreateOrg" kind of error.
+type ErrUserNotAllowedCreateOrg struct{}
+
+// IsErrUserNotAllowedCreateOrg checks if an error is an ErrUserNotAllowedCreateOrg.
+func IsErrUserNotAllowedCreateOrg(err error) bool {
+	_, ok := err.(ErrUserNotAllowedCreateOrg)
+	return ok
+}
+
+func (err ErrUserNotAllowedCreateOrg) Error() string {
+	return "user is not allowed to create organizations"
+}
+
+// Organization represents an organization
+type Organization user_model.User
+
+// OrgFromUser converts user to organization
+func OrgFromUser(user *user_model.User) *Organization {
+	return (*Organization)(user)
+}
+
+// TableName represents the real table name of Organization
+func (Organization) TableName() string {
+	return "user"
+}
+
+// IsOwnedBy returns true if given user is in the owner team.
+func (org *Organization) IsOwnedBy(uid int64) (bool, error) {
+	return IsOrganizationOwner(db.DefaultContext, org.ID, uid)
+}
+
+// IsOrgMember returns true if given user is member of organization.
+func (org *Organization) IsOrgMember(uid int64) (bool, error) {
+	return IsOrganizationMember(db.DefaultContext, org.ID, uid)
+}
+
+// CanCreateOrgRepo returns true if given user can create repo in organization
+func (org *Organization) CanCreateOrgRepo(uid int64) (bool, error) {
+	return CanCreateOrgRepo(org.ID, uid)
+}
+
+func (org *Organization) getTeam(ctx context.Context, name string) (*Team, error) {
+	return getTeam(ctx, org.ID, name)
+}
+
+// GetTeam returns named team of organization.
+func (org *Organization) GetTeam(name string) (*Team, error) {
+	return org.getTeam(db.DefaultContext, name)
+}
+
+func (org *Organization) getOwnerTeam(ctx context.Context) (*Team, error) {
+	return org.getTeam(ctx, OwnerTeamName)
+}
+
+// GetOwnerTeam returns owner team of organization.
+func (org *Organization) GetOwnerTeam() (*Team, error) {
+	return org.getOwnerTeam(db.DefaultContext)
+}
+
+// FindOrgTeams returns all teams of a given organization
+func FindOrgTeams(ctx context.Context, orgID int64) ([]*Team, error) {
+	var teams []*Team
+	return teams, db.GetEngine(ctx).
+		Where("org_id=?", orgID).
+		OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
+		Find(&teams)
+}
+
+// LoadTeams load teams if not loaded.
+func (org *Organization) LoadTeams() ([]*Team, error) {
+	return FindOrgTeams(db.DefaultContext, org.ID)
+}
+
+// GetMembers returns all members of organization.
+func (org *Organization) GetMembers() (user_model.UserList, map[int64]bool, error) {
+	return FindOrgMembers(&FindOrgMembersOpts{
+		OrgID: org.ID,
+	})
+}
+
+// HasMemberWithUserID returns true if user with userID is part of the u organisation.
+func (org *Organization) HasMemberWithUserID(userID int64) bool {
+	return org.hasMemberWithUserID(db.DefaultContext, userID)
+}
+
+func (org *Organization) hasMemberWithUserID(ctx context.Context, userID int64) bool {
+	isMember, err := IsOrganizationMember(ctx, org.ID, userID)
+	if err != nil {
+		log.Error("IsOrganizationMember: %v", err)
+		return false
+	}
+	return isMember
+}
+
+// AvatarLink returns the full avatar link with http host
+func (org *Organization) AvatarLink() string {
+	return org.AsUser().AvatarLink()
+}
+
+// HTMLURL returns the organization's full link.
+func (org *Organization) HTMLURL() string {
+	return org.AsUser().HTMLURL()
+}
+
+// OrganisationLink returns the organization sub page link.
+func (org *Organization) OrganisationLink() string {
+	return org.AsUser().OrganisationLink()
+}
+
+// ShortName ellipses username to length
+func (org *Organization) ShortName(length int) string {
+	return org.AsUser().ShortName(length)
+}
+
+// HomeLink returns the user or organization home page link.
+func (org *Organization) HomeLink() string {
+	return org.AsUser().HomeLink()
+}
+
+// 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 (org *Organization) CanCreateRepo() bool {
+	return org.AsUser().CanCreateRepo()
+}
+
+// FindOrgMembersOpts represensts find org members conditions
+type FindOrgMembersOpts struct {
+	db.ListOptions
+	OrgID      int64
+	PublicOnly bool
+}
+
+// CountOrgMembers counts the organization's members
+func CountOrgMembers(opts *FindOrgMembersOpts) (int64, error) {
+	sess := db.GetEngine(db.DefaultContext).Where("org_id=?", opts.OrgID)
+	if opts.PublicOnly {
+		sess.And("is_public = ?", true)
+	}
+	return sess.Count(new(OrgUser))
+}
+
+// FindOrgMembers loads organization members according conditions
+func FindOrgMembers(opts *FindOrgMembersOpts) (user_model.UserList, map[int64]bool, error) {
+	ous, err := GetOrgUsersByOrgID(opts)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	ids := make([]int64, len(ous))
+	idsIsPublic := make(map[int64]bool, len(ous))
+	for i, ou := range ous {
+		ids[i] = ou.UID
+		idsIsPublic[ou.UID] = ou.IsPublic
+	}
+
+	users, err := user_model.GetUsersByIDs(ids)
+	if err != nil {
+		return nil, nil, err
+	}
+	return users, idsIsPublic, nil
+}
+
+// AsUser returns the org as user object
+func (org *Organization) AsUser() *user_model.User {
+	return (*user_model.User)(org)
+}
+
+// DisplayName returns full name if it's not empty,
+// returns username otherwise.
+func (org *Organization) DisplayName() string {
+	return org.AsUser().DisplayName()
+}
+
+// CustomAvatarRelativePath returns user custom avatar relative path.
+func (org *Organization) CustomAvatarRelativePath() string {
+	return org.Avatar
+}
+
+// CreateOrganization creates record of a new organization.
+func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
+	if !owner.CanCreateOrganization() {
+		return ErrUserNotAllowedCreateOrg{}
+	}
+
+	if err = user_model.IsUsableUsername(org.Name); err != nil {
+		return err
+	}
+
+	isExist, err := user_model.IsUserExist(0, org.Name)
+	if err != nil {
+		return err
+	} else if isExist {
+		return user_model.ErrUserAlreadyExist{Name: org.Name}
+	}
+
+	org.LowerName = strings.ToLower(org.Name)
+	if org.Rands, err = user_model.GetUserSalt(); err != nil {
+		return err
+	}
+	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 = user_model.UserTypeOrganization
+
+	ctx, committer, err := db.TxContext()
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if err = user_model.DeleteUserRedirect(ctx, org.Name); err != nil {
+		return err
+	}
+
+	if err = db.Insert(ctx, org); err != nil {
+		return fmt.Errorf("insert organization: %v", err)
+	}
+	if err = user_model.GenerateRandomAvatarCtx(ctx, org.AsUser()); err != nil {
+		return fmt.Errorf("generate random avatar: %v", err)
+	}
+
+	// Add initial creator to organization and owner team.
+	if err = db.Insert(ctx, &OrgUser{
+		UID:   owner.ID,
+		OrgID: org.ID,
+	}); err != nil {
+		return fmt.Errorf("insert org-user relation: %v", err)
+	}
+
+	// Create default owner team.
+	t := &Team{
+		OrgID:                   org.ID,
+		LowerName:               strings.ToLower(OwnerTeamName),
+		Name:                    OwnerTeamName,
+		AccessMode:              perm.AccessModeOwner,
+		NumMembers:              1,
+		IncludesAllRepositories: true,
+		CanCreateOrgRepo:        true,
+	}
+	if err = db.Insert(ctx, t); err != nil {
+		return fmt.Errorf("insert owner team: %v", err)
+	}
+
+	// insert units for team
+	units := make([]TeamUnit, 0, len(unit.AllRepoUnitTypes))
+	for _, tp := range unit.AllRepoUnitTypes {
+		units = append(units, TeamUnit{
+			OrgID:  org.ID,
+			TeamID: t.ID,
+			Type:   tp,
+		})
+	}
+
+	if err = db.Insert(ctx, &units); err != nil {
+		return err
+	}
+
+	if err = db.Insert(ctx, &TeamUser{
+		UID:    owner.ID,
+		OrgID:  org.ID,
+		TeamID: t.ID,
+	}); err != nil {
+		return fmt.Errorf("insert team-user relation: %v", err)
+	}
+
+	return committer.Commit()
+}
+
+// GetOrgByName returns organization by given name.
+func GetOrgByName(name string) (*Organization, error) {
+	if len(name) == 0 {
+		return nil, ErrOrgNotExist{0, name}
+	}
+	u := &Organization{
+		LowerName: strings.ToLower(name),
+		Type:      user_model.UserTypeOrganization,
+	}
+	has, err := db.GetEngine(db.DefaultContext).Get(u)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrOrgNotExist{0, name}
+	}
+	return u, nil
+}
+
+// CountOrganizations returns number of organizations.
+func CountOrganizations() int64 {
+	count, _ := db.GetEngine(db.DefaultContext).
+		Where("type=1").
+		Count(new(Organization))
+	return count
+}
+
+// DeleteOrganization deletes models associated to an organization.
+func DeleteOrganization(ctx context.Context, org *Organization) error {
+	if org.Type != user_model.UserTypeOrganization {
+		return fmt.Errorf("%s is a user not an organization", org.Name)
+	}
+
+	if err := db.DeleteBeans(ctx,
+		&Team{OrgID: org.ID},
+		&OrgUser{OrgID: org.ID},
+		&TeamUser{OrgID: org.ID},
+		&TeamUnit{OrgID: org.ID},
+	); err != nil {
+		return fmt.Errorf("deleteBeans: %v", err)
+	}
+
+	if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
+		return fmt.Errorf("Delete: %v", err)
+	}
+
+	return nil
+}
+
+// GetOrgUserMaxAuthorizeLevel returns highest authorize level of user in an organization
+func (org *Organization) GetOrgUserMaxAuthorizeLevel(uid int64) (perm.AccessMode, error) {
+	var authorize perm.AccessMode
+	_, err := db.GetEngine(db.DefaultContext).
+		Select("max(team.authorize)").
+		Table("team").
+		Join("INNER", "team_user", "team_user.team_id = team.id").
+		Where("team_user.uid = ?", uid).
+		And("team_user.org_id = ?", org.ID).
+		Get(&authorize)
+	return authorize, err
+}
+
+// GetUsersWhoCanCreateOrgRepo returns users which are able to create repo in organization
+func GetUsersWhoCanCreateOrgRepo(ctx context.Context, orgID int64) ([]*user_model.User, error) {
+	users := make([]*user_model.User, 0, 10)
+	return users, db.GetEngine(ctx).
+		Join("INNER", "`team_user`", "`team_user`.uid=`user`.id").
+		Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
+		Where(builder.Eq{"team.can_create_org_repo": true}.Or(builder.Eq{"team.authorize": perm.AccessModeOwner})).
+		And("team_user.org_id = ?", orgID).Asc("`user`.name").Find(&users)
+}
+
+// SearchOrganizationsOptions options to filter organizations
+type SearchOrganizationsOptions struct {
+	db.ListOptions
+	All bool
+}
+
+// FindOrgOptions finds orgs options
+type FindOrgOptions struct {
+	db.ListOptions
+	UserID         int64
+	IncludePrivate bool
+}
+
+func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
+	cond := builder.Eq{"uid": userID}
+	if !includePrivate {
+		cond["is_public"] = true
+	}
+	return builder.Select("org_id").From("org_user").Where(cond)
+}
+
+func (opts FindOrgOptions) toConds() builder.Cond {
+	cond := builder.NewCond()
+	if opts.UserID > 0 {
+		cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
+	}
+	if !opts.IncludePrivate {
+		cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
+	}
+	return cond
+}
+
+// FindOrgs returns a list of organizations according given conditions
+func FindOrgs(opts FindOrgOptions) ([]*Organization, error) {
+	orgs := make([]*Organization, 0, 10)
+	sess := db.GetEngine(db.DefaultContext).
+		Where(opts.toConds()).
+		Asc("`user`.name")
+	if opts.Page > 0 && opts.PageSize > 0 {
+		sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1))
+	}
+	return orgs, sess.Find(&orgs)
+}
+
+// CountOrgs returns total count organizations according options
+func CountOrgs(opts FindOrgOptions) (int64, error) {
+	return db.GetEngine(db.DefaultContext).
+		Where(opts.toConds()).
+		Count(new(user_model.User))
+}
+
+func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*Organization, error) {
+	orgs := make([]*Organization, 0, 10)
+	return orgs, sess.
+		Join("INNER", "`team_user`", "`team_user`.org_id=`user`.id").
+		Join("INNER", "`team`", "`team`.id=`team_user`.team_id").
+		Where("`team_user`.uid=?", userID).
+		And("`team`.authorize=?", perm.AccessModeOwner).
+		Asc("`user`.name").
+		Find(&orgs)
+}
+
+// HasOrgOrUserVisible tells if the given user can see the given org or user
+func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
+	// Not SignedUser
+	if user == nil {
+		return orgOrUser.Visibility == structs.VisibleTypePublic
+	}
+
+	if user.IsAdmin || orgOrUser.ID == user.ID {
+		return true
+	}
+
+	if (orgOrUser.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !OrgFromUser(orgOrUser).hasMemberWithUserID(ctx, user.ID) {
+		return false
+	}
+	return true
+}
+
+// HasOrgsVisible tells if the given user can see at least one of the orgs provided
+func HasOrgsVisible(orgs []*Organization, user *user_model.User) bool {
+	if len(orgs) == 0 {
+		return false
+	}
+
+	for _, org := range orgs {
+		if HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user) {
+			return true
+		}
+	}
+	return false
+}
+
+// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
+func GetOwnedOrgsByUserID(userID int64) ([]*Organization, error) {
+	return getOwnedOrgsByUserID(db.GetEngine(db.DefaultContext), userID)
+}
+
+// GetOwnedOrgsByUserIDDesc returns a list of organizations are owned by
+// given user ID, ordered descending by the given condition.
+func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*Organization, error) {
+	return getOwnedOrgsByUserID(db.GetEngine(db.DefaultContext).Desc(desc), userID)
+}
+
+// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
+// are allowed to create repos.
+func GetOrgsCanCreateRepoByUserID(userID int64) ([]*Organization, error) {
+	orgs := make([]*Organization, 0, 10)
+
+	return orgs, db.GetEngine(db.DefaultContext).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
+		Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
+		Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
+		Where(builder.Eq{"`team_user`.uid": userID}).
+		And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
+		Asc("`user`.name").
+		Find(&orgs)
+}
+
+// GetOrgUsersByUserID returns all organization-user relations by user ID.
+func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) {
+	ous := make([]*OrgUser, 0, 10)
+	sess := db.GetEngine(db.DefaultContext).
+		Join("LEFT", "`user`", "`org_user`.org_id=`user`.id").
+		Where("`org_user`.uid=?", uid)
+	if !opts.All {
+		// Only show public organizations
+		sess.And("is_public=?", true)
+	}
+
+	if opts.PageSize != 0 {
+		sess = db.SetSessionPagination(sess, opts)
+	}
+
+	err := sess.
+		Asc("`user`.name").
+		Find(&ous)
+	return ous, err
+}
+
+// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
+func GetOrgUsersByOrgID(opts *FindOrgMembersOpts) ([]*OrgUser, error) {
+	return getOrgUsersByOrgID(db.GetEngine(db.DefaultContext), opts)
+}
+
+func getOrgUsersByOrgID(e db.Engine, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
+	sess := e.Where("org_id=?", opts.OrgID)
+	if opts.PublicOnly {
+		sess.And("is_public = ?", true)
+	}
+	if opts.ListOptions.PageSize > 0 {
+		sess = db.SetSessionPagination(sess, opts)
+
+		ous := make([]*OrgUser, 0, opts.PageSize)
+		return ous, sess.Find(&ous)
+	}
+
+	var ous []*OrgUser
+	return ous, sess.Find(&ous)
+}
+
+// ChangeOrgUserStatus changes public or private membership status.
+func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
+	ou := new(OrgUser)
+	has, err := db.GetEngine(db.DefaultContext).
+		Where("uid=?", uid).
+		And("org_id=?", orgID).
+		Get(ou)
+	if err != nil {
+		return err
+	} else if !has {
+		return nil
+	}
+
+	ou.IsPublic = public
+	_, err = db.GetEngine(db.DefaultContext).ID(ou.ID).Cols("is_public").Update(ou)
+	return err
+}
+
+// AddOrgUser adds new user to given organization.
+func AddOrgUser(orgID, uid int64) error {
+	isAlreadyMember, err := IsOrganizationMember(db.DefaultContext, orgID, uid)
+	if err != nil || isAlreadyMember {
+		return err
+	}
+
+	ctx, committer, err := db.TxContext()
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	ou := &OrgUser{
+		UID:      uid,
+		OrgID:    orgID,
+		IsPublic: setting.Service.DefaultOrgMemberVisible,
+	}
+
+	if err := db.Insert(ctx, ou); err != nil {
+		return err
+	} else if _, err = db.Exec(ctx, "UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
+		return err
+	}
+
+	return committer.Commit()
+}
+
+// GetOrgByIDCtx returns the user object by given ID if exists.
+func GetOrgByIDCtx(ctx context.Context, id int64) (*Organization, error) {
+	u := new(Organization)
+	has, err := db.GetEngine(ctx).ID(id).Get(u)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, user_model.ErrUserNotExist{
+			UID:   id,
+			Name:  "",
+			KeyID: 0,
+		}
+	}
+	return u, nil
+}
+
+// GetOrgByID returns the user object by given ID if exists.
+func GetOrgByID(id int64) (*Organization, error) {
+	return GetOrgByIDCtx(db.DefaultContext, id)
+}
+
+// RemoveOrgRepo removes all team-repository relations of organization.
+func RemoveOrgRepo(ctx context.Context, orgID, repoID int64) error {
+	teamRepos := make([]*TeamRepo, 0, 10)
+	e := db.GetEngine(ctx)
+	if err := e.Find(&teamRepos, &TeamRepo{OrgID: orgID, RepoID: repoID}); err != nil {
+		return err
+	}
+
+	if len(teamRepos) == 0 {
+		return nil
+	}
+
+	if _, err := e.Delete(&TeamRepo{
+		OrgID:  orgID,
+		RepoID: repoID,
+	}); err != nil {
+		return err
+	}
+
+	teamIDs := make([]int64, len(teamRepos))
+	for i, teamRepo := range teamRepos {
+		teamIDs[i] = teamRepo.TeamID
+	}
+
+	_, err := e.Decr("num_repos").In("id", teamIDs).Update(new(Team))
+	return err
+}
+
+func (org *Organization) getUserTeams(e db.Engine, userID int64, cols ...string) ([]*Team, error) {
+	teams := make([]*Team, 0, org.NumTeams)
+	return teams, e.
+		Where("`team_user`.org_id = ?", org.ID).
+		Join("INNER", "team_user", "`team_user`.team_id = team.id").
+		Join("INNER", "`user`", "`user`.id=team_user.uid").
+		And("`team_user`.uid = ?", userID).
+		Asc("`user`.name").
+		Cols(cols...).
+		Find(&teams)
+}
+
+func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]int64, error) {
+	teamIDs := make([]int64, 0, org.NumTeams)
+	return teamIDs, db.GetEngine(ctx).
+		Table("team").
+		Cols("team.id").
+		Where("`team_user`.org_id = ?", org.ID).
+		Join("INNER", "team_user", "`team_user`.team_id = team.id").
+		And("`team_user`.uid = ?", userID).
+		Find(&teamIDs)
+}
+
+// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
+func (org *Organization) TeamsWithAccessToRepo(repoID int64, mode perm.AccessMode) ([]*Team, error) {
+	return GetTeamsWithAccessToRepo(org.ID, repoID, mode)
+}
+
+// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
+func (org *Organization) GetUserTeamIDs(userID int64) ([]int64, error) {
+	return org.getUserTeamIDs(db.DefaultContext, userID)
+}
+
+// GetUserTeams returns all teams that belong to user,
+// and that the user has joined.
+func (org *Organization) GetUserTeams(userID int64) ([]*Team, error) {
+	return org.getUserTeams(db.GetEngine(db.DefaultContext), userID)
+}
+
+// AccessibleReposEnvironment operations involving the repositories that are
+// accessible to a particular user
+type AccessibleReposEnvironment interface {
+	CountRepos() (int64, error)
+	RepoIDs(page, pageSize int) ([]int64, error)
+	Repos(page, pageSize int) ([]*repo_model.Repository, error)
+	MirrorRepos() ([]*repo_model.Repository, error)
+	AddKeyword(keyword string)
+	SetSort(db.SearchOrderBy)
+}
+
+type accessibleReposEnv struct {
+	org     *Organization
+	user    *user_model.User
+	team    *Team
+	teamIDs []int64
+	e       db.Engine
+	keyword string
+	orderBy db.SearchOrderBy
+}
+
+// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
+// that are accessible to the specified user.
+func AccessibleReposEnv(ctx context.Context, org *Organization, userID int64) (AccessibleReposEnvironment, error) {
+	var user *user_model.User
+
+	if userID > 0 {
+		u, err := user_model.GetUserByIDCtx(ctx, userID)
+		if err != nil {
+			return nil, err
+		}
+		user = u
+	}
+
+	teamIDs, err := org.getUserTeamIDs(ctx, userID)
+	if err != nil {
+		return nil, err
+	}
+	return &accessibleReposEnv{
+		org:     org,
+		user:    user,
+		teamIDs: teamIDs,
+		e:       db.GetEngine(ctx),
+		orderBy: db.SearchOrderByRecentUpdated,
+	}, nil
+}
+
+// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
+// that are accessible to the specified team.
+func (org *Organization) AccessibleTeamReposEnv(team *Team) AccessibleReposEnvironment {
+	return &accessibleReposEnv{
+		org:     org,
+		team:    team,
+		e:       db.GetEngine(db.DefaultContext),
+		orderBy: db.SearchOrderByRecentUpdated,
+	}
+}
+
+func (env *accessibleReposEnv) cond() builder.Cond {
+	cond := builder.NewCond()
+	if env.team != nil {
+		cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
+	} else {
+		if env.user == nil || !env.user.IsRestricted {
+			cond = cond.Or(builder.Eq{
+				"`repository`.owner_id":   env.org.ID,
+				"`repository`.is_private": false,
+			})
+		}
+		if len(env.teamIDs) > 0 {
+			cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
+		}
+	}
+	if env.keyword != "" {
+		cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
+	}
+	return cond
+}
+
+func (env *accessibleReposEnv) CountRepos() (int64, error) {
+	repoCount, err := env.e.
+		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
+		Where(env.cond()).
+		Distinct("`repository`.id").
+		Count(&repo_model.Repository{})
+	if err != nil {
+		return 0, fmt.Errorf("count user repositories in organization: %v", err)
+	}
+	return repoCount, nil
+}
+
+func (env *accessibleReposEnv) RepoIDs(page, pageSize int) ([]int64, error) {
+	if page <= 0 {
+		page = 1
+	}
+
+	repoIDs := make([]int64, 0, pageSize)
+	return repoIDs, env.e.
+		Table("repository").
+		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
+		Where(env.cond()).
+		GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
+		OrderBy(string(env.orderBy)).
+		Limit(pageSize, (page-1)*pageSize).
+		Cols("`repository`.id").
+		Find(&repoIDs)
+}
+
+func (env *accessibleReposEnv) Repos(page, pageSize int) ([]*repo_model.Repository, error) {
+	repoIDs, err := env.RepoIDs(page, pageSize)
+	if err != nil {
+		return nil, fmt.Errorf("GetUserRepositoryIDs: %v", err)
+	}
+
+	repos := make([]*repo_model.Repository, 0, len(repoIDs))
+	if len(repoIDs) == 0 {
+		return repos, nil
+	}
+
+	return repos, env.e.
+		In("`repository`.id", repoIDs).
+		OrderBy(string(env.orderBy)).
+		Find(&repos)
+}
+
+func (env *accessibleReposEnv) MirrorRepoIDs() ([]int64, error) {
+	repoIDs := make([]int64, 0, 10)
+	return repoIDs, env.e.
+		Table("repository").
+		Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
+		Where(env.cond()).
+		GroupBy("`repository`.id, `repository`.updated_unix").
+		OrderBy(string(env.orderBy)).
+		Cols("`repository`.id").
+		Find(&repoIDs)
+}
+
+func (env *accessibleReposEnv) MirrorRepos() ([]*repo_model.Repository, error) {
+	repoIDs, err := env.MirrorRepoIDs()
+	if err != nil {
+		return nil, fmt.Errorf("MirrorRepoIDs: %v", err)
+	}
+
+	repos := make([]*repo_model.Repository, 0, len(repoIDs))
+	if len(repoIDs) == 0 {
+		return repos, nil
+	}
+
+	return repos, env.e.
+		In("`repository`.id", repoIDs).
+		Find(&repos)
+}
+
+func (env *accessibleReposEnv) AddKeyword(keyword string) {
+	env.keyword = keyword
+}
+
+func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
+	env.orderBy = orderBy
+}
diff --git a/models/organization/org_test.go b/models/organization/org_test.go
new file mode 100644
index 0000000000..71cdbd869f
--- /dev/null
+++ b/models/organization/org_test.go
@@ -0,0 +1,478 @@
+// 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 organization
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	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/structs"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestUser_IsOwnedBy(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	for _, testCase := range []struct {
+		OrgID         int64
+		UserID        int64
+		ExpectedOwner bool
+	}{
+		{3, 2, true},
+		{3, 1, false},
+		{3, 3, false},
+		{3, 4, false},
+		{2, 2, false}, // user2 is not an organization
+		{2, 3, false},
+	} {
+		org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: testCase.OrgID}).(*Organization)
+		isOwner, err := org.IsOwnedBy(testCase.UserID)
+		assert.NoError(t, err)
+		assert.Equal(t, testCase.ExpectedOwner, isOwner)
+	}
+}
+
+func TestUser_IsOrgMember(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	for _, testCase := range []struct {
+		OrgID          int64
+		UserID         int64
+		ExpectedMember bool
+	}{
+		{3, 2, true},
+		{3, 4, true},
+		{3, 1, false},
+		{3, 3, false},
+		{2, 2, false}, // user2 is not an organization
+		{2, 3, false},
+	} {
+		org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: testCase.OrgID}).(*Organization)
+		isMember, err := org.IsOrgMember(testCase.UserID)
+		assert.NoError(t, err)
+		assert.Equal(t, testCase.ExpectedMember, isMember)
+	}
+}
+
+func TestUser_GetTeam(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	team, err := org.GetTeam("team1")
+	assert.NoError(t, err)
+	assert.Equal(t, org.ID, team.OrgID)
+	assert.Equal(t, "team1", team.LowerName)
+
+	_, err = org.GetTeam("does not exist")
+	assert.True(t, IsErrTeamNotExist(err))
+
+	nonOrg := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 2}).(*Organization)
+	_, err = nonOrg.GetTeam("team")
+	assert.True(t, IsErrTeamNotExist(err))
+}
+
+func TestUser_GetOwnerTeam(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	team, err := org.GetOwnerTeam()
+	assert.NoError(t, err)
+	assert.Equal(t, org.ID, team.OrgID)
+
+	nonOrg := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 2}).(*Organization)
+	_, err = nonOrg.GetOwnerTeam()
+	assert.True(t, IsErrTeamNotExist(err))
+}
+
+func TestUser_GetTeams(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	teams, err := org.LoadTeams()
+	assert.NoError(t, err)
+	if assert.Len(t, teams, 4) {
+		assert.Equal(t, int64(1), teams[0].ID)
+		assert.Equal(t, int64(2), teams[1].ID)
+		assert.Equal(t, int64(12), teams[2].ID)
+		assert.Equal(t, int64(7), teams[3].ID)
+	}
+}
+
+func TestUser_GetMembers(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	members, _, err := org.GetMembers()
+	assert.NoError(t, err)
+	if assert.Len(t, members, 3) {
+		assert.Equal(t, int64(2), members[0].ID)
+		assert.Equal(t, int64(28), members[1].ID)
+		assert.Equal(t, int64(4), members[2].ID)
+	}
+}
+
+func TestGetOrgByName(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	org, err := GetOrgByName("user3")
+	assert.NoError(t, err)
+	assert.EqualValues(t, 3, org.ID)
+	assert.Equal(t, "user3", org.Name)
+
+	_, err = GetOrgByName("user2") // user2 is an individual
+	assert.True(t, IsErrOrgNotExist(err))
+
+	_, err = GetOrgByName("") // corner case
+	assert.True(t, IsErrOrgNotExist(err))
+}
+
+func TestCountOrganizations(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&user_model.User{})
+	assert.NoError(t, err)
+	assert.Equal(t, expected, CountOrganizations())
+}
+
+func TestIsOrganizationOwner(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(orgID, userID int64, expected bool) {
+		isOwner, err := IsOrganizationOwner(db.DefaultContext, orgID, userID)
+		assert.NoError(t, err)
+		assert.EqualValues(t, expected, isOwner)
+	}
+	test(3, 2, true)
+	test(3, 3, false)
+	test(6, 5, true)
+	test(6, 4, false)
+	test(unittest.NonexistentID, unittest.NonexistentID, false)
+}
+
+func TestIsOrganizationMember(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(orgID, userID int64, expected bool) {
+		isMember, err := IsOrganizationMember(db.DefaultContext, orgID, userID)
+		assert.NoError(t, err)
+		assert.EqualValues(t, expected, isMember)
+	}
+	test(3, 2, true)
+	test(3, 3, false)
+	test(3, 4, true)
+	test(6, 5, true)
+	test(6, 4, false)
+	test(unittest.NonexistentID, unittest.NonexistentID, false)
+}
+
+func TestIsPublicMembership(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(orgID, userID int64, expected bool) {
+		isMember, err := IsPublicMembership(orgID, userID)
+		assert.NoError(t, err)
+		assert.EqualValues(t, expected, isMember)
+	}
+	test(3, 2, true)
+	test(3, 3, false)
+	test(3, 4, false)
+	test(6, 5, true)
+	test(6, 4, false)
+	test(unittest.NonexistentID, unittest.NonexistentID, false)
+}
+
+func TestFindOrgs(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	orgs, err := FindOrgs(FindOrgOptions{
+		UserID:         4,
+		IncludePrivate: true,
+	})
+	assert.NoError(t, err)
+	if assert.Len(t, orgs, 1) {
+		assert.EqualValues(t, 3, orgs[0].ID)
+	}
+
+	orgs, err = FindOrgs(FindOrgOptions{
+		UserID:         4,
+		IncludePrivate: false,
+	})
+	assert.NoError(t, err)
+	assert.Len(t, orgs, 0)
+
+	total, err := CountOrgs(FindOrgOptions{
+		UserID:         4,
+		IncludePrivate: true,
+	})
+	assert.NoError(t, err)
+	assert.EqualValues(t, 1, total)
+}
+
+func TestGetOwnedOrgsByUserID(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	orgs, err := GetOwnedOrgsByUserID(2)
+	assert.NoError(t, err)
+	if assert.Len(t, orgs, 1) {
+		assert.EqualValues(t, 3, orgs[0].ID)
+	}
+
+	orgs, err = GetOwnedOrgsByUserID(4)
+	assert.NoError(t, err)
+	assert.Len(t, orgs, 0)
+}
+
+func TestGetOwnedOrgsByUserIDDesc(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	orgs, err := GetOwnedOrgsByUserIDDesc(5, "id")
+	assert.NoError(t, err)
+	if assert.Len(t, orgs, 2) {
+		assert.EqualValues(t, 7, orgs[0].ID)
+		assert.EqualValues(t, 6, orgs[1].ID)
+	}
+
+	orgs, err = GetOwnedOrgsByUserIDDesc(4, "id")
+	assert.NoError(t, err)
+	assert.Len(t, orgs, 0)
+}
+
+func TestGetOrgUsersByUserID(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	orgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: true})
+	assert.NoError(t, err)
+	if assert.Len(t, orgUsers, 2) {
+		assert.Equal(t, OrgUser{
+			ID:       orgUsers[0].ID,
+			OrgID:    6,
+			UID:      5,
+			IsPublic: true,
+		}, *orgUsers[0])
+		assert.Equal(t, OrgUser{
+			ID:       orgUsers[1].ID,
+			OrgID:    7,
+			UID:      5,
+			IsPublic: false,
+		}, *orgUsers[1])
+	}
+
+	publicOrgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: false})
+	assert.NoError(t, err)
+	assert.Len(t, publicOrgUsers, 1)
+	assert.Equal(t, *orgUsers[0], *publicOrgUsers[0])
+
+	orgUsers, err = GetOrgUsersByUserID(1, &SearchOrganizationsOptions{All: true})
+	assert.NoError(t, err)
+	assert.Len(t, orgUsers, 0)
+}
+
+func TestGetOrgUsersByOrgID(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	orgUsers, err := GetOrgUsersByOrgID(&FindOrgMembersOpts{
+		ListOptions: db.ListOptions{},
+		OrgID:       3,
+		PublicOnly:  false,
+	})
+	assert.NoError(t, err)
+	if assert.Len(t, orgUsers, 3) {
+		assert.Equal(t, OrgUser{
+			ID:       orgUsers[0].ID,
+			OrgID:    3,
+			UID:      2,
+			IsPublic: true,
+		}, *orgUsers[0])
+		assert.Equal(t, OrgUser{
+			ID:       orgUsers[1].ID,
+			OrgID:    3,
+			UID:      4,
+			IsPublic: false,
+		}, *orgUsers[1])
+	}
+
+	orgUsers, err = GetOrgUsersByOrgID(&FindOrgMembersOpts{
+		ListOptions: db.ListOptions{},
+		OrgID:       unittest.NonexistentID,
+		PublicOnly:  false,
+	})
+	assert.NoError(t, err)
+	assert.Len(t, orgUsers, 0)
+}
+
+func TestChangeOrgUserStatus(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testSuccess := func(orgID, userID int64, public bool) {
+		assert.NoError(t, ChangeOrgUserStatus(orgID, userID, public))
+		orgUser := unittest.AssertExistsAndLoadBean(t, &OrgUser{OrgID: orgID, UID: userID}).(*OrgUser)
+		assert.Equal(t, public, orgUser.IsPublic)
+	}
+
+	testSuccess(3, 2, false)
+	testSuccess(3, 2, false)
+	testSuccess(3, 4, true)
+	assert.NoError(t, ChangeOrgUserStatus(unittest.NonexistentID, unittest.NonexistentID, true))
+}
+
+func TestUser_GetUserTeamIDs(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	testSuccess := func(userID int64, expected []int64) {
+		teamIDs, err := org.GetUserTeamIDs(userID)
+		assert.NoError(t, err)
+		assert.Equal(t, expected, teamIDs)
+	}
+	testSuccess(2, []int64{1, 2})
+	testSuccess(4, []int64{2})
+	testSuccess(unittest.NonexistentID, []int64{})
+}
+
+func TestAccessibleReposEnv_CountRepos(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	testSuccess := func(userID, expectedCount int64) {
+		env, err := AccessibleReposEnv(db.DefaultContext, org, userID)
+		assert.NoError(t, err)
+		count, err := env.CountRepos()
+		assert.NoError(t, err)
+		assert.EqualValues(t, expectedCount, count)
+	}
+	testSuccess(2, 3)
+	testSuccess(4, 2)
+}
+
+func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	testSuccess := func(userID, _, pageSize int64, expectedRepoIDs []int64) {
+		env, err := AccessibleReposEnv(db.DefaultContext, org, userID)
+		assert.NoError(t, err)
+		repoIDs, err := env.RepoIDs(1, 100)
+		assert.NoError(t, err)
+		assert.Equal(t, expectedRepoIDs, repoIDs)
+	}
+	testSuccess(2, 1, 100, []int64{3, 5, 32})
+	testSuccess(4, 0, 100, []int64{3, 32})
+}
+
+func TestAccessibleReposEnv_Repos(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	testSuccess := func(userID int64, expectedRepoIDs []int64) {
+		env, err := AccessibleReposEnv(db.DefaultContext, org, userID)
+		assert.NoError(t, err)
+		repos, err := env.Repos(1, 100)
+		assert.NoError(t, err)
+		expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
+		for i, repoID := range expectedRepoIDs {
+			expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
+				&repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+		}
+		assert.Equal(t, expectedRepos, repos)
+	}
+	testSuccess(2, []int64{3, 5, 32})
+	testSuccess(4, []int64{3, 32})
+}
+
+func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	org := unittest.AssertExistsAndLoadBean(t, &Organization{ID: 3}).(*Organization)
+	testSuccess := func(userID int64, expectedRepoIDs []int64) {
+		env, err := AccessibleReposEnv(db.DefaultContext, org, userID)
+		assert.NoError(t, err)
+		repos, err := env.MirrorRepos()
+		assert.NoError(t, err)
+		expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
+		for i, repoID := range expectedRepoIDs {
+			expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
+				&repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+		}
+		assert.Equal(t, expectedRepos, repos)
+	}
+	testSuccess(2, []int64{5})
+	testSuccess(4, []int64{})
+}
+
+func TestHasOrgVisibleTypePublic(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	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{
+		Name:       newOrgName,
+		Visibility: structs.VisibleTypePublic,
+	}
+
+	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: user_model.UserTypeOrganization}).(*Organization)
+	test1 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
+	test2 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
+	test3 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
+	assert.True(t, test1) // owner of org
+	assert.True(t, test2) // user not a part of org
+	assert.True(t, test3) // logged out user
+}
+
+func TestHasOrgVisibleTypeLimited(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	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{
+		Name:       newOrgName,
+		Visibility: structs.VisibleTypeLimited,
+	}
+
+	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: user_model.UserTypeOrganization}).(*Organization)
+	test1 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
+	test2 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
+	test3 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
+	assert.True(t, test1)  // owner of org
+	assert.True(t, test2)  // user not a part of org
+	assert.False(t, test3) // logged out user
+}
+
+func TestHasOrgVisibleTypePrivate(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	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{
+		Name:       newOrgName,
+		Visibility: structs.VisibleTypePrivate,
+	}
+
+	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: user_model.UserTypeOrganization}).(*Organization)
+	test1 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
+	test2 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
+	test3 := HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
+	assert.True(t, test1)  // owner of org
+	assert.False(t, test2) // user not a part of org
+	assert.False(t, test3) // logged out user
+}
+
+func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	users, err := GetUsersWhoCanCreateOrgRepo(db.DefaultContext, 3)
+	assert.NoError(t, err)
+	assert.Len(t, users, 2)
+	var ids []int64
+	for i := range users {
+		ids = append(ids, users[i].ID)
+	}
+	assert.ElementsMatch(t, ids, []int64{2, 28})
+
+	users, err = GetUsersWhoCanCreateOrgRepo(db.DefaultContext, 7)
+	assert.NoError(t, err)
+	assert.Len(t, users, 1)
+	assert.EqualValues(t, 5, users[0].ID)
+}
diff --git a/models/organization/org_user.go b/models/organization/org_user.go
new file mode 100644
index 0000000000..b679246d0b
--- /dev/null
+++ b/models/organization/org_user.go
@@ -0,0 +1,83 @@
+// Copyright 2022 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 organization
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
+	"code.gitea.io/gitea/modules/log"
+
+	"xorm.io/builder"
+)
+
+// ________                ____ ___
+// \_____  \_______  ____ |    |   \______ ___________
+//  /   |   \_  __ \/ ___\|    |   /  ___// __ \_  __ \
+// /    |    \  | \/ /_/  >    |  /\___ \\  ___/|  | \/
+// \_______  /__|  \___  /|______//____  >\___  >__|
+//         \/     /_____/              \/     \/
+
+// OrgUser represents an organization-user relation.
+type OrgUser struct {
+	ID       int64 `xorm:"pk autoincr"`
+	UID      int64 `xorm:"INDEX UNIQUE(s)"`
+	OrgID    int64 `xorm:"INDEX UNIQUE(s)"`
+	IsPublic bool  `xorm:"INDEX"`
+}
+
+func init() {
+	db.RegisterModel(new(OrgUser))
+}
+
+// GetOrganizationCount returns count of membership of organization of the user.
+func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
+	return db.GetEngine(ctx).
+		Where("uid=?", u.ID).
+		Count(new(OrgUser))
+}
+
+// IsOrganizationOwner returns true if given user is in the owner team.
+func IsOrganizationOwner(ctx context.Context, orgID, uid int64) (bool, error) {
+	ownerTeam, err := GetOwnerTeam(ctx, orgID)
+	if err != nil {
+		if IsErrTeamNotExist(err) {
+			log.Error("Organization does not have owner team: %d", orgID)
+			return false, nil
+		}
+		return false, err
+	}
+	return IsTeamMember(ctx, orgID, ownerTeam.ID, uid)
+}
+
+// IsOrganizationMember returns true if given user is member of organization.
+func IsOrganizationMember(ctx context.Context, orgID, uid int64) (bool, error) {
+	return db.GetEngine(ctx).
+		Where("uid=?", uid).
+		And("org_id=?", orgID).
+		Table("org_user").
+		Exist()
+}
+
+// IsPublicMembership returns true if the given user's membership of given org is public.
+func IsPublicMembership(orgID, uid int64) (bool, error) {
+	return db.GetEngine(db.DefaultContext).
+		Where("uid=?", uid).
+		And("org_id=?", orgID).
+		And("is_public=?", true).
+		Table("org_user").
+		Exist()
+}
+
+// CanCreateOrgRepo returns true if user can create repo in organization
+func CanCreateOrgRepo(orgID, uid int64) (bool, error) {
+	return db.GetEngine(db.DefaultContext).
+		Where(builder.Eq{"team.can_create_org_repo": true}).
+		Join("INNER", "team_user", "team_user.team_id = team.id").
+		And("team_user.uid = ?", uid).
+		And("team_user.org_id = ?", orgID).
+		Exist(new(Team))
+}
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
new file mode 100644
index 0000000000..b323002934
--- /dev/null
+++ b/models/organization/org_user_test.go
@@ -0,0 +1,72 @@
+// Copyright 2022 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 organization
+
+import (
+	"fmt"
+	"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"
+)
+
+func TestUserIsPublicMember(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	tt := []struct {
+		uid      int64
+		orgid    int64
+		expected bool
+	}{
+		{2, 3, true},
+		{4, 3, false},
+		{5, 6, true},
+		{5, 7, false},
+	}
+	for _, v := range tt {
+		t.Run(fmt.Sprintf("UserId%dIsPublicMemberOf%d", v.uid, v.orgid), func(t *testing.T) {
+			testUserIsPublicMember(t, v.uid, v.orgid, v.expected)
+		})
+	}
+}
+
+func testUserIsPublicMember(t *testing.T, uid, orgID int64, expected bool) {
+	user, err := user_model.GetUserByID(uid)
+	assert.NoError(t, err)
+	is, err := IsPublicMembership(orgID, user.ID)
+	assert.NoError(t, err)
+	assert.Equal(t, expected, is)
+}
+
+func TestIsUserOrgOwner(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	tt := []struct {
+		uid      int64
+		orgid    int64
+		expected bool
+	}{
+		{2, 3, true},
+		{4, 3, false},
+		{5, 6, true},
+		{5, 7, true},
+	}
+	for _, v := range tt {
+		t.Run(fmt.Sprintf("UserId%dIsOrgOwnerOf%d", v.uid, v.orgid), func(t *testing.T) {
+			testIsUserOrgOwner(t, v.uid, v.orgid, v.expected)
+		})
+	}
+}
+
+func testIsUserOrgOwner(t *testing.T, uid, orgID int64, expected bool) {
+	user, err := user_model.GetUserByID(uid)
+	assert.NoError(t, err)
+	is, err := IsOrganizationOwner(db.DefaultContext, orgID, user.ID)
+	assert.NoError(t, err)
+	assert.Equal(t, expected, is)
+}
diff --git a/models/organization/team.go b/models/organization/team.go
new file mode 100644
index 0000000000..1617005841
--- /dev/null
+++ b/models/organization/team.go
@@ -0,0 +1,359 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Copyright 2016 The Gogs 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 organization
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
+	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/log"
+
+	"xorm.io/builder"
+)
+
+// ___________
+// \__    ___/___ _____    _____
+//   |    |_/ __ \\__  \  /     \
+//   |    |\  ___/ / __ \|  Y Y  \
+//   |____| \___  >____  /__|_|  /
+//              \/     \/      \/
+
+// ErrTeamAlreadyExist represents a "TeamAlreadyExist" kind of error.
+type ErrTeamAlreadyExist struct {
+	OrgID int64
+	Name  string
+}
+
+// IsErrTeamAlreadyExist checks if an error is a ErrTeamAlreadyExist.
+func IsErrTeamAlreadyExist(err error) bool {
+	_, ok := err.(ErrTeamAlreadyExist)
+	return ok
+}
+
+func (err ErrTeamAlreadyExist) Error() string {
+	return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
+}
+
+// ErrTeamNotExist represents a "TeamNotExist" error
+type ErrTeamNotExist struct {
+	OrgID  int64
+	TeamID int64
+	Name   string
+}
+
+// IsErrTeamNotExist checks if an error is a ErrTeamNotExist.
+func IsErrTeamNotExist(err error) bool {
+	_, ok := err.(ErrTeamNotExist)
+	return ok
+}
+
+func (err ErrTeamNotExist) Error() string {
+	return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
+}
+
+// OwnerTeamName return the owner team name
+const OwnerTeamName = "Owners"
+
+// Team represents a organization team.
+type Team struct {
+	ID                      int64 `xorm:"pk autoincr"`
+	OrgID                   int64 `xorm:"INDEX"`
+	LowerName               string
+	Name                    string
+	Description             string
+	AccessMode              perm.AccessMode          `xorm:"'authorize'"`
+	Repos                   []*repo_model.Repository `xorm:"-"`
+	Members                 []*user_model.User       `xorm:"-"`
+	NumRepos                int
+	NumMembers              int
+	Units                   []*TeamUnit `xorm:"-"`
+	IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"`
+	CanCreateOrgRepo        bool        `xorm:"NOT NULL DEFAULT false"`
+}
+
+func init() {
+	db.RegisterModel(new(Team))
+	db.RegisterModel(new(TeamUser))
+	db.RegisterModel(new(TeamRepo))
+	db.RegisterModel(new(TeamUnit))
+}
+
+// SearchTeamOptions holds the search options
+type SearchTeamOptions struct {
+	db.ListOptions
+	UserID      int64
+	Keyword     string
+	OrgID       int64
+	IncludeDesc bool
+}
+
+// SearchTeam search for teams. Caller is responsible to check permissions.
+func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
+	if opts.Page <= 0 {
+		opts.Page = 1
+	}
+	if opts.PageSize == 0 {
+		// Default limit
+		opts.PageSize = 10
+	}
+
+	cond := builder.NewCond()
+
+	if len(opts.Keyword) > 0 {
+		lowerKeyword := strings.ToLower(opts.Keyword)
+		var keywordCond builder.Cond = builder.Like{"lower_name", lowerKeyword}
+		if opts.IncludeDesc {
+			keywordCond = keywordCond.Or(builder.Like{"LOWER(description)", lowerKeyword})
+		}
+		cond = cond.And(keywordCond)
+	}
+
+	cond = cond.And(builder.Eq{"org_id": opts.OrgID})
+
+	sess := db.GetEngine(db.DefaultContext)
+
+	count, err := sess.
+		Where(cond).
+		Count(new(Team))
+	if err != nil {
+		return nil, 0, err
+	}
+
+	sess = sess.Where(cond)
+	if opts.PageSize == -1 {
+		opts.PageSize = int(count)
+	} else {
+		sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
+	}
+
+	teams := make([]*Team, 0, opts.PageSize)
+	if err = sess.
+		OrderBy("lower_name").
+		Find(&teams); err != nil {
+		return nil, 0, err
+	}
+
+	return teams, count, nil
+}
+
+// ColorFormat provides a basic color format for a Team
+func (t *Team) ColorFormat(s fmt.State) {
+	if t == nil {
+		log.ColorFprintf(s, "%d:%s (OrgID: %d) %-v",
+			log.NewColoredIDValue(0),
+			"<nil>",
+			log.NewColoredIDValue(0),
+			0)
+		return
+	}
+	log.ColorFprintf(s, "%d:%s (OrgID: %d) %-v",
+		log.NewColoredIDValue(t.ID),
+		t.Name,
+		log.NewColoredIDValue(t.OrgID),
+		t.AccessMode)
+}
+
+// GetUnits return a list of available units for a team
+func (t *Team) GetUnits() error {
+	return t.getUnits(db.DefaultContext)
+}
+
+func (t *Team) getUnits(ctx context.Context) (err error) {
+	if t.Units != nil {
+		return nil
+	}
+
+	t.Units, err = getUnitsByTeamID(ctx, t.ID)
+	return err
+}
+
+// GetUnitNames returns the team units names
+func (t *Team) GetUnitNames() (res []string) {
+	if t.AccessMode >= perm.AccessModeAdmin {
+		return unit.AllUnitKeyNames()
+	}
+
+	for _, u := range t.Units {
+		res = append(res, unit.Units[u.Type].NameKey)
+	}
+	return
+}
+
+// GetUnitsMap returns the team units permissions
+func (t *Team) GetUnitsMap() map[string]string {
+	m := make(map[string]string)
+	if t.AccessMode >= perm.AccessModeAdmin {
+		for _, u := range unit.Units {
+			m[u.NameKey] = t.AccessMode.String()
+		}
+	} else {
+		for _, u := range t.Units {
+			m[u.Unit().NameKey] = u.AccessMode.String()
+		}
+	}
+	return m
+}
+
+// IsOwnerTeam returns true if team is owner team.
+func (t *Team) IsOwnerTeam() bool {
+	return t.Name == OwnerTeamName
+}
+
+// IsMember returns true if given user is a member of team.
+func (t *Team) IsMember(userID int64) bool {
+	isMember, err := IsTeamMember(db.DefaultContext, t.OrgID, t.ID, userID)
+	if err != nil {
+		log.Error("IsMember: %v", err)
+		return false
+	}
+	return isMember
+}
+
+// GetRepositoriesCtx returns paginated repositories in team of organization.
+func (t *Team) GetRepositoriesCtx(ctx context.Context) (err error) {
+	if t.Repos != nil {
+		return nil
+	}
+	t.Repos, err = GetTeamRepositories(ctx, &SearchTeamRepoOptions{
+		TeamID: t.ID,
+	})
+	return
+}
+
+// GetMembersCtx returns paginated members in team of organization.
+func (t *Team) GetMembersCtx(ctx context.Context) (err error) {
+	t.Members, err = GetTeamMembers(ctx, &SearchMembersOptions{
+		TeamID: t.ID,
+	})
+	return err
+}
+
+// UnitEnabled returns if the team has the given unit type enabled
+func (t *Team) UnitEnabled(tp unit.Type) bool {
+	return t.unitEnabled(db.DefaultContext, tp)
+}
+
+func (t *Team) unitEnabled(ctx context.Context, tp unit.Type) bool {
+	return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone
+}
+
+// UnitAccessMode returns if the team has the given unit type enabled
+func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode {
+	if err := t.getUnits(ctx); err != nil {
+		log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
+	}
+
+	for _, unit := range t.Units {
+		if unit.Type == tp {
+			return unit.AccessMode
+		}
+	}
+	return perm.AccessModeNone
+}
+
+// IsUsableTeamName tests if a name could be as team name
+func IsUsableTeamName(name string) error {
+	switch name {
+	case "new":
+		return db.ErrNameReserved{Name: name}
+	default:
+		return nil
+	}
+}
+
+func getTeam(ctx context.Context, orgID int64, name string) (*Team, error) {
+	t := &Team{
+		OrgID:     orgID,
+		LowerName: strings.ToLower(name),
+	}
+	has, err := db.GetByBean(ctx, t)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrTeamNotExist{orgID, 0, name}
+	}
+	return t, nil
+}
+
+// GetTeam returns team by given team name and organization.
+func GetTeam(orgID int64, name string) (*Team, error) {
+	return getTeam(db.DefaultContext, orgID, name)
+}
+
+// GetTeamIDsByNames returns a slice of team ids corresponds to names.
+func GetTeamIDsByNames(orgID int64, names []string, ignoreNonExistent bool) ([]int64, error) {
+	ids := make([]int64, 0, len(names))
+	for _, name := range names {
+		u, err := GetTeam(orgID, name)
+		if err != nil {
+			if ignoreNonExistent {
+				continue
+			} else {
+				return nil, err
+			}
+		}
+		ids = append(ids, u.ID)
+	}
+	return ids, nil
+}
+
+// GetOwnerTeam returns team by given team name and organization.
+func GetOwnerTeam(ctx context.Context, orgID int64) (*Team, error) {
+	return getTeam(ctx, orgID, OwnerTeamName)
+}
+
+// GetTeamByIDCtx returns team by given ID.
+func GetTeamByIDCtx(ctx context.Context, teamID int64) (*Team, error) {
+	t := new(Team)
+	has, err := db.GetEngine(ctx).ID(teamID).Get(t)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrTeamNotExist{0, teamID, ""}
+	}
+	return t, nil
+}
+
+// GetTeamByID returns team by given ID.
+func GetTeamByID(teamID int64) (*Team, error) {
+	return GetTeamByIDCtx(db.DefaultContext, teamID)
+}
+
+// GetTeamNamesByID returns team's lower name from a list of team ids.
+func GetTeamNamesByID(teamIDs []int64) ([]string, error) {
+	if len(teamIDs) == 0 {
+		return []string{}, nil
+	}
+
+	var teamNames []string
+	err := db.GetEngine(db.DefaultContext).Table("team").
+		Select("lower_name").
+		In("id", teamIDs).
+		Asc("name").
+		Find(&teamNames)
+
+	return teamNames, err
+}
+
+func getRepoTeams(e db.Engine, repo *repo_model.Repository) (teams []*Team, err error) {
+	return teams, e.
+		Join("INNER", "team_repo", "team_repo.team_id = team.id").
+		Where("team.org_id = ?", repo.OwnerID).
+		And("team_repo.repo_id=?", repo.ID).
+		OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
+		Find(&teams)
+}
+
+// GetRepoTeams gets the list of teams that has access to the repository
+func GetRepoTeams(repo *repo_model.Repository) ([]*Team, error) {
+	return getRepoTeams(db.GetEngine(db.DefaultContext), repo)
+}
diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go
new file mode 100644
index 0000000000..e6ac751829
--- /dev/null
+++ b/models/organization/team_repo.go
@@ -0,0 +1,84 @@
+// Copyright 2022 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 organization
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
+	repo_model "code.gitea.io/gitea/models/repo"
+	"xorm.io/builder"
+)
+
+// TeamRepo represents an team-repository relation.
+type TeamRepo struct {
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	RepoID int64 `xorm:"UNIQUE(s)"`
+}
+
+// HasTeamRepo returns true if given repository belongs to team.
+func HasTeamRepo(ctx context.Context, orgID, teamID, repoID int64) bool {
+	has, _ := db.GetEngine(ctx).
+		Where("org_id=?", orgID).
+		And("team_id=?", teamID).
+		And("repo_id=?", repoID).
+		Get(new(TeamRepo))
+	return has
+}
+
+type SearchTeamRepoOptions struct {
+	db.ListOptions
+	TeamID int64
+}
+
+// GetRepositories returns paginated repositories in team of organization.
+func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) ([]*repo_model.Repository, error) {
+	sess := db.GetEngine(ctx)
+	if opts.TeamID > 0 {
+		sess = sess.In("id",
+			builder.Select("repo_id").
+				From("team_repo").
+				Where(builder.Eq{"team_id": opts.TeamID}),
+		)
+	}
+	if opts.PageSize > 0 {
+		sess.Limit(opts.PageSize, opts.Page*opts.PageSize)
+	}
+	var repos []*repo_model.Repository
+	return repos, sess.OrderBy("repository.name").
+		Find(&repos)
+}
+
+// AddTeamRepo addes a repo for an organization's team
+func AddTeamRepo(ctx context.Context, orgID, teamID, repoID int64) error {
+	_, err := db.GetEngine(ctx).Insert(&TeamRepo{
+		OrgID:  orgID,
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// RemoveTeamRepo remove repository from team
+func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
+	_, err := db.DeleteByBean(ctx, &TeamRepo{
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
+func GetTeamsWithAccessToRepo(orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) {
+	teams := make([]*Team, 0, 5)
+	return teams, db.GetEngine(db.DefaultContext).Where("team.authorize >= ?", mode).
+		Join("INNER", "team_repo", "team_repo.team_id = team.id").
+		And("team_repo.org_id = ?", orgID).
+		And("team_repo.repo_id = ?", repoID).
+		Find(&teams)
+}
diff --git a/models/organization/team_test.go b/models/organization/team_test.go
new file mode 100644
index 0000000000..bbf9f789f6
--- /dev/null
+++ b/models/organization/team_test.go
@@ -0,0 +1,199 @@
+// 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 organization
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/unittest"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestTeam_IsOwnerTeam(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
+	assert.True(t, team.IsOwnerTeam())
+
+	team = unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
+	assert.False(t, team.IsOwnerTeam())
+}
+
+func TestTeam_IsMember(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 1}).(*Team)
+	assert.True(t, team.IsMember(2))
+	assert.False(t, team.IsMember(4))
+	assert.False(t, team.IsMember(unittest.NonexistentID))
+
+	team = unittest.AssertExistsAndLoadBean(t, &Team{ID: 2}).(*Team)
+	assert.True(t, team.IsMember(2))
+	assert.True(t, team.IsMember(4))
+	assert.False(t, team.IsMember(unittest.NonexistentID))
+}
+
+func TestTeam_GetRepositories(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	test := func(teamID int64) {
+		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		assert.NoError(t, team.GetRepositoriesCtx(db.DefaultContext))
+		assert.Len(t, team.Repos, team.NumRepos)
+		for _, repo := range team.Repos {
+			unittest.AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repo.ID})
+		}
+	}
+	test(1)
+	test(3)
+}
+
+func TestTeam_GetMembers(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	test := func(teamID int64) {
+		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		assert.NoError(t, team.GetMembersCtx(db.DefaultContext))
+		assert.Len(t, team.Members, team.NumMembers)
+		for _, member := range team.Members {
+			unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID})
+		}
+	}
+	test(1)
+	test(3)
+}
+
+func TestGetTeam(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testSuccess := func(orgID int64, name string) {
+		team, err := GetTeam(orgID, name)
+		assert.NoError(t, err)
+		assert.EqualValues(t, orgID, team.OrgID)
+		assert.Equal(t, name, team.Name)
+	}
+	testSuccess(3, "Owners")
+	testSuccess(3, "team1")
+
+	_, err := GetTeam(3, "nonexistent")
+	assert.Error(t, err)
+	_, err = GetTeam(unittest.NonexistentID, "Owners")
+	assert.Error(t, err)
+}
+
+func TestGetTeamByID(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	testSuccess := func(teamID int64) {
+		team, err := GetTeamByID(teamID)
+		assert.NoError(t, err)
+		assert.EqualValues(t, teamID, team.ID)
+	}
+	testSuccess(1)
+	testSuccess(2)
+	testSuccess(3)
+	testSuccess(4)
+
+	_, err := GetTeamByID(unittest.NonexistentID)
+	assert.Error(t, err)
+}
+
+func TestIsTeamMember(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(orgID, teamID, userID int64, expected bool) {
+		isMember, err := IsTeamMember(db.DefaultContext, orgID, teamID, userID)
+		assert.NoError(t, err)
+		assert.Equal(t, expected, isMember)
+	}
+
+	test(3, 1, 2, true)
+	test(3, 1, 4, false)
+	test(3, 1, unittest.NonexistentID, false)
+
+	test(3, 2, 2, true)
+	test(3, 2, 4, true)
+
+	test(3, unittest.NonexistentID, unittest.NonexistentID, false)
+	test(unittest.NonexistentID, unittest.NonexistentID, unittest.NonexistentID, false)
+}
+
+func TestGetTeamMembers(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	test := func(teamID int64) {
+		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		members, err := GetTeamMembers(db.DefaultContext, &SearchMembersOptions{
+			TeamID: teamID,
+		})
+		assert.NoError(t, err)
+		assert.Len(t, members, team.NumMembers)
+		for _, member := range members {
+			unittest.AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID})
+		}
+	}
+	test(1)
+	test(3)
+}
+
+func TestGetUserTeams(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(userID int64) {
+		teams, _, err := SearchTeam(&SearchTeamOptions{UserID: userID})
+		assert.NoError(t, err)
+		for _, team := range teams {
+			unittest.AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})
+		}
+	}
+	test(2)
+	test(5)
+	test(unittest.NonexistentID)
+}
+
+func TestGetUserOrgTeams(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+	test := func(orgID, userID int64) {
+		teams, err := GetUserOrgTeams(db.DefaultContext, orgID, userID)
+		assert.NoError(t, err)
+		for _, team := range teams {
+			assert.EqualValues(t, orgID, team.OrgID)
+			unittest.AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})
+		}
+	}
+	test(3, 2)
+	test(3, 4)
+	test(3, unittest.NonexistentID)
+}
+
+func TestHasTeamRepo(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	test := func(teamID, repoID int64, expected bool) {
+		team := unittest.AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
+		assert.Equal(t, expected, HasTeamRepo(db.DefaultContext, team.OrgID, teamID, repoID))
+	}
+	test(1, 1, false)
+	test(1, 3, true)
+	test(1, 5, true)
+	test(1, unittest.NonexistentID, false)
+
+	test(2, 3, true)
+	test(2, 5, false)
+}
+
+func TestUsersInTeamsCount(t *testing.T) {
+	assert.NoError(t, unittest.PrepareTestDatabase())
+
+	test := func(teamIDs, userIDs []int64, expected int64) {
+		count, err := UsersInTeamsCount(teamIDs, userIDs)
+		assert.NoError(t, err)
+		assert.Equal(t, expected, count)
+	}
+
+	test([]int64{2}, []int64{1, 2, 3, 4}, 1)          // only userid 2
+	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
+	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
+}
diff --git a/models/organization/team_unit.go b/models/organization/team_unit.go
new file mode 100644
index 0000000000..a712ddb2eb
--- /dev/null
+++ b/models/organization/team_unit.go
@@ -0,0 +1,52 @@
+// Copyright 2022 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 organization
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/perm"
+	"code.gitea.io/gitea/models/unit"
+)
+
+// TeamUnit describes all units of a repository
+type TeamUnit struct {
+	ID         int64     `xorm:"pk autoincr"`
+	OrgID      int64     `xorm:"INDEX"`
+	TeamID     int64     `xorm:"UNIQUE(s)"`
+	Type       unit.Type `xorm:"UNIQUE(s)"`
+	AccessMode perm.AccessMode
+}
+
+// Unit returns Unit
+func (t *TeamUnit) Unit() unit.Unit {
+	return unit.Units[t.Type]
+}
+
+func getUnitsByTeamID(ctx context.Context, teamID int64) (units []*TeamUnit, err error) {
+	return units, db.GetEngine(ctx).Where("team_id = ?", teamID).Find(&units)
+}
+
+// UpdateTeamUnits updates a teams's units
+func UpdateTeamUnits(team *Team, units []TeamUnit) (err error) {
+	ctx, committer, err := db.TxContext()
+	if err != nil {
+		return err
+	}
+	defer committer.Close()
+
+	if _, err = db.GetEngine(ctx).Where("team_id = ?", team.ID).Delete(new(TeamUnit)); err != nil {
+		return err
+	}
+
+	if len(units) > 0 {
+		if err = db.Insert(ctx, units); err != nil {
+			return err
+		}
+	}
+
+	return committer.Commit()
+}
diff --git a/models/organization/team_user.go b/models/organization/team_user.go
new file mode 100644
index 0000000000..e972817f49
--- /dev/null
+++ b/models/organization/team_user.go
@@ -0,0 +1,109 @@
+// Copyright 2022 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 organization
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
+	"xorm.io/builder"
+)
+
+// TeamUser represents an team-user relation.
+type TeamUser struct {
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	UID    int64 `xorm:"UNIQUE(s)"`
+}
+
+// IsTeamMember returns true if given user is a member of team.
+func IsTeamMember(ctx context.Context, orgID, teamID, userID int64) (bool, error) {
+	return db.GetEngine(ctx).
+		Where("org_id=?", orgID).
+		And("team_id=?", teamID).
+		And("uid=?", userID).
+		Table("team_user").
+		Exist()
+}
+
+// GetTeamUsersByTeamID returns team users for a team
+func GetTeamUsersByTeamID(ctx context.Context, teamID int64) ([]*TeamUser, error) {
+	teamUsers := make([]*TeamUser, 0, 10)
+	return teamUsers, db.GetEngine(ctx).
+		Where("team_id=?", teamID).
+		Find(&teamUsers)
+}
+
+// SearchMembersOptions holds the search options
+type SearchMembersOptions struct {
+	db.ListOptions
+	TeamID int64
+}
+
+func (opts SearchMembersOptions) ToConds() builder.Cond {
+	cond := builder.NewCond()
+	if opts.TeamID > 0 {
+		cond = cond.And(builder.Eq{"": opts.TeamID})
+	}
+	return cond
+}
+
+// GetTeamMembers returns all members in given team of organization.
+func GetTeamMembers(ctx context.Context, opts *SearchMembersOptions) ([]*user_model.User, error) {
+	var members []*user_model.User
+	sess := db.GetEngine(ctx)
+	if opts.TeamID > 0 {
+		sess = sess.In("id",
+			builder.Select("uid").
+				From("team_user").
+				Where(builder.Eq{"team_id": opts.TeamID}),
+		)
+	}
+	if opts.PageSize > 0 && opts.Page > -1 {
+		sess = sess.Limit(opts.PageSize, opts.Page*opts.PageSize)
+	}
+	if err := sess.OrderBy("full_name, name").Find(&members); err != nil {
+		return nil, err
+	}
+	return members, nil
+}
+
+// GetUserOrgTeams returns all teams that user belongs to in given organization.
+func GetUserOrgTeams(ctx context.Context, orgID, userID int64) (teams []*Team, err error) {
+	return teams, db.GetEngine(ctx).
+		Join("INNER", "team_user", "team_user.team_id = team.id").
+		Where("team.org_id = ?", orgID).
+		And("team_user.uid=?", userID).
+		Find(&teams)
+}
+
+// GetUserRepoTeams returns user repo's teams
+func GetUserRepoTeams(ctx context.Context, orgID, userID, repoID int64) (teams []*Team, err error) {
+	return teams, db.GetEngine(ctx).
+		Join("INNER", "team_user", "team_user.team_id = team.id").
+		Join("INNER", "team_repo", "team_repo.team_id = team.id").
+		Where("team.org_id = ?", orgID).
+		And("team_user.uid=?", userID).
+		And("team_repo.repo_id=?", repoID).
+		Find(&teams)
+}
+
+// IsUserInTeams returns if a user in some teams
+func IsUserInTeams(ctx context.Context, userID int64, teamIDs []int64) (bool, error) {
+	return db.GetEngine(ctx).Where("uid=?", userID).In("team_id", teamIDs).Exist(new(TeamUser))
+}
+
+// UsersInTeamsCount counts the number of users which are in userIDs and teamIDs
+func UsersInTeamsCount(userIDs, teamIDs []int64) (int64, error) {
+	var ids []int64
+	if err := db.GetEngine(db.DefaultContext).In("uid", userIDs).In("team_id", teamIDs).
+		Table("team_user").
+		Cols("uid").GroupBy("uid").Find(&ids); err != nil {
+		return 0, err
+	}
+	return int64(len(ids)), nil
+}
diff --git a/models/protected_tag.go b/models/protected_tag.go
index c9cc0fa1ba..db6ff50462 100644
--- a/models/protected_tag.go
+++ b/models/protected_tag.go
@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/timeutil"
 
@@ -83,7 +84,7 @@ func IsUserAllowedModifyTag(pt *ProtectedTag, userID int64) (bool, error) {
 		return false, nil
 	}
 
-	in, err := IsUserInTeams(userID, pt.AllowlistTeamIDs)
+	in, err := organization.IsUserInTeams(db.DefaultContext, userID, pt.AllowlistTeamIDs)
 	if err != nil {
 		return false, err
 	}
diff --git a/models/repo.go b/models/repo.go
index d20e5f81d3..8e6f777f9b 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -20,6 +20,7 @@ import (
 	admin_model "code.gitea.io/gitea/models/admin"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -268,7 +269,7 @@ func GetReviewers(repo *repo_model.Repository, doerID, posterID int64) ([]*user_
 }
 
 // GetReviewerTeams get all teams can be requested to review
-func GetReviewerTeams(repo *repo_model.Repository) ([]*Team, error) {
+func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) {
 	if err := repo.GetOwner(db.DefaultContext); err != nil {
 		return nil, err
 	}
@@ -276,7 +277,7 @@ func GetReviewerTeams(repo *repo_model.Repository) ([]*Team, error) {
 		return nil, nil
 	}
 
-	teams, err := GetTeamsWithAccessToRepo(repo.OwnerID, repo.ID, perm.AccessModeRead)
+	teams, err := organization.GetTeamsWithAccessToRepo(repo.OwnerID, repo.ID, perm.AccessModeRead)
 	if err != nil {
 		return nil, err
 	}
@@ -313,7 +314,7 @@ func CanUserForkRepo(user *user_model.User, repo *repo_model.Repository) (bool,
 	if repo.OwnerID != user.ID && !repo_model.HasForkedRepo(user.ID, repo.ID) {
 		return true, nil
 	}
-	ownedOrgs, err := GetOrgsCanCreateRepoByUserID(user.ID)
+	ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(user.ID)
 	if err != nil {
 		return false, err
 	}
@@ -372,7 +373,7 @@ func CanUserDelete(repo *repo_model.Repository, user *user_model.User) (bool, er
 	}
 
 	if repo.Owner.IsOrganization() {
-		isOwner, err := OrgFromUser(repo.Owner).IsOwnedBy(user.ID)
+		isOwner, err := organization.OrgFromUser(repo.Owner).IsOwnedBy(user.ID)
 		if err != nil {
 			return false, err
 		} else if isOwner {
@@ -550,19 +551,19 @@ func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_
 
 	// Give access to all members in teams with access to all repositories.
 	if u.IsOrganization() {
-		teams, err := OrgFromUser(u).loadTeams(db.GetEngine(ctx))
+		teams, err := organization.FindOrgTeams(ctx, u.ID)
 		if err != nil {
 			return fmt.Errorf("loadTeams: %v", err)
 		}
 		for _, t := range teams {
 			if t.IncludesAllRepositories {
-				if err := t.addRepository(ctx, repo); err != nil {
+				if err := addRepository(ctx, t, repo); err != nil {
 					return fmt.Errorf("addRepository: %v", err)
 				}
 			}
 		}
 
-		if isAdmin, err := isUserRepoAdmin(db.GetEngine(ctx), repo, doer); err != nil {
+		if isAdmin, err := isUserRepoAdmin(ctx, repo, doer); err != nil {
 			return fmt.Errorf("isUserRepoAdmin: %v", err)
 		} else if !isAdmin {
 			// Make creator repo admin if it wasn't assigned automatically
@@ -768,14 +769,14 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
 	}
 
 	if org.IsOrganization() {
-		teams, err := OrgFromUser(org).loadTeams(sess)
+		teams, err := organization.FindOrgTeams(ctx, org.ID)
 		if err != nil {
 			return err
 		}
 		for _, t := range teams {
-			if !t.hasRepository(sess, repoID) {
+			if !hasRepository(ctx, t, repoID) {
 				continue
-			} else if err = t.removeRepository(ctx, repo, false); err != nil {
+			} else if err = removeRepository(ctx, t, repo, false); err != nil {
 				return err
 			}
 		}
@@ -1326,7 +1327,7 @@ func DeleteDeployKey(ctx context.Context, doer *user_model.User, id int64) error
 		if err != nil {
 			return fmt.Errorf("GetRepositoryByID: %v", err)
 		}
-		has, err := isUserRepoAdmin(sess, repo, doer)
+		has, err := isUserRepoAdmin(ctx, repo, doer)
 		if err != nil {
 			return fmt.Errorf("GetUserRepoPermission: %v", err)
 		} else if !has {
diff --git a/models/repo/repo.go b/models/repo/repo.go
index 8cd055c67b..8dd772a4ec 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -751,37 +751,3 @@ func countRepositories(userID int64, private bool) int64 {
 func CountRepositories(private bool) int64 {
 	return countRepositories(-1, private)
 }
-
-// CountUserRepositories returns number of repositories user owns.
-// Argument private only takes effect when it is false,
-// set it true to count all repositories.
-func CountUserRepositories(userID int64, private bool) int64 {
-	return countRepositories(userID, private)
-}
-
-func getRepositoryCount(e db.Engine, ownerID int64) (int64, error) {
-	return e.Count(&Repository{OwnerID: ownerID})
-}
-
-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_model.User) (int64, error) {
-	return e.Where("is_private = ?", true).Count(&Repository{OwnerID: u.ID})
-}
-
-// GetRepositoryCount returns the total number of repositories of user.
-func GetRepositoryCount(ctx context.Context, ownerID int64) (int64, error) {
-	return getRepositoryCount(db.GetEngine(ctx), ownerID)
-}
-
-// GetPublicRepositoryCount returns the total number of public repositories of user.
-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_model.User) (int64, error) {
-	return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u)
-}
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
new file mode 100644
index 0000000000..18a04f7267
--- /dev/null
+++ b/models/repo/user_repo.go
@@ -0,0 +1,87 @@
+// Copyright 2022 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 repo
+
+import (
+	"context"
+
+	"code.gitea.io/gitea/models/db"
+	user_model "code.gitea.io/gitea/models/user"
+)
+
+// 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).
+		Join("LEFT", "star", "`repository`.id=`star`.repo_id")
+	if !private {
+		sess = sess.And("is_private=?", false)
+	}
+
+	if listOptions.Page != 0 {
+		sess = db.SetSessionPagination(sess, &listOptions)
+
+		repos := make([]*Repository, 0, listOptions.PageSize)
+		return repos, sess.Find(&repos)
+	}
+
+	repos := make([]*Repository, 0, 10)
+	return repos, sess.Find(&repos)
+}
+
+// GetWatchedRepos returns the repos watched by a particular user
+func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([]*Repository, int64, error) {
+	sess := db.GetEngine(db.DefaultContext).Where("watch.user_id=?", userID).
+		And("`watch`.mode<>?", WatchModeDont).
+		Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
+	if !private {
+		sess = sess.And("is_private=?", false)
+	}
+
+	if listOptions.Page != 0 {
+		sess = db.SetSessionPagination(sess, &listOptions)
+
+		repos := make([]*Repository, 0, listOptions.PageSize)
+		total, err := sess.FindAndCount(&repos)
+		return repos, total, err
+	}
+
+	repos := make([]*Repository, 0, 10)
+	total, err := sess.FindAndCount(&repos)
+	return repos, total, err
+}
+
+// CountUserRepositories returns number of repositories user owns.
+// Argument private only takes effect when it is false,
+// set it true to count all repositories.
+func CountUserRepositories(userID int64, private bool) int64 {
+	return countRepositories(userID, private)
+}
+
+func getRepositoryCount(e db.Engine, ownerID int64) (int64, error) {
+	return e.Count(&Repository{OwnerID: ownerID})
+}
+
+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_model.User) (int64, error) {
+	return e.Where("is_private = ?", true).Count(&Repository{OwnerID: u.ID})
+}
+
+// GetRepositoryCount returns the total number of repositories of user.
+func GetRepositoryCount(ctx context.Context, ownerID int64) (int64, error) {
+	return getRepositoryCount(db.GetEngine(ctx), ownerID)
+}
+
+// GetPublicRepositoryCount returns the total number of public repositories of user.
+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_model.User) (int64, error) {
+	return getPrivateRepositoryCount(db.GetEngine(db.DefaultContext), u)
+}
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index 3aca1023e6..d94b61b449 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -10,6 +10,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -259,20 +260,6 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int
 	return removeIssueWatchersByRepoID(db.GetEngine(ctx), uid, repo.ID)
 }
 
-func getRepoTeams(e db.Engine, repo *repo_model.Repository) (teams []*Team, err error) {
-	return teams, e.
-		Join("INNER", "team_repo", "team_repo.team_id = team.id").
-		Where("team.org_id = ?", repo.OwnerID).
-		And("team_repo.repo_id=?", repo.ID).
-		OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
-		Find(&teams)
-}
-
-// GetRepoTeams gets the list of teams that has access to the repository
-func GetRepoTeams(repo *repo_model.Repository) ([]*Team, error) {
-	return getRepoTeams(db.GetEngine(db.DefaultContext), repo)
-}
-
 // IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository
 func IsOwnerMemberCollaborator(repo *repo_model.Repository, userID int64) (bool, error) {
 	if repo.OwnerID == userID {
@@ -282,7 +269,7 @@ func IsOwnerMemberCollaborator(repo *repo_model.Repository, userID int64) (bool,
 		Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id").
 		Where("team_repo.repo_id = ?", repo.ID).
 		And("team_unit.`type` = ?", unit.TypeCode).
-		And("team_user.uid = ?", userID).Table("team_user").Exist(&TeamUser{})
+		And("team_user.uid = ?", userID).Table("team_user").Exist(&organization.TeamUser{})
 	if err != nil {
 		return false, err
 	}
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 4e5cbfd558..4dfc63d4e1 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	perm_model "code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -186,7 +187,7 @@ func getUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 
 	// Prevent strangers from checking out public repo of private organization/users
 	// Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
-	if !hasOrgOrUserVisible(e, repo.Owner, user) && !is {
+	if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !is {
 		perm.AccessMode = perm_model.AccessModeNone
 		return
 	}
@@ -232,7 +233,7 @@ func getUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	}
 
 	// get units mode from teams
-	teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+	teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
 	if err != nil {
 		return
 	}
@@ -249,7 +250,7 @@ func getUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	for _, u := range repo.Units {
 		var found bool
 		for _, team := range teams {
-			teamMode := team.unitAccessMode(e, u.Type)
+			teamMode := team.UnitAccessMode(ctx, u.Type)
 			if teamMode > perm_model.AccessModeNone {
 				m := perm.UnitsMode[u.Type]
 				if m < teamMode {
@@ -300,10 +301,10 @@ func IsUserRealRepoAdmin(repo *repo_model.Repository, user *user_model.User) (bo
 
 // IsUserRepoAdmin return true if user has admin right of a repo
 func IsUserRepoAdmin(repo *repo_model.Repository, user *user_model.User) (bool, error) {
-	return isUserRepoAdmin(db.GetEngine(db.DefaultContext), repo, user)
+	return isUserRepoAdmin(db.DefaultContext, repo, user)
 }
 
-func isUserRepoAdmin(e db.Engine, repo *repo_model.Repository, user *user_model.User) (bool, error) {
+func isUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (bool, error) {
 	if user == nil || repo == nil {
 		return false, nil
 	}
@@ -311,6 +312,7 @@ func isUserRepoAdmin(e db.Engine, repo *repo_model.Repository, user *user_model.
 		return true, nil
 	}
 
+	e := db.GetEngine(ctx)
 	mode, err := accessLevel(e, user, repo)
 	if err != nil {
 		return false, err
@@ -319,7 +321,7 @@ func isUserRepoAdmin(e db.Engine, repo *repo_model.Repository, user *user_model.
 		return true, nil
 	}
 
-	teams, err := getUserRepoTeams(e, repo.OwnerID, user.ID, repo.ID)
+	teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
 	if err != nil {
 		return false, err
 	}
diff --git a/models/repo_permission_test.go b/models/repo_permission_test.go
index f2664d8101..c103a9363f 100644
--- a/models/repo_permission_test.go
+++ b/models/repo_permission_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	perm_model "code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -227,8 +228,8 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
 	}
 
 	// update team information and then check permission
-	team := unittest.AssertExistsAndLoadBean(t, &Team{ID: 5}).(*Team)
-	err = UpdateTeamUnits(team, nil)
+	team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5}).(*organization.Team)
+	err = organization.UpdateTeamUnits(team, nil)
 	assert.NoError(t, err)
 	perm, err = GetUserRepoPermission(repo, owner)
 	assert.NoError(t, err)
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index f7d5e20990..f9a758a20b 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -10,6 +10,7 @@ import (
 	"os"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
@@ -26,7 +27,7 @@ type RepoTransfer struct {
 	Recipient   *user_model.User `xorm:"-"`
 	RepoID      int64
 	TeamIDs     []int64
-	Teams       []*Team `xorm:"-"`
+	Teams       []*organization.Team `xorm:"-"`
 
 	CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"`
 	UpdatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL updated"`
@@ -49,7 +50,7 @@ func (r *RepoTransfer) LoadAttributes() error {
 
 	if r.Recipient.IsOrganization() && len(r.TeamIDs) != len(r.Teams) {
 		for _, v := range r.TeamIDs {
-			team, err := GetTeamByID(v)
+			team, err := organization.GetTeamByID(v)
 			if err != nil {
 				return err
 			}
@@ -87,7 +88,7 @@ func (r *RepoTransfer) CanUserAcceptTransfer(u *user_model.User) bool {
 		return r.RecipientID == u.ID
 	}
 
-	allowed, err := CanCreateOrgRepo(r.RecipientID, u.ID)
+	allowed, err := organization.CanCreateOrgRepo(r.RecipientID, u.ID)
 	if err != nil {
 		log.Error("CanCreateOrgRepo: %v", err)
 		return false
@@ -152,7 +153,7 @@ func TestRepositoryReadyForTransfer(status repo_model.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_model.User, repoID int64, teams []*Team) error {
+func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return err
@@ -296,7 +297,7 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
 		}
 
 		if c.ID != newOwner.ID {
-			isMember, err := isOrganizationMember(sess, newOwner.ID, c.ID)
+			isMember, err := organization.IsOrganizationMember(ctx, newOwner.ID, c.ID)
 			if err != nil {
 				return fmt.Errorf("IsOrgMember: %v", err)
 			} else if !isMember {
@@ -312,19 +313,19 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
 
 	// Remove old team-repository relations.
 	if oldOwner.IsOrganization() {
-		if err := OrgFromUser(oldOwner).removeOrgRepo(sess, repo.ID); err != nil {
+		if err := organization.RemoveOrgRepo(ctx, oldOwner.ID, repo.ID); err != nil {
 			return fmt.Errorf("removeOrgRepo: %v", err)
 		}
 	}
 
 	if newOwner.IsOrganization() {
-		teams, err := OrgFromUser(newOwner).loadTeams(sess)
+		teams, err := organization.FindOrgTeams(ctx, newOwner.ID)
 		if err != nil {
 			return fmt.Errorf("LoadTeams: %v", err)
 		}
 		for _, t := range teams {
 			if t.IncludesAllRepositories {
-				if err := t.addRepository(ctx, repo); err != nil {
+				if err := addRepository(ctx, t, repo); err != nil {
 					return fmt.Errorf("addRepository: %v", err)
 				}
 			}
diff --git a/models/review.go b/models/review.go
index 22c47486a1..6ef8d530b6 100644
--- a/models/review.go
+++ b/models/review.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -58,10 +59,10 @@ func (rt ReviewType) Icon() string {
 type Review struct {
 	ID               int64 `xorm:"pk autoincr"`
 	Type             ReviewType
-	Reviewer         *user_model.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     *organization.Team `xorm:"-"`
 	OriginalAuthor   string
 	OriginalAuthorID int64
 	Issue            *Issue `xorm:"-"`
@@ -114,12 +115,12 @@ func (r *Review) loadReviewer(e db.Engine) (err error) {
 	return
 }
 
-func (r *Review) loadReviewerTeam(e db.Engine) (err error) {
+func (r *Review) loadReviewerTeam(ctx context.Context) (err error) {
 	if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil {
 		return
 	}
 
-	r.ReviewerTeam, err = getTeamByID(e, r.ReviewerTeamID)
+	r.ReviewerTeam, err = organization.GetTeamByIDCtx(ctx, r.ReviewerTeamID)
 	return
 }
 
@@ -130,7 +131,7 @@ func (r *Review) LoadReviewer() error {
 
 // LoadReviewerTeam loads reviewer team
 func (r *Review) LoadReviewerTeam() error {
-	return r.loadReviewerTeam(db.GetEngine(db.DefaultContext))
+	return r.loadReviewerTeam(db.DefaultContext)
 }
 
 // LoadAttributes loads all attributes except CodeComments
@@ -145,7 +146,7 @@ func (r *Review) LoadAttributes(ctx context.Context) (err error) {
 	if err = r.loadReviewer(e); err != nil {
 		return
 	}
-	if err = r.loadReviewerTeam(e); err != nil {
+	if err = r.loadReviewerTeam(ctx); err != nil {
 		return
 	}
 	return
@@ -221,7 +222,7 @@ type CreateReviewOptions struct {
 	Type         ReviewType
 	Issue        *Issue
 	Reviewer     *user_model.User
-	ReviewerTeam *Team
+	ReviewerTeam *organization.Team
 	Official     bool
 	CommitID     string
 	Stale        bool
@@ -255,11 +256,11 @@ func isOfficialReviewer(ctx context.Context, issue *Issue, reviewers ...*user_mo
 }
 
 // IsOfficialReviewerTeam check if reviewer in this team can make official reviews in issue (counts towards required approvals)
-func IsOfficialReviewerTeam(issue *Issue, team *Team) (bool, error) {
+func IsOfficialReviewerTeam(issue *Issue, team *organization.Team) (bool, error) {
 	return isOfficialReviewerTeam(db.DefaultContext, issue, team)
 }
 
-func isOfficialReviewerTeam(ctx context.Context, issue *Issue, team *Team) (bool, error) {
+func isOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organization.Team) (bool, error) {
 	pr, err := getPullRequestByIssueID(db.GetEngine(ctx), issue.ID)
 	if err != nil {
 		return false, err
@@ -272,7 +273,7 @@ func isOfficialReviewerTeam(ctx context.Context, issue *Issue, team *Team) (bool
 	}
 
 	if !pr.ProtectedBranch.EnableApprovalsWhitelist {
-		return team.UnitAccessMode(unit.TypeCode) >= perm.AccessModeWrite, nil
+		return team.UnitAccessMode(ctx, unit.TypeCode) >= perm.AccessModeWrite, nil
 	}
 
 	return base.Int64sContains(pr.ProtectedBranch.ApprovalsWhitelistTeamIDs, team.ID), nil
@@ -447,7 +448,7 @@ func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, co
 		}
 
 		for _, teamReviewRequest := range teamReviewRequests {
-			ok, err := isTeamMember(sess, issue.Repo.OwnerID, teamReviewRequest.ReviewerTeamID, doer.ID)
+			ok, err := organization.IsTeamMember(ctx, issue.Repo.OwnerID, teamReviewRequest.ReviewerTeamID, doer.ID)
 			if err != nil {
 				return nil, nil, err
 			} else if !ok {
@@ -732,7 +733,7 @@ func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Commen
 }
 
 // AddTeamReviewRequest add a review request from one team
-func AddTeamReviewRequest(issue *Issue, reviewer *Team, doer *user_model.User) (*Comment, error) {
+func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return nil, err
@@ -792,7 +793,7 @@ func AddTeamReviewRequest(issue *Issue, reviewer *Team, doer *user_model.User) (
 }
 
 // RemoveTeamReviewRequest remove a review request from one team
-func RemoveTeamReviewRequest(issue *Issue, reviewer *Team, doer *user_model.User) (*Comment, error) {
+func RemoveTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_model.User) (*Comment, error) {
 	ctx, committer, err := db.TxContext()
 	if err != nil {
 		return nil, err
diff --git a/models/statistic.go b/models/statistic.go
index dfe543d063..67be4c5d42 100644
--- a/models/statistic.go
+++ b/models/statistic.go
@@ -8,6 +8,7 @@ import (
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/models/webhook"
@@ -47,7 +48,7 @@ type IssueByRepositoryCount struct {
 func GetStatistic() (stats Statistic) {
 	e := db.GetEngine(db.DefaultContext)
 	stats.Counter.User = user_model.CountUsers()
-	stats.Counter.Org = CountOrganizations()
+	stats.Counter.Org = organization.CountOrganizations()
 	stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
 	stats.Counter.Repo = repo_model.CountRepositories(true)
 	stats.Counter.Watch, _ = e.Count(new(repo_model.Watch))
@@ -103,7 +104,7 @@ func GetStatistic() (stats Statistic) {
 	stats.Counter.Milestone, _ = e.Count(new(Milestone))
 	stats.Counter.Label, _ = e.Count(new(Label))
 	stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask))
-	stats.Counter.Team, _ = e.Count(new(Team))
+	stats.Counter.Team, _ = e.Count(new(organization.Team))
 	stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment))
 	stats.Counter.Project, _ = e.Count(new(Project))
 	stats.Counter.ProjectBoard, _ = e.Count(new(ProjectBoard))
diff --git a/models/user.go b/models/user.go
index 1dbc025155..7744222809 100644
--- a/models/user.go
+++ b/models/user.go
@@ -14,22 +14,13 @@ import (
 
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	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"
-
-	"xorm.io/builder"
 )
 
-// GetOrganizationCount returns count of membership of organization of the user.
-func GetOrganizationCount(ctx context.Context, u *user_model.User) (int64, error) {
-	return db.GetEngine(ctx).
-		Where("uid=?", u.ID).
-		Count(new(OrgUser))
-}
-
 // DeleteUser deletes models associated to an user.
 func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
 	e := db.GetEngine(ctx)
@@ -86,7 +77,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
 		&user_model.EmailAddress{UID: u.ID},
 		&user_model.UserOpenID{UID: u.ID},
 		&Reaction{UserID: u.ID},
-		&TeamUser{UID: u.ID},
+		&organization.TeamUser{UID: u.ID},
 		&Collaboration{UserID: u.ID},
 		&Stopwatch{UserID: u.ID},
 		&user_model.Setting{UserID: u.ID},
@@ -204,100 +195,3 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
 
 	return nil
 }
-
-// GetStarredRepos returns the repos starred by a particular user
-func GetStarredRepos(userID int64, private bool, listOptions db.ListOptions) ([]*repo_model.Repository, error) {
-	sess := db.GetEngine(db.DefaultContext).Where("star.uid=?", userID).
-		Join("LEFT", "star", "`repository`.id=`star`.repo_id")
-	if !private {
-		sess = sess.And("is_private=?", false)
-	}
-
-	if listOptions.Page != 0 {
-		sess = db.SetSessionPagination(sess, &listOptions)
-
-		repos := make([]*repo_model.Repository, 0, listOptions.PageSize)
-		return repos, sess.Find(&repos)
-	}
-
-	repos := make([]*repo_model.Repository, 0, 10)
-	return repos, sess.Find(&repos)
-}
-
-// GetWatchedRepos returns the repos watched by a particular user
-func GetWatchedRepos(userID int64, private bool, listOptions db.ListOptions) ([]*repo_model.Repository, int64, error) {
-	sess := db.GetEngine(db.DefaultContext).Where("watch.user_id=?", userID).
-		And("`watch`.mode<>?", repo_model.WatchModeDont).
-		Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
-	if !private {
-		sess = sess.And("is_private=?", false)
-	}
-
-	if listOptions.Page != 0 {
-		sess = db.SetSessionPagination(sess, &listOptions)
-
-		repos := make([]*repo_model.Repository, 0, listOptions.PageSize)
-		total, err := sess.FindAndCount(&repos)
-		return repos, total, err
-	}
-
-	repos := make([]*repo_model.Repository, 0, 10)
-	total, err := sess.FindAndCount(&repos)
-	return repos, total, err
-}
-
-// IsUserVisibleToViewer check if viewer is able to see user profile
-func IsUserVisibleToViewer(u, viewer *user_model.User) bool {
-	return isUserVisibleToViewer(db.GetEngine(db.DefaultContext), u, viewer)
-}
-
-func isUserVisibleToViewer(e db.Engine, u, viewer *user_model.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
-}
diff --git a/models/user/user.go b/models/user/user.go
index a3094a13ce..0e51cf955c 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -1211,3 +1211,59 @@ func GetAdminUser() (*User, error) {
 
 	return &admin, nil
 }
+
+// IsUserVisibleToViewer check if viewer is able to see user profile
+func IsUserVisibleToViewer(u, viewer *User) bool {
+	return isUserVisibleToViewer(db.GetEngine(db.DefaultContext), u, viewer)
+}
+
+func isUserVisibleToViewer(e db.Engine, u, 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 := 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()
+		if err != nil {
+			return false
+		}
+
+		if count < 0 {
+			// No common organization
+			return false
+		}
+
+		// they are in an organization together
+		return true
+	}
+	return false
+}
diff --git a/models/user_heatmap.go b/models/user_heatmap.go
index f331a0c16b..e908837ae8 100644
--- a/models/user_heatmap.go
+++ b/models/user_heatmap.go
@@ -6,6 +6,7 @@ package models
 
 import (
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
@@ -23,11 +24,11 @@ func GetUserHeatmapDataByUser(user, doer *user_model.User) ([]*UserHeatmapData,
 }
 
 // GetUserHeatmapDataByUserTeam returns an array of UserHeatmapData
-func GetUserHeatmapDataByUserTeam(user *user_model.User, team *Team, doer *user_model.User) ([]*UserHeatmapData, error) {
+func GetUserHeatmapDataByUserTeam(user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
 	return getUserHeatmapData(user, team, doer)
 }
 
-func getUserHeatmapData(user *user_model.User, team *Team, doer *user_model.User) ([]*UserHeatmapData, error) {
+func getUserHeatmapData(user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
 	hdata := make([]*UserHeatmapData, 0)
 
 	if !activityReadable(user, doer) {
diff --git a/models/user_test.go b/models/user_test.go
index 83201ff4cb..bf6f71895e 100644
--- a/models/user_test.go
+++ b/models/user_test.go
@@ -5,7 +5,6 @@
 package models
 
 import (
-	"fmt"
 	"testing"
 
 	"code.gitea.io/gitea/models/unittest"
@@ -42,59 +41,3 @@ func TestUnfollowUser(t *testing.T) {
 
 	unittest.CheckConsistencyFor(t, &user_model.User{})
 }
-
-func TestUserIsPublicMember(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	tt := []struct {
-		uid      int64
-		orgid    int64
-		expected bool
-	}{
-		{2, 3, true},
-		{4, 3, false},
-		{5, 6, true},
-		{5, 7, false},
-	}
-	for _, v := range tt {
-		t.Run(fmt.Sprintf("UserId%dIsPublicMemberOf%d", v.uid, v.orgid), func(t *testing.T) {
-			testUserIsPublicMember(t, v.uid, v.orgid, v.expected)
-		})
-	}
-}
-
-func testUserIsPublicMember(t *testing.T, uid, orgID int64, expected bool) {
-	user, err := user_model.GetUserByID(uid)
-	assert.NoError(t, err)
-	is, err := IsPublicMembership(orgID, user.ID)
-	assert.NoError(t, err)
-	assert.Equal(t, expected, is)
-}
-
-func TestIsUserOrgOwner(t *testing.T) {
-	assert.NoError(t, unittest.PrepareTestDatabase())
-
-	tt := []struct {
-		uid      int64
-		orgid    int64
-		expected bool
-	}{
-		{2, 3, true},
-		{4, 3, false},
-		{5, 6, true},
-		{5, 7, true},
-	}
-	for _, v := range tt {
-		t.Run(fmt.Sprintf("UserId%dIsOrgOwnerOf%d", v.uid, v.orgid), func(t *testing.T) {
-			testIsUserOrgOwner(t, v.uid, v.orgid, v.expected)
-		})
-	}
-}
-
-func testIsUserOrgOwner(t *testing.T, uid, orgID int64, expected bool) {
-	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)
-}
diff --git a/models/userlist.go b/models/userlist.go
index 102c587dfe..fbe1995b40 100644
--- a/models/userlist.go
+++ b/models/userlist.go
@@ -5,9 +5,11 @@
 package models
 
 import (
+	"context"
 	"fmt"
 
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 )
@@ -18,7 +20,7 @@ func IsUserOrgOwner(users user_model.UserList, orgID int64) map[int64]bool {
 	for _, user := range users {
 		results[user.ID] = false // Set default to false
 	}
-	ownerMaps, err := loadOrganizationOwners(db.GetEngine(db.DefaultContext), users, orgID)
+	ownerMaps, err := loadOrganizationOwners(db.DefaultContext, users, orgID)
 	if err == nil {
 		for _, owner := range ownerMaps {
 			results[owner.UID] = true
@@ -27,13 +29,13 @@ func IsUserOrgOwner(users user_model.UserList, orgID int64) map[int64]bool {
 	return results
 }
 
-func loadOrganizationOwners(e db.Engine, users user_model.UserList, orgID int64) (map[int64]*TeamUser, error) {
+func loadOrganizationOwners(ctx context.Context, users user_model.UserList, orgID int64) (map[int64]*organization.TeamUser, error) {
 	if len(users) == 0 {
 		return nil, nil
 	}
-	ownerTeam, err := getOwnerTeam(e, orgID)
+	ownerTeam, err := organization.GetOwnerTeam(ctx, orgID)
 	if err != nil {
-		if IsErrTeamNotExist(err) {
+		if organization.IsErrTeamNotExist(err) {
 			log.Error("Organization does not have owner team: %d", orgID)
 			return nil, nil
 		}
@@ -41,8 +43,8 @@ func loadOrganizationOwners(e db.Engine, users user_model.UserList, orgID int64)
 	}
 
 	userIDs := users.GetUserIDs()
-	ownerMaps := make(map[int64]*TeamUser)
-	err = e.In("uid", userIDs).
+	ownerMaps := make(map[int64]*organization.TeamUser)
+	err = db.GetEngine(ctx).In("uid", userIDs).
 		And("org_id=?", orgID).
 		And("team_id=?", ownerTeam.ID).
 		Find(&ownerMaps)
diff --git a/models/userlist_test.go b/models/userlist_test.go
index 6776850b60..9b3c796e64 100644
--- a/models/userlist_test.go
+++ b/models/userlist_test.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"testing"
 
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unittest"
 
 	"github.com/stretchr/testify/assert"
@@ -33,7 +34,7 @@ func TestUserListIsPublicMember(t *testing.T) {
 }
 
 func testUserListIsPublicMember(t *testing.T, orgID int64, expected map[int64]bool) {
-	org, err := GetOrgByID(orgID)
+	org, err := organization.GetOrgByID(orgID)
 	assert.NoError(t, err)
 	_, membersIsPublic, err := org.GetMembers()
 	assert.NoError(t, err)
@@ -60,7 +61,7 @@ func TestUserListIsUserOrgOwner(t *testing.T) {
 }
 
 func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bool) {
-	org, err := GetOrgByID(orgID)
+	org, err := organization.GetOrgByID(orgID)
 	assert.NoError(t, err)
 	members, _, err := org.GetMembers()
 	assert.NoError(t, err)
diff --git a/modules/context/api_org.go b/modules/context/api_org.go
index 6d86fa6ed2..2231677d42 100644
--- a/modules/context/api_org.go
+++ b/modules/context/api_org.go
@@ -4,12 +4,10 @@
 
 package context
 
-import (
-	"code.gitea.io/gitea/models"
-)
+import "code.gitea.io/gitea/models/organization"
 
 // APIOrganization contains organization and team
 type APIOrganization struct {
-	Organization *models.Organization
-	Team         *models.Team
+	Organization *organization.Organization
+	Team         *organization.Team
 }
diff --git a/modules/context/org.go b/modules/context/org.go
index 8e292fa1c5..f8607bd29f 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -8,7 +8,7 @@ package context
 import (
 	"strings"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	user_model "code.gitea.io/gitea/models/user"
 )
@@ -19,12 +19,12 @@ type Organization struct {
 	IsMember         bool
 	IsTeamMember     bool // Is member of team.
 	IsTeamAdmin      bool // In owner team or team that has admin permission level.
-	Organization     *models.Organization
+	Organization     *organization.Organization
 	OrgLink          string
 	CanCreateOrgRepo bool
 
-	Team  *models.Team
-	Teams []*models.Team
+	Team  *organization.Team
+	Teams []*organization.Team
 }
 
 // HandleOrgAssignment handles organization assignment
@@ -51,9 +51,9 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 	orgName := ctx.Params(":org")
 
 	var err error
-	ctx.Org.Organization, err = models.GetOrgByName(orgName)
+	ctx.Org.Organization, err = organization.GetOrgByName(orgName)
 	if err != nil {
-		if models.IsErrOrgNotExist(err) {
+		if organization.IsErrOrgNotExist(err) {
 			redirectUserID, err := user_model.LookupUserRedirect(orgName)
 			if err == nil {
 				RedirectToUser(ctx, orgName, redirectUserID)
@@ -120,7 +120,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 	ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
 	ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
 	ctx.Data["IsPublicMember"] = func(uid int64) bool {
-		is, _ := models.IsPublicMembership(ctx.Org.Organization.ID, uid)
+		is, _ := organization.IsPublicMembership(ctx.Org.Organization.ID, uid)
 		return is
 	}
 	ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index 41a044c6d7..43300f603e 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -14,6 +14,7 @@ import (
 	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/auth"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
@@ -98,15 +99,15 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
 	if err != nil {
 		log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err)
 	}
-	pushWhitelistTeams, err := models.GetTeamNamesByID(bp.WhitelistTeamIDs)
+	pushWhitelistTeams, err := organization.GetTeamNamesByID(bp.WhitelistTeamIDs)
 	if err != nil {
 		log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
 	}
-	mergeWhitelistTeams, err := models.GetTeamNamesByID(bp.MergeWhitelistTeamIDs)
+	mergeWhitelistTeams, err := organization.GetTeamNamesByID(bp.MergeWhitelistTeamIDs)
 	if err != nil {
 		log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
 	}
-	approvalsWhitelistTeams, err := models.GetTeamNamesByID(bp.ApprovalsWhitelistTeamIDs)
+	approvalsWhitelistTeams, err := organization.GetTeamNamesByID(bp.ApprovalsWhitelistTeamIDs)
 	if err != nil {
 		log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err)
 	}
@@ -280,7 +281,7 @@ func ToDeployKey(apiLink string, key *asymkey_model.DeployKey) *api.DeployKey {
 }
 
 // ToOrganization convert user_model.User to api.Organization
-func ToOrganization(org *models.Organization) *api.Organization {
+func ToOrganization(org *organization.Organization) *api.Organization {
 	return &api.Organization{
 		ID:                        org.ID,
 		AvatarURL:                 org.AsUser().AvatarLink(),
@@ -294,8 +295,8 @@ func ToOrganization(org *models.Organization) *api.Organization {
 	}
 }
 
-// ToTeam convert models.Team to api.Team
-func ToTeam(team *models.Team) *api.Team {
+// ToTeam convert organization.Team to api.Team
+func ToTeam(team *organization.Team) *api.Team {
 	if team == nil {
 		return nil
 	}
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index 4e232a8609..b6a89a7ed6 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -22,13 +23,13 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
 
 	testTeamRepositories := func(teamID int64, repoIds []int64) {
-		team := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
-		assert.NoError(t, team.GetRepositories(&models.SearchOrgTeamOptions{}), "%s: GetRepositories", team.Name)
+		team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+		assert.NoError(t, team.GetRepositoriesCtx(db.DefaultContext), "%s: GetRepositories", team.Name)
 		assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
 		assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name)
 		for i, rid := range repoIds {
 			if rid > 0 {
-				assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i)
+				assert.True(t, models.HasRepository(team, rid), "%s: HasRepository(%d) %d", rid, i)
 			}
 		}
 	}
@@ -38,13 +39,13 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	assert.NoError(t, err, "GetUserByID")
 
 	// Create org.
-	org := &models.Organization{
+	org := &organization.Organization{
 		Name:       "All_repo",
 		IsActive:   true,
 		Type:       user_model.UserTypeOrganization,
 		Visibility: structs.VisibleTypePublic,
 	}
-	assert.NoError(t, models.CreateOrganization(org, user), "CreateOrganization")
+	assert.NoError(t, organization.CreateOrganization(org, user), "CreateOrganization")
 
 	// Check Owner team.
 	ownerTeam, err := org.GetOwnerTeam()
@@ -65,7 +66,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 	assert.NoError(t, err, "GetOwnerTeam")
 
 	// Create teams and check repositories.
-	teams := []*models.Team{
+	teams := []*organization.Team{
 		ownerTeam,
 		{
 			OrgID:                   org.ID,
@@ -144,5 +145,5 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 			assert.NoError(t, models.DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i)
 		}
 	}
-	assert.NoError(t, models.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
+	assert.NoError(t, organization.DeleteOrganization(db.DefaultContext, org), "DeleteOrganization")
 }
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 98388bbd7b..dbf4b527bf 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"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/git"
@@ -55,7 +56,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
 	repoPath := repo_model.RepoPath(u.Name, opts.RepoName)
 
 	if u.IsOrganization() {
-		t, err := models.OrgFromUser(u).GetOwnerTeam()
+		t, err := organization.OrgFromUser(u).GetOwnerTeam()
 		if err != nil {
 			return nil, err
 		}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 2973b2c938..80ad7066a7 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -25,6 +25,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/avatars"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
@@ -569,7 +570,7 @@ func Avatar(item interface{}, others ...interface{}) template.HTML {
 		if src != "" {
 			return AvatarHTML(src, size, class, t.DisplayName())
 		}
-	case *models.Organization:
+	case *organization.Organization:
 		src := t.AsUser().AvatarLinkWithSize(size * setting.Avatar.RenderedSizeFactor)
 		if src != "" {
 			return AvatarHTML(src, size, class, t.AsUser().DisplayName())
diff --git a/routers/api/v1/admin/org.go b/routers/api/v1/admin/org.go
index e4850ac494..727f3193cf 100644
--- a/routers/api/v1/admin/org.go
+++ b/routers/api/v1/admin/org.go
@@ -8,8 +8,8 @@ package admin
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -52,7 +52,7 @@ func CreateOrg(ctx *context.APIContext) {
 		visibility = api.VisibilityModes[form.Visibility]
 	}
 
-	org := &models.Organization{
+	org := &organization.Organization{
 		Name:        form.UserName,
 		FullName:    form.FullName,
 		Description: form.Description,
@@ -63,7 +63,7 @@ func CreateOrg(ctx *context.APIContext) {
 		Visibility:  visibility,
 	}
 
-	if err := models.CreateOrganization(org, ctx.ContextUser); err != nil {
+	if err := organization.CreateOrganization(org, ctx.ContextUser); err != nil {
 		if user_model.IsErrUserAlreadyExist(err) ||
 			db.IsErrNameReserved(err) ||
 			db.IsErrNameCharsNotAllowed(err) ||
@@ -115,7 +115,7 @@ func GetAllOrgs(ctx *context.APIContext) {
 	}
 	orgs := make([]*api.Organization, len(users))
 	for i := range users {
-		orgs[i] = convert.ToOrganization(models.OrgFromUser(users[i]))
+		orgs[i] = convert.ToOrganization(organization.OrgFromUser(users[i]))
 	}
 
 	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 3debf58a17..1fed95284b 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -71,6 +71,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -309,7 +310,7 @@ func reqOrgOwnership() func(ctx *context.APIContext) {
 			return
 		}
 
-		isOwner, err := models.IsOrganizationOwner(orgID, ctx.Doer.ID)
+		isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
 		if err != nil {
 			ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err)
 			return
@@ -336,7 +337,7 @@ func reqTeamMembership() func(ctx *context.APIContext) {
 		}
 
 		orgID := ctx.Org.Team.OrgID
-		isOwner, err := models.IsOrganizationOwner(orgID, ctx.Doer.ID)
+		isOwner, err := organization.IsOrganizationOwner(ctx, orgID, ctx.Doer.ID)
 		if err != nil {
 			ctx.Error(http.StatusInternalServerError, "IsOrganizationOwner", err)
 			return
@@ -344,11 +345,11 @@ func reqTeamMembership() func(ctx *context.APIContext) {
 			return
 		}
 
-		if isTeamMember, err := models.IsTeamMember(orgID, ctx.Org.Team.ID, ctx.Doer.ID); err != nil {
+		if isTeamMember, err := organization.IsTeamMember(ctx, orgID, ctx.Org.Team.ID, ctx.Doer.ID); err != nil {
 			ctx.Error(http.StatusInternalServerError, "IsTeamMember", err)
 			return
 		} else if !isTeamMember {
-			isOrgMember, err := models.IsOrganizationMember(orgID, ctx.Doer.ID)
+			isOrgMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID)
 			if err != nil {
 				ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
 			} else if isOrgMember {
@@ -378,7 +379,7 @@ func reqOrgMembership() func(ctx *context.APIContext) {
 			return
 		}
 
-		if isMember, err := models.IsOrganizationMember(orgID, ctx.Doer.ID); err != nil {
+		if isMember, err := organization.IsOrganizationMember(ctx, orgID, ctx.Doer.ID); err != nil {
 			ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
 			return
 		} else if !isMember {
@@ -427,9 +428,9 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
 
 		var err error
 		if assignOrg {
-			ctx.Org.Organization, err = models.GetOrgByName(ctx.Params(":org"))
+			ctx.Org.Organization, err = organization.GetOrgByName(ctx.Params(":org"))
 			if err != nil {
-				if models.IsErrOrgNotExist(err) {
+				if organization.IsErrOrgNotExist(err) {
 					redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org"))
 					if err == nil {
 						context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID)
@@ -447,9 +448,9 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
 		}
 
 		if assignTeam {
-			ctx.Org.Team, err = models.GetTeamByID(ctx.ParamsInt64(":teamid"))
+			ctx.Org.Team, err = organization.GetTeamByID(ctx.ParamsInt64(":teamid"))
 			if err != nil {
-				if models.IsErrTeamNotExist(err) {
+				if organization.IsErrTeamNotExist(err) {
 					ctx.NotFound()
 				} else {
 					ctx.Error(http.StatusInternalServerError, "GetTeamById", err)
diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go
index 9dae15462f..85fe2ded4d 100644
--- a/routers/api/v1/org/member.go
+++ b/routers/api/v1/org/member.go
@@ -9,6 +9,7 @@ import (
 	"net/url"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	"code.gitea.io/gitea/modules/setting"
@@ -19,19 +20,19 @@ import (
 
 // listMembers list an organization's members
 func listMembers(ctx *context.APIContext, publicOnly bool) {
-	opts := &models.FindOrgMembersOpts{
+	opts := &organization.FindOrgMembersOpts{
 		OrgID:       ctx.Org.Organization.ID,
 		PublicOnly:  publicOnly,
 		ListOptions: utils.GetListOptions(ctx),
 	}
 
-	count, err := models.CountOrgMembers(opts)
+	count, err := organization.CountOrgMembers(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
 	}
 
-	members, _, err := models.FindOrgMembers(opts)
+	members, _, err := organization.FindOrgMembers(opts)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -190,7 +191,7 @@ func IsPublicMember(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	is, err := models.IsPublicMembership(ctx.Org.Organization.ID, userToCheck.ID)
+	is, err := organization.IsPublicMembership(ctx.Org.Organization.ID, userToCheck.ID)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "IsPublicMembership", err)
 		return
@@ -234,7 +235,7 @@ func PublicizeMember(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Cannot publicize another member")
 		return
 	}
-	err := models.ChangeOrgUserStatus(ctx.Org.Organization.ID, userToPublicize.ID, true)
+	err := organization.ChangeOrgUserStatus(ctx.Org.Organization.ID, userToPublicize.ID, true)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err)
 		return
@@ -274,7 +275,7 @@ func ConcealMember(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Cannot conceal another member")
 		return
 	}
-	err := models.ChangeOrgUserStatus(ctx.Org.Organization.ID, userToConceal.ID, false)
+	err := organization.ChangeOrgUserStatus(ctx.Org.Organization.ID, userToConceal.ID, false)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err)
 		return
@@ -308,8 +309,8 @@ func DeleteMember(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	if err := ctx.Org.Organization.RemoveMember(member.ID); err != nil {
-		ctx.Error(http.StatusInternalServerError, "RemoveMember", err)
+	if err := models.RemoveOrgUser(ctx.Org.Organization.ID, member.ID); err != nil {
+		ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err)
 	}
 	ctx.Status(http.StatusNoContent)
 }
diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go
index fcc98f4b78..d55a4a4514 100644
--- a/routers/api/v1/org/org.go
+++ b/routers/api/v1/org/org.go
@@ -8,8 +8,8 @@ package org
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
@@ -25,17 +25,17 @@ func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
 	listOptions := utils.GetListOptions(ctx)
 	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == u.ID)
 
-	opts := models.FindOrgOptions{
+	opts := organization.FindOrgOptions{
 		ListOptions:    listOptions,
 		UserID:         u.ID,
 		IncludePrivate: showPrivate,
 	}
-	orgs, err := models.FindOrgs(opts)
+	orgs, err := organization.FindOrgs(opts)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "FindOrgs", err)
 		return
 	}
-	maxResults, err := models.CountOrgs(opts)
+	maxResults, err := organization.CountOrgs(opts)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "CountOrgs", err)
 		return
@@ -135,12 +135,12 @@ func GetUserOrgsPermissions(ctx *context.APIContext) {
 
 	op := api.OrganizationPermissions{}
 
-	if !models.HasOrgOrUserVisible(o, ctx.ContextUser) {
+	if !organization.HasOrgOrUserVisible(ctx, o, ctx.ContextUser) {
 		ctx.NotFound("HasOrgOrUserVisible", nil)
 		return
 	}
 
-	org := models.OrgFromUser(o)
+	org := organization.OrgFromUser(o)
 	authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx.ContextUser.ID)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err)
@@ -212,7 +212,7 @@ func GetAll(ctx *context.APIContext) {
 	}
 	orgs := make([]*api.Organization, len(publicOrgs))
 	for i := range publicOrgs {
-		orgs[i] = convert.ToOrganization(models.OrgFromUser(publicOrgs[i]))
+		orgs[i] = convert.ToOrganization(organization.OrgFromUser(publicOrgs[i]))
 	}
 
 	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
@@ -252,7 +252,7 @@ func Create(ctx *context.APIContext) {
 		visibility = api.VisibilityModes[form.Visibility]
 	}
 
-	org := &models.Organization{
+	org := &organization.Organization{
 		Name:                      form.UserName,
 		FullName:                  form.FullName,
 		Description:               form.Description,
@@ -263,7 +263,7 @@ func Create(ctx *context.APIContext) {
 		Visibility:                visibility,
 		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
 	}
-	if err := models.CreateOrganization(org, ctx.Doer); err != nil {
+	if err := organization.CreateOrganization(org, ctx.Doer); err != nil {
 		if user_model.IsErrUserAlreadyExist(err) ||
 			db.IsErrNameReserved(err) ||
 			db.IsErrNameCharsNotAllowed(err) ||
@@ -295,7 +295,7 @@ func Get(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/Organization"
 
-	if !models.HasOrgOrUserVisible(ctx.Org.Organization.AsUser(), ctx.Doer) {
+	if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
 		ctx.NotFound("HasOrgOrUserVisible", nil)
 		return
 	}
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index ca8cda269a..206012530f 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
@@ -47,7 +48,7 @@ func ListTeams(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/TeamList"
 
-	teams, count, err := models.SearchOrgTeams(&models.SearchOrgTeamOptions{
+	teams, count, err := organization.SearchTeam(&organization.SearchTeamOptions{
 		ListOptions: utils.GetListOptions(ctx),
 		OrgID:       ctx.Org.Organization.ID,
 	})
@@ -90,7 +91,7 @@ func ListUserTeams(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/TeamList"
 
-	teams, count, err := models.GetUserTeams(&models.GetUserTeamOptions{
+	teams, count, err := organization.SearchTeam(&organization.SearchTeamOptions{
 		ListOptions: utils.GetListOptions(ctx),
 		UserID:      ctx.Doer.ID,
 	})
@@ -104,7 +105,7 @@ func ListUserTeams(ctx *context.APIContext) {
 	for i := range teams {
 		apiOrg, ok := cache[teams[i].OrgID]
 		if !ok {
-			org, err := models.GetOrgByID(teams[i].OrgID)
+			org, err := organization.GetOrgByID(teams[i].OrgID)
 			if err != nil {
 				ctx.Error(http.StatusInternalServerError, "GetUserByID", err)
 				return
@@ -150,11 +151,11 @@ func GetTeam(ctx *context.APIContext) {
 	ctx.JSON(http.StatusOK, convert.ToTeam(ctx.Org.Team))
 }
 
-func attachTeamUnits(team *models.Team, units []string) {
+func attachTeamUnits(team *organization.Team, units []string) {
 	unitTypes := unit_model.FindUnitTypes(units...)
-	team.Units = make([]*models.TeamUnit, 0, len(units))
+	team.Units = make([]*organization.TeamUnit, 0, len(units))
 	for _, tp := range unitTypes {
-		team.Units = append(team.Units, &models.TeamUnit{
+		team.Units = append(team.Units, &organization.TeamUnit{
 			OrgID:      team.OrgID,
 			Type:       tp,
 			AccessMode: team.AccessMode,
@@ -170,10 +171,10 @@ func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.Access
 	return res
 }
 
-func attachTeamUnitsMap(team *models.Team, unitsMap map[string]string) {
-	team.Units = make([]*models.TeamUnit, 0, len(unitsMap))
+func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
+	team.Units = make([]*organization.TeamUnit, 0, len(unitsMap))
 	for unitKey, p := range unitsMap {
-		team.Units = append(team.Units, &models.TeamUnit{
+		team.Units = append(team.Units, &organization.TeamUnit{
 			OrgID:      team.OrgID,
 			Type:       unit_model.TypeFromKey(unitKey),
 			AccessMode: perm.ParseAccessMode(p),
@@ -210,7 +211,7 @@ func CreateTeam(ctx *context.APIContext) {
 	if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
 		p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
 	}
-	team := &models.Team{
+	team := &organization.Team{
 		OrgID:                   ctx.Org.Organization.ID,
 		Name:                    form.Name,
 		Description:             form.Description,
@@ -231,7 +232,7 @@ func CreateTeam(ctx *context.APIContext) {
 	}
 
 	if err := models.NewTeam(team); err != nil {
-		if models.IsErrTeamAlreadyExist(err) {
+		if organization.IsErrTeamAlreadyExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "NewTeam", err)
@@ -368,7 +369,7 @@ func GetTeamMembers(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/UserList"
 
-	isMember, err := models.IsOrganizationMember(ctx.Org.Team.OrgID, ctx.Doer.ID)
+	isMember, err := organization.IsOrganizationMember(ctx, ctx.Org.Team.OrgID, ctx.Doer.ID)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err)
 		return
@@ -377,14 +378,17 @@ func GetTeamMembers(ctx *context.APIContext) {
 		return
 	}
 
-	if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{
+	teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
 		ListOptions: utils.GetListOptions(ctx),
-	}); err != nil {
+		TeamID:      ctx.Org.Team.ID,
+	})
+	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err)
 		return
 	}
+
 	members := make([]*api.User, len(ctx.Org.Team.Members))
-	for i, member := range ctx.Org.Team.Members {
+	for i, member := range teamMembers {
 		members[i] = convert.ToUser(member, ctx.Doer)
 	}
 
@@ -422,7 +426,7 @@ func GetTeamMember(ctx *context.APIContext) {
 		return
 	}
 	teamID := ctx.ParamsInt64("teamid")
-	isTeamMember, err := models.IsUserInTeams(u.ID, []int64{teamID})
+	isTeamMember, err := organization.IsUserInTeams(ctx, u.ID, []int64{teamID})
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "IsUserInTeams", err)
 		return
@@ -462,7 +466,7 @@ func AddTeamMember(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	if err := ctx.Org.Team.AddMember(u.ID); err != nil {
+	if err := models.AddTeamMember(ctx.Org.Team, u.ID); err != nil {
 		ctx.Error(http.StatusInternalServerError, "AddMember", err)
 		return
 	}
@@ -499,8 +503,8 @@ func RemoveTeamMember(ctx *context.APIContext) {
 		return
 	}
 
-	if err := ctx.Org.Team.RemoveMember(u.ID); err != nil {
-		ctx.Error(http.StatusInternalServerError, "RemoveMember", err)
+	if err := models.RemoveTeamMember(ctx.Org.Team, u.ID); err != nil {
+		ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err)
 		return
 	}
 	ctx.Status(http.StatusNoContent)
@@ -533,13 +537,16 @@ func GetTeamRepos(ctx *context.APIContext) {
 	//     "$ref": "#/responses/RepositoryList"
 
 	team := ctx.Org.Team
-	if err := team.GetRepositories(&models.SearchOrgTeamOptions{
+	teamRepos, err := organization.GetTeamRepositories(ctx, &organization.SearchTeamRepoOptions{
 		ListOptions: utils.GetListOptions(ctx),
-	}); err != nil {
+		TeamID:      team.ID,
+	})
+	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err)
+		return
 	}
 	repos := make([]*api.Repository, len(team.Repos))
-	for i, repo := range team.Repos {
+	for i, repo := range teamRepos {
 		access, err := models.AccessLevel(ctx.Doer, repo)
 		if err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err)
@@ -606,7 +613,7 @@ func AddTeamRepository(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
 		return
 	}
-	if err := ctx.Org.Team.AddRepository(repo); err != nil {
+	if err := models.AddRepository(ctx.Org.Team, repo); err != nil {
 		ctx.Error(http.StatusInternalServerError, "AddRepository", err)
 		return
 	}
@@ -656,7 +663,7 @@ func RemoveTeamRepository(ctx *context.APIContext) {
 		ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
 		return
 	}
-	if err := ctx.Org.Team.RemoveRepository(repo.ID); err != nil {
+	if err := models.RemoveRepository(ctx.Org.Team, repo.ID); err != nil {
 		ctx.Error(http.StatusInternalServerError, "RemoveRepository", err)
 		return
 	}
@@ -707,14 +714,15 @@ func SearchTeam(ctx *context.APIContext) {
 
 	listOptions := utils.GetListOptions(ctx)
 
-	opts := &models.SearchOrgTeamOptions{
+	opts := &organization.SearchTeamOptions{
+		UserID:      ctx.Doer.ID,
 		Keyword:     ctx.FormTrim("q"),
 		OrgID:       ctx.Org.Organization.ID,
 		IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
 		ListOptions: listOptions,
 	}
 
-	teams, maxResults, err := models.SearchOrgTeams(opts)
+	teams, maxResults, err := organization.SearchTeam(opts)
 	if err != nil {
 		log.Error("SearchTeam failed: %v", err)
 		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go
index 065bad2708..f22bed813e 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"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -448,27 +449,27 @@ func CreateBranchProtection(ctx *context.APIContext) {
 	}
 	var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
 	if repo.Owner.IsOrganization() {
-		whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
+		whitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
 		if err != nil {
-			if models.IsErrTeamNotExist(err) {
+			if organization.IsErrTeamNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 				return
 			}
 			ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 			return
 		}
-		mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
+		mergeWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
 		if err != nil {
-			if models.IsErrTeamNotExist(err) {
+			if organization.IsErrTeamNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 				return
 			}
 			ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
 			return
 		}
-		approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
+		approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
 		if err != nil {
-			if models.IsErrTeamNotExist(err) {
+			if organization.IsErrTeamNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 				return
 			}
@@ -692,9 +693,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 	var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
 	if repo.Owner.IsOrganization() {
 		if form.PushWhitelistTeams != nil {
-			whitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
+			whitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.PushWhitelistTeams, false)
 			if err != nil {
-				if models.IsErrTeamNotExist(err) {
+				if organization.IsErrTeamNotExist(err) {
 					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 					return
 				}
@@ -705,9 +706,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 			whitelistTeams = protectBranch.WhitelistTeamIDs
 		}
 		if form.MergeWhitelistTeams != nil {
-			mergeWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
+			mergeWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.MergeWhitelistTeams, false)
 			if err != nil {
-				if models.IsErrTeamNotExist(err) {
+				if organization.IsErrTeamNotExist(err) {
 					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 					return
 				}
@@ -718,9 +719,9 @@ func EditBranchProtection(ctx *context.APIContext) {
 			mergeWhitelistTeams = protectBranch.MergeWhitelistTeamIDs
 		}
 		if form.ApprovalsWhitelistTeams != nil {
-			approvalsWhitelistTeams, err = models.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
+			approvalsWhitelistTeams, err = organization.GetTeamIDsByNames(repo.OwnerID, form.ApprovalsWhitelistTeams, false)
 			if err != nil {
-				if models.IsErrTeamNotExist(err) {
+				if organization.IsErrTeamNotExist(err) {
 					ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
 					return
 				}
diff --git a/routers/api/v1/repo/fork.go b/routers/api/v1/repo/fork.go
index 6cf51b2467..aaff9107cf 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"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -108,9 +109,9 @@ func CreateFork(ctx *context.APIContext) {
 	if form.Organization == nil {
 		forker = ctx.Doer
 	} else {
-		org, err := models.GetOrgByName(*form.Organization)
+		org, err := organization.GetOrgByName(*form.Organization)
 		if err != nil {
-			if models.IsErrOrgNotExist(err) {
+			if organization.IsErrOrgNotExist(err) {
 				ctx.Error(http.StatusUnprocessableEntity, "", err)
 			} else {
 				ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index ef8c2d008e..05c9c27144 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
@@ -161,9 +162,9 @@ func SearchIssues(ctx *context.APIContext) {
 			ctx.Error(http.StatusBadRequest, "", "Owner organisation is required for filtering on team")
 			return
 		}
-		team, err := models.GetTeam(opts.OwnerID, ctx.FormString("team"))
+		team, err := organization.GetTeam(opts.OwnerID, ctx.FormString("team"))
 		if err != nil {
-			if models.IsErrTeamNotExist(err) {
+			if organization.IsErrTeamNotExist(err) {
 				ctx.Error(http.StatusBadRequest, "Team not found", err)
 			} else {
 				ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index 1461c2a145..1adf5bbe86 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -90,7 +91,7 @@ func Migrate(ctx *context.APIContext) {
 
 		if repoOwner.IsOrganization() {
 			// Check ownership of organization.
-			isOwner, err := models.OrgFromUser(repoOwner).IsOwnedBy(ctx.Doer.ID)
+			isOwner, err := organization.OrgFromUser(repoOwner).IsOwnedBy(ctx.Doer.ID)
 			if err != nil {
 				ctx.Error(http.StatusInternalServerError, "IsOwnedBy", err)
 				return
diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go
index b4a484f68c..331b78e1d8 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"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
@@ -722,12 +723,12 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
 
 	if ctx.Repo.Repository.Owner.IsOrganization() && len(opts.TeamReviewers) > 0 {
 
-		teamReviewers := make([]*models.Team, 0, len(opts.TeamReviewers))
+		teamReviewers := make([]*organization.Team, 0, len(opts.TeamReviewers))
 		for _, t := range opts.TeamReviewers {
-			var teamReviewer *models.Team
-			teamReviewer, err = models.GetTeam(ctx.Repo.Owner.ID, t)
+			var teamReviewer *organization.Team
+			teamReviewer, err = organization.GetTeam(ctx.Repo.Owner.ID, t)
 			if err != nil {
-				if models.IsErrTeamNotExist(err) {
+				if organization.IsErrTeamNotExist(err) {
 					ctx.NotFound("TeamNotExist", fmt.Sprintf("Team '%s' not exist", t))
 					return
 				}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index 7e386d2e5b..2d979235a0 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -13,6 +13,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
@@ -398,7 +399,7 @@ func Generate(ctx *context.APIContext) {
 		}
 
 		if !ctx.Doer.IsAdmin {
-			canCreate, err := models.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx.Doer.ID)
+			canCreate, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx.Doer.ID)
 			if err != nil {
 				ctx.ServerError("CanCreateOrgRepo", err)
 				return
@@ -484,9 +485,9 @@ func CreateOrgRepo(ctx *context.APIContext) {
 	//   "403":
 	//     "$ref": "#/responses/forbidden"
 	opt := web.GetForm(ctx).(*api.CreateRepoOption)
-	org, err := models.GetOrgByName(ctx.Params(":org"))
+	org, err := organization.GetOrgByName(ctx.Params(":org"))
 	if err != nil {
-		if models.IsErrOrgNotExist(err) {
+		if organization.IsErrOrgNotExist(err) {
 			ctx.Error(http.StatusUnprocessableEntity, "", err)
 		} else {
 			ctx.Error(http.StatusInternalServerError, "GetOrgByName", err)
@@ -494,7 +495,7 @@ func CreateOrgRepo(ctx *context.APIContext) {
 		return
 	}
 
-	if !models.HasOrgOrUserVisible(org.AsUser(), ctx.Doer) {
+	if !organization.HasOrgOrUserVisible(ctx, org.AsUser(), ctx.Doer) {
 		ctx.NotFound("HasOrgOrUserVisible", nil)
 		return
 	}
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index 024224b5a3..1e3ea326d3 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/convert"
 	api "code.gitea.io/gitea/modules/structs"
@@ -41,7 +42,7 @@ func ListTeams(ctx *context.APIContext) {
 		return
 	}
 
-	teams, err := models.GetRepoTeams(ctx.Repo.Repository)
+	teams, err := organization.GetRepoTeams(ctx.Repo.Repository)
 	if err != nil {
 		ctx.InternalServerError(err)
 		return
@@ -101,7 +102,7 @@ func IsTeam(ctx *context.APIContext) {
 		return
 	}
 
-	if team.HasRepository(ctx.Repo.Repository.ID) {
+	if models.HasRepository(team, ctx.Repo.Repository.ID) {
 		if err := team.GetUnits(); err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetUnits", err)
 			return
@@ -196,20 +197,20 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
 		return
 	}
 
-	repoHasTeam := team.HasRepository(ctx.Repo.Repository.ID)
+	repoHasTeam := models.HasRepository(team, ctx.Repo.Repository.ID)
 	var err error
 	if add {
 		if repoHasTeam {
 			ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
 			return
 		}
-		err = team.AddRepository(ctx.Repo.Repository)
+		err = models.AddRepository(team, ctx.Repo.Repository)
 	} else {
 		if !repoHasTeam {
 			ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
 			return
 		}
-		err = team.RemoveRepository(ctx.Repo.Repository.ID)
+		err = models.RemoveRepository(team, ctx.Repo.Repository.ID)
 	}
 	if err != nil {
 		ctx.InternalServerError(err)
@@ -219,10 +220,10 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
 	ctx.Status(http.StatusNoContent)
 }
 
-func getTeamByParam(ctx *context.APIContext) *models.Team {
-	team, err := models.GetTeam(ctx.Repo.Owner.ID, ctx.Params(":team"))
+func getTeamByParam(ctx *context.APIContext) *organization.Team {
+	team, err := organization.GetTeam(ctx.Repo.Owner.ID, ctx.Params(":team"))
 	if err != nil {
-		if models.IsErrTeamNotExist(err) {
+		if organization.IsErrTeamNotExist(err) {
 			ctx.Error(http.StatusNotFound, "TeamNotExit", err)
 			return nil
 		}
diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go
index e149f9c8f0..7578fbd187 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"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -67,23 +68,23 @@ func Transfer(ctx *context.APIContext) {
 	}
 
 	if newOwner.Type == user_model.UserTypeOrganization {
-		if !ctx.Doer.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !models.OrgFromUser(newOwner).HasMemberWithUserID(ctx.Doer.ID) {
+		if !ctx.Doer.IsAdmin && newOwner.Visibility == api.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx.Doer.ID) {
 			// The user shouldn't know about this organization
 			ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
 			return
 		}
 	}
 
-	var teams []*models.Team
+	var teams []*organization.Team
 	if opts.TeamIDs != nil {
 		if !newOwner.IsOrganization() {
 			ctx.Error(http.StatusUnprocessableEntity, "repoTransfer", "Teams can only be added to organization-owned repositories")
 			return
 		}
 
-		org := convert.ToOrganization(models.OrgFromUser(newOwner))
+		org := convert.ToOrganization(organization.OrgFromUser(newOwner))
 		for _, tID := range *opts.TeamIDs {
-			team, err := models.GetTeamByID(tID)
+			team, err := organization.GetTeamByID(tID)
 			if err != nil {
 				ctx.Error(http.StatusUnprocessableEntity, "team", fmt.Errorf("team %d not found", tID))
 				return
diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go
index 83f97f8f01..cdbc35471b 100644
--- a/routers/api/v1/user/star.go
+++ b/routers/api/v1/user/star.go
@@ -21,7 +21,7 @@ import (
 // getStarredRepos returns the repos that the user with the specified userID has
 // starred
 func getStarredRepos(user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, error) {
-	starredRepos, err := models.GetStarredRepos(user.ID, private, listOptions)
+	starredRepos, err := repo_model.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 e9d871c5c1..018f75762f 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -98,7 +98,7 @@ func GetInfo(ctx *context.APIContext) {
 	//   "404":
 	//     "$ref": "#/responses/notFound"
 
-	if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
+	if !user_model.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
 		// fake ErrUserNotExist error message to not leak information about existence
 		ctx.NotFound("GetUserByName", user_model.ErrUserNotExist{Name: ctx.Params(":username")})
 		return
diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go
index 22e76f5ebc..e7c6837cb8 100644
--- a/routers/api/v1/user/watch.go
+++ b/routers/api/v1/user/watch.go
@@ -19,7 +19,7 @@ import (
 
 // getWatchedRepos returns the repos that the user with the specified userID is watching
 func getWatchedRepos(user *user_model.User, private bool, listOptions db.ListOptions) ([]*api.Repository, int64, error) {
-	watchedRepos, total, err := models.GetWatchedRepos(user.ID, private, listOptions)
+	watchedRepos, total, err := repo_model.GetWatchedRepos(user.ID, private, listOptions)
 	if err != nil {
 		return nil, 0, err
 	}
diff --git a/routers/web/org/home.go b/routers/web/org/home.go
index 1466731b9e..24a0f13b54 100644
--- a/routers/web/org/home.go
+++ b/routers/web/org/home.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -39,7 +40,7 @@ func Home(ctx *context.Context) {
 
 	org := ctx.Org.Organization
 
-	if !models.HasOrgOrUserVisible(org.AsUser(), ctx.Doer) {
+	if !organization.HasOrgOrUserVisible(ctx, org.AsUser(), ctx.Doer) {
 		ctx.NotFound("HasOrgOrUserVisible", nil)
 		return
 	}
@@ -122,7 +123,7 @@ func Home(ctx *context.Context) {
 		return
 	}
 
-	opts := &models.FindOrgMembersOpts{
+	opts := &organization.FindOrgMembersOpts{
 		OrgID:       org.ID,
 		PublicOnly:  true,
 		ListOptions: db.ListOptions{Page: 1, PageSize: 25},
@@ -137,13 +138,13 @@ func Home(ctx *context.Context) {
 		opts.PublicOnly = !isMember && !ctx.Doer.IsAdmin
 	}
 
-	members, _, err := models.FindOrgMembers(opts)
+	members, _, err := organization.FindOrgMembers(opts)
 	if err != nil {
 		ctx.ServerError("FindOrgMembers", err)
 		return
 	}
 
-	membersCount, err := models.CountOrgMembers(opts)
+	membersCount, err := organization.CountOrgMembers(opts)
 	if err != nil {
 		ctx.ServerError("CountOrgMembers", err)
 		return
diff --git a/routers/web/org/members.go b/routers/web/org/members.go
index 9e65e61404..add8e724bd 100644
--- a/routers/web/org/members.go
+++ b/routers/web/org/members.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -31,7 +32,7 @@ func Members(ctx *context.Context) {
 		page = 1
 	}
 
-	opts := &models.FindOrgMembersOpts{
+	opts := &organization.FindOrgMembersOpts{
 		OrgID:      org.ID,
 		PublicOnly: true,
 	}
@@ -45,7 +46,7 @@ func Members(ctx *context.Context) {
 		opts.PublicOnly = !isMember && !ctx.Doer.IsAdmin
 	}
 
-	total, err := models.CountOrgMembers(opts)
+	total, err := organization.CountOrgMembers(opts)
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "CountOrgMembers")
 		return
@@ -54,7 +55,7 @@ func Members(ctx *context.Context) {
 	pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5)
 	opts.ListOptions.Page = page
 	opts.ListOptions.PageSize = setting.UI.MembersPagingNum
-	members, membersIsPublic, err := models.FindOrgMembers(opts)
+	members, membersIsPublic, err := organization.FindOrgMembers(opts)
 	if err != nil {
 		ctx.ServerError("GetMembers", err)
 		return
@@ -84,20 +85,20 @@ func MembersAction(ctx *context.Context) {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = models.ChangeOrgUserStatus(org.ID, uid, false)
+		err = organization.ChangeOrgUserStatus(org.ID, uid, false)
 	case "public":
 		if ctx.Doer.ID != uid && !ctx.Org.IsOwner {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = models.ChangeOrgUserStatus(org.ID, uid, true)
+		err = organization.ChangeOrgUserStatus(org.ID, uid, true)
 	case "remove":
 		if !ctx.Org.IsOwner {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = org.RemoveMember(uid)
-		if models.IsErrLastOrgOwner(err) {
+		err = models.RemoveOrgUser(org.ID, uid)
+		if organization.IsErrLastOrgOwner(err) {
 			ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 			ctx.JSON(http.StatusOK, map[string]interface{}{
 				"redirect": ctx.Org.OrgLink + "/members",
@@ -105,8 +106,8 @@ func MembersAction(ctx *context.Context) {
 			return
 		}
 	case "leave":
-		err = org.RemoveMember(ctx.Doer.ID)
-		if models.IsErrLastOrgOwner(err) {
+		err = models.RemoveOrgUser(org.ID, ctx.Doer.ID)
+		if organization.IsErrLastOrgOwner(err) {
 			ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 			ctx.JSON(http.StatusOK, map[string]interface{}{
 				"redirect": ctx.Org.OrgLink + "/members",
diff --git a/routers/web/org/org.go b/routers/web/org/org.go
index b641f768b2..32d8787995 100644
--- a/routers/web/org/org.go
+++ b/routers/web/org/org.go
@@ -9,8 +9,8 @@ import (
 	"errors"
 	"net/http"
 
-	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -51,7 +51,7 @@ func CreatePost(ctx *context.Context) {
 		return
 	}
 
-	org := &models.Organization{
+	org := &organization.Organization{
 		Name:                      form.OrgName,
 		IsActive:                  true,
 		Type:                      user_model.UserTypeOrganization,
@@ -59,7 +59,7 @@ func CreatePost(ctx *context.Context) {
 		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
 	}
 
-	if err := models.CreateOrganization(org, ctx.Doer); err != nil {
+	if err := organization.CreateOrganization(org, ctx.Doer); err != nil {
 		ctx.Data["Err_OrgName"] = true
 		switch {
 		case user_model.IsErrUserAlreadyExist(err):
@@ -68,7 +68,7 @@ func CreatePost(ctx *context.Context) {
 			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):
+		case organization.IsErrUserNotAllowedCreateOrg(err):
 			ctx.RenderWithErr(ctx.Tr("org.form.create_org_not_allowed"), tplCreateOrg, &form)
 		default:
 			ctx.ServerError("CreateOrganization", err)
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index d13c4e854d..034a8ce978 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -13,6 +13,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
@@ -43,7 +44,7 @@ func Teams(ctx *context.Context) {
 	ctx.Data["PageIsOrgTeams"] = true
 
 	for _, t := range ctx.Org.Teams {
-		if err := t.GetMembers(&models.SearchMembersOptions{}); err != nil {
+		if err := t.GetMembersCtx(ctx); err != nil {
 			ctx.ServerError("GetMembers", err)
 			return
 		}
@@ -69,11 +70,11 @@ func TeamsAction(ctx *context.Context) {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = ctx.Org.Team.AddMember(ctx.Doer.ID)
+		err = models.AddTeamMember(ctx.Org.Team, ctx.Doer.ID)
 	case "leave":
-		err = ctx.Org.Team.RemoveMember(ctx.Doer.ID)
+		err = models.RemoveTeamMember(ctx.Org.Team, ctx.Doer.ID)
 		if err != nil {
-			if models.IsErrLastOrgOwner(err) {
+			if organization.IsErrLastOrgOwner(err) {
 				ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 			} else {
 				log.Error("Action(%s): %v", ctx.Params(":action"), err)
@@ -94,9 +95,9 @@ func TeamsAction(ctx *context.Context) {
 			ctx.Error(http.StatusNotFound)
 			return
 		}
-		err = ctx.Org.Team.RemoveMember(uid)
+		err = models.RemoveTeamMember(ctx.Org.Team, uid)
 		if err != nil {
-			if models.IsErrLastOrgOwner(err) {
+			if organization.IsErrLastOrgOwner(err) {
 				ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 			} else {
 				log.Error("Action(%s): %v", ctx.Params(":action"), err)
@@ -139,14 +140,14 @@ func TeamsAction(ctx *context.Context) {
 		if ctx.Org.Team.IsMember(u.ID) {
 			ctx.Flash.Error(ctx.Tr("org.teams.add_duplicate_users"))
 		} else {
-			err = ctx.Org.Team.AddMember(u.ID)
+			err = models.AddTeamMember(ctx.Org.Team, u.ID)
 		}
 
 		page = "team"
 	}
 
 	if err != nil {
-		if models.IsErrLastOrgOwner(err) {
+		if organization.IsErrLastOrgOwner(err) {
 			ctx.Flash.Error(ctx.Tr("form.last_org_owner"))
 		} else {
 			log.Error("Action(%s): %v", ctx.Params(":action"), err)
@@ -191,13 +192,13 @@ func TeamsRepoAction(ctx *context.Context) {
 			ctx.ServerError("GetRepositoryByName", err)
 			return
 		}
-		err = ctx.Org.Team.AddRepository(repo)
+		err = models.AddRepository(ctx.Org.Team, repo)
 	case "remove":
-		err = ctx.Org.Team.RemoveRepository(ctx.FormInt64("repoid"))
+		err = models.RemoveRepository(ctx.Org.Team, ctx.FormInt64("repoid"))
 	case "addall":
-		err = ctx.Org.Team.AddAllRepositories()
+		err = models.AddAllRepositories(ctx.Org.Team)
 	case "removeall":
-		err = ctx.Org.Team.RemoveAllRepositories()
+		err = models.RemoveAllRepositories(ctx.Org.Team)
 	}
 
 	if err != nil {
@@ -220,7 +221,7 @@ func NewTeam(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Org.Organization.FullName
 	ctx.Data["PageIsOrgTeams"] = true
 	ctx.Data["PageIsOrgTeamsNew"] = true
-	ctx.Data["Team"] = &models.Team{}
+	ctx.Data["Team"] = &organization.Team{}
 	ctx.Data["Units"] = unit_model.Units
 	ctx.HTML(http.StatusOK, tplTeamNew)
 }
@@ -251,7 +252,7 @@ func NewTeamPost(ctx *context.Context) {
 		p = unit_model.MinUnitAccessMode(unitPerms)
 	}
 
-	t := &models.Team{
+	t := &organization.Team{
 		OrgID:                   ctx.Org.Organization.ID,
 		Name:                    form.TeamName,
 		Description:             form.Description,
@@ -261,9 +262,9 @@ func NewTeamPost(ctx *context.Context) {
 	}
 
 	if t.AccessMode < perm.AccessModeAdmin {
-		units := make([]*models.TeamUnit, 0, len(unitPerms))
+		units := make([]*organization.TeamUnit, 0, len(unitPerms))
 		for tp, perm := range unitPerms {
-			units = append(units, &models.TeamUnit{
+			units = append(units, &organization.TeamUnit{
 				OrgID:      ctx.Org.Organization.ID,
 				Type:       tp,
 				AccessMode: perm,
@@ -291,7 +292,7 @@ func NewTeamPost(ctx *context.Context) {
 	if err := models.NewTeam(t); err != nil {
 		ctx.Data["Err_TeamName"] = true
 		switch {
-		case models.IsErrTeamAlreadyExist(err):
+		case organization.IsErrTeamAlreadyExist(err):
 			ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
 		default:
 			ctx.ServerError("NewTeam", err)
@@ -307,7 +308,7 @@ func TeamMembers(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Org.Team.Name
 	ctx.Data["PageIsOrgTeams"] = true
 	ctx.Data["PageIsOrgTeamMembers"] = true
-	if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{}); err != nil {
+	if err := ctx.Org.Team.GetMembersCtx(ctx); err != nil {
 		ctx.ServerError("GetMembers", err)
 		return
 	}
@@ -320,7 +321,7 @@ func TeamRepositories(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Org.Team.Name
 	ctx.Data["PageIsOrgTeams"] = true
 	ctx.Data["PageIsOrgTeamRepos"] = true
-	if err := ctx.Org.Team.GetRepositories(&models.SearchOrgTeamOptions{}); err != nil {
+	if err := ctx.Org.Team.GetRepositoriesCtx(ctx); err != nil {
 		ctx.ServerError("GetRepositories", err)
 		return
 	}
@@ -374,17 +375,17 @@ func EditTeamPost(ctx *context.Context) {
 	}
 	t.Description = form.Description
 	if t.AccessMode < perm.AccessModeAdmin {
-		units := make([]models.TeamUnit, 0, len(unitPerms))
+		units := make([]organization.TeamUnit, 0, len(unitPerms))
 		for tp, perm := range unitPerms {
-			units = append(units, models.TeamUnit{
+			units = append(units, organization.TeamUnit{
 				OrgID:      t.OrgID,
 				TeamID:     t.ID,
 				Type:       tp,
 				AccessMode: perm,
 			})
 		}
-		if err := models.UpdateTeamUnits(t, units); err != nil {
-			ctx.Error(http.StatusInternalServerError, "LoadIssue", err.Error())
+		if err := organization.UpdateTeamUnits(t, units); err != nil {
+			ctx.Error(http.StatusInternalServerError, "UpdateTeamUnits", err.Error())
 			return
 		}
 	}
@@ -403,7 +404,7 @@ func EditTeamPost(ctx *context.Context) {
 	if err := models.UpdateTeam(t, isAuthChanged, isIncludeAllChanged); err != nil {
 		ctx.Data["Err_TeamName"] = true
 		switch {
-		case models.IsErrTeamAlreadyExist(err):
+		case organization.IsErrTeamAlreadyExist(err):
 			ctx.RenderWithErr(ctx.Tr("form.team_name_been_taken"), tplTeamNew, &form)
 		default:
 			ctx.ServerError("UpdateTeam", err)
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index aeb1f0a020..ee4738c970 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -19,6 +19,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -471,7 +472,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
 // repoReviewerSelection items to bee shown
 type repoReviewerSelection struct {
 	IsTeam    bool
-	Team      *models.Team
+	Team      *organization.Team
 	User      *user_model.User
 	Review    *models.Review
 	CanChange bool
@@ -504,7 +505,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
 		pullReviews         []*repoReviewerSelection
 		reviewersResult     []*repoReviewerSelection
 		teamReviewersResult []*repoReviewerSelection
-		teamReviewers       []*models.Team
+		teamReviewers       []*organization.Team
 		reviewers           []*user_model.User
 	)
 
@@ -586,7 +587,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is
 				item.User = item.Review.Reviewer
 			} else if item.Review.ReviewerTeamID > 0 {
 				if err = item.Review.LoadReviewerTeam(); err != nil {
-					if models.IsErrTeamNotExist(err) {
+					if organization.IsErrTeamNotExist(err) {
 						continue
 					}
 					ctx.ServerError("LoadReviewerTeam", err)
@@ -1965,9 +1966,9 @@ func UpdatePullReviewRequest(ctx *context.Context) {
 				return
 			}
 
-			team, err := models.GetTeamByID(-reviewID)
+			team, err := organization.GetTeamByID(-reviewID)
 			if err != nil {
-				ctx.ServerError("models.GetTeamByID", err)
+				ctx.ServerError("GetTeamByID", err)
 				return
 			}
 
@@ -2702,8 +2703,8 @@ func handleTeamMentions(ctx *context.Context) {
 
 	var isAdmin bool
 	var err error
-	var teams []*models.Team
-	org := models.OrgFromUser(ctx.Repo.Owner)
+	var teams []*organization.Team
+	org := organization.OrgFromUser(ctx.Repo.Owner)
 	// Admin has super access.
 	if ctx.Doer.IsAdmin {
 		isAdmin = true
diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go
index ec04fa1660..a8866fc072 100644
--- a/routers/web/repo/issue_label.go
+++ b/routers/web/repo/issue_label.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
@@ -77,7 +78,7 @@ func RetrieveLabels(ctx *context.Context) {
 		}
 		ctx.Data["OrgLabels"] = orgLabels
 
-		org, err := models.GetOrgByName(ctx.Repo.Owner.LowerName)
+		org, err := organization.GetOrgByName(ctx.Repo.Owner.LowerName)
 		if err != nil {
 			ctx.ServerError("GetOrgByName", err)
 			return
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index a4edfd9ee4..af99f1b275 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -18,6 +18,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -111,12 +112,12 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
 
 	ctx.Data["ForkRepo"] = forkRepo
 
-	ownedOrgs, err := models.GetOrgsCanCreateRepoByUserID(ctx.Doer.ID)
+	ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx.Doer.ID)
 	if err != nil {
 		ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
 		return nil
 	}
-	var orgs []*models.Organization
+	var orgs []*organization.Organization
 	for _, org := range ownedOrgs {
 		if forkRepo.OwnerID != org.ID && !repo_model.HasForkedRepo(org.ID, forkRepo.ID) {
 			orgs = append(orgs, org)
@@ -216,7 +217,7 @@ func ForkPost(ctx *context.Context) {
 
 	// Check if user is allowed to create repo's on the organization.
 	if ctxUser.IsOrganization() {
-		isAllowedToFork, err := models.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx.Doer.ID)
+		isAllowedToFork, err := organization.OrgFromUser(ctxUser).CanCreateOrgRepo(ctx.Doer.ID)
 		if err != nil {
 			ctx.ServerError("CanCreateOrgRepo", err)
 			return
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index 4e07d47203..8b03133611 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -57,14 +58,14 @@ func MustBeAbleToUpload(ctx *context.Context) {
 }
 
 func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
-	orgs, err := models.GetOrgsCanCreateRepoByUserID(ctx.Doer.ID)
+	orgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx.Doer.ID)
 	if err != nil {
 		ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
 		return nil
 	}
 
 	if !ctx.Doer.IsAdmin {
-		orgsAvailable := []*models.Organization{}
+		orgsAvailable := []*organization.Organization{}
 		for i := 0; i < len(orgs); i++ {
 			if orgs[i].CanCreateRepo() {
 				orgsAvailable = append(orgsAvailable, orgs[i])
@@ -96,7 +97,7 @@ func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
 		return nil
 	}
 	if !ctx.Doer.IsAdmin {
-		canCreate, err := models.OrgFromUser(org).CanCreateOrgRepo(ctx.Doer.ID)
+		canCreate, err := organization.OrgFromUser(org).CanCreateOrgRepo(ctx.Doer.ID)
 		if err != nil {
 			ctx.ServerError("CanCreateOrgRepo", err)
 			return nil
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 75db7ca3e2..ef99eee15a 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	unit_model "code.gitea.io/gitea/models/unit"
@@ -648,7 +649,7 @@ func SettingsPost(ctx *context.Context) {
 		}
 
 		if newOwner.Type == user_model.UserTypeOrganization {
-			if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !models.OrgFromUser(newOwner).HasMemberWithUserID(ctx.Doer.ID) {
+			if !ctx.Doer.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !organization.OrgFromUser(newOwner).HasMemberWithUserID(ctx.Doer.ID) {
 				// The user shouldn't know about this organization
 				ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
 				return
@@ -835,7 +836,7 @@ func Collaboration(ctx *context.Context) {
 	}
 	ctx.Data["Collaborators"] = users
 
-	teams, err := models.GetRepoTeams(ctx.Repo.Repository)
+	teams, err := organization.GetRepoTeams(ctx.Repo.Repository)
 	if err != nil {
 		ctx.ServerError("GetRepoTeams", err)
 		return
@@ -938,9 +939,9 @@ func AddTeamPost(ctx *context.Context) {
 		return
 	}
 
-	team, err := models.OrgFromUser(ctx.Repo.Owner).GetTeam(name)
+	team, err := organization.OrgFromUser(ctx.Repo.Owner).GetTeam(name)
 	if err != nil {
-		if models.IsErrTeamNotExist(err) {
+		if organization.IsErrTeamNotExist(err) {
 			ctx.Flash.Error(ctx.Tr("form.team_not_exist"))
 			ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
 		} else {
@@ -955,13 +956,13 @@ func AddTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if models.HasTeamRepo(ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) {
+	if organization.HasTeamRepo(ctx, ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) {
 		ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate"))
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
 		return
 	}
 
-	if err = team.AddRepository(ctx.Repo.Repository); err != nil {
+	if err = models.AddRepository(team, ctx.Repo.Repository); err != nil {
 		ctx.ServerError("team.AddRepository", err)
 		return
 	}
@@ -978,13 +979,13 @@ func DeleteTeam(ctx *context.Context) {
 		return
 	}
 
-	team, err := models.GetTeamByID(ctx.FormInt64("id"))
+	team, err := organization.GetTeamByID(ctx.FormInt64("id"))
 	if err != nil {
 		ctx.ServerError("GetTeamByID", err)
 		return
 	}
 
-	if err = team.RemoveRepository(ctx.Repo.Repository.ID); err != nil {
+	if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil {
 		ctx.ServerError("team.RemoveRepositorys", err)
 		return
 	}
diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go
index dae618a758..a26a0a620a 100644
--- a/routers/web/repo/setting_protected_branch.go
+++ b/routers/web/repo/setting_protected_branch.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/modules/base"
@@ -158,7 +159,7 @@ func SettingsProtectedBranch(c *context.Context) {
 	}
 
 	if c.Repo.Owner.IsOrganization() {
-		teams, err := models.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead)
+		teams, err := organization.OrgFromUser(c.Repo.Owner).TeamsWithAccessToRepo(c.Repo.Repository.ID, perm.AccessModeRead)
 		if err != nil {
 			c.ServerError("Repo.Owner.TeamsWithAccessToRepo", err)
 			return
diff --git a/routers/web/repo/settings_test.go b/routers/web/repo/settings_test.go
index db1e905869..36d02de273 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"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
@@ -231,7 +232,7 @@ func TestAddTeamPost(t *testing.T) {
 		Type:      user_model.UserTypeOrganization,
 	}
 
-	team := &models.Team{
+	team := &organization.Team{
 		ID:    11,
 		OrgID: 26,
 	}
@@ -255,7 +256,7 @@ func TestAddTeamPost(t *testing.T) {
 
 	AddTeamPost(ctx)
 
-	assert.True(t, team.HasRepository(re.ID))
+	assert.True(t, models.HasRepository(team, re.ID))
 	assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
 	assert.Empty(t, ctx.Flash.ErrorMsg)
 }
@@ -271,7 +272,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
 		Type:      user_model.UserTypeOrganization,
 	}
 
-	team := &models.Team{
+	team := &organization.Team{
 		ID:    11,
 		OrgID: 26,
 	}
@@ -295,7 +296,7 @@ func TestAddTeamPost_NotAllowed(t *testing.T) {
 
 	AddTeamPost(ctx)
 
-	assert.False(t, team.HasRepository(re.ID))
+	assert.False(t, models.HasRepository(team, re.ID))
 	assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
 	assert.NotEmpty(t, ctx.Flash.ErrorMsg)
 }
@@ -311,7 +312,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
 		Type:      user_model.UserTypeOrganization,
 	}
 
-	team := &models.Team{
+	team := &organization.Team{
 		ID:    11,
 		OrgID: 26,
 	}
@@ -336,7 +337,7 @@ func TestAddTeamPost_AddTeamTwice(t *testing.T) {
 	AddTeamPost(ctx)
 
 	AddTeamPost(ctx)
-	assert.True(t, team.HasRepository(re.ID))
+	assert.True(t, models.HasRepository(team, re.ID))
 	assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status())
 	assert.NotEmpty(t, ctx.Flash.ErrorMsg)
 }
@@ -385,7 +386,7 @@ func TestDeleteTeam(t *testing.T) {
 		Type:      user_model.UserTypeOrganization,
 	}
 
-	team := &models.Team{
+	team := &organization.Team{
 		ID:    2,
 		OrgID: 3,
 	}
@@ -409,5 +410,5 @@ func TestDeleteTeam(t *testing.T) {
 
 	DeleteTeam(ctx)
 
-	assert.False(t, team.HasRepository(re.ID))
+	assert.False(t, models.HasRepository(team, re.ID))
 }
diff --git a/routers/web/repo/tag.go b/routers/web/repo/tag.go
index 0114704f82..7da1e36c81 100644
--- a/routers/web/repo/tag.go
+++ b/routers/web/repo/tag.go
@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
@@ -150,7 +151,7 @@ func setTagsContext(ctx *context.Context) error {
 	ctx.Data["Users"] = users
 
 	if ctx.Repo.Owner.IsOrganization() {
-		teams, err := models.OrgFromUser(ctx.Repo.Owner).TeamsWithAccessToRepo(ctx.Repo.Repository.ID, perm.AccessModeRead)
+		teams, err := organization.OrgFromUser(ctx.Repo.Owner).TeamsWithAccessToRepo(ctx.Repo.Repository.ID, perm.AccessModeRead)
 		if err != nil {
 			ctx.ServerError("Repo.Owner.TeamsWithAccessToRepo", err)
 			return err
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index e0beb0cbee..156cf0fa64 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -74,7 +75,7 @@ func Dashboard(ctx *context.Context) {
 	ctx.Data["Title"] = ctxUser.DisplayName() + " - " + ctx.Tr("dashboard")
 	ctx.Data["PageIsDashboard"] = true
 	ctx.Data["PageIsNews"] = true
-	cnt, _ := models.GetOrganizationCount(ctx, ctxUser)
+	cnt, _ := organization.GetOrganizationCount(ctx, ctxUser)
 	ctx.Data["UserOrgsCount"] = cnt
 
 	var uid int64
@@ -99,11 +100,11 @@ func Dashboard(ctx *context.Context) {
 	var err error
 	var mirrors []*repo_model.Repository
 	if ctxUser.IsOrganization() {
-		var env models.AccessibleReposEnvironment
+		var env organization.AccessibleReposEnvironment
 		if ctx.Org.Team != nil {
-			env = models.OrgFromUser(ctxUser).AccessibleTeamReposEnv(ctx.Org.Team)
+			env = organization.OrgFromUser(ctxUser).AccessibleTeamReposEnv(ctx.Org.Team)
 		} else {
-			env, err = models.OrgFromUser(ctxUser).AccessibleReposEnv(ctx.Doer.ID)
+			env, err = organization.AccessibleReposEnv(ctx, organization.OrgFromUser(ctxUser), ctx.Doer.ID)
 			if err != nil {
 				ctx.ServerError("AccessibleReposEnv", err)
 				return
@@ -404,8 +405,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
 	// --------------------------------------------------------------------------
 
 	// Get repository IDs where User/Org/Team has access.
-	var team *models.Team
-	var org *models.Organization
+	var team *organization.Team
+	var org *organization.Organization
 	if ctx.Org != nil {
 		org = ctx.Org.Organization
 		team = ctx.Org.Team
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index db2660af61..99198e8866 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/context"
@@ -40,7 +41,7 @@ func Profile(ctx *context.Context) {
 	}
 
 	// check view permissions
-	if !models.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
+	if !user_model.IsUserVisibleToViewer(ctx.ContextUser, ctx.Doer) {
 		ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
 		return
 	}
@@ -91,7 +92,7 @@ func Profile(ctx *context.Context) {
 
 	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
 
-	orgs, err := models.FindOrgs(models.FindOrgOptions{
+	orgs, err := organization.FindOrgs(organization.FindOrgOptions{
 		UserID:         ctx.ContextUser.ID,
 		IncludePrivate: showPrivate,
 	})
@@ -101,7 +102,7 @@ func Profile(ctx *context.Context) {
 	}
 
 	ctx.Data["Orgs"] = orgs
-	ctx.Data["HasOrgsVisible"] = models.HasOrgsVisible(orgs, ctx.Doer)
+	ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer)
 
 	tab := ctx.FormString("tab")
 	ctx.Data["TabName"] = tab
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index 09871ef763..7a875e38cb 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -17,6 +17,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/base"
@@ -218,7 +219,7 @@ func Organization(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("settings")
 	ctx.Data["PageIsSettingsOrganization"] = true
 
-	opts := models.FindOrgOptions{
+	opts := organization.FindOrgOptions{
 		ListOptions: db.ListOptions{
 			PageSize: setting.UI.Admin.UserPagingNum,
 			Page:     ctx.FormInt("page"),
@@ -231,12 +232,12 @@ func Organization(ctx *context.Context) {
 		opts.Page = 1
 	}
 
-	orgs, err := models.FindOrgs(opts)
+	orgs, err := organization.FindOrgs(opts)
 	if err != nil {
 		ctx.ServerError("FindOrgs", err)
 		return
 	}
-	total, err := models.CountOrgs(opts)
+	total, err := organization.CountOrgs(opts)
 	if err != nil {
 		ctx.ServerError("CountOrgs", err)
 		return
diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go
index 2c1bcc29cc..ddd70627ed 100644
--- a/services/auth/source/ldap/source_authenticate.go
+++ b/services/auth/source/ldap/source_authenticate.go
@@ -8,10 +8,10 @@ import (
 	"fmt"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/auth"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/services/mailer"
 	user_service "code.gitea.io/gitea/services/user"
@@ -65,8 +65,8 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
 
 	if user != nil {
 		if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) {
-			orgCache := make(map[string]*models.Organization)
-			teamCache := make(map[string]*models.Team)
+			orgCache := make(map[string]*organization.Organization)
+			teamCache := make(map[string]*organization.Team)
 			source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache)
 		}
 		if isAttributeSSHPublicKeySet && asymkey_model.SynchronizePublicKeys(user, source.authSource, sr.SSHPublicKey) {
@@ -111,8 +111,8 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
 		_ = user_service.UploadAvatar(user, sr.Avatar)
 	}
 	if source.GroupsEnabled && (source.GroupTeamMap != "" || source.GroupTeamMapRemoval) {
-		orgCache := make(map[string]*models.Organization)
-		teamCache := make(map[string]*models.Team)
+		orgCache := make(map[string]*organization.Organization)
+		teamCache := make(map[string]*organization.Team)
 		source.SyncLdapGroupsToTeams(user, sr.LdapTeamAdd, sr.LdapTeamRemove, orgCache, teamCache)
 	}
 
diff --git a/services/auth/source/ldap/source_group_sync.go b/services/auth/source/ldap/source_group_sync.go
index 7c62af705e..e797e015b2 100644
--- a/services/auth/source/ldap/source_group_sync.go
+++ b/services/auth/source/ldap/source_group_sync.go
@@ -6,12 +6,14 @@ package ldap
 
 import (
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 )
 
 // SyncLdapGroupsToTeams maps LDAP groups to organization and team memberships
-func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) {
+func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd, ldapTeamRemove map[string][]string, orgCache map[string]*organization.Organization, teamCache map[string]*organization.Team) {
 	var err error
 	if source.GroupsEnabled && source.GroupTeamMapRemoval {
 		// when the user is not a member of configs LDAP group, remove mapped organizations/teams memberships
@@ -20,7 +22,7 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd,
 	for orgName, teamNames := range ldapTeamAdd {
 		org, ok := orgCache[orgName]
 		if !ok {
-			org, err = models.GetOrgByName(orgName)
+			org, err = organization.GetOrgByName(orgName)
 			if err != nil {
 				// organization must be created before LDAP group sync
 				log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err)
@@ -28,14 +30,7 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd,
 			}
 			orgCache[orgName] = org
 		}
-		if isMember, err := models.IsOrganizationMember(org.ID, user.ID); !isMember && err == nil {
-			log.Trace("LDAP group sync: adding user [%s] to organization [%s]", user.Name, org.Name)
-			err = org.AddMember(user.ID)
-			if err != nil {
-				log.Error("LDAP group sync: Could not add user to organization: %v", err)
-				continue
-			}
-		}
+
 		for _, teamName := range teamNames {
 			team, ok := teamCache[orgName+teamName]
 			if !ok {
@@ -47,12 +42,12 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd,
 				}
 				teamCache[orgName+teamName] = team
 			}
-			if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); !isMember && err == nil {
+			if isMember, err := organization.IsTeamMember(db.DefaultContext, org.ID, team.ID, user.ID); !isMember && err == nil {
 				log.Trace("LDAP group sync: adding user [%s] to team [%s]", user.Name, org.Name)
 			} else {
 				continue
 			}
-			err := team.AddMember(user.ID)
+			err := models.AddTeamMember(team, user.ID)
 			if err != nil {
 				log.Error("LDAP group sync: Could not add user to team: %v", err)
 			}
@@ -63,12 +58,12 @@ func (source *Source) SyncLdapGroupsToTeams(user *user_model.User, ldapTeamAdd,
 // remove membership to organizations/teams if user is not member of corresponding LDAP group
 // e.g. lets assume user is member of LDAP group "x", but LDAP group team map contains LDAP groups "x" and "y"
 // then users membership gets removed for all organizations/teams mapped by LDAP group "y"
-func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][]string, orgCache map[string]*models.Organization, teamCache map[string]*models.Team) {
+func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][]string, orgCache map[string]*organization.Organization, teamCache map[string]*organization.Team) {
 	var err error
 	for orgName, teamNames := range ldapTeamRemove {
 		org, ok := orgCache[orgName]
 		if !ok {
-			org, err = models.GetOrgByName(orgName)
+			org, err = organization.GetOrgByName(orgName)
 			if err != nil {
 				// organization must be created before LDAP group sync
 				log.Warn("LDAP group sync: Could not find organisation %s: %v", orgName, err)
@@ -86,12 +81,12 @@ func removeMappedMemberships(user *user_model.User, ldapTeamRemove map[string][]
 					continue
 				}
 			}
-			if isMember, err := models.IsTeamMember(org.ID, team.ID, user.ID); isMember && err == nil {
+			if isMember, err := organization.IsTeamMember(db.DefaultContext, org.ID, team.ID, user.ID); isMember && err == nil {
 				log.Trace("LDAP group sync: removing user [%s] from team [%s]", user.Name, org.Name)
 			} else {
 				continue
 			}
-			err = team.RemoveMember(user.ID)
+			err = models.RemoveTeamMember(team, user.ID)
 			if err != nil {
 				log.Error("LDAP group sync: Could not remove user from team: %v", err)
 			}
diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go
index b7ea1f0584..65efed78c1 100644
--- a/services/auth/source/ldap/source_sync.go
+++ b/services/auth/source/ldap/source_sync.go
@@ -10,9 +10,9 @@ import (
 	"sort"
 	"strings"
 
-	"code.gitea.io/gitea/models"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
 	user_service "code.gitea.io/gitea/services/user"
@@ -62,8 +62,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error {
 	})
 
 	userPos := 0
-	orgCache := make(map[string]*models.Organization)
-	teamCache := make(map[string]*models.Team)
+	orgCache := make(map[string]*organization.Organization)
+	teamCache := make(map[string]*organization.Team)
 
 	for _, su := range sr {
 		select {
diff --git a/services/issue/assignee.go b/services/issue/assignee.go
index f09c51293b..86d653dfa6 100644
--- a/services/issue/assignee.go
+++ b/services/issue/assignee.go
@@ -6,6 +6,8 @@ package issue
 
 import (
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/models/unit"
 	user_model "code.gitea.io/gitea/models/user"
@@ -166,7 +168,7 @@ func IsValidReviewRequest(reviewer, doer *user_model.User, isAdd bool, issue *mo
 }
 
 // IsValidTeamReviewRequest Check permission for ReviewRequest Team
-func IsValidTeamReviewRequest(reviewer *models.Team, doer *user_model.User, isAdd bool, issue *models.Issue) error {
+func IsValidTeamReviewRequest(reviewer *organization.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",
@@ -183,7 +185,7 @@ func IsValidTeamReviewRequest(reviewer *models.Team, doer *user_model.User, isAd
 
 	if isAdd {
 		if issue.Repo.IsPrivate {
-			hasTeam := models.HasTeamRepo(reviewer.OrgID, reviewer.ID, issue.RepoID)
+			hasTeam := organization.HasTeamRepo(db.DefaultContext, reviewer.OrgID, reviewer.ID, issue.RepoID)
 
 			if !hasTeam {
 				return models.ErrNotValidReviewRequest{
@@ -221,7 +223,7 @@ func IsValidTeamReviewRequest(reviewer *models.Team, doer *user_model.User, isAd
 }
 
 // TeamReviewRequest add or remove a review request from a team for this PR, and make comment for it.
-func TeamReviewRequest(issue *models.Issue, doer *user_model.User, reviewer *models.Team, isAdd bool) (comment *models.Comment, err error) {
+func TeamReviewRequest(issue *models.Issue, doer *user_model.User, reviewer *organization.Team, isAdd bool) (comment *models.Comment, err error) {
 	if isAdd {
 		comment, err = models.AddTeamReviewRequest(issue, reviewer, doer)
 	} else {
@@ -241,11 +243,14 @@ func TeamReviewRequest(issue *models.Issue, doer *user_model.User, reviewer *mod
 		return
 	}
 
-	if err = reviewer.GetMembers(&models.SearchMembersOptions{}); err != nil {
+	members, err := organization.GetTeamMembers(db.DefaultContext, &organization.SearchMembersOptions{
+		TeamID: reviewer.ID,
+	})
+	if err != nil {
 		return
 	}
 
-	for _, member := range reviewer.Members {
+	for _, member := range members {
 		if member.ID == comment.Issue.PosterID {
 			continue
 		}
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index 0abc666f1a..7f856f2d40 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -8,7 +8,8 @@ import (
 	"bytes"
 	"fmt"
 
-	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/setting"
@@ -24,7 +25,7 @@ func SendRepoTransferNotifyMail(doer, newOwner *user_model.User, repo *repo_mode
 	}
 
 	if newOwner.IsOrganization() {
-		users, err := models.GetUsersWhoCanCreateOrgRepo(newOwner.ID)
+		users, err := organization.GetUsersWhoCanCreateOrgRepo(db.DefaultContext, newOwner.ID)
 		if err != nil {
 			return err
 		}
diff --git a/services/org/org.go b/services/org/org.go
index 4c0cfe87fc..da7a71fec5 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"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/storage"
@@ -16,7 +17,7 @@ import (
 )
 
 // DeleteOrganization completely and permanently deletes everything of organization.
-func DeleteOrganization(org *models.Organization) error {
+func DeleteOrganization(org *organization.Organization) error {
 	ctx, commiter, err := db.TxContext()
 	if err != nil {
 		return err
@@ -31,7 +32,7 @@ func DeleteOrganization(org *models.Organization) error {
 		return models.ErrUserOwnRepos{UID: org.ID}
 	}
 
-	if err := models.DeleteOrganization(ctx, org); err != nil {
+	if err := organization.DeleteOrganization(ctx, org); err != nil {
 		return fmt.Errorf("DeleteOrganization: %v", err)
 	}
 
diff --git a/services/org/org_test.go b/services/org/org_test.go
index aaa2756bb5..b30bfddbb4 100644
--- a/services/org/org_test.go
+++ b/services/org/org_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 
@@ -21,18 +22,18 @@ func TestMain(m *testing.M) {
 
 func TestDeleteOrganization(t *testing.T) {
 	assert.NoError(t, unittest.PrepareTestDatabase())
-	org := unittest.AssertExistsAndLoadBean(t, &models.Organization{ID: 6}).(*models.Organization)
+	org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 6}).(*organization.Organization)
 	assert.NoError(t, DeleteOrganization(org))
-	unittest.AssertNotExistsBean(t, &models.Organization{ID: 6})
-	unittest.AssertNotExistsBean(t, &models.OrgUser{OrgID: 6})
-	unittest.AssertNotExistsBean(t, &models.Team{OrgID: 6})
+	unittest.AssertNotExistsBean(t, &organization.Organization{ID: 6})
+	unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: 6})
+	unittest.AssertNotExistsBean(t, &organization.Team{OrgID: 6})
 
-	org = unittest.AssertExistsAndLoadBean(t, &models.Organization{ID: 3}).(*models.Organization)
+	org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
 	err := DeleteOrganization(org)
 	assert.Error(t, err)
 	assert.True(t, models.IsErrUserOwnRepos(err))
 
-	user := unittest.AssertExistsAndLoadBean(t, &models.Organization{ID: 5}).(*models.Organization)
+	user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5}).(*organization.Organization)
 	assert.Error(t, DeleteOrganization(user))
-	unittest.CheckConsistencyFor(t, &user_model.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
 }
diff --git a/services/repository/repository.go b/services/repository/repository.go
index c3ca867187..5886b96ab8 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -9,6 +9,7 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/log"
@@ -50,7 +51,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod
 func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_model.Repository, error) {
 	if !authUser.IsAdmin {
 		if owner.IsOrganization() {
-			if ok, err := models.CanCreateOrgRepo(owner.ID, authUser.ID); err != nil {
+			if ok, err := organization.CanCreateOrgRepo(owner.ID, authUser.ID); err != nil {
 				return nil, err
 			} else if !ok {
 				return nil, fmt.Errorf("cannot push-create repository for org")
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index 1e8b78dfc6..0abb03a88d 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -9,6 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	"code.gitea.io/gitea/models/perm"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
@@ -21,7 +22,7 @@ import (
 var repoWorkingPool = sync.NewExclusivePool()
 
 // TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*models.Team) error {
+func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
 	if err := repo.GetOwner(db.DefaultContext); err != nil {
 		return err
 	}
@@ -46,7 +47,7 @@ func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Reposit
 	}
 
 	for _, team := range teams {
-		if err := team.AddRepository(newRepo); err != nil {
+		if err := models.AddRepository(team, newRepo); err != nil {
 			return err
 		}
 	}
@@ -81,7 +82,7 @@ func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, ne
 
 // 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 *user_model.User, repo *repo_model.Repository, teams []*models.Team) error {
+func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error {
 	if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil {
 		return err
 	}
@@ -93,7 +94,7 @@ func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *repo_model.R
 
 	// If new owner is an org and user can create repos he can transfer directly too
 	if newOwner.IsOrganization() {
-		allowed, err := models.CanCreateOrgRepo(newOwner.ID, doer.ID)
+		allowed, err := organization.CanCreateOrgRepo(newOwner.ID, doer.ID)
 		if err != nil {
 			return err
 		}
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index d4fbe95834..1081c76c7e 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -9,6 +9,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -53,7 +54,7 @@ func TestTransferOwnership(t *testing.T) {
 		Content:   "user3/repo3",
 	})
 
-	unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &organization.Team{})
 }
 
 func TestStartRepositoryTransferSetPermission(t *testing.T) {
@@ -74,5 +75,5 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) {
 	assert.NoError(t, err)
 	assert.True(t, hasAccess)
 
-	unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &models.Team{})
+	unittest.CheckConsistencyFor(t, &repo_model.Repository{}, &user_model.User{}, &organization.Team{})
 }
diff --git a/services/user/user.go b/services/user/user.go
index e31b174c0c..f88c0df93d 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -16,6 +16,7 @@ import (
 	admin_model "code.gitea.io/gitea/models/admin"
 	asymkey_model "code.gitea.io/gitea/models/asymkey"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/avatar"
@@ -50,7 +51,7 @@ func DeleteUser(u *user_model.User) error {
 	}
 
 	// Check membership of organization.
-	count, err = models.GetOrganizationCount(ctx, u)
+	count, err = organization.GetOrganizationCount(ctx, u)
 	if err != nil {
 		return fmt.Errorf("GetOrganizationCount: %v", err)
 	} else if count > 0 {
diff --git a/services/user/user_test.go b/services/user/user_test.go
index 5b35db790c..7835530394 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/models/db"
+	"code.gitea.io/gitea/models/organization"
 	repo_model "code.gitea.io/gitea/models/repo"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
@@ -36,11 +37,11 @@ func TestDeleteUser(t *testing.T) {
 			return
 		}
 
-		orgUsers := make([]*models.OrgUser, 0, 10)
-		assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&orgUsers, &models.OrgUser{UID: userID}))
+		orgUsers := make([]*organization.OrgUser, 0, 10)
+		assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&orgUsers, &organization.OrgUser{UID: userID}))
 		for _, orgUser := range orgUsers {
 			if err := models.RemoveOrgUser(orgUser.OrgID, orgUser.UID); err != nil {
-				assert.True(t, models.IsErrLastOrgOwner(err))
+				assert.True(t, organization.IsErrLastOrgOwner(err))
 				return
 			}
 		}