Allow Protected Branches to Whitelist Deploy Keys (#8483)
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 false
This commit is contained in:
parent
b1c1e1549b
commit
0bfe5eb10b
13 changed files with 48 additions and 2 deletions
|
@ -66,6 +66,7 @@ func runHookPreReceive(c *cli.Context) error {
|
||||||
reponame := os.Getenv(models.EnvRepoName)
|
reponame := os.Getenv(models.EnvRepoName)
|
||||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
|
||||||
|
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
@ -98,6 +99,7 @@ func runHookPreReceive(c *cli.Context) error {
|
||||||
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
|
||||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||||
ProtectedBranchID: prID,
|
ProtectedBranchID: prID,
|
||||||
|
IsDeployKey: isDeployKey,
|
||||||
})
|
})
|
||||||
switch statusCode {
|
switch statusCode {
|
||||||
case http.StatusInternalServerError:
|
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.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||||
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
|
os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||||
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
|
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
|
//LFS token authentication
|
||||||
if verb == lfsAuthenticateVerb {
|
if verb == lfsAuthenticateVerb {
|
||||||
|
|
|
@ -34,6 +34,7 @@ type ProtectedBranch struct {
|
||||||
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||||
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
|
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||||
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
|
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
|
||||||
|
|
|
@ -260,6 +260,8 @@ var migrations = []Migration{
|
||||||
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
|
NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
|
||||||
// v102 -> v103
|
// v102 -> v103
|
||||||
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
|
NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
|
||||||
|
// v103 -> v104
|
||||||
|
NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Migrate database to current version
|
// Migrate database to current version
|
||||||
|
|
18
models/migrations/v103.go
Normal file
18
models/migrations/v103.go
Normal file
|
@ -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"
|
EnvPusherName = "GITEA_PUSHER_NAME"
|
||||||
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
||||||
EnvPusherID = "GITEA_PUSHER_ID"
|
EnvPusherID = "GITEA_PUSHER_ID"
|
||||||
|
EnvKeyID = "GITEA_KEY_ID"
|
||||||
|
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||||
|
|
|
@ -152,6 +152,7 @@ type ProtectBranchForm struct {
|
||||||
EnableWhitelist bool
|
EnableWhitelist bool
|
||||||
WhitelistUsers string
|
WhitelistUsers string
|
||||||
WhitelistTeams string
|
WhitelistTeams string
|
||||||
|
WhitelistDeployKeys bool
|
||||||
EnableMergeWhitelist bool
|
EnableMergeWhitelist bool
|
||||||
MergeWhitelistUsers string
|
MergeWhitelistUsers string
|
||||||
MergeWhitelistTeams string
|
MergeWhitelistTeams string
|
||||||
|
|
|
@ -31,11 +31,12 @@ type HookOptions struct {
|
||||||
GitAlternativeObjectDirectories string
|
GitAlternativeObjectDirectories string
|
||||||
GitQuarantinePath string
|
GitQuarantinePath string
|
||||||
ProtectedBranchID int64
|
ProtectedBranchID int64
|
||||||
|
IsDeployKey bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HookPreReceive check whether the provided commits are allowed
|
// HookPreReceive check whether the provided commits are allowed
|
||||||
func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) {
|
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(ownerName),
|
||||||
url.PathEscape(repoName),
|
url.PathEscape(repoName),
|
||||||
url.QueryEscape(opts.OldCommitID),
|
url.QueryEscape(opts.OldCommitID),
|
||||||
|
@ -46,6 +47,7 @@ func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string)
|
||||||
url.QueryEscape(opts.GitAlternativeObjectDirectories),
|
url.QueryEscape(opts.GitAlternativeObjectDirectories),
|
||||||
url.QueryEscape(opts.GitQuarantinePath),
|
url.QueryEscape(opts.GitQuarantinePath),
|
||||||
opts.ProtectedBranchID,
|
opts.ProtectedBranchID,
|
||||||
|
opts.IsDeployKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
resp, err := newInternalRequest(reqURL, "GET").Response()
|
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_this_branch_desc = Prevent deletion and disable any Git pushing to the branch.
|
||||||
settings.protect_whitelist_committers = Enable Push Whitelist
|
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_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_users = Whitelisted users for pushing:
|
||||||
settings.protect_whitelist_search_users = Search users…
|
settings.protect_whitelist_search_users = Search users…
|
||||||
settings.protect_whitelist_teams = Whitelisted teams for pushing:
|
settings.protect_whitelist_teams = Whitelisted teams for pushing:
|
||||||
|
|
|
@ -33,6 +33,7 @@ func HookPreReceive(ctx *macaron.Context) {
|
||||||
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
|
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
|
||||||
gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
|
gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
|
||||||
prID := ctx.QueryInt64("prID")
|
prID := ctx.QueryInt64("prID")
|
||||||
|
isDeployKey := ctx.QueryBool("isDeployKey")
|
||||||
|
|
||||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
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 {
|
if !canPush && prID > 0 {
|
||||||
pr, err := models.GetPullRequestByID(prID)
|
pr, err := models.GetPullRequestByID(prID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -263,6 +263,7 @@ func HTTP(ctx *context.Context) {
|
||||||
models.EnvPusherName + "=" + authUser.Name,
|
models.EnvPusherName + "=" + authUser.Name,
|
||||||
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
|
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
|
||||||
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
|
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
|
||||||
|
models.EnvIsDeployKey + "=false",
|
||||||
}
|
}
|
||||||
|
|
||||||
if !authUser.KeepEmailPrivate {
|
if !authUser.KeepEmailPrivate {
|
||||||
|
|
|
@ -213,6 +213,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
|
||||||
|
|
||||||
protectBranch.EnableStatusCheck = f.EnableStatusCheck
|
protectBranch.EnableStatusCheck = f.EnableStatusCheck
|
||||||
protectBranch.StatusCheckContexts = f.StatusCheckContexts
|
protectBranch.StatusCheckContexts = f.StatusCheckContexts
|
||||||
|
protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys
|
||||||
|
|
||||||
protectBranch.RequiredApprovals = f.RequiredApprovals
|
protectBranch.RequiredApprovals = f.RequiredApprovals
|
||||||
if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
|
if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
|
||||||
|
|
|
@ -59,6 +59,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{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>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
|
Reference in a new issue