From ddf7014b9b16e571e86c87962be9f9cbe140a93b Mon Sep 17 00:00:00 2001
From: Peter Smit <peter@smitmail.eu>
Date: Fri, 13 Feb 2015 13:58:19 +0200
Subject: [PATCH 1/4] Rewrite of access migration

The old migration had a few issues:
 - It left old column names around
 - It did not give the right access levels for owners and admins

Also, this includes a migration that fixes the authorization of owner teams, which was previously ORG_ADMIN (instead of ORG_OWNER)
---
 models/migrations/migrations.go | 144 ++++++++++++++++++--------------
 1 file changed, 81 insertions(+), 63 deletions(-)

diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index e69b0a1f5e..f6ef513207 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -51,7 +51,8 @@ type Version struct {
 // update _MIN_VER_DB accordingly
 var migrations = []Migration{
 	NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1
-	NewMigration("refactor access table to use id's", accessRefactor),         // V1 -> V2
+	NewMigration("make authorize 4 if team is owners", ownerTeamUpdate),       // V1 -> V2
+	NewMigration("refactor access table to use id's", accessRefactor),         // V2 -> V3
 }
 
 // Migrate database to current version
@@ -212,31 +213,91 @@ func accessToCollaboration(x *xorm.Engine) (err error) {
 	return sess.Commit()
 }
 
+func ownerTeamUpdate(x *xorm.Engine) (err error) {
+	if _, err := x.Exec("UPDATE team SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
+		return fmt.Errorf("drop table: %v", err)
+	}
+	return nil
+}
+
 func accessRefactor(x *xorm.Engine) (err error) {
 	type (
 		AccessMode int
 		Access     struct {
-			ID       int64 `xorm:"pk autoincr"`
-			UserName string
-			RepoName string
-			UserID   int64 `xorm:"UNIQUE(s)"`
-			RepoID   int64 `xorm:"UNIQUE(s)"`
-			Mode     AccessMode
+			ID     int64 `xorm:"pk autoincr"`
+			UserID int64 `xorm:"UNIQUE(s)"`
+			RepoID int64 `xorm:"UNIQUE(s)"`
+			Mode   AccessMode
+		}
+		UserRepo struct {
+			UserID int64
+			RepoID int64
 		}
 	)
 
-	var rawSQL string
-	switch {
-	case setting.UseSQLite3, setting.UsePostgreSQL:
-		rawSQL = "DROP INDEX IF EXISTS `UQE_access_S`"
-	case setting.UseMySQL:
-		rawSQL = "DROP INDEX `UQE_access_S` ON `access`"
+	// We consiously don't start a session yet as we make only reads for now, no writes
+
+	accessMap := make(map[UserRepo]AccessMode, 50)
+
+	results, err := x.Query("SELECT r.id as `repo_id`, r.is_private as `is_private`, r.owner_id as `owner_id`, u.type as `owner_type` FROM `repository` r LEFT JOIN user u ON r.owner_id=u.id")
+	if err != nil {
+		return err
 	}
-	if _, err = x.Exec(rawSQL); err != nil &&
-		!strings.Contains(err.Error(), "check that column/key exists") {
-		return fmt.Errorf("drop index: %v", err)
+	for _, repo := range results {
+		repoID := com.StrTo(repo["repo_id"]).MustInt64()
+		isPrivate := com.StrTo(repo["is_private"]).MustInt() > 0
+		ownerID := com.StrTo(repo["owner_id"]).MustInt64()
+		ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0
+
+		results, err := x.Query("SELECT user_id FROM collaboration WHERE repo_id=?", repoID)
+		if err != nil {
+			return fmt.Errorf("select repos: %v", err)
+		}
+		for _, user := range results {
+			userID := com.StrTo(user["user_id"]).MustInt64()
+			accessMap[UserRepo{userID, repoID}] = 2 // WRITE ACCESS
+		}
+
+		if !ownerIsOrganization {
+			continue
+		}
+
+		minAccessLevel := AccessMode(0)
+		if !isPrivate {
+			minAccessLevel = 1
+		}
+
+		repoString := "$" + string(repo["repo_id"]) + "|"
+
+		results, err = x.Query("SELECT id, authorize, repo_ids FROM team WHERE org_id=? AND authorize > ? ORDER BY authorize ASC", ownerID, int(minAccessLevel))
+		if err != nil {
+			return fmt.Errorf("select teams from org: %v", err)
+		}
+
+		for _, team := range results {
+			if !strings.Contains(string(team["repo_ids"]), repoString) {
+				continue
+			}
+			teamID := com.StrTo(team["id"]).MustInt64()
+			mode := AccessMode(com.StrTo(team["authorize"]).MustInt())
+
+			results, err := x.Query("SELECT uid FROM team_user WHERE team_id=?", teamID)
+			if err != nil {
+				return fmt.Errorf("select users from team: %v", err)
+			}
+			for _, user := range results {
+				userID := com.StrTo(user["uid"]).MustInt64()
+				accessMap[UserRepo{userID, repoID}] = mode
+			}
+		}
 	}
 
+	// Drop table can't be in a session (at least not in sqlite)
+	if _, err = x.Exec("DROP TABLE access"); err != nil {
+		return fmt.Errorf("drop table: %v", err)
+	}
+
+	// Now we start writing so we make a session
 	sess := x.NewSession()
 	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
@@ -247,55 +308,12 @@ func accessRefactor(x *xorm.Engine) (err error) {
 		return fmt.Errorf("sync: %v", err)
 	}
 
-	accesses := make([]*Access, 0, 50)
-	if err = sess.Iterate(new(Access), func(idx int, bean interface{}) error {
-		a := bean.(*Access)
-
-		// Update username to user ID.
-		users, err := sess.Query("SELECT `id` FROM `user` WHERE lower_name=?", a.UserName)
-		if err != nil {
-			return fmt.Errorf("query user: %v", err)
-		} else if len(users) < 1 {
-			return nil
-		}
-		a.UserID = com.StrTo(users[0]["id"]).MustInt64()
-
-		// Update repository name(username/reponame) to repository ID.
-		names := strings.Split(a.RepoName, "/")
-		ownerName := names[0]
-		repoName := names[1]
-
-		// Check if user is the owner of the repository.
-		ownerID := a.UserID
-		if ownerName != a.UserName {
-			users, err := sess.Query("SELECT `id` FROM `user` WHERE lower_name=?", ownerName)
-			if err != nil {
-				return fmt.Errorf("query owner: %v", err)
-			} else if len(users) < 1 {
-				return nil
-			}
-			ownerID = com.StrTo(users[0]["id"]).MustInt64()
-		}
-
-		repos, err := sess.Query("SELECT `id` FROM `repository` WHERE owner_id=? AND lower_name=?", ownerID, repoName)
-		if err != nil {
-			return fmt.Errorf("query repository: %v", err)
-		} else if len(repos) < 1 {
-			return nil
-		}
-		a.RepoID = com.StrTo(repos[0]["id"]).MustInt64()
-
-		accesses = append(accesses, a)
-		return nil
-	}); err != nil {
-		return fmt.Errorf("iterate: %v", err)
+	accesses := make([]*Access, 0, len(accessMap))
+	for ur, mode := range accessMap {
+		accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode})
 	}
 
-	for i := range accesses {
-		if _, err = sess.Id(accesses[i].ID).Update(accesses[i]); err != nil {
-			return fmt.Errorf("update: %v", err)
-		}
-	}
+	_, err = sess.Insert(accesses)
 
 	return sess.Commit()
 }

From 0009a1d2b16baf1bcc9b69078853e9475662ab7f Mon Sep 17 00:00:00 2001
From: Peter Smit <peter@smitmail.eu>
Date: Fri, 13 Feb 2015 23:12:33 +0200
Subject: [PATCH 2/4] Fix access for team mode update

---
 models/org.go | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/models/org.go b/models/org.go
index 42b14bf072..115a062b4e 100644
--- a/models/org.go
+++ b/models/org.go
@@ -666,6 +666,11 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
 		return err
 	}
 
+	t.LowerName = strings.ToLower(t.Name)
+	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+		return err
+	}
+
 	// Update access for team members if needed.
 	if authChanged {
 		if err = t.getRepositories(sess); err != nil {
@@ -679,10 +684,6 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
 		}
 	}
 
-	t.LowerName = strings.ToLower(t.Name)
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-		return err
-	}
 	return sess.Commit()
 }
 

From f9454cc32c94780eb4c49753fc0ccd9b60b1deb7 Mon Sep 17 00:00:00 2001
From: Peter Smit <peter@smitmail.eu>
Date: Mon, 16 Feb 2015 12:00:06 +0200
Subject: [PATCH 3/4] Make sure that a mirror can't be written to by http or
 ssh

---
 cmd/serve.go         | 5 +++++
 routers/repo/http.go | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/cmd/serve.go b/cmd/serve.go
index e8e5c186c7..9e34b95c5a 100644
--- a/cmd/serve.go
+++ b/cmd/serve.go
@@ -164,6 +164,11 @@ func runServ(c *cli.Context) {
 			println("You have no right to write this repository")
 			log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath)
 		}
+
+		if repo.IsMirror {
+			println("You can't write to a mirror repository")
+			log.GitLogger.Fatal(2, "User %s tried to write to a mirror repository %s", user.Name, repoPath)
+		}
 	case isRead:
 		if !repo.IsPrivate {
 			break
diff --git a/routers/repo/http.go b/routers/repo/http.go
index 034b5a7b5e..d47d73ef05 100644
--- a/routers/repo/http.go
+++ b/routers/repo/http.go
@@ -158,6 +158,11 @@ func Http(ctx *middleware.Context) {
 					return
 				}
 			}
+
+			if !isPull && repo.IsMirror {
+				ctx.Handle(401, "can't push to mirror", nil)
+				return
+			}
 		}
 	}
 

From ed89b39984a9191380263eaf357c3a9c71770674 Mon Sep 17 00:00:00 2001
From: Peter Smit <peter@smitmail.eu>
Date: Mon, 16 Feb 2015 12:51:56 +0200
Subject: [PATCH 4/4] Updating context and fixing permission issues

The boolean flags in the repo context have been replaced with mode and two methods

Also, the permissions have been brought more in line with https://help.github.com/articles/permission-levels-for-an-organization-repository/ , Admin Team members are able to change settings of their repositories.
---
 cmd/web.go                    |  4 +--
 modules/middleware/context.go | 55 ++++++++++++++++++++---------------
 modules/middleware/repo.go    | 47 +++++++++++-------------------
 routers/api/v1/repo_file.go   |  2 +-
 routers/repo/issue.go         | 14 ++++-----
 routers/repo/release.go       | 10 +++----
 routers/repo/repo.go          |  2 +-
 templates/repo/header.tmpl    |  2 +-
 templates/repo/sidebar.tmpl   |  2 +-
 templates/repo/toolbar.tmpl   |  2 +-
 10 files changed, 68 insertions(+), 72 deletions(-)

diff --git a/cmd/web.go b/cmd/web.go
index 3284acb9df..8b3b03c45e 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -319,7 +319,7 @@ func runWeb(ctx *cli.Context) {
 		m.Get("/template/*", dev.TemplatePreview)
 	}
 
-	reqTrueOwner := middleware.RequireTrueOwner()
+	reqAdmin := middleware.RequireAdmin()
 
 	// Organization.
 	m.Group("/org", func() {
@@ -394,7 +394,7 @@ func runWeb(ctx *cli.Context) {
 				m.Post("/:name", repo.GitHooksEditPost)
 			}, middleware.GitHookService())
 		})
-	}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
+	}, reqSignIn, middleware.RepoAssignment(true), reqAdmin)
 
 	m.Group("/:username/:reponame", func() {
 		m.Get("/action/:action", repo.Action)
diff --git a/modules/middleware/context.go b/modules/middleware/context.go
index 28be3a3025..a266109691 100644
--- a/modules/middleware/context.go
+++ b/modules/middleware/context.go
@@ -38,29 +38,7 @@ type Context struct {
 	IsSigned    bool
 	IsBasicAuth bool
 
-	Repo struct {
-		IsOwner      bool
-		IsTrueOwner  bool
-		IsWatching   bool
-		IsBranch     bool
-		IsTag        bool
-		IsCommit     bool
-		IsAdmin      bool // Current user is admin level.
-		HasAccess    bool
-		Repository   *models.Repository
-		Owner        *models.User
-		Commit       *git.Commit
-		Tag          *git.Tag
-		GitRepo      *git.Repository
-		BranchName   string
-		TagName      string
-		TreeName     string
-		CommitId     string
-		RepoLink     string
-		CloneLink    models.CloneLink
-		CommitsCount int
-		Mirror       *models.Mirror
-	}
+	Repo RepoContext
 
 	Org struct {
 		IsOwner      bool
@@ -73,6 +51,37 @@ type Context struct {
 	}
 }
 
+type RepoContext struct {
+	AccessMode   models.AccessMode
+	IsWatching   bool
+	IsBranch     bool
+	IsTag        bool
+	IsCommit     bool
+	Repository   *models.Repository
+	Owner        *models.User
+	Commit       *git.Commit
+	Tag          *git.Tag
+	GitRepo      *git.Repository
+	BranchName   string
+	TagName      string
+	TreeName     string
+	CommitId     string
+	RepoLink     string
+	CloneLink    models.CloneLink
+	CommitsCount int
+	Mirror       *models.Mirror
+}
+
+// Return if the current user has write access for this repository
+func (r RepoContext) IsOwner() bool {
+	return r.AccessMode >= models.ACCESS_MODE_WRITE
+}
+
+// Return if the current user has read access for this repository
+func (r RepoContext) HasAccess() bool {
+	return r.AccessMode >= models.ACCESS_MODE_READ
+}
+
 // HasError returns true if error occurs in form validation.
 func (ctx *Context) HasApiError() bool {
 	hasErr, ok := ctx.Data["HasError"]
diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go
index bd298819d7..5c863dc01f 100644
--- a/modules/middleware/repo.go
+++ b/modules/middleware/repo.go
@@ -58,24 +58,19 @@ func ApiRepoAssignment() macaron.Handler {
 			return
 		}
 
-		if ctx.IsSigned {
-			mode, err := models.AccessLevel(ctx.User, repo)
-			if err != nil {
-				ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL})
-				return
-			}
-
-			ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE
-			ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ
-			ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER
+		mode, err := models.AccessLevel(ctx.User, repo)
+		if err != nil {
+			ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL})
+			return
 		}
 
+		ctx.Repo.AccessMode = mode
+
 		// Check access.
-		if repo.IsPrivate && !ctx.Repo.IsOwner {
+		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
 			ctx.Error(404)
 			return
 		}
-		ctx.Repo.HasAccess = true
 
 		ctx.Repo.Repository = repo
 	}
@@ -239,26 +234,18 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 			return
 		}
 
-		if ctx.IsSigned {
-			mode, err := models.AccessLevel(ctx.User, repo)
-			if err != nil {
-				ctx.Handle(500, "AccessLevel", err)
-				return
-			}
-			ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE
-			ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ
-			ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER
-			if !ctx.Repo.IsTrueOwner && ctx.Repo.Owner.IsOrganization() {
-				ctx.Repo.IsTrueOwner = ctx.Repo.Owner.IsOwnedBy(ctx.User.Id)
-			}
+		mode, err := models.AccessLevel(ctx.User, repo)
+		if err != nil {
+			ctx.Handle(500, "AccessLevel", err)
+			return
 		}
+		ctx.Repo.AccessMode = mode
 
 		// Check access.
-		if repo.IsPrivate && !ctx.Repo.IsOwner {
+		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
 			ctx.Handle(404, "no access right", err)
 			return
 		}
-		ctx.Repo.HasAccess = true
 
 		ctx.Data["HasAccess"] = true
 
@@ -306,8 +293,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 		ctx.Data["Title"] = u.Name + "/" + repo.Name
 		ctx.Data["Repository"] = repo
 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner
-		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
-		ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
+		ctx.Data["IsRepositoryOwner"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_WRITE
+		ctx.Data["IsRepositoryAdmin"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_ADMIN
 
 		ctx.Data["DisableSSH"] = setting.DisableSSH
 		ctx.Repo.CloneLink, err = repo.CloneLink()
@@ -362,9 +349,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 	}
 }
 
-func RequireTrueOwner() macaron.Handler {
+func RequireAdmin() macaron.Handler {
 	return func(ctx *Context) {
-		if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
+		if ctx.Repo.AccessMode < models.ACCESS_MODE_ADMIN {
 			if !ctx.IsSigned {
 				ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
 				ctx.Redirect(setting.AppSubUrl + "/user/login")
diff --git a/routers/api/v1/repo_file.go b/routers/api/v1/repo_file.go
index a049904f95..73f97b2cae 100644
--- a/routers/api/v1/repo_file.go
+++ b/routers/api/v1/repo_file.go
@@ -12,7 +12,7 @@ import (
 )
 
 func GetRepoRawFile(ctx *middleware.Context) {
-	if ctx.Repo.Repository.IsPrivate && !ctx.Repo.HasAccess {
+	if !ctx.Repo.HasAccess() {
 		ctx.Error(404)
 		return
 	}
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index bf39d9aba6..40e9338970 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -230,7 +230,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
 	}
 
 	// Only collaborators can assign.
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		form.AssigneeId = 0
 	}
 	issue := &models.Issue{
@@ -434,7 +434,7 @@ func ViewIssue(ctx *middleware.Context) {
 	ctx.Data["Title"] = issue.Name
 	ctx.Data["Issue"] = issue
 	ctx.Data["Comments"] = comments
-	ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
+	ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
 	ctx.Data["IsRepoToolbarIssues"] = true
 	ctx.Data["IsRepoToolbarIssuesList"] = false
 	ctx.HTML(200, ISSUE_VIEW)
@@ -457,7 +457,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
 		return
 	}
 
-	if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner {
+	if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		return
 	}
@@ -484,7 +484,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
 }
 
 func UpdateIssueLabel(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		return
 	}
@@ -560,7 +560,7 @@ func UpdateIssueLabel(ctx *middleware.Context) {
 }
 
 func UpdateIssueMilestone(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		return
 	}
@@ -606,7 +606,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) {
 }
 
 func UpdateAssignee(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		return
 	}
@@ -752,7 +752,7 @@ func Comment(ctx *middleware.Context) {
 
 	// Check if issue owner changes the status of issue.
 	var newStatus string
-	if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id {
+	if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id {
 		newStatus = ctx.Query("change_status")
 	}
 	if len(newStatus) > 0 {
diff --git a/routers/repo/release.go b/routers/repo/release.go
index 591810cc5f..52d78b1967 100644
--- a/routers/repo/release.go
+++ b/routers/repo/release.go
@@ -41,7 +41,7 @@ func Releases(ctx *middleware.Context) {
 	tags := make([]*models.Release, len(rawTags))
 	for i, rawTag := range rawTags {
 		for j, rel := range rels {
-			if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) {
+			if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner()) {
 				continue
 			}
 			if rel.TagName == rawTag {
@@ -140,7 +140,7 @@ func Releases(ctx *middleware.Context) {
 }
 
 func NewRelease(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		return
 	}
@@ -153,7 +153,7 @@ func NewRelease(ctx *middleware.Context) {
 }
 
 func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		return
 	}
@@ -211,7 +211,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
 }
 
 func EditRelease(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesEdit", nil)
 		return
 	}
@@ -234,7 +234,7 @@ func EditRelease(ctx *middleware.Context) {
 }
 
 func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.EditReleasePost", nil)
 		return
 	}
diff --git a/routers/repo/repo.go b/routers/repo/repo.go
index 48f7b09bc0..0053720030 100644
--- a/routers/repo/repo.go
+++ b/routers/repo/repo.go
@@ -343,7 +343,7 @@ func Action(ctx *middleware.Context) {
 	case "unstar":
 		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
 	case "desc":
-		if !ctx.Repo.IsOwner {
+		if !ctx.Repo.IsOwner() {
 			ctx.Error(404)
 			return
 		}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index a0b927be60..21f9cea882 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -49,7 +49,7 @@
                 </a>
             </li>
             <li id="repo-header-fork">
-                <a id="repo-header-fork-btn" {{if or (not $.IsRepositoryTrueOwner) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
+                <a id="repo-header-fork-btn" {{if or (not $.IsRepositoryAdmin) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
                     <button class="btn btn-gray text-bold btn-radius">
                         <i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
                         <span class="num">{{.NumForks}}</span>
diff --git a/templates/repo/sidebar.tmpl b/templates/repo/sidebar.tmpl
index 9d6abb47bc..1609603821 100644
--- a/templates/repo/sidebar.tmpl
+++ b/templates/repo/sidebar.tmpl
@@ -20,7 +20,7 @@
         <!-- <li>
             <a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
         </li> -->
-        {{if .IsRepositoryTrueOwner}}
+        {{if .IsRepositoryAdmin}}
         <li class="border-bottom"></li>
         <li>
             <a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a>
diff --git a/templates/repo/toolbar.tmpl b/templates/repo/toolbar.tmpl
index 6357b3c4c0..f2254d21dd 100644
--- a/templates/repo/toolbar.tmpl
+++ b/templates/repo/toolbar.tmpl
@@ -35,7 +35,7 @@
                             <li><a href="#">Pulse</a></li>
                             <li><a href="#">Network</a></li>
                         </ul>
-                    </li> -->{{end}}{{if .IsRepositoryTrueOwner}}
+                    </li> -->{{end}}{{if .IsRepositoryAdmin}}
                     <li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
                     </li>{{end}}
                 </ul>