diff options
Diffstat (limited to 'models/repo_branch.go')
-rw-r--r-- | models/repo_branch.go | 145 |
1 files changed, 142 insertions, 3 deletions
diff --git a/models/repo_branch.go b/models/repo_branch.go index 6fe2222d..77e1db7f 100644 --- a/models/repo_branch.go +++ b/models/repo_branch.go @@ -6,8 +6,12 @@ package models import ( "fmt" + "strings" + "github.com/Unknwon/com" "github.com/gogits/git-module" + + "github.com/gogits/gogs/modules/base" ) type Branch struct { @@ -58,6 +62,20 @@ func (br *Branch) GetCommit() (*git.Commit, error) { return gitRepo.GetBranchCommit(br.Name) } +type ProtectBranchWhitelist struct { + ID int64 + ProtectBranchID int64 + RepoID int64 `xorm:"UNIQUE(protect_branch_whitelist)"` + Name string `xorm:"UNIQUE(protect_branch_whitelist)"` + UserID int64 `xorm:"UNIQUE(protect_branch_whitelist)"` +} + +// IsUserInProtectBranchWhitelist returns true if given user is in the whitelist of a branch in a repository. +func IsUserInProtectBranchWhitelist(repoID, userID int64, branch string) bool { + has, err := x.Where("repo_id = ?", repoID).And("user_id = ?", userID).And("name = ?", branch).Get(new(ProtectBranchWhitelist)) + return has && err == nil +} + // ProtectBranch contains options of a protected branch. type ProtectBranch struct { ID int64 @@ -65,6 +83,9 @@ type ProtectBranch struct { Name string `xorm:"UNIQUE(protect_branch)"` Protected bool RequirePullRequest bool + EnableWhitelist bool + WhitelistUserIDs string `xorm:"TEXT"` + WhitelistTeamIDs string `xorm:"TEXT"` } // GetProtectBranchOfRepoByName returns *ProtectBranch by branch name in given repostiory. @@ -94,15 +115,133 @@ func IsBranchOfRepoRequirePullRequest(repoID int64, name string) bool { // UpdateProtectBranch saves branch protection options. // If ID is 0, it creates a new record. Otherwise, updates existing record. func UpdateProtectBranch(protectBranch *ProtectBranch) (err error) { + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + + if protectBranch.ID == 0 { + if _, err = sess.Insert(protectBranch); err != nil { + return fmt.Errorf("Insert: %v", err) + } + return + } + + if _, err = sess.Id(protectBranch.ID).AllCols().Update(protectBranch); err != nil { + return fmt.Errorf("Update: %v", err) + } + + return sess.Commit() +} + +// UpdateOrgProtectBranch saves branch protection options of organizational repository. +// If ID is 0, it creates a new record. Otherwise, updates existing record. +// This function also performs check if whitelist user and team's IDs have been changed +// to avoid unnecessary whitelist delete and regenerate. +func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whitelistUserIDs, whitelistTeamIDs string) (err error) { + if err = repo.GetOwner(); err != nil { + return fmt.Errorf("GetOwner: %v", err) + } else if !repo.Owner.IsOrganization() { + return fmt.Errorf("expect repository owner to be an organization") + } + + hasUsersChanged := false + validUserIDs := base.StringsToInt64s(strings.Split(protectBranch.WhitelistUserIDs, ",")) + if protectBranch.WhitelistUserIDs != whitelistUserIDs { + hasUsersChanged = true + userIDs := base.StringsToInt64s(strings.Split(whitelistUserIDs, ",")) + validUserIDs = make([]int64, 0, len(userIDs)) + for _, userID := range userIDs { + has, err := HasAccess(userID, repo, ACCESS_MODE_WRITE) + if err != nil { + return fmt.Errorf("HasAccess [user_id: %d, repo_id: %d]: %v", userID, protectBranch.RepoID, err) + } else if !has { + continue // Drop invalid user ID + } + + validUserIDs = append(validUserIDs, userID) + } + + protectBranch.WhitelistUserIDs = strings.Join(base.Int64sToStrings(validUserIDs), ",") + } + + hasTeamsChanged := false + validTeamIDs := base.StringsToInt64s(strings.Split(protectBranch.WhitelistTeamIDs, ",")) + if protectBranch.WhitelistTeamIDs != whitelistTeamIDs { + hasTeamsChanged = true + teamIDs := base.StringsToInt64s(strings.Split(whitelistTeamIDs, ",")) + teams, err := GetTeamsByOrgID(repo.OwnerID) + if err != nil { + return fmt.Errorf("GetTeamsByOrgID [org_id: %d]: %v", repo.OwnerID, err) + } + validTeamIDs = make([]int64, 0, len(teams)) + for i := range teams { + if teams[i].HasWriteAccess() && com.IsSliceContainsInt64(teamIDs, teams[i].ID) { + validTeamIDs = append(validTeamIDs, teams[i].ID) + } + } + + protectBranch.WhitelistTeamIDs = strings.Join(base.Int64sToStrings(validTeamIDs), ",") + } + + // Merge users and members of teams + var whitelists []*ProtectBranchWhitelist + if hasUsersChanged || hasTeamsChanged { + mergedUserIDs := make(map[int64]bool) + for _, userID := range validUserIDs { + mergedUserIDs[userID] = true + } + + for _, teamID := range validTeamIDs { + members, err := GetTeamMembers(teamID) + if err != nil { + return fmt.Errorf("GetTeamMembers [team_id: %d]: %v", teamID, err) + } + + for i := range members { + mergedUserIDs[members[i].ID] = true + } + } + + whitelists = make([]*ProtectBranchWhitelist, 0, len(mergedUserIDs)) + for userID := range mergedUserIDs { + whitelists = append(whitelists, &ProtectBranchWhitelist{ + ProtectBranchID: protectBranch.ID, + RepoID: repo.ID, + Name: protectBranch.Name, + UserID: userID, + }) + } + } + + sess := x.NewSession() + defer sessionRelease(sess) + if err = sess.Begin(); err != nil { + return err + } + if protectBranch.ID == 0 { - if _, err = x.Insert(protectBranch); err != nil { + if _, err = sess.Insert(protectBranch); err != nil { return fmt.Errorf("Insert: %v", err) } return } - _, err = x.Id(protectBranch.ID).AllCols().Update(protectBranch) - return err + if _, err = sess.Id(protectBranch.ID).AllCols().Update(protectBranch); err != nil { + return fmt.Errorf("Update: %v", err) + } + + // Refresh whitelists + if hasUsersChanged || hasTeamsChanged { + if _, err = sess.Delete(&ProtectBranchWhitelist{ProtectBranchID: protectBranch.ID}); err != nil { + return fmt.Errorf("delete old protect branch whitelists: %v", err) + } else if _, err = sess.Insert(whitelists); err != nil { + return fmt.Errorf("insert new protect branch whitelists: %v", err) + } + } + + return sess.Commit() } // GetProtectBranchesByRepoID returns a list of *ProtectBranch in given repostiory. |