]> source.dussan.org Git - gitea.git/commitdiff
Sign protected branches (#8993)
authorzeripath <art27@cantab.net>
Sun, 15 Dec 2019 11:06:31 +0000 (11:06 +0000)
committerGitHub <noreply@github.com>
Sun, 15 Dec 2019 11:06:31 +0000 (11:06 +0000)
* Move SignMerge to PullRequest

* Add approved signing mode

* As per @guillep2k comment

custom/conf/app.ini.sample
docs/content/doc/advanced/config-cheat-sheet.en-us.md
docs/content/doc/advanced/signing.en-us.md
models/pull_sign.go [new file with mode: 0644]
models/repo_sign.go
services/pull/merge.go

index 76889484b58a67e8e05452489d384d1b12796ff9..701374d4b85201669bbee26557177e2e0355fabd 100644 (file)
@@ -110,6 +110,7 @@ WIKI = never
 ; Determines when to sign on merges
 ; - basesigned: require that the parent of commit on the base repo is signed.
 ; - commitssigned: require that all the commits in the head branch are signed.
+; - approved: only sign when merging an approved pr to a protected branch
 MERGES = pubkey, twofa, basesigned, commitssigned
 
 [cors]
index 4174a874715c826a3c592e7280a187fee3e25707..36e56c3fed347de12a310812b78b60cee9e706fc 100644 (file)
@@ -96,7 +96,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `CRUD_ACTIONS`: **pubkey, twofa, parentsigned**: \[never, pubkey, twofa, parentsigned, always\]: Sign CRUD actions.
   - Options as above, with the addition of:
   - `parentsigned`: Only sign if the parent commit is signed.
-- `MERGES`: **pubkey, twofa, basesigned, commitssigned**: \[never, pubkey, twofa, basesigned, commitssigned, always\]: Sign merges.
+- `MERGES`: **pubkey, twofa, basesigned, commitssigned**: \[never, pubkey, twofa, approved, basesigned, commitssigned, always\]: Sign merges.
+  - `approved`: Only sign approved merges to a protected branch.
   - `basesigned`: Only sign if the parent commit in the base repo is signed.
   - `headsigned`: Only sign if the head commit in the head branch is signed.
   - `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
index b6c99e269e09b07849c37fb7e3330073c9d428d6..72d294e7bd62ae295ed86ebf2e9cb77246118bbd 100644 (file)
@@ -136,6 +136,7 @@ The possible options are:
 * `basesigned`: Only sign if the parent commit in the base repo is signed.
 * `headsigned`: Only sign if the head commit in the head branch is signed.
 * `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
+* `approved`: Only sign approved merges to a protected branch.
 * `always`: Always sign
 
 Options other than `never` and `always` can be combined as a comma
diff --git a/models/pull_sign.go b/models/pull_sign.go
new file mode 100644 (file)
index 0000000..19d8907
--- /dev/null
@@ -0,0 +1,121 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+       "code.gitea.io/gitea/modules/git"
+       "code.gitea.io/gitea/modules/log"
+       "code.gitea.io/gitea/modules/setting"
+)
+
+// SignMerge determines if we should sign a PR merge commit to the base repository
+func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string) {
+       if err := pr.GetBaseRepo(); err != nil {
+               log.Error("Unable to get Base Repo for pull request")
+               return false, ""
+       }
+       repo := pr.BaseRepo
+
+       signingKey := signingKey(repo.RepoPath())
+       if signingKey == "" {
+               return false, ""
+       }
+       rules := signingModeFromStrings(setting.Repository.Signing.Merges)
+
+       var gitRepo *git.Repository
+       var err error
+
+       for _, rule := range rules {
+               switch rule {
+               case never:
+                       return false, ""
+               case always:
+                       break
+               case pubkey:
+                       keys, err := ListGPGKeys(u.ID)
+                       if err != nil || len(keys) == 0 {
+                               return false, ""
+                       }
+               case twofa:
+                       twofa, err := GetTwoFactorByUID(u.ID)
+                       if err != nil || twofa == nil {
+                               return false, ""
+                       }
+               case approved:
+                       protectedBranch, err := GetProtectedBranchBy(repo.ID, pr.BaseBranch)
+                       if err != nil || protectedBranch == nil {
+                               return false, ""
+                       }
+                       if protectedBranch.GetGrantedApprovalsCount(pr) < 1 {
+                               return false, ""
+                       }
+               case baseSigned:
+                       if gitRepo == nil {
+                               gitRepo, err = git.OpenRepository(tmpBasePath)
+                               if err != nil {
+                                       return false, ""
+                               }
+                               defer gitRepo.Close()
+                       }
+                       commit, err := gitRepo.GetCommit(baseCommit)
+                       if err != nil {
+                               return false, ""
+                       }
+                       verification := ParseCommitWithSignature(commit)
+                       if !verification.Verified {
+                               return false, ""
+                       }
+               case headSigned:
+                       if gitRepo == nil {
+                               gitRepo, err = git.OpenRepository(tmpBasePath)
+                               if err != nil {
+                                       return false, ""
+                               }
+                               defer gitRepo.Close()
+                       }
+                       commit, err := gitRepo.GetCommit(headCommit)
+                       if err != nil {
+                               return false, ""
+                       }
+                       verification := ParseCommitWithSignature(commit)
+                       if !verification.Verified {
+                               return false, ""
+                       }
+               case commitsSigned:
+                       if gitRepo == nil {
+                               gitRepo, err = git.OpenRepository(tmpBasePath)
+                               if err != nil {
+                                       return false, ""
+                               }
+                               defer gitRepo.Close()
+                       }
+                       commit, err := gitRepo.GetCommit(headCommit)
+                       if err != nil {
+                               return false, ""
+                       }
+                       verification := ParseCommitWithSignature(commit)
+                       if !verification.Verified {
+                               return false, ""
+                       }
+                       // need to work out merge-base
+                       mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
+                       if err != nil {
+                               return false, ""
+                       }
+                       commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
+                       if err != nil {
+                               return false, ""
+                       }
+                       for e := commitList.Front(); e != nil; e = e.Next() {
+                               commit = e.Value.(*git.Commit)
+                               verification := ParseCommitWithSignature(commit)
+                               if !verification.Verified {
+                                       return false, ""
+                               }
+                       }
+               }
+       }
+       return true, signingKey
+}
index a02b027f89f8929f597ab114d89c8655e90bdfce..a684efb55fc442e0f5516b5929716edfa575575d 100644 (file)
@@ -24,6 +24,7 @@ const (
        baseSigned    signingMode = "basesigned"
        headSigned    signingMode = "headsigned"
        commitsSigned signingMode = "commitssigned"
+       approved      signingMode = "approved"
 )
 
 func signingModeFromStrings(modeStrings []string) []signingMode {
@@ -45,6 +46,8 @@ func signingModeFromStrings(modeStrings []string) []signingMode {
                        fallthrough
                case headSigned:
                        fallthrough
+               case approved:
+                       fallthrough
                case commitsSigned:
                        returnable = append(returnable, signMode)
                }
@@ -211,98 +214,3 @@ func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string
        }
        return true, signingKey
 }
-
-// SignMerge determines if we should sign a merge commit to this repository
-func (repo *Repository) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string) {
-       rules := signingModeFromStrings(setting.Repository.Signing.Merges)
-       signingKey := signingKey(repo.RepoPath())
-       if signingKey == "" {
-               return false, ""
-       }
-       var gitRepo *git.Repository
-       var err error
-
-       for _, rule := range rules {
-               switch rule {
-               case never:
-                       return false, ""
-               case always:
-                       break
-               case pubkey:
-                       keys, err := ListGPGKeys(u.ID)
-                       if err != nil || len(keys) == 0 {
-                               return false, ""
-                       }
-               case twofa:
-                       twofa, err := GetTwoFactorByUID(u.ID)
-                       if err != nil || twofa == nil {
-                               return false, ""
-                       }
-               case baseSigned:
-                       if gitRepo == nil {
-                               gitRepo, err = git.OpenRepository(tmpBasePath)
-                               if err != nil {
-                                       return false, ""
-                               }
-                               defer gitRepo.Close()
-                       }
-                       commit, err := gitRepo.GetCommit(baseCommit)
-                       if err != nil {
-                               return false, ""
-                       }
-                       verification := ParseCommitWithSignature(commit)
-                       if !verification.Verified {
-                               return false, ""
-                       }
-               case headSigned:
-                       if gitRepo == nil {
-                               gitRepo, err = git.OpenRepository(tmpBasePath)
-                               if err != nil {
-                                       return false, ""
-                               }
-                               defer gitRepo.Close()
-                       }
-                       commit, err := gitRepo.GetCommit(headCommit)
-                       if err != nil {
-                               return false, ""
-                       }
-                       verification := ParseCommitWithSignature(commit)
-                       if !verification.Verified {
-                               return false, ""
-                       }
-               case commitsSigned:
-                       if gitRepo == nil {
-                               gitRepo, err = git.OpenRepository(tmpBasePath)
-                               if err != nil {
-                                       return false, ""
-                               }
-                               defer gitRepo.Close()
-                       }
-                       commit, err := gitRepo.GetCommit(headCommit)
-                       if err != nil {
-                               return false, ""
-                       }
-                       verification := ParseCommitWithSignature(commit)
-                       if !verification.Verified {
-                               return false, ""
-                       }
-                       // need to work out merge-base
-                       mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
-                       if err != nil {
-                               return false, ""
-                       }
-                       commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
-                       if err != nil {
-                               return false, ""
-                       }
-                       for e := commitList.Front(); e != nil; e = e.Next() {
-                               commit = e.Value.(*git.Commit)
-                               verification := ParseCommitWithSignature(commit)
-                               if !verification.Verified {
-                                       return false, ""
-                               }
-                       }
-               }
-       }
-       return true, signingKey
-}
index 9b75c5ffda7655ef964711be1539da94898daae3..8aa38bf11eab7a7bc11b6e3e2cba952c7d854611 100644 (file)
@@ -162,7 +162,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
        // Determine if we should sign
        signArg := ""
        if version.Compare(binVersion, "1.7.9", ">=") {
-               sign, keyID := pr.BaseRepo.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch)
+               sign, keyID := pr.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch)
                if sign {
                        signArg = "-S" + keyID
                } else if version.Compare(binVersion, "2.0.0", ">=") {