Files
kubesphere/pkg/models/iam/am.go
hongming 4144404b0b use go 1.12
Signed-off-by: hongming <talonwan@yunify.com>
2019-03-15 18:24:00 +08:00

824 lines
21 KiB
Go

/*
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 iam
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"log"
"net/http"
"regexp"
"strings"
"github.com/go-ldap/ldap"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/models/iam/policy"
)
func GetNamespaces(username string) ([]*corev1.Namespace, error) {
roles, err := GetRoles(username, "")
if err != nil {
return nil, err
}
namespaces := make([]*corev1.Namespace, 0)
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
for _, role := range roles {
namespace, err := namespaceLister.Get(role.Name)
if err != nil {
return nil, err
}
namespaces = append(namespaces, namespace)
}
return namespaces, nil
}
func GetNamespacesByWorkspace(workspace string) ([]*corev1.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
return namespaceLister.List(labels.SelectorFromSet(labels.Set{"kubesphere.io/workspace": workspace}))
}
func GetDevopsRole(projectId string, username string) (string, error) {
//Hard fix
if username == "admin" {
return "owner", nil
}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/api/v1alpha/projects/%s/members", constants.DevopsAPIServer, projectId), nil)
if err != nil {
return "", err
}
req.Header.Set(constants.UserNameHeader, username)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
if resp.StatusCode > 200 {
return "", errors.New(string(data))
}
var result []map[string]string
err = json.Unmarshal(data, &result)
if err != nil {
return "", err
}
for _, item := range result {
if item["username"] == username {
return item["role"], nil
}
}
return "", nil
}
func GetNamespace(namespaceName string) (*corev1.Namespace, error) {
namespaceLister := informers.SharedInformerFactory().Core().V1().Namespaces().Lister()
return namespaceLister.Get(namespaceName)
}
func GetRoles(username string, namespace string) ([]*v1.Role, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleLister := informers.SharedInformerFactory().Rbac().V1().Roles().Lister()
roleBindings, err := roleBindingLister.RoleBindings(namespace).List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]*v1.Role, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
if roleBinding.RoleRef.Kind == ClusterRoleKind {
clusterRole, err := clusterRoleLister.Get(roleBinding.RoleRef.Name)
if err == nil {
var role = v1.Role{TypeMeta: (*clusterRole).TypeMeta, ObjectMeta: (*clusterRole).ObjectMeta, Rules: (*clusterRole).Rules}
role.Namespace = roleBinding.Namespace
roles = append(roles, &role)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
} else {
if subject.Kind == v1.UserKind && subject.Name == username {
rule, err := roleLister.Roles(roleBinding.Namespace).Get(roleBinding.RoleRef.Name)
if err == nil {
roles = append(roles, rule)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
}
}
}
}
}
return roles, nil
}
func GetClusterRoles(username string) ([]*v1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
roles := make([]*v1.ClusterRole, 0)
for _, rb := range clusterRoleBindings {
if rb.RoleRef.Kind == ClusterRoleKind {
for _, subject := range rb.Subjects {
if subject.Kind == v1.UserKind && subject.Name == username {
role, err := clusterRoleLister.Get(rb.RoleRef.Name)
role = role.DeepCopy()
if err == nil {
if role.Annotations == nil {
role.Annotations = make(map[string]string, 0)
}
role.Annotations["rbac.authorization.k8s.io/clusterrolebinding"] = rb.Name
if rb.Annotations != nil &&
rb.Annotations["rbac.authorization.k8s.io/clusterrole"] == rb.RoleRef.Name {
role.Annotations["rbac.authorization.k8s.io/clusterrole"] = "true"
}
roles = append(roles, role)
break
} else if apierrors.IsNotFound(err) {
log.Println(err)
break
} else {
return nil, err
}
}
}
}
}
return roles, nil
}
func GetRoleBindings(namespace string, roleName string) ([]*v1.RoleBinding, error) {
roleBindingLister := informers.SharedInformerFactory().Rbac().V1().RoleBindings().Lister()
roleBindingList, err := roleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.RoleBinding, 0)
for _, roleBinding := range roleBindingList {
if roleName == "" {
items = append(items, roleBinding)
} else if roleBinding.RoleRef.Name == roleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func GetClusterRoleBindings(clusterRoleName string) ([]*v1.ClusterRoleBinding, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
roleBindingList, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return nil, err
}
items := make([]*v1.ClusterRoleBinding, 0)
for _, roleBinding := range roleBindingList {
if roleBinding.RoleRef.Name == clusterRoleName {
items = append(items, roleBinding)
}
}
return items, nil
}
func ClusterRoleUsers(clusterRoleName string) ([]*models.User, error) {
roleBindings, err := GetClusterRoleBindings(clusterRoleName)
if err != nil {
return nil, err
}
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !strings.HasPrefix(subject.Name, "system") &&
!slice.ContainsString(names, subject.Name, nil) {
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
users = append(users, user)
}
}
}
return users, nil
}
func RoleUsers(namespace string, roleName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespace, roleName)
if err != nil {
return nil, err
}
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind &&
!strings.HasPrefix(subject.Name, "system") &&
!slice.ContainsString(names, subject.Name, nil) {
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
users = append(users, user)
}
}
}
return users, nil
}
func NamespaceUsers(namespaceName string) ([]*models.User, error) {
roleBindings, err := GetRoleBindings(namespaceName, "")
if err != nil {
return nil, err
}
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
names := make([]string, 0)
users := make([]*models.User, 0)
for _, roleBinding := range roleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind &&
!slice.ContainsString(names, subject.Name, nil) &&
!strings.HasPrefix(subject.Name, "system") {
if roleBinding.Name == "viewer" {
continue
}
if roleBinding.Name == "admin" {
continue
}
names = append(names, subject.Name)
user, err := UserDetail(subject.Name, conn)
if ldap.IsErrorWithCode(err, 32) {
continue
}
if err != nil {
return nil, err
}
user.Role = roleBinding.RoleRef.Name
user.RoleBinding = roleBinding.Name
users = append(users, user)
}
}
}
return users, nil
}
func GetWorkspaceRoles(clusterRoles []*v1.ClusterRole) map[string]string {
workspaceRoles := make(map[string]string, 0)
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
workspaceRoles[groups[1]] = groups[2]
}
}
return workspaceRoles
}
func GetWorkspaceRole(clusterRoles []*v1.ClusterRole, workspace string) string {
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
if groups[1] == workspace {
return groups[2]
}
}
}
return ""
}
func GetWorkspaceSimpleRules(clusterRoles []*v1.ClusterRole, workspace string) map[string][]models.SimpleRule {
workspaceRules := make(map[string][]models.SimpleRule, 0)
clusterSimpleRules := make([]models.SimpleRule, 0)
clusterRules := make([]v1.PolicyRule, 0)
for _, clusterRole := range clusterRoles {
clusterRules = append(clusterRules, clusterRole.Rules...)
}
for i := 0; i < len(policy.WorkspaceRoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.WorkspaceRoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < (len(policy.WorkspaceRoleRuleMapping[i].Actions)); j++ {
if RulesMatchesAction(clusterRules, policy.WorkspaceRoleRuleMapping[i].Actions[j]) {
rule.Actions = append(rule.Actions, policy.WorkspaceRoleRuleMapping[i].Actions[j].Name)
}
}
if len(rule.Actions) > 0 {
clusterSimpleRules = append(clusterSimpleRules, rule)
}
}
if len(clusterRules) > 0 {
workspaceRules["*"] = clusterSimpleRules
}
for _, v := range clusterRoles {
if groups := regexp.MustCompile(fmt.Sprintf(`^system:(\S+):(%s)$`, strings.Join(constants.WorkSpaceRoles, "|"))).FindStringSubmatch(v.Name); len(groups) == 3 {
if workspace != "" && groups[1] != workspace {
continue
}
policyRules := make([]v1.PolicyRule, 0)
for _, rule := range v.Rules {
rule.ResourceNames = nil
policyRules = append(policyRules, rule)
}
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.WorkspaceRoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.WorkspaceRoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < (len(policy.WorkspaceRoleRuleMapping[i].Actions)); j++ {
action := policy.WorkspaceRoleRuleMapping[i].Actions[j]
if RulesMatchesAction(policyRules, action) {
rule.Actions = append(rule.Actions, action.Name)
}
}
if len(rule.Actions) > 0 {
rules = append(rules, rule)
}
}
workspaceRules[groups[1]] = merge(rules, clusterSimpleRules)
}
}
return workspaceRules
}
func merge(clusterRules, rules []models.SimpleRule) []models.SimpleRule {
for _, clusterRule := range clusterRules {
exist := false
for i := 0; i < len(rules); i++ {
if rules[i].Name == clusterRule.Name {
exist = true
for _, action := range clusterRule.Actions {
if !slice.ContainsString(rules[i].Actions, action, nil) {
rules[i].Actions = append(rules[i].Actions, action)
}
}
}
}
if !exist {
rules = append(rules, clusterRule)
}
}
return rules
}
// Convert cluster roles to rules
func GetClusterRoleSimpleRules(clusterRoles []*v1.ClusterRole) ([]models.SimpleRule, error) {
clusterRules := make([]v1.PolicyRule, 0)
for _, v := range clusterRoles {
clusterRules = append(clusterRules, v.Rules...)
}
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.ClusterRoleRuleMapping); i++ {
validActions := make([]string, 0)
for j := 0; j < (len(policy.ClusterRoleRuleMapping[i].Actions)); j++ {
if RulesMatchesAction(clusterRules, policy.ClusterRoleRuleMapping[i].Actions[j]) {
validActions = append(validActions, policy.ClusterRoleRuleMapping[i].Actions[j].Name)
}
}
if len(validActions) > 0 {
rules = append(rules, models.SimpleRule{Name: policy.ClusterRoleRuleMapping[i].Name, Actions: validActions})
}
}
return rules, nil
}
// Convert roles to rules
func GetRoleSimpleRules(roles []*v1.Role, namespace string) (map[string][]models.SimpleRule, error) {
rulesMapping := make(map[string][]models.SimpleRule, 0)
policyRulesMapping := make(map[string][]v1.PolicyRule, 0)
for _, v := range roles {
if namespace != "" && v.Namespace != namespace {
continue
}
policyRules := policyRulesMapping[v.Namespace]
if policyRules == nil {
policyRules = make([]v1.PolicyRule, 0)
}
policyRules = append(policyRules, v.Rules...)
policyRulesMapping[v.Namespace] = policyRules
}
for namespace, policyRules := range policyRulesMapping {
rules := make([]models.SimpleRule, 0)
for i := 0; i < len(policy.RoleRuleMapping); i++ {
rule := models.SimpleRule{Name: policy.RoleRuleMapping[i].Name}
rule.Actions = make([]string, 0)
for j := 0; j < len(policy.RoleRuleMapping[i].Actions); j++ {
if RulesMatchesAction(policyRules, policy.RoleRuleMapping[i].Actions[j]) {
rule.Actions = append(rule.Actions, policy.RoleRuleMapping[i].Actions[j].Name)
}
}
if len(rule.Actions) > 0 {
rules = append(rules, rule)
}
}
rulesMapping[namespace] = rules
}
return rulesMapping, nil
}
func CreateClusterRoleBinding(username string, clusterRoleName string) error {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
_, err := clusterRoleLister.Get(clusterRoleName)
if err != nil {
return err
}
clusterRoles, err := GetClusterRoles(username)
if err != nil {
return err
}
for _, clusterRole := range clusterRoles {
if clusterRole.Annotations["rbac.authorization.k8s.io/clusterrole"] == "true" {
if clusterRole.Name == clusterRoleName {
return nil
}
clusterRoleBindingName := clusterRole.Annotations["rbac.authorization.k8s.io/clusterrolebinding"]
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBinding, err := clusterRoleBindingLister.Get(clusterRoleBindingName)
if err != nil {
return err
}
for i, v := range clusterRoleBinding.Subjects {
if v.Kind == v1.UserKind && v.Name == username {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects[:i], clusterRoleBinding.Subjects[i+1:]...)
break
}
}
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding)
if err != nil {
return err
}
break
}
}
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
if err != nil {
return err
}
var clusterRoleBinding *v1.ClusterRoleBinding
for _, roleBinding := range clusterRoleBindings {
if roleBinding.Annotations != nil && roleBinding.Annotations["rbac.authorization.k8s.io/clusterrole"] == clusterRoleName &&
roleBinding.RoleRef.Name == clusterRoleName {
clusterRoleBinding = roleBinding
break
}
}
if clusterRoleBinding != nil {
clusterRoleBinding.Subjects = append(clusterRoleBinding.Subjects, v1.Subject{Kind: v1.UserKind, Name: username})
_, err := k8s.Client().RbacV1().ClusterRoleBindings().Update(clusterRoleBinding)
if err != nil {
return err
}
} else {
clusterRoleBinding = new(v1.ClusterRoleBinding)
clusterRoleBinding.Annotations = map[string]string{"rbac.authorization.k8s.io/clusterrole": clusterRoleName}
clusterRoleBinding.Name = clusterRoleName
clusterRoleBinding.RoleRef = v1.RoleRef{Name: clusterRoleName, Kind: ClusterRoleKind}
clusterRoleBinding.Subjects = []v1.Subject{{Kind: v1.UserKind, Name: username}}
_, err = k8s.Client().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
if err != nil {
return err
}
}
return nil
}
func GetRole(namespace string, roleName string) (*v1.Role, error) {
return informers.SharedInformerFactory().Rbac().V1().Roles().Lister().Roles(namespace).Get(roleName)
}
func GetClusterRole(clusterRoleName string) (*v1.ClusterRole, error) {
clusterRoleLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoles().Lister()
return clusterRoleLister.Get(clusterRoleName)
}
func RulesMatchesAction(rules []v1.PolicyRule, action models.Action) bool {
for _, required := range action.Rules {
if !rulesMatchesRequired(rules, required) {
return false
}
}
return true
}
func rulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}
func ruleMatchesRequired(rule v1.PolicyRule, required v1.PolicyRule) bool {
if len(required.NonResourceURLs) == 0 {
for _, apiGroup := range required.APIGroups {
for _, resource := range required.Resources {
resources := strings.Split(resource, "/")
resource = resources[0]
var subsource string
if len(resources) > 1 {
subsource = resources[1]
}
if len(required.ResourceNames) == 0 {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, "", verb) {
return false
}
}
} else {
for _, resourceName := range required.ResourceNames {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, "", resource, subsource, resourceName, verb) {
return false
}
}
}
}
}
}
} else {
for _, apiGroup := range required.APIGroups {
for _, nonResourceURL := range required.NonResourceURLs {
for _, verb := range required.Verbs {
if !ruleMatchesRequest(rule, apiGroup, nonResourceURL, "", "", "", verb) {
return false
}
}
}
}
}
return true
}
func ruleMatchesResources(rule v1.PolicyRule, apiGroup string, resource string, subresource string, resourceName string) bool {
if resource == "" {
return false
}
if !hasString(rule.APIGroups, apiGroup) && !hasString(rule.APIGroups, v1.ResourceAll) {
return false
}
if len(rule.ResourceNames) > 0 && !hasString(rule.ResourceNames, resourceName) {
return false
}
combinedResource := resource
if subresource != "" {
combinedResource = combinedResource + "/" + subresource
}
for _, res := range rule.Resources {
// match "*"
if res == v1.ResourceAll || res == combinedResource {
return true
}
// match "*/subresource"
if len(subresource) > 0 && strings.HasPrefix(res, "*/") && subresource == strings.TrimLeft(res, "*/") {
return true
}
// match "resource/*"
if strings.HasSuffix(res, "/*") && resource == strings.TrimRight(res, "/*") {
return true
}
}
return false
}
func ruleMatchesRequest(rule v1.PolicyRule, apiGroup string, nonResourceURL string, resource string, subresource string, resourceName string, verb string) bool {
if !hasString(rule.Verbs, verb) && !hasString(rule.Verbs, v1.VerbAll) {
return false
}
if nonResourceURL == "" {
return ruleMatchesResources(rule, apiGroup, resource, subresource, resourceName)
} else {
return ruleMatchesNonResource(rule, nonResourceURL)
}
}
func ruleMatchesNonResource(rule v1.PolicyRule, nonResourceURL string) bool {
if nonResourceURL == "" {
return false
}
for _, spec := range rule.NonResourceURLs {
if pathMatches(nonResourceURL, spec) {
return true
}
}
return false
}
func pathMatches(path, spec string) bool {
// Allow wildcard match
if spec == "*" {
return true
}
// Allow exact match
if spec == path {
return true
}
// Allow a trailing * subpath match
if strings.HasSuffix(spec, "*") && strings.HasPrefix(path, strings.TrimRight(spec, "*")) {
return true
}
return false
}
func hasString(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}