improve IAM module

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2020-05-22 09:35:05 +08:00
parent 0d12529051
commit 8f93266ec0
640 changed files with 50221 additions and 18179 deletions

View File

@@ -18,18 +18,19 @@
package am
import (
"encoding/json"
"fmt"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
k8sinformers "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/query"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"net/http"
@@ -38,7 +39,7 @@ import (
type AccessManagementInterface interface {
GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error)
GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error)
GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error)
GetClusterRoleOfUser(username string) (*rbacv1.ClusterRole, error)
GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error)
ListRoles(username string, query *query.Query) (*api.ListResult, error)
ListClusterRoles(query *query.Query) (*api.ListResult, error)
@@ -50,42 +51,55 @@ type AccessManagementInterface interface {
ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error)
ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error)
GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error)
GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) (string, []rbacv1.PolicyRule, error)
GetGlobalRole(globalRole string) (*iamv1alpha2.GlobalRole, error)
GetWorkspaceRole(workspace string, name string) (*iamv1alpha2.WorkspaceRole, error)
CreateOrUpdateGlobalRoleBinding(username string, globalRole string) error
CreateOrUpdateWorkspaceRole(workspace string, workspaceRole *iamv1alpha2.WorkspaceRole) (*iamv1alpha2.WorkspaceRole, error)
CreateOrUpdateGlobalRole(globalRole *iamv1alpha2.GlobalRole) (*iamv1alpha2.GlobalRole, error)
DeleteWorkspaceRole(workspace string, name string) error
DeleteGlobalRole(name string) error
CreateOrUpdateClusterRole(clusterRole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error)
DeleteClusterRole(name string) error
GetClusterRole(name string) (*rbacv1.ClusterRole, error)
GetNamespaceRole(namespace string, name string) (*rbacv1.Role, error)
CreateOrUpdateNamespaceRole(namespace string, role *rbacv1.Role) (*rbacv1.Role, error)
DeleteNamespaceRole(namespace string, name string) error
CreateOrUpdateWorkspaceRoleBinding(username string, workspace string, role string) error
RemoveUserFromWorkspace(username string, workspace string) error
CreateOrUpdateNamespaceRoleBinding(username string, namespace string, role string) error
RemoveUserFromNamespace(username string, namespace string) error
CreateOrUpdateClusterRoleBinding(username string, role string) error
RemoveUserFromCluster(username string) error
GetControlledNamespace(devops string) (string, error)
}
type amOperator struct {
ksinformer ksinformers.SharedInformerFactory
k8sinformer k8sinformers.SharedInformerFactory
resourceGetter *resourcev1alpha3.ResourceGetter
ksclient kubesphere.Interface
k8sclient kubernetes.Interface
}
func NewAMOperator(factory informers.InformerFactory) AccessManagementInterface {
func NewReadOnlyOperator(factory informers.InformerFactory) AccessManagementInterface {
return &amOperator{
ksinformer: factory.KubeSphereSharedInformerFactory(),
k8sinformer: factory.KubernetesSharedInformerFactory(),
resourceGetter: resourcev1alpha3.NewResourceGetter(factory),
}
}
func NewOperator(factory informers.InformerFactory, ksclient kubesphere.Interface, k8sclient kubernetes.Interface) AccessManagementInterface {
return &amOperator{
resourceGetter: resourcev1alpha3.NewResourceGetter(factory),
ksclient: ksclient,
k8sclient: k8sclient,
}
}
func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error) {
roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything())
if err != nil {
klog.Error(err)
return nil, err
}
userRoleBindings := make([]*iamv1alpha2.GlobalRoleBinding, 0)
for _, roleBinding := range roleBindings {
if contains(roleBinding.Subjects, username) {
userRoleBindings = append(userRoleBindings, roleBinding)
}
}
userRoleBindings, err := am.ListGlobalRoleBindings(username)
if len(userRoleBindings) > 0 {
role, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(userRoleBindings[0].RoleRef.Name)
role, err := am.GetGlobalRole(userRoleBindings[0].RoleRef.Name)
if err != nil {
klog.Error(err)
return nil, err
@@ -93,7 +107,13 @@ func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalR
if len(userRoleBindings) > 1 {
klog.Warningf("conflict global role binding, username: %s", username)
}
return role, nil
out := role.DeepCopy()
if out.Annotations == nil {
out.Annotations = make(map[string]string, 0)
}
out.Annotations[iamv1alpha2.GlobalRoleAnnotation] = role.Name
return out, nil
}
err = &errors.StatusError{ErrStatus: metav1.Status{
@@ -112,23 +132,15 @@ func (am *amOperator) GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalR
func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error) {
roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.SelectorFromValidatedSet(labels.Set{tenantv1alpha1.WorkspaceLabel: workspace}))
userRoleBindings, err := am.ListWorkspaceRoleBindings(username, workspace)
if err != nil {
klog.Error(err)
return nil, err
}
userRoleBindings := make([]*iamv1alpha2.WorkspaceRoleBinding, 0)
for _, roleBinding := range roleBindings {
if contains(roleBinding.Subjects, username) {
userRoleBindings = append(userRoleBindings, roleBinding)
}
}
if len(userRoleBindings) > 0 {
role, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(userRoleBindings[0].RoleRef.Name)
role, err := am.GetWorkspaceRole(workspace, userRoleBindings[0].RoleRef.Name)
if err != nil {
klog.Error(err)
@@ -139,7 +151,12 @@ func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1
klog.Warningf("conflict workspace role binding, username: %s", username)
}
return role, nil
out := role.DeepCopy()
if out.Annotations == nil {
out.Annotations = make(map[string]string, 0)
}
out.Annotations[iamv1alpha2.WorkspaceRoleAnnotation] = role.Name
return out, nil
}
err = &errors.StatusError{ErrStatus: metav1.Status{
@@ -157,23 +174,15 @@ func (am *amOperator) GetWorkspaceRoleOfUser(username, workspace string) (*iamv1
}
func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) {
roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().List(labels.Everything())
userRoleBindings, err := am.ListRoleBindings(username, namespace)
if err != nil {
klog.Error(err)
return nil, err
}
userRoleBindings := make([]*rbacv1.RoleBinding, 0)
for _, roleBinding := range roleBindings {
if contains(roleBinding.Subjects, username) {
userRoleBindings = append(userRoleBindings, roleBinding)
}
}
if len(userRoleBindings) > 0 {
role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(namespace).Get(userRoleBindings[0].RoleRef.Name)
role, err := am.GetNamespaceRole(namespace, userRoleBindings[0].RoleRef.Name)
if err != nil {
klog.Error(err)
return nil, err
@@ -181,7 +190,13 @@ func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv
if len(userRoleBindings) > 1 {
klog.Warningf("conflict role binding, username: %s", username)
}
return role, nil
out := role.DeepCopy()
if out.Annotations == nil {
out.Annotations = make(map[string]string, 0)
}
out.Annotations[iamv1alpha2.RoleAnnotation] = role.Name
return out, nil
}
err = &errors.StatusError{ErrStatus: metav1.Status{
@@ -190,7 +205,7 @@ func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv
Reason: metav1.StatusReasonNotFound,
Details: &metav1.StatusDetails{
Group: rbacv1.SchemeGroupVersion.Group,
Kind: "RoleBinding",
Kind: iamv1alpha2.ResourceKindRoleBinding,
},
Message: fmt.Sprintf("role binding not found for %s in %s", username, namespace),
}}
@@ -198,33 +213,31 @@ func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv
return nil, err
}
// Get federated clusterrole of user if cluster is not empty, if cluster is empty means current cluster
func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.ClusterRole, error) {
roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
func (am *amOperator) GetClusterRoleOfUser(username string) (*rbacv1.ClusterRole, error) {
userRoleBindings, err := am.ListClusterRoleBindings(username)
if err != nil {
klog.Error(err)
return nil, err
}
userRoleBindings := make([]*rbacv1.ClusterRoleBinding, 0)
for _, roleBinding := range roleBindings {
if contains(roleBinding.Subjects, username) {
userRoleBindings = append(userRoleBindings, roleBinding)
}
}
if len(userRoleBindings) > 0 {
role, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(userRoleBindings[0].RoleRef.Name)
role, err := am.GetClusterRole(userRoleBindings[0].RoleRef.Name)
if err != nil {
klog.Error(err)
return nil, err
}
if len(userRoleBindings) > 1 {
klog.Warningf("conflict cluster role binding, username: %s", username)
}
return role, nil
out := role.DeepCopy()
if out.Annotations == nil {
out.Annotations = make(map[string]string, 0)
}
out.Annotations[iamv1alpha2.ClusterRoleAnnotation] = role.Name
return out, nil
}
err = &errors.StatusError{ErrStatus: metav1.Status{
Status: metav1.StatusFailure,
@@ -234,14 +247,14 @@ func (am *amOperator) GetClusterRoleOfUser(username, cluster string) (*rbacv1.Cl
Group: rbacv1.SchemeGroupVersion.Group,
Kind: "ClusterRoleBinding",
},
Message: fmt.Sprintf("cluster role binding not found for %s in %s", username, cluster),
Message: fmt.Sprintf("cluster role binding not found for %s", username),
}}
return nil, err
}
func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) {
roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.Everything())
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralWorkspaceRoleBinding, "", query.New())
if err != nil {
return nil, err
@@ -249,7 +262,8 @@ func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*
result := make([]*iamv1alpha2.WorkspaceRoleBinding, 0)
for _, roleBinding := range roleBindings {
for _, obj := range roleBindings.Items {
roleBinding := obj.(*iamv1alpha2.WorkspaceRoleBinding)
inSpecifiedWorkspace := workspace == "" || roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] == workspace
if contains(roleBinding.Subjects, username) && inSpecifiedWorkspace {
result = append(result, roleBinding)
@@ -261,7 +275,7 @@ func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*
func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) {
roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralClusterRoleBinding, "", query.New())
if err != nil {
return nil, err
@@ -269,7 +283,8 @@ func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.Cluste
result := make([]*rbacv1.ClusterRoleBinding, 0)
for _, roleBinding := range roleBindings {
for _, obj := range roleBindings.Items {
roleBinding := obj.(*rbacv1.ClusterRoleBinding)
if contains(roleBinding.Subjects, username) {
result = append(result, roleBinding)
}
@@ -279,7 +294,7 @@ func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.Cluste
}
func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) {
roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything())
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRoleBinding, "", query.New())
if err != nil {
return nil, err
@@ -287,7 +302,8 @@ func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.Gl
result := make([]*iamv1alpha2.GlobalRoleBinding, 0)
for _, roleBinding := range roleBindings {
for _, obj := range roleBindings.Items {
roleBinding := obj.(*iamv1alpha2.GlobalRoleBinding)
if contains(roleBinding.Subjects, username) {
result = append(result, roleBinding)
}
@@ -298,7 +314,7 @@ func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.Gl
func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) {
roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything())
roleBindings, err := am.resourceGetter.List(iamv1alpha2.ResourcesPluralRoleBinding, namespace, query.New())
if err != nil {
klog.Error(err)
@@ -307,7 +323,8 @@ func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.Ro
result := make([]*rbacv1.RoleBinding, 0)
for _, roleBinding := range roleBindings {
for _, obj := range roleBindings.Items {
roleBinding := obj.(*rbacv1.RoleBinding)
if contains(roleBinding.Subjects, username) {
result = append(result, roleBinding)
}
@@ -341,34 +358,557 @@ func (am *amOperator) ListGlobalRoles(query *query.Query) (*api.ListResult, erro
return am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRole, "", query)
}
func (am *amOperator) GetGlobalRole(globalRole string) (*iamv1alpha2.GlobalRole, error) {
obj, err := am.resourceGetter.Get(iamv1alpha2.ResourcesPluralGlobalRole, "", globalRole)
if err != nil {
klog.Error(err)
return nil, err
}
return obj.(*iamv1alpha2.GlobalRole), nil
}
func (am *amOperator) CreateOrUpdateGlobalRoleBinding(username string, globalRole string) error {
_, err := am.GetGlobalRole(globalRole)
if err != nil {
klog.Error(err)
return err
}
roleBindings, err := am.ListGlobalRoleBindings(username)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
if globalRole == roleBinding.RoleRef.Name {
return nil
}
err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
globalRoleBinding := iamv1alpha2.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", username, globalRole),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.SchemeGroupVersion.Group,
Name: username,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
Kind: iamv1alpha2.ResourceKindGlobalRole,
Name: globalRole,
},
}
if _, err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Create(&globalRoleBinding); err != nil {
return err
}
return nil
}
func (am *amOperator) CreateOrUpdateWorkspaceRole(workspace string, workspaceRole *iamv1alpha2.WorkspaceRole) (*iamv1alpha2.WorkspaceRole, error) {
if workspaceRole.Labels == nil {
workspaceRole.Labels = make(map[string]string, 0)
}
workspaceRole.Labels[tenantv1alpha1.WorkspaceLabel] = workspace
workspaceRole.Rules = make([]rbacv1.PolicyRule, 0)
var aggregateRoles []string
if err := json.Unmarshal([]byte(workspaceRole.Annotations[iamv1alpha2.AggregationRolesAnnotation]), &aggregateRoles); err == nil {
for _, roleName := range aggregateRoles {
role, err := am.GetWorkspaceRole("", roleName)
if err != nil {
klog.Error(err)
return nil, err
}
workspaceRole.Rules = append(workspaceRole.Rules, role.Rules...)
}
}
old, err := am.GetWorkspaceRole("", workspaceRole.Name)
if err != nil && !errors.IsNotFound(err) {
klog.Error(err)
return nil, err
}
var created *iamv1alpha2.WorkspaceRole
if old != nil {
created, err = am.ksclient.IamV1alpha2().WorkspaceRoles().Update(workspaceRole)
} else {
created, err = am.ksclient.IamV1alpha2().WorkspaceRoles().Create(workspaceRole)
}
return created, err
}
func (am *amOperator) CreateOrUpdateWorkspaceRoleBinding(username string, workspace string, role string) error {
_, err := am.GetWorkspaceRole(workspace, role)
if err != nil {
klog.Error(err)
return err
}
roleBindings, err := am.ListWorkspaceRoleBindings(username, workspace)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
if role == roleBinding.RoleRef.Name {
return nil
}
err := am.ksclient.IamV1alpha2().WorkspaceRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
roleBinding := iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s-%s", workspace, username, role),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username,
tenantv1alpha1.WorkspaceLabel: workspace},
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.SchemeGroupVersion.Group,
Name: username,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
Kind: iamv1alpha2.ResourceKindWorkspaceRole,
Name: role,
},
}
if _, err := am.ksclient.IamV1alpha2().WorkspaceRoleBindings().Create(&roleBinding); err != nil {
return err
}
return nil
}
func (am *amOperator) CreateOrUpdateClusterRoleBinding(username string, role string) error {
_, err := am.GetClusterRole(role)
if err != nil {
klog.Error(err)
return err
}
roleBindings, err := am.ListClusterRoleBindings(username)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
if role == roleBinding.RoleRef.Name {
return nil
}
err := am.k8sclient.RbacV1().ClusterRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
roleBinding := rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", username, role),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.SchemeGroupVersion.Group,
Name: username,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.SchemeGroupVersion.Group,
Kind: iamv1alpha2.ResourceKindClusterRole,
Name: role,
},
}
if _, err := am.k8sclient.RbacV1().ClusterRoleBindings().Create(&roleBinding); err != nil {
return err
}
return nil
}
func (am *amOperator) CreateOrUpdateNamespaceRoleBinding(username string, namespace string, role string) error {
_, err := am.GetNamespaceRole(namespace, role)
if err != nil {
klog.Error(err)
return err
}
roleBindings, err := am.ListRoleBindings(username, namespace)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
if role == roleBinding.RoleRef.Name {
return nil
}
err := am.k8sclient.RbacV1().RoleBindings(namespace).Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
roleBinding := rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", username, role),
Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
APIGroup: rbacv1.SchemeGroupVersion.Group,
Name: username,
},
},
RoleRef: rbacv1.RoleRef{
APIGroup: rbacv1.SchemeGroupVersion.Group,
Kind: iamv1alpha2.ResourceKindRole,
Name: role,
},
}
if _, err := am.k8sclient.RbacV1().RoleBindings(namespace).Create(&roleBinding); err != nil {
return err
}
return nil
}
func (am *amOperator) RemoveUserFromWorkspace(username string, workspace string) error {
roleBindings, err := am.ListWorkspaceRoleBindings(username, workspace)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
err := am.ksclient.IamV1alpha2().WorkspaceRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
return nil
}
func (am *amOperator) RemoveUserFromNamespace(username string, namespace string) error {
roleBindings, err := am.ListRoleBindings(username, namespace)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
err := am.k8sclient.RbacV1().RoleBindings(namespace).Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
return nil
}
func (am *amOperator) RemoveUserFromCluster(username string) error {
roleBindings, err := am.ListClusterRoleBindings(username)
if err != nil {
klog.Error(err)
return err
}
for _, roleBinding := range roleBindings {
err := am.k8sclient.RbacV1().ClusterRoleBindings().Delete(roleBinding.Name, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
continue
}
klog.Error(err)
return err
}
}
return nil
}
func (am *amOperator) CreateOrUpdateGlobalRole(globalRole *iamv1alpha2.GlobalRole) (*iamv1alpha2.GlobalRole, error) {
globalRole.Rules = make([]rbacv1.PolicyRule, 0)
var aggregateRoles []string
if err := json.Unmarshal([]byte(globalRole.Annotations[iamv1alpha2.AggregationRolesAnnotation]), &aggregateRoles); err == nil {
for _, roleName := range aggregateRoles {
role, err := am.GetGlobalRole(roleName)
if err != nil {
klog.Error(err)
return nil, err
}
globalRole.Rules = append(globalRole.Rules, role.Rules...)
}
}
old, err := am.GetGlobalRole(globalRole.Name)
if err != nil && !errors.IsNotFound(err) {
klog.Error(err)
return nil, err
}
var created *iamv1alpha2.GlobalRole
if old != nil {
created, err = am.ksclient.IamV1alpha2().GlobalRoles().Update(globalRole)
} else {
created, err = am.ksclient.IamV1alpha2().GlobalRoles().Create(globalRole)
}
return created, err
}
func (am *amOperator) CreateOrUpdateClusterRole(clusterRole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
clusterRole.Rules = make([]rbacv1.PolicyRule, 0)
var aggregateRoles []string
if err := json.Unmarshal([]byte(clusterRole.Annotations[iamv1alpha2.AggregationRolesAnnotation]), &aggregateRoles); err == nil {
for _, roleName := range aggregateRoles {
role, err := am.GetClusterRole(roleName)
if err != nil {
klog.Error(err)
return nil, err
}
clusterRole.Rules = append(clusterRole.Rules, role.Rules...)
}
}
old, err := am.GetClusterRole(clusterRole.Name)
if err != nil && !errors.IsNotFound(err) {
klog.Error(err)
return nil, err
}
var created *rbacv1.ClusterRole
if old != nil {
created, err = am.k8sclient.RbacV1().ClusterRoles().Update(clusterRole)
} else {
created, err = am.k8sclient.RbacV1().ClusterRoles().Create(clusterRole)
}
return created, err
}
func (am *amOperator) CreateOrUpdateNamespaceRole(namespace string, role *rbacv1.Role) (*rbacv1.Role, error) {
role.Rules = make([]rbacv1.PolicyRule, 0)
role.Namespace = namespace
var aggregateRoles []string
if err := json.Unmarshal([]byte(role.Annotations[iamv1alpha2.AggregationRolesAnnotation]), &aggregateRoles); err == nil {
for _, roleName := range aggregateRoles {
role, err := am.GetNamespaceRole(namespace, roleName)
if err != nil {
klog.Error(err)
return nil, err
}
role.Rules = append(role.Rules, role.Rules...)
}
}
old, err := am.GetNamespaceRole(namespace, role.Name)
if err != nil && !errors.IsNotFound(err) {
klog.Error(err)
return nil, err
}
var created *rbacv1.Role
if old != nil {
created, err = am.k8sclient.RbacV1().Roles(namespace).Update(role)
} else {
created, err = am.k8sclient.RbacV1().Roles(namespace).Create(role)
}
return created, err
}
func (am *amOperator) DeleteWorkspaceRole(workspace string, name string) error {
workspaceRole, err := am.GetWorkspaceRole(workspace, name)
if err != nil {
return err
}
return am.ksclient.IamV1alpha2().WorkspaceRoles().Delete(workspaceRole.Name, metav1.NewDeleteOptions(0))
}
func (am *amOperator) DeleteGlobalRole(name string) error {
return am.ksclient.IamV1alpha2().GlobalRoles().Delete(name, metav1.NewDeleteOptions(0))
}
func (am *amOperator) DeleteClusterRole(name string) error {
return am.k8sclient.RbacV1().ClusterRoles().Delete(name, metav1.NewDeleteOptions(0))
}
func (am *amOperator) DeleteNamespaceRole(namespace string, name string) error {
return am.k8sclient.RbacV1().Roles(namespace).Delete(name, metav1.NewDeleteOptions(0))
}
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) {
func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, namespace string) (string, []rbacv1.PolicyRule, error) {
switch roleRef.Kind {
case iamv1alpha2.ResourceKindRole:
role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(bindingNamespace).Get(roleRef.Name)
role, err := am.GetNamespaceRole(namespace, roleRef.Name)
if err != nil {
return nil, err
return "", nil, err
}
return role.Rules, nil
return role.Annotations[iamv1alpha2.RegoOverrideAnnotation], role.Rules, nil
case iamv1alpha2.ResourceKindClusterRole:
clusterRole, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(roleRef.Name)
clusterRole, err := am.GetClusterRole(roleRef.Name)
if err != nil {
return nil, err
return "", nil, err
}
return clusterRole.Rules, nil
return clusterRole.Annotations[iamv1alpha2.RegoOverrideAnnotation], clusterRole.Rules, nil
case iamv1alpha2.ResourceKindGlobalRole:
globalRole, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(roleRef.Name)
globalRole, err := am.GetGlobalRole(roleRef.Name)
if err != nil {
return nil, err
return "", nil, err
}
return globalRole.Rules, nil
return globalRole.Annotations[iamv1alpha2.RegoOverrideAnnotation], globalRole.Rules, nil
case iamv1alpha2.ResourceKindWorkspaceRole:
workspaceRole, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(roleRef.Name)
workspaceRole, err := am.GetWorkspaceRole("", roleRef.Name)
if err != nil {
return nil, err
return "", nil, err
}
return workspaceRole.Rules, nil
return workspaceRole.Annotations[iamv1alpha2.RegoOverrideAnnotation], workspaceRole.Rules, nil
default:
return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
return "", nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
}
}
func (am *amOperator) GetWorkspaceRole(workspace string, name string) (*iamv1alpha2.WorkspaceRole, error) {
obj, err := am.resourceGetter.Get(iamv1alpha2.ResourcesPluralWorkspaceRole, "", name)
if err != nil {
klog.Error(err)
return nil, err
}
workspaceRole := obj.(*iamv1alpha2.WorkspaceRole)
if workspace != "" && workspaceRole.Labels[tenantv1alpha1.WorkspaceLabel] != workspace {
err := errors.NewNotFound(iamv1alpha2.Resource(iamv1alpha2.ResourcesSingularWorkspaceRole), name)
klog.Error(err)
return nil, err
}
return workspaceRole, nil
}
func (am *amOperator) GetNamespaceRole(namespace string, name string) (*rbacv1.Role, error) {
obj, err := am.resourceGetter.Get(iamv1alpha2.ResourcesPluralRole, namespace, name)
if err != nil {
klog.Error(err)
return nil, err
}
return obj.(*rbacv1.Role), nil
}
func (am *amOperator) GetClusterRole(name string) (*rbacv1.ClusterRole, error) {
obj, err := am.resourceGetter.Get(iamv1alpha2.ResourcesPluralClusterRole, "", name)
if err != nil {
klog.Error(err)
return nil, err
}
return obj.(*rbacv1.ClusterRole), nil
}
func (am *amOperator) GetControlledNamespace(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.Status.AdminNamespace, nil
}

View File

@@ -28,13 +28,14 @@ import (
kubesphereclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
"net/mail"
)
type IdentityManagementInterface interface {
CreateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error)
ListUsers(query *query.Query) (*api.ListResult, error)
DeleteUser(username string) error
ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error)
UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error)
DescribeUser(username string) (*iamv1alpha2.User, error)
Authenticate(username, password string) (*iamv1alpha2.User, error)
}
@@ -60,33 +61,85 @@ type defaultIMOperator struct {
resourceGetter *resourcev1alpha3.ResourceGetter
}
func (im *defaultIMOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
func (im *defaultIMOperator) UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", user.Name)
if err != nil {
klog.Error(err)
return nil, err
}
old := obj.(*iamv1alpha2.User).DeepCopy()
user.Annotations[iamv1alpha2.PasswordEncryptedAnnotation] = old.Annotations[iamv1alpha2.PasswordEncryptedAnnotation]
user.Spec.EncryptedPassword = old.Spec.EncryptedPassword
return im.ksClient.IamV1alpha2().Users().Update(user)
}
func (im *defaultIMOperator) Authenticate(username, password string) (*iamv1alpha2.User, error) {
user, err := im.DescribeUser(username)
var user *iamv1alpha2.User
if err != nil {
klog.Error(err)
return nil, err
if _, err := mail.ParseAddress(username); err != nil {
obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username)
if err != nil {
klog.Error(err)
return nil, err
}
user = obj.(*iamv1alpha2.User)
} else {
objs, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", &query.Query{
Pagination: query.NoPagination,
Filters: map[query.Field]query.Value{iamv1alpha2.FieldEmail: query.Value(username)},
})
if err != nil {
klog.Error(err)
return nil, err
}
if len(objs.Items) != 1 {
if len(objs.Items) == 0 {
klog.Warningf("username or email: %s not exist", username)
} else {
klog.Errorf("duplicate user entries: %+v", objs)
}
return nil, AuthFailedIncorrectPassword
}
user = objs.Items[0].(*iamv1alpha2.User)
}
if checkPasswordHash(password, user.Spec.EncryptedPassword) {
return user, nil
}
return nil, AuthFailedIncorrectPassword
}
func (im *defaultIMOperator) ListUsers(query *query.Query) (*api.ListResult, error) {
result, err := im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query)
func (im *defaultIMOperator) ListUsers(query *query.Query) (result *api.ListResult, err error) {
result, err = im.resourceGetter.List(iamv1alpha2.ResourcesPluralUser, "", query)
if err != nil {
klog.Error(err)
return nil, err
}
items := make([]interface{}, 0)
for _, item := range result.Items {
user := item.(*iamv1alpha2.User)
out := user.DeepCopy()
// ensure encrypted password will not be output
out.Spec.EncryptedPassword = ""
items = append(items, out)
}
result.Items = items
return result, nil
}
@@ -96,13 +149,19 @@ func checkPasswordHash(password, hash string) bool {
}
func (im *defaultIMOperator) DescribeUser(username string) (*iamv1alpha2.User, error) {
user, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username)
obj, err := im.resourceGetter.Get(iamv1alpha2.ResourcesPluralUser, "", username)
if err != nil {
klog.Error(err)
return nil, err
}
return user.(*iamv1alpha2.User), nil
user := obj.(*iamv1alpha2.User)
out := user.DeepCopy()
// ensure encrypted password will not be output
out.Spec.EncryptedPassword = ""
return out, nil
}
func (im *defaultIMOperator) DeleteUser(username string) error {

View File

@@ -36,7 +36,7 @@ func NewLDAPOperator(ldapClient ldap.Interface) IdentityManagementInterface {
}
}
func (im *ldapOperator) ModifyUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
func (im *ldapOperator) UpdateUser(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
err := im.ldapClient.Update(user)

View File

@@ -18,17 +18,309 @@
package kubeconfig
import (
"bytes"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
certutil "k8s.io/client-go/util/cert"
"k8s.io/klog"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/scheme"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/utils/pkiutil"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"time"
)
const (
inClusterCAFilePath = "/run/secrets/kubernetes.io/serviceaccount/ca.crt"
configMapPrefix = "kubeconfig-"
kubeconfigNameFormat = configMapPrefix + "%s"
defaultClusterName = "local"
defaultNamespace = "default"
kubeconfigFileName = "config"
configMapKind = "ConfigMap"
configMapAPIVersion = "v1"
)
type Interface interface {
GetKubeConfig(username string) (string, error)
CreateKubeConfig(user *iamv1alpha2.User) error
DelKubeConfig(username string) error
UpdateKubeconfig(username string, certificate []byte) error
}
type operator struct {
k8sclient kubernetes.Interface
config *rest.Config
masterURL string
}
func (o operator) GetKubeConfig(username string) (string, error) {
panic("implement me")
func NewOperator(k8sclient kubernetes.Interface, config *rest.Config, masterURL string) Interface {
return &operator{k8sclient: k8sclient, config: config, masterURL: masterURL}
}
func NewKubeconfigOperator() Interface {
return &operator{}
func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {
configName := fmt.Sprintf(kubeconfigNameFormat, user.Name)
_, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
if err == nil {
return nil
}
if !errors.IsNotFound(err) {
klog.Error(err)
return err
}
var ca []byte
if len(o.config.CAData) > 0 {
ca = o.config.CAData
} else {
ca, err = ioutil.ReadFile(inClusterCAFilePath)
if err != nil {
klog.Errorln(err)
return err
}
}
clientKey, err := o.createCSR(user.Name)
if err != nil {
klog.Errorln(err)
return err
}
currentContext := fmt.Sprintf("%s@%s", user.Name, defaultClusterName)
config := clientcmdapi.Config{
Kind: configMapKind,
APIVersion: configMapAPIVersion,
Preferences: clientcmdapi.Preferences{},
Clusters: map[string]*clientcmdapi.Cluster{defaultClusterName: {
Server: o.config.Host,
InsecureSkipTLSVerify: false,
CertificateAuthorityData: ca,
}},
AuthInfos: map[string]*clientcmdapi.AuthInfo{user.Name: {
ClientKeyData: clientKey,
}},
Contexts: map[string]*clientcmdapi.Context{currentContext: {
Cluster: defaultClusterName,
AuthInfo: user.Name,
Namespace: defaultNamespace,
}},
CurrentContext: currentContext,
}
kubeconfig, err := clientcmd.Write(config)
if err != nil {
klog.Error(err)
return err
}
cm := &corev1.ConfigMap{TypeMeta: metav1.TypeMeta{Kind: configMapKind, APIVersion: configMapAPIVersion},
ObjectMeta: metav1.ObjectMeta{Name: configName, Annotations: map[string]string{constants.UsernameAnnotationKey: user.Name}},
Data: map[string]string{kubeconfigFileName: string(kubeconfig)}}
err = controllerutil.SetControllerReference(user, cm, scheme.Scheme)
if err != nil {
klog.Errorln(err)
return err
}
_, err = o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(cm)
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func (o *operator) GetKubeConfig(username string) (string, error) {
configName := fmt.Sprintf(kubeconfigNameFormat, username)
configMap, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
if err != nil {
klog.Errorln(err)
return "", err
}
data := []byte(configMap.Data[kubeconfigFileName])
kubeconfig, err := clientcmd.Load(data)
if err != nil {
klog.Errorln(err)
return "", err
}
masterURL := o.masterURL
if cluster := kubeconfig.Clusters[defaultClusterName]; cluster != nil {
cluster.Server = masterURL
}
data, err = clientcmd.Write(*kubeconfig)
if err != nil {
klog.Errorln(err)
return "", err
}
return string(data), nil
}
func (o *operator) DelKubeConfig(username string) error {
configName := fmt.Sprintf(kubeconfigNameFormat, username)
deletePolicy := metav1.DeletePropagationBackground
err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(configName, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func (o *operator) createCSR(username string) ([]byte, error) {
csrConfig := &certutil.Config{
CommonName: username,
Organization: nil,
AltNames: certutil.AltNames{},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
}
x509csr, x509key, err := pkiutil.NewCSRAndKey(csrConfig)
if err != nil {
klog.Errorln(err)
return nil, err
}
var csrBuffer, keyBuffer bytes.Buffer
err = pem.Encode(&keyBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(x509key)})
if err != nil {
klog.Errorln(err)
return nil, err
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, x509csr, x509key)
if err != nil {
klog.Errorln(err)
return nil, err
}
err = pem.Encode(&csrBuffer, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes})
if err != nil {
klog.Errorln(err)
return nil, err
}
csr := csrBuffer.Bytes()
key := keyBuffer.Bytes()
csrName := fmt.Sprintf("%s-csr-%d", username, time.Now().Unix())
k8sCSR := &certificatesv1beta1.CertificateSigningRequest{
TypeMeta: metav1.TypeMeta{
Kind: "CertificateSigningRequest",
APIVersion: "certificates.k8s.io/v1beta1",
},
ObjectMeta: metav1.ObjectMeta{
Name: csrName,
Annotations: map[string]string{constants.UsernameAnnotationKey: username},
},
Spec: certificatesv1beta1.CertificateSigningRequestSpec{
Request: csr,
Usages: []certificatesv1beta1.KeyUsage{certificatesv1beta1.UsageServerAuth, certificatesv1beta1.UsageKeyEncipherment, certificatesv1beta1.UsageClientAuth, certificatesv1beta1.UsageDigitalSignature},
Username: username,
Groups: []string{user.AllAuthenticated},
},
}
// create csr
k8sCSR, err = o.k8sclient.CertificatesV1beta1().CertificateSigningRequests().Create(k8sCSR)
if err != nil {
klog.Errorln(err)
return nil, err
}
return key, nil
}
func (o *operator) UpdateKubeconfig(username string, certificate []byte) error {
configName := fmt.Sprintf(kubeconfigNameFormat, username)
configMap, err := o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{})
if err != nil {
klog.Errorln(err)
return err
}
configMap = appendCert(configMap, certificate)
_, err = o.k8sclient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Update(configMap)
if err != nil {
klog.Errorln(err)
return err
}
return nil
}
func appendCert(cm *corev1.ConfigMap, cert []byte) *corev1.ConfigMap {
data := []byte(cm.Data[kubeconfigFileName])
kubeconfig, err := clientcmd.Load(data)
// ignore if invalid format
if err != nil {
klog.Warning(err)
return cm
}
username := getControlledUsername(cm)
if kubeconfig.AuthInfos[username] != nil {
kubeconfig.AuthInfos[username].ClientCertificateData = cert
}
data, err = clientcmd.Write(*kubeconfig)
// ignore if invalid format
if err != nil {
klog.Warning(err)
return cm
}
cm.Data[kubeconfigFileName] = string(data)
return cm
}
func getControlledUsername(cm *corev1.ConfigMap) string {
for _, ownerReference := range cm.OwnerReferences {
if ownerReference.Kind == iamv1alpha2.ResourceKindUser {
return ownerReference.Name
}
}
return ""
}

View File

@@ -20,6 +20,7 @@ package kubectl
import (
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
@@ -32,19 +33,18 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/api/errors"
"kubesphere.io/kubesphere/pkg/constants"
)
const (
namespace = constants.KubeSphereControlNamespace
namespace = constants.KubeSphereControlNamespace
deployNameFormat = "kubectl-%s"
)
type Interface interface {
GetKubectlPod(username string) (models.PodInfo, error)
CreateKubectlDeploy(username string) error
DelKubectlDeploy(username string) error
DeleteKubectlDeploy(username string) error
}
type operator struct {
@@ -52,7 +52,7 @@ type operator struct {
informers informers.SharedInformerFactory
}
func NewKubectlOperator(k8sClient kubernetes.Interface, informers informers.SharedInformerFactory) Interface {
func NewOperator(k8sClient kubernetes.Interface, informers informers.SharedInformerFactory) Interface {
return &operator{k8sClient: k8sClient, informers: informers}
}
@@ -65,7 +65,7 @@ func init() {
}
func (o *operator) GetKubectlPod(username string) (models.PodInfo, error) {
deployName := fmt.Sprintf("kubectl-%s", username)
deployName := fmt.Sprintf(deployNameFormat, username)
deploy, err := o.informers.Apps().V1().Deployments().Lister().Deployments(namespace).Get(deployName)
if err != nil {
klog.Errorln(err)
@@ -113,12 +113,7 @@ func selectCorrectPod(namespace string, pods []*v1.Pod) (kubectlPod *v1.Pod, err
}
func (o *operator) CreateKubectlDeploy(username string) error {
k8sClient := o.k8sClient
deployName := fmt.Sprintf("kubectl-%s", username)
_, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{})
if err == nil {
return nil
}
deployName := fmt.Sprintf(deployNameFormat, username)
replica := int32(1)
selector := metav1.LabelSelector{MatchLabels: map[string]string{"username": username}}
@@ -147,29 +142,28 @@ func (o *operator) CreateKubectlDeploy(username string) error {
},
}
_, err = k8sClient.AppsV1().Deployments(namespace).Create(&deployment)
return err
}
func (o *operator) DelKubectlDeploy(username string) error {
k8sClient := o.k8sClient
deployName := fmt.Sprintf("kubectl-%s", username)
_, err := k8sClient.AppsV1().Deployments(namespace).Get(deployName, metav1.GetOptions{})
if errors.IsNotFound(err) {
return nil
}
_, err := o.k8sClient.AppsV1().Deployments(namespace).Create(&deployment)
if err != nil {
err := fmt.Errorf("delete username %s failed, reason:%v", username, err)
return err
}
deletePolicy := metav1.DeletePropagationBackground
err = k8sClient.AppsV1().Deployments(namespace).Delete(deployName, &metav1.DeleteOptions{PropagationPolicy: &deletePolicy})
if err != nil {
err := fmt.Errorf("delete username %s failed, reason:%v", username, err)
if errors.IsAlreadyExists(err) {
return nil
}
klog.Error(err)
return err
}
return nil
}
func (o *operator) DeleteKubectlDeploy(username string) error {
deployName := fmt.Sprintf(deployNameFormat, username)
err := o.k8sClient.AppsV1().Deployments(namespace).Delete(deployName, metav1.NewDeleteOptions(0))
if err != nil {
if errors.IsNotFound(err) {
return nil
}
klog.Error(err)
return err
}

View File

@@ -73,7 +73,7 @@ type Interface interface {
func ObjectMetaExactlyMath(key, value string, item metav1.ObjectMeta) bool {
switch key {
case Name:
names := strings.Split(value, "|")
names := strings.Split(value, ",")
if !sliceutil.HasString(names, item.Name) {
return false
}

View File

@@ -40,13 +40,13 @@ func (d *applicationsGetter) Get(namespace, name string) (runtime.Object, error)
}
func (d *applicationsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
all, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(query.Selector())
applications, err := d.informer.App().V1beta1().Applications().Lister().Applications(namespace).List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, app := range all {
for _, app := range applications {
result = append(result, app)
}

View File

@@ -20,7 +20,11 @@ func New(informers externalversions.SharedInformerFactory) v1alpha3.Interface {
}
func (c clustersGetter) Get(_, name string) (runtime.Object, error) {
return c.informers.Cluster().V1alpha1().Clusters().Lister().Get(name)
cluster, err := c.informers.Cluster().V1alpha1().Clusters().Lister().Get(name)
if err != nil {
return nil, err
}
return c.transform(cluster), nil
}
func (c clustersGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
@@ -30,11 +34,18 @@ func (c clustersGetter) List(_ string, query *query.Query) (*api.ListResult, err
}
var result []runtime.Object
for _, deploy := range clusters {
result = append(result, deploy)
for _, cluster := range clusters {
result = append(result, cluster)
}
return v1alpha3.DefaultList(result, query, c.compare, c.filter), nil
return v1alpha3.DefaultList(result, query, c.compare, c.filter, c.transform), nil
}
func (c clustersGetter) transform(obj runtime.Object) runtime.Object {
in := obj.(*clusterv1alpha1.Cluster)
out := in.DeepCopy()
out.Spec.Connection.KubeConfig = nil
return out
}
func (c clustersGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {

View File

@@ -18,57 +18,70 @@
package clusterrole
import (
"encoding/json"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type rolesGetter struct {
type clusterrolesGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &rolesGetter{sharedInformers: sharedInformers}
return &clusterrolesGetter{sharedInformers: sharedInformers}
}
func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) {
func (d *clusterrolesGetter) Get(namespace, name string) (runtime.Object, error) {
return d.sharedInformers.Rbac().V1().ClusterRoles().Lister().Get(name)
}
func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
func (d *clusterrolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
var roles []*rbacv1.ClusterRole
var err error
if aggregateTo := query.Filters[iamv1alpha2.AggregateTo]; aggregateTo != "" {
roles, err = d.fetchAggregationRoles(string(aggregateTo))
delete(query.Filters, iamv1alpha2.AggregateTo)
} else {
roles, err = d.sharedInformers.Rbac().V1().ClusterRoles().Lister().List(query.Selector())
}
all, err := d.sharedInformers.Rbac().V1().ClusterRoles().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, clusterrole := range roles {
result = append(result, clusterrole)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *rolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
func (d *clusterrolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRole, ok := left.(*rbacv1.ClusterRole)
leftClusterRole, ok := left.(*rbacv1.ClusterRole)
if !ok {
return false
}
rightRole, ok := right.(*rbacv1.ClusterRole)
rightClusterRole, ok := right.(*rbacv1.ClusterRole)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field)
return v1alpha3.DefaultObjectMetaCompare(leftClusterRole.ObjectMeta, rightClusterRole.ObjectMeta, field)
}
func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool {
func (d *clusterrolesGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*rbacv1.ClusterRole)
if !ok {
@@ -77,3 +90,38 @@ func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool {
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}
func (d *clusterrolesGetter) fetchAggregationRoles(name string) ([]*rbacv1.ClusterRole, error) {
roles := make([]*rbacv1.ClusterRole, 0)
obj, err := d.Get("", name)
if err != nil {
if errors.IsNotFound(err) {
return roles, nil
}
return nil, err
}
if annotation := obj.(*rbacv1.ClusterRole).Annotations[iamv1alpha2.AggregationRolesAnnotation]; annotation != "" {
var roleNames []string
if err = json.Unmarshal([]byte(annotation), &roleNames); err == nil {
for _, roleName := range roleNames {
role, err := d.Get("", roleName)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("invalid aggregation role found: %s, %s", name, roleName)
continue
}
return nil, err
}
roles = append(roles, role.(*rbacv1.ClusterRole))
}
}
}
return roles, nil
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2019 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 clusterrolebinding
import (
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type clusterrolebindingsGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &clusterrolebindingsGetter{sharedInformers: sharedInformers}
}
func (d *clusterrolebindingsGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Rbac().V1().ClusterRoleBindings().Lister().Get(name)
}
func (d *clusterrolebindingsGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
roleBindings, err := d.sharedInformers.Rbac().V1().ClusterRoleBindings().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, roleBinding := range roleBindings {
result = append(result, roleBinding)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *clusterrolebindingsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRoleBinding, ok := left.(*rbacv1.ClusterRoleBinding)
if !ok {
return false
}
rightRoleBinding, ok := right.(*rbacv1.ClusterRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRoleBinding.ObjectMeta, rightRoleBinding.ObjectMeta, field)
}
func (d *clusterrolebindingsGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*rbacv1.ClusterRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}

View File

@@ -0,0 +1,95 @@
package clusterrolebinding
import (
"github.com/google/go-cmp/cmp"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListRoles(t *testing.T) {
tests := []struct {
description string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")},
},
&api.ListResult{
Items: []interface{}{
foo2,
},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := getter.List("", test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}
var (
foo1 = &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
Namespace: "bar",
},
}
foo2 = &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
Namespace: "bar",
},
}
bar1 = &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar1",
Namespace: "bar",
},
}
clusterRoleBindings = []interface{}{foo1, foo2, bar1}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, clusterRoleBinding := range clusterRoleBindings {
informer.Rbac().V1().ClusterRoleBindings().Informer().GetIndexer().Add(clusterRoleBinding)
}
return New(informer)
}

View File

@@ -39,14 +39,14 @@ func (d *configmapsGetter) Get(namespace, name string) (runtime.Object, error) {
}
func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
all, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(query.Selector())
configmaps, err := d.informer.Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, app := range all {
result = append(result, app)
for _, configmap := range configmaps {
result = append(result, configmap)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
@@ -54,17 +54,17 @@ func (d *configmapsGetter) List(namespace string, query *query.Query) (*api.List
func (d *configmapsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftConfigMap, ok := left.(*corev1.ConfigMap)
leftCM, ok := left.(*corev1.ConfigMap)
if !ok {
return false
}
rightConfigMap, ok := right.(*corev1.ConfigMap)
rightCM, ok := right.(*corev1.ConfigMap)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftConfigMap.ObjectMeta, rightConfigMap.ObjectMeta, field)
return v1alpha3.DefaultObjectMetaCompare(leftCM.ObjectMeta, rightCM.ObjectMeta, field)
}
func (d *configmapsGetter) filter(object runtime.Object, filter query.Filter) bool {

View File

@@ -30,8 +30,8 @@ func (c crdGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
}
var result []runtime.Object
for _, deploy := range crds {
result = append(result, deploy)
for _, crd := range crds {
result = append(result, crd)
}
return v1alpha3.DefaultList(result, query, c.compare, c.filter), nil

View File

@@ -49,14 +49,14 @@ func (d *deploymentsGetter) Get(namespace, name string) (runtime.Object, error)
func (d *deploymentsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
// first retrieves all deployments within given namespace
all, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(query.Selector())
deployments, err := d.sharedInformers.Apps().V1().Deployments().Lister().Deployments(namespace).List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, deployment := range deployments {
result = append(result, deployment)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil

View File

@@ -0,0 +1,57 @@
package devops
import (
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
"kubesphere.io/kubesphere/pkg/apiserver/query"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type devopsGetter struct {
informers ksinformers.SharedInformerFactory
}
func New(ksinformer ksinformers.SharedInformerFactory) v1alpha3.Interface {
return &devopsGetter{informers: ksinformer}
}
func (n devopsGetter) Get(_, name string) (runtime.Object, error) {
return n.informers.Devops().V1alpha3().DevOpsProjects().Lister().Get(name)
}
func (n devopsGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
projects, err := n.informers.Devops().V1alpha3().DevOpsProjects().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, project := range projects {
result = append(result, project)
}
return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil
}
func (n devopsGetter) filter(item runtime.Object, filter query.Filter) bool {
devOpsProject, ok := item.(*devopsv1alpha3.DevOpsProject)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(devOpsProject.ObjectMeta, filter)
}
func (n devopsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftProject, ok := left.(*devopsv1alpha3.DevOpsProject)
if !ok {
return false
}
rightProject, ok := right.(*devopsv1alpha3.DevOpsProject)
if !ok {
return true
}
return v1alpha3.DefaultObjectMetaCompare(leftProject.ObjectMeta, rightProject.ObjectMeta, field)
}

View File

@@ -0,0 +1 @@
package devops

View File

@@ -18,7 +18,10 @@
package globalrole
import (
"encoding/json"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
@@ -40,14 +43,23 @@ func (d *globalrolesGetter) Get(_, name string) (runtime.Object, error) {
func (d *globalrolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
all, err := d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().List(query.Selector())
var roles []*iamv1alpha2.GlobalRole
var err error
if aggregateTo := query.Filters[iamv1alpha2.AggregateTo]; aggregateTo != "" {
roles, err = d.fetchAggregationRoles(string(aggregateTo))
delete(query.Filters, iamv1alpha2.AggregateTo)
} else {
roles, err = d.sharedInformers.Iam().V1alpha2().GlobalRoles().Lister().List(query.Selector())
}
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, role := range roles {
result = append(result, role)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
@@ -77,3 +89,38 @@ func (d *globalrolesGetter) filter(object runtime.Object, filter query.Filter) b
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}
func (d *globalrolesGetter) fetchAggregationRoles(name string) ([]*iamv1alpha2.GlobalRole, error) {
roles := make([]*iamv1alpha2.GlobalRole, 0)
obj, err := d.Get("", name)
if err != nil {
if errors.IsNotFound(err) {
return roles, nil
}
return nil, err
}
if annotation := obj.(*iamv1alpha2.GlobalRole).Annotations[iamv1alpha2.AggregationRolesAnnotation]; annotation != "" {
var roleNames []string
if err = json.Unmarshal([]byte(annotation), &roleNames); err == nil {
for _, roleName := range roleNames {
role, err := d.Get("", roleName)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("invalid aggregation role found: %s, %s", name, roleName)
continue
}
return nil, err
}
roles = append(roles, role.(*iamv1alpha2.GlobalRole))
}
}
}
return roles, nil
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2019 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 globalrolebinding
import (
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type globalrolebindingsGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &globalrolebindingsGetter{sharedInformers: sharedInformers}
}
func (d *globalrolebindingsGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Iam().V1alpha2().GlobalRoleBindings().Lister().Get(name)
}
func (d *globalrolebindingsGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
globalRoleBindings, err := d.sharedInformers.Iam().V1alpha2().GlobalRoleBindings().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, globalRoleBinding := range globalRoleBindings {
result = append(result, globalRoleBinding)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *globalrolebindingsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRoleBinding, ok := left.(*iamv1alpha2.GlobalRoleBinding)
if !ok {
return false
}
rightRoleBinding, ok := right.(*iamv1alpha2.GlobalRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRoleBinding.ObjectMeta, rightRoleBinding.ObjectMeta, field)
}
func (d *globalrolebindingsGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*iamv1alpha2.GlobalRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}

View File

@@ -0,0 +1,95 @@
package globalrolebinding
import (
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListRoles(t *testing.T) {
tests := []struct {
description string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")},
},
&api.ListResult{
Items: []interface{}{
foo2,
},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := getter.List("", test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}
var (
foo1 = &iamv1alpha2.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
Namespace: "bar",
},
}
foo2 = &iamv1alpha2.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
Namespace: "bar",
},
}
bar1 = &iamv1alpha2.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar1",
Namespace: "bar",
},
}
globalRoleBindings = []interface{}{foo1, foo2, bar1}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, globalRoleBinding := range globalRoleBindings {
informer.Iam().V1alpha2().GlobalRoleBindings().Informer().GetIndexer().Add(globalRoleBinding)
}
return New(informer)
}

View File

@@ -5,6 +5,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/constants"
"sort"
"strings"
)
@@ -22,7 +23,9 @@ type CompareFunc func(runtime.Object, runtime.Object, query.Field) bool
type FilterFunc func(runtime.Object, query.Filter) bool
func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFunc, filterFunc FilterFunc) *api.ListResult {
type TransformFunc func(runtime.Object) runtime.Object
func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFunc, filterFunc FilterFunc, transformFuncs ...TransformFunc) *api.ListResult {
// selected matched ones
var filtered []runtime.Object
for _, object := range objects {
@@ -35,6 +38,9 @@ func DefaultList(objects []runtime.Object, q *query.Query, compareFunc CompareFu
}
if selected {
for _, transform := range transformFuncs {
object = transform(object)
}
filtered = append(filtered, object)
}
}
@@ -84,6 +90,13 @@ func DefaultObjectMetaCompare(left, right metav1.ObjectMeta, sortBy query.Field)
// Default metadata filter
func DefaultObjectMetaFilter(item metav1.ObjectMeta, filter query.Filter) bool {
switch filter.Field {
case query.FieldNames:
for _, name := range strings.Split(string(filter.Value), ",") {
if item.Name == name || item.Annotations[constants.DisplayNameAnnotationKey] == name {
return true
}
}
return false
// /namespaces?page=1&limit=10&name=default
case query.FieldName:
return strings.Contains(item.Name, string(filter.Value))

View File

@@ -10,19 +10,19 @@ import (
"strings"
)
type namespaceGetter struct {
type namespacesGetter struct {
informers informers.SharedInformerFactory
}
func New(informers informers.SharedInformerFactory) v1alpha3.Interface {
return &namespaceGetter{informers: informers}
return &namespacesGetter{informers: informers}
}
func (n namespaceGetter) Get(_, name string) (runtime.Object, error) {
func (n namespacesGetter) Get(_, name string) (runtime.Object, error) {
return n.informers.Core().V1().Namespaces().Lister().Get(name)
}
func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
func (n namespacesGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
ns, err := n.informers.Core().V1().Namespaces().Lister().List(query.Selector())
if err != nil {
return nil, err
@@ -36,7 +36,7 @@ func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, er
return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil
}
func (n namespaceGetter) filter(item runtime.Object, filter query.Filter) bool {
func (n namespacesGetter) filter(item runtime.Object, filter query.Filter) bool {
namespace, ok := item.(*v1.Namespace)
if !ok {
return false
@@ -49,7 +49,7 @@ func (n namespaceGetter) filter(item runtime.Object, filter query.Filter) bool {
}
}
func (n namespaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
func (n namespacesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftNs, ok := left.(*v1.Namespace)
if !ok {
return false

View File

@@ -47,14 +47,14 @@ func (p *podsGetter) Get(namespace, name string) (runtime.Object, error) {
func (p *podsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
all, err := p.informer.Core().V1().Pods().Lister().Pods(namespace).List(query.Selector())
pods, err := p.informer.Core().V1().Pods().Lister().Pods(namespace).List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, app := range all {
result = append(result, app)
for _, pod := range pods {
result = append(result, pod)
}
return v1alpha3.DefaultList(result, query, p.compare, p.filter), nil

View File

@@ -25,28 +25,36 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha3 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha3"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/application"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/cluster"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrole"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/clusterrolebinding"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/configmap"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/customresourcedefinition"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/deployment"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/devops"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrole"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/globalrolebinding"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/namespace"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/networkpolicy"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/node"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/persistentvolumeclaim"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/pod"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/role"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/rolebinding"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/user"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/volumesnapshot"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspace"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacerole"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacerolebinding"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/workspacetemplate"
)
var ErrResourceNotSupported = errors.New("resource is not supported")
@@ -65,12 +73,18 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes"}] = node.New(factory.KubernetesSharedInformerFactory())
getters[schema.GroupVersionResource{Group: "app.k8s.io", Version: "v1beta1", Resource: "applications"}] = application.New(factory.ApplicationSharedInformerFactory())
getters[schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1", Resource: "networkpolicies"}] = networkpolicy.New(factory.KubernetesSharedInformerFactory())
getters[devopsv1alpha3.SchemeGroupVersion.WithResource(devopsv1alpha3.ResourcePluralDevOpsProject)] = devops.New(factory.KubeSphereSharedInformerFactory())
getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha1.ResourcePluralWorkspace)] = workspace.New(factory.KubeSphereSharedInformerFactory())
getters[tenantv1alpha1.SchemeGroupVersion.WithResource(tenantv1alpha2.ResourcePluralWorkspaceTemplate)] = workspacetemplate.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRole)] = globalrole.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRole)] = workspacerole.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralUser)] = user.New(factory.KubeSphereSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource("roles")] = role.New(factory.KubernetesSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource("clusterroles")] = clusterrole.New(factory.KubernetesSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralUser)] = user.New(factory.KubeSphereSharedInformerFactory(), factory.KubernetesSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralGlobalRoleBinding)] = globalrolebinding.New(factory.KubeSphereSharedInformerFactory())
getters[iamv1alpha2.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralWorkspaceRoleBinding)] = workspacerolebinding.New(factory.KubeSphereSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralRole)] = role.New(factory.KubernetesSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralClusterRole)] = clusterrole.New(factory.KubernetesSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralRoleBinding)] = rolebinding.New(factory.KubernetesSharedInformerFactory())
getters[rbacv1.SchemeGroupVersion.WithResource(iamv1alpha2.ResourcesPluralClusterRoleBinding)] = clusterrolebinding.New(factory.KubernetesSharedInformerFactory())
getters[schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumeclaims"}] = persistentvolumeclaim.New(factory.KubernetesSharedInformerFactory(), factory.SnapshotSharedInformerFactory())
getters[snapshotv1beta1.SchemeGroupVersion.WithResource("volumesnapshots")] = volumesnapshot.New(factory.SnapshotSharedInformerFactory())
getters[schema.GroupVersionResource{Group: "cluster.kubesphere.io", Version: "v1alpha1", Resource: "clusters"}] = cluster.New(factory.KubeSphereSharedInformerFactory())

View File

@@ -18,10 +18,14 @@
package role
import (
"encoding/json"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
@@ -39,15 +43,24 @@ func (d *rolesGetter) Get(namespace, name string) (runtime.Object, error) {
}
func (d *rolesGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
all, err := d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).List(query.Selector())
var roles []*rbacv1.Role
var err error
if aggregateTo := query.Filters[iamv1alpha2.AggregateTo]; aggregateTo != "" {
roles, err = d.fetchAggregationRoles(namespace, string(aggregateTo))
delete(query.Filters, iamv1alpha2.AggregateTo)
} else {
roles, err = d.sharedInformers.Rbac().V1().Roles().Lister().Roles(namespace).List(query.Selector())
}
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, role := range roles {
result = append(result, role)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
@@ -77,3 +90,38 @@ func (d *rolesGetter) filter(object runtime.Object, filter query.Filter) bool {
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}
func (d *rolesGetter) fetchAggregationRoles(namespace, name string) ([]*rbacv1.Role, error) {
roles := make([]*rbacv1.Role, 0)
obj, err := d.Get(namespace, name)
if err != nil {
if errors.IsNotFound(err) {
return roles, nil
}
return nil, err
}
if annotation := obj.(*rbacv1.Role).Annotations[iamv1alpha2.AggregationRolesAnnotation]; annotation != "" {
var roleNames []string
if err = json.Unmarshal([]byte(annotation), &roleNames); err == nil {
for _, roleName := range roleNames {
role, err := d.Get("", roleName)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("invalid aggregation role found: %s, %s", name, roleName)
continue
}
return nil, err
}
roles = append(roles, role.(*rbacv1.Role))
}
}
}
return roles, nil
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2019 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 rolebinding
import (
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type rolebindingsGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &rolebindingsGetter{sharedInformers: sharedInformers}
}
func (d *rolebindingsGetter) Get(namespace, name string) (runtime.Object, error) {
return d.sharedInformers.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).Get(name)
}
func (d *rolebindingsGetter) List(namespace string, query *query.Query) (*api.ListResult, error) {
roleBindings, err := d.sharedInformers.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, roleBinding := range roleBindings {
result = append(result, roleBinding)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *rolebindingsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRoleBinding, ok := left.(*rbacv1.RoleBinding)
if !ok {
return false
}
rightRoleBinding, ok := right.(*rbacv1.RoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRoleBinding.ObjectMeta, rightRoleBinding.ObjectMeta, field)
}
func (d *rolebindingsGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*rbacv1.RoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}

View File

@@ -0,0 +1,97 @@
package rolebinding
import (
"github.com/google/go-cmp/cmp"
rbacv1 "k8s.io/api/rbac/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListRoles(t *testing.T) {
tests := []struct {
description string
namespace string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
"bar",
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")},
},
&api.ListResult{
Items: []interface{}{
foo2,
},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := getter.List(test.namespace, test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}
var (
foo1 = &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
Namespace: "bar",
},
}
foo2 = &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
Namespace: "bar",
},
}
bar1 = &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar1",
Namespace: "bar",
},
}
roleBindings = []interface{}{foo1, foo2, bar1}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, roleBinding := range roleBindings {
informer.Rbac().V1().RoleBindings().Informer().GetIndexer().Add(roleBinding)
}
return New(informer)
}

View File

@@ -18,36 +18,66 @@
package user
import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
k8sinformers "k8s.io/client-go/informers"
"k8s.io/klog"
"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"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type usersGetter struct {
sharedInformers informers.SharedInformerFactory
ksInformer ksinformers.SharedInformerFactory
k8sInformer k8sinformers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &usersGetter{sharedInformers: sharedInformers}
func New(ksinformer ksinformers.SharedInformerFactory, k8sinformer k8sinformers.SharedInformerFactory) v1alpha3.Interface {
return &usersGetter{ksInformer: ksinformer, k8sInformer: k8sinformer}
}
func (d *usersGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Iam().V1alpha2().Users().Lister().Get(name)
return d.ksInformer.Iam().V1alpha2().Users().Lister().Get(name)
}
func (d *usersGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
all, err := d.sharedInformers.Iam().V1alpha2().Users().Lister().List(query.Selector())
var users []*iamv1alpha2.User
var err error
if namespace := query.Filters[iamv1alpha2.ScopeNamespace]; namespace != "" {
role := query.Filters[iamv1alpha2.ResourcesSingularRole]
users, err = d.listAllUsersInNamespace(string(namespace), string(role))
delete(query.Filters, iamv1alpha2.ScopeNamespace)
delete(query.Filters, iamv1alpha2.ResourcesSingularRole)
} else if workspace := query.Filters[iamv1alpha2.ScopeWorkspace]; workspace != "" {
workspaceRole := query.Filters[iamv1alpha2.ResourcesSingularWorkspaceRole]
users, err = d.listAllUsersInWorkspace(string(workspace), string(workspaceRole))
delete(query.Filters, iamv1alpha2.ScopeWorkspace)
delete(query.Filters, iamv1alpha2.ResourcesSingularWorkspaceRole)
} else if cluster := query.Filters[iamv1alpha2.ScopeCluster]; cluster == iamv1alpha2.LocalCluster {
clusterRole := query.Filters[iamv1alpha2.ResourcesSingularClusterRole]
users, err = d.listAllUsersInCluster(string(clusterRole))
delete(query.Filters, iamv1alpha2.ScopeCluster)
delete(query.Filters, iamv1alpha2.ResourcesSingularClusterRole)
} else if globalRole := query.Filters[iamv1alpha2.ResourcesSingularGlobalRole]; globalRole != "" {
users, err = d.listAllUsersByGlobalRole(string(globalRole))
delete(query.Filters, iamv1alpha2.ResourcesSingularGlobalRole)
} else {
users, err = d.ksInformer.Iam().V1alpha2().Users().Lister().List(query.Selector())
}
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, user := range users {
result = append(result, user)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
@@ -55,25 +85,231 @@ func (d *usersGetter) List(_ string, query *query.Query) (*api.ListResult, error
func (d *usersGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRole, ok := left.(*iamv1alpha2.User)
leftUser, ok := left.(*iamv1alpha2.User)
if !ok {
return false
}
rightRole, ok := right.(*iamv1alpha2.User)
rightUser, ok := right.(*iamv1alpha2.User)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field)
return v1alpha3.DefaultObjectMetaCompare(leftUser.ObjectMeta, rightUser.ObjectMeta, field)
}
func (d *usersGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*iamv1alpha2.User)
user, ok := object.(*iamv1alpha2.User)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
switch filter.Field {
case iamv1alpha2.FieldEmail:
return user.Spec.Email == string(filter.Value)
default:
return v1alpha3.DefaultObjectMetaFilter(user.ObjectMeta, filter)
}
}
func (d *usersGetter) listAllUsersInWorkspace(workspace, role string) ([]*iamv1alpha2.User, error) {
var users []*iamv1alpha2.User
var err error
workspaceRoleBindings, err := d.ksInformer.Iam().V1alpha2().
WorkspaceRoleBindings().Lister().List(labels.SelectorFromValidatedSet(labels.Set{tenantv1alpha1.WorkspaceLabel: workspace}))
if err != nil {
klog.Error(err)
return nil, err
}
for _, roleBinding := range workspaceRoleBindings {
if role != "" && roleBinding.RoleRef.Name != role {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == iamv1alpha2.ResourceKindUser {
if contains(users, subject.Name) {
klog.Warningf("conflict role binding found: %s, username:%s", roleBinding.ObjectMeta.String(), subject.Name)
continue
}
obj, err := d.Get("", subject.Name)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("orphan subject: %s", subject.String())
continue
}
klog.Error(err)
return nil, err
}
user := obj.(*iamv1alpha2.User)
user = user.DeepCopy()
if user.Annotations == nil {
user.Annotations = make(map[string]string, 0)
}
user.Annotations[iamv1alpha2.WorkspaceRoleAnnotation] = roleBinding.RoleRef.Name
users = append(users, user)
}
}
}
return users, nil
}
func (d *usersGetter) listAllUsersInNamespace(namespace, role string) ([]*iamv1alpha2.User, error) {
var users []*iamv1alpha2.User
var err error
roleBindings, err := d.k8sInformer.Rbac().V1().
RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything())
if err != nil {
klog.Error(err)
return nil, err
}
for _, roleBinding := range roleBindings {
if role != "" && roleBinding.RoleRef.Name != role {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == iamv1alpha2.ResourceKindUser {
if contains(users, subject.Name) {
klog.Warningf("conflict role binding found: %s, username:%s", roleBinding.ObjectMeta.String(), subject.Name)
continue
}
obj, err := d.Get("", subject.Name)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("orphan subject: %s", subject.String())
continue
}
klog.Error(err)
return nil, err
}
user := obj.(*iamv1alpha2.User)
user = user.DeepCopy()
if user.Annotations == nil {
user.Annotations = make(map[string]string, 0)
}
user.Annotations[iamv1alpha2.RoleAnnotation] = roleBinding.RoleRef.Name
users = append(users, user)
}
}
}
return users, nil
}
func (d *usersGetter) listAllUsersByGlobalRole(globalRole string) ([]*iamv1alpha2.User, error) {
var users []*iamv1alpha2.User
var err error
globalRoleBindings, err := d.ksInformer.Iam().V1alpha2().
GlobalRoleBindings().Lister().List(labels.Everything())
if err != nil {
klog.Error(err)
return nil, err
}
for _, roleBinding := range globalRoleBindings {
if roleBinding.RoleRef.Name != globalRole {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == iamv1alpha2.ResourceKindUser {
if contains(users, subject.Name) {
klog.Warningf("conflict role binding found: %s, username:%s", roleBinding.ObjectMeta.String(), subject.Name)
continue
}
obj, err := d.Get("", subject.Name)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("orphan subject: %s", subject.String())
continue
}
klog.Error(err)
return nil, err
}
user := obj.(*iamv1alpha2.User)
user = user.DeepCopy()
if user.Annotations == nil {
user.Annotations = make(map[string]string, 0)
}
user.Annotations[iamv1alpha2.GlobalRoleAnnotation] = roleBinding.RoleRef.Name
users = append(users, user)
}
}
}
return users, nil
}
func (d *usersGetter) listAllUsersInCluster(clusterRole string) ([]*iamv1alpha2.User, error) {
var users []*iamv1alpha2.User
var err error
roleBindings, err := d.k8sInformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
if err != nil {
klog.Error(err)
return nil, err
}
for _, roleBinding := range roleBindings {
if clusterRole != "" && roleBinding.RoleRef.Name != clusterRole {
continue
}
for _, subject := range roleBinding.Subjects {
if subject.Kind == iamv1alpha2.ResourceKindUser {
if contains(users, subject.Name) {
klog.Warningf("conflict role binding found: %s, username:%s", roleBinding.ObjectMeta.String(), subject.Name)
continue
}
obj, err := d.Get("", subject.Name)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("orphan subject: %s", subject.String())
continue
}
klog.Error(err)
return nil, err
}
user := obj.(*iamv1alpha2.User)
user = user.DeepCopy()
if user.Annotations == nil {
user.Annotations = make(map[string]string, 0)
}
user.Annotations[iamv1alpha2.ClusterRoleAnnotation] = roleBinding.RoleRef.Name
users = append(users, user)
}
}
}
return users, nil
}
func contains(users []*iamv1alpha2.User, username string) bool {
for _, user := range users {
if user.Name == username {
return true
}
}
return false
}

View File

@@ -90,5 +90,5 @@ func prepare() v1alpha3.Interface {
for _, user := range users {
informer.Iam().V1alpha2().Users().Informer().GetIndexer().Add(user)
}
return New(informer)
return New(informer, nil)
}

View File

@@ -40,14 +40,14 @@ func (d *workspaceGetter) Get(_, name string) (runtime.Object, error) {
func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
all, err := d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().List(query.Selector())
workspaces, err := d.sharedInformers.Tenant().V1alpha1().Workspaces().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, workspace := range workspaces {
result = append(result, workspace)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
@@ -55,17 +55,17 @@ func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, e
func (d *workspaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRole, ok := left.(*tenantv1alpha1.Workspace)
leftWorkspace, ok := left.(*tenantv1alpha1.Workspace)
if !ok {
return false
}
rightRole, ok := right.(*tenantv1alpha1.Workspace)
rightWorkspace, ok := right.(*tenantv1alpha1.Workspace)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRole.ObjectMeta, rightRole.ObjectMeta, field)
return v1alpha3.DefaultObjectMetaCompare(leftWorkspace.ObjectMeta, rightWorkspace.ObjectMeta, field)
}
func (d *workspaceGetter) filter(object runtime.Object, filter query.Filter) bool {

View File

@@ -18,9 +18,13 @@
package workspacerole
import (
"encoding/json"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog"
"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"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
@@ -38,19 +42,28 @@ func (d *workspacerolesGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().Get(name)
}
func (d *workspacerolesGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
func (d *workspacerolesGetter) List(_ string, queryParam *query.Query) (*api.ListResult, error) {
var roles []*iamv1alpha2.WorkspaceRole
var err error
if aggregateTo := queryParam.Filters[iamv1alpha2.AggregateTo]; aggregateTo != "" {
roles, err = d.fetchAggregationRoles(string(aggregateTo))
delete(queryParam.Filters, iamv1alpha2.AggregateTo)
} else {
roles, err = d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().List(queryParam.Selector())
}
all, err := d.sharedInformers.Iam().V1alpha2().WorkspaceRoles().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, deploy := range all {
result = append(result, deploy)
for _, role := range roles {
result = append(result, role)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
return v1alpha3.DefaultList(result, queryParam, d.compare, d.filter), nil
}
func (d *workspacerolesGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
@@ -75,5 +88,46 @@ func (d *workspacerolesGetter) filter(object runtime.Object, filter query.Filter
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
switch filter.Field {
case iamv1alpha2.ScopeWorkspace:
return role.Labels[tenantv1alpha1.WorkspaceLabel] == string(filter.Value)
default:
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}
}
func (d *workspacerolesGetter) fetchAggregationRoles(name string) ([]*iamv1alpha2.WorkspaceRole, error) {
roles := make([]*iamv1alpha2.WorkspaceRole, 0)
obj, err := d.Get("", name)
if err != nil {
if errors.IsNotFound(err) {
return roles, nil
}
return nil, err
}
if annotation := obj.(*iamv1alpha2.WorkspaceRole).Annotations[iamv1alpha2.AggregationRolesAnnotation]; annotation != "" {
var roleNames []string
if err = json.Unmarshal([]byte(annotation), &roleNames); err == nil {
for _, roleName := range roleNames {
role, err := d.Get("", roleName)
if err != nil {
if errors.IsNotFound(err) {
klog.Warningf("invalid aggregation role found: %s, %s", name, roleName)
continue
}
return nil, err
}
roles = append(roles, role.(*iamv1alpha2.WorkspaceRole))
}
}
}
return roles, nil
}

View File

@@ -0,0 +1,80 @@
/*
Copyright 2019 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 workspacerolebinding
import (
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type workspacerolebindingsGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &workspacerolebindingsGetter{sharedInformers: sharedInformers}
}
func (d *workspacerolebindingsGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Iam().V1alpha2().GlobalRoleBindings().Lister().Get(name)
}
func (d *workspacerolebindingsGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
globalRoleBindings, err := d.sharedInformers.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, globalRoleBinding := range globalRoleBindings {
result = append(result, globalRoleBinding)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *workspacerolebindingsGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftRoleBinding, ok := left.(*iamv1alpha2.WorkspaceRoleBinding)
if !ok {
return false
}
rightRoleBinding, ok := right.(*iamv1alpha2.WorkspaceRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftRoleBinding.ObjectMeta, rightRoleBinding.ObjectMeta, field)
}
func (d *workspacerolebindingsGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*iamv1alpha2.WorkspaceRoleBinding)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}

View File

@@ -0,0 +1,95 @@
package workspacerolebinding
import (
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListRoles(t *testing.T) {
tests := []struct {
description string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")},
},
&api.ListResult{
Items: []interface{}{
foo2,
},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := getter.List("", test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}
var (
foo1 = &iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
Namespace: "bar",
},
}
foo2 = &iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
Namespace: "bar",
},
}
bar1 = &iamv1alpha2.WorkspaceRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar1",
Namespace: "bar",
},
}
workspaceRoleBindings = []interface{}{foo1, foo2, bar1}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, workspaceRoleBinding := range workspaceRoleBindings {
informer.Iam().V1alpha2().WorkspaceRoleBindings().Informer().GetIndexer().Add(workspaceRoleBinding)
}
return New(informer)
}

View File

@@ -0,0 +1,79 @@
/*
Copyright 2019 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 workspacetemplate
import (
"k8s.io/apimachinery/pkg/runtime"
"kubesphere.io/kubesphere/pkg/api"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
)
type workspaceGetter struct {
sharedInformers informers.SharedInformerFactory
}
func New(sharedInformers informers.SharedInformerFactory) v1alpha3.Interface {
return &workspaceGetter{sharedInformers: sharedInformers}
}
func (d *workspaceGetter) Get(_, name string) (runtime.Object, error) {
return d.sharedInformers.Tenant().V1alpha2().WorkspaceTemplates().Lister().Get(name)
}
func (d *workspaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
workspaces, err := d.sharedInformers.Tenant().V1alpha2().WorkspaceTemplates().Lister().List(query.Selector())
if err != nil {
return nil, err
}
var result []runtime.Object
for _, workspace := range workspaces {
result = append(result, workspace)
}
return v1alpha3.DefaultList(result, query, d.compare, d.filter), nil
}
func (d *workspaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftWorkspace, ok := left.(*tenantv1alpha2.WorkspaceTemplate)
if !ok {
return false
}
rightWorkspace, ok := right.(*tenantv1alpha2.WorkspaceTemplate)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaCompare(leftWorkspace.ObjectMeta, rightWorkspace.ObjectMeta, field)
}
func (d *workspaceGetter) filter(object runtime.Object, filter query.Filter) bool {
role, ok := object.(*tenantv1alpha2.WorkspaceTemplate)
if !ok {
return false
}
return v1alpha3.DefaultObjectMetaFilter(role.ObjectMeta, filter)
}

View File

@@ -0,0 +1,94 @@
package workspacetemplate
import (
"github.com/google/go-cmp/cmp"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"kubesphere.io/kubesphere/pkg/api"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"testing"
)
func TestListWorkspaces(t *testing.T) {
tests := []struct {
description string
namespace string
query *query.Query
expected *api.ListResult
expectedErr error
}{
{
"test name filter",
"bar",
&query.Query{
Pagination: &query.Pagination{
Limit: 1,
Offset: 0,
},
SortBy: query.FieldName,
Ascending: false,
Filters: map[query.Field]query.Value{query.FieldName: query.Value("foo2")},
},
&api.ListResult{
Items: []interface{}{
foo2,
},
TotalItems: 1,
},
nil,
},
}
getter := prepare()
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
got, err := getter.List(test.namespace, test.query)
if test.expectedErr != nil && err != test.expectedErr {
t.Errorf("expected error, got nothing")
} else if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(got, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
}
})
}
}
var (
foo1 = &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "foo1",
},
}
foo2 = &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "foo2",
},
}
bar1 = &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "bar1",
},
}
workspaces = []interface{}{foo1, foo2, bar1}
)
func prepare() v1alpha3.Interface {
client := fake.NewSimpleClientset()
informer := informers.NewSharedInformerFactory(client, 0)
for _, workspace := range workspaces {
informer.Tenant().V1alpha2().WorkspaceTemplates().Informer().GetIndexer().Add(workspace)
}
return New(informer)
}

View File

@@ -21,16 +21,21 @@ import (
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
eventsv1alpha1 "kubesphere.io/kubesphere/pkg/api/events/v1alpha1"
clusterv1alpha1 "kubesphere.io/kubesphere/pkg/apis/cluster/v1alpha1"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"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 "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/models/events"
"kubesphere.io/kubesphere/pkg/models/iam/am"
@@ -45,25 +50,34 @@ import (
type Interface interface {
ListWorkspaces(user user.Info, query *query.Query) (*api.ListResult, error)
ListNamespaces(user user.Info, workspace string, query *query.Query) (*api.ListResult, error)
CreateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error)
CreateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
DeleteWorkspace(workspace string) error
UpdateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error)
DescribeWorkspace(workspace string) (*tenantv1alpha2.WorkspaceTemplate, error)
ListWorkspaceClusters(workspace string) (*api.ListResult, error)
Events(user user.Info, queryParam *eventsv1alpha1.Query) (*eventsv1alpha1.APIResponse, error)
}
type tenantOperator struct {
am am.AccessManagementInterface
authorizer authorizer.Authorizer
k8sclient kubernetes.Interface
ksclient kubesphere.Interface
resourceGetter *resourcesv1alpha3.ResourceGetter
events events.Interface
}
func New(informers informers.InformerFactory, evtsClient eventsclient.Client) Interface {
amOperator := am.NewAMOperator(informers)
rbacAuthorizer := authorizerfactory.NewRBACAuthorizer(amOperator)
opaAuthorizer := authorizerfactory.NewOPAAuthorizer(amOperator)
authorizers := unionauthorizer.New(opaAuthorizer, rbacAuthorizer)
func New(informers informers.InformerFactory, k8sclient kubernetes.Interface, ksclient kubesphere.Interface, evtsClient eventsclient.Client) Interface {
amOperator := am.NewReadOnlyOperator(informers)
authorizer := authorizerfactory.NewRBACAuthorizer(amOperator)
return &tenantOperator{
am: amOperator,
authorizer: authorizers,
authorizer: authorizer,
resourceGetter: resourcesv1alpha3.NewResourceGetter(informers),
k8sclient: k8sclient,
ksclient: ksclient,
events: events.NewEventsOperator(evtsClient),
}
}
@@ -88,7 +102,7 @@ func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query)
if decision == authorizer.DecisionAllow {
result, err := t.resourceGetter.List(tenantv1alpha1.ResourcePluralWorkspace, "", queryParam)
result, err := t.resourceGetter.List(tenantv1alpha2.ResourcePluralWorkspaceTemplate, "", queryParam)
if err != nil {
klog.Error(err)
@@ -110,10 +124,10 @@ func (t *tenantOperator) ListWorkspaces(user user.Info, queryParam *query.Query)
for _, roleBinding := range workspaceRoleBindings {
workspaceName := roleBinding.Labels[tenantv1alpha1.WorkspaceLabel]
workspace, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspaceName)
workspace, err := t.resourceGetter.Get(tenantv1alpha2.ResourcePluralWorkspaceTemplate, "", workspaceName)
if errors.IsNotFound(err) {
klog.Warningf("workspace role binding: %+v found but workspace not exist", roleBinding.ObjectMeta)
klog.Warningf("workspace role binding: %+v found but workspace not exist", roleBinding.ObjectMeta.String())
continue
}
@@ -209,6 +223,65 @@ func (t *tenantOperator) ListNamespaces(user user.Info, workspace string, queryP
return result, nil
}
func (t *tenantOperator) CreateNamespace(workspace string, namespace *corev1.Namespace) (*corev1.Namespace, error) {
_, err := t.resourceGetter.Get(tenantv1alpha1.ResourcePluralWorkspace, "", workspace)
if err != nil {
return nil, err
}
if namespace.Annotations == nil {
namespace.Annotations = make(map[string]string, 0)
}
namespace.Annotations[tenantv1alpha1.WorkspaceLabel] = workspace
return t.k8sclient.CoreV1().Namespaces().Create(namespace)
}
func (t *tenantOperator) CreateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error) {
return t.ksclient.TenantV1alpha2().WorkspaceTemplates().Create(workspace)
}
func (t *tenantOperator) UpdateWorkspace(workspace *tenantv1alpha2.WorkspaceTemplate) (*tenantv1alpha2.WorkspaceTemplate, error) {
return t.ksclient.TenantV1alpha2().WorkspaceTemplates().Update(workspace)
}
func (t *tenantOperator) DescribeWorkspace(workspace string) (*tenantv1alpha2.WorkspaceTemplate, error) {
obj, err := t.resourceGetter.Get(tenantv1alpha2.ResourcePluralWorkspaceTemplate, "", workspace)
if err != nil {
klog.Error(err)
return nil, err
}
return obj.(*tenantv1alpha2.WorkspaceTemplate), nil
}
func (t *tenantOperator) ListWorkspaceClusters(workspaceName string) (*api.ListResult, error) {
workspace, err := t.DescribeWorkspace(workspaceName)
if err != nil {
klog.Error(err)
return nil, err
}
clusters := make([]interface{}, 0)
for _, cluster := range workspace.Spec.Clusters {
obj, err := t.resourceGetter.Get(clusterv1alpha1.ResourcesPluralCluster, "", cluster)
if err != nil {
klog.Error(err)
if errors.IsNotFound(err) {
continue
}
return nil, err
}
cluster := obj.(*clusterv1alpha1.Cluster)
clusters = append(clusters, cluster)
}
return &api.ListResult{Items: clusters, TotalItems: len(clusters)}, nil
}
func (t *tenantOperator) DeleteWorkspace(workspace string) error {
return t.ksclient.TenantV1alpha2().WorkspaceTemplates().Delete(workspace, metav1.NewDeleteOptions(0))
}
// listIntersectedNamespaces lists the namespaces which meet all the following conditions at the same time
// 1. the namespace which belongs to user.
// 2. the namespace in workspace which is in workspaces when workspaces is not empty.

View File

@@ -30,6 +30,7 @@ import (
"kubesphere.io/kubesphere/pkg/api"
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
tenantv1alpha1 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/query"
fakeks "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
@@ -163,7 +164,6 @@ var (
Resources: []string{"*"},
},
},
AggregationRule: nil,
}
regularGlobalRole = &iamv1alpha2.GlobalRole{
ObjectMeta: metav1.ObjectMeta{
@@ -176,7 +176,6 @@ var (
Resources: []string{},
},
},
AggregationRule: nil,
}
reguarWorksapceRole = &iamv1alpha2.WorkspaceRole{
ObjectMeta: metav1.ObjectMeta{
@@ -190,7 +189,6 @@ var (
Resources: []string{},
},
},
AggregationRule: nil,
}
adminGlobalRoleBinding = &iamv1alpha2.GlobalRoleBinding{
ObjectMeta: metav1.ObjectMeta{
@@ -264,12 +262,12 @@ var (
Name: "admin",
},
}
workspaceFoo = &tenantv1alpha1.Workspace{
workspaceFoo = &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
}
workspaceBar = &tenantv1alpha1.Workspace{
workspaceBar = &tenantv1alpha2.WorkspaceTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
@@ -293,8 +291,8 @@ func prepare() Interface {
fakeInformerFactory := informers.NewInformerFactories(k8sClient, ksClient, istioClient, appClient, nil, nil)
for _, workspace := range workspaces {
fakeInformerFactory.KubeSphereSharedInformerFactory().Tenant().V1alpha1().
Workspaces().Informer().GetIndexer().Add(workspace)
fakeInformerFactory.KubeSphereSharedInformerFactory().Tenant().V1alpha2().
WorkspaceTemplates().Informer().GetIndexer().Add(workspace)
}
for _, namespace := range namespaces {
@@ -332,5 +330,5 @@ func prepare() Interface {
RoleBindings().Informer().GetIndexer().Add(roleBinding)
}
return New(fakeInformerFactory, nil)
return New(fakeInformerFactory, nil, nil, nil)
}