Procházet zdrojové kódy

Merge branch 'access' of github.com:gogits/gogs into access

Unknwon před 10 roky
rodič
revize
10e4b5b6c6

+ 5 - 0
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

+ 2 - 2
cmd/web.go

@@ -318,7 +318,7 @@ func runWeb(ctx *cli.Context) {
 		m.Get("/template/*", dev.TemplatePreview)
 	}
 
-	reqTrueOwner := middleware.RequireTrueOwner()
+	reqAdmin := middleware.RequireAdmin()
 
 	// Organization.
 	m.Group("/org", func() {
@@ -393,7 +393,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)

+ 78 - 60
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,90 +213,107 @@ 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`"
-	}
-	if _, err = x.Exec(rawSQL); err != nil &&
-		!strings.Contains(err.Error(), "check that column/key exists") {
-		return fmt.Errorf("drop index: %v", err)
-	}
+	// We consiously don't start a session yet as we make only reads for now, no writes
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
+	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
 	}
+	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
 
-	if err = sess.Sync2(new(Access)); err != nil {
-		return fmt.Errorf("sync: %v", err)
-	}
+		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
+		}
 
-	accesses := make([]*Access, 0, 50)
-	if err = sess.Iterate(new(Access), func(idx int, bean interface{}) error {
-		a := bean.(*Access)
+		if !ownerIsOrganization {
+			continue
+		}
 
-		// Update username to user ID.
-		users, err := sess.Query("SELECT `id` FROM `user` WHERE lower_name=?", a.UserName)
+		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("query user: %v", err)
-		} else if len(users) < 1 {
-			return nil
+			return fmt.Errorf("select teams from org: %v", err)
 		}
-		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]
+		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())
 
-		// 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)
+			results, err := x.Query("SELECT uid FROM team_user WHERE team_id=?", teamID)
 			if err != nil {
-				return fmt.Errorf("query owner: %v", err)
-			} else if len(users) < 1 {
-				return 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
 			}
-			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()
+	// 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)
+	}
 
-		accesses = append(accesses, a)
-		return nil
-	}); err != nil {
-		return fmt.Errorf("iterate: %v", err)
+	// Now we start writing so we make a session
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
 	}
 
-	for i := range accesses {
-		if _, err = sess.Id(accesses[i].ID).Update(accesses[i]); err != nil {
-			return fmt.Errorf("update: %v", err)
-		}
+	if err = sess.Sync2(new(Access)); err != nil {
+		return fmt.Errorf("sync: %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})
 	}
 
+	_, err = sess.Insert(accesses)
+
 	return sess.Commit()
 }

+ 5 - 4
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()
 }
 

+ 32 - 23
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"]

+ 17 - 30
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()
@@ -361,9 +348,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")

+ 1 - 1
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
 	}

+ 5 - 0
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
+			}
 		}
 	}
 

+ 7 - 7
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 {

+ 5 - 5
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
 	}

+ 1 - 1
routers/repo/repo.go

@@ -349,7 +349,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
 		}

+ 1 - 1
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>

+ 1 - 1
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>

+ 1 - 1
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>