Restrict only specific users or admin can approve a pipeline

Signed-off-by: rick <rick@jenkins-zh.cn>
This commit is contained in:
rick
2020-11-27 22:37:46 +08:00
parent c5de21af4a
commit 8f4a6d9b93
5 changed files with 144 additions and 16 deletions

View File

@@ -210,7 +210,8 @@ func (s *APIServer) installKubeSphereAPIs() {
s.SonarClient, s.SonarClient,
s.KubernetesClient.KubeSphere(), s.KubernetesClient.KubeSphere(),
s.S3Client, 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, urlruntime.Must(devopsv1alpha3.AddToContainer(s.container,
s.DevopsClient, s.DevopsClient,
s.KubernetesClient.Kubernetes(), s.KubernetesClient.Kubernetes(),

View File

@@ -17,10 +17,17 @@ limitations under the License.
package v1alpha2 package v1alpha2
import ( import (
"encoding/json"
"errors"
"fmt"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/apiserver/pkg/authentication/user"
log "k8s.io/klog" log "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api" "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" "kubesphere.io/kubesphere/pkg/models/devops"
clientDevOps "kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http" "net/http"
"strings" "strings"
) )
@@ -202,6 +209,78 @@ func (h *ProjectPipelineHandler) GetPipelineRunNodes(req *restful.Request, resp
resp.WriteAsJson(res) 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) { func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *restful.Response) {
projectName := req.PathParameter("devops") projectName := req.PathParameter("devops")
pipelineName := req.PathParameter("pipeline") pipelineName := req.PathParameter("pipeline")
@@ -209,13 +288,27 @@ func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *res
nodeId := req.PathParameter("node") nodeId := req.PathParameter("node")
stepId := req.PathParameter("step") stepId := req.PathParameter("step")
res, err := h.devopsOperator.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) var (
if err != nil { response []byte
parseErr(err, resp) err error
return 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) { 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") nodeId := req.PathParameter("node")
stepId := req.PathParameter("step") 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) res, err := h.devopsOperator.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request)
if err != nil { if err != nil {
parseErr(err, resp) parseErr(err, resp)

View File

@@ -20,6 +20,7 @@ import (
"kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam/am"
devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube" "kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
@@ -28,16 +29,18 @@ import (
type ProjectPipelineHandler struct { type ProjectPipelineHandler struct {
devopsOperator devops.DevopsOperator devopsOperator devops.DevopsOperator
projectCredentialGetter devops.ProjectCredentialGetter projectCredentialGetter devops.ProjectCredentialGetter
abc am.AccessManagementInterface
} }
type PipelineSonarHandler struct { type PipelineSonarHandler struct {
pipelineSonarGetter devops.PipelineSonarGetter pipelineSonarGetter devops.PipelineSonarGetter
} }
func NewProjectPipelineHandler(devopsClient devopsClient.Interface) ProjectPipelineHandler { func NewProjectPipelineHandler(devopsClient devopsClient.Interface, abc am.AccessManagementInterface) ProjectPipelineHandler {
return ProjectPipelineHandler{ return ProjectPipelineHandler{
devopsOperator: devops.NewDevopsOperator(devopsClient, nil, nil, nil, nil), devopsOperator: devops.NewDevopsOperator(devopsClient, nil, nil, nil, nil),
projectCredentialGetter: devops.NewProjectCredentialOperator(devopsClient), projectCredentialGetter: devops.NewProjectCredentialOperator(devopsClient),
abc: abc,
} }
} }

View File

@@ -28,6 +28,7 @@ import (
"kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/constants" "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/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube" "kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
@@ -46,10 +47,10 @@ const (
var GroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"} 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) ws := runtime.NewWebService(GroupVersion)
err := AddPipelineToWebService(ws, devopsClient) err := AddPipelineToWebService(ws, devopsClient, abc)
if err != nil { if err != nil {
return err return err
} }
@@ -74,12 +75,12 @@ func AddToContainer(container *restful.Container, ksInformers externalversions.S
return nil 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 projectPipelineEnable := devopsClient != nil
if projectPipelineEnable { if projectPipelineEnable {
projectPipelineHandler := NewProjectPipelineHandler(devopsClient) projectPipelineHandler := NewProjectPipelineHandler(devopsClient, abc)
webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}/usage"). webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}/usage").
To(projectPipelineHandler.GetProjectCredentialUsage). To(projectPipelineHandler.GetProjectCredentialUsage).

View File

@@ -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) { func (d devopsOperator) GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) {
res, err := d.devopsClient.GetPipelineRunNodes(projectName, pipelineName, runId, convertToHttpParameters(req)) res, err := d.devopsClient.GetPipelineRunNodes(projectName, pipelineName, runId, convertToHttpParameters(req))
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
return nil, err return nil, err
} }
fmt.Println()
return res, err return res, err
} }
func (d devopsOperator) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { func (d devopsOperator) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) {
newBody, err := getInputReqBody(req.Body) newBody, err := getInputReqBody(req.Body)
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)