diff --git a/docs/content/doc/usage/permissions.en-us.md b/docs/content/doc/usage/permissions.en-us.md
new file mode 100644
index 000000000..1eea78b55
--- /dev/null
+++ b/docs/content/doc/usage/permissions.en-us.md
@@ -0,0 +1,73 @@
+---
+date: "2021-12-13:10:10+08:00"
+title: "Permissions"
+slug: "permissions"
+weight: 14
+toc: false
+draft: false
+menu:
+  sidebar:
+    parent: "usage"
+    name: "Permissions"
+    weight: 14
+    identifier: "permissions"
+---
+
+# Permissions
+
+**Table of Contents**
+
+{{< toc >}}
+
+Gitea supports permissions for repository so that you can give different access for different people. At first, we need to know about `Unit`.
+
+## Unit
+
+In Gitea, we call a sub module of a repository `Unit`. Now we have following units.
+
+| Name            | Description                                          | Permissions |
+| --------------- | ---------------------------------------------------- | ----------- |
+| Code            | Access source code, files, commits and branches.     | Read Write  |
+| Issues          | Organize bug reports, tasks and milestones.          | Read Write  |
+| PullRequests    | Enable pull requests and code reviews.               | Read Write  |
+| Releases        | Track project versions and downloads.                | Read Write  |
+| Wiki            | Write and share documentation with collaborators.    | Read Write  |
+| ExternalWiki    | Link to an external wiki                             | Read        |
+| ExternalTracker | Link to an external issue tracker                    | Read        |
+| Projects        | The URL to the template repository                   | Read Write  |
+| Settings        | Manage the repository                                | Admin       |
+
+With different permissions, people could do different things with these units.
+
+| Name            | Read                                               | Write                        | Admin                     |
+| --------------- | -------------------------------------------------  | ---------------------------- | ------------------------- |
+| Code            | View code trees, files, commits, branches and etc. | Push codes.                  | -                         |
+| Issues          | View issues and create new issues.                 | Add labels, assign, close    | -                         |
+| PullRequests    | View pull requests and create new pull requests.   | Add labels, assign, close    | -                         |
+| Releases        | View releases and download files.                  | Create/Edit releases         | -                         |
+| Wiki            | View wiki pages. Clone the wiki repository.        | Create/Edit wiki pages, push | -                         |
+| ExternalWiki    | Link to an external wiki                           | -                            | -                         |
+| ExternalTracker | Link to an external issue tracker                  | -                            | -                         |
+| Projects        | View the boards                                    | Change issues across boards  | -                         |
+| Settings        | -                                                  | -                            | Manage the repository     |
+
+And there are some differences for permissions between individual repositories and organization repositories.
+
+## Individual Repository
+
+For individual repositories, the creators are the only owners of repositories and have no limit to change anything of this 
+repository or delete it. Repositories owners could add collaborators to help maintain the repositories. Collaborators could have `Read`, `Write` and `Admin` permissions.
+
+## Organization Repository
+
+Different from individual repositories, the owner of organization repositories are the owner team of this organization.
+
+### Team
+
+A team in an organization has unit permissions settings. It can have members and repositories scope. A team could access all the repositories in this organization or special repositories changed by the owner team. A team could also be allowed to create new
+repositories.
+
+The owner team will be created when the organization created and the creator will become the first member of the owner team.
+Notice Gitea will not allow a people is a member of organization but not in any team. The owner team could not be deleted and only
+members of owner team could create a new team. Admin team could be created to manage some of repositories, members of admin team
+could do anything with these repositories. Generate team could be created by the owner team to do the permissions allowed operations.
diff --git a/integrations/api_repo_teams_test.go b/integrations/api_repo_teams_test.go
index 07a8b9418..a3baeba63 100644
--- a/integrations/api_repo_teams_test.go
+++ b/integrations/api_repo_teams_test.go
@@ -10,9 +10,11 @@ import (
 	"testing"
 
 	repo_model "code.gitea.io/gitea/models/repo"
+	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	api "code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/util"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -36,7 +38,7 @@ func TestAPIRepoTeams(t *testing.T) {
 	if assert.Len(t, teams, 2) {
 		assert.EqualValues(t, "Owners", teams[0].Name)
 		assert.False(t, teams[0].CanCreateOrgRepo)
-		assert.EqualValues(t, []string{"repo.code", "repo.issues", "repo.pulls", "repo.releases", "repo.wiki", "repo.ext_wiki", "repo.ext_issues"}, teams[0].Units)
+		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)
 
 		assert.EqualValues(t, "test_team", teams[1].Name)
diff --git a/integrations/api_team_test.go b/integrations/api_team_test.go
index da22d4047..a622c6314 100644
--- a/integrations/api_team_test.go
+++ b/integrations/api_team_test.go
@@ -11,6 +11,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/models/unittest"
 	user_model "code.gitea.io/gitea/models/user"
 	"code.gitea.io/gitea/modules/convert"
@@ -65,11 +66,12 @@ func TestAPITeam(t *testing.T) {
 	}
 	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
 	resp = session.MakeRequest(t, req, http.StatusCreated)
+	apiTeam = api.Team{}
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
-		teamToCreate.Permission, teamToCreate.Units)
+		teamToCreate.Permission, teamToCreate.Units, nil)
 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
-		teamToCreate.Permission, teamToCreate.Units)
+		teamToCreate.Permission, teamToCreate.Units, nil)
 	teamID := apiTeam.ID
 
 	// Edit team.
@@ -85,30 +87,100 @@ func TestAPITeam(t *testing.T) {
 
 	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit)
 	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
-		teamToEdit.Permission, teamToEdit.Units)
+		teamToEdit.Permission, unit.AllUnitKeyNames(), nil)
 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
-		teamToEdit.Permission, teamToEdit.Units)
+		teamToEdit.Permission, unit.AllUnitKeyNames(), nil)
 
 	// Edit team Description only
 	editDescription = "first team"
 	teamToEditDesc := api.EditTeamOption{Description: &editDescription}
 	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEditDesc)
 	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
-		teamToEdit.Permission, teamToEdit.Units)
+		teamToEdit.Permission, unit.AllUnitKeyNames(), nil)
 	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
-		teamToEdit.Permission, teamToEdit.Units)
+		teamToEdit.Permission, unit.AllUnitKeyNames(), nil)
 
 	// Read team.
 	teamRead := unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
+	assert.NoError(t, teamRead.GetUnits())
 	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
 	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories,
-		teamRead.Authorize.String(), teamRead.GetUnitNames())
+		teamRead.AccessMode.String(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
+
+	// 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})
+
+	// create team again via UnitsMap
+	// Create team.
+	teamToCreate = &api.CreateTeamOption{
+		Name:                    "team2",
+		Description:             "team two",
+		IncludesAllRepositories: true,
+		Permission:              "write",
+		UnitsMap:                map[string]string{"repo.code": "read", "repo.issues": "write", "repo.wiki": "none"},
+	}
+	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
+	resp = session.MakeRequest(t, req, http.StatusCreated)
+	apiTeam = api.Team{}
+	DecodeJSON(t, resp, &apiTeam)
+	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
+		"read", nil, teamToCreate.UnitsMap)
+	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
+		"read", nil, teamToCreate.UnitsMap)
+	teamID = apiTeam.ID
+
+	// Edit team.
+	editDescription = "team 1"
+	editFalse = false
+	teamToEdit = &api.EditTeamOption{
+		Name:                    "teamtwo",
+		Description:             &editDescription,
+		Permission:              "write",
+		IncludesAllRepositories: &editFalse,
+		UnitsMap:                map[string]string{"repo.code": "read", "repo.pulls": "read", "repo.releases": "write"},
+	}
+
+	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
+	DecodeJSON(t, resp, &apiTeam)
+	checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
+		"read", nil, teamToEdit.UnitsMap)
+	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEdit.Description, *teamToEdit.IncludesAllRepositories,
+		"read", nil, teamToEdit.UnitsMap)
+
+	// Edit team Description only
+	editDescription = "second team"
+	teamToEditDesc = api.EditTeamOption{Description: &editDescription}
+	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEditDesc)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
+	DecodeJSON(t, resp, &apiTeam)
+	checkTeamResponse(t, &apiTeam, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
+		"read", nil, teamToEdit.UnitsMap)
+	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, *teamToEditDesc.Description, *teamToEdit.IncludesAllRepositories,
+		"read", nil, teamToEdit.UnitsMap)
+
+	// Read team.
+	teamRead = unittest.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
+	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
+	resp = session.MakeRequest(t, req, http.StatusOK)
+	apiTeam = api.Team{}
+	DecodeJSON(t, resp, &apiTeam)
+	assert.NoError(t, teamRead.GetUnits())
+	checkTeamResponse(t, &apiTeam, teamRead.Name, *teamToEditDesc.Description, teamRead.IncludesAllRepositories,
+		teamRead.AccessMode.String(), teamRead.GetUnitNames(), teamRead.GetUnitsMap())
 
 	// Delete team.
 	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
@@ -116,20 +188,27 @@ func TestAPITeam(t *testing.T) {
 	unittest.AssertNotExistsBean(t, &models.Team{ID: teamID})
 }
 
-func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string) {
-	assert.Equal(t, name, apiTeam.Name, "name")
-	assert.Equal(t, description, apiTeam.Description, "description")
-	assert.Equal(t, includesAllRepositories, apiTeam.IncludesAllRepositories, "includesAllRepositories")
-	assert.Equal(t, permission, apiTeam.Permission, "permission")
-	sort.StringSlice(units).Sort()
-	sort.StringSlice(apiTeam.Units).Sort()
-	assert.EqualValues(t, units, apiTeam.Units, "units")
+func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string, unitsMap map[string]string) {
+	t.Run(name+description, func(t *testing.T) {
+		assert.Equal(t, name, apiTeam.Name, "name")
+		assert.Equal(t, description, apiTeam.Description, "description")
+		assert.Equal(t, includesAllRepositories, apiTeam.IncludesAllRepositories, "includesAllRepositories")
+		assert.Equal(t, permission, apiTeam.Permission, "permission")
+		if units != nil {
+			sort.StringSlice(units).Sort()
+			sort.StringSlice(apiTeam.Units).Sort()
+			assert.EqualValues(t, units, apiTeam.Units, "units")
+		}
+		if unitsMap != nil {
+			assert.EqualValues(t, unitsMap, apiTeam.UnitsMap, "unitsMap")
+		}
+	})
 }
 
-func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []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)
 	assert.NoError(t, team.GetUnits(), "GetUnits")
-	checkTeamResponse(t, convert.ToTeam(team), name, description, includesAllRepositories, permission, units)
+	checkTeamResponse(t, convert.ToTeam(team), name, description, includesAllRepositories, permission, units, unitsMap)
 }
 
 type TeamSearchResults struct {
@@ -162,5 +241,4 @@ func TestAPITeamSearch(t *testing.T) {
 	req = NewRequestf(t, "GET", "/api/v1/orgs/%s/teams/search?q=%s", org.Name, "team")
 	req.Header.Add("X-Csrf-Token", csrf)
 	session.MakeRequest(t, req, http.StatusForbidden)
-
 }
diff --git a/integrations/org_test.go b/integrations/org_test.go
index e94e4ea74..794475a92 100644
--- a/integrations/org_test.go
+++ b/integrations/org_test.go
@@ -156,10 +156,10 @@ func TestOrgRestrictedUser(t *testing.T) {
 	resp := adminSession.MakeRequest(t, req, http.StatusCreated)
 	DecodeJSON(t, resp, &apiTeam)
 	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
-		teamToCreate.Permission, teamToCreate.Units)
+		teamToCreate.Permission, teamToCreate.Units, nil)
 	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
-		teamToCreate.Permission, teamToCreate.Units)
-	//teamID := apiTeam.ID
+		teamToCreate.Permission, teamToCreate.Units, nil)
+	// teamID := apiTeam.ID
 
 	// Now we need to add the restricted user to the team
 	req = NewRequest(t, "PUT",
@@ -172,5 +172,4 @@ func TestOrgRestrictedUser(t *testing.T) {
 
 	req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
 	restrictedSession.MakeRequest(t, req, http.StatusOK)
-
 }
diff --git a/models/access.go b/models/access.go
index 6a97bcffc..48b65c2c0 100644
--- a/models/access.go
+++ b/models/access.go
@@ -162,7 +162,7 @@ func recalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, i
 		// Owner team gets owner access, and skip for teams that do not
 		// have relations with repository.
 		if t.IsOwnerTeam() {
-			t.Authorize = perm.AccessModeOwner
+			t.AccessMode = perm.AccessModeOwner
 		} else if !t.hasRepository(e, repo.ID) {
 			continue
 		}
@@ -171,7 +171,7 @@ func recalculateTeamAccesses(ctx context.Context, repo *repo_model.Repository, i
 			return fmt.Errorf("getMembers '%d': %v", t.ID, err)
 		}
 		for _, m := range t.Members {
-			updateUserAccess(accessMap, m, t.Authorize)
+			updateUserAccess(accessMap, m, t.AccessMode)
 		}
 	}
 
@@ -210,10 +210,10 @@ func recalculateUserAccess(ctx context.Context, repo *repo_model.Repository, uid
 
 		for _, t := range teams {
 			if t.IsOwnerTeam() {
-				t.Authorize = perm.AccessModeOwner
+				t.AccessMode = perm.AccessModeOwner
 			}
 
-			accessMode = maxAccessMode(accessMode, t.Authorize)
+			accessMode = maxAccessMode(accessMode, t.AccessMode)
 		}
 	}
 
diff --git a/models/fixtures/team_unit.yml b/models/fixtures/team_unit.yml
index 943745c00..66f0d22ef 100644
--- a/models/fixtures/team_unit.yml
+++ b/models/fixtures/team_unit.yml
@@ -2,223 +2,268 @@
   id: 1
   team_id: 1
   type: 1
+  access_mode: 4
 
 -
   id: 2
   team_id: 1
   type: 2
+  access_mode: 4
 
 -
   id: 3
   team_id: 1
   type: 3
+  access_mode: 4
 
 -
   id: 4
   team_id: 1
   type: 4
+  access_mode: 4
 
 -
   id: 5
   team_id: 1
   type: 5
+  access_mode: 4
 
 -
   id: 6
   team_id: 1
   type: 6
+  access_mode: 4
 
 -
   id: 7
   team_id: 1
   type: 7
+  access_mode: 4
 
 -
   id: 8
   team_id: 2
   type: 1
+  access_mode: 2
 
 -
   id: 9
   team_id: 2
   type: 2
+  access_mode: 2
 
 -
   id: 10
   team_id: 2
   type: 3
+  access_mode: 2
 
 -
   id: 11
   team_id: 2
   type: 4
+  access_mode: 2
 
 -
   id: 12
   team_id: 2
   type: 5
+  access_mode: 2
 
 -
   id: 13
   team_id: 2
   type: 6
+  access_mode: 2
 
 -
   id: 14
   team_id: 2
   type: 7
+  access_mode: 2
 
 -
   id: 15
   team_id: 3
   type: 1
+  access_mode: 4
 
 -
   id: 16
   team_id: 3
   type: 2
+  access_mode: 4
 
 -
   id: 17
   team_id: 3
   type: 3
+  access_mode: 4
 
 -
   id: 18
   team_id: 3
   type: 4
+  access_mode: 4
 
 -
   id: 19
   team_id: 3
   type: 5
+  access_mode: 4
 
 -
   id: 20
   team_id: 3
   type: 6
+  access_mode: 4
 
 -
   id: 21
   team_id: 3
   type: 7
+  access_mode: 4
 
 -
   id: 22
   team_id: 4
   type: 1
+  access_mode: 4
 
 -
   id: 23
   team_id: 4
   type: 2
+  access_mode: 4
 
 -
   id: 24
   team_id: 4
   type: 3
+  access_mode: 4
 
 -
   id: 25
   team_id: 4
   type: 4
+  access_mode: 4
 
 -
   id: 26
   team_id: 4
   type: 5
+  access_mode: 4
 
 -
   id: 27
   team_id: 4
   type: 6
+  access_mode: 4
 
 -
   id: 28
   team_id: 4
   type: 7
+  access_mode: 4
 
 -
   id: 29
   team_id: 5
   type: 1
+  access_mode: 4
 
 -
   id: 30
   team_id: 5
   type: 2
+  access_mode: 4
 
 -
   id: 31
   team_id: 5
   type: 3
+  access_mode: 4
 
 -
   id: 32
   team_id: 5
   type: 4
+  access_mode: 4
 
 -
   id: 33
   team_id: 5
   type: 5
+  access_mode: 4
 
 -
   id: 34
   team_id: 5
   type: 6
+  access_mode: 4
 
 -
   id: 35
   team_id: 5
   type: 7
+  access_mode: 4
 
 -
   id: 36
   team_id: 6
   type: 1
+  access_mode: 4
 
 -
   id: 37
   team_id: 6
   type: 2
+  access_mode: 4
 
 -
   id: 38
   team_id: 6
   type: 3
+  access_mode: 4
 
 -
   id: 39
   team_id: 6
   type: 4
+  access_mode: 4
 
 -
   id: 40
   team_id: 6
   type: 5
+  access_mode: 4
 
 -
   id: 41
   team_id: 6
   type: 6
+  access_mode: 4
 
 -
   id: 42
   team_id: 6
   type: 7
+  access_mode: 4
 
 -
   id: 43
   team_id: 7
   type: 2 # issues
+  access_mode: 2
 
 -
   id: 44
   team_id: 8
   type: 2 # issues
+  access_mode: 2
 
 -
   id: 45
   team_id: 9
-  type: 1 # code
\ No newline at end of file
+  type: 1 # code
+  access_mode: 1
diff --git a/models/issue.go b/models/issue.go
index f0040fbbc..108d9b217 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -1350,8 +1350,8 @@ 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 {
-	var cond = builder.NewCond()
-	var unitType = unit.TypeIssues
+	cond := builder.NewCond()
+	unitType := unit.TypeIssues
 	if isPull {
 		unitType = unit.TypePullRequests
 	}
@@ -2147,7 +2147,7 @@ func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *user_
 				unittype = unit.TypePullRequests
 			}
 			for _, team := range teams {
-				if team.Authorize >= perm.AccessModeOwner {
+				if team.AccessMode >= perm.AccessModeAdmin {
 					checked = append(checked, team.ID)
 					resolved[issue.Repo.Owner.LowerName+"/"+team.LowerName] = true
 					continue
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 4b720c3f0..9423e5c5f 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -60,7 +60,6 @@ type Version struct {
 // If you want to "retire" a migration, remove it from the top of the list and
 // update minDBVersion accordingly
 var migrations = []Migration{
-
 	// Gitea 1.5.0 ends at v69
 
 	// v70 -> v71
@@ -365,6 +364,8 @@ var migrations = []Migration{
 	NewMigration("Add key is verified to ssh key", addSSHKeyIsVerified),
 	// v205 -> v206
 	NewMigration("Migrate to higher varchar on user struct", migrateUserPasswordSalt),
+	// v206 -> v207
+	NewMigration("Add authorize column to team_unit table", addAuthorizeColForTeamUnit),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v206.go b/models/migrations/v206.go
new file mode 100644
index 000000000..c6a5dc811
--- /dev/null
+++ b/models/migrations/v206.go
@@ -0,0 +1,29 @@
+// 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 migrations
+
+import (
+	"fmt"
+
+	"xorm.io/xorm"
+)
+
+func addAuthorizeColForTeamUnit(x *xorm.Engine) error {
+	type TeamUnit struct {
+		ID         int64 `xorm:"pk autoincr"`
+		OrgID      int64 `xorm:"INDEX"`
+		TeamID     int64 `xorm:"UNIQUE(s)"`
+		Type       int   `xorm:"UNIQUE(s)"`
+		AccessMode int
+	}
+
+	if err := x.Sync2(new(TeamUnit)); err != nil {
+		return fmt.Errorf("sync2: %v", err)
+	}
+
+	// migrate old permission
+	_, err := x.Exec("UPDATE team_unit SET access_mode = (SELECT authorize FROM team WHERE team.id = team_unit.team_id)")
+	return err
+}
diff --git a/models/org.go b/models/org.go
index c135bb9d3..0ea2ce688 100644
--- a/models/org.go
+++ b/models/org.go
@@ -265,7 +265,7 @@ func CreateOrganization(org *Organization, owner *user_model.User) (err error) {
 		OrgID:                   org.ID,
 		LowerName:               strings.ToLower(ownerTeamName),
 		Name:                    ownerTeamName,
-		Authorize:               perm.AccessModeOwner,
+		AccessMode:              perm.AccessModeOwner,
 		NumMembers:              1,
 		IncludesAllRepositories: true,
 		CanCreateOrgRepo:        true,
@@ -523,7 +523,7 @@ type FindOrgOptions struct {
 }
 
 func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
-	var cond = builder.Eq{"uid": userID}
+	cond := builder.Eq{"uid": userID}
 	if !includePrivate {
 		cond["is_public"] = true
 	}
@@ -531,7 +531,7 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
 }
 
 func (opts FindOrgOptions) toConds() builder.Cond {
-	var cond = builder.NewCond()
+	cond := builder.NewCond()
 	if opts.UserID > 0 {
 		cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
 	}
diff --git a/models/org_team.go b/models/org_team.go
index 7eac0f7bc..bce4afb06 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -32,7 +32,7 @@ type Team struct {
 	LowerName               string
 	Name                    string
 	Description             string
-	Authorize               perm.AccessMode
+	AccessMode              perm.AccessMode          `xorm:"'authorize'"`
 	Repos                   []*repo_model.Repository `xorm:"-"`
 	Members                 []*user_model.User       `xorm:"-"`
 	NumRepos                int
@@ -126,7 +126,7 @@ func (t *Team) ColorFormat(s fmt.State) {
 		log.NewColoredIDValue(t.ID),
 		t.Name,
 		log.NewColoredIDValue(t.OrgID),
-		t.Authorize)
+		t.AccessMode)
 }
 
 // GetUnits return a list of available units for a team
@@ -145,15 +145,29 @@ func (t *Team) getUnits(e db.Engine) (err error) {
 
 // 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
 }
 
-// HasWriteAccess returns true if team has at least write level access mode.
-func (t *Team) HasWriteAccess() bool {
-	return t.Authorize >= perm.AccessModeWrite
+// 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.
@@ -455,16 +469,25 @@ func (t *Team) UnitEnabled(tp unit.Type) bool {
 }
 
 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 true
+			return unit.AccessMode
 		}
 	}
-	return false
+	return perm.AccessModeNone
 }
 
 // IsUsableTeamName tests if a name could be as team name
@@ -661,7 +684,7 @@ func UpdateTeam(t *Team, authChanged, includeAllChanged bool) (err error) {
 			Delete(new(TeamUnit)); err != nil {
 			return err
 		}
-		if _, err = sess.Cols("org_id", "team_id", "type").Insert(&t.Units); err != nil {
+		if _, err = sess.Cols("org_id", "team_id", "type", "access_mode").Insert(&t.Units); err != nil {
 			return err
 		}
 	}
@@ -1033,10 +1056,11 @@ func GetTeamsWithAccessToRepo(orgID, repoID int64, mode perm.AccessMode) ([]*Tea
 
 // 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)"`
+	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
diff --git a/models/org_team_test.go b/models/org_team_test.go
index 59b7b6d5a..aa62cc58e 100644
--- a/models/org_team_test.go
+++ b/models/org_team_test.go
@@ -211,7 +211,7 @@ func TestUpdateTeam(t *testing.T) {
 	team.LowerName = "newname"
 	team.Name = "newName"
 	team.Description = strings.Repeat("A long description!", 100)
-	team.Authorize = perm.AccessModeAdmin
+	team.AccessMode = perm.AccessModeAdmin
 	assert.NoError(t, UpdateTeam(team, true, false))
 
 	team = unittest.AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team)
diff --git a/models/perm/access_mode.go b/models/perm/access_mode.go
index f2c0a322a..dfa7f7b75 100644
--- a/models/perm/access_mode.go
+++ b/models/perm/access_mode.go
@@ -51,11 +51,13 @@ func (mode AccessMode) ColorFormat(s fmt.State) {
 // ParseAccessMode returns corresponding access mode to given permission string.
 func ParseAccessMode(permission string) AccessMode {
 	switch permission {
+	case "read":
+		return AccessModeRead
 	case "write":
 		return AccessModeWrite
 	case "admin":
 		return AccessModeAdmin
 	default:
-		return AccessModeRead
+		return AccessModeNone
 	}
 }
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 40b63aa80..4e5cbfd55 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -239,7 +239,7 @@ func getUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 
 	// if user in an owner team
 	for _, team := range teams {
-		if team.Authorize >= perm_model.AccessModeOwner {
+		if team.AccessMode >= perm_model.AccessModeAdmin {
 			perm.AccessMode = perm_model.AccessModeOwner
 			perm.UnitsMode = nil
 			return
@@ -249,10 +249,11 @@ func getUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
 	for _, u := range repo.Units {
 		var found bool
 		for _, team := range teams {
-			if team.unitEnabled(e, u.Type) {
+			teamMode := team.unitAccessMode(e, u.Type)
+			if teamMode > perm_model.AccessModeNone {
 				m := perm.UnitsMode[u.Type]
-				if m < team.Authorize {
-					perm.UnitsMode[u.Type] = team.Authorize
+				if m < teamMode {
+					perm.UnitsMode[u.Type] = teamMode
 				}
 				found = true
 			}
@@ -324,7 +325,7 @@ func isUserRepoAdmin(e db.Engine, repo *repo_model.Repository, user *user_model.
 	}
 
 	for _, team := range teams {
-		if team.Authorize >= perm_model.AccessModeAdmin {
+		if team.AccessMode >= perm_model.AccessModeAdmin {
 			return true, nil
 		}
 	}
diff --git a/models/review.go b/models/review.go
index eeb33611c..023f98c3e 100644
--- a/models/review.go
+++ b/models/review.go
@@ -280,7 +280,7 @@ func isOfficialReviewerTeam(ctx context.Context, issue *Issue, team *Team) (bool
 	}
 
 	if !pr.ProtectedBranch.EnableApprovalsWhitelist {
-		return team.Authorize >= perm.AccessModeWrite, nil
+		return team.UnitAccessMode(unit.TypeCode) >= perm.AccessModeWrite, nil
 	}
 
 	return base.Int64sContains(pr.ProtectedBranch.ApprovalsWhitelistTeamIDs, team.ID), nil
diff --git a/models/unit/unit.go b/models/unit/unit.go
index 0af4640b7..b05f34b64 100644
--- a/models/unit/unit.go
+++ b/models/unit/unit.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"strings"
 
+	"code.gitea.io/gitea/models/perm"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 )
@@ -17,14 +18,15 @@ type Type int
 
 // Enumerate all the unit types
 const (
-	TypeCode            Type = iota + 1 // 1 code
-	TypeIssues                          // 2 issues
-	TypePullRequests                    // 3 PRs
-	TypeReleases                        // 4 Releases
-	TypeWiki                            // 5 Wiki
-	TypeExternalWiki                    // 6 ExternalWiki
-	TypeExternalTracker                 // 7 ExternalTracker
-	TypeProjects                        // 8 Kanban board
+	TypeInvalid         Type = iota // 0 invalid
+	TypeCode                        // 1 code
+	TypeIssues                      // 2 issues
+	TypePullRequests                // 3 PRs
+	TypeReleases                    // 4 Releases
+	TypeWiki                        // 5 Wiki
+	TypeExternalWiki                // 6 ExternalWiki
+	TypeExternalTracker             // 7 ExternalTracker
+	TypeProjects                    // 8 Kanban board
 )
 
 // Value returns integer value for unit type
@@ -170,11 +172,12 @@ func (u *Type) CanBeDefault() bool {
 
 // Unit is a section of one repository
 type Unit struct {
-	Type    Type
-	NameKey string
-	URI     string
-	DescKey string
-	Idx     int
+	Type          Type
+	NameKey       string
+	URI           string
+	DescKey       string
+	Idx           int
+	MaxAccessMode perm.AccessMode // The max access mode of the unit. i.e. Read means this unit can only be read.
 }
 
 // CanDisable returns if this unit could be disabled.
@@ -198,6 +201,7 @@ var (
 		"/",
 		"repo.code.desc",
 		0,
+		perm.AccessModeOwner,
 	}
 
 	UnitIssues = Unit{
@@ -206,6 +210,7 @@ var (
 		"/issues",
 		"repo.issues.desc",
 		1,
+		perm.AccessModeOwner,
 	}
 
 	UnitExternalTracker = Unit{
@@ -214,6 +219,7 @@ var (
 		"/issues",
 		"repo.ext_issues.desc",
 		1,
+		perm.AccessModeRead,
 	}
 
 	UnitPullRequests = Unit{
@@ -222,6 +228,7 @@ var (
 		"/pulls",
 		"repo.pulls.desc",
 		2,
+		perm.AccessModeOwner,
 	}
 
 	UnitReleases = Unit{
@@ -230,6 +237,7 @@ var (
 		"/releases",
 		"repo.releases.desc",
 		3,
+		perm.AccessModeOwner,
 	}
 
 	UnitWiki = Unit{
@@ -238,6 +246,7 @@ var (
 		"/wiki",
 		"repo.wiki.desc",
 		4,
+		perm.AccessModeOwner,
 	}
 
 	UnitExternalWiki = Unit{
@@ -246,6 +255,7 @@ var (
 		"/wiki",
 		"repo.ext_wiki.desc",
 		4,
+		perm.AccessModeRead,
 	}
 
 	UnitProjects = Unit{
@@ -254,6 +264,7 @@ var (
 		"/projects",
 		"repo.projects.desc",
 		5,
+		perm.AccessModeOwner,
 	}
 
 	// Units contains all the units
@@ -269,15 +280,51 @@ var (
 	}
 )
 
-// FindUnitTypes give the unit key name and return unit
+// FindUnitTypes give the unit key names and return unit
 func FindUnitTypes(nameKeys ...string) (res []Type) {
 	for _, key := range nameKeys {
+		var found bool
 		for t, u := range Units {
 			if strings.EqualFold(key, u.NameKey) {
 				res = append(res, t)
+				found = true
 				break
 			}
 		}
+		if !found {
+			res = append(res, TypeInvalid)
+		}
 	}
 	return
 }
+
+// TypeFromKey give the unit key name and return unit
+func TypeFromKey(nameKey string) Type {
+	for t, u := range Units {
+		if strings.EqualFold(nameKey, u.NameKey) {
+			return t
+		}
+	}
+	return TypeInvalid
+}
+
+// AllUnitKeyNames returns all unit key names
+func AllUnitKeyNames() []string {
+	res := make([]string, 0, len(Units))
+	for _, u := range Units {
+		res = append(res, u.NameKey)
+	}
+	return res
+}
+
+// MinUnitAccessMode returns the minial permission of the permission map
+func MinUnitAccessMode(unitsMap map[Type]perm.AccessMode) perm.AccessMode {
+	res := perm.AccessModeNone
+	for _, mode := range unitsMap {
+		// get the minial permission great than AccessModeNone except all are AccessModeNone
+		if mode > perm.AccessModeNone && (res == perm.AccessModeNone || mode < res) {
+			res = mode
+		}
+	}
+	return res
+}
diff --git a/modules/context/org.go b/modules/context/org.go
index eb81f6644..585a5fd76 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -168,7 +168,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 			return
 		}
 
-		ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.Authorize >= perm.AccessModeAdmin
+		ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin
 		ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
 		if requireTeamAdmin && !ctx.Org.IsTeamAdmin {
 			ctx.NotFound("OrgAssignment", err)
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index f2b62a74b..41a044c6d 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -306,8 +306,9 @@ func ToTeam(team *models.Team) *api.Team {
 		Description:             team.Description,
 		IncludesAllRepositories: team.IncludesAllRepositories,
 		CanCreateOrgRepo:        team.CanCreateOrgRepo,
-		Permission:              team.Authorize.String(),
+		Permission:              team.AccessMode.String(),
 		Units:                   team.GetUnitNames(),
+		UnitsMap:                team.GetUnitsMap(),
 	}
 }
 
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index 18995f4ec..ed890ace4 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -70,25 +70,25 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
 		{
 			OrgID:                   org.ID,
 			Name:                    "team one",
-			Authorize:               perm.AccessModeRead,
+			AccessMode:              perm.AccessModeRead,
 			IncludesAllRepositories: true,
 		},
 		{
 			OrgID:                   org.ID,
 			Name:                    "team 2",
-			Authorize:               perm.AccessModeRead,
+			AccessMode:              perm.AccessModeRead,
 			IncludesAllRepositories: false,
 		},
 		{
 			OrgID:                   org.ID,
 			Name:                    "team three",
-			Authorize:               perm.AccessModeWrite,
+			AccessMode:              perm.AccessModeWrite,
 			IncludesAllRepositories: true,
 		},
 		{
 			OrgID:                   org.ID,
 			Name:                    "team 4",
-			Authorize:               perm.AccessModeWrite,
+			AccessMode:              perm.AccessModeWrite,
 			IncludesAllRepositories: false,
 		},
 	}
diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go
index 3b2c5e783..53e3fcf62 100644
--- a/modules/structs/org_team.go
+++ b/modules/structs/org_team.go
@@ -15,8 +15,11 @@ type Team struct {
 	// enum: none,read,write,admin,owner
 	Permission string `json:"permission"`
 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
-	Units            []string `json:"units"`
-	CanCreateOrgRepo bool     `json:"can_create_org_repo"`
+	// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
+	Units []string `json:"units"`
+	// example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+	UnitsMap         map[string]string `json:"units_map"`
+	CanCreateOrgRepo bool              `json:"can_create_org_repo"`
 }
 
 // CreateTeamOption options for creating a team
@@ -28,8 +31,11 @@ type CreateTeamOption struct {
 	// enum: read,write,admin
 	Permission string `json:"permission"`
 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
-	Units            []string `json:"units"`
-	CanCreateOrgRepo bool     `json:"can_create_org_repo"`
+	// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
+	Units []string `json:"units"`
+	// example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+	UnitsMap         map[string]string `json:"units_map"`
+	CanCreateOrgRepo bool              `json:"can_create_org_repo"`
 }
 
 // EditTeamOption options for editing a team
@@ -41,6 +47,9 @@ type EditTeamOption struct {
 	// enum: read,write,admin
 	Permission string `json:"permission"`
 	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
-	Units            []string `json:"units"`
-	CanCreateOrgRepo *bool    `json:"can_create_org_repo"`
+	// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
+	Units []string `json:"units"`
+	// example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+	UnitsMap         map[string]string `json:"units_map"`
+	CanCreateOrgRepo *bool             `json:"can_create_org_repo"`
 }
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 9164d5ffd..7a3dbd50a 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1099,7 +1099,7 @@ commits.signed_by_untrusted_user_unmatched = Signed by untrusted user who does n
 commits.gpg_key_id = GPG Key ID
 commits.ssh_key_fingerprint = SSH Key Fingerprint
 
-ext_issues = Ext. Issues
+ext_issues = Access to External Issues
 ext_issues.desc = Link to an external issue tracker.
 
 projects = Projects
@@ -1579,7 +1579,7 @@ signing.wont_sign.commitssigned = The merge will not be signed as all the associ
 signing.wont_sign.approved = The merge will not be signed as the PR is not approved
 signing.wont_sign.not_signed_in = You are not signed in
 
-ext_wiki = Ext. Wiki
+ext_wiki = Access to External Wiki
 ext_wiki.desc = Link to an external wiki.
 
 wiki = Wiki
@@ -2261,9 +2261,13 @@ teams.leave = Leave
 teams.leave.detail = Leave %s?
 teams.can_create_org_repo = Create repositories
 teams.can_create_org_repo_helper = Members can create new repositories in organization. Creator will get administrator access to the new repository.
-teams.read_access = Read Access
+teams.none_access = No Access
+teams.none_access_helper = Members cannot view or do any other action on this unit.
+teams.general_access = General Access
+teams.general_access_helper = Members permissions will be decided by below permission table.
+teams.read_access = Read
 teams.read_access_helper = Members can view and clone team repositories.
-teams.write_access = Write Access
+teams.write_access = Write
 teams.write_access_helper = Members can read and push to team repositories.
 teams.admin_access = Administrator Access
 teams.admin_access_helper = Members can pull and push to team repositories and add collaborators to them.
@@ -2892,5 +2896,6 @@ error.probable_bad_signature = "WARNING! Although there is a key with this ID in
 error.probable_bad_default_signature = "WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS."
 
 [units]
+unit = Unit
 error.no_unit_allowed_repo = You are not allowed to access any section of this repository.
 error.unit_not_allowed = You are not allowed to access this repository section.
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index d39125b05..cc7a63af3 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -6,6 +6,7 @@
 package org
 
 import (
+	"errors"
 	"net/http"
 
 	"code.gitea.io/gitea/models"
@@ -50,7 +51,6 @@ func ListTeams(ctx *context.APIContext) {
 		ListOptions: utils.GetListOptions(ctx),
 		OrgID:       ctx.Org.Organization.ID,
 	})
-
 	if err != nil {
 		ctx.Error(http.StatusInternalServerError, "LoadTeams", err)
 		return
@@ -112,6 +112,10 @@ func ListUserTeams(ctx *context.APIContext) {
 			apiOrg = convert.ToOrganization(org)
 			cache[teams[i].OrgID] = apiOrg
 		}
+		if err := teams[i].GetUnits(); err != nil {
+			ctx.Error(http.StatusInternalServerError, "teams[i].GetUnits()", err)
+			return
+		}
 		apiTeams[i] = convert.ToTeam(teams[i])
 		apiTeams[i].Organization = apiOrg
 	}
@@ -138,9 +142,45 @@ func GetTeam(ctx *context.APIContext) {
 	//   "200":
 	//     "$ref": "#/responses/Team"
 
+	if err := ctx.Org.Team.GetUnits(); err != nil {
+		ctx.Error(http.StatusInternalServerError, "team.GetUnits", err)
+		return
+	}
+
 	ctx.JSON(http.StatusOK, convert.ToTeam(ctx.Org.Team))
 }
 
+func attachTeamUnits(team *models.Team, units []string) {
+	unitTypes := unit_model.FindUnitTypes(units...)
+	team.Units = make([]*models.TeamUnit, 0, len(units))
+	for _, tp := range unitTypes {
+		team.Units = append(team.Units, &models.TeamUnit{
+			OrgID:      team.OrgID,
+			Type:       tp,
+			AccessMode: team.AccessMode,
+		})
+	}
+}
+
+func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.AccessMode {
+	res := make(map[unit_model.Type]perm.AccessMode, len(unitsMap))
+	for unitKey, p := range unitsMap {
+		res[unit_model.TypeFromKey(unitKey)] = perm.ParseAccessMode(p)
+	}
+	return res
+}
+
+func attachTeamUnitsMap(team *models.Team, unitsMap map[string]string) {
+	team.Units = make([]*models.TeamUnit, 0, len(unitsMap))
+	for unitKey, p := range unitsMap {
+		team.Units = append(team.Units, &models.TeamUnit{
+			OrgID:      team.OrgID,
+			Type:       unit_model.TypeFromKey(unitKey),
+			AccessMode: perm.ParseAccessMode(p),
+		})
+	}
+}
+
 // CreateTeam api for create a team
 func CreateTeam(ctx *context.APIContext) {
 	// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
@@ -166,26 +206,28 @@ func CreateTeam(ctx *context.APIContext) {
 	//   "422":
 	//     "$ref": "#/responses/validationError"
 	form := web.GetForm(ctx).(*api.CreateTeamOption)
+	p := perm.ParseAccessMode(form.Permission)
+	if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
+		p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
+	}
 	team := &models.Team{
 		OrgID:                   ctx.Org.Organization.ID,
 		Name:                    form.Name,
 		Description:             form.Description,
 		IncludesAllRepositories: form.IncludesAllRepositories,
 		CanCreateOrgRepo:        form.CanCreateOrgRepo,
-		Authorize:               perm.ParseAccessMode(form.Permission),
+		AccessMode:              p,
 	}
 
-	unitTypes := unit_model.FindUnitTypes(form.Units...)
-
-	if team.Authorize < perm.AccessModeOwner {
-		var units = make([]*models.TeamUnit, 0, len(form.Units))
-		for _, tp := range unitTypes {
-			units = append(units, &models.TeamUnit{
-				OrgID: ctx.Org.Organization.ID,
-				Type:  tp,
-			})
+	if team.AccessMode < perm.AccessModeAdmin {
+		if len(form.UnitsMap) > 0 {
+			attachTeamUnitsMap(team, form.UnitsMap)
+		} else if len(form.Units) > 0 {
+			attachTeamUnits(team, form.Units)
+		} else {
+			ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty"))
+			return
 		}
-		team.Units = units
 	}
 
 	if err := models.NewTeam(team); err != nil {
@@ -224,7 +266,6 @@ func EditTeam(ctx *context.APIContext) {
 	//     "$ref": "#/responses/Team"
 
 	form := web.GetForm(ctx).(*api.EditTeamOption)
-
 	team := ctx.Org.Team
 	if err := team.GetUnits(); err != nil {
 		ctx.InternalServerError(err)
@@ -247,11 +288,14 @@ func EditTeam(ctx *context.APIContext) {
 	isIncludeAllChanged := false
 	if !team.IsOwnerTeam() && len(form.Permission) != 0 {
 		// Validate permission level.
-		auth := perm.ParseAccessMode(form.Permission)
+		p := perm.ParseAccessMode(form.Permission)
+		if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 {
+			p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap))
+		}
 
-		if team.Authorize != auth {
+		if team.AccessMode != p {
 			isAuthChanged = true
-			team.Authorize = auth
+			team.AccessMode = p
 		}
 
 		if form.IncludesAllRepositories != nil {
@@ -260,17 +304,11 @@ func EditTeam(ctx *context.APIContext) {
 		}
 	}
 
-	if team.Authorize < perm.AccessModeOwner {
-		if len(form.Units) > 0 {
-			var units = make([]*models.TeamUnit, 0, len(form.Units))
-			unitTypes := unit_model.FindUnitTypes(form.Units...)
-			for _, tp := range unitTypes {
-				units = append(units, &models.TeamUnit{
-					OrgID: ctx.Org.Team.OrgID,
-					Type:  tp,
-				})
-			}
-			team.Units = units
+	if team.AccessMode < perm.AccessModeAdmin {
+		if len(form.UnitsMap) > 0 {
+			attachTeamUnitsMap(team, form.UnitsMap)
+		} else if len(form.Units) > 0 {
+			attachTeamUnits(team, form.Units)
 		}
 	}
 
@@ -706,5 +744,4 @@ func SearchTeam(ctx *context.APIContext) {
 		"ok":   true,
 		"data": apiTeams,
 	})
-
 }
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index 40fba5cd0..732f24b22 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"net/url"
 	"path"
+	"strconv"
 	"strings"
 
 	"code.gitea.io/gitea/models"
@@ -224,35 +225,57 @@ func NewTeam(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplTeamNew)
 }
 
+func getUnitPerms(forms url.Values) map[unit_model.Type]perm.AccessMode {
+	unitPerms := make(map[unit_model.Type]perm.AccessMode)
+	for k, v := range forms {
+		if strings.HasPrefix(k, "unit_") {
+			t, _ := strconv.Atoi(k[5:])
+			if t > 0 {
+				vv, _ := strconv.Atoi(v[0])
+				unitPerms[unit_model.Type(t)] = perm.AccessMode(vv)
+			}
+		}
+	}
+	return unitPerms
+}
+
 // NewTeamPost response for create new team
 func NewTeamPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.CreateTeamForm)
-	ctx.Data["Title"] = ctx.Org.Organization.FullName
-	ctx.Data["PageIsOrgTeams"] = true
-	ctx.Data["PageIsOrgTeamsNew"] = true
-	ctx.Data["Units"] = unit_model.Units
-	var includesAllRepositories = form.RepoAccess == "all"
+	includesAllRepositories := form.RepoAccess == "all"
+	unitPerms := getUnitPerms(ctx.Req.Form)
+	p := perm.ParseAccessMode(form.Permission)
+	if p < perm.AccessModeAdmin {
+		// if p is less than admin accessmode, then it should be general accessmode,
+		// so we should calculate the minial accessmode from units accessmodes.
+		p = unit_model.MinUnitAccessMode(unitPerms)
+	}
 
 	t := &models.Team{
 		OrgID:                   ctx.Org.Organization.ID,
 		Name:                    form.TeamName,
 		Description:             form.Description,
-		Authorize:               perm.ParseAccessMode(form.Permission),
+		AccessMode:              p,
 		IncludesAllRepositories: includesAllRepositories,
 		CanCreateOrgRepo:        form.CanCreateOrgRepo,
 	}
 
-	if t.Authorize < perm.AccessModeOwner {
-		var units = make([]*models.TeamUnit, 0, len(form.Units))
-		for _, tp := range form.Units {
+	if t.AccessMode < perm.AccessModeAdmin {
+		units := make([]*models.TeamUnit, 0, len(unitPerms))
+		for tp, perm := range unitPerms {
 			units = append(units, &models.TeamUnit{
-				OrgID: ctx.Org.Organization.ID,
-				Type:  tp,
+				OrgID:      ctx.Org.Organization.ID,
+				Type:       tp,
+				AccessMode: perm,
 			})
 		}
 		t.Units = units
 	}
 
+	ctx.Data["Title"] = ctx.Org.Organization.FullName
+	ctx.Data["PageIsOrgTeams"] = true
+	ctx.Data["PageIsOrgTeamsNew"] = true
+	ctx.Data["Units"] = unit_model.Units
 	ctx.Data["Team"] = t
 
 	if ctx.HasError() {
@@ -260,7 +283,7 @@ func NewTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if t.Authorize < perm.AccessModeAdmin && len(form.Units) == 0 {
+	if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
 		ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
 		return
 	}
@@ -317,22 +340,29 @@ func EditTeam(ctx *context.Context) {
 func EditTeamPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.CreateTeamForm)
 	t := ctx.Org.Team
+	unitPerms := getUnitPerms(ctx.Req.Form)
+	isAuthChanged := false
+	isIncludeAllChanged := false
+	includesAllRepositories := form.RepoAccess == "all"
+
 	ctx.Data["Title"] = ctx.Org.Organization.FullName
 	ctx.Data["PageIsOrgTeams"] = true
 	ctx.Data["Team"] = t
 	ctx.Data["Units"] = unit_model.Units
 
-	isAuthChanged := false
-	isIncludeAllChanged := false
-	var includesAllRepositories = form.RepoAccess == "all"
 	if !t.IsOwnerTeam() {
 		// Validate permission level.
-		auth := perm.ParseAccessMode(form.Permission)
+		newAccessMode := perm.ParseAccessMode(form.Permission)
+		if newAccessMode < perm.AccessModeAdmin {
+			// if p is less than admin accessmode, then it should be general accessmode,
+			// so we should calculate the minial accessmode from units accessmodes.
+			newAccessMode = unit_model.MinUnitAccessMode(unitPerms)
+		}
 
 		t.Name = form.TeamName
-		if t.Authorize != auth {
+		if t.AccessMode != newAccessMode {
 			isAuthChanged = true
-			t.Authorize = auth
+			t.AccessMode = newAccessMode
 		}
 
 		if t.IncludesAllRepositories != includesAllRepositories {
@@ -341,17 +371,17 @@ func EditTeamPost(ctx *context.Context) {
 		}
 	}
 	t.Description = form.Description
-	if t.Authorize < perm.AccessModeOwner {
-		var units = make([]models.TeamUnit, 0, len(form.Units))
-		for _, tp := range form.Units {
+	if t.AccessMode < perm.AccessModeAdmin {
+		units := make([]models.TeamUnit, 0, len(unitPerms))
+		for tp, perm := range unitPerms {
 			units = append(units, models.TeamUnit{
-				OrgID:  t.OrgID,
-				TeamID: t.ID,
-				Type:   tp,
+				OrgID:      t.OrgID,
+				TeamID:     t.ID,
+				Type:       tp,
+				AccessMode: perm,
 			})
 		}
-		err := models.UpdateTeamUnits(t, units)
-		if err != nil {
+		if err := models.UpdateTeamUnits(t, units); err != nil {
 			ctx.Error(http.StatusInternalServerError, "LoadIssue", err.Error())
 			return
 		}
@@ -363,7 +393,7 @@ func EditTeamPost(ctx *context.Context) {
 		return
 	}
 
-	if t.Authorize < perm.AccessModeAdmin && len(form.Units) == 0 {
+	if t.AccessMode < perm.AccessModeAdmin && len(unitPerms) == 0 {
 		ctx.RenderWithErr(ctx.Tr("form.team_no_units_error"), tplTeamNew, &form)
 		return
 	}
diff --git a/services/forms/org.go b/services/forms/org.go
index 7c8f17f95..dec2dbfa6 100644
--- a/services/forms/org.go
+++ b/services/forms/org.go
@@ -8,7 +8,6 @@ package forms
 import (
 	"net/http"
 
-	"code.gitea.io/gitea/models/unit"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/web/middleware"
@@ -66,7 +65,6 @@ type CreateTeamForm struct {
 	TeamName         string `binding:"Required;AlphaDashDot;MaxSize(30)"`
 	Description      string `binding:"MaxSize(255)"`
 	Permission       string
-	Units            []unit.Type
 	RepoAccess       string
 	CanCreateOrgRepo bool
 }
diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl
index 783e025eb..1cf2dd023 100644
--- a/templates/org/team/new.tmpl
+++ b/templates/org/team/new.tmpl
@@ -56,21 +56,14 @@