diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 60aa85ea2..70a0e76fb 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -210,7 +210,8 @@ func (s *APIServer) installKubeSphereAPIs() { s.SonarClient, s.KubernetesClient.KubeSphere(), s.S3Client, - s.Config.DevopsOptions.Host)) + s.Config.DevopsOptions.Host, + am.NewOperator(s.InformerFactory, s.KubernetesClient.KubeSphere(), s.KubernetesClient.Kubernetes()))) urlruntime.Must(devopsv1alpha3.AddToContainer(s.container, s.DevopsClient, s.KubernetesClient.Kubernetes(), diff --git a/pkg/kapis/devops/v1alpha2/devops.go b/pkg/kapis/devops/v1alpha2/devops.go index 382cf972c..7f8eef92d 100644 --- a/pkg/kapis/devops/v1alpha2/devops.go +++ b/pkg/kapis/devops/v1alpha2/devops.go @@ -17,10 +17,17 @@ limitations under the License. package v1alpha2 import ( + "encoding/json" + "errors" + "fmt" "github.com/emicklei/go-restful" + "k8s.io/apiserver/pkg/authentication/user" log "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + "kubesphere.io/kubesphere/pkg/apiserver/request" "kubesphere.io/kubesphere/pkg/models/devops" + clientDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" "strings" ) @@ -202,6 +209,78 @@ func (h *ProjectPipelineHandler) GetPipelineRunNodes(req *restful.Request, resp resp.WriteAsJson(res) } +func (h *ProjectPipelineHandler) hasSubmitPermission(req *restful.Request) (hasPermit bool, err error) { + var currentUserName string + var userInfo user.Info + var ok bool + ctx := req.Request.Context() + if userInfo, ok = request.UserFrom(ctx); ok { + // check if current user belong to the admin group, grant it if it's true + var role *iamv1alpha2.GlobalRole + currentUserName = userInfo.GetName() + if role, err = h.abc.GetGlobalRoleOfUser(currentUserName); err == nil { + if role.Name == iamv1alpha2.PlatformAdmin { + hasPermit = true + return + } + } else { + return + } + } + + // step 2, check if current user if was addressed + httpReq := &http.Request{ + URL: req.Request.URL, + Header: req.Request.Header, + Form: req.Request.Form, + PostForm: req.Request.PostForm, + } + + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") + + // find the expected submitter list which separated by common + var expectedSubmitter string + var res []clientDevOps.NodesDetail + if res, err = h.devopsOperator.GetNodesDetail(projectName, pipelineName, runId, httpReq); err == nil { + for _, node := range res { + if node.ID != nodeId { + continue + } + + for _, step := range node.Steps { + if step.ID != stepId || step.Input == nil { + continue + } + + expectedSubmitter = fmt.Sprintf("%v", step.Input.Submitter) + break + } + break + } + } else { + log.Errorf("cannot get nodes detail, error: %v", err) + err = errors.New("cannot get the submitters of current pipeline run") + return + } + + // grant all users if there's no specific one + if expectedSubmitter == "" { + hasPermit = true + } else { + for _, submitter := range strings.Split(expectedSubmitter, ",") { + if strings.TrimSpace(submitter) == currentUserName { + hasPermit = true + break + } + } + } + return +} + func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") @@ -209,13 +288,27 @@ func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *res nodeId := req.PathParameter("node") stepId := req.PathParameter("step") - res, err := h.devopsOperator.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) - if err != nil { - parseErr(err, resp) - return - } + var ( + response []byte + err error + ok bool + ) - resp.Write(res) + if ok, err = h.hasSubmitPermission(req); !ok || err != nil { + msg := map[string]string{ + "allow": "false", + "message": fmt.Sprintf("%v", err), + } + + response, _ = json.Marshal(msg) + } else { + response, err = h.devopsOperator.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) + if err != nil { + parseErr(err, resp) + return + } + } + resp.Write(response) } func (h *ProjectPipelineHandler) GetNodesDetail(req *restful.Request, resp *restful.Response) { @@ -401,6 +494,40 @@ func (h *ProjectPipelineHandler) SubmitBranchInputStep(req *restful.Request, res nodeId := req.PathParameter("node") stepId := req.PathParameter("step") + var currentUesrName string + ctx := req.Request.Context() + if user, ok := request.UserFrom(ctx); ok { + currentUesrName = user.GetName() + } + + fmt.Println("current user", currentUesrName, "nodeId", nodeId, "stepid", stepId) + req.Request.UserAgent() + if res, err := h.devopsOperator.GetNodesDetail(projectName, pipelineName, runId, req.Request); err == nil { + for _, node := range res { + fmt.Println("nodeid", node.ID) + if node.ID != nodeId { + continue + } + + for _, step := range node.Steps { + fmt.Println("stepid", step.ID, step.Input) + if step.ID != stepId && step.Input != nil { + continue + } + + submitter := step.Input.Submitter + fmt.Println(submitter) + + if currentUesrName != submitter { + resp.Write([]byte("no permission")) + return + } + } + } + } else { + log.Infof("cannot get the nodes detail when submit a branch input step") + } + res, err := h.devopsOperator.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) if err != nil { parseErr(err, resp) diff --git a/pkg/kapis/devops/v1alpha2/handler.go b/pkg/kapis/devops/v1alpha2/handler.go index 45c6d0e41..447b0f6ac 100644 --- a/pkg/kapis/devops/v1alpha2/handler.go +++ b/pkg/kapis/devops/v1alpha2/handler.go @@ -20,6 +20,7 @@ import ( "kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/models/iam/am" devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" @@ -28,16 +29,18 @@ import ( type ProjectPipelineHandler struct { devopsOperator devops.DevopsOperator projectCredentialGetter devops.ProjectCredentialGetter + abc am.AccessManagementInterface } type PipelineSonarHandler struct { pipelineSonarGetter devops.PipelineSonarGetter } -func NewProjectPipelineHandler(devopsClient devopsClient.Interface) ProjectPipelineHandler { +func NewProjectPipelineHandler(devopsClient devopsClient.Interface, abc am.AccessManagementInterface) ProjectPipelineHandler { return ProjectPipelineHandler{ devopsOperator: devops.NewDevopsOperator(devopsClient, nil, nil, nil, nil), projectCredentialGetter: devops.NewProjectCredentialOperator(devopsClient), + abc: abc, } } diff --git a/pkg/kapis/devops/v1alpha2/register.go b/pkg/kapis/devops/v1alpha2/register.go index 5b6e7ec37..e8916b09d 100644 --- a/pkg/kapis/devops/v1alpha2/register.go +++ b/pkg/kapis/devops/v1alpha2/register.go @@ -28,6 +28,7 @@ import ( "kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/iam/am" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" "kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/sonarqube" @@ -46,10 +47,10 @@ const ( var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} -func AddToContainer(container *restful.Container, ksInformers externalversions.SharedInformerFactory, devopsClient devops.Interface, sonarqubeClient sonarqube.SonarInterface, ksClient versioned.Interface, s3Client s3.Interface, endpoint string) error { +func AddToContainer(container *restful.Container, ksInformers externalversions.SharedInformerFactory, devopsClient devops.Interface, sonarqubeClient sonarqube.SonarInterface, ksClient versioned.Interface, s3Client s3.Interface, endpoint string, abc am.AccessManagementInterface) error { ws := runtime.NewWebService(GroupVersion) - err := AddPipelineToWebService(ws, devopsClient) + err := AddPipelineToWebService(ws, devopsClient, abc) if err != nil { return err } @@ -74,12 +75,12 @@ func AddToContainer(container *restful.Container, ksInformers externalversions.S return nil } -func AddPipelineToWebService(webservice *restful.WebService, devopsClient devops.Interface) error { +func AddPipelineToWebService(webservice *restful.WebService, devopsClient devops.Interface, abc am.AccessManagementInterface) error { projectPipelineEnable := devopsClient != nil if projectPipelineEnable { - projectPipelineHandler := NewProjectPipelineHandler(devopsClient) + projectPipelineHandler := NewProjectPipelineHandler(devopsClient, abc) webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}/usage"). To(projectPipelineHandler.GetProjectCredentialUsage). diff --git a/pkg/models/devops/devops.go b/pkg/models/devops/devops.go index 8fdf15922..550fc3a33 100644 --- a/pkg/models/devops/devops.go +++ b/pkg/models/devops/devops.go @@ -487,20 +487,16 @@ func (d devopsOperator) GetNodeSteps(projectName, pipelineName, runId, nodeId st } func (d devopsOperator) GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) { - res, err := d.devopsClient.GetPipelineRunNodes(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - fmt.Println() - return res, err } func (d devopsOperator) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { - newBody, err := getInputReqBody(req.Body) if err != nil { klog.Error(err)