From 6071095e242b40f8930a21c5ea64fb68499ac26d Mon Sep 17 00:00:00 2001 From: hongming Date: Tue, 26 Jun 2018 13:46:54 +0800 Subject: [PATCH] Refactor iam module. --- pkg/apis/v1alpha/iam/iam_handler.go | 40 +-- pkg/apis/v1alpha/iam/tools.go | 228 ------------------ pkg/apis/v1alpha/users/user.go | 4 +- pkg/app/app.go | 4 +- pkg/constants/common.go | 18 +- pkg/models/components.go | 16 +- .../{clusterRoles.go => clusterroles.go} | 0 pkg/models/controllers/namespaces.go | 161 +++++++------ pkg/models/{roles.go => iam/iam.go} | 115 ++++++++- pkg/{apis/v1alpha => models}/iam/policy.go | 104 ++++---- pkg/models/iam/tools.go | 161 +++++++++++++ pkg/models/kubeconfig.go | 6 +- pkg/models/kubectl.go | 2 +- pkg/models/routes.go | 42 ++-- pkg/version/version.go | 2 +- 15 files changed, 478 insertions(+), 425 deletions(-) delete mode 100644 pkg/apis/v1alpha/iam/tools.go rename pkg/models/controllers/{clusterRoles.go => clusterroles.go} (100%) rename pkg/models/{roles.go => iam/iam.go} (63%) rename pkg/{apis/v1alpha => models}/iam/policy.go (91%) create mode 100644 pkg/models/iam/tools.go diff --git a/pkg/apis/v1alpha/iam/iam_handler.go b/pkg/apis/v1alpha/iam/iam_handler.go index c18568913..38ca6c0ac 100644 --- a/pkg/apis/v1alpha/iam/iam_handler.go +++ b/pkg/apis/v1alpha/iam/iam_handler.go @@ -28,9 +28,19 @@ import ( "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/filter/route" - "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/iam" ) +type roleList struct { + ClusterRoles []v1.ClusterRole `json:"clusterRoles" protobuf:"bytes,2,rep,name=clusterRoles"` + Roles []v1.Role `json:"roles" protobuf:"bytes,2,rep,name=roles"` +} + +type userRuleList struct { + ClusterRules []iam.Rule `json:"clusterRules"` + Rules map[string][]iam.Rule `json:"rules"` +} + func Register(ws *restful.WebService) { //roles ws.Route(ws.GET("/users/{username}/roles").To(userRolesHandler).Filter(route.RouteLogging)).Produces(restful.MIME_JSON) @@ -53,14 +63,14 @@ func userRolesHandler(req *restful.Request, resp *restful.Response) { username := req.PathParameter("username") - roles, err := models.GetRoles(username) + roles, err := iam.GetRoles(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return } - clusterRoles, err := models.GetClusterRoles(username) + clusterRoles, err := iam.GetClusterRoles(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -79,7 +89,7 @@ func roleUsersHandler(req *restful.Request, resp *restful.Response) { name := req.PathParameter("name") namespace := req.PathParameter("namespace") - roleBindings, err := models.GetRoleBindings(namespace, name) + roleBindings, err := iam.GetRoleBindings(namespace, name) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -105,7 +115,7 @@ func roleUsersHandler(req *restful.Request, resp *restful.Response) { func clusterRoleUsersHandler(req *restful.Request, resp *restful.Response) { name := req.PathParameter("name") - roleBindings, err := models.GetClusterRoleBindings(name) + roleBindings, err := iam.GetClusterRoleBindings(name) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -138,14 +148,14 @@ func usersRulesHandler(req *restful.Request, resp *restful.Response) { userRuleList := userRuleList{} - clusterRules, err := getUserClusterRules(username) + clusterRules, err := iam.GetUserClusterRules(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return } - rules, err := getUserRules(username) + rules, err := iam.GetUserRules(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -168,14 +178,14 @@ func userRulesHandler(req *restful.Request, resp *restful.Response) { userRuleList := userRuleList{} - clusterRules, err := getUserClusterRules(username) + clusterRules, err := iam.GetUserClusterRules(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return } - rules, err := getUserRules(username) + rules, err := iam.GetUserRules(username) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) @@ -193,13 +203,13 @@ func clusterRoleRulesHandler(req *restful.Request, resp *restful.Response) { name := req.PathParameter("name") - var rules []rule + var rules []iam.Rule if name == "" { - rules = clusterRoleRuleGroup + rules = iam.ClusterRoleRuleGroup } else { var err error - rules, err = getClusterRoleRules(name) + rules, err = iam.GetClusterRoleRules(name) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return @@ -214,13 +224,13 @@ func roleRulesHandler(req *restful.Request, resp *restful.Response) { name := req.PathParameter("name") namespace := req.PathParameter("namespace") - var rules []rule + var rules []iam.Rule if namespace == "" && name == "" { - rules = roleRuleGroup + rules = iam.RoleRuleGroup } else { var err error - rules, err = getRoleRules(namespace, name) + rules, err = iam.GetRoleRules(namespace, name) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) return diff --git a/pkg/apis/v1alpha/iam/tools.go b/pkg/apis/v1alpha/iam/tools.go deleted file mode 100644 index c519f35e9..000000000 --- a/pkg/apis/v1alpha/iam/tools.go +++ /dev/null @@ -1,228 +0,0 @@ -/* - Copyright 2018 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 iam - -import ( - "k8s.io/api/rbac/v1" - - "k8s.io/kubernetes/pkg/util/slice" - - "kubesphere.io/kubesphere/pkg/models" -) - -func getUserRules(username string) (map[string][]rule, error) { - - items := make(map[string][]rule, 0) - roles, err := models.GetRoles(username) - - if err != nil { - return nil, err - } - - namespaces := make([]string, 0) - - for i := 0; i < len(roles); i++ { - if !slice.ContainsString(namespaces, roles[i].Namespace, nil) { - namespaces = append(namespaces, roles[i].Namespace) - } - } - - for _, namespace := range namespaces { - rules := getMergeRules(namespace, roles) - if len(rules) > 0 { - items[namespace] = rules - } - } - - return items, nil -} - -func getMergeRules(namespace string, roles []v1.Role) []rule { - rules := make([]rule, 0) - - for i := 0; i < (len(roleRuleGroup)); i++ { - rule := rule{Name: roleRuleGroup[i].Name} - rule.Actions = make([]action, 0) - for j := 0; j < (len(roleRuleGroup[i].Actions)); j++ { - permit := false - for _, role := range roles { - if role.Namespace == namespace && actionValidate(role.Rules, roleRuleGroup[i].Actions[j]) { - permit = true - break - } - } - if permit { - rule.Actions = append(rule.Actions, roleRuleGroup[i].Actions[j]) - } - } - - if len(rule.Actions) > 0 { - rules = append(rules, rule) - } - } - - return rules -} - -func getUserClusterRules(username string) ([]rule, error) { - - rules := make([]rule, 0) - - roles, err := models.GetClusterRoles(username) - - if err != nil { - return nil, err - } - - for i := 0; i < (len(clusterRoleRuleGroup)); i++ { - rule := rule{Name: clusterRoleRuleGroup[i].Name} - rule.Actions = make([]action, 0) - for j := 0; j < (len(clusterRoleRuleGroup[i].Actions)); j++ { - actionPermit := false - for _, role := range roles { - if actionValidate(role.Rules, clusterRoleRuleGroup[i].Actions[j]) { - actionPermit = true - break - } - } - if actionPermit { - rule.Actions = append(rule.Actions, clusterRoleRuleGroup[i].Actions[j]) - } - } - - if len(rule.Actions) > 0 { - rules = append(rules, rule) - } - } - - return rules, nil -} - -func getClusterRoleRules(name string) ([]rule, error) { - - clusterRole, err := models.GetClusterRole(name) - - if err != nil { - return nil, err - } - - rules := make([]rule, 0) - - for i := 0; i < len(clusterRoleRuleGroup); i++ { - rule := rule{Name: clusterRoleRuleGroup[i].Name} - rule.Actions = make([]action, 0) - for j := 0; j < (len(clusterRoleRuleGroup[i].Actions)); j++ { - if actionValidate(clusterRole.Rules, clusterRoleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, clusterRoleRuleGroup[i].Actions[j]) - } - } - if len(rule.Actions) > 0 { - rules = append(rules, rule) - } - } - - return rules, nil -} - -func getRoleRules(namespace string, name string) ([]rule, error) { - role, err := models.GetRole(namespace, name) - if err != nil { - return nil, err - } - rules := make([]rule, 0) - for i := 0; i < len(roleRuleGroup); i++ { - rule := rule{Name: roleRuleGroup[i].Name} - rule.Actions = make([]action, 0) - for j := 0; j < len(roleRuleGroup[i].Actions); j++ { - if actionValidate(role.Rules, roleRuleGroup[i].Actions[j]) { - rule.Actions = append(rule.Actions, roleRuleGroup[i].Actions[j]) - } - } - if len(rule.Actions) > 0 { - rules = append(rules, rule) - } - } - return rules, nil -} - -func actionValidate(rules []v1.PolicyRule, action action) bool { - for _, rule := range action.Rules { - if !ruleValidate(rules, rule) { - return false - } - } - return true -} - -func ruleValidate(rules []v1.PolicyRule, rule v1.PolicyRule) bool { - - for _, apiGroup := range rule.APIGroups { - if len(rule.NonResourceURLs) == 0 { - for _, resource := range rule.Resources { - - //if len(rule.ResourceNames) == 0 { - - for _, verb := range rule.Verbs { - if !verbValidate(rules, apiGroup, "", resource, "", verb) { - return false - } - } - - //} else { - // for _, resourceName := range rule.ResourceNames { - // for _, verb := range rule.Verbs { - // if !verbValidate(rules, apiGroup, "", resource, resourceName, verb) { - // return false - // } - // } - // } - //} - } - } else { - for _, nonResourceURL := range rule.NonResourceURLs { - for _, verb := range rule.Verbs { - if !verbValidate(rules, apiGroup, nonResourceURL, "", "", verb) { - return false - } - } - } - } - } - return true -} - -func verbValidate(rules []v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, resourceName string, verb string) bool { - for _, rule := range rules { - if slice.ContainsString(rule.APIGroups, apiGroup, nil) || slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) { - if slice.ContainsString(rule.Verbs, verb, nil) || slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { - if nonResourceURL == "" { - if slice.ContainsString(rule.Resources, resource, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { - if resourceName == "" { - return true - } else if slice.ContainsString(rule.ResourceNames, resourceName, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { - return true - } - } - } else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) || slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) { - return true - } - } - } - } - return false -} diff --git a/pkg/apis/v1alpha/users/user.go b/pkg/apis/v1alpha/users/user.go index 23ea8766a..73fbafe58 100644 --- a/pkg/apis/v1alpha/users/user.go +++ b/pkg/apis/v1alpha/users/user.go @@ -20,11 +20,11 @@ import ( "net/http" "github.com/emicklei/go-restful" - apierrors "k8s.io/apimachinery/pkg/api/errors" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/models" + "kubesphere.io/kubesphere/pkg/models/iam" ) func Register(ws *restful.WebService, subPath string) { @@ -74,7 +74,7 @@ func delUser(req *restful.Request, resp *restful.Response) { return } - err = models.DeleteRoleBindings(user) + err = iam.DeleteRoleBindings(user) if err != nil { resp.WriteHeaderAndEntity(http.StatusInternalServerError, constants.MessageResponse{Message: err.Error()}) diff --git a/pkg/app/app.go b/pkg/app/app.go index 6619692f3..2f46bf70e 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -66,11 +66,11 @@ func preCheck() error { return err } for _, ns := range nsList.Items { - if ns.Name == constants.KubeSphereControlNameSpace { + if ns.Name == constants.KubeSphereControlNamespace { return nil } } - namespace := v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: constants.KubeSphereControlNameSpace}} + namespace := v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: constants.KubeSphereControlNamespace}} _, err = k8sClient.CoreV1().Namespaces().Create(&namespace) return err } diff --git a/pkg/constants/common.go b/pkg/constants/common.go index 26de2799e..8ac6a8cb0 100644 --- a/pkg/constants/common.go +++ b/pkg/constants/common.go @@ -26,12 +26,16 @@ type PageableResponse struct { } const ( - APIVERSION = "v1alpha1" - KIND = "kubesphere" - KubeSphereControlNameSpace = "kubesphere-controls-system" + APIVersion = "v1alpha1" - DataHome = "/etc/kubesphere" - IngressControllerFolder = DataHome + "/ingress-controller" - IngressControllerNamespace = KubeSphereControlNameSpace - IngressControllerPrefix = "kubesphere-router-" + KubeSystemNamespace = "kube-system" + OpenPitrixNamespace = "openpitrix-system" + IstioNamespace = "istio-system" + KubeSphereNamespace = "kubesphere-system" + KubeSphereControlNamespace = "kubesphere-controls-system" + IngressControllerNamespace = KubeSphereControlNamespace + + DataHome = "/etc/kubesphere" + IngressControllerFolder = DataHome + "/ingress-controller" + IngressControllerPrefix = "kubesphere-router-" ) diff --git a/pkg/models/components.go b/pkg/models/components.go index 44730fe1e..fad60e1f7 100644 --- a/pkg/models/components.go +++ b/pkg/models/components.go @@ -23,13 +23,9 @@ import ( meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "kubesphere.io/kubesphere/pkg/client" + "kubesphere.io/kubesphere/pkg/constants" ) -const KUBESYSTEM = "kube-system" -const OPENPITRIX = "openpitrix-system" -const ISTIO = "istio-system" -const KUBESPHERE = "kubesphere-system" - type ComponentsCount struct { KubernetesCount int `json:"kubernetesCount"` OpenpitrixCount int `json:"openpitrixCount"` @@ -63,10 +59,10 @@ func GetComponents() (map[string]interface{}, error) { LabelSelector: label, } - namespaces := []string{KUBESYSTEM, OPENPITRIX, ISTIO, KUBESPHERE} + namespaces := []string{constants.KubeSystemNamespace, constants.OpenPitrixNamespace, constants.IstioNamespace, constants.KubeSphereNamespace} for _, ns := range namespaces { - if ns != KUBESYSTEM { + if ns != constants.KubeSystemNamespace { option.LabelSelector = "" } servicelists, err := k8sClient.CoreV1().Services(ns).List(option) @@ -84,11 +80,11 @@ func GetComponents() (map[string]interface{}, error) { switch ns { - case KUBESYSTEM: + case constants.KubeSystemNamespace: count.KubernetesCount++ - case OPENPITRIX: + case constants.OpenPitrixNamespace: count.OpenpitrixCount++ - case KUBESPHERE: + case constants.KubeSphereNamespace: count.KubesphereCount++ default: diff --git a/pkg/models/controllers/clusterRoles.go b/pkg/models/controllers/clusterroles.go similarity index 100% rename from pkg/models/controllers/clusterRoles.go rename to pkg/models/controllers/clusterroles.go diff --git a/pkg/models/controllers/namespaces.go b/pkg/models/controllers/namespaces.go index ca62a4d73..2961b05f0 100644 --- a/pkg/models/controllers/namespaces.go +++ b/pkg/models/controllers/namespaces.go @@ -33,19 +33,23 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/kubernetes/pkg/util/slice" + "kubesphere.io/kubesphere/pkg/client" "kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/options" ) const ( - provider = "kubernetes" - admin = "admin" - editor = "editor" - viewer = "viewer" - kubectlNamespace = constants.KubeSphereControlNameSpace - kubectlConfigKey = "config" - openpitrix_runtime = "openpitrix_runtime" + provider = "kubernetes" + admin = "admin" + editor = "editor" + viewer = "viewer" + kubectlNamespace = constants.KubeSphereControlNamespace + kubectlConfigKey = "config" + openPitrixRuntimeAnnotateKey = "openpitrix_runtime" + creatorAnnotateKey = "creator" ) var adminRules = []rbac.PolicyRule{{Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}}} @@ -96,7 +100,13 @@ func (ctl *NamespaceCtl) getKubeConfig(user string) (string, error) { func (ctl *NamespaceCtl) deleteOpRuntime(item v1.Namespace) { - runtimeId := item.Annotations["openpitrix_runtime"] + var runtimeId string + if item.Annotations == nil { + runtimeId = "" + } else { + runtimeId = item.Annotations[openPitrixRuntimeAnnotateKey] + } + if len(runtimeId) == 0 { return } @@ -107,9 +117,10 @@ func (ctl *NamespaceCtl) deleteOpRuntime(item v1.Namespace) { body, err := json.Marshal(deleteRuntime) if err != nil { - glog.Error(err) + glog.Error("runtime release failed:", item.Name, runtimeId, err) return } + glog.Info("runtime release succeeded:", item.Name, runtimeId) // todo: if delete failed, what's to be done? makeHttpRequest("DELETE", url, string(body)) @@ -137,19 +148,15 @@ func (ctl *NamespaceCtl) createOpRuntime(namespace string) ([]byte, error) { } func (ctl *NamespaceCtl) createDefaultRoleBinding(ns, user string) error { - rolebinding, _ := ctl.K8sClient.RbacV1().RoleBindings(ns).Get(admin, metaV1.GetOptions{}) - if rolebinding.Name != admin { + roleBinding := &rbac.RoleBinding{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: ns}, + Subjects: []rbac.Subject{{Name: user, Kind: rbac.UserKind}}, RoleRef: rbac.RoleRef{Kind: "Role", Name: admin}} - roleBinding := &rbac.RoleBinding{ObjectMeta: metaV1.ObjectMeta{Name: admin, Namespace: ns}, - Subjects: []rbac.Subject{{Name: user, Kind: rbac.UserKind}}, RoleRef: rbac.RoleRef{Kind: "Role", Name: admin}} + _, err := ctl.K8sClient.RbacV1().RoleBindings(ns).Create(roleBinding) - _, err := ctl.K8sClient.RbacV1().RoleBindings(ns).Create(roleBinding) - - if err != nil { - glog.Error(err) - return err - } + if err != nil && !errors.IsAlreadyExists(err) { + glog.Error(err) + return err } return nil @@ -160,72 +167,86 @@ func (ctl *NamespaceCtl) createDefaultRole(ns string) error { editorRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: editor, Namespace: ns}, Rules: editorRules} viewerRole := &rbac.Role{ObjectMeta: metaV1.ObjectMeta{Name: viewer, Namespace: ns}, Rules: viewerRules} - role, _ := ctl.K8sClient.RbacV1().Roles(ns).Get(admin, metaV1.GetOptions{}) + _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(adminRole) - if role.Name != admin { - _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(adminRole) - if err != nil { - glog.Error(err) - return err - } + if err != nil && !errors.IsAlreadyExists(err) { + return err } - role, _ = ctl.K8sClient.RbacV1().Roles(ns).Get(editor, metaV1.GetOptions{}) + _, err = ctl.K8sClient.RbacV1().Roles(ns).Create(editorRole) - if role.Name != editor { - _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(editorRole) - if err != nil { - glog.Error(err) - return err - } + if err != nil && !errors.IsAlreadyExists(err) { + return err } - role, _ = ctl.K8sClient.RbacV1().Roles(ns).Get(viewer, metaV1.GetOptions{}) + _, err = ctl.K8sClient.RbacV1().Roles(ns).Create(viewerRole) - if role.Name != viewer { - _, err := ctl.K8sClient.RbacV1().Roles(ns).Create(viewerRole) - if err != nil { - glog.Error(err) - return err - } + if err != nil && !errors.IsAlreadyExists(err) { + return err } + return nil } func (ctl *NamespaceCtl) createRoleAndRuntime(item v1.Namespace) { - user := item.Annotations["creator"] + var creator string + var runtime string ns := item.Name - if len(user) > 0 && len(item.Annotations[openpitrix_runtime]) == 0 { - err := ctl.createDefaultRole(ns) - if err != nil { - return - } - resp, err := ctl.createOpRuntime(ns) - if err != nil { - glog.Error(err) - return - } - - err = ctl.createDefaultRoleBinding(ns, user) - if err != nil { - glog.Error(err) - return - } - - var runtime runTime - err = json.Unmarshal(resp, &runtime) - if err != nil { - glog.Error(err) - return - } - - item.Annotations[openpitrix_runtime] = runtime.RuntimeId - _, err = ctl.K8sClient.CoreV1().Namespaces().Update(&item) - if err != nil { - glog.Error(err) - } + if item.Annotations == nil { + creator = "" + runtime = "" + } else { + runtime = item.Annotations[openPitrixRuntimeAnnotateKey] + creator = item.Annotations[creatorAnnotateKey] } + + componentsNamespaces := []string{constants.KubeSystemNamespace, constants.OpenPitrixNamespace, constants.IstioNamespace, constants.KubeSphereNamespace} + + if len(runtime) == 0 && !slice.ContainsString(componentsNamespaces, ns, nil) { + glog.Infoln("create runtime:", ns) + var runtimeCreateError error + resp, runtimeCreateError := ctl.createOpRuntime(ns) + + if runtimeCreateError == nil { + var runtime runTime + runtimeCreateError = json.Unmarshal(resp, &runtime) + if runtimeCreateError == nil { + + if item.Annotations == nil { + item.Annotations = make(map[string]string, 0) + } + + item.Annotations[openPitrixRuntimeAnnotateKey] = runtime.RuntimeId + _, runtimeCreateError = ctl.K8sClient.CoreV1().Namespaces().Update(&item) + + } + } + + if runtimeCreateError != nil { + glog.Error("runtime create error:", runtimeCreateError) + } + + if len(creator) > 0 { + roleCreateError := ctl.createDefaultRole(ns) + glog.Infoln("create default role:", ns) + if roleCreateError == nil { + + roleBindingError := ctl.createDefaultRoleBinding(ns, creator) + glog.Infoln("create default role binding:", ns) + if roleBindingError != nil { + glog.Error("default role binding create error:", roleBindingError) + } + + } else { + glog.Error("default role create error:", roleCreateError) + } + + } + } else { + glog.Infoln("runtime has been init:", ns, runtime) + } + } func (ctl *NamespaceCtl) generateObject(item v1.Namespace) *Namespace { diff --git a/pkg/models/roles.go b/pkg/models/iam/iam.go similarity index 63% rename from pkg/models/roles.go rename to pkg/models/iam/iam.go index e695b47f6..c019b3db5 100644 --- a/pkg/models/roles.go +++ b/pkg/models/iam/iam.go @@ -1,4 +1,4 @@ -package models +package iam import ( "github.com/golang/glog" @@ -6,11 +6,66 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/util/slice" + "kubesphere.io/kubesphere/pkg/client" ) const ClusterRoleKind = "ClusterRole" +func GetUserNamespaces(username string, requiredRule v1.PolicyRule) (allNamespace bool, namespaces []string, err error) { + + clusterRoles, err := GetClusterRoles(username) + + if err != nil { + return false, nil, err + } + + clusterRules := make([]v1.PolicyRule, 0) + for _, role := range clusterRoles { + clusterRules = append(clusterRules, role.Rules...) + } + + if requiredRule.Size() == 0 { + if ruleValidate(clusterRules, v1.PolicyRule{ + Verbs: []string{"get", "list"}, + APIGroups: []string{""}, + Resources: []string{"namespaces"}, + }) { + return true, nil, nil + } + } else if ruleValidate(clusterRules, requiredRule) { + return true, nil, nil + } + + roles, err := GetRoles(username) + + if err != nil { + return false, nil, err + } + + rulesMapping := make(map[string][]v1.PolicyRule, 0) + + for _, role := range roles { + rules := rulesMapping[role.Namespace] + if rules == nil { + rules = make([]v1.PolicyRule, 0) + } + rules = append(rules, role.Rules...) + rulesMapping[role.Namespace] = rules + } + + namespaces = make([]string, 0) + + for namespace, rules := range rulesMapping { + if requiredRule.Size() == 0 || ruleValidate(rules, requiredRule) { + namespaces = append(namespaces, namespace) + } + } + + return false, namespaces, nil +} + func DeleteRoleBindings(username string) error { k8s := client.NewK8sClient() @@ -208,3 +263,61 @@ func GetClusterRoles(username string) ([]v1.ClusterRole, error) { return roles, nil } + +func ruleValidate(rules []v1.PolicyRule, rule v1.PolicyRule) bool { + + for _, apiGroup := range rule.APIGroups { + if len(rule.NonResourceURLs) == 0 { + for _, resource := range rule.Resources { + + //if len(Rule.ResourceNames) == 0 { + + for _, verb := range rule.Verbs { + if !verbValidate(rules, apiGroup, "", resource, "", verb) { + return false + } + } + + //} else { + // for _, resourceName := range Rule.ResourceNames { + // for _, verb := range Rule.Verbs { + // if !verbValidate(rules, apiGroup, "", resource, resourceName, verb) { + // return false + // } + // } + // } + //} + } + } else { + for _, nonResourceURL := range rule.NonResourceURLs { + for _, verb := range rule.Verbs { + if !verbValidate(rules, apiGroup, nonResourceURL, "", "", verb) { + return false + } + } + } + } + } + return true +} + +func verbValidate(rules []v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, resourceName string, verb string) bool { + for _, rule := range rules { + if slice.ContainsString(rule.APIGroups, apiGroup, nil) || slice.ContainsString(rule.APIGroups, v1.APIGroupAll, nil) { + if slice.ContainsString(rule.Verbs, verb, nil) || slice.ContainsString(rule.Verbs, v1.VerbAll, nil) { + if nonResourceURL == "" { + if slice.ContainsString(rule.Resources, resource, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { + if resourceName == "" { + return true + } else if slice.ContainsString(rule.ResourceNames, resourceName, nil) || slice.ContainsString(rule.Resources, v1.ResourceAll, nil) { + return true + } + } + } else if slice.ContainsString(rule.NonResourceURLs, nonResourceURL, nil) || slice.ContainsString(rule.NonResourceURLs, v1.NonResourceAll, nil) { + return true + } + } + } + } + return false +} diff --git a/pkg/apis/v1alpha/iam/policy.go b/pkg/models/iam/policy.go similarity index 91% rename from pkg/apis/v1alpha/iam/policy.go rename to pkg/models/iam/policy.go index 54e603104..ffe1b04ca 100644 --- a/pkg/apis/v1alpha/iam/policy.go +++ b/pkg/models/iam/policy.go @@ -21,7 +21,6 @@ import ( "encoding/json" "io/ioutil" - "github.com/golang/glog" "k8s.io/api/rbac/v1" ) @@ -30,59 +29,47 @@ const ( clusterRulesConfigPath = "/etc/kubesphere/rules/clusterrules.json" ) -type roleList struct { - ClusterRoles []v1.ClusterRole `json:"clusterRoles" protobuf:"bytes,2,rep,name=clusterRoles"` - Roles []v1.Role `json:"roles" protobuf:"bytes,2,rep,name=roles"` -} - -type action struct { +type Action struct { Name string `json:"name"` Rules []v1.PolicyRule `json:"rules"` } -type rule struct { +type Rule struct { Name string `json:"name"` - Actions []action `json:"actions"` -} - -type userRuleList struct { - ClusterRules []rule `json:"clusterRules"` - Rules map[string][]rule `json:"rules"` + Actions []Action `json:"actions"` } func init() { rulesConfig, err := ioutil.ReadFile(rulesConfigPath) if err == nil { - config := &[]rule{} + config := &[]Rule{} json.Unmarshal(rulesConfig, config) if len(*config) > 0 { - roleRuleGroup = *config - glog.Info("rules config load success") + RoleRuleGroup = *config } } clusterRulesConfig, err := ioutil.ReadFile(clusterRulesConfigPath) if err == nil { - config := &[]rule{} + config := &[]Rule{} json.Unmarshal(clusterRulesConfig, config) if len(*config) > 0 { - clusterRoleRuleGroup = *config - glog.Info("cluster rules config load success") + ClusterRoleRuleGroup = *config } } } var ( - clusterRoleRuleGroup = []rule{projects, users, roles, images, + ClusterRoleRuleGroup = []Rule{projects, users, roles, images, volumes, storageclasses, nodes, appCatalog, apps, components, deployments, statefulsets, daemonsets, pods, services, routes} - roleRuleGroup = []rule{project, deployments, statefulsets, daemonsets, pods, + RoleRuleGroup = []Rule{project, deployments, statefulsets, daemonsets, pods, services, routes, volumes} - components = rule{ + components = Rule{ Name: "components", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -95,9 +82,9 @@ var ( }, } - projects = rule{ + projects = Rule{ Name: "projects", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -155,9 +142,9 @@ var ( }, } - project = rule{ + project = Rule{ Name: "projects", - Actions: []action{ + Actions: []Action{ {Name: "members", Rules: []v1.PolicyRule{ { @@ -196,9 +183,9 @@ var ( }, }, } - users = rule{ + users = Rule{ Name: "users", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -253,9 +240,9 @@ var ( }, } - roles = rule{ + roles = Rule{ Name: "roles", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -296,9 +283,9 @@ var ( }, } - nodes = rule{ + nodes = Rule{ Name: "nodes", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -329,9 +316,9 @@ var ( }, } - volumes = rule{ + volumes = Rule{ Name: "volumes", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -376,9 +363,9 @@ var ( }, } - storageclasses = rule{ + storageclasses = Rule{ Name: "storageclasses", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -418,9 +405,9 @@ var ( }, } - images = rule{ + images = Rule{ Name: "images", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -473,9 +460,9 @@ var ( }, } - appCatalog = rule{ + appCatalog = Rule{ Name: "app_catalog", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -515,9 +502,9 @@ var ( }, } - apps = rule{ + apps = Rule{ Name: "apps", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -530,9 +517,9 @@ var ( }, } - statefulsets = rule{ + statefulsets = Rule{ Name: "statefulsets", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -591,9 +578,9 @@ var ( }, } - daemonsets = rule{ + daemonsets = Rule{ Name: "daemonsets", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -643,9 +630,9 @@ var ( }, } - services = rule{ + services = Rule{ Name: "services", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -691,9 +678,9 @@ var ( }, } - routes = rule{ + routes = Rule{ Name: "routes", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -701,11 +688,6 @@ var ( APIGroups: []string{"extensions"}, Resources: []string{"ingresses"}, }, - { - Verbs: []string{"list"}, - APIGroups: []string{""}, - Resources: []string{"namespaces"}, - }, }, }, {Name: "create", @@ -738,9 +720,9 @@ var ( }, } - deployments = rule{ + deployments = Rule{ Name: "deployments", - Actions: []action{ + Actions: []Action{ {Name: "view", Rules: []v1.PolicyRule{ { @@ -801,9 +783,9 @@ var ( }, } - pods = rule{ + pods = Rule{ Name: "pods", - Actions: []action{ + Actions: []Action{ {Name: "terminal", Rules: []v1.PolicyRule{ { diff --git a/pkg/models/iam/tools.go b/pkg/models/iam/tools.go new file mode 100644 index 000000000..79b619c04 --- /dev/null +++ b/pkg/models/iam/tools.go @@ -0,0 +1,161 @@ +/* + Copyright 2018 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 iam + +import ( + "k8s.io/api/rbac/v1" +) + +func GetUserRules(username string) (map[string][]Rule, error) { + + items := make(map[string][]Rule, 0) + userRoles, err := GetRoles(username) + + if err != nil { + return nil, err + } + + rulesMapping := make(map[string][]v1.PolicyRule, 0) + + for _, role := range userRoles { + rules := rulesMapping[role.Namespace] + if rules == nil { + rules = make([]v1.PolicyRule, 0) + } + rules = append(rules, role.Rules...) + rulesMapping[role.Namespace] = rules + } + + for namespace, policyRules := range rulesMapping { + rules := convertToRules(policyRules) + if len(rules) > 0 { + items[namespace] = rules + } + } + + return items, nil +} + +func convertToRules(policyRules []v1.PolicyRule) []Rule { + rules := make([]Rule, 0) + + for i := 0; i < (len(RoleRuleGroup)); i++ { + rule := Rule{Name: RoleRuleGroup[i].Name} + rule.Actions = make([]Action, 0) + for j := 0; j < (len(RoleRuleGroup[i].Actions)); j++ { + if actionValidate(policyRules, RoleRuleGroup[i].Actions[j]) { + rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j]) + } + } + + if len(rule.Actions) > 0 { + rules = append(rules, rule) + } + } + + return rules +} + +func GetUserClusterRules(username string) ([]Rule, error) { + + rules := make([]Rule, 0) + + clusterRoles, err := GetClusterRoles(username) + + if err != nil { + return nil, err + } + + clusterRules := make([]v1.PolicyRule, 0) + + for _, role := range clusterRoles { + clusterRules = append(clusterRules, role.Rules...) + } + + for i := 0; i < (len(ClusterRoleRuleGroup)); i++ { + rule := Rule{Name: ClusterRoleRuleGroup[i].Name} + rule.Actions = make([]Action, 0) + for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ { + if actionValidate(clusterRules, ClusterRoleRuleGroup[i].Actions[j]) { + rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j]) + } + } + if len(rule.Actions) > 0 { + rules = append(rules, rule) + } + } + + return rules, nil +} + +func GetClusterRoleRules(name string) ([]Rule, error) { + + clusterRole, err := GetClusterRole(name) + + if err != nil { + return nil, err + } + + rules := make([]Rule, 0) + + for i := 0; i < len(ClusterRoleRuleGroup); i++ { + rule := Rule{Name: ClusterRoleRuleGroup[i].Name} + rule.Actions = make([]Action, 0) + for j := 0; j < (len(ClusterRoleRuleGroup[i].Actions)); j++ { + if actionValidate(clusterRole.Rules, ClusterRoleRuleGroup[i].Actions[j]) { + rule.Actions = append(rule.Actions, ClusterRoleRuleGroup[i].Actions[j]) + } + } + if len(rule.Actions) > 0 { + rules = append(rules, rule) + } + } + + return rules, nil +} + +func GetRoleRules(namespace string, name string) ([]Rule, error) { + role, err := GetRole(namespace, name) + if err != nil { + return nil, err + } + + rules := make([]Rule, 0) + for i := 0; i < len(RoleRuleGroup); i++ { + rule := Rule{Name: RoleRuleGroup[i].Name} + rule.Actions = make([]Action, 0) + for j := 0; j < len(RoleRuleGroup[i].Actions); j++ { + if actionValidate(role.Rules, RoleRuleGroup[i].Actions[j]) { + rule.Actions = append(rule.Actions, RoleRuleGroup[i].Actions[j]) + } + } + if len(rule.Actions) > 0 { + rules = append(rules, rule) + } + } + return rules, nil +} + +func actionValidate(rules []v1.PolicyRule, action Action) bool { + for _, rule := range action.Rules { + if !ruleValidate(rules, rule) { + return false + } + } + return true +} diff --git a/pkg/models/kubeconfig.go b/pkg/models/kubeconfig.go index 43bc464e0..068155783 100644 --- a/pkg/models/kubeconfig.go +++ b/pkg/models/kubeconfig.go @@ -246,7 +246,7 @@ func CreateKubeConfig(user string) error { data := map[string]string{"config": string(config)} var configmap = v1.ConfigMap{TypeMeta: metav1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Name: user}, Data: data} - _, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNameSpace).Create(&configmap) + _, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configmap) if err != nil { glog.Errorln(err) return err @@ -257,7 +257,7 @@ func CreateKubeConfig(user string) error { func GetKubeConfig(user string) (string, error) { k8sClient := client.NewK8sClient() - configmap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNameSpace).Get(user, metav1.GetOptions{}) + configmap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(user, metav1.GetOptions{}) if err != nil { glog.Errorln(err) return "", err @@ -267,7 +267,7 @@ func GetKubeConfig(user string) (string, error) { func DelKubeConfig(user string) error { k8sClient := client.NewK8sClient() - err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNameSpace).Delete(user, &metav1.DeleteOptions{}) + err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(user, &metav1.DeleteOptions{}) if err != nil { glog.Errorln(err) return err diff --git a/pkg/models/kubectl.go b/pkg/models/kubectl.go index 1ee744455..8b58dc584 100644 --- a/pkg/models/kubectl.go +++ b/pkg/models/kubectl.go @@ -31,7 +31,7 @@ import ( "kubesphere.io/kubesphere/pkg/options" ) -const namespace = constants.KubeSphereControlNameSpace +const namespace = constants.KubeSphereControlNamespace type kubectlPodInfo struct { Namespace string `json:"namespace"` diff --git a/pkg/models/routes.go b/pkg/models/routes.go index 2f7625b12..4d31ac9ce 100644 --- a/pkg/models/routes.go +++ b/pkg/models/routes.go @@ -31,6 +31,7 @@ import ( "kubesphere.io/kubesphere/pkg/client" "kubesphere.io/kubesphere/pkg/constants" + "kubesphere.io/kubesphere/pkg/models/iam" ) func GetAllRouters() ([]coreV1.Service, error) { @@ -71,37 +72,30 @@ func inArray(val interface{}, array interface{}) (exists bool) { func GetAllRoutersOfUser(username string) ([]coreV1.Service, error) { routers := make([]coreV1.Service, 0) - clusterRoles, err := GetClusterRoles(username) + + allNamespace, namespaces, err := iam.GetUserNamespaces(username, v1.PolicyRule{ + Verbs: []string{"get", "list"}, + APIGroups: []string{"extensions"}, + Resources: []string{"ingresses"}, + }) // return by cluster role if err != nil { glog.Error(err) return routers, err - } else { - for _, clusterRole := range clusterRoles { - for _, rulePolicy := range clusterRole.Rules { - if (inArray(v1.VerbAll, rulePolicy.Verbs) || inArray("view", rulePolicy.Verbs)) && - (inArray(v1.ResourceAll, rulePolicy.Resources) || inArray("namespaces", rulePolicy.Resources)) { - return GetAllRouters() - } - } - } } - // return by role - roles, err := GetRoles(username) - if err != nil { - glog.Error(err) - return routers, err - } else { - for _, projectRole := range roles { - router, err := GetRouter(projectRole.Namespace) - if err != nil { - glog.Error(err) - return routers, err - } else if router != nil { - routers = append(routers, *router) - } + if allNamespace { + return GetAllRouters() + } + + for _, namespace := range namespaces { + router, err := GetRouter(namespace) + if err != nil { + glog.Error(err) + return routers, err + } else if router != nil { + routers = append(routers, *router) } } diff --git a/pkg/version/version.go b/pkg/version/version.go index f2d9db125..628543a83 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -35,7 +35,7 @@ var ( func PrintAndExitIfRequested() { if *versionFlag { - fmt.Printf("Kubesphere %s\n", constants.APIVERSION) + fmt.Printf("Kubesphere %s\n", constants.APIVersion) os.Exit(0) } }