Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add require signed commit for protected branch #9708

Merged
merged 28 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ba15ed9
Add require signed commit for protected branch
zeripath Jan 11, 2020
f482e28
Fix fmt
zeripath Jan 11, 2020
43c5df5
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 11, 2020
f9f8557
Make editor show if they will be signed
zeripath Jan 11, 2020
27c0354
Merge branch 'protect-branch-signed-commits-only' of github.com:zerip…
zeripath Jan 11, 2020
f2a28e8
bugfix
zeripath Jan 11, 2020
9a7ec7f
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 11, 2020
e4d987a
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 12, 2020
594126a
Add basic merge check and better information for CRUD
zeripath Jan 12, 2020
dd05f6a
linting comment
zeripath Jan 12, 2020
de67865
Add descriptors to merge signing
zeripath Jan 12, 2020
97b811d
Slight refactor
zeripath Jan 12, 2020
ea21e79
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 12, 2020
68a8e11
Slight improvement to appearances
zeripath Jan 12, 2020
acf011a
Handle Merge API
zeripath Jan 12, 2020
24eb4a2
manage CRUD API
zeripath Jan 12, 2020
bd8d89f
Move error to error.go
zeripath Jan 13, 2020
bfb7df0
Remove fix to delete.go
zeripath Jan 13, 2020
50cf492
prep for merge
zeripath Jan 13, 2020
6793e8d
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 13, 2020
7a04a47
need to tolerate \r\n in message
zeripath Jan 14, 2020
161d110
check protected branch before trying to load it
zeripath Jan 14, 2020
335ec50
Apply suggestions from code review
zeripath Jan 14, 2020
5ad5a56
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 14, 2020
ea40bd6
fix commit-reader
zeripath Jan 14, 2020
d554a62
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 14, 2020
2d454cd
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 14, 2020
788ed2f
Merge branch 'master' into protect-branch-signed-commits-only
zeripath Jan 15, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions models/branches.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type ProtectedBranch struct {
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
BlockOnRejectedReviews bool `xorm:"NOT NULL DEFAULT false"`
DismissStaleApprovals bool `xorm:"NOT NULL DEFAULT false"`
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`

CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
Expand Down
16 changes: 16 additions & 0 deletions models/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,22 @@ func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
return fmt.Sprintf("user doesn't have acces to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
}

// ErrWontSign explains the first reason why a commit would not be signed
// There may be other reasons - this is just the first reason found
type ErrWontSign struct {
Reason signingMode
}

func (e *ErrWontSign) Error() string {
return fmt.Sprintf("wont sign: %s", e.Reason)
}

// IsErrWontSign checks if an error is a ErrWontSign
func IsErrWontSign(err error) bool {
_, ok := err.(*ErrWontSign)
return ok
}

// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ var migrations = []Migration{
NewMigration("Add owner_name on table repository", addOwnerNameOnRepository),
// v121 -> v122
NewMigration("add is_restricted column for users table", addIsRestricted),
// v122 -> v123
NewMigration("Add Require Signed Commits to ProtectedBranch", addRequireSignedCommits),
}

// Migrate database to current version
Expand Down
18 changes: 18 additions & 0 deletions models/migrations/v122.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2020 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 migrations

import (
"xorm.io/xorm"
)

func addRequireSignedCommits(x *xorm.Engine) error {

type ProtectedBranch struct {
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
}

return x.Sync2(new(ProtectedBranch))
}
18 changes: 10 additions & 8 deletions models/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,18 @@ func (pr *PullRequest) LoadProtectedBranch() (err error) {
}

func (pr *PullRequest) loadProtectedBranch(e Engine) (err error) {
if pr.BaseRepo == nil {
if pr.BaseRepoID == 0 {
return nil
}
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
if err != nil {
return
if pr.ProtectedBranch == nil {
if pr.BaseRepo == nil {
if pr.BaseRepoID == 0 {
return nil
}
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
if err != nil {
return
}
}
pr.ProtectedBranch, err = getProtectedBranchBy(e, pr.BaseRepo.ID, pr.BaseBranch)
}
pr.ProtectedBranch, err = getProtectedBranchBy(e, pr.BaseRepo.ID, pr.BaseBranch)
return
}

Expand Down
59 changes: 34 additions & 25 deletions models/pull_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import (
)

// 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) {
func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string, error) {
if err := pr.GetBaseRepo(); err != nil {
log.Error("Unable to get Base Repo for pull request")
return false, ""
return false, "", err
}
repo := pr.BaseRepo

signingKey := signingKey(repo.RepoPath())
if signingKey == "" {
return false, ""
return false, "", &ErrWontSign{noKey}
}
rules := signingModeFromStrings(setting.Repository.Signing.Merges)

Expand All @@ -30,92 +30,101 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st
for _, rule := range rules {
switch rule {
case never:
return false, ""
return false, "", &ErrWontSign{never}
case always:
break
case pubkey:
keys, err := ListGPGKeys(u.ID)
if err != nil || len(keys) == 0 {
return false, ""
if err != nil {
return false, "", err
}
if len(keys) == 0 {
return false, "", &ErrWontSign{pubkey}
}
case twofa:
twofa, err := GetTwoFactorByUID(u.ID)
if err != nil || twofa == nil {
return false, ""
twofaModel, err := GetTwoFactorByUID(u.ID)
if err != nil {
return false, "", err
}
if twofaModel == nil {
return false, "", &ErrWontSign{twofa}
}
case approved:
protectedBranch, err := GetProtectedBranchBy(repo.ID, pr.BaseBranch)
if err != nil || protectedBranch == nil {
return false, ""
if err != nil {
return false, "", err
}
if protectedBranch == nil {
return false, "", &ErrWontSign{approved}
}
if protectedBranch.GetGrantedApprovalsCount(pr) < 1 {
return false, ""
return false, "", &ErrWontSign{approved}
}
case baseSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(tmpBasePath)
if err != nil {
return false, ""
return false, "", err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(baseCommit)
if err != nil {
return false, ""
return false, "", err
}
verification := ParseCommitWithSignature(commit)
if !verification.Verified {
return false, ""
return false, "", &ErrWontSign{baseSigned}
}
case headSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(tmpBasePath)
if err != nil {
return false, ""
return false, "", err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(headCommit)
if err != nil {
return false, ""
return false, "", err
}
verification := ParseCommitWithSignature(commit)
if !verification.Verified {
return false, ""
return false, "", &ErrWontSign{headSigned}
}
case commitsSigned:
if gitRepo == nil {
gitRepo, err = git.OpenRepository(tmpBasePath)
if err != nil {
return false, ""
return false, "", err
}
defer gitRepo.Close()
}
commit, err := gitRepo.GetCommit(headCommit)
if err != nil {
return false, ""
return false, "", err
}
verification := ParseCommitWithSignature(commit)
if !verification.Verified {
return false, ""
return false, "", &ErrWontSign{commitsSigned}
}
// need to work out merge-base
mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
if err != nil {
return false, ""
return false, "", err
}
commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
if err != nil {
return false, ""
return false, "", err
}
for e := commitList.Front(); e != nil; e = e.Next() {
commit = e.Value.(*git.Commit)
verification := ParseCommitWithSignature(commit)
if !verification.Verified {
return false, ""
return false, "", &ErrWontSign{commitsSigned}
}
}
}
}
return true, signingKey
return true, signingKey, nil
}
Loading