Restrict only specific users or admin can approve a pipeline
Signed-off-by: rick <rick@jenkins-zh.cn>
This commit is contained in:
@@ -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(),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user