Add an option to protected branches to add writing deploy keys to the whitelist for pushing. Please note this is technically a breaking change: previously if the owner of a repository was on the whitelist then any writing deploy key was effectively on the whitelist. This option will now need to be set if that is desired. Closes #8472 Details: * Allow Protected Branches to Whitelist Deploy Keys * Add migration * Ensure that IsDeployKey is set to false on the http pushes * add not null default falsetags/v1.11.0-rc1
@@ -66,6 +66,7 @@ func runHookPreReceive(c *cli.Context) error { | |||
reponame := os.Getenv(models.EnvRepoName) | |||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64) | |||
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64) | |||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey)) | |||
buf := bytes.NewBuffer(nil) | |||
scanner := bufio.NewScanner(os.Stdin) | |||
@@ -98,6 +99,7 @@ func runHookPreReceive(c *cli.Context) error { | |||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory), | |||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath), | |||
ProtectedBranchID: prID, | |||
IsDeployKey: isDeployKey, | |||
}) | |||
switch statusCode { | |||
case http.StatusInternalServerError: |
@@ -191,6 +191,8 @@ func runServ(c *cli.Context) error { | |||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10)) | |||
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10)) | |||
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0)) | |||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey)) | |||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID)) | |||
//LFS token authentication | |||
if verb == lfsAuthenticateVerb { |
@@ -34,6 +34,7 @@ type ProtectedBranch struct { | |||
WhitelistUserIDs []int64 `xorm:"JSON TEXT"` | |||
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"` | |||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"` | |||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` | |||
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"` | |||
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"` | |||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"` |
@@ -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 WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches), | |||
} | |||
// Migrate database to current version |
@@ -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 addWhitelistDeployKeysToBranches(x *xorm.Engine) error { | |||
type ProtectedBranch struct { | |||
ID int64 | |||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"` | |||
} | |||
return x.Sync2(new(ProtectedBranch)) | |||
} |
@@ -22,6 +22,8 @@ const ( | |||
EnvPusherName = "GITEA_PUSHER_NAME" | |||
EnvPusherEmail = "GITEA_PUSHER_EMAIL" | |||
EnvPusherID = "GITEA_PUSHER_ID" | |||
EnvKeyID = "GITEA_KEY_ID" | |||
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY" | |||
) | |||
// CommitToPushCommit transforms a git.Commit to PushCommit type. |
@@ -152,6 +152,7 @@ type ProtectBranchForm struct { | |||
EnableWhitelist bool | |||
WhitelistUsers string | |||
WhitelistTeams string | |||
WhitelistDeployKeys bool | |||
EnableMergeWhitelist bool | |||
MergeWhitelistUsers string | |||
MergeWhitelistTeams string |
@@ -31,11 +31,12 @@ type HookOptions struct { | |||
GitAlternativeObjectDirectories string | |||
GitQuarantinePath string | |||
ProtectedBranchID int64 | |||
IsDeployKey bool | |||
} | |||
// HookPreReceive check whether the provided commits are allowed | |||
func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) { | |||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d", | |||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d&isDeployKey=%t", | |||
url.PathEscape(ownerName), | |||
url.PathEscape(repoName), | |||
url.QueryEscape(opts.OldCommitID), | |||
@@ -46,6 +47,7 @@ func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) | |||
url.QueryEscape(opts.GitAlternativeObjectDirectories), | |||
url.QueryEscape(opts.GitQuarantinePath), | |||
opts.ProtectedBranchID, | |||
opts.IsDeployKey, | |||
) | |||
resp, err := newInternalRequest(reqURL, "GET").Response() |
@@ -1334,6 +1334,7 @@ settings.protect_this_branch = Enable Branch Protection | |||
settings.protect_this_branch_desc = Prevent deletion and disable any Git pushing to the branch. | |||
settings.protect_whitelist_committers = Enable Push Whitelist | |||
settings.protect_whitelist_committers_desc = Allow whitelisted users or teams to push to this branch (but not force push). | |||
settings.protect_whitelist_deploy_keys = Whitelist deploy keys with write access to push | |||
settings.protect_whitelist_users = Whitelisted users for pushing: | |||
settings.protect_whitelist_search_users = Search users… | |||
settings.protect_whitelist_teams = Whitelisted teams for pushing: |
@@ -33,6 +33,7 @@ func HookPreReceive(ctx *macaron.Context) { | |||
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories") | |||
gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath") | |||
prID := ctx.QueryInt64("prID") | |||
isDeployKey := ctx.QueryBool("isDeployKey") | |||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix) | |||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) | |||
@@ -95,7 +96,12 @@ func HookPreReceive(ctx *macaron.Context) { | |||
} | |||
} | |||
canPush := protectBranch.CanUserPush(userID) | |||
canPush := false | |||
if isDeployKey { | |||
canPush = protectBranch.WhitelistDeployKeys | |||
} else { | |||
canPush = protectBranch.CanUserPush(userID) | |||
} | |||
if !canPush && prID > 0 { | |||
pr, err := models.GetPullRequestByID(prID) | |||
if err != nil { |
@@ -263,6 +263,7 @@ func HTTP(ctx *context.Context) { | |||
models.EnvPusherName + "=" + authUser.Name, | |||
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID), | |||
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID), | |||
models.EnvIsDeployKey + "=false", | |||
} | |||
if !authUser.KeepEmailPrivate { |
@@ -213,6 +213,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm) | |||
protectBranch.EnableStatusCheck = f.EnableStatusCheck | |||
protectBranch.StatusCheckContexts = f.StatusCheckContexts | |||
protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys | |||
protectBranch.RequiredApprovals = f.RequiredApprovals | |||
if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" { |
@@ -59,6 +59,13 @@ | |||
</div> | |||
</div> | |||
{{end}} | |||
<br> | |||
<div class="whitelist field"> | |||
<div class="ui checkbox"> | |||
<input type="checkbox" name="whitelist_deploy_keys" {{if .Branch.WhitelistDeployKeys}}checked{{end}}> | |||
<label for="whitelist_deploy_keys">{{.i18n.Tr "repo.settings.protect_whitelist_deploy_keys"}}</label> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="field"> |