@@ -23,15 +23,18 @@ const (
23
23
24
24
// ProtectedBranch struct
25
25
type ProtectedBranch struct {
26
- ID int64 `xorm:"pk autoincr"`
27
- RepoID int64 `xorm:"UNIQUE(s)"`
28
- BranchName string `xorm:"UNIQUE(s)"`
29
- CanPush bool `xorm:"NOT NULL DEFAULT false"`
30
- EnableWhitelist bool
31
- WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
32
- WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
33
- CreatedUnix util.TimeStamp `xorm:"created"`
34
- UpdatedUnix util.TimeStamp `xorm:"updated"`
26
+ ID int64 `xorm:"pk autoincr"`
27
+ RepoID int64 `xorm:"UNIQUE(s)"`
28
+ BranchName string `xorm:"UNIQUE(s)"`
29
+ CanPush bool `xorm:"NOT NULL DEFAULT false"`
30
+ EnableWhitelist bool
31
+ WhitelistUserIDs []int64 `xorm:"JSON TEXT"`
32
+ WhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
33
+ EnableMergeWhitelist bool `xorm:"NOT NULL DEFAULT false"`
34
+ MergeWhitelistUserIDs []int64 `xorm:"JSON TEXT"`
35
+ MergeWhitelistTeamIDs []int64 `xorm:"JSON TEXT"`
36
+ CreatedUnix util.TimeStamp `xorm:"created"`
37
+ UpdatedUnix util.TimeStamp `xorm:"updated"`
35
38
}
36
39
37
40
// IsProtected returns if the branch is protected
@@ -61,6 +64,28 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool {
61
64
return in
62
65
}
63
66
67
+ // CanUserMerge returns if some user could merge a pull request to this protected branch
68
+ func (protectBranch * ProtectedBranch ) CanUserMerge (userID int64 ) bool {
69
+ if ! protectBranch .EnableMergeWhitelist {
70
+ return true
71
+ }
72
+
73
+ if base .Int64sContains (protectBranch .MergeWhitelistUserIDs , userID ) {
74
+ return true
75
+ }
76
+
77
+ if len (protectBranch .WhitelistTeamIDs ) == 0 {
78
+ return false
79
+ }
80
+
81
+ in , err := IsUserInTeams (userID , protectBranch .MergeWhitelistTeamIDs )
82
+ if err != nil {
83
+ log .Error (1 , "IsUserInTeams:" , err )
84
+ return false
85
+ }
86
+ return in
87
+ }
88
+
64
89
// GetProtectedBranchByRepoID getting protected branch by repo ID
65
90
func GetProtectedBranchByRepoID (RepoID int64 ) ([]* ProtectedBranch , error ) {
66
91
protectedBranches := make ([]* ProtectedBranch , 0 )
@@ -97,40 +122,35 @@ func GetProtectedBranchByID(id int64) (*ProtectedBranch, error) {
97
122
// If ID is 0, it creates a new record. Otherwise, updates existing record.
98
123
// This function also performs check if whitelist user and team's IDs have been changed
99
124
// to avoid unnecessary whitelist delete and regenerate.
100
- func UpdateProtectBranch (repo * Repository , protectBranch * ProtectedBranch , whitelistUserIDs , whitelistTeamIDs []int64 ) (err error ) {
125
+ func UpdateProtectBranch (repo * Repository , protectBranch * ProtectedBranch , whitelistUserIDs , whitelistTeamIDs , mergeWhitelistUserIDs , mergeWhitelistTeamIDs []int64 ) (err error ) {
101
126
if err = repo .GetOwner (); err != nil {
102
127
return fmt .Errorf ("GetOwner: %v" , err )
103
128
}
104
129
105
- hasUsersChanged := ! util .IsSliceInt64Eq (protectBranch .WhitelistUserIDs , whitelistUserIDs )
106
- if hasUsersChanged {
107
- protectBranch .WhitelistUserIDs = make ([]int64 , 0 , len (whitelistUserIDs ))
108
- for _ , userID := range whitelistUserIDs {
109
- has , err := hasAccess (x , userID , repo , AccessModeWrite )
110
- if err != nil {
111
- return fmt .Errorf ("HasAccess [user_id: %d, repo_id: %d]: %v" , userID , protectBranch .RepoID , err )
112
- } else if ! has {
113
- continue // Drop invalid user ID
114
- }
130
+ whitelist , err := updateUserWhitelist (repo , protectBranch .WhitelistUserIDs , whitelistUserIDs )
131
+ if err != nil {
132
+ return err
133
+ }
134
+ protectBranch .WhitelistUserIDs = whitelist
115
135
116
- protectBranch .WhitelistUserIDs = append (protectBranch .WhitelistUserIDs , userID )
117
- }
136
+ whitelist , err = updateUserWhitelist (repo , protectBranch .MergeWhitelistUserIDs , mergeWhitelistUserIDs )
137
+ if err != nil {
138
+ return err
118
139
}
140
+ protectBranch .MergeWhitelistUserIDs = whitelist
119
141
120
- // if the repo is in an orgniziation
121
- hasTeamsChanged := ! util .IsSliceInt64Eq (protectBranch .WhitelistTeamIDs , whitelistTeamIDs )
122
- if hasTeamsChanged {
123
- teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeWrite )
124
- if err != nil {
125
- return fmt .Errorf ("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v" , repo .OwnerID , repo .ID , err )
126
- }
127
- protectBranch .WhitelistTeamIDs = make ([]int64 , 0 , len (teams ))
128
- for i := range teams {
129
- if teams [i ].HasWriteAccess () && com .IsSliceContainsInt64 (whitelistTeamIDs , teams [i ].ID ) {
130
- protectBranch .WhitelistTeamIDs = append (protectBranch .WhitelistTeamIDs , teams [i ].ID )
131
- }
132
- }
142
+ // if the repo is in an organization
143
+ whitelist , err = updateTeamWhitelist (repo , protectBranch .WhitelistTeamIDs , whitelistTeamIDs )
144
+ if err != nil {
145
+ return err
146
+ }
147
+ protectBranch .WhitelistTeamIDs = whitelist
148
+
149
+ whitelist , err = updateTeamWhitelist (repo , protectBranch .MergeWhitelistTeamIDs , mergeWhitelistTeamIDs )
150
+ if err != nil {
151
+ return err
133
152
}
153
+ protectBranch .MergeWhitelistTeamIDs = whitelist
134
154
135
155
// Make sure protectBranch.ID is not 0 for whitelists
136
156
if protectBranch .ID == 0 {
@@ -174,6 +194,73 @@ func (repo *Repository) IsProtectedBranch(branchName string, doer *User) (bool,
174
194
return false , nil
175
195
}
176
196
197
+ // IsProtectedBranchForMerging checks if branch is protected for merging
198
+ func (repo * Repository ) IsProtectedBranchForMerging (branchName string , doer * User ) (bool , error ) {
199
+ if doer == nil {
200
+ return true , nil
201
+ }
202
+
203
+ protectedBranch := & ProtectedBranch {
204
+ RepoID : repo .ID ,
205
+ BranchName : branchName ,
206
+ }
207
+
208
+ has , err := x .Get (protectedBranch )
209
+ if err != nil {
210
+ return true , err
211
+ } else if has {
212
+ return ! protectedBranch .CanUserMerge (doer .ID ), nil
213
+ }
214
+
215
+ return false , nil
216
+ }
217
+
218
+ // updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
219
+ // the users from newWhitelist which have write access to the repo.
220
+ func updateUserWhitelist (repo * Repository , currentWhitelist , newWhitelist []int64 ) (whitelist []int64 , err error ) {
221
+ hasUsersChanged := ! util .IsSliceInt64Eq (currentWhitelist , newWhitelist )
222
+ if ! hasUsersChanged {
223
+ return currentWhitelist , nil
224
+ }
225
+
226
+ whitelist = make ([]int64 , 0 , len (newWhitelist ))
227
+ for _ , userID := range newWhitelist {
228
+ has , err := hasAccess (x , userID , repo , AccessModeWrite )
229
+ if err != nil {
230
+ return nil , fmt .Errorf ("HasAccess [user_id: %d, repo_id: %d]: %v" , userID , repo .ID , err )
231
+ } else if ! has {
232
+ continue // Drop invalid user ID
233
+ }
234
+
235
+ whitelist = append (whitelist , userID )
236
+ }
237
+
238
+ return
239
+ }
240
+
241
+ // updateTeamWhitelist checks whether the team whitelist changed and returns a whitelist with
242
+ // the teams from newWhitelist which have write access to the repo.
243
+ func updateTeamWhitelist (repo * Repository , currentWhitelist , newWhitelist []int64 ) (whitelist []int64 , err error ) {
244
+ hasTeamsChanged := ! util .IsSliceInt64Eq (currentWhitelist , newWhitelist )
245
+ if ! hasTeamsChanged {
246
+ return currentWhitelist , nil
247
+ }
248
+
249
+ teams , err := GetTeamsWithAccessToRepo (repo .OwnerID , repo .ID , AccessModeWrite )
250
+ if err != nil {
251
+ return nil , fmt .Errorf ("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v" , repo .OwnerID , repo .ID , err )
252
+ }
253
+
254
+ whitelist = make ([]int64 , 0 , len (teams ))
255
+ for i := range teams {
256
+ if teams [i ].HasWriteAccess () && com .IsSliceContainsInt64 (newWhitelist , teams [i ].ID ) {
257
+ whitelist = append (whitelist , teams [i ].ID )
258
+ }
259
+ }
260
+
261
+ return
262
+ }
263
+
177
264
// DeleteProtectedBranch removes ProtectedBranch relation between the user and repository.
178
265
func (repo * Repository ) DeleteProtectedBranch (id int64 ) (err error ) {
179
266
protectedBranch := & ProtectedBranch {
0 commit comments