Skip to content

Commit 66ee9b8

Browse files
zeripathguillep2k
authored andcommitted
Add require signed commit for protected branch (#9708)
* Add require signed commit for protected branch * Fix fmt * Make editor show if they will be signed * bugfix * Add basic merge check and better information for CRUD * linting comment * Add descriptors to merge signing * Slight refactor * Slight improvement to appearances * Handle Merge API * manage CRUD API * Move error to error.go * Remove fix to delete.go * prep for merge * need to tolerate \r\n in message * check protected branch before trying to load it * Apply suggestions from code review Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * fix commit-reader Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
1 parent 6b1fa12 commit 66ee9b8

File tree

29 files changed

+618
-122
lines changed

29 files changed

+618
-122
lines changed

models/branches.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type ProtectedBranch struct {
4646
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
4747
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
4848
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
49+
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
4950

5051
CreatedUnix timeutil.TimeStamp `xorm:"created"`
5152
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`

models/error.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,22 @@ func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
916916
return fmt.Sprintf("user doesn't have acces to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
917917
}
918918

919+
// ErrWontSign explains the first reason why a commit would not be signed
920+
// There may be other reasons - this is just the first reason found
921+
type ErrWontSign struct {
922+
Reason signingMode
923+
}
924+
925+
func (e *ErrWontSign) Error() string {
926+
return fmt.Sprintf("wont sign: %s", e.Reason)
927+
}
928+
929+
// IsErrWontSign checks if an error is a ErrWontSign
930+
func IsErrWontSign(err error) bool {
931+
_, ok := err.(*ErrWontSign)
932+
return ok
933+
}
934+
919935
// __________ .__
920936
// \______ \____________ ____ ____ | |__
921937
// | | _/\_ __ \__ \ / \_/ ___\| | \

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ var migrations = []Migration{
298298
NewMigration("Add owner_name on table repository", addOwnerNameOnRepository),
299299
// v121 -> v122
300300
NewMigration("add is_restricted column for users table", addIsRestricted),
301+
// v122 -> v123
302+
NewMigration("Add Require Signed Commits to ProtectedBranch", addRequireSignedCommits),
301303
}
302304

303305
// Migrate database to current version

models/migrations/v122.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package migrations
6+
7+
import (
8+
"xorm.io/xorm"
9+
)
10+
11+
func addRequireSignedCommits(x *xorm.Engine) error {
12+
13+
type ProtectedBranch struct {
14+
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
15+
}
16+
17+
return x.Sync2(new(ProtectedBranch))
18+
}

models/pull.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,18 @@ func (pr *PullRequest) LoadProtectedBranch() (err error) {
152152
}
153153

154154
func (pr *PullRequest) loadProtectedBranch(e Engine) (err error) {
155-
if pr.BaseRepo == nil {
156-
if pr.BaseRepoID == 0 {
157-
return nil
158-
}
159-
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
160-
if err != nil {
161-
return
155+
if pr.ProtectedBranch == nil {
156+
if pr.BaseRepo == nil {
157+
if pr.BaseRepoID == 0 {
158+
return nil
159+
}
160+
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
161+
if err != nil {
162+
return
163+
}
162164
}
165+
pr.ProtectedBranch, err = getProtectedBranchBy(e, pr.BaseRepo.ID, pr.BaseBranch)
163166
}
164-
pr.ProtectedBranch, err = getProtectedBranchBy(e, pr.BaseRepo.ID, pr.BaseBranch)
165167
return
166168
}
167169

models/pull_sign.go

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ import (
1111
)
1212

1313
// SignMerge determines if we should sign a PR merge commit to the base repository
14-
func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string) {
14+
func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, error) {
1515
if err := pr.GetBaseRepo(); err != nil {
1616
log.Error("Unable to get Base Repo for pull request")
17-
return false, ""
17+
return false, "", err
1818
}
1919
repo := pr.BaseRepo
2020

2121
signingKey := signingKey(repo.RepoPath())
2222
if signingKey == "" {
23-
return false, ""
23+
return false, "", &ErrWontSign{noKey}
2424
}
2525
rules := signingModeFromStrings(setting.Repository.Signing.Merges)
2626

@@ -30,92 +30,101 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st
3030
for _, rule := range rules {
3131
switch rule {
3232
case never:
33-
return false, ""
33+
return false, "", &ErrWontSign{never}
3434
case always:
3535
break
3636
case pubkey:
3737
keys, err := ListGPGKeys(u.ID)
38-
if err != nil || len(keys) == 0 {
39-
return false, ""
38+
if err != nil {
39+
return false, "", err
40+
}
41+
if len(keys) == 0 {
42+
return false, "", &ErrWontSign{pubkey}
4043
}
4144
case twofa:
42-
twofa, err := GetTwoFactorByUID(u.ID)
43-
if err != nil || twofa == nil {
44-
return false, ""
45+
twofaModel, err := GetTwoFactorByUID(u.ID)
46+
if err != nil {
47+
return false, "", err
48+
}
49+
if twofaModel == nil {
50+
return false, "", &ErrWontSign{twofa}
4551
}
4652
case approved:
4753
protectedBranch, err := GetProtectedBranchBy(repo.ID, pr.BaseBranch)
48-
if err != nil || protectedBranch == nil {
49-
return false, ""
54+
if err != nil {
55+
return false, "", err
56+
}
57+
if protectedBranch == nil {
58+
return false, "", &ErrWontSign{approved}
5059
}
5160
if protectedBranch.GetGrantedApprovalsCount(pr) < 1 {
52-
return false, ""
61+
return false, "", &ErrWontSign{approved}
5362
}
5463
case baseSigned:
5564
if gitRepo == nil {
5665
gitRepo, err = git.OpenRepository(tmpBasePath)
5766
if err != nil {
58-
return false, ""
67+
return false, "", err
5968
}
6069
defer gitRepo.Close()
6170
}
6271
commit, err := gitRepo.GetCommit(baseCommit)
6372
if err != nil {
64-
return false, ""
73+
return false, "", err
6574
}
6675
verification := ParseCommitWithSignature(commit)
6776
if !verification.Verified {
68-
return false, ""
77+
return false, "", &ErrWontSign{baseSigned}
6978
}
7079
case headSigned:
7180
if gitRepo == nil {
7281
gitRepo, err = git.OpenRepository(tmpBasePath)
7382
if err != nil {
74-
return false, ""
83+
return false, "", err
7584
}
7685
defer gitRepo.Close()
7786
}
7887
commit, err := gitRepo.GetCommit(headCommit)
7988
if err != nil {
80-
return false, ""
89+
return false, "", err
8190
}
8291
verification := ParseCommitWithSignature(commit)
8392
if !verification.Verified {
84-
return false, ""
93+
return false, "", &ErrWontSign{headSigned}
8594
}
8695
case commitsSigned:
8796
if gitRepo == nil {
8897
gitRepo, err = git.OpenRepository(tmpBasePath)
8998
if err != nil {
90-
return false, ""
99+
return false, "", err
91100
}
92101
defer gitRepo.Close()
93102
}
94103
commit, err := gitRepo.GetCommit(headCommit)
95104
if err != nil {
96-
return false, ""
105+
return false, "", err
97106
}
98107
verification := ParseCommitWithSignature(commit)
99108
if !verification.Verified {
100-
return false, ""
109+
return false, "", &ErrWontSign{commitsSigned}
101110
}
102111
// need to work out merge-base
103112
mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
104113
if err != nil {
105-
return false, ""
114+
return false, "", err
106115
}
107116
commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
108117
if err != nil {
109-
return false, ""
118+
return false, "", err
110119
}
111120
for e := commitList.Front(); e != nil; e = e.Next() {
112121
commit = e.Value.(*git.Commit)
113122
verification := ParseCommitWithSignature(commit)
114123
if !verification.Verified {
115-
return false, ""
124+
return false, "", &ErrWontSign{commitsSigned}
116125
}
117126
}
118127
}
119128
}
120-
return true, signingKey
129+
return true, signingKey, nil
121130
}

0 commit comments

Comments
 (0)