From 992be764a1cf3728a273ded1a0048718e2f14f4e Mon Sep 17 00:00:00 2001 From: hongming Date: Thu, 7 May 2020 09:14:54 +0800 Subject: [PATCH] fix list namespaces Signed-off-by: hongming --- pkg/apiserver/filters/kubeapiserver.go | 2 +- pkg/models/iam/am/am.go | 8 +- pkg/models/resources/v1alpha3/interface.go | 4 +- pkg/models/tenant/tenant.go | 44 +-- pkg/models/tenant/tenent_test.go | 336 +++++++++++++++++++++ 5 files changed, 367 insertions(+), 27 deletions(-) create mode 100644 pkg/models/tenant/tenent_test.go diff --git a/pkg/apiserver/filters/kubeapiserver.go b/pkg/apiserver/filters/kubeapiserver.go index ba972085..72be1cc0 100644 --- a/pkg/apiserver/filters/kubeapiserver.go +++ b/pkg/apiserver/filters/kubeapiserver.go @@ -35,8 +35,8 @@ func WithKubeAPIServer(handler http.Handler, config *rest.Config, failed proxy.E // Do not cover k8s client authorization header req.Header.Del("Authorization") - httpProxy := proxy.NewUpgradeAwareHandler(&s, defaultTransport, true, false, failed) + httpProxy.UpgradeTransport = proxy.NewUpgradeRequestRoundTripper(defaultTransport, defaultTransport) httpProxy.ServeHTTP(w, req) return } diff --git a/pkg/models/iam/am/am.go b/pkg/models/iam/am/am.go index c0f8eee5..2e173793 100644 --- a/pkg/models/iam/am/am.go +++ b/pkg/models/iam/am/am.go @@ -90,7 +90,7 @@ func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalR klog.Error(err) return nil, err } - if len(roleBindings) > 1 { + if len(userRoleBindings) > 1 { klog.Warningf("conflict global role binding, username: %s", username) } return role, nil @@ -135,7 +135,7 @@ func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1 return nil, err } - if len(roleBindings) > 1 { + if len(userRoleBindings) > 1 { klog.Warningf("conflict workspace role binding, username: %s", username) } @@ -178,7 +178,7 @@ func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv klog.Error(err) return nil, err } - if len(roleBindings) > 1 { + if len(userRoleBindings) > 1 { klog.Warningf("conflict role binding, username: %s", username) } return role, nil @@ -221,7 +221,7 @@ func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.Cl klog.Error(err) return nil, err } - if len(roleBindings) > 1 { + if len(userRoleBindings) > 1 { klog.Warningf("conflict cluster role binding, username: %s", username) } return role, nil diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go index 6e3040b1..d50a6ea1 100644 --- a/pkg/models/resources/v1alpha3/interface.go +++ b/pkg/models/resources/v1alpha3/interface.go @@ -63,6 +63,8 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) case query.FieldName: return strings.Compare(left.Name, right.Name) > 0 // ?sortBy=creationTimestamp + default: + fallthrough case query.FieldCreateTime: fallthrough case query.FieldCreationTimeStamp: @@ -71,8 +73,6 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field) return strings.Compare(left.Name, right.Name) > 0 } return left.CreationTimestamp.After(right.CreationTimestamp.Time) - default: - return false } } diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index 3bd88d6f..ba08a91c 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -28,6 +28,7 @@ import ( tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer" "kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory" + unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union" "kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/informers" "kubesphere.io/kubesphere/pkg/models/iam/am" @@ -48,10 +49,12 @@ type tenantOperator struct { func New(informers informers.InformerFactory) Interface { amOperator := am.NewAMOperator(informers) + rbacAuthorizer := authorizerfactory.NewRBACAuthorizer(amOperator) opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator) + authorizers := unionauthorizer.New(opaAuthorizer, rbacAuthorizer) return &tenantOperator{ am: amOperator, - authorizer: opaAuthorizer, + authorizer: authorizers, resourceGetter: resourcesv1alpha3.NewResourceGetter(informers), } } @@ -59,11 +62,12 @@ func New(informers informers.InformerFactory) Interface { func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) (*api.ListResult, error) { listWS := authorizer.AttributesRecord{ - User: user, - Verb: "list", - APIGroup: "tenant.kubesphere.io", - APIVersion: "v1alpha2", - Resource: "workspaces", + User: user, + Verb: "list", + APIGroup: "tenant.kubesphere.io", + APIVersion: "v1alpha2", + Resource: "workspaces", + ResourceRequest: true, } decision, _, err := t.authorizer.Authorize(listWS) @@ -100,7 +104,7 @@ func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) workspace, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspaceName) if errors.IsNotFound(err) { - klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) + klog.Warningf("workspace role binding: %+v found but workspace not exist", roleBinding.ObjectMeta) continue } @@ -126,12 +130,13 @@ func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query) func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryParam *query.Query) (*api.ListResult, error) { listNSInWS := authorizer.AttributesRecord{ - User: user, - Verb: "list", - APIGroup: "", - APIVersion: "v1", - Workspace: workspace, - Resource: "namespaces", + User: user, + Verb: "list", + APIGroup: "", + APIVersion: "v1", + Workspace: workspace, + Resource: "namespaces", + ResourceRequest: true, } decision, _, err := t.authorizer.Authorize(listNSInWS) @@ -165,19 +170,18 @@ func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryP namespaces := make([]runtime.Object, 0) for _, roleBinding := range roleBindings { - namespaceName := roleBinding.Namespace - namespace, err := t.resourceGetter.Get("namespaces", "", namespaceName) - - if errors.IsNotFound(err) { - klog.Warningf("workspace role: %+v found but workspace not exist", roleBinding.ObjectMeta) - continue - } + namespace, err := t.resourceGetter.Get("namespaces", "", roleBinding.Namespace) if err != nil { klog.Error(err) return nil, err } + // skip if not controlled by the specified workspace + if ns := namespace.(*corev1.Namespace); ns.Labels[tenantv1alpha1.WorkspaceLabel] != workspace { + continue + } + if !contains(namespaces, namespace) { namespaces = append(namespaces, namespace) } diff --git a/pkg/models/tenant/tenent_test.go b/pkg/models/tenant/tenent_test.go new file mode 100644 index 00000000..3468f30e --- /dev/null +++ b/pkg/models/tenant/tenent_test.go @@ -0,0 +1,336 @@ +/* + * + * Copyright 2020 The KubeSphere Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * / + */ + +package tenant + +import ( + "github.com/google/go-cmp/cmp" + fakeapp "github.com/kubernetes-sigs/application/pkg/client/clientset/versioned/fake" + fakeistio "istio.io/client-go/pkg/clientset/versioned/fake" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" + fakek8s "k8s.io/client-go/kubernetes/fake" + "kubesphere.io/kubesphere/pkg/api" + iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2" + tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1" + "kubesphere.io/kubesphere/pkg/apiserver/query" + fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" + "kubesphere.io/kubesphere/pkg/informers" + "testing" +) + +func TestTenantOperator_ListWorkspaces(t *testing.T) { + tenantOperator := prepare() + tests := []struct { + name string + result *api.ListResult + username string + expectError error + }{ + { + name: "list workspace", + username: "admin", + result: &api.ListResult{ + Items: workspaces, + TotalItems: len(workspaces), + }, + }, + { + name: "list workspaces", + username: "regular", + result: &api.ListResult{ + Items: []interface{}{workspaceBar}, + TotalItems: 1, + }, + }, + } + + for _, test := range tests { + result, err := tenantOperator.ListWorkspaces(&user.DefaultInfo{Name: test.username}, query.New()) + + if err != nil { + if test.expectError != err { + t.Error(err) + } + continue + } + + if diff := cmp.Diff(result, test.result); diff != "" { + t.Error(diff) + } + } +} + +func TestTenantOperator_ListNamespaces(t *testing.T) { + tenantOperator := prepare() + tests := []struct { + name string + result *api.ListResult + username string + workspace string + expectError error + }{ + { + name: "list namespaces", + workspace: "foo", + username: "admin", + result: &api.ListResult{ + Items: []interface{}{foo2, foo1}, + TotalItems: 2, + }, + }, + { + name: "list namespaces", + workspace: "foo", + username: "regular", + result: &api.ListResult{ + Items: []interface{}{}, + TotalItems: 0, + }, + }, + { + name: "list namespaces", + workspace: "bar", + username: "regular", + result: &api.ListResult{ + Items: []interface{}{bar1}, + TotalItems: 1, + }, + }, + } + + for _, test := range tests { + result, err := tenantOperator.ListNamespaces(&user.DefaultInfo{Name: test.username}, test.workspace, query.New()) + + if err != nil { + if test.expectError != err { + t.Error(err) + } + continue + } + + if diff := cmp.Diff(result, test.result); diff != "" { + t.Error(diff) + } + } +} + +var ( + foo1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo1", + Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: "foo"}, + }, + } + + foo2 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo2", + Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: "foo"}, + }, + } + bar1 = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar1", + Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: "bar"}, + }, + } + adminGlobalRole = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + }, + AggregationRule: nil, + } + regularGlobalRole = &iamv1alpha2.GlobalRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "regular", + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{}, + APIGroups: []string{}, + Resources: []string{}, + }, + }, + AggregationRule: nil, + } + reguarWorksapceRole = &iamv1alpha2.WorkspaceRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "workspace-regular", + Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: "bar"}, + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{}, + APIGroups: []string{}, + Resources: []string{}, + }, + }, + AggregationRule: nil, + } + adminGlobalRoleBinding = &iamv1alpha2.GlobalRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "global-admin", + }, + Subjects: []rbacv1.Subject{{ + Kind: "User", + Name: "admin", + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamv1alpha2.SchemeGroupVersion.String(), + Kind: iamv1alpha2.ResourceKindGlobalRole, + Name: "global-admin", + }, + } + regularGlobalRoleBinding = &iamv1alpha2.GlobalRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "regular", + }, + Subjects: []rbacv1.Subject{{ + Kind: "User", + Name: "regular", + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamv1alpha2.SchemeGroupVersion.String(), + Kind: iamv1alpha2.ResourceKindGlobalRole, + Name: "regular", + }, + } + + regularWorkspaceRoleBinding = &iamv1alpha2.WorkspaceRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "workspace-regular", + Labels: map[string]string{tenantv1alpha1.WorkspaceLabel: "bar"}, + }, + Subjects: []rbacv1.Subject{{ + Kind: "User", + Name: "regular", + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: iamv1alpha2.SchemeGroupVersion.String(), + Kind: iamv1alpha2.ResourceKindGlobalRole, + Name: "workspace-regular", + }, + } + bar1NamespaceRole = &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "admin", + Namespace: "bar1", + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"*"}, + APIGroups: []string{"*"}, + Resources: []string{"*"}, + }, + }, + } + bar1NamespaceRoleBinding = &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "admin", + Namespace: "bar1", + }, + Subjects: []rbacv1.Subject{{ + Kind: "User", + Name: "regular", + }}, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.SchemeGroupVersion.String(), + Kind: "Role", + Name: "admin", + }, + } + workspaceFoo = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + } + workspaceBar = &tenantv1alpha1.Workspace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + }, + } + + workspaces = []interface{}{workspaceFoo, workspaceBar} + namespaces = []interface{}{foo1, foo2, bar1} + globalRoles = []interface{}{adminGlobalRole, regularGlobalRole} + globalRoleBindings = []interface{}{adminGlobalRoleBinding, regularGlobalRoleBinding} + workspaceRoles = []interface{}{regularGlobalRole} + workspaceRoleBindings = []interface{}{regularWorkspaceRoleBinding} + namespaceRoles = []interface{}{bar1NamespaceRole} + namespaceRoleBindings = []interface{}{bar1NamespaceRoleBinding} +) + +func prepare() Interface { + ksClient := fakeks.NewSimpleClientset() + k8sClient := fakek8s.NewSimpleClientset() + istioClient := fakeistio.NewSimpleClientset() + appClient := fakeapp.NewSimpleClientset() + fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient) + + for _, workspace := range workspaces { + fakeInformerFactory.KubeSphereSharedInformerFactory().Tenant().V1alpha1(). + Workspaces().Informer().GetIndexer().Add(workspace) + } + + for _, namespace := range namespaces { + fakeInformerFactory.KubernetesSharedInformerFactory().Core().V1(). + Namespaces().Informer().GetIndexer().Add(namespace) + } + + for _, globalRole := range globalRoles { + fakeInformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2(). + GlobalRoles().Informer().GetIndexer().Add(globalRole) + } + + for _, globalRoleBinding := range globalRoleBindings { + fakeInformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2(). + GlobalRoleBindings().Informer().GetIndexer().Add(globalRoleBinding) + } + + for _, workspaceRole := range workspaceRoles { + fakeInformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2(). + WorkspaceRoles().Informer().GetIndexer().Add(workspaceRole) + } + + for _, workspaceRoleBinding := range workspaceRoleBindings { + fakeInformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2(). + WorkspaceRoleBindings().Informer().GetIndexer().Add(workspaceRoleBinding) + } + + for _, role := range namespaceRoles { + fakeInformerFactory.KubernetesSharedInformerFactory().Rbac().V1(). + Roles().Informer().GetIndexer().Add(role) + } + + for _, roleBinding := range namespaceRoleBindings { + fakeInformerFactory.KubernetesSharedInformerFactory().Rbac().V1(). + RoleBindings().Informer().GetIndexer().Add(roleBinding) + } + + return New(fakeInformerFactory) +} -- GitLab