refine tenant api

Signed-off-by: hongming <talonwan@yunify.com>
This commit is contained in:
hongming
2019-04-01 02:59:19 +08:00
parent 744bd053e3
commit 93ad572e19
202 changed files with 13517 additions and 7951 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,54 +0,0 @@
/*
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 "sync"
type Counter struct {
value int
m *sync.Mutex
}
func NewCounter(value int) Counter {
c := Counter{}
c.m = &sync.Mutex{}
c.Set(value)
return c
}
func (c *Counter) Set(value int) {
c.m.Lock()
c.value = value
c.m.Unlock()
}
func (c *Counter) Add(value int) {
c.m.Lock()
c.value += value
c.m.Unlock()
}
func (c *Counter) Sub(value int) {
c.m.Lock()
c.value -= value
c.m.Unlock()
}
func (c *Counter) Get() int {
return c.value
}

View File

@@ -1,188 +0,0 @@
/*
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 (
"fmt"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
"k8s.io/api/rbac/v1"
"k8s.io/kubernetes/pkg/util/slice"
"kubesphere.io/kubesphere/pkg/models"
)
const ClusterRoleKind = "ClusterRole"
// Get user list based on workspace role
func WorkspaceRoleUsers(workspace string, roleName string) ([]models.User, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
workspaceRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, roleName))
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, subject := range workspaceRoleBinding.Subjects {
if subject.Kind == v1.UserKind {
names = append(names, subject.Name)
}
}
users, err := GetUsers(names)
if err != nil {
return nil, err
}
for i := 0; i < len(users); i++ {
users[i].WorkspaceRole = roleName
}
return users, nil
}
func GetUsers(names []string) ([]models.User, error) {
var users []models.User
if names == nil || len(names) == 0 {
return make([]models.User, 0), nil
}
conn, err := ldap.Client()
if err != nil {
return nil, err
}
for _, name := range names {
user, err := UserDetail(name, conn)
if err != nil {
return nil, err
}
users = append(users, *user)
}
return users, nil
}
func GetUser(name string) (*models.User, error) {
conn, err := ldap.Client()
if err != nil {
return nil, err
}
user, err := UserDetail(name, conn)
if err != nil {
return nil, err
}
return user, nil
}
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 RulesMatchesRequired(clusterRules, v1.PolicyRule{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
}) {
return true, nil, nil
}
} else {
if RulesMatchesRequired(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 || RulesMatchesRequired(rules, requiredRule) {
namespaces = append(namespaces, namespace)
}
}
return false, namespaces, nil
}
func GetWorkspaceUsers(workspace string, workspaceRole string) ([]string, error) {
clusterRoleBindingLister := informers.SharedInformerFactory().Rbac().V1().ClusterRoleBindings().Lister()
clusterRoleBinding, err := clusterRoleBindingLister.Get(fmt.Sprintf("system:%s:%s", workspace, workspaceRole))
if err != nil {
return nil, err
}
users := make([]string, 0)
for _, s := range clusterRoleBinding.Subjects {
if s.Kind == v1.UserKind && !slice.ContainsString(users, s.Name, nil) {
users = append(users, s.Name)
}
}
return users, nil
}
func RulesMatchesRequired(rules []v1.PolicyRule, required v1.PolicyRule) bool {
for _, rule := range rules {
if ruleMatchesRequired(rule, required) {
return true
}
}
return false
}

View File

@@ -22,9 +22,12 @@ import (
"fmt"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/params"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/redis"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"regexp"
"sort"
"strconv"
"strings"
"time"
@@ -38,11 +41,10 @@ import (
ldapclient "kubesphere.io/kubesphere/pkg/simple/client/ldap"
"kubesphere.io/kubesphere/pkg/models"
jwtutils "kubesphere.io/kubesphere/pkg/utils/jwt"
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
)
var (
counter Counter
adminEmail string
adminPassword string
tokenExpireTime time.Duration
@@ -82,7 +84,7 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
nil,
)
groups, err := conn.Search(groupSearchRequest)
_, err := conn.Search(groupSearchRequest)
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
err = createGroupsBaseDN(conn)
@@ -95,14 +97,6 @@ func checkAndCreateDefaultGroup(conn ldap.Client) error {
return fmt.Errorf("iam database init failed: %s\n", err)
}
if groups == nil || len(groups.Entries) == 0 {
_, err = CreateGroup(models.Group{Path: constants.SystemWorkspace, Name: constants.SystemWorkspace, Creator: constants.AdminUserName, Description: "system workspace"})
if err != nil {
return fmt.Errorf("system-workspace create failed: %s\n", err)
}
}
return nil
}
@@ -130,13 +124,10 @@ func checkAndCreateDefaultUser(conn ldap.Client) error {
}
if users == nil || len(users.Entries) == 0 {
counter = NewCounter(0)
err := CreateUser(models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
_, err := CreateUser(&models.User{Username: constants.AdminUserName, Email: adminEmail, Password: adminPassword, Description: "Administrator account that was always created by default."})
if err != nil {
return fmt.Errorf("admin create failed: %s\n", err)
}
} else {
counter = NewCounter(len(users.Entries))
}
return nil
@@ -164,12 +155,12 @@ func createGroupsBaseDN(conn ldap.Client) error {
}
// User login
func Login(username string, password string, ip string) (string, error) {
func Login(username string, password string, ip string) (*models.Token, error) {
conn, err := ldapclient.Client()
if err != nil {
return "", err
return nil, err
}
defer conn.Close()
@@ -185,11 +176,11 @@ func Login(username string, password string, ip string) (string, error) {
result, err := conn.Search(userSearchRequest)
if err != nil {
return "", err
return nil, err
}
if len(result.Entries) != 1 {
return "", ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
return nil, ldap.NewError(ldap.LDAPResultInvalidCredentials, errors.New("incorrect password"))
}
uid := result.Entries[0].GetAttributeValue("uid")
@@ -200,7 +191,7 @@ func Login(username string, password string, ip string) (string, error) {
err = conn.Bind(dn, password)
if err != nil {
return "", err
return nil, err
}
claims := jwt.MapClaims{}
@@ -209,13 +200,11 @@ func Login(username string, password string, ip string) (string, error) {
claims["username"] = uid
claims["email"] = email
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
uToken, _ := token.SignedString(jwtutils.Secret)
token := jwtutil.MustSigned(claims)
loginLog(uid, ip)
return uToken, nil
return &models.Token{Token: token}, nil
}
func loginLog(uid, ip string) {
@@ -226,99 +215,6 @@ func loginLog(uid, ip string) {
}
}
func UserList(limit int, offset int) (int, []models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return 0, nil, err
}
defer conn.Close()
users := make([]models.User, 0)
pageControl := ldap.NewControlPaging(1000)
entries := make([]*ldap.Entry, 0)
cursor := 0
l1:
for {
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
"(&(objectClass=inetOrgPerson))",
[]string{"uid", "mail", "description"},
[]ldap.Control{pageControl},
)
response, err := conn.Search(userSearchRequest)
if err != nil {
return 0, nil, err
}
for _, entry := range response.Entries {
cursor++
if cursor > offset {
if len(entries) < limit {
entries = append(entries, entry)
} else {
break l1
}
}
}
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
if ctrl, ok := updatedControl.(*ldap.ControlPaging); ctrl != nil && ok && len(ctrl.Cookie) != 0 {
pageControl.SetCookie(ctrl.Cookie)
continue
}
break
}
redisClient := redis.Client()
for _, v := range entries {
uid := v.GetAttributeValue("uid")
email := v.GetAttributeValue("mail")
description := v.GetAttributeValue("description")
user := models.User{Username: uid, Email: email, Description: description}
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
if err != nil {
return 0, nil, err
}
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
}
}
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
if err != nil {
return 0, nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
user.ClusterRules = make([]models.SimpleRule, 0)
users = append(users, user)
}
return counter.Get(), users, nil
}
func LoginLog(username string) ([]string, error) {
redisClient := redis.Client()
@@ -331,48 +227,77 @@ func LoginLog(username string) ([]string, error) {
return data, nil
}
func Search(keyword string, limit int, offset int) (int, []models.User, error) {
func ListUsersByName(names []string) (*models.PageableResponse, error) {
users := make([]*models.User, 0)
for _, name := range names {
if !k8sutil.ContainsUser(users, name) {
user, err := DescribeUser(name)
if err != nil {
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
continue
}
return nil, err
}
users = append(users, user)
}
}
items := make([]interface{}, 0)
for _, u := range users {
items = append(items, u)
}
return &models.PageableResponse{Items: items, TotalCount: len(items)}, nil
}
func ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
conn, err := ldapclient.Client()
if err != nil {
return 0, nil, err
return nil, err
}
defer conn.Close()
users := make([]models.User, 0)
pageControl := ldap.NewControlPaging(80)
entries := make([]*ldap.Entry, 0)
users := make([]models.User, 0)
filter := "(&(objectClass=inetOrgPerson))"
if keyword := conditions.Match["keyword"]; keyword != "" {
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
}
cursor := 0
l1:
for {
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword),
[]string{"uid", "mail", "description"},
filter,
[]string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"},
[]ldap.Control{pageControl},
)
response, err := conn.Search(userSearchRequest)
if err != nil {
return 0, nil, err
return nil, err
}
for _, entry := range response.Entries {
cursor++
if cursor > offset {
if len(entries) < limit {
entries = append(entries, entry)
} else {
break l1
}
}
uid := entry.GetAttributeValue("uid")
email := entry.GetAttributeValue("mail")
description := entry.GetAttributeValue("description")
lang := entry.GetAttributeValue("preferredLanguage")
createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp"))
user := models.User{Username: uid, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
users = append(users, user)
}
updatedControl := ldap.FindControl(response.Controls, ldap.ControlTypePaging)
@@ -384,52 +309,104 @@ l1:
break
}
redisClient := redis.Client()
for _, v := range entries {
uid := v.GetAttributeValue("uid")
email := v.GetAttributeValue("mail")
description := v.GetAttributeValue("description")
user := models.User{Username: uid, Email: email, Description: description}
avatar, err := redisClient.HMGet("kubesphere:users:avatar", uid).Result()
if err != nil {
return 0, nil, err
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
case "username":
fallthrough
case "createTime":
return users[i].CreateTime.Before(users[j].CreateTime)
default:
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
items := make([]interface{}, 0)
for i, user := range users {
if i >= offset && len(items) < limit {
avatar, err := getAvatar(user.Username)
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
lastLoginTime, err := getLastLoginTime(user.Username)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
clusterRole, err := GetUserClusterRole(user.Username)
if err != nil {
return nil, err
}
user.ClusterRole = clusterRole.Name
items = append(items, user)
}
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", uid), -1, -1).Result()
if err != nil {
return 0, nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
user.ClusterRules = make([]models.SimpleRule, 0)
users = append(users, user)
}
return counter.Get(), users, nil
return &models.PageableResponse{Items: items, TotalCount: len(users)}, nil
}
func UserDetail(username string, conn ldap.Client) (*models.User, error) {
func DescribeUser(username string) (*models.User, error) {
user, err := GetUserInfo(username)
if err != nil {
return nil, err
}
groups, err := GetUserGroups(username)
if err != nil {
return nil, err
}
user.Groups = groups
avatar, err := getAvatar(username)
if err != nil {
return nil, err
}
user.AvatarUrl = avatar
lastLoginTime, err := getLastLoginTime(username)
if err != nil {
return nil, err
}
user.LastLoginTime = lastLoginTime
return user, nil
}
// Get user info only included email description & lang
func GetUserInfo(username string) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
userSearchRequest := ldap.NewSearchRequest(
ldapclient.UserSearchBase,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username),
[]string{"mail", "description", "preferredLanguage"},
[]string{"mail", "description", "preferredLanguage", "createTimestamp"},
nil,
)
@@ -446,7 +423,20 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
email := result.Entries[0].GetAttributeValue("mail")
description := result.Entries[0].GetAttributeValue("description")
lang := result.Entries[0].GetAttributeValue("preferredLanguage")
user := models.User{Username: username, Email: email, Description: description, Lang: lang}
createTimestamp, _ := time.Parse("20060102150405Z", result.Entries[0].GetAttributeValue("createTimestamp"))
user := &models.User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
return user, nil
}
func GetUserGroups(username string) ([]string, error) {
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
groupSearchRequest := ldap.NewSearchRequest(
ldapclient.GroupSearchBase,
@@ -456,11 +446,10 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
nil,
)
result, err = conn.Search(groupSearchRequest)
result, err := conn.Search(groupSearchRequest)
if err != nil {
return nil, err
}
groups := make([]string, 0)
@@ -470,41 +459,47 @@ func UserDetail(username string, conn ldap.Client) (*models.User, error) {
groups = append(groups, groupName)
}
user.Groups = groups
return groups, nil
}
redisClient := redis.Client()
avatar, err := redisClient.HMGet("kubesphere:users:avatar", username).Result()
func getLastLoginTime(username string) (string, error) {
lastLogin, err := redis.Client().LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil {
return nil, err
return "", err
}
if len(lastLogin) > 0 {
return strings.Split(lastLogin[0], ",")[0], nil
}
return "", nil
}
func setAvatar(username, avatar string) error {
_, err := redis.Client().HMSet("kubesphere:users:avatar", map[string]interface{}{"username": avatar}).Result()
return err
}
func getAvatar(username string) (string, error) {
avatar, err := redis.Client().HMGet("kubesphere:users:avatar", username).Result()
if err != nil {
return "", err
}
if len(avatar) > 0 {
if url, ok := avatar[0].(string); ok {
user.AvatarUrl = url
return url, nil
}
}
user.Status = 0
lastLogin, err := redisClient.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -1, -1).Result()
if err != nil {
return nil, err
}
if len(lastLogin) > 0 {
user.LastLoginTime = strings.Split(lastLogin[0], ",")[0]
}
return &user, nil
return "", nil
}
func DeleteUser(username string) error {
// bind root DN
conn, err := ldapclient.Client()
if err != nil {
return err
}
@@ -521,13 +516,7 @@ func DeleteUser(username string) error {
err = deleteRoleBindings(username)
if err != nil {
return err
}
counter.Sub(1)
return nil
return err
}
func deleteRoleBindings(username string) error {
@@ -539,7 +528,7 @@ func deleteRoleBindings(username string) error {
}
for _, roleBinding := range roleBindings {
roleBinding = roleBinding.DeepCopy()
length1 := len(roleBinding.Subjects)
for index, subject := range roleBinding.Subjects {
@@ -571,6 +560,7 @@ func deleteRoleBindings(username string) error {
clusterRoleBindings, err := clusterRoleBindingLister.List(labels.Everything())
for _, clusterRoleBinding := range clusterRoleBindings {
clusterRoleBinding = clusterRoleBinding.DeepCopy()
length1 := len(clusterRoleBinding.Subjects)
for index, subject := range clusterRoleBinding.Subjects {
@@ -637,7 +627,7 @@ func UserCreateCheck(check string) (exist bool, err error) {
}
}
func CreateUser(user models.User) error {
func CreateUser(user *models.User) (*models.User, error) {
user.Username = strings.TrimSpace(user.Username)
user.Email = strings.TrimSpace(user.Email)
user.Password = strings.TrimSpace(user.Password)
@@ -646,7 +636,7 @@ func CreateUser(user models.User) error {
conn, err := ldapclient.Client()
if err != nil {
return err
return nil, err
}
defer conn.Close()
@@ -662,17 +652,17 @@ func CreateUser(user models.User) error {
result, err := conn.Search(userSearchRequest)
if err != nil {
return err
return nil, err
}
if len(result.Entries) > 0 {
return ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
return nil, ldap.NewError(ldap.LDAPResultEntryAlreadyExists, fmt.Errorf("username or email already exists"))
}
maxUid, err := getMaxUid(conn)
if err != nil {
return err
return nil, err
}
maxUid += 1
@@ -688,7 +678,7 @@ func CreateUser(user models.User) error {
userCreateRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox
userCreateRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user
if user.Lang != "" {
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang}) // RFC4519/2307: password of user
userCreateRequest.Attribute("preferredLanguage", []string{user.Lang})
}
if user.Description != "" {
userCreateRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information
@@ -697,16 +687,22 @@ func CreateUser(user models.User) error {
err = conn.Add(userCreateRequest)
if err != nil {
return err
return nil, err
}
counter.Add(1)
if user.AvatarUrl != "" {
setAvatar(user.Username, user.AvatarUrl)
}
if user.ClusterRole != "" {
CreateClusterRoleBinding(user.Username, user.ClusterRole)
err := CreateClusterRoleBinding(user.Username, user.ClusterRole)
if err != nil {
return nil, err
}
}
return nil
return DescribeUser(user.Username)
}
func getMaxUid(conn ldap.Client) (int, error) {
@@ -768,11 +764,12 @@ func getMaxGid(conn ldap.Client) (int, error) {
return maxGid, nil
}
func UpdateUser(user models.User) error {
func UpdateUser(user *models.User) (*models.User, error) {
conn, err := ldapclient.Client()
if err != nil {
return err
return nil, err
}
defer conn.Close()
@@ -794,19 +791,27 @@ func UpdateUser(user models.User) error {
userModifyRequest.Replace("userPassword", []string{user.Password})
}
if user.AvatarUrl != "" {
err = setAvatar(user.Username, user.AvatarUrl)
}
if err != nil {
return nil, err
}
err = conn.Modify(userModifyRequest)
if err != nil {
return err
return nil, err
}
err = CreateClusterRoleBinding(user.Username, user.ClusterRole)
if err != nil {
return err
return nil, err
}
return nil
return DescribeUser(user.Username)
}
func DeleteGroup(path string) error {
@@ -829,13 +834,14 @@ func DeleteGroup(path string) error {
return nil
}
func CreateGroup(group models.Group) (*models.Group, error) {
func CreateGroup(group *models.Group) (*models.Group, error) {
// bind root DN
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
defer conn.Close()
maxGid, err := getMaxGid(conn)
@@ -861,7 +867,9 @@ func CreateGroup(group models.Group) (*models.Group, error) {
groupCreateRequest.Attribute("description", []string{group.Description})
}
groupCreateRequest.Attribute("memberUid", []string{group.Creator})
if group.Members != nil {
groupCreateRequest.Attribute("memberUid", group.Members)
}
err = conn.Add(groupCreateRequest)
@@ -871,18 +879,7 @@ func CreateGroup(group models.Group) (*models.Group, error) {
group.Gid = strconv.Itoa(maxGid)
group.CreateTime = time.Now().UTC().Format("2006-01-02T15:04:05Z")
redisClient := redis.Client()
if err := redisClient.HMSet("kubesphere:groups:create-time", map[string]interface{}{group.Name: group.CreateTime}).Err(); err != nil {
return nil, err
}
if err := redisClient.HMSet("kubesphere:groups:creator", map[string]interface{}{group.Name: group.Creator}).Err(); err != nil {
return nil, err
}
return &group, nil
return DescribeGroup(group.Path)
}
func UpdateGroup(group *models.Group) (*models.Group, error) {
@@ -894,7 +891,7 @@ func UpdateGroup(group *models.Group) (*models.Group, error) {
}
defer conn.Close()
old, err := GroupDetail(group.Path, conn)
old, err := DescribeGroup(group.Path)
if err != nil {
return nil, err
@@ -1027,34 +1024,22 @@ func ChildList(path string) ([]models.Group, error) {
group.ChildGroups = childGroups
redisClient := redis.Client()
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
if len(createTime) > 0 {
if t, ok := createTime[0].(string); ok {
group.CreateTime = t
}
}
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
if len(creator) > 0 {
if t, ok := creator[0].(string); ok {
group.Creator = t
}
}
groups = append(groups, group)
}
return groups, nil
}
func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
func DescribeGroup(path string) (*models.Group, error) {
searchBase, cn := splitPath(path)
conn, err := ldapclient.Client()
if err != nil {
return nil, err
}
groupSearchRequest := ldap.NewSearchRequest(searchBase,
ldap.ScopeSingleLevel, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=posixGroup)(cn=%s))", cn),
@@ -1083,24 +1068,76 @@ func GroupDetail(path string, conn ldap.Client) (*models.Group, error) {
group.ChildGroups = childGroups
redisClient := redis.Client()
createTime, _ := redisClient.HMGet("kubesphere:groups:create-time", group.Name).Result()
if len(createTime) > 0 {
if t, ok := createTime[0].(string); ok {
group.CreateTime = t
}
}
creator, _ := redisClient.HMGet("kubesphere:groups:creator", group.Name).Result()
if len(creator) > 0 {
if t, ok := creator[0].(string); ok {
group.Creator = t
}
}
return &group, nil
}
func WorkspaceUsersTotalCount(workspace string) (int, error) {
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
if err != nil {
return 0, err
}
users := make([]string, 0)
for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
users = append(users, subject.Name)
}
}
}
return len(users), nil
}
func ListWorkspaceUsers(workspace string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
workspaceRoleBindings, err := GetWorkspaceRoleBindings(workspace)
if err != nil {
return nil, err
}
users := make([]*models.User, 0)
for _, roleBinding := range workspaceRoleBindings {
for _, subject := range roleBinding.Subjects {
if subject.Kind == v1.UserKind && !k8sutil.ContainsUser(users, subject.Name) {
user, err := DescribeUser(subject.Name)
if err != nil {
return nil, err
}
prefix := fmt.Sprintf("workspace:%s:", workspace)
user.WorkspaceRole = fmt.Sprintf("workspace-%s", strings.TrimPrefix(roleBinding.Name, prefix))
users = append(users, user)
}
}
}
// order & reverse
sort.Slice(users, func(i, j int) bool {
if reverse {
tmp := i
i = j
j = tmp
}
switch orderBy {
default:
fallthrough
case "name":
return strings.Compare(users[i].Username, users[j].Username) <= 0
}
})
result := make([]interface{}, 0)
for i, d := range users {
if i >= offset && (limit == -1 || len(result) < limit) {
result = append(result, d)
}
}
return &models.PageableResponse{Items: result, TotalCount: len(users)}, nil
}

View File

@@ -20,6 +20,7 @@ package policy
import (
"encoding/json"
"fmt"
"io/ioutil"
"kubesphere.io/kubesphere/pkg/models"
@@ -55,292 +56,25 @@ func init() {
}
var (
WorkspaceRoleRuleMapping = []models.Rule{
{
Name: "workspaces",
Actions: []models.Action{
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces"},
}, {
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/*"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io"},
Resources: []string{"*"},
}, {
Verbs: []string{"*"},
APIGroups: []string{"devops.kubesphere.io"},
Resources: []string{"*"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces"},
},
},
},
},
},
{Name: "members",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"patch", "update"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/members"},
},
},
},
},
},
{
Name: "devops",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/devops"},
},
},
},
},
},
{
Name: "projects",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/namespaces"},
},
},
},
},
},
{
Name: "organizations",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "edit",
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
},
{Name: "delete",
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"workspaces/organizations"},
},
},
}},
},
{
Name: "roles",
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces/roles"},
},
}},
},
},
}
ClusterRoleRuleMapping = []models.Rule{
{Name: "workspaces",
Actions: []models.Action{
{
Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"users"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"workspaces"},
Resources: []string{"monitoring/*"},
},
{
Verbs: []string{"list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"quota", "status", "monitoring", "persistentvolumeclaims"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"resources"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"workspaces", "workspaces/*"},
},
{
Verbs: []string{"get"},
APIGroups: []string{""},
Resources: []string{"namespaces"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"", "apps", "extensions", "batch"},
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings", "roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"members"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"router"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
Resources: []string{"*"},
},
},
},
{
Name: "create",
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"tenant.kubesphere.io"},
Resources: []string{"workspaces"},
},
},
},
{
Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"tenant.kubesphere.io"},
Resources: []string{"workspaces"},
},
},
@@ -349,7 +83,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"tenant.kubesphere.io", "monitoring.kubesphere.io"},
Resources: []string{"workspaces", "workspaces/*"},
},
{
@@ -359,7 +93,7 @@ var (
},
{
Verbs: []string{"*"},
APIGroups: []string{"", "apps", "extensions", "batch"},
APIGroups: []string{"", "apps", "extensions", "batch", "resources.kubesphere.io"},
Resources: []string{"serviceaccounts", "limitranges", "deployments", "configmaps", "secrets", "jobs", "cronjobs", "persistentvolumeclaims", "statefulsets", "daemonsets", "ingresses", "services", "pods/*", "pods", "events", "deployments/scale"},
},
{
@@ -367,16 +101,6 @@ var (
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings", "roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"members"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"router"},
},
{
Verbs: []string{"*"},
APIGroups: []string{"jenkins.kubesphere.io", "devops.kubesphere.io"},
@@ -391,9 +115,13 @@ var (
Actions: []models.Action{
{Name: "view",
Rules: []v1.PolicyRule{{
Verbs: []string{"*"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"monitoring", "health", "monitoring/*"},
Verbs: []string{"get", "list"},
APIGroups: []string{"monitoring.kubesphere.io"},
Resources: []string{"*"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"health"},
}},
},
},
@@ -405,14 +133,14 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "watch", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users", "users/*"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"clusterrules"},
ResourceNames: []string{"mapping"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"rulesmapping"},
ResourceNames: []string{"clusterroles"},
},
{
Verbs: []string{"get", "watch", "list"},
@@ -425,12 +153,12 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"create", "get", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
{
Verbs: []string{"get"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"clusterrules"},
ResourceNames: []string{"mapping"},
},
@@ -445,7 +173,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list", "update", "patch"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
{
@@ -459,8 +187,8 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete", "deletecollection"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"accounts"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
},
},
@@ -483,8 +211,8 @@ var (
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
Resources: []string{"clusterroles/*"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"clusterroles", "clusterroles/*"},
},
},
},
@@ -527,15 +255,9 @@ var (
APIGroups: []string{"storage.k8s.io"},
Resources: []string{"storageclasses"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"storage-classes"},
Resources: []string{"resources"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"storage/*"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"storageclasses", "storageclasses/*"},
},
},
},
@@ -578,15 +300,13 @@ var (
Resources: []string{"nodes", "events"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"nodes"},
Resources: []string{"resources", "monitoring", "monitoring/*"},
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"nodes", "nodes/*"},
}, {
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"pods"},
Resources: []string{"resources"},
Verbs: []string{"get", "list"},
APIGroups: []string{"monitoring.kubesphere.io"},
Resources: []string{"nodes"},
},
},
},
@@ -669,14 +389,9 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"list", "get"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"components", "components/*"},
},
{
Verbs: []string{"list", "get"},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
},
},
@@ -726,12 +441,12 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
Resources: []string{"rolebindings"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"account.kubesphere.io"},
APIGroups: []string{"iam.kubesphere.io"},
Resources: []string{"users"},
},
},
@@ -772,15 +487,9 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"rbac.authorization.k8s.io"},
APIGroups: []string{"rbac.authorization.k8s.io", "resources.kubesphere.io"},
Resources: []string{"roles"},
},
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"roles"},
Resources: []string{"resources"},
},
},
},
{Name: "create",
@@ -819,7 +528,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps", "extensions"},
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
Resources: []string{"deployments", "deployments/scale"},
},
{
@@ -875,7 +584,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps"},
APIGroups: []string{"apps", "resources.kubesphere.io"},
Resources: []string{"statefulsets"},
},
{
@@ -929,7 +638,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"apps", "extensions"},
APIGroups: []string{"apps", "extensions", "resources.kubesphere.io"},
Resources: []string{"daemonsets"},
},
{
@@ -974,8 +683,17 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
Resources: []string{"pod/shell"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"pod/terminal"},
},
},
},
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"pods"},
},
},
},
@@ -997,7 +715,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"list", "get"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"services"},
},
},
@@ -1039,7 +757,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1048,7 +766,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"create"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1057,7 +775,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"update", "patch"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1066,7 +784,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"delete"},
APIGroups: []string{"kubesphere.io"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"router"},
},
},
@@ -1081,7 +799,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{"extensions"},
APIGroups: []string{"extensions", "resources.kubesphere.io"},
Resources: []string{"ingresses"},
},
},
@@ -1121,7 +839,7 @@ var (
Rules: []v1.PolicyRule{
{
Verbs: []string{"get", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"persistentvolumeclaims"},
},
},
@@ -1160,10 +878,9 @@ var (
{Name: "view",
Rules: []v1.PolicyRule{
{
Verbs: []string{"get"},
APIGroups: []string{"kubesphere.io"},
ResourceNames: []string{"applications"},
Resources: []string{"resources"},
Verbs: []string{"get", "list"},
APIGroups: []string{"resources.kubesphere.io"},
Resources: []string{"applications"},
},
{
Verbs: []string{"list"},
@@ -1203,7 +920,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{"batch"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"jobs"},
},
}},
@@ -1236,7 +953,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{"batch"},
APIGroups: []string{"batch", "resources.kubesphere.io"},
Resources: []string{"cronjobs"},
},
}},
@@ -1269,7 +986,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"secrets"},
},
}},
@@ -1302,7 +1019,7 @@ var (
{Name: "view", Rules: []v1.PolicyRule{
{
Verbs: []string{"view", "list"},
APIGroups: []string{""},
APIGroups: []string{"", "resources.kubesphere.io"},
Resources: []string{"configmaps"},
},
}},
@@ -1331,3 +1048,16 @@ var (
},
}
)
func GetClusterAction(module, action string) (models.Action, error) {
for _, rule := range ClusterRoleRuleMapping {
if rule.Name == module {
for _, act := range rule.Actions {
if act.Name == action {
return act, nil
}
}
}
}
return models.Action{}, fmt.Errorf("not found")
}