@@ -44,6 +44,11 @@ type ProtectedBranch struct {
4444 WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
4545 MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
4646 MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
47+ CanForcePush bool `xorm:"NOT NULL DEFAULT false"`
48+ EnableForcePushAllowlist bool `xorm:"NOT NULL DEFAULT false"`
49+ ForcePushAllowlistUserIDs []int64 `xorm:"JSON TEXT"`
50+ ForcePushAllowlistTeamIDs []int64 `xorm:"JSON TEXT"`
51+ ForcePushAllowlistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
4752 EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
4853 StatusCheckContexts []string `xorm:"JSON TEXT"`
4954 EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
@@ -143,6 +148,33 @@ func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, user *use
143148 return in
144149}
145150
151+ // CanUserForcePush returns if some user could force push to this protected branch
152+ // Since force-push extends normal push, we also check if user has regular push access
153+ func (protectBranch * ProtectedBranch ) CanUserForcePush (ctx context.Context , user * user_model.User ) bool {
154+ if ! protectBranch .CanForcePush {
155+ return false
156+ }
157+
158+ if ! protectBranch .EnableForcePushAllowlist {
159+ return protectBranch .CanUserPush (ctx , user )
160+ }
161+
162+ if slices .Contains (protectBranch .ForcePushAllowlistUserIDs , user .ID ) {
163+ return protectBranch .CanUserPush (ctx , user )
164+ }
165+
166+ if len (protectBranch .ForcePushAllowlistTeamIDs ) == 0 {
167+ return false
168+ }
169+
170+ in , err := organization .IsUserInTeams (ctx , user .ID , protectBranch .ForcePushAllowlistTeamIDs )
171+ if err != nil {
172+ log .Error ("IsUserInTeams: %v" , err )
173+ return false
174+ }
175+ return in && protectBranch .CanUserPush (ctx , user )
176+ }
177+
146178// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
147179func IsUserMergeWhitelisted (ctx context.Context , protectBranch * ProtectedBranch , userID int64 , permissionInRepo access_model.Permission ) bool {
148180 if ! protectBranch .EnableMergeWhitelist {
@@ -301,6 +333,9 @@ type WhitelistOptions struct {
301333 UserIDs []int64
302334 TeamIDs []int64
303335
336+ ForcePushUserIDs []int64
337+ ForcePushTeamIDs []int64
338+
304339 MergeUserIDs []int64
305340 MergeTeamIDs []int64
306341
@@ -328,6 +363,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
328363 }
329364 protectBranch .WhitelistUserIDs = whitelist
330365
366+ whitelist , err = updateUserWhitelist (ctx , repo , protectBranch .ForcePushAllowlistUserIDs , opts .ForcePushUserIDs )
367+ if err != nil {
368+ return err
369+ }
370+ protectBranch .ForcePushAllowlistUserIDs = whitelist
371+
331372 whitelist , err = updateUserWhitelist (ctx , repo , protectBranch .MergeWhitelistUserIDs , opts .MergeUserIDs )
332373 if err != nil {
333374 return err
@@ -347,6 +388,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
347388 }
348389 protectBranch .WhitelistTeamIDs = whitelist
349390
391+ whitelist , err = updateTeamWhitelist (ctx , repo , protectBranch .ForcePushAllowlistTeamIDs , opts .ForcePushTeamIDs )
392+ if err != nil {
393+ return err
394+ }
395+ protectBranch .ForcePushAllowlistTeamIDs = whitelist
396+
350397 whitelist , err = updateTeamWhitelist (ctx , repo , protectBranch .MergeWhitelistTeamIDs , opts .MergeTeamIDs )
351398 if err != nil {
352399 return err
@@ -468,43 +515,58 @@ func DeleteProtectedBranch(ctx context.Context, repo *repo_model.Repository, id
468515 return nil
469516}
470517
471- // RemoveUserIDFromProtectedBranch remove all user ids from protected branch options
472- func RemoveUserIDFromProtectedBranch (ctx context.Context , p * ProtectedBranch , userID int64 ) error {
473- lenIDs , lenApprovalIDs , lenMergeIDs := len (p .WhitelistUserIDs ), len (p .ApprovalsWhitelistUserIDs ), len (p .MergeWhitelistUserIDs )
474- p .WhitelistUserIDs = util .SliceRemoveAll (p .WhitelistUserIDs , userID )
475- p .ApprovalsWhitelistUserIDs = util .SliceRemoveAll (p .ApprovalsWhitelistUserIDs , userID )
476- p .MergeWhitelistUserIDs = util .SliceRemoveAll (p .MergeWhitelistUserIDs , userID )
477-
478- if lenIDs != len (p .WhitelistUserIDs ) || lenApprovalIDs != len (p .ApprovalsWhitelistUserIDs ) ||
479- lenMergeIDs != len (p .MergeWhitelistUserIDs ) {
480- if _ , err := db .GetEngine (ctx ).ID (p .ID ).Cols (
481- "whitelist_user_i_ds" ,
482- "merge_whitelist_user_i_ds" ,
483- "approvals_whitelist_user_i_ds" ,
484- ).Update (p ); err != nil {
518+ // removeIDsFromProtectedBranch is a helper function to remove IDs from protected branch options
519+ func removeIDsFromProtectedBranch (ctx context.Context , p * ProtectedBranch , userID , teamID int64 , columnNames []string ) error {
520+ lenUserIDs , lenForcePushIDs , lenApprovalIDs , lenMergeIDs := len (p .WhitelistUserIDs ), len (p .ForcePushAllowlistUserIDs ), len (p .ApprovalsWhitelistUserIDs ), len (p .MergeWhitelistUserIDs )
521+ lenTeamIDs , lenForcePushTeamIDs , lenApprovalTeamIDs , lenMergeTeamIDs := len (p .WhitelistTeamIDs ), len (p .ForcePushAllowlistTeamIDs ), len (p .ApprovalsWhitelistTeamIDs ), len (p .MergeWhitelistTeamIDs )
522+
523+ if userID > 0 {
524+ p .WhitelistUserIDs = util .SliceRemoveAll (p .WhitelistUserIDs , userID )
525+ p .ForcePushAllowlistUserIDs = util .SliceRemoveAll (p .ForcePushAllowlistUserIDs , userID )
526+ p .ApprovalsWhitelistUserIDs = util .SliceRemoveAll (p .ApprovalsWhitelistUserIDs , userID )
527+ p .MergeWhitelistUserIDs = util .SliceRemoveAll (p .MergeWhitelistUserIDs , userID )
528+ }
529+
530+ if teamID > 0 {
531+ p .WhitelistTeamIDs = util .SliceRemoveAll (p .WhitelistTeamIDs , teamID )
532+ p .ForcePushAllowlistTeamIDs = util .SliceRemoveAll (p .ForcePushAllowlistTeamIDs , teamID )
533+ p .ApprovalsWhitelistTeamIDs = util .SliceRemoveAll (p .ApprovalsWhitelistTeamIDs , teamID )
534+ p .MergeWhitelistTeamIDs = util .SliceRemoveAll (p .MergeWhitelistTeamIDs , teamID )
535+ }
536+
537+ if (lenUserIDs != len (p .WhitelistUserIDs ) ||
538+ lenForcePushIDs != len (p .ForcePushAllowlistUserIDs ) ||
539+ lenApprovalIDs != len (p .ApprovalsWhitelistUserIDs ) ||
540+ lenMergeIDs != len (p .MergeWhitelistUserIDs )) ||
541+ (lenTeamIDs != len (p .WhitelistTeamIDs ) ||
542+ lenForcePushTeamIDs != len (p .ForcePushAllowlistTeamIDs ) ||
543+ lenApprovalTeamIDs != len (p .ApprovalsWhitelistTeamIDs ) ||
544+ lenMergeTeamIDs != len (p .MergeWhitelistTeamIDs )) {
545+ if _ , err := db .GetEngine (ctx ).ID (p .ID ).Cols (columnNames ... ).Update (p ); err != nil {
485546 return fmt .Errorf ("updateProtectedBranches: %v" , err )
486547 }
487548 }
488549 return nil
489550}
490551
491- // RemoveTeamIDFromProtectedBranch remove all team ids from protected branch options
552+ // RemoveUserIDFromProtectedBranch removes all user ids from protected branch options
553+ func RemoveUserIDFromProtectedBranch (ctx context.Context , p * ProtectedBranch , userID int64 ) error {
554+ columnNames := []string {
555+ "whitelist_user_i_ds" ,
556+ "force_push_allowlist_user_i_ds" ,
557+ "merge_whitelist_user_i_ds" ,
558+ "approvals_whitelist_user_i_ds" ,
559+ }
560+ return removeIDsFromProtectedBranch (ctx , p , userID , 0 , columnNames )
561+ }
562+
563+ // RemoveTeamIDFromProtectedBranch removes all team ids from protected branch options
492564func RemoveTeamIDFromProtectedBranch (ctx context.Context , p * ProtectedBranch , teamID int64 ) error {
493- lenIDs , lenApprovalIDs , lenMergeIDs := len (p .WhitelistTeamIDs ), len (p .ApprovalsWhitelistTeamIDs ), len (p .MergeWhitelistTeamIDs )
494- p .WhitelistTeamIDs = util .SliceRemoveAll (p .WhitelistTeamIDs , teamID )
495- p .ApprovalsWhitelistTeamIDs = util .SliceRemoveAll (p .ApprovalsWhitelistTeamIDs , teamID )
496- p .MergeWhitelistTeamIDs = util .SliceRemoveAll (p .MergeWhitelistTeamIDs , teamID )
497-
498- if lenIDs != len (p .WhitelistTeamIDs ) ||
499- lenApprovalIDs != len (p .ApprovalsWhitelistTeamIDs ) ||
500- lenMergeIDs != len (p .MergeWhitelistTeamIDs ) {
501- if _ , err := db .GetEngine (ctx ).ID (p .ID ).Cols (
502- "whitelist_team_i_ds" ,
503- "merge_whitelist_team_i_ds" ,
504- "approvals_whitelist_team_i_ds" ,
505- ).Update (p ); err != nil {
506- return fmt .Errorf ("updateProtectedBranches: %v" , err )
507- }
565+ columnNames := []string {
566+ "whitelist_team_i_ds" ,
567+ "force_push_allowlist_team_i_ds" ,
568+ "merge_whitelist_team_i_ds" ,
569+ "approvals_whitelist_team_i_ds" ,
508570 }
509- return nil
571+ return removeIDsFromProtectedBranch ( ctx , p , 0 , teamID , columnNames )
510572}
0 commit comments