提交 bda48c24 编写于 作者: H hongming

fix: devop IAM bugs

Signed-off-by: Nhongming <talonwan@yunify.com>
上级 afcd0efe
...@@ -122,7 +122,7 @@ func addControllers( ...@@ -122,7 +122,7 @@ func addControllers(
client.KubeSphere(), devopsClient, client.KubeSphere(), devopsClient,
informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(), informerFactory.KubernetesSharedInformerFactory().Core().V1().Namespaces(),
informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().DevOpsProjects(), informerFactory.KubeSphereSharedInformerFactory().Devops().V1alpha3().DevOpsProjects(),
) informerFactory.KubeSphereSharedInformerFactory().Tenant().V1alpha1().Workspaces())
devopsPipelineController = pipeline.NewController(client.Kubernetes(), devopsPipelineController = pipeline.NewController(client.Kubernetes(),
client.KubeSphere(), client.KubeSphere(),
......
...@@ -51,6 +51,9 @@ type Attributes interface { ...@@ -51,6 +51,9 @@ type Attributes interface {
// The namespace of the object, if a request is for a REST object. // The namespace of the object, if a request is for a REST object.
GetNamespace() string GetNamespace() string
// The devops project of the object, if a request is for a REST object.
GetDevOps() string
// The kind of object, if a request is for a REST object. // The kind of object, if a request is for a REST object.
GetResource() string GetResource() string
...@@ -109,6 +112,7 @@ type AttributesRecord struct { ...@@ -109,6 +112,7 @@ type AttributesRecord struct {
Cluster string Cluster string
Workspace string Workspace string
Namespace string Namespace string
DevOps string
APIGroup string APIGroup string
APIVersion string APIVersion string
Resource string Resource string
...@@ -144,6 +148,10 @@ func (a AttributesRecord) GetNamespace() string { ...@@ -144,6 +148,10 @@ func (a AttributesRecord) GetNamespace() string {
return a.Namespace return a.Namespace
} }
func (a AttributesRecord) GetDevOps() string {
return a.DevOps
}
func (a AttributesRecord) GetResource() string { func (a AttributesRecord) GetResource() string {
return a.Resource return a.Resource
} }
......
...@@ -234,11 +234,21 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes, ...@@ -234,11 +234,21 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
} }
} }
if requestAttributes.GetResourceScope() == request.WorkspaceScope || requestAttributes.GetResourceScope() == request.NamespaceScope { if requestAttributes.GetResourceScope() == request.WorkspaceScope ||
requestAttributes.GetResourceScope() == request.NamespaceScope ||
requestAttributes.GetResourceScope() == request.DevOpsScope {
var workspace string var workspace string
var err error var err error
// all of resource under namespace and devops belong to workspace
if requestAttributes.GetResourceScope() == request.NamespaceScope { if requestAttributes.GetResourceScope() == request.NamespaceScope {
if workspace, err = r.am.GetControlledWorkspace(requestAttributes.GetNamespace()); err != nil { if workspace, err = r.am.GetNamespaceControlledWorkspace(requestAttributes.GetNamespace()); err != nil {
if !visitor(nil, "", nil, err) {
return
}
}
} else if requestAttributes.GetResourceScope() == request.DevOpsScope {
if workspace, err = r.am.GetDevOpsControlledWorkspace(requestAttributes.GetDevOps()); err != nil {
if !visitor(nil, "", nil, err) { if !visitor(nil, "", nil, err) {
return return
} }
...@@ -279,19 +289,33 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes, ...@@ -279,19 +289,33 @@ func (r *RBACAuthorizer) visitRulesFor(requestAttributes authorizer.Attributes,
} }
} }
if requestAttributes.GetResourceScope() == request.NamespaceScope { if requestAttributes.GetResourceScope() == request.NamespaceScope ||
if roleBindings, err := r.am.ListRoleBindings("", requestAttributes.GetNamespace()); err != nil { requestAttributes.GetResourceScope() == request.DevOpsScope {
namespace := requestAttributes.GetNamespace()
// list devops role binding
if requestAttributes.GetResourceScope() == request.DevOpsScope {
if relatedNamespace, err := r.am.GetDevOpsRelatedNamespace(requestAttributes.GetDevOps()); err != nil {
if !visitor(nil, "", nil, err) {
return
}
} else {
namespace = relatedNamespace
}
}
if roleBindings, err := r.am.ListRoleBindings("", namespace); err != nil {
if !visitor(nil, "", nil, err) { if !visitor(nil, "", nil, err) {
return return
} }
} else { } else {
sourceDescriber := &roleBindingDescriber{} sourceDescriber := &roleBindingDescriber{}
for _, roleBinding := range roleBindings { for _, roleBinding := range roleBindings {
subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, requestAttributes.GetNamespace()) subjectIndex, applies := appliesTo(requestAttributes.GetUser(), roleBinding.Subjects, namespace)
if !applies { if !applies {
continue continue
} }
regoPolicy, rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, requestAttributes.GetNamespace()) regoPolicy, rules, err := r.am.GetRoleReferenceRules(roleBinding.RoleRef, namespace)
if err != nil { if err != nil {
visitor(nil, "", nil, err) visitor(nil, "", nil, err)
continue continue
......
...@@ -82,12 +82,12 @@ func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) ...@@ -82,12 +82,12 @@ func getAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error)
attribs.Cluster = requestInfo.Cluster attribs.Cluster = requestInfo.Cluster
attribs.Workspace = requestInfo.Workspace attribs.Workspace = requestInfo.Workspace
attribs.KubernetesRequest = requestInfo.IsKubernetesRequest attribs.KubernetesRequest = requestInfo.IsKubernetesRequest
attribs.APIGroup = requestInfo.APIGroup attribs.APIGroup = requestInfo.APIGroup
attribs.APIVersion = requestInfo.APIVersion attribs.APIVersion = requestInfo.APIVersion
attribs.Resource = requestInfo.Resource attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace attribs.Namespace = requestInfo.Namespace
attribs.DevOps = requestInfo.DevOps
attribs.Name = requestInfo.Name attribs.Name = requestInfo.Name
return &attribs, nil return &attribs, nil
......
...@@ -69,6 +69,9 @@ type RequestInfo struct { ...@@ -69,6 +69,9 @@ type RequestInfo struct {
// Cluster of requested resource, this is empty in single-cluster environment // Cluster of requested resource, this is empty in single-cluster environment
Cluster string Cluster string
// DevOps project of requested resource
DevOps string
// Scope of requested resource. // Scope of requested resource.
ResourceScope string ResourceScope string
} }
...@@ -195,15 +198,6 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -195,15 +198,6 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
} }
} }
selector := req.URL.Query().Get("labelSelector")
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace=system-workspace
if strings.HasPrefix(selector, workspaceSelectorPrefix) {
workspace := strings.TrimPrefix(selector, workspaceSelectorPrefix)
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace==system-workspace
workspace = strings.TrimPrefix(workspace, "=")
requestInfo.Workspace = workspace
}
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind // URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "namespaces" { if currentParts[0] == "namespaces" {
if len(currentParts) > 1 { if len(currentParts) > 1 {
...@@ -215,8 +209,19 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -215,8 +209,19 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
currentParts = currentParts[2:] currentParts = currentParts[2:]
} }
} }
} else if currentParts[0] == "devops" {
if len(currentParts) > 1 {
requestInfo.DevOps = currentParts[1]
// if there is another step after the devops name
// move currentParts to include it as a resource in its own right
if len(currentParts) > 2 {
currentParts = currentParts[2:]
}
}
} else { } else {
requestInfo.Namespace = metav1.NamespaceNone requestInfo.Namespace = metav1.NamespaceNone
requestInfo.DevOps = metav1.NamespaceNone
} }
// parsing successful, so we now know the proper value for .Parts // parsing successful, so we now know the proper value for .Parts
...@@ -260,6 +265,15 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er ...@@ -260,6 +265,15 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
requestInfo.Verb = "list" requestInfo.Verb = "list"
} }
// URL forms: /api/v1/watch/namespaces?labelSelector=kubesphere.io/workspace=system-workspace
if requestInfo.Verb == "watch" {
selector := req.URL.Query().Get("labelSelector")
if strings.HasPrefix(selector, workspaceSelectorPrefix) {
workspace := strings.TrimPrefix(selector, workspaceSelectorPrefix)
requestInfo.Workspace = workspace
}
}
if opts.FieldSelector != nil { if opts.FieldSelector != nil {
if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok { if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok {
if len(path.IsValidPathSegmentName(name)) == 0 { if len(path.IsValidPathSegmentName(name)) == 0 {
...@@ -306,6 +320,7 @@ const ( ...@@ -306,6 +320,7 @@ const (
ClusterScope = "Cluster" ClusterScope = "Cluster"
WorkspaceScope = "Workspace" WorkspaceScope = "Workspace"
NamespaceScope = "Namespace" NamespaceScope = "Namespace"
DevOpsScope = "DevOps"
workspaceSelectorPrefix = constants.WorkspaceLabelKey + "=" workspaceSelectorPrefix = constants.WorkspaceLabelKey + "="
) )
...@@ -318,6 +333,10 @@ func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string { ...@@ -318,6 +333,10 @@ func (r *RequestInfoFactory) resolveResourceScope(request RequestInfo) string {
return NamespaceScope return NamespaceScope
} }
if request.DevOps != "" {
return DevOpsScope
}
if request.Workspace != "" { if request.Workspace != "" {
return WorkspaceScope return WorkspaceScope
} }
......
...@@ -18,6 +18,8 @@ import ( ...@@ -18,6 +18,8 @@ import (
"k8s.io/klog" "k8s.io/klog"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3" devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
tenantv1alpha1informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/tenant/v1alpha1"
tenantv1alpha1listers "kubesphere.io/kubesphere/pkg/client/listers/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/utils/k8sutil" "kubesphere.io/kubesphere/pkg/utils/k8sutil"
...@@ -48,6 +50,9 @@ type Controller struct { ...@@ -48,6 +50,9 @@ type Controller struct {
namespaceLister corev1lister.NamespaceLister namespaceLister corev1lister.NamespaceLister
namespaceSynced cache.InformerSynced namespaceSynced cache.InformerSynced
workspaceLister tenantv1alpha1listers.WorkspaceLister
workspaceSynced cache.InformerSynced
workqueue workqueue.RateLimitingInterface workqueue workqueue.RateLimitingInterface
workerLoopPeriod time.Duration workerLoopPeriod time.Duration
...@@ -59,7 +64,8 @@ func NewController(client clientset.Interface, ...@@ -59,7 +64,8 @@ func NewController(client clientset.Interface,
kubesphereClient kubesphereclient.Interface, kubesphereClient kubesphereclient.Interface,
devopsClinet devopsClient.Interface, devopsClinet devopsClient.Interface,
namespaceInformer corev1informer.NamespaceInformer, namespaceInformer corev1informer.NamespaceInformer,
devopsInformer devopsinformers.DevOpsProjectInformer) *Controller { devopsInformer devopsinformers.DevOpsProjectInformer,
workspaceInformer tenantv1alpha1informers.WorkspaceInformer) *Controller {
broadcaster := record.NewBroadcaster() broadcaster := record.NewBroadcaster()
broadcaster.StartLogging(func(format string, args ...interface{}) { broadcaster.StartLogging(func(format string, args ...interface{}) {
...@@ -77,6 +83,8 @@ func NewController(client clientset.Interface, ...@@ -77,6 +83,8 @@ func NewController(client clientset.Interface,
devOpsProjectSynced: devopsInformer.Informer().HasSynced, devOpsProjectSynced: devopsInformer.Informer().HasSynced,
namespaceLister: namespaceInformer.Lister(), namespaceLister: namespaceInformer.Lister(),
namespaceSynced: namespaceInformer.Informer().HasSynced, namespaceSynced: namespaceInformer.Informer().HasSynced,
workspaceLister: workspaceInformer.Lister(),
workspaceSynced: workspaceInformer.Informer().HasSynced,
workerLoopPeriod: time.Second, workerLoopPeriod: time.Second,
} }
...@@ -163,7 +171,7 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) error { ...@@ -163,7 +171,7 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) error {
klog.Info("starting devops project controller") klog.Info("starting devops project controller")
defer klog.Info("shutting down devops project controller") defer klog.Info("shutting down devops project controller")
if !cache.WaitForCacheSync(stopCh, c.devOpsProjectSynced) { if !cache.WaitForCacheSync(stopCh, c.devOpsProjectSynced, c.devOpsProjectSynced, c.workspaceSynced) {
return fmt.Errorf("failed to wait for caches to sync") return fmt.Errorf("failed to wait for caches to sync")
} }
...@@ -273,12 +281,18 @@ func (c *Controller) syncHandler(key string) error { ...@@ -273,12 +281,18 @@ func (c *Controller) syncHandler(key string) error {
} }
if !reflect.DeepEqual(copyProject, project) { if !reflect.DeepEqual(copyProject, project) {
_, err := c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject) copyProject, err = c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(copyProject)
if err != nil { if err != nil {
klog.V(8).Info(err, fmt.Sprintf("failed to update ns %s ", key)) klog.V(8).Info(err, fmt.Sprintf("failed to update ns %s ", key))
return err return err
} }
} }
if copyProject, err = c.bindWorkspace(copyProject); err != nil {
klog.Error(err)
return err
}
// Check project exists, otherwise we will create it. // Check project exists, otherwise we will create it.
_, err := c.devopsClient.GetDevOpsProject(copyProject.Status.AdminNamespace) _, err := c.devopsClient.GetDevOpsProject(copyProject.Status.AdminNamespace)
if err != nil { if err != nil {
...@@ -312,6 +326,38 @@ func (c *Controller) syncHandler(key string) error { ...@@ -312,6 +326,38 @@ func (c *Controller) syncHandler(key string) error {
return nil return nil
} }
func (c *Controller) bindWorkspace(project *devopsv1alpha3.DevOpsProject) (*devopsv1alpha3.DevOpsProject, error) {
workspaceName := project.Labels[constants.WorkspaceLabelKey]
if workspaceName == "" {
return project, nil
}
workspace, err := c.workspaceLister.Get(workspaceName)
if err != nil {
// skip if workspace not found
if errors.IsNotFound(err) {
return project, nil
}
klog.Error(err)
return nil, err
}
if !metav1.IsControlledBy(project, workspace) {
project.OwnerReferences = nil
if err := controllerutil.SetControllerReference(workspace, project, scheme.Scheme); err != nil {
klog.Error(err)
return nil, err
}
return c.kubesphereClient.DevopsV1alpha3().DevOpsProjects().Update(project)
}
return project, nil
}
func (c *Controller) deleteDevOpsProjectInDevOps(project *devopsv1alpha3.DevOpsProject) error { func (c *Controller) deleteDevOpsProjectInDevOps(project *devopsv1alpha3.DevOpsProject) error {
err := c.devopsClient.DeleteDevOpsProject(project.Status.AdminNamespace) err := c.devopsClient.DeleteDevOpsProject(project.Status.AdminNamespace)
...@@ -330,9 +376,16 @@ func (c *Controller) generateNewNamespace(project *devopsv1alpha3.DevOpsProject) ...@@ -330,9 +376,16 @@ func (c *Controller) generateNewNamespace(project *devopsv1alpha3.DevOpsProject)
}, },
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
GenerateName: project.Name, GenerateName: project.Name,
Labels: map[string]string{constants.DevOpsProjectLabelKey: project.Name}, Labels: map[string]string{
constants.DevOpsProjectLabelKey: project.Name,
},
}, },
} }
if creator := project.Annotations[constants.CreatorAnnotationKey]; creator != "" {
ns.Annotations = map[string]string{constants.CreatorAnnotationKey: creator}
}
controllerutil.SetControllerReference(project, ns, scheme.Scheme) controllerutil.SetControllerReference(project, ns, scheme.Scheme)
return ns return ns
} }
...@@ -120,8 +120,10 @@ func (f *fixture) newController() (*Controller, informers.SharedInformerFactory, ...@@ -120,8 +120,10 @@ func (f *fixture) newController() (*Controller, informers.SharedInformerFactory,
k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc()) k8sI := kubeinformers.NewSharedInformerFactory(f.kubeclient, noResyncPeriodFunc())
dI := fakeDevOps.New(f.initDevOpsProject...) dI := fakeDevOps.New(f.initDevOpsProject...)
c := NewController(f.kubeclient, f.client, dI, k8sI.Core().V1().Namespaces(), c := NewController(f.kubeclient, f.client, dI,
i.Devops().V1alpha3().DevOpsProjects()) k8sI.Core().V1().Namespaces(),
i.Devops().V1alpha3().DevOpsProjects(),
i.Tenant().V1alpha1().Workspaces())
c.devOpsProjectSynced = alwaysReady c.devOpsProjectSynced = alwaysReady
c.eventRecorder = &record.FakeRecorder{} c.eventRecorder = &record.FakeRecorder{}
...@@ -251,7 +253,9 @@ func filterInformerActions(actions []core.Action) []core.Action { ...@@ -251,7 +253,9 @@ func filterInformerActions(actions []core.Action) []core.Action {
(action.Matches("list", devopsprojects.ResourcePluralDevOpsProject) || (action.Matches("list", devopsprojects.ResourcePluralDevOpsProject) ||
action.Matches("watch", devopsprojects.ResourcePluralDevOpsProject) || action.Matches("watch", devopsprojects.ResourcePluralDevOpsProject) ||
action.Matches("list", "namespaces") || action.Matches("list", "namespaces") ||
action.Matches("watch", "namespaces")) { action.Matches("watch", "namespaces") ||
action.Matches("watch", "workspaces") ||
action.Matches("list", "workspaces")) {
continue continue
} }
ret = append(ret, action) ret = append(ret, action)
......
...@@ -229,17 +229,16 @@ func (c *Controller) reconcile(key string) error { ...@@ -229,17 +229,16 @@ func (c *Controller) reconcile(key string) error {
} }
if globalRoleBinding.RoleRef.Name == iamv1alpha2.PlatformAdmin { if globalRoleBinding.RoleRef.Name == iamv1alpha2.PlatformAdmin {
if err := c.relateToClusterAdmin(globalRoleBinding); err != nil { if err := c.assignClusterAdminRole(globalRoleBinding); err != nil {
klog.Error(err) klog.Error(err)
return err return err
} }
if c.devopsClient != nil { }
username := findExpectUsername(globalRoleBinding)
err = c.devopsClient.AssignGlobalRole(modeldevops.JenkinsAdminRoleName, username) if c.devopsClient != nil {
if err != nil { if err := c.assignDevOpsAdminRole(globalRoleBinding); err != nil {
klog.Errorf("%+v", err) klog.Error(err)
return err return err
}
} }
} }
...@@ -299,11 +298,9 @@ func (c *Controller) multiClusterSync(globalRoleBinding *iamv1alpha2.GlobalRoleB ...@@ -299,11 +298,9 @@ func (c *Controller) multiClusterSync(globalRoleBinding *iamv1alpha2.GlobalRoleB
return nil return nil
} }
func (c *Controller) relateToClusterAdmin(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error { func (c *Controller) assignClusterAdminRole(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
username := findExpectUsername(globalRoleBinding) username := findExpectUsername(globalRoleBinding)
// unexpected
if username == "" { if username == "" {
return nil return nil
} }
...@@ -436,6 +433,16 @@ func (c *Controller) ensureNotControlledByKubefed(globalRoleBinding *iamv1alpha2 ...@@ -436,6 +433,16 @@ func (c *Controller) ensureNotControlledByKubefed(globalRoleBinding *iamv1alpha2
return nil return nil
} }
func (c *Controller) assignDevOpsAdminRole(globalRoleBinding *iamv1alpha2.GlobalRoleBinding) error {
if username := findExpectUsername(globalRoleBinding); username != "" {
if err := c.devopsClient.AssignGlobalRole(modeldevops.JenkinsAdminRoleName, username); err != nil {
klog.Errorf("%+v", err)
return err
}
}
return nil
}
func ensureSubjectAPIVersionIsValid(subjects []rbacv1.Subject) []rbacv1.Subject { func ensureSubjectAPIVersionIsValid(subjects []rbacv1.Subject) []rbacv1.Subject {
validSubjects := make([]rbacv1.Subject, 0) validSubjects := make([]rbacv1.Subject, 0)
for _, subject := range subjects { for _, subject := range subjects {
......
...@@ -5,8 +5,6 @@ import ( ...@@ -5,8 +5,6 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apiserver/pkg/authentication/user" "k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
...@@ -1079,7 +1077,7 @@ func (h *iamHandler) resolveNamespace(namespace string, devops string) (string, ...@@ -1079,7 +1077,7 @@ func (h *iamHandler) resolveNamespace(namespace string, devops string) (string,
if devops == "" { if devops == "" {
return namespace, nil return namespace, nil
} }
return h.am.GetControlledNamespace(devops) return h.am.GetDevOpsRelatedNamespace(devops)
} }
func (h *iamHandler) PatchWorkspaceRole(request *restful.Request, response *restful.Response) { func (h *iamHandler) PatchWorkspaceRole(request *restful.Request, response *restful.Response) {
...@@ -1213,18 +1211,7 @@ func (h *iamHandler) updateGlobalRoleBinding(operator user.Info, user *iamv1alph ...@@ -1213,18 +1211,7 @@ func (h *iamHandler) updateGlobalRoleBinding(operator user.Info, user *iamv1alph
func (h *iamHandler) ListUserLoginRecords(request *restful.Request, response *restful.Response) { func (h *iamHandler) ListUserLoginRecords(request *restful.Request, response *restful.Response) {
username := request.PathParameter("user") username := request.PathParameter("user")
queryParam := query.ParseQueryParameter(request) queryParam := query.ParseQueryParameter(request)
selector, _ := labels.Parse(queryParam.LabelSelector) queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", iamv1alpha2.UserReferenceLabel, username))
if selector == nil {
selector = labels.NewSelector()
}
requirement, err := labels.NewRequirement(iamv1alpha2.UserReferenceLabel, selection.Equals, []string{username})
if err != nil {
klog.Error(err)
handleError(request, response, err)
return
}
selector.Add(*requirement)
queryParam.LabelSelector = selector.String()
result, err := h.im.ListLoginRecords(queryParam) result, err := h.im.ListLoginRecords(queryParam)
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
......
...@@ -290,6 +290,7 @@ func (h *handler) passwordGrant(username string, password string, req *restful.R ...@@ -290,6 +290,7 @@ func (h *handler) passwordGrant(username string, password string, req *restful.R
return return
default: default:
response.WriteError(http.StatusInternalServerError, apierrors.NewInternalError(err)) response.WriteError(http.StatusInternalServerError, apierrors.NewInternalError(err))
return
} }
} }
......
...@@ -101,6 +101,29 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo ...@@ -101,6 +101,29 @@ func (h *tenantHandler) ListNamespaces(req *restful.Request, resp *restful.Respo
resp.WriteEntity(result) resp.WriteEntity(result)
} }
func (h *tenantHandler) ListDevOpsProjects(req *restful.Request, resp *restful.Response) {
user, ok := request.UserFrom(req.Request.Context())
queryParam := query.ParseQueryParameter(req)
if !ok {
err := fmt.Errorf("cannot obtain user info")
klog.Errorln(err)
api.HandleForbidden(resp, nil, err)
return
}
workspace := req.PathParameter("workspace")
result, err := h.tenant.ListDevOpsProjects(user, workspace, queryParam)
if err != nil {
api.HandleInternalError(resp, nil, err)
return
}
resp.WriteEntity(result)
}
func (h *tenantHandler) CreateNamespace(request *restful.Request, response *restful.Response) { func (h *tenantHandler) CreateNamespace(request *restful.Request, response *restful.Response) {
workspace := request.PathParameter("workspace") workspace := request.PathParameter("workspace")
var namespace corev1.Namespace var namespace corev1.Namespace
......
...@@ -27,7 +27,6 @@ import ( ...@@ -27,7 +27,6 @@ import (
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1" eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2" tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
typesv1beta1 "kubesphere.io/kubesphere/pkg/apis/types/v1beta1"
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned" kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
...@@ -105,31 +104,37 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s ...@@ -105,31 +104,37 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
To(handler.ListNamespaces). To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces for the current user"). Doc("List the namespaces for the current user").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/federatednamespaces"). ws.Route(ws.GET("/federatednamespaces").
To(handler.ListFederatedNamespaces). To(handler.ListFederatedNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the federated namespaces for the current user"). Doc("List the federated namespaces for the current user").
Returns(http.StatusOK, api.StatusOK, []typesv1beta1.FederatedNamespace{}). Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/federatednamespaces"). ws.Route(ws.GET("/workspaces/{workspace}/federatednamespaces").
To(handler.ListFederatedNamespaces). To(handler.ListFederatedNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the federated namespaces of the specified workspace for the current user"). Doc("List the federated namespaces of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, []typesv1beta1.FederatedNamespace{}). Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces"). ws.Route(ws.GET("/workspaces/{workspace}/namespaces").
To(handler.ListNamespaces). To(handler.ListNamespaces).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user"). Doc("List the namespaces of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(handler.ListDevOpsProjects).
Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the devops projects of the specified workspace for the current user").
Returns(http.StatusOK, api.StatusOK, api.ListResult{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/namespaces/{namespace}"). ws.Route(ws.GET("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.DescribeNamespace). To(handler.DescribeNamespace).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Retrieve namespace details."). Doc("Retrieve namespace details.").
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}"). ws.Route(ws.DELETE("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.DeleteNamespace). To(handler.DeleteNamespace).
...@@ -142,20 +147,20 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s ...@@ -142,20 +147,20 @@ func AddToContainer(c *restful.Container, factory informers.InformerFactory, k8s
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("List the namespaces of the specified workspace for the current user"). Doc("List the namespaces of the specified workspace for the current user").
Reads(corev1.Namespace{}). Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.PUT("/workspaces/{workspace}/namespaces/{namespace}"). ws.Route(ws.PUT("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.UpdateNamespace). To(handler.UpdateNamespace).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Reads(corev1.Namespace{}). Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.PATCH("/workspaces/{workspace}/namespaces/{namespace}"). ws.Route(ws.PATCH("/workspaces/{workspace}/namespaces/{namespace}").
To(handler.PatchNamespace). To(handler.PatchNamespace).
Consumes(mimePatch...). Consumes(mimePatch...).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Reads(corev1.Namespace{}). Reads(corev1.Namespace{}).
Returns(http.StatusOK, api.StatusOK, []corev1.Namespace{}). Returns(http.StatusOK, api.StatusOK, corev1.Namespace{}).
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/events"). ws.Route(ws.GET("/events").
......
...@@ -70,8 +70,9 @@ type AccessManagementInterface interface { ...@@ -70,8 +70,9 @@ type AccessManagementInterface interface {
RemoveUserFromNamespace(username string, namespace string) error RemoveUserFromNamespace(username string, namespace string) error
CreateClusterRoleBinding(username string, role string) error CreateClusterRoleBinding(username string, role string) error
RemoveUserFromCluster(username string) error RemoveUserFromCluster(username string) error
GetControlledNamespace(devops string) (string, error) GetDevOpsRelatedNamespace(devops string) (string, error)
GetControlledWorkspace(namespace string) (string, error) GetNamespaceControlledWorkspace(namespace string) (string, error)
GetDevOpsControlledWorkspace(devops string) (string, error)
PatchNamespaceRole(namespace string, role *rbacv1.Role) (*rbacv1.Role, error) PatchNamespaceRole(namespace string, role *rbacv1.Role) (*rbacv1.Role, error)
PatchClusterRole(clusterRole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) PatchClusterRole(clusterRole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error)
} }
...@@ -279,23 +280,19 @@ func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.Gl ...@@ -279,23 +280,19 @@ func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.Gl
} }
func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) { func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) {
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralRoleBinding, namespace, query.New()) roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralRoleBinding, namespace, query.New())
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
return nil, err return nil, err
} }
result := make([]*rbacv1.RoleBinding, 0) result := make([]*rbacv1.RoleBinding, 0)
for _, obj := range roleBindings.Items { for _, obj := range roleBindings.Items {
roleBinding := obj.(*rbacv1.RoleBinding) roleBinding := obj.(*rbacv1.RoleBinding)
if contains(roleBinding.Subjects, username) { if contains(roleBinding.Subjects, username) {
result = append(result, roleBinding) result = append(result, roleBinding)
} }
} }
return result, nil return result, nil
} }
...@@ -964,7 +961,7 @@ func (am *amOperator) GetClusterRole(name string) (*rbacv1.ClusterRole, error) { ...@@ -964,7 +961,7 @@ func (am *amOperator) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
} }
return obj.(*rbacv1.ClusterRole), nil return obj.(*rbacv1.ClusterRole), nil
} }
func (am *amOperator) GetControlledNamespace(devops string) (string, error) { func (am *amOperator) GetDevOpsRelatedNamespace(devops string) (string, error) {
obj, err := am.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", devops) obj, err := am.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", devops)
if err != nil { if err != nil {
klog.Error(err) klog.Error(err)
...@@ -975,7 +972,17 @@ func (am *amOperator) GetControlledNamespace(devops string) (string, error) { ...@@ -975,7 +972,17 @@ func (am *amOperator) GetControlledNamespace(devops string) (string, error) {
return devopsProject.Status.AdminNamespace, nil return devopsProject.Status.AdminNamespace, nil
} }
func (am *amOperator) GetControlledWorkspace(namespace string) (string, error) { func (am *amOperator) GetDevOpsControlledWorkspace(devops string) (string, error) {
obj, err := am.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", devops)
if err != nil {
klog.Error(err)
return "", err
}
devopsProject := obj.(*devopsv1alpha3.DevOpsProject)
return devopsProject.Labels[tenantv1alpha1.WorkspaceLabel], nil
}
func (am *amOperator) GetNamespaceControlledWorkspace(namespace string) (string, error) {
obj, err := am.resourceGetter.Get("namespaces", "", namespace) obj, err := am.resourceGetter.Get("namespaces", "", namespace)
if err != nil { if err != nil {
if errors.IsNotFound(err) { if errors.IsNotFound(err) {
......
...@@ -17,26 +17,109 @@ limitations under the License. ...@@ -17,26 +17,109 @@ limitations under the License.
package tenant package tenant
import ( import (
"kubesphere.io/kubesphere/pkg/models" "fmt"
"kubesphere.io/kubesphere/pkg/server/params" corev1 "k8s.io/api/core/v1"
dsClient "kubesphere.io/kubesphere/pkg/simple/client/devops" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/apiserver/request"
"kubesphere.io/kubesphere/pkg/constants"
resources "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
) )
type DevOpsProjectLister interface { func (t *tenantOperator) ListDevOpsProjects(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) {
ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) scope := request.ClusterScope
} if workspace != "" {
scope = request.WorkspaceScope
}
type devopsProjectLister struct { listDevOps := authorizer.AttributesRecord{
dsProject dsClient.ProjectOperator User: user,
} Verb: "list",
Workspace: workspace,
Resource: "devops",
ResourceRequest: true,
ResourceScope: scope,
}
func newProjectLister(client dsClient.ProjectOperator) DevOpsProjectLister { decision, _, err := t.authorizer.Authorize(listDevOps)
return &devopsProjectLister{ if err != nil {
dsProject: client, klog.Error(err)
return nil, err
} }
}
func (o *devopsProjectLister) ListDevOpsProjects(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { // allowed list devops in the specified scope
//TODO: @runzexia use informer to impl it if decision == authorizer.DecisionAllow {
return nil, nil // filter by workspace
if workspace != "" {
queryParam.Filters[query.FieldLabel] = query.Value(fmt.Sprintf("%s=%s", tenantv1alpha1.WorkspaceLabel, workspace))
}
result, err := t.resourceGetter.List(devopsv1alpha3.ResourcePluralDevOpsProject, "", queryParam)
if err != nil {
klog.Error(err)
return nil, err
}
return result, nil
}
roleBindings, err := t.am.ListRoleBindings(user.GetName(), "")
if err != nil {
klog.Error(err)
return nil, err
}
devopsProjects := make([]runtime.Object, 0)
for _, roleBinding := range roleBindings {
obj, err := t.resourceGetter.Get("namespaces", "", roleBinding.Namespace)
if err != nil {
klog.Error(err)
return nil, err
}
namespace := obj.(*corev1.Namespace)
controlledDevOpsProject := namespace.Labels[constants.DevOpsProjectLabelKey]
// skip if not controlled by devops project
if controlledDevOpsProject == "" {
continue
}
devopsProject, err := t.resourceGetter.Get(devopsv1alpha3.ResourcePluralDevOpsProject, "", controlledDevOpsProject)
if err != nil {
if errors.IsNotFound(err) {
klog.Warning("orphan devops project found ", devopsProject)
continue
}
klog.Error(err)
return nil, err
}
// skip if not controlled by the specified workspace
if workspace != "" &&
devopsProject.(*devopsv1alpha3.DevOpsProject).Labels[tenantv1alpha1.WorkspaceLabel] != workspace {
continue
}
if !contains(devopsProjects, devopsProject) {
devopsProjects = append(devopsProjects, namespace)
}
}
result := resources.DefaultList(devopsProjects, queryParam, func(left runtime.Object, right runtime.Object, field query.Field) bool {
return resources.DefaultObjectMetaCompare(left.(*devopsv1alpha3.DevOpsProject).ObjectMeta, right.(*devopsv1alpha3.DevOpsProject).ObjectMeta, field)
}, func(object runtime.Object, filter query.Filter) bool {
devopsProject := object.(*devopsv1alpha3.DevOpsProject).ObjectMeta
if workspace != "" {
if workspaceLabel, ok := devopsProject.Labels[tenantv1alpha1.WorkspaceLabel]; !ok || workspaceLabel != workspace {
return false
}
}
return resources.DefaultObjectMetaFilter(devopsProject, filter)
})
return result, nil
} }
...@@ -60,6 +60,7 @@ import ( ...@@ -60,6 +60,7 @@ import (
type Interface interface { type Interface interface {
ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error) ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error)
ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error) ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
ListDevOpsProjects(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
ListFederatedNamespaces(info user.Info, workspace string, param *query.Query) (*api.ListResult, error) ListFederatedNamespaces(info user.Info, workspace string, param *query.Query) (*api.ListResult, error)
CreateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error) CreateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
CreateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error) CreateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册