mirror of
https://github.com/go-gitea/gitea
synced 2024-09-21 11:16:03 +00:00
Allow force pushes to protected branches
This commit is contained in:
parent
6c7744370f
commit
50819b0424
@ -40,8 +40,13 @@ type ProtectedBranch struct {
|
||||
EnableWhitelist bool
|
||||
WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
||||
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CanForcePush bool `xorm:"NOT NULL DEFAULT false"`
|
||||
EnableForcePushWhitelist bool
|
||||
ForcePushWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
ForcePushWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
ForcePushWhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
|
||||
EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
|
||||
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
|
||||
MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
|
||||
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
|
||||
@ -142,6 +147,33 @@ func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, user *use
|
||||
return in
|
||||
}
|
||||
|
||||
// CanUserForcePush returns if some user could force push to this protected branch
|
||||
// Since force-push extends normal push, we also check if user has regular push access
|
||||
func (protectBranch *ProtectedBranch) CanUserForcePush(ctx context.Context, user *user_model.User) bool {
|
||||
if !protectBranch.CanForcePush {
|
||||
return false
|
||||
}
|
||||
|
||||
if !protectBranch.EnableForcePushWhitelist {
|
||||
return protectBranch.CanUserPush(ctx, user)
|
||||
}
|
||||
|
||||
if base.Int64sContains(protectBranch.ForcePushWhitelistUserIDs, user.ID) {
|
||||
return protectBranch.CanUserPush(ctx, user)
|
||||
}
|
||||
|
||||
if len(protectBranch.ForcePushWhitelistTeamIDs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
in, err := organization.IsUserInTeams(ctx, user.ID, protectBranch.ForcePushWhitelistTeamIDs)
|
||||
if err != nil {
|
||||
log.Error("IsUserInTeams: %v", err)
|
||||
return false
|
||||
}
|
||||
return in && protectBranch.CanUserPush(ctx, user)
|
||||
}
|
||||
|
||||
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
|
||||
func IsUserMergeWhitelisted(ctx context.Context, protectBranch *ProtectedBranch, userID int64, permissionInRepo access_model.Permission) bool {
|
||||
if !protectBranch.EnableMergeWhitelist {
|
||||
@ -303,6 +335,9 @@ type WhitelistOptions struct {
|
||||
UserIDs []int64
|
||||
TeamIDs []int64
|
||||
|
||||
ForcePushUserIDs []int64
|
||||
ForcePushTeamIDs []int64
|
||||
|
||||
MergeUserIDs []int64
|
||||
MergeTeamIDs []int64
|
||||
|
||||
@ -330,6 +365,13 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
|
||||
}
|
||||
protectBranch.WhitelistUserIDs = whitelist
|
||||
|
||||
whitelist, err = updateUserWhitelist(ctx, repo, protectBranch.ForcePushWhitelistUserIDs, opts.ForcePushUserIDs)
|
||||
log.Info("%v", whitelist, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
protectBranch.ForcePushWhitelistUserIDs = whitelist
|
||||
|
||||
whitelist, err = updateUserWhitelist(ctx, repo, protectBranch.MergeWhitelistUserIDs, opts.MergeUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -349,6 +391,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
|
||||
}
|
||||
protectBranch.WhitelistTeamIDs = whitelist
|
||||
|
||||
whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.ForcePushWhitelistTeamIDs, opts.ForcePushTeamIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
protectBranch.ForcePushWhitelistTeamIDs = whitelist
|
||||
|
||||
whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.MergeWhitelistTeamIDs, opts.MergeTeamIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -474,13 +522,17 @@ func DeleteProtectedBranch(ctx context.Context, repo *repo_model.Repository, id
|
||||
func RemoveUserIDFromProtectedBranch(ctx context.Context, p *ProtectedBranch, userID int64) error {
|
||||
lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistUserIDs), len(p.ApprovalsWhitelistUserIDs), len(p.MergeWhitelistUserIDs)
|
||||
p.WhitelistUserIDs = util.SliceRemoveAll(p.WhitelistUserIDs, userID)
|
||||
p.ForcePushWhitelistUserIDs = util.SliceRemoveAll(p.ForcePushWhitelistUserIDs, userID)
|
||||
p.ApprovalsWhitelistUserIDs = util.SliceRemoveAll(p.ApprovalsWhitelistUserIDs, userID)
|
||||
p.MergeWhitelistUserIDs = util.SliceRemoveAll(p.MergeWhitelistUserIDs, userID)
|
||||
|
||||
if lenIDs != len(p.WhitelistUserIDs) || lenApprovalIDs != len(p.ApprovalsWhitelistUserIDs) ||
|
||||
if lenIDs != len(p.WhitelistUserIDs) ||
|
||||
lenApprovalIDs != len(p.ForcePushWhitelistUserIDs) ||
|
||||
lenApprovalIDs != len(p.ApprovalsWhitelistUserIDs) ||
|
||||
lenMergeIDs != len(p.MergeWhitelistUserIDs) {
|
||||
if _, err := db.GetEngine(ctx).ID(p.ID).Cols(
|
||||
"whitelist_user_i_ds",
|
||||
"force_push_whitelist_user_i_ds",
|
||||
"merge_whitelist_user_i_ds",
|
||||
"approvals_whitelist_user_i_ds",
|
||||
).Update(p); err != nil {
|
||||
@ -502,6 +554,7 @@ func RemoveTeamIDFromProtectedBranch(ctx context.Context, p *ProtectedBranch, te
|
||||
lenMergeIDs != len(p.MergeWhitelistTeamIDs) {
|
||||
if _, err := db.GetEngine(ctx).ID(p.ID).Cols(
|
||||
"whitelist_team_i_ds",
|
||||
"force_push_whitelist_team_i_ds",
|
||||
"merge_whitelist_team_i_ds",
|
||||
"approvals_whitelist_team_i_ds",
|
||||
).Update(p); err != nil {
|
||||
|
@ -30,6 +30,11 @@ type BranchProtection struct {
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableForcePush bool `json:"enable_force_push"`
|
||||
EnableForcePushWhitelist bool `json:"enable_force_push_whitelist"`
|
||||
ForcePushWhitelistUsernames []string `json:"force_push_whitelist_usernames"`
|
||||
ForcePushWhitelistTeams []string `json:"force_push_whitelist_teams"`
|
||||
ForcePushWhitelistDeployKeys bool `json:"force_push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
@ -62,6 +67,11 @@ type CreateBranchProtectionOption struct {
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableForcePush bool `json:"enable_force_push"`
|
||||
EnableForcePushWhitelist bool `json:"enable_force_push_whitelist"`
|
||||
ForcePushWhitelistUsernames []string `json:"force_push_whitelist_usernames"`
|
||||
ForcePushWhitelistTeams []string `json:"force_push_whitelist_teams"`
|
||||
ForcePushWhitelistDeployKeys bool `json:"force_push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
@ -87,6 +97,11 @@ type EditBranchProtectionOption struct {
|
||||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
|
||||
PushWhitelistTeams []string `json:"push_whitelist_teams"`
|
||||
PushWhitelistDeployKeys *bool `json:"push_whitelist_deploy_keys"`
|
||||
EnableForcePush *bool `json:"enable_force_push"`
|
||||
EnableForcePushWhitelist *bool `json:"enable_force_push_whitelist"`
|
||||
ForcePushWhitelistUsernames []string `json:"force_push_whitelist_usernames"`
|
||||
ForcePushWhitelistTeams []string `json:"force_push_whitelist_teams"`
|
||||
ForcePushWhitelistDeployKeys *bool `json:"force_push_whitelist_deploy_keys"`
|
||||
EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
|
||||
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
|
||||
MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
|
||||
|
@ -2185,6 +2185,7 @@ settings.event_wiki_desc = Wiki page created, renamed, edited or deleted.
|
||||
settings.event_release = Release
|
||||
settings.event_release_desc = Release published, updated or deleted in a repository.
|
||||
settings.event_push = Push
|
||||
settings.event_force_push = Force Push
|
||||
settings.event_push_desc = Git push to a repository.
|
||||
settings.event_repository = Repository
|
||||
settings.event_repository_desc = Repository created or deleted.
|
||||
@ -2278,8 +2279,14 @@ settings.protect_this_branch = Enable Branch Protection
|
||||
settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch.
|
||||
settings.protect_disable_push = Disable Push
|
||||
settings.protect_disable_push_desc = No pushing will be allowed to this branch.
|
||||
settings.protect_disable_force_push = Disable Force Push
|
||||
settings.protect_disable_force_push_desc = No force pushing will be allowed to this branch.
|
||||
settings.protect_enable_push = Enable Push
|
||||
settings.protect_enable_push_desc = Anyone with write access will be allowed to push to this branch (but not force push).
|
||||
settings.protect_enable_force_push_all = Everyone
|
||||
settings.protect_enable_force_push_all_desc = Anyone with push access will be allowed to force push to this branch.
|
||||
settings.protect_enable_force_push_whitelist = Whitelist Restricted Force Push
|
||||
settings.protect_enable_force_push_whitelist_desc = Only whitelisted users or teams with push access will be allowed to force push to this branch.
|
||||
settings.protect_enable_merge = Enable Merge
|
||||
settings.protect_enable_merge_desc = Anyone with write access will be allowed to merge the pull requests into this branch.
|
||||
settings.protect_whitelist_committers = Whitelist Restricted Push
|
||||
@ -2289,6 +2296,9 @@ settings.protect_whitelist_users = Whitelisted users for pushing:
|
||||
settings.protect_whitelist_search_users = Search users…
|
||||
settings.protect_whitelist_teams = Whitelisted teams for pushing:
|
||||
settings.protect_whitelist_search_teams = Search teams…
|
||||
settings.protect_force_push_whitelist_users = Whitelisted users for force pushing:
|
||||
settings.protect_force_push_whitelist_teams = Whitelisted teams for force pushing:
|
||||
settings.protect_force_push_whitelist_deploy_keys = Whitelist deploy keys with write access to push.
|
||||
settings.protect_merge_whitelist_committers = Enable Merge Whitelist
|
||||
settings.protect_merge_whitelist_committers_desc = Allow only whitelisted users or teams to merge pull requests into this branch.
|
||||
settings.protect_merge_whitelist_users = Whitelisted users for merging:
|
||||
|
@ -552,6 +552,15 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
|
||||
return
|
||||
}
|
||||
forcePushWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.ForcePushWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
|
||||
return
|
||||
}
|
||||
mergeWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
@ -570,7 +579,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
|
||||
return
|
||||
}
|
||||
var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
|
||||
var whitelistTeams, forcePushWhitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
|
||||
if repo.Owner.IsOrganization() {
|
||||
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
|
||||
if err != nil {
|
||||
@ -581,6 +590,15 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
|
||||
return
|
||||
}
|
||||
forcePushWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ForcePushWhitelistTeams, false)
|
||||
if err != nil {
|
||||
if organization.IsErrTeamNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
|
||||
return
|
||||
}
|
||||
mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
|
||||
if err != nil {
|
||||
if organization.IsErrTeamNotExist(err) {
|
||||
@ -606,8 +624,11 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
RuleName: ruleName,
|
||||
CanPush: form.EnablePush,
|
||||
EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
|
||||
EnableMergeWhitelist: form.EnableMergeWhitelist,
|
||||
WhitelistDeployKeys: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys,
|
||||
CanForcePush: form.EnablePush && form.EnableForcePush,
|
||||
EnableForcePushWhitelist: form.EnablePush && form.EnableForcePush && form.EnableForcePushWhitelist,
|
||||
ForcePushWhitelistDeployKeys: form.EnablePush && form.EnableForcePush && form.EnableForcePushWhitelist && form.ForcePushWhitelistDeployKeys,
|
||||
EnableMergeWhitelist: form.EnableMergeWhitelist,
|
||||
EnableStatusCheck: form.EnableStatusCheck,
|
||||
StatusCheckContexts: form.StatusCheckContexts,
|
||||
EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
|
||||
@ -624,6 +645,8 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
|
||||
UserIDs: whitelistUsers,
|
||||
TeamIDs: whitelistTeams,
|
||||
ForcePushUserIDs: forcePushWhitelistUsers,
|
||||
ForcePushTeamIDs: forcePushWhitelistTeams,
|
||||
MergeUserIDs: mergeWhitelistUsers,
|
||||
MergeTeamIDs: mergeWhitelistTeams,
|
||||
ApprovalsUserIDs: approvalsWhitelistUsers,
|
||||
@ -754,6 +777,27 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
if form.EnableForcePush != nil {
|
||||
if !*form.EnableForcePush {
|
||||
protectBranch.CanForcePush = false
|
||||
protectBranch.EnableForcePushWhitelist = false
|
||||
protectBranch.ForcePushWhitelistDeployKeys = false
|
||||
} else {
|
||||
protectBranch.CanForcePush = true
|
||||
if form.EnableForcePushWhitelist != nil {
|
||||
if !*form.EnableForcePushWhitelist {
|
||||
protectBranch.EnableForcePushWhitelist = false
|
||||
protectBranch.ForcePushWhitelistDeployKeys = false
|
||||
} else {
|
||||
protectBranch.EnableForcePushWhitelist = true
|
||||
if form.ForcePushWhitelistDeployKeys != nil {
|
||||
protectBranch.ForcePushWhitelistDeployKeys = *form.ForcePushWhitelistDeployKeys
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if form.EnableMergeWhitelist != nil {
|
||||
protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
|
||||
}
|
||||
@ -802,7 +846,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch
|
||||
}
|
||||
|
||||
var whitelistUsers []int64
|
||||
var whitelistUsers, forcePushWhitelistUsers, mergeWhitelistUsers, approvalsWhitelistUsers []int64
|
||||
if form.PushWhitelistUsernames != nil {
|
||||
whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
@ -816,7 +860,19 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
} else {
|
||||
whitelistUsers = protectBranch.WhitelistUserIDs
|
||||
}
|
||||
var mergeWhitelistUsers []int64
|
||||
if form.ForcePushWhitelistUsernames != nil {
|
||||
forcePushWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ForcePushWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "User does not exist", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
forcePushWhitelistUsers = protectBranch.ForcePushWhitelistUserIDs
|
||||
}
|
||||
if form.MergeWhitelistUsernames != nil {
|
||||
mergeWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
@ -830,7 +886,6 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
} else {
|
||||
mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
|
||||
}
|
||||
var approvalsWhitelistUsers []int64
|
||||
if form.ApprovalsWhitelistUsernames != nil {
|
||||
approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
|
||||
if err != nil {
|
||||
@ -845,7 +900,7 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
|
||||
}
|
||||
|
||||
var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
|
||||
var whitelistTeams, forcePushWhitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
|
||||
if repo.Owner.IsOrganization() {
|
||||
if form.PushWhitelistTeams != nil {
|
||||
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
|
||||
@ -860,6 +915,19 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
} else {
|
||||
whitelistTeams = protectBranch.WhitelistTeamIDs
|
||||
}
|
||||
if form.ForcePushWhitelistTeams != nil {
|
||||
forcePushWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.ForcePushWhitelistTeams, false)
|
||||
if err != nil {
|
||||
if organization.IsErrTeamNotExist(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Team does not exist", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
forcePushWhitelistTeams = protectBranch.ForcePushWhitelistTeamIDs
|
||||
}
|
||||
if form.MergeWhitelistTeams != nil {
|
||||
mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
|
||||
if err != nil {
|
||||
@ -891,6 +959,8 @@ func EditBranchProtection(ctx *context.APIContext) {
|
||||
err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
|
||||
UserIDs: whitelistUsers,
|
||||
TeamIDs: whitelistTeams,
|
||||
ForcePushUserIDs: forcePushWhitelistUsers,
|
||||
ForcePushTeamIDs: forcePushWhitelistTeams,
|
||||
MergeUserIDs: mergeWhitelistUsers,
|
||||
MergeTeamIDs: mergeWhitelistTeams,
|
||||
ApprovalsUserIDs: approvalsWhitelistUsers,
|
||||
|
@ -182,7 +182,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
||||
return
|
||||
}
|
||||
|
||||
// 2. Disallow force pushes to protected branches
|
||||
isForcePush := false
|
||||
|
||||
// 2. Disallow force pushes to protected branches if the option is unchecked
|
||||
if git.EmptySHA != oldCommitID {
|
||||
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(oldCommitID, "^"+newCommitID).RunStdString(&git.RunOpts{Dir: repo.RepoPath(), Env: ctx.env})
|
||||
if err != nil {
|
||||
@ -192,12 +194,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
||||
})
|
||||
return
|
||||
} else if len(output) > 0 {
|
||||
if protectBranch.CanForcePush {
|
||||
isForcePush = true
|
||||
} else {
|
||||
log.Warn("Forbidden: Branch: %s in %-v is protected from force push", branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, private.Response{
|
||||
UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName),
|
||||
})
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,10 +249,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Check if the doer is allowed to push
|
||||
// 5. Check if the doer is allowed to push (and force-push if the incoming push is a force-push)
|
||||
var canPush bool
|
||||
if ctx.opts.DeployKeyID != 0 {
|
||||
// This flag is only ever true if protectBranch.CanForcePush is true
|
||||
if isForcePush {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableForcePushWhitelist || protectBranch.ForcePushWhitelistDeployKeys)
|
||||
} else {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
|
||||
}
|
||||
} else {
|
||||
user, err := user_model.GetUserByID(ctx, ctx.opts.UserID)
|
||||
if err != nil {
|
||||
@ -257,8 +267,12 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
||||
})
|
||||
return
|
||||
}
|
||||
if isForcePush {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanUserForcePush(ctx, user)
|
||||
} else {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx, user)
|
||||
}
|
||||
}
|
||||
|
||||
// 6. If we're not allowed to push directly
|
||||
if !canPush {
|
||||
@ -293,6 +307,13 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
||||
}
|
||||
|
||||
// Or we're simply not able to push to this protected branch
|
||||
if isForcePush {
|
||||
log.Warn("Forbidden: User %d is not allowed to force-push to protected branch: %s in %-v", ctx.opts.UserID, branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, private.Response{
|
||||
UserMsg: fmt.Sprintf("Not allowued to force-push to protected branch %s", branchName),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v", ctx.opts.UserID, branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, private.Response{
|
||||
UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s", branchName),
|
||||
|
@ -77,6 +77,7 @@ func SettingsProtectedBranch(c *context.Context) {
|
||||
}
|
||||
c.Data["Users"] = users
|
||||
c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",")
|
||||
c.Data["force_push_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ForcePushWhitelistUserIDs), ",")
|
||||
c.Data["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",")
|
||||
c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",")
|
||||
c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n")
|
||||
@ -91,6 +92,7 @@ func SettingsProtectedBranch(c *context.Context) {
|
||||
}
|
||||
c.Data["Teams"] = teams
|
||||
c.Data["whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.WhitelistTeamIDs), ",")
|
||||
c.Data["force_push_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.ForcePushWhitelistTeamIDs), ",")
|
||||
c.Data["merge_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistTeamIDs), ",")
|
||||
c.Data["approvals_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistTeamIDs), ",")
|
||||
}
|
||||
@ -149,7 +151,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
|
||||
var whitelistUsers, whitelistTeams, forcePushWhitelistUsers, forcePushWhitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
|
||||
protectBranch.RuleName = f.RuleName
|
||||
if f.RequiredApprovals < 0 {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
|
||||
@ -178,6 +180,27 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
protectBranch.WhitelistDeployKeys = false
|
||||
}
|
||||
|
||||
switch f.EnableForcePush {
|
||||
case "all":
|
||||
protectBranch.CanForcePush = true
|
||||
protectBranch.EnableForcePushWhitelist = false
|
||||
protectBranch.ForcePushWhitelistDeployKeys = false
|
||||
case "whitelist":
|
||||
protectBranch.CanForcePush = true
|
||||
protectBranch.EnableForcePushWhitelist = true
|
||||
protectBranch.ForcePushWhitelistDeployKeys = f.ForcePushWhitelistDeployKeys
|
||||
if strings.TrimSpace(f.ForcePushWhitelistUsers) != "" {
|
||||
forcePushWhitelistUsers, _ = base.StringsToInt64s(strings.Split(f.ForcePushWhitelistUsers, ","))
|
||||
}
|
||||
if strings.TrimSpace(f.ForcePushWhitelistTeams) != "" {
|
||||
forcePushWhitelistTeams, _ = base.StringsToInt64s(strings.Split(f.ForcePushWhitelistTeams, ","))
|
||||
}
|
||||
default:
|
||||
protectBranch.CanForcePush = false
|
||||
protectBranch.EnableForcePushWhitelist = false
|
||||
protectBranch.ForcePushWhitelistDeployKeys = false
|
||||
}
|
||||
|
||||
protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist
|
||||
if f.EnableMergeWhitelist {
|
||||
if strings.TrimSpace(f.MergeWhitelistUsers) != "" {
|
||||
@ -236,6 +259,8 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
|
||||
err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
|
||||
UserIDs: whitelistUsers,
|
||||
TeamIDs: whitelistTeams,
|
||||
ForcePushUserIDs: forcePushWhitelistUsers,
|
||||
ForcePushTeamIDs: forcePushWhitelistTeams,
|
||||
MergeUserIDs: mergeWhitelistUsers,
|
||||
MergeTeamIDs: mergeWhitelistTeams,
|
||||
ApprovalsUserIDs: approvalsWhitelistUsers,
|
||||
|
@ -111,6 +111,10 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
|
||||
if err != nil {
|
||||
log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err)
|
||||
}
|
||||
forcePushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ForcePushWhitelistUserIDs)
|
||||
if err != nil {
|
||||
log.Error("GetUserNamesByIDs (ForcePushWhitelistUserIDs): %v", err)
|
||||
}
|
||||
mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs)
|
||||
if err != nil {
|
||||
log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
|
||||
@ -123,6 +127,10 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
|
||||
if err != nil {
|
||||
log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err)
|
||||
}
|
||||
forcePushWhitelistTeams, err := user_model.GetUserNamesByIDs(ctx, bp.ForcePushWhitelistTeamIDs)
|
||||
if err != nil {
|
||||
log.Error("GetUserNamesByIDs (ForcePushWhitelistTeamIDs): %v", err)
|
||||
}
|
||||
mergeWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.MergeWhitelistTeamIDs)
|
||||
if err != nil {
|
||||
log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
|
||||
@ -145,6 +153,11 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
|
||||
PushWhitelistUsernames: pushWhitelistUsernames,
|
||||
PushWhitelistTeams: pushWhitelistTeams,
|
||||
PushWhitelistDeployKeys: bp.WhitelistDeployKeys,
|
||||
EnableForcePush: bp.CanForcePush,
|
||||
EnableForcePushWhitelist: bp.EnableForcePushWhitelist,
|
||||
ForcePushWhitelistUsernames: forcePushWhitelistUsernames,
|
||||
ForcePushWhitelistTeams: forcePushWhitelistTeams,
|
||||
ForcePushWhitelistDeployKeys: bp.ForcePushWhitelistDeployKeys,
|
||||
EnableMergeWhitelist: bp.EnableMergeWhitelist,
|
||||
MergeWhitelistUsernames: mergeWhitelistUsernames,
|
||||
MergeWhitelistTeams: mergeWhitelistTeams,
|
||||
|
@ -198,6 +198,10 @@ type ProtectBranchForm struct {
|
||||
WhitelistUsers string
|
||||
WhitelistTeams string
|
||||
WhitelistDeployKeys bool
|
||||
EnableForcePush string
|
||||
ForcePushWhitelistUsers string
|
||||
ForcePushWhitelistTeams string
|
||||
ForcePushWhitelistDeployKeys bool
|
||||
EnableMergeWhitelist bool
|
||||
MergeWhitelistUsers string
|
||||
MergeWhitelistTeams string
|
||||
|
@ -116,8 +116,6 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
// can't do rebase on protected branch because need force push
|
||||
if pb == nil {
|
||||
if err := pr.LoadBaseRepo(ctx); err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@ -129,14 +127,14 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
|
||||
log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
|
||||
return false, false, err
|
||||
}
|
||||
rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
|
||||
}
|
||||
|
||||
// Update function need push permission
|
||||
rebaseAllowed = prUnit.PullRequestsConfig().AllowRebaseUpdate
|
||||
|
||||
// If branch protected, disable rebase unless user is whitelisted to force push (which extends regular push)
|
||||
if pb != nil {
|
||||
pb.Repo = pull.BaseRepo
|
||||
if !pb.CanUserPush(ctx, user) {
|
||||
return false, false, nil
|
||||
if !pb.CanUserForcePush(ctx, user) {
|
||||
rebaseAllowed = false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,69 @@
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.require_signed_commits_desc"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.event_force_push"}}</h5>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="enable_force_push" value="none" class="toggle-target-disabled" data-target="#force_push_whitelist_box" {{if not .Rule.CanForcePush}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_disable_force_push"}}</label>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.protect_disable_force_push_desc"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="enable_force_push" value="all" class="toggle-target-disabled" data-target="#force_push_whitelist_box" {{if and (.Rule.CanForcePush) (not .Rule.EnableForcePushWhitelist)}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_enable_force_push_all"}}</label>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.protect_enable_force_push_all_desc"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grouped fields">
|
||||
<div class="field">
|
||||
<div class="ui radio checkbox">
|
||||
<input type="radio" name="enable_force_push" value="whitelist" class="toggle-target-enabled" data-target="#force_push_whitelist_box" {{if and (.Rule.CanForcePush) (.Rule.EnableForcePushWhitelist)}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_enable_force_push_whitelist"}}</label>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.protect_enable_force_push_whitelist_desc"}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="force_push_whitelist_box" class="grouped fields {{if not .Rule.EnableForcePushWhitelist}}disabled{{end}}">
|
||||
<div class="checkbox-sub-item field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_force_push_whitelist_users"}}</label>
|
||||
<div class="ui multiple search selection dropdown">
|
||||
<input type="hidden" name="force_push_whitelist_users" value="{{.force_push_whitelist_users}}">
|
||||
<div class="default text">{{ctx.Locale.Tr "repo.settings.protect_whitelist_search_users"}}</div>
|
||||
<div class="menu">
|
||||
{{range .Users}}
|
||||
<div class="item" data-value="{{.ID}}">
|
||||
{{ctx.AvatarUtils.Avatar . 28 "mini"}}{{template "repo/search_name" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{if .Owner.IsOrganization}}
|
||||
<div class="checkbox-sub-item field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_force_push_whitelist_teams"}}</label>
|
||||
<div class="ui multiple search selection dropdown">
|
||||
<input type="hidden" name="force_push_whitelist_teams" value="{{.force_push_whitelist_teams}}">
|
||||
<div class="default text">{{ctx.Locale.Tr "repo.settings.protect_whitelist_search_teams"}}</div>
|
||||
<div class="menu">
|
||||
{{range .Teams}}
|
||||
<div class="item" data-value="{{.ID}}">
|
||||
{{svg "octicon-people"}}
|
||||
{{.Name}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="checkbox-sub-item field">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" name="force_push_whitelist_deploy_keys" {{if .Rule.ForcePushWhitelistDeployKeys}}checked{{end}}>
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_force_push_whitelist_deploy_keys"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.event_pull_request_approvals"}}</h5>
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "repo.settings.protect_required_approvals"}}</label>
|
||||
|
78
templates/swagger/v1_json.tmpl
generated
78
templates/swagger/v1_json.tmpl
generated
@ -16926,6 +16926,14 @@
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnablePushWhitelist"
|
||||
},
|
||||
"enable_force_push": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePush"
|
||||
},
|
||||
"enable_force_push_whitelist": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePushWhitelist"
|
||||
},
|
||||
"enable_status_check": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableStatusCheck"
|
||||
@ -16966,6 +16974,24 @@
|
||||
},
|
||||
"x-go-name": "PushWhitelistUsernames"
|
||||
},
|
||||
"force_push_whitelist_deploy_keys": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "ForcePushWhitelistDeployKeys"
|
||||
},
|
||||
"force_push_whitelist_teams": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistTeams"
|
||||
},
|
||||
"force_push_whitelist_usernames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistUsernames"
|
||||
},
|
||||
"require_signed_commits": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "RequireSignedCommits"
|
||||
@ -17567,6 +17593,14 @@
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnablePushWhitelist"
|
||||
},
|
||||
"enable_force_push": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePush"
|
||||
},
|
||||
"enable_force_push_whitelist": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePushWhitelist"
|
||||
},
|
||||
"enable_status_check": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableStatusCheck"
|
||||
@ -17607,6 +17641,24 @@
|
||||
},
|
||||
"x-go-name": "PushWhitelistUsernames"
|
||||
},
|
||||
"force_push_whitelist_deploy_keys": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "ForcePushWhitelistDeployKeys"
|
||||
},
|
||||
"force_push_whitelist_teams": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistTeams"
|
||||
},
|
||||
"force_push_whitelist_usernames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistUsernames"
|
||||
},
|
||||
"require_signed_commits": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "RequireSignedCommits"
|
||||
@ -18699,6 +18751,14 @@
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnablePushWhitelist"
|
||||
},
|
||||
"enable_force_push": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePush"
|
||||
},
|
||||
"enable_force_push_whitelist": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableForcePushWhitelist"
|
||||
},
|
||||
"enable_status_check": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnableStatusCheck"
|
||||
@ -18739,6 +18799,24 @@
|
||||
},
|
||||
"x-go-name": "PushWhitelistUsernames"
|
||||
},
|
||||
"force_push_whitelist_deploy_keys": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "ForcePushWhitelistDeployKeys"
|
||||
},
|
||||
"force_push_whitelist_teams": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistTeams"
|
||||
},
|
||||
"force_push_whitelist_usernames": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"x-go-name": "ForcePushWhitelistUsernames"
|
||||
},
|
||||
"require_signed_commits": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "RequireSignedCommits"
|
||||
|
Loading…
Reference in New Issue
Block a user