@@ -12,6 +12,8 @@ import (
12
12
"strings"
13
13
14
14
"code.gitea.io/gitea/models"
15
+ asymkey_model "code.gitea.io/gitea/models/asymkey"
16
+ perm_model "code.gitea.io/gitea/models/perm"
15
17
"code.gitea.io/gitea/models/unit"
16
18
user_model "code.gitea.io/gitea/models/user"
17
19
gitea_context "code.gitea.io/gitea/modules/context"
@@ -24,8 +26,12 @@ import (
24
26
25
27
type preReceiveContext struct {
26
28
* gitea_context.PrivateContext
27
- user * user_model.User
28
- perm models.Permission
29
+
30
+ // loadedPusher indicates that where the following information are loaded
31
+ loadedPusher bool
32
+ user * user_model.User // it's the org user if a DeployKey is used
33
+ userPerm models.Permission
34
+ deployKeyAccessMode perm_model.AccessMode
29
35
30
36
canCreatePullRequest bool
31
37
checkedCanCreatePullRequest bool
@@ -41,62 +47,52 @@ type preReceiveContext struct {
41
47
opts * private.HookOptions
42
48
}
43
49
44
- // User gets or loads User
45
- func (ctx * preReceiveContext ) User () * user_model.User {
46
- if ctx .user == nil {
47
- ctx .user , ctx .perm = loadUserAndPermission (ctx .PrivateContext , ctx .opts .UserID )
48
- }
49
- return ctx .user
50
- }
51
-
52
- // Perm gets or loads Perm
53
- func (ctx * preReceiveContext ) Perm () * models.Permission {
54
- if ctx .user == nil {
55
- ctx .user , ctx .perm = loadUserAndPermission (ctx .PrivateContext , ctx .opts .UserID )
56
- }
57
- return & ctx .perm
58
- }
59
-
60
- // CanWriteCode returns true if can write code
50
+ // CanWriteCode returns true if pusher can write code
61
51
func (ctx * preReceiveContext ) CanWriteCode () bool {
62
52
if ! ctx .checkedCanWriteCode {
63
- ctx .canWriteCode = ctx .Perm ().CanWrite (unit .TypeCode )
53
+ if ! ctx .loadPusherAndPermission () {
54
+ return false
55
+ }
56
+ ctx .canWriteCode = ctx .userPerm .CanWrite (unit .TypeCode ) || ctx .deployKeyAccessMode >= perm_model .AccessModeWrite
64
57
ctx .checkedCanWriteCode = true
65
58
}
66
59
return ctx .canWriteCode
67
60
}
68
61
69
- // AssertCanWriteCode returns true if can write code
62
+ // AssertCanWriteCode returns true if pusher can write code
70
63
func (ctx * preReceiveContext ) AssertCanWriteCode () bool {
71
64
if ! ctx .CanWriteCode () {
72
65
if ctx .Written () {
73
66
return false
74
67
}
75
68
ctx .JSON (http .StatusForbidden , map [string ]interface {}{
76
- "err" : "User permission denied." ,
69
+ "err" : "User permission denied for writing ." ,
77
70
})
78
71
return false
79
72
}
80
73
return true
81
74
}
82
75
83
- // CanCreatePullRequest returns true if can create pull requests
76
+ // CanCreatePullRequest returns true if pusher can create pull requests
84
77
func (ctx * preReceiveContext ) CanCreatePullRequest () bool {
85
78
if ! ctx .checkedCanCreatePullRequest {
86
- ctx .canCreatePullRequest = ctx .Perm ().CanRead (unit .TypePullRequests )
79
+ if ! ctx .loadPusherAndPermission () {
80
+ return false
81
+ }
82
+ ctx .canCreatePullRequest = ctx .userPerm .CanRead (unit .TypePullRequests )
87
83
ctx .checkedCanCreatePullRequest = true
88
84
}
89
85
return ctx .canCreatePullRequest
90
86
}
91
87
92
- // AssertCanCreatePullRequest returns true if can create pull requests
88
+ // AssertCreatePullRequest returns true if can create pull requests
93
89
func (ctx * preReceiveContext ) AssertCreatePullRequest () bool {
94
90
if ! ctx .CanCreatePullRequest () {
95
91
if ctx .Written () {
96
92
return false
97
93
}
98
94
ctx .JSON (http .StatusForbidden , map [string ]interface {}{
99
- "err" : "User permission denied." ,
95
+ "err" : "User permission denied for creating pull-request ." ,
100
96
})
101
97
return false
102
98
}
@@ -246,7 +242,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
246
242
247
243
// 5. Check if the doer is allowed to push
248
244
canPush := false
249
- if ctx .opts .IsDeployKey {
245
+ if ctx .opts .DeployKeyID != 0 {
250
246
canPush = ! changedProtectedfiles && protectBranch .CanPush && (! protectBranch .EnableWhitelist || protectBranch .WhitelistDeployKeys )
251
247
} else {
252
248
canPush = ! changedProtectedfiles && protectBranch .CanUserPush (ctx .opts .UserID )
@@ -303,9 +299,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
303
299
return
304
300
}
305
301
302
+ // although we should have called `loadPusherAndPermission` before, here we call it explicitly again because we need to access ctx.user below
303
+ if ! ctx .loadPusherAndPermission () {
304
+ // if error occurs, loadPusherAndPermission had written the error response
305
+ return
306
+ }
307
+
306
308
// Now check if the user is allowed to merge PRs for this repository
307
309
// Note: we can use ctx.perm and ctx.user directly as they will have been loaded above
308
- allowedMerge , err := pull_service .IsUserAllowedToMerge (pr , ctx .perm , ctx .user )
310
+ allowedMerge , err := pull_service .IsUserAllowedToMerge (pr , ctx .userPerm , ctx .user )
309
311
if err != nil {
310
312
log .Error ("Error calculating if allowed to merge: %v" , err )
311
313
ctx .JSON (http .StatusInternalServerError , private.Response {
@@ -323,7 +325,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
323
325
}
324
326
325
327
// If we're an admin for the repository we can ignore status checks, reviews and override protected files
326
- if ctx .perm .IsAdmin () {
328
+ if ctx .userPerm .IsAdmin () {
327
329
return
328
330
}
329
331
@@ -450,24 +452,44 @@ func generateGitEnv(opts *private.HookOptions) (env []string) {
450
452
return env
451
453
}
452
454
453
- func loadUserAndPermission (ctx * gitea_context.PrivateContext , id int64 ) (user * user_model.User , perm models.Permission ) {
454
- user , err := user_model .GetUserByID (id )
455
+ // loadPusherAndPermission returns false if an error occurs, and it writes the error response
456
+ func (ctx * preReceiveContext ) loadPusherAndPermission () bool {
457
+ if ctx .loadedPusher {
458
+ return true
459
+ }
460
+
461
+ user , err := user_model .GetUserByID (ctx .opts .UserID )
455
462
if err != nil {
456
- log .Error ("Unable to get User id %d Error: %v" , id , err )
463
+ log .Error ("Unable to get User id %d Error: %v" , ctx . opts . UserID , err )
457
464
ctx .JSON (http .StatusInternalServerError , private.Response {
458
- Err : fmt .Sprintf ("Unable to get User id %d Error: %v" , id , err ),
465
+ Err : fmt .Sprintf ("Unable to get User id %d Error: %v" , ctx . opts . UserID , err ),
459
466
})
460
- return
467
+ return false
461
468
}
469
+ ctx .user = user
462
470
463
- perm , err = models .GetUserRepoPermission (ctx .Repo .Repository , user )
471
+ userPerm , err : = models .GetUserRepoPermission (ctx .Repo .Repository , user )
464
472
if err != nil {
465
473
log .Error ("Unable to get Repo permission of repo %s/%s of User %s" , ctx .Repo .Repository .OwnerName , ctx .Repo .Repository .Name , user .Name , err )
466
474
ctx .JSON (http .StatusInternalServerError , private.Response {
467
475
Err : fmt .Sprintf ("Unable to get Repo permission of repo %s/%s of User %s: %v" , ctx .Repo .Repository .OwnerName , ctx .Repo .Repository .Name , user .Name , err ),
468
476
})
469
- return
477
+ return false
478
+ }
479
+ ctx .userPerm = userPerm
480
+
481
+ if ctx .opts .DeployKeyID != 0 {
482
+ deployKey , err := asymkey_model .GetDeployKeyByID (ctx , ctx .opts .DeployKeyID )
483
+ if err != nil {
484
+ log .Error ("Unable to get DeployKey id %d Error: %v" , ctx .opts .DeployKeyID , err )
485
+ ctx .JSON (http .StatusInternalServerError , private.Response {
486
+ Err : fmt .Sprintf ("Unable to get DeployKey id %d Error: %v" , ctx .opts .DeployKeyID , err ),
487
+ })
488
+ return false
489
+ }
490
+ ctx .deployKeyAccessMode = deployKey .Mode
470
491
}
471
492
472
- return
493
+ ctx .loadedPusher = true
494
+ return true
473
495
}
0 commit comments