375 lines
12 KiB
Go
375 lines
12 KiB
Go
/*
|
|
*
|
|
* Copyright 2020 The KubeSphere Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
* /
|
|
*/
|
|
package am
|
|
|
|
import (
|
|
"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/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"
|
|
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
|
"kubesphere.io/kubesphere/pkg/informers"
|
|
resourcev1alpha3 "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3/resource"
|
|
"net/http"
|
|
)
|
|
|
|
type AccessManagementInterface interface {
|
|
GetGlobalRoleOfUser(username string) (*iamv1alpha2.GlobalRole, error)
|
|
GetWorkspaceRoleOfUser(username, workspace string) (*iamv1alpha2.WorkspaceRole, error)
|
|
GetClusterRoleOfUser(username, cluster 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)
|
|
ListWorkspaceRoles(query *query.Query) (*api.ListResult, error)
|
|
ListGlobalRoles(query *query.Query) (*api.ListResult, error)
|
|
|
|
ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error)
|
|
ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error)
|
|
ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error)
|
|
ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error)
|
|
|
|
GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error)
|
|
}
|
|
|
|
type amOperator struct {
|
|
ksinformer ksinformers.SharedInformerFactory
|
|
k8sinformer k8sinformers.SharedInformerFactory
|
|
resourceGetter *resourcev1alpha3.ResourceGetter
|
|
}
|
|
|
|
func NewAMOperator(factory informers.InformerFactory) AccessManagementInterface {
|
|
return &amOperator{
|
|
ksinformer: factory.KubeSphereSharedInformerFactory(),
|
|
k8sinformer: factory.KubernetesSharedInformerFactory(),
|
|
resourceGetter: resourcev1alpha3.NewResourceGetter(factory),
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
if len(userRoleBindings) > 0 {
|
|
role, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(userRoleBindings[0].RoleRef.Name)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
if len(roleBindings) > 1 {
|
|
klog.Warningf("conflict global role binding, username: %s", username)
|
|
}
|
|
return role, nil
|
|
}
|
|
|
|
err = &errors.StatusError{ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusNotFound,
|
|
Reason: metav1.StatusReasonNotFound,
|
|
Details: &metav1.StatusDetails{
|
|
Group: iamv1alpha2.SchemeGroupVersion.Group,
|
|
Kind: iamv1alpha2.ResourceKindGlobalRoleBinding,
|
|
},
|
|
Message: fmt.Sprintf("global role binding not found for %s", username),
|
|
}}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
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}))
|
|
|
|
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)
|
|
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
|
|
if len(roleBindings) > 1 {
|
|
klog.Warningf("conflict workspace role binding, username: %s", username)
|
|
}
|
|
|
|
return role, nil
|
|
}
|
|
|
|
err = &errors.StatusError{ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusNotFound,
|
|
Reason: metav1.StatusReasonNotFound,
|
|
Details: &metav1.StatusDetails{
|
|
Group: iamv1alpha2.SchemeGroupVersion.Group,
|
|
Kind: iamv1alpha2.ResourceKindWorkspaceRoleBinding,
|
|
},
|
|
Message: fmt.Sprintf("workspace role binding not found for %s", username),
|
|
}}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
func (am *amOperator) GetNamespaceRoleOfUser(username, namespace string) (*rbacv1.Role, error) {
|
|
roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().List(labels.Everything())
|
|
|
|
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)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
if len(roleBindings) > 1 {
|
|
klog.Warningf("conflict role binding, username: %s", username)
|
|
}
|
|
return role, nil
|
|
}
|
|
|
|
err = &errors.StatusError{ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusNotFound,
|
|
Reason: metav1.StatusReasonNotFound,
|
|
Details: &metav1.StatusDetails{
|
|
Group: rbacv1.SchemeGroupVersion.Group,
|
|
Kind: "RoleBinding",
|
|
},
|
|
Message: fmt.Sprintf("role binding not found for %s in %s", username, namespace),
|
|
}}
|
|
|
|
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())
|
|
|
|
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)
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
if len(roleBindings) > 1 {
|
|
klog.Warningf("conflict cluster role binding, username: %s", username)
|
|
}
|
|
return role, nil
|
|
}
|
|
err = &errors.StatusError{ErrStatus: metav1.Status{
|
|
Status: metav1.StatusFailure,
|
|
Code: http.StatusNotFound,
|
|
Reason: metav1.StatusReasonNotFound,
|
|
Details: &metav1.StatusDetails{
|
|
Group: rbacv1.SchemeGroupVersion.Group,
|
|
Kind: "ClusterRoleBinding",
|
|
},
|
|
Message: fmt.Sprintf("cluster role binding not found for %s in %s", username, cluster),
|
|
}}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
func (am *amOperator) ListWorkspaceRoleBindings(username, workspace string) ([]*iamv1alpha2.WorkspaceRoleBinding, error) {
|
|
roleBindings, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoleBindings().Lister().List(labels.Everything())
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]*iamv1alpha2.WorkspaceRoleBinding, 0)
|
|
|
|
for _, roleBinding := range roleBindings {
|
|
inSpecifiedWorkspace := workspace == "" || roleBinding.Labels[tenantv1alpha1.WorkspaceLabel] == workspace
|
|
if contains(roleBinding.Subjects, username) && inSpecifiedWorkspace {
|
|
result = append(result, roleBinding)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (am *amOperator) ListClusterRoleBindings(username string) ([]*rbacv1.ClusterRoleBinding, error) {
|
|
|
|
roleBindings, err := am.k8sinformer.Rbac().V1().ClusterRoleBindings().Lister().List(labels.Everything())
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]*rbacv1.ClusterRoleBinding, 0)
|
|
|
|
for _, roleBinding := range roleBindings {
|
|
if contains(roleBinding.Subjects, username) {
|
|
result = append(result, roleBinding)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (am *amOperator) ListGlobalRoleBindings(username string) ([]*iamv1alpha2.GlobalRoleBinding, error) {
|
|
roleBindings, err := am.ksinformer.Iam().V1alpha2().GlobalRoleBindings().Lister().List(labels.Everything())
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]*iamv1alpha2.GlobalRoleBinding, 0)
|
|
|
|
for _, roleBinding := range roleBindings {
|
|
if contains(roleBinding.Subjects, username) {
|
|
result = append(result, roleBinding)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (am *amOperator) ListRoleBindings(username, namespace string) ([]*rbacv1.RoleBinding, error) {
|
|
|
|
roleBindings, err := am.k8sinformer.Rbac().V1().RoleBindings().Lister().RoleBindings(namespace).List(labels.Everything())
|
|
|
|
if err != nil {
|
|
klog.Error(err)
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]*rbacv1.RoleBinding, 0)
|
|
|
|
for _, roleBinding := range roleBindings {
|
|
if contains(roleBinding.Subjects, username) {
|
|
result = append(result, roleBinding)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func contains(subjects []rbacv1.Subject, username string) bool {
|
|
for _, subject := range subjects {
|
|
if subject.Kind == rbacv1.UserKind && (username == "" || subject.Name == username) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (am *amOperator) ListRoles(namespace string, query *query.Query) (*api.ListResult, error) {
|
|
return am.resourceGetter.List("roles", namespace, query)
|
|
}
|
|
|
|
func (am *amOperator) ListClusterRoles(query *query.Query) (*api.ListResult, error) {
|
|
return am.resourceGetter.List("clusterroles", "", query)
|
|
}
|
|
|
|
func (am *amOperator) ListWorkspaceRoles(queryParam *query.Query) (*api.ListResult, error) {
|
|
return am.resourceGetter.List(iamv1alpha2.ResourcesPluralWorkspaceRole, "", queryParam)
|
|
}
|
|
|
|
func (am *amOperator) ListGlobalRoles(query *query.Query) (*api.ListResult, error) {
|
|
return am.resourceGetter.List(iamv1alpha2.ResourcesPluralGlobalRole, "", query)
|
|
}
|
|
|
|
// GetRoleReferenceRules attempts to resolve the RoleBinding or ClusterRoleBinding.
|
|
func (am *amOperator) GetRoleReferenceRules(roleRef rbacv1.RoleRef, bindingNamespace string) ([]rbacv1.PolicyRule, error) {
|
|
switch roleRef.Kind {
|
|
case iamv1alpha2.ResourceKindRole:
|
|
role, err := am.k8sinformer.Rbac().V1().Roles().Lister().Roles(bindingNamespace).Get(roleRef.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return role.Rules, nil
|
|
case iamv1alpha2.ResourceKindClusterRole:
|
|
clusterRole, err := am.k8sinformer.Rbac().V1().ClusterRoles().Lister().Get(roleRef.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return clusterRole.Rules, nil
|
|
case iamv1alpha2.ResourceKindGlobalRole:
|
|
globalRole, err := am.ksinformer.Iam().V1alpha2().GlobalRoles().Lister().Get(roleRef.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return globalRole.Rules, nil
|
|
case iamv1alpha2.ResourceKindWorkspaceRole:
|
|
workspaceRole, err := am.ksinformer.Iam().V1alpha2().WorkspaceRoles().Lister().Get(roleRef.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return workspaceRole.Rules, nil
|
|
default:
|
|
return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
|
|
}
|
|
}
|