@@ -23,9 +23,10 @@ import (
23
23
"github.com/emicklei/go-restful"
24
24
"k8s.io/apiserver/pkg/authentication/user"
25
25
log "k8s.io/klog"
26
+ "k8s.io/klog/v2"
26
27
"kubesphere.io/kubesphere/pkg/api"
27
28
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
28
- iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2 "
29
+ "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer "
29
30
"kubesphere.io/kubesphere/pkg/apiserver/request"
30
31
"kubesphere.io/kubesphere/pkg/constants"
31
32
"kubesphere.io/kubesphere/pkg/models/devops"
@@ -277,11 +278,53 @@ func (h *ProjectPipelineHandler) GetPipelineRunNodes(req *restful.Request, resp
277
278
resp .WriteAsJson (res )
278
279
}
279
280
280
- func (h * ProjectPipelineHandler ) approvableCheck (nodes []clientDevOps.NodesDetail , req * restful.Request ) {
281
- currentUserName , roleName := h .getCurrentUser (req )
281
+ // there're two situation here:
282
+ // 1. the particular submitters exist
283
+ // the users who are the owner of this Pipeline or the submitter of this Pipeline, or has the auth to create a DevOps project
284
+ // 2. no particular submitters
285
+ // only the owner of this Pipeline can approve or reject it
286
+ func (h * ProjectPipelineHandler ) approvableCheck (nodes []clientDevOps.NodesDetail , pipe pipelineParam ) {
287
+ var userInfo user.Info
288
+ var ok bool
289
+ var isAdmin bool
282
290
// check if current user belong to the admin group, grant it if it's true
283
- isAdmin := roleName == iamv1alpha2 .PlatformAdmin
291
+ if userInfo , ok = request .UserFrom (pipe .Context ); ok {
292
+ createAuth := authorizer.AttributesRecord {
293
+ User : userInfo ,
294
+ Verb : authorizer .VerbCreate ,
295
+ Workspace : pipe .Workspace ,
296
+ Resource : "devopsprojects" ,
297
+ ResourceRequest : true ,
298
+ ResourceScope : request .DevOpsScope ,
299
+ }
300
+
301
+ if decision , _ , err := h .authorizer .Authorize (createAuth ); err == nil {
302
+ isAdmin = decision == authorizer .DecisionAllow
303
+ } else {
304
+ // this is an expected case, printing the debug info for troubleshooting
305
+ klog .V (8 ).Infof ("authorize failed with '%v', error is '%v'" ,
306
+ createAuth , err )
307
+ }
308
+ } else {
309
+ klog .V (6 ).Infof ("cannot get the current user when checking the approvable with pipeline '%s/%s'" ,
310
+ pipe .ProjectName , pipe .Name )
311
+ return
312
+ }
284
313
314
+ var createdByCurrentUser bool // indicate if the current user is the owner
315
+ if pipeline , err := h .devopsOperator .GetPipelineObj (pipe .ProjectName , pipe .Name ); err == nil {
316
+ if creator , ok := pipeline .GetAnnotations ()[constants .CreatorAnnotationKey ]; ok {
317
+ createdByCurrentUser = userInfo .GetName () == creator
318
+ } else {
319
+ klog .V (6 ).Infof ("annotation '%s' is necessary but it is missing from '%s/%s'" ,
320
+ constants .CreatorAnnotationKey , pipe .ProjectName , pipe .Name )
321
+ }
322
+ } else {
323
+ klog .V (6 ).Infof ("cannot find pipeline '%s/%s', error is '%v'" , pipe .ProjectName , pipe .Name , err )
324
+ return
325
+ }
326
+
327
+ // check every input steps if it's approvable
285
328
for i , node := range nodes {
286
329
if node .State != clientDevOps .StatePaused {
287
330
continue
@@ -292,7 +335,7 @@ func (h *ProjectPipelineHandler) approvableCheck(nodes []clientDevOps.NodesDetai
292
335
continue
293
336
}
294
337
295
- nodes [i ].Steps [j ].Approvable = isAdmin || step .Input .Approvable (currentUserName )
338
+ nodes [i ].Steps [j ].Approvable = isAdmin || createdByCurrentUser || step .Input .Approvable (userInfo . GetName () )
296
339
}
297
340
}
298
341
}
@@ -308,33 +351,8 @@ func (h *ProjectPipelineHandler) createdBy(projectName string, pipelineName stri
308
351
return false
309
352
}
310
353
311
- func (h * ProjectPipelineHandler ) getCurrentUser (req * restful.Request ) (username , roleName string ) {
312
- var userInfo user.Info
313
- var ok bool
314
- var err error
315
-
316
- ctx := req .Request .Context ()
317
- if userInfo , ok = request .UserFrom (ctx ); ok {
318
- var role * iamv1alpha2.GlobalRole
319
- username = userInfo .GetName ()
320
- if role , err = h .amInterface .GetGlobalRoleOfUser (username ); err == nil {
321
- roleName = role .Name
322
- }
323
- }
324
- return
325
- }
326
-
327
354
func (h * ProjectPipelineHandler ) hasSubmitPermission (req * restful.Request ) (hasPermit bool , err error ) {
328
- currentUserName , roleName := h .getCurrentUser (req )
329
- projectName := req .PathParameter ("devops" )
330
- pipelineName := req .PathParameter ("pipeline" )
331
- // check if current user belong to the admin group or he's the owner, grant it if it's true
332
- if roleName == iamv1alpha2 .PlatformAdmin || h .createdBy (projectName , pipelineName , currentUserName ) {
333
- hasPermit = true
334
- return
335
- }
336
-
337
- // step 2, check if current user if was addressed
355
+ pipeParam := parsePipelineParam (req )
338
356
httpReq := & http.Request {
339
357
URL : req .Request .URL ,
340
358
Header : req .Request .Header ,
@@ -348,7 +366,9 @@ func (h *ProjectPipelineHandler) hasSubmitPermission(req *restful.Request) (hasP
348
366
349
367
// check if current user can approve this input
350
368
var res []clientDevOps.NodesDetail
351
- if res , err = h .devopsOperator .GetNodesDetail (projectName , pipelineName , runId , httpReq ); err == nil {
369
+ if res , err = h .devopsOperator .GetNodesDetail (pipeParam .ProjectName , pipeParam .Name , runId , httpReq ); err == nil {
370
+ h .approvableCheck (res , parsePipelineParam (req ))
371
+
352
372
for _ , node := range res {
353
373
if node .ID != nodeId {
354
374
continue
@@ -359,7 +379,7 @@ func (h *ProjectPipelineHandler) hasSubmitPermission(req *restful.Request) (hasP
359
379
continue
360
380
}
361
381
362
- hasPermit = step .Input . Approvable ( currentUserName )
382
+ hasPermit = step .Approvable
363
383
break
364
384
}
365
385
break
@@ -410,7 +430,7 @@ func (h *ProjectPipelineHandler) GetNodesDetail(req *restful.Request, resp *rest
410
430
parseErr (err , resp )
411
431
return
412
432
}
413
- h .approvableCheck (res , req )
433
+ h .approvableCheck (res , parsePipelineParam ( req ) )
414
434
415
435
resp .WriteAsJson (res )
416
436
}
@@ -618,10 +638,19 @@ func (h *ProjectPipelineHandler) GetBranchNodesDetail(req *restful.Request, resp
618
638
parseErr (err , resp )
619
639
return
620
640
}
621
- h .approvableCheck (res , req )
641
+ h .approvableCheck (res , parsePipelineParam ( req ) )
622
642
resp .WriteAsJson (res )
623
643
}
624
644
645
+ func parsePipelineParam (req * restful.Request ) pipelineParam {
646
+ return pipelineParam {
647
+ Workspace : req .PathParameter ("workspace" ),
648
+ ProjectName : req .PathParameter ("devops" ),
649
+ Name : req .PathParameter ("pipeline" ),
650
+ Context : req .Request .Context (),
651
+ }
652
+ }
653
+
625
654
func (h * ProjectPipelineHandler ) GetPipelineBranch (req * restful.Request , resp * restful.Response ) {
626
655
projectName := req .PathParameter ("devops" )
627
656
pipelineName := req .PathParameter ("pipeline" )
0 commit comments