1
1
mirror of https://github.com/go-gitea/gitea synced 2024-09-21 19:26:06 +00:00

Allow force pushes to protected branches

This commit is contained in:
Henry Goodman 2023-11-16 19:46:11 +11:00 committed by Henry Goodman
parent 6c7744370f
commit 50819b0424
11 changed files with 387 additions and 37 deletions

View File

@ -40,8 +40,13 @@ type ProtectedBranch struct {
EnableWhitelist bool EnableWhitelist bool
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"`
WhitelistDeployKeys 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"` 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"`
@ -142,6 +147,33 @@ func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, user *use
return in 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 // 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 { func IsUserMergeWhitelisted(ctx context.Context, protectBranch *ProtectedBranch, userID int64, permissionInRepo access_model.Permission) bool {
if !protectBranch.EnableMergeWhitelist { if !protectBranch.EnableMergeWhitelist {
@ -303,6 +335,9 @@ type WhitelistOptions struct {
UserIDs []int64 UserIDs []int64
TeamIDs []int64 TeamIDs []int64
ForcePushUserIDs []int64
ForcePushTeamIDs []int64
MergeUserIDs []int64 MergeUserIDs []int64
MergeTeamIDs []int64 MergeTeamIDs []int64
@ -330,6 +365,13 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
} }
protectBranch.WhitelistUserIDs = whitelist 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) whitelist, err = updateUserWhitelist(ctx, repo, protectBranch.MergeWhitelistUserIDs, opts.MergeUserIDs)
if err != nil { if err != nil {
return err return err
@ -349,6 +391,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
} }
protectBranch.WhitelistTeamIDs = whitelist 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) whitelist, err = updateTeamWhitelist(ctx, repo, protectBranch.MergeWhitelistTeamIDs, opts.MergeTeamIDs)
if err != nil { if err != nil {
return err 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 { func RemoveUserIDFromProtectedBranch(ctx context.Context, p *ProtectedBranch, userID int64) error {
lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistUserIDs), len(p.ApprovalsWhitelistUserIDs), len(p.MergeWhitelistUserIDs) lenIDs, lenApprovalIDs, lenMergeIDs := len(p.WhitelistUserIDs), len(p.ApprovalsWhitelistUserIDs), len(p.MergeWhitelistUserIDs)
p.WhitelistUserIDs = util.SliceRemoveAll(p.WhitelistUserIDs, userID) p.WhitelistUserIDs = util.SliceRemoveAll(p.WhitelistUserIDs, userID)
p.ForcePushWhitelistUserIDs = util.SliceRemoveAll(p.ForcePushWhitelistUserIDs, userID)
p.ApprovalsWhitelistUserIDs = util.SliceRemoveAll(p.ApprovalsWhitelistUserIDs, userID) p.ApprovalsWhitelistUserIDs = util.SliceRemoveAll(p.ApprovalsWhitelistUserIDs, userID)
p.MergeWhitelistUserIDs = util.SliceRemoveAll(p.MergeWhitelistUserIDs, 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) { lenMergeIDs != len(p.MergeWhitelistUserIDs) {
if _, err := db.GetEngine(ctx).ID(p.ID).Cols( if _, err := db.GetEngine(ctx).ID(p.ID).Cols(
"whitelist_user_i_ds", "whitelist_user_i_ds",
"force_push_whitelist_user_i_ds",
"merge_whitelist_user_i_ds", "merge_whitelist_user_i_ds",
"approvals_whitelist_user_i_ds", "approvals_whitelist_user_i_ds",
).Update(p); err != nil { ).Update(p); err != nil {
@ -502,6 +554,7 @@ func RemoveTeamIDFromProtectedBranch(ctx context.Context, p *ProtectedBranch, te
lenMergeIDs != len(p.MergeWhitelistTeamIDs) { lenMergeIDs != len(p.MergeWhitelistTeamIDs) {
if _, err := db.GetEngine(ctx).ID(p.ID).Cols( if _, err := db.GetEngine(ctx).ID(p.ID).Cols(
"whitelist_team_i_ds", "whitelist_team_i_ds",
"force_push_whitelist_team_i_ds",
"merge_whitelist_team_i_ds", "merge_whitelist_team_i_ds",
"approvals_whitelist_team_i_ds", "approvals_whitelist_team_i_ds",
).Update(p); err != nil { ).Update(p); err != nil {

View File

@ -30,6 +30,11 @@ type BranchProtection struct {
PushWhitelistUsernames []string `json:"push_whitelist_usernames"` PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"` PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"` 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"` EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"` MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"` MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
@ -62,6 +67,11 @@ type CreateBranchProtectionOption struct {
PushWhitelistUsernames []string `json:"push_whitelist_usernames"` PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"` PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys bool `json:"push_whitelist_deploy_keys"` 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"` EnableMergeWhitelist bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"` MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"` MergeWhitelistTeams []string `json:"merge_whitelist_teams"`
@ -87,6 +97,11 @@ type EditBranchProtectionOption struct {
PushWhitelistUsernames []string `json:"push_whitelist_usernames"` PushWhitelistUsernames []string `json:"push_whitelist_usernames"`
PushWhitelistTeams []string `json:"push_whitelist_teams"` PushWhitelistTeams []string `json:"push_whitelist_teams"`
PushWhitelistDeployKeys *bool `json:"push_whitelist_deploy_keys"` 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"` EnableMergeWhitelist *bool `json:"enable_merge_whitelist"`
MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"` MergeWhitelistUsernames []string `json:"merge_whitelist_usernames"`
MergeWhitelistTeams []string `json:"merge_whitelist_teams"` MergeWhitelistTeams []string `json:"merge_whitelist_teams"`

View File

@ -2185,6 +2185,7 @@ settings.event_wiki_desc = Wiki page created, renamed, edited or deleted.
settings.event_release = Release settings.event_release = Release
settings.event_release_desc = Release published, updated or deleted in a repository. settings.event_release_desc = Release published, updated or deleted in a repository.
settings.event_push = Push settings.event_push = Push
settings.event_force_push = Force Push
settings.event_push_desc = Git push to a repository. settings.event_push_desc = Git push to a repository.
settings.event_repository = Repository settings.event_repository = Repository
settings.event_repository_desc = Repository created or deleted. 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_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch.
settings.protect_disable_push = Disable Push settings.protect_disable_push = Disable Push
settings.protect_disable_push_desc = No pushing will be allowed to this branch. 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 = 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_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 = Enable Merge
settings.protect_enable_merge_desc = Anyone with write access will be allowed to merge the pull requests into this branch. 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 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_search_users = Search users…
settings.protect_whitelist_teams = Whitelisted teams for pushing: settings.protect_whitelist_teams = Whitelisted teams for pushing:
settings.protect_whitelist_search_teams = Search teams… 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 = 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_committers_desc = Allow only whitelisted users or teams to merge pull requests into this branch.
settings.protect_merge_whitelist_users = Whitelisted users for merging: settings.protect_merge_whitelist_users = Whitelisted users for merging:

View File

@ -552,6 +552,15 @@ func CreateBranchProtection(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err) ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
return 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) mergeWhitelistUsers, err := user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
@ -570,7 +579,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err) ctx.Error(http.StatusInternalServerError, "GetUserIDsByNames", err)
return return
} }
var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64 var whitelistTeams, forcePushWhitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
if repo.Owner.IsOrganization() { if repo.Owner.IsOrganization() {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false) whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
if err != nil { if err != nil {
@ -581,6 +590,15 @@ func CreateBranchProtection(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err) ctx.Error(http.StatusInternalServerError, "GetTeamIDsByNames", err)
return 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) mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil { if err != nil {
if organization.IsErrTeamNotExist(err) { if organization.IsErrTeamNotExist(err) {
@ -606,8 +624,11 @@ func CreateBranchProtection(ctx *context.APIContext) {
RuleName: ruleName, RuleName: ruleName,
CanPush: form.EnablePush, CanPush: form.EnablePush,
EnableWhitelist: form.EnablePush && form.EnablePushWhitelist, EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
EnableMergeWhitelist: form.EnableMergeWhitelist,
WhitelistDeployKeys: form.EnablePush && form.EnablePushWhitelist && form.PushWhitelistDeployKeys, 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, EnableStatusCheck: form.EnableStatusCheck,
StatusCheckContexts: form.StatusCheckContexts, StatusCheckContexts: form.StatusCheckContexts,
EnableApprovalsWhitelist: form.EnableApprovalsWhitelist, EnableApprovalsWhitelist: form.EnableApprovalsWhitelist,
@ -624,6 +645,8 @@ func CreateBranchProtection(ctx *context.APIContext) {
err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
UserIDs: whitelistUsers, UserIDs: whitelistUsers,
TeamIDs: whitelistTeams, TeamIDs: whitelistTeams,
ForcePushUserIDs: forcePushWhitelistUsers,
ForcePushTeamIDs: forcePushWhitelistTeams,
MergeUserIDs: mergeWhitelistUsers, MergeUserIDs: mergeWhitelistUsers,
MergeTeamIDs: mergeWhitelistTeams, MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers, 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 { if form.EnableMergeWhitelist != nil {
protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist protectBranch.EnableMergeWhitelist = *form.EnableMergeWhitelist
} }
@ -802,7 +846,7 @@ func EditBranchProtection(ctx *context.APIContext) {
protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch protectBranch.BlockOnOutdatedBranch = *form.BlockOnOutdatedBranch
} }
var whitelistUsers []int64 var whitelistUsers, forcePushWhitelistUsers, mergeWhitelistUsers, approvalsWhitelistUsers []int64
if form.PushWhitelistUsernames != nil { if form.PushWhitelistUsernames != nil {
whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false) whitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.PushWhitelistUsernames, false)
if err != nil { if err != nil {
@ -816,7 +860,19 @@ func EditBranchProtection(ctx *context.APIContext) {
} else { } else {
whitelistUsers = protectBranch.WhitelistUserIDs 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 { if form.MergeWhitelistUsernames != nil {
mergeWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false) mergeWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.MergeWhitelistUsernames, false)
if err != nil { if err != nil {
@ -830,7 +886,6 @@ func EditBranchProtection(ctx *context.APIContext) {
} else { } else {
mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs mergeWhitelistUsers = protectBranch.MergeWhitelistUserIDs
} }
var approvalsWhitelistUsers []int64
if form.ApprovalsWhitelistUsernames != nil { if form.ApprovalsWhitelistUsernames != nil {
approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false) approvalsWhitelistUsers, err = user_model.GetUserIDsByNames(ctx, form.ApprovalsWhitelistUsernames, false)
if err != nil { if err != nil {
@ -845,7 +900,7 @@ func EditBranchProtection(ctx *context.APIContext) {
approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs approvalsWhitelistUsers = protectBranch.ApprovalsWhitelistUserIDs
} }
var whitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64 var whitelistTeams, forcePushWhitelistTeams, mergeWhitelistTeams, approvalsWhitelistTeams []int64
if repo.Owner.IsOrganization() { if repo.Owner.IsOrganization() {
if form.PushWhitelistTeams != nil { if form.PushWhitelistTeams != nil {
whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false) whitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.PushWhitelistTeams, false)
@ -860,6 +915,19 @@ func EditBranchProtection(ctx *context.APIContext) {
} else { } else {
whitelistTeams = protectBranch.WhitelistTeamIDs 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 { if form.MergeWhitelistTeams != nil {
mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false) mergeWhitelistTeams, err = organization.GetTeamIDsByNames(ctx, repo.OwnerID, form.MergeWhitelistTeams, false)
if err != nil { if err != nil {
@ -891,6 +959,8 @@ func EditBranchProtection(ctx *context.APIContext) {
err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{ err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
UserIDs: whitelistUsers, UserIDs: whitelistUsers,
TeamIDs: whitelistTeams, TeamIDs: whitelistTeams,
ForcePushUserIDs: forcePushWhitelistUsers,
ForcePushTeamIDs: forcePushWhitelistTeams,
MergeUserIDs: mergeWhitelistUsers, MergeUserIDs: mergeWhitelistUsers,
MergeTeamIDs: mergeWhitelistTeams, MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers, ApprovalsUserIDs: approvalsWhitelistUsers,

View File

@ -182,7 +182,9 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
return 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 { 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}) 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 { if err != nil {
@ -192,12 +194,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
}) })
return return
} else if len(output) > 0 { } 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) log.Warn("Forbidden: Branch: %s in %-v is protected from force push", branchName, repo)
ctx.JSON(http.StatusForbidden, private.Response{ ctx.JSON(http.StatusForbidden, private.Response{
UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName), UserMsg: fmt.Sprintf("branch %s is protected from force push", branchName),
}) })
return 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 var canPush bool
if ctx.opts.DeployKeyID != 0 { 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) canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
}
} else { } else {
user, err := user_model.GetUserByID(ctx, ctx.opts.UserID) user, err := user_model.GetUserByID(ctx, ctx.opts.UserID)
if err != nil { if err != nil {
@ -257,8 +267,12 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
}) })
return return
} }
if isForcePush {
canPush = !changedProtectedfiles && protectBranch.CanUserForcePush(ctx, user)
} else {
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx, user) canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx, user)
} }
}
// 6. If we're not allowed to push directly // 6. If we're not allowed to push directly
if !canPush { 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 // 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) 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{ ctx.JSON(http.StatusForbidden, private.Response{
UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s", branchName), UserMsg: fmt.Sprintf("Not allowed to push to protected branch %s", branchName),

View File

@ -77,6 +77,7 @@ func SettingsProtectedBranch(c *context.Context) {
} }
c.Data["Users"] = users c.Data["Users"] = users
c.Data["whitelist_users"] = strings.Join(base.Int64sToStrings(rule.WhitelistUserIDs), ",") 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["merge_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistUserIDs), ",")
c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",") c.Data["approvals_whitelist_users"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistUserIDs), ",")
c.Data["status_check_contexts"] = strings.Join(rule.StatusCheckContexts, "\n") 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["Teams"] = teams
c.Data["whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.WhitelistTeamIDs), ",") 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["merge_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.MergeWhitelistTeamIDs), ",")
c.Data["approvals_whitelist_teams"] = strings.Join(base.Int64sToStrings(rule.ApprovalsWhitelistTeamIDs), ",") 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 protectBranch.RuleName = f.RuleName
if f.RequiredApprovals < 0 { if f.RequiredApprovals < 0 {
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min")) ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
@ -178,6 +180,27 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
protectBranch.WhitelistDeployKeys = false 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 protectBranch.EnableMergeWhitelist = f.EnableMergeWhitelist
if f.EnableMergeWhitelist { if f.EnableMergeWhitelist {
if strings.TrimSpace(f.MergeWhitelistUsers) != "" { 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{ err = git_model.UpdateProtectBranch(ctx, ctx.Repo.Repository, protectBranch, git_model.WhitelistOptions{
UserIDs: whitelistUsers, UserIDs: whitelistUsers,
TeamIDs: whitelistTeams, TeamIDs: whitelistTeams,
ForcePushUserIDs: forcePushWhitelistUsers,
ForcePushTeamIDs: forcePushWhitelistTeams,
MergeUserIDs: mergeWhitelistUsers, MergeUserIDs: mergeWhitelistUsers,
MergeTeamIDs: mergeWhitelistTeams, MergeTeamIDs: mergeWhitelistTeams,
ApprovalsUserIDs: approvalsWhitelistUsers, ApprovalsUserIDs: approvalsWhitelistUsers,

View File

@ -111,6 +111,10 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
if err != nil { if err != nil {
log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err) 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) mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs)
if err != nil { if err != nil {
log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err) log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err)
@ -123,6 +127,10 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
if err != nil { if err != nil {
log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err) 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) mergeWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.MergeWhitelistTeamIDs)
if err != nil { if err != nil {
log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err) log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err)
@ -145,6 +153,11 @@ func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api
PushWhitelistUsernames: pushWhitelistUsernames, PushWhitelistUsernames: pushWhitelistUsernames,
PushWhitelistTeams: pushWhitelistTeams, PushWhitelistTeams: pushWhitelistTeams,
PushWhitelistDeployKeys: bp.WhitelistDeployKeys, PushWhitelistDeployKeys: bp.WhitelistDeployKeys,
EnableForcePush: bp.CanForcePush,
EnableForcePushWhitelist: bp.EnableForcePushWhitelist,
ForcePushWhitelistUsernames: forcePushWhitelistUsernames,
ForcePushWhitelistTeams: forcePushWhitelistTeams,
ForcePushWhitelistDeployKeys: bp.ForcePushWhitelistDeployKeys,
EnableMergeWhitelist: bp.EnableMergeWhitelist, EnableMergeWhitelist: bp.EnableMergeWhitelist,
MergeWhitelistUsernames: mergeWhitelistUsernames, MergeWhitelistUsernames: mergeWhitelistUsernames,
MergeWhitelistTeams: mergeWhitelistTeams, MergeWhitelistTeams: mergeWhitelistTeams,

View File

@ -198,6 +198,10 @@ type ProtectBranchForm struct {
WhitelistUsers string WhitelistUsers string
WhitelistTeams string WhitelistTeams string
WhitelistDeployKeys bool WhitelistDeployKeys bool
EnableForcePush string
ForcePushWhitelistUsers string
ForcePushWhitelistTeams string
ForcePushWhitelistDeployKeys bool
EnableMergeWhitelist bool EnableMergeWhitelist bool
MergeWhitelistUsers string MergeWhitelistUsers string
MergeWhitelistTeams string MergeWhitelistTeams string

View File

@ -116,8 +116,6 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
return false, false, err 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 { if err := pr.LoadBaseRepo(ctx); err != nil {
return false, false, err 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) log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err)
return false, false, 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 { if pb != nil {
pb.Repo = pull.BaseRepo pb.Repo = pull.BaseRepo
if !pb.CanUserPush(ctx, user) { if !pb.CanUserForcePush(ctx, user) {
return false, false, nil rebaseAllowed = false
} }
} }

View File

@ -94,6 +94,69 @@
<p class="help">{{ctx.Locale.Tr "repo.settings.require_signed_commits_desc"}}</p> <p class="help">{{ctx.Locale.Tr "repo.settings.require_signed_commits_desc"}}</p>
</div> </div>
</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> <h5 class="ui dividing header">{{ctx.Locale.Tr "repo.settings.event_pull_request_approvals"}}</h5>
<div class="field"> <div class="field">
<label>{{ctx.Locale.Tr "repo.settings.protect_required_approvals"}}</label> <label>{{ctx.Locale.Tr "repo.settings.protect_required_approvals"}}</label>

View File

@ -16926,6 +16926,14 @@
"type": "boolean", "type": "boolean",
"x-go-name": "EnablePushWhitelist" "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": { "enable_status_check": {
"type": "boolean", "type": "boolean",
"x-go-name": "EnableStatusCheck" "x-go-name": "EnableStatusCheck"
@ -16966,6 +16974,24 @@
}, },
"x-go-name": "PushWhitelistUsernames" "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": { "require_signed_commits": {
"type": "boolean", "type": "boolean",
"x-go-name": "RequireSignedCommits" "x-go-name": "RequireSignedCommits"
@ -17567,6 +17593,14 @@
"type": "boolean", "type": "boolean",
"x-go-name": "EnablePushWhitelist" "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": { "enable_status_check": {
"type": "boolean", "type": "boolean",
"x-go-name": "EnableStatusCheck" "x-go-name": "EnableStatusCheck"
@ -17607,6 +17641,24 @@
}, },
"x-go-name": "PushWhitelistUsernames" "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": { "require_signed_commits": {
"type": "boolean", "type": "boolean",
"x-go-name": "RequireSignedCommits" "x-go-name": "RequireSignedCommits"
@ -18699,6 +18751,14 @@
"type": "boolean", "type": "boolean",
"x-go-name": "EnablePushWhitelist" "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": { "enable_status_check": {
"type": "boolean", "type": "boolean",
"x-go-name": "EnableStatusCheck" "x-go-name": "EnableStatusCheck"
@ -18739,6 +18799,24 @@
}, },
"x-go-name": "PushWhitelistUsernames" "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": { "require_signed_commits": {
"type": "boolean", "type": "boolean",
"x-go-name": "RequireSignedCommits" "x-go-name": "RequireSignedCommits"