@@ -44,6 +44,11 @@ type ProtectedBranch struct {
44
44
WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
45
45
MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
46
46
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"`
47
52
EnableStatusCheck bool `xorm:"NOT NULL DEFAULT false"`
48
53
StatusCheckContexts []string `xorm:"JSON TEXT"`
49
54
EnableApprovalsWhitelist bool `xorm:"NOT NULL DEFAULT false"`
@@ -143,6 +148,33 @@ func (protectBranch *ProtectedBranch) CanUserPush(ctx context.Context, user *use
143
148
return in
144
149
}
145
150
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
+
146
178
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
147
179
func IsUserMergeWhitelisted (ctx context.Context , protectBranch * ProtectedBranch , userID int64 , permissionInRepo access_model.Permission ) bool {
148
180
if ! protectBranch .EnableMergeWhitelist {
@@ -301,6 +333,9 @@ type WhitelistOptions struct {
301
333
UserIDs []int64
302
334
TeamIDs []int64
303
335
336
+ ForcePushUserIDs []int64
337
+ ForcePushTeamIDs []int64
338
+
304
339
MergeUserIDs []int64
305
340
MergeTeamIDs []int64
306
341
@@ -328,6 +363,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
328
363
}
329
364
protectBranch .WhitelistUserIDs = whitelist
330
365
366
+ whitelist , err = updateUserWhitelist (ctx , repo , protectBranch .ForcePushAllowlistUserIDs , opts .ForcePushUserIDs )
367
+ if err != nil {
368
+ return err
369
+ }
370
+ protectBranch .ForcePushAllowlistUserIDs = whitelist
371
+
331
372
whitelist , err = updateUserWhitelist (ctx , repo , protectBranch .MergeWhitelistUserIDs , opts .MergeUserIDs )
332
373
if err != nil {
333
374
return err
@@ -347,6 +388,12 @@ func UpdateProtectBranch(ctx context.Context, repo *repo_model.Repository, prote
347
388
}
348
389
protectBranch .WhitelistTeamIDs = whitelist
349
390
391
+ whitelist , err = updateTeamWhitelist (ctx , repo , protectBranch .ForcePushAllowlistTeamIDs , opts .ForcePushTeamIDs )
392
+ if err != nil {
393
+ return err
394
+ }
395
+ protectBranch .ForcePushAllowlistTeamIDs = whitelist
396
+
350
397
whitelist , err = updateTeamWhitelist (ctx , repo , protectBranch .MergeWhitelistTeamIDs , opts .MergeTeamIDs )
351
398
if err != nil {
352
399
return err
@@ -468,43 +515,58 @@ func DeleteProtectedBranch(ctx context.Context, repo *repo_model.Repository, id
468
515
return nil
469
516
}
470
517
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 {
485
546
return fmt .Errorf ("updateProtectedBranches: %v" , err )
486
547
}
487
548
}
488
549
return nil
489
550
}
490
551
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
492
564
func 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" ,
508
570
}
509
- return nil
571
+ return removeIDsFromProtectedBranch ( ctx , p , 0 , teamID , columnNames )
510
572
}
0 commit comments