diff options
author | zeripath <art27@cantab.net> | 2020-01-15 08:32:57 +0000 |
---|---|---|
committer | Antoine GIRARD <sapk@users.noreply.github.com> | 2020-01-15 09:32:57 +0100 |
commit | 66ee9b87f9aaabef836ec72bfaf8032b359b29c1 (patch) | |
tree | b6d134fb5ccc83c4b7ddad6a0eb6206496cc8b76 /models | |
parent | 6b1fa1235904947187266789b204f19bc03872be (diff) | |
download | gitea-66ee9b87f9aaabef836ec72bfaf8032b359b29c1.tar.gz gitea-66ee9b87f9aaabef836ec72bfaf8032b359b29c1.zip |
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>
Diffstat (limited to 'models')
-rw-r--r-- | models/branches.go | 1 | ||||
-rw-r--r-- | models/error.go | 16 | ||||
-rw-r--r-- | models/migrations/migrations.go | 2 | ||||
-rw-r--r-- | models/migrations/v122.go | 18 | ||||
-rw-r--r-- | models/pull.go | 18 | ||||
-rw-r--r-- | models/pull_sign.go | 59 | ||||
-rw-r--r-- | models/repo_sign.go | 89 |
7 files changed, 135 insertions, 68 deletions
diff --git a/models/branches.go b/models/branches.go index b6398f5694..75f5c0a3a7 100644 --- a/models/branches.go +++ b/models/branches.go @@ -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"` diff --git a/models/error.go b/models/error.go index f0d5699aad..fe9af70f3a 100644 --- a/models/error.go +++ b/models/error.go @@ -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 +} + // __________ .__ // \______ \____________ ____ ____ | |__ // | | _/\_ __ \__ \ / \_/ ___\| | \ diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 6bdec1dfba..edea36cf79 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -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 diff --git a/models/migrations/v122.go b/models/migrations/v122.go new file mode 100644 index 0000000000..e28adc1d82 --- /dev/null +++ b/models/migrations/v122.go @@ -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)) +} diff --git a/models/pull.go b/models/pull.go index 0435311e4e..1edd890035 100644 --- a/models/pull.go +++ b/models/pull.go @@ -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 } diff --git a/models/pull_sign.go b/models/pull_sign.go index 19d8907c3d..1d3474abe7 100644 --- a/models/pull_sign.go +++ b/models/pull_sign.go @@ -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) @@ -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 } diff --git a/models/repo_sign.go b/models/repo_sign.go index a684efb55f..64f70ac7bd 100644 --- a/models/repo_sign.go +++ b/models/repo_sign.go @@ -25,6 +25,7 @@ const ( headSigned signingMode = "headsigned" commitsSigned signingMode = "commitssigned" approved signingMode = "approved" + noKey signingMode = "nokey" ) func signingModeFromStrings(modeStrings []string) []signingMode { @@ -95,122 +96,140 @@ func PublicSigningKey(repoPath string) (string, error) { } // SignInitialCommit determines if we should sign the initial commit to this repository -func SignInitialCommit(repoPath string, u *User) (bool, string) { +func SignInitialCommit(repoPath string, u *User) (bool, string, error) { rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit) signingKey := signingKey(repoPath) if signingKey == "" { - return false, "" + return false, "", &ErrWontSign{noKey} } 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} } } } - return true, signingKey + return true, signingKey, nil } // SignWikiCommit determines if we should sign the commits to this repository wiki -func (repo *Repository) SignWikiCommit(u *User) (bool, string) { +func (repo *Repository) SignWikiCommit(u *User) (bool, string, error) { rules := signingModeFromStrings(setting.Repository.Signing.Wiki) signingKey := signingKey(repo.WikiPath()) if signingKey == "" { - return false, "" + return false, "", &ErrWontSign{noKey} } 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 parentSigned: gitRepo, err := git.OpenRepository(repo.WikiPath()) if err != nil { - return false, "" + return false, "", err } defer gitRepo.Close() commit, err := gitRepo.GetCommit("HEAD") if err != nil { - return false, "" + return false, "", err } if commit.Signature == nil { - return false, "" + return false, "", &ErrWontSign{parentSigned} } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "" + return false, "", &ErrWontSign{parentSigned} } } } - return true, signingKey + return true, signingKey, nil } // SignCRUDAction determines if we should sign a CRUD commit to this repository -func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string) { +func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string, error) { rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions) signingKey := signingKey(repo.RepoPath()) if signingKey == "" { - return false, "" + return false, "", &ErrWontSign{noKey} } 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 parentSigned: gitRepo, err := git.OpenRepository(tmpBasePath) if err != nil { - return false, "" + return false, "", err } defer gitRepo.Close() commit, err := gitRepo.GetCommit(parentCommit) if err != nil { - return false, "" + return false, "", err } if commit.Signature == nil { - return false, "" + return false, "", &ErrWontSign{parentSigned} } verification := ParseCommitWithSignature(commit) if !verification.Verified { - return false, "" + return false, "", &ErrWontSign{parentSigned} } } } - return true, signingKey + return true, signingKey, nil } |