Skip to content

WIP: Add require signed commits feature #8584

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -41,6 +41,7 @@ type ProtectedBranch struct {
ApprovalsWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
ApprovalsWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
RequiredApprovals int64 `xorm:"NOT NULL DEFAULT 0"`
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ var migrations = []Migration{
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
// v102 -> v103
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
// v103 -> v104
NewMigration("add require signed commits on protected branch", addRequireSignedCommitsOnProtectedBranch),
}

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

import (
"xorm.io/xorm"
)

func addRequireSignedCommitsOnProtectedBranch(x *xorm.Engine) error {
type ProtectedBranch struct {
ID int64 `xorm:"pk autoincr"`
RequireSignedCommits bool `xorm:"NOT NULL DEFAULT false"`
}

return x.Sync2(new(ProtectedBranch))
}
3 changes: 2 additions & 1 deletion modules/auth/repo_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ type ProtectBranchForm struct {
EnableMergeWhitelist bool
MergeWhitelistUsers string
MergeWhitelistTeams string
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
EnableStatusCheck bool
StatusCheckContexts []string
RequiredApprovals int64
ApprovalsWhitelistUsers string
ApprovalsWhitelistTeams string
RequireSignedCommits bool
}

// Validate validates the fields
Expand Down
2 changes: 2 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,8 @@ settings.protect_merge_whitelist_teams = Whitelisted teams for merging:
settings.protect_check_status_contexts = Enable Status Check
settings.protect_check_status_contexts_desc = Require status checks to pass before merging Choose which status checks must pass before branches can be merged into a branch that matches this rule. When enabled, commits must first be pushed to another branch, then merged or pushed directly to a branch that matches this rule after status checks have passed. If no contexts are selected, the last commit must be successful regardless of context.
settings.protect_check_status_contexts_list = Status checks found in the last week for this repository
settings.requrie_signed_commits = Require signed commits
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
settings.requrie_signed_commits = Require signed commits
settings.require_signed_commits = Require signed commits

settings.requrie_signed_commits_desc = Commits pushed to this branch must have verified signatures.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
settings.requrie_signed_commits_desc = Commits pushed to this branch must have verified signatures.
settings.require_signed_commits_desc = Commits pushed to this branch must have verified signatures.

settings.protect_required_approvals = Required approvals:
settings.protect_required_approvals_desc = Allow only to merge pull request with enough positive reviews of whitelisted users or teams.
settings.protect_approvals_whitelist_users = Whitelisted reviewers:
Expand Down
43 changes: 43 additions & 0 deletions routers/private/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package private

import (
"bytes"
"fmt"
"net/http"
"os"
Expand Down Expand Up @@ -119,6 +120,48 @@ func HookPreReceive(ctx *macaron.Context) {
})
return
}

// check signed commits
if protectBranch.RequireSignedCommits {
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
log.Error("Unable to find the git repository %s/%s: %v", ownerName, repoName, err)
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": fmt.Sprintf("Unable to find the git repository %s/%s", ownerName, repoName),
})
return
}

stdout, err := git.NewCommand("rev-list", oldCommitID+"..."+newCommitID).RunInDirBytes(repo.RepoPath())
Copy link
Contributor

@zeripath zeripath Oct 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In pre-receive the new commits and objects are not placed directly into the object tree (depending on your git version of course). They are placed in to Quarantine and/or various other places.

You need to set the environment for any git commands to tell it to look in those directories for objects too.

env := os.Environ()
if gitAlternativeObjectDirectories != "" {
env = append(env,
private.GitAlternativeObjectDirectories+"="+gitAlternativeObjectDirectories)
}
if gitObjectDirectory != "" {
env = append(env,
private.GitObjectDirectory+"="+gitObjectDirectory)
}
if gitQuarantinePath != "" {
env = append(env,
private.GitQuarantinePath+"="+gitQuarantinePath)
}
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
if err != nil {

I wouldn't use OpenRepository either just use the git repo commands directly. If you want to use gogit commands I'm fairly certain you can set its environs similarly.

if err != nil {
log.Error("Unable to get commit via id %v : %v", newCommitID, err)
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": fmt.Sprintf("Unable to get commit via id %v", newCommitID),
})
return
}

parts := bytes.Split(stdout, []byte{'\n'})
for _, commitID := range parts {
commit, err := gitRepo.GetCommit(string(commitID))
if err != nil {
log.Error("Unable to get commit via id %v : %v", newCommitID, err)
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err": fmt.Sprintf("Unable to get commit via id %v", newCommitID),
})
return
}

v := models.ParseCommitWithSignature(commit)
if !v.Verified {
log.Warn("protected branch %s require signed commits, but %v isn't signed", branchName, commit.ID)
ctx.JSON(http.StatusForbidden, map[string]interface{}{
"err": fmt.Sprintf("protected branch %s require signed commits, but %v isn't signed", branchName, commit.ID),
})
return
}
}
}
}
ctx.PlainText(http.StatusOK, []byte("ok"))
}
Expand Down
2 changes: 2 additions & 0 deletions routers/repo/setting_protected_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
if f.RequiredApprovals < 0 {
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
return
}

var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
Expand All @@ -213,6 +214,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)

protectBranch.EnableStatusCheck = f.EnableStatusCheck
protectBranch.StatusCheckContexts = f.StatusCheckContexts
protectBranch.RequireSignedCommits = f.RequireSignedCommits

protectBranch.RequiredApprovals = f.RequiredApprovals
if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
Expand Down
8 changes: 8 additions & 0 deletions templates/repo/settings/protected_branch.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@
</div>
</div>

<div class="field">
<div class="ui checkbox">
<input class="require-signedcommits" name="require_signed_commits" type="checkbox" {{if .Branch.RequireSignedCommits}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.requrie_signed_commits"}}</label>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<label>{{.i18n.Tr "repo.settings.requrie_signed_commits"}}</label>
<label>{{.i18n.Tr "repo.settings.require_signed_commits"}}</label>

<p class="help">{{.i18n.Tr "repo.settings.requrie_signed_commits_desc"}}</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<p class="help">{{.i18n.Tr "repo.settings.requrie_signed_commits_desc"}}</p>
<p class="help">{{.i18n.Tr "repo.settings.require_signed_commits_desc"}}</p>

</div>
</div>

<div class="field">
<label for="required-approvals">{{.i18n.Tr "repo.settings.protect_required_approvals"}}</label>
<input name="required_approvals" id="required-approvals" type="number" value="{{.Branch.RequiredApprovals}}">
Expand Down