This is a huge commit, it does following things: (#1942)
1. Remove ks-iam standalone binary, move it to ks-apiserver 2. Generate all devops apis inside kubesphere repository, no need to import s2ioperator. 3. Reorganize ldap code, make it more flexible to use.
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
@@ -33,7 +34,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/resource"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2/role"
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client"
|
||||
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
|
||||
)
|
||||
|
||||
@@ -62,8 +62,9 @@ type AccessManagementInterface interface {
|
||||
}
|
||||
|
||||
type amOperator struct {
|
||||
informers informers.SharedInformerFactory
|
||||
resources resource.ResourceGetter
|
||||
informers informers.SharedInformerFactory
|
||||
resources resource.ResourceGetter
|
||||
kubeClient kubernetes.Interface
|
||||
}
|
||||
|
||||
func (am *amOperator) ListClusterRoleBindings(clusterRole string) ([]*rbacv1.ClusterRoleBinding, error) {
|
||||
@@ -90,11 +91,15 @@ func (am *amOperator) UnBindAllRoles(username string) error {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func NewAMOperator(informers informers.SharedInformerFactory) *amOperator {
|
||||
func NewAMOperator(kubeClient kubernetes.Interface, informers informers.SharedInformerFactory) *amOperator {
|
||||
resourceGetter := resource.ResourceGetter{}
|
||||
resourceGetter.Add(v1alpha2.Role, role.NewRoleSearcher(informers))
|
||||
resourceGetter.Add(v1alpha2.ClusterRoles, clusterrole.NewClusterRoleSearcher(informers))
|
||||
return &amOperator{informers: informers, resources: resourceGetter}
|
||||
return &amOperator{
|
||||
informers: informers,
|
||||
resources: resourceGetter,
|
||||
kubeClient: kubeClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (am *amOperator) GetDevopsRoleSimpleRules(role string) []policy.SimpleRule {
|
||||
@@ -560,7 +565,7 @@ func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName
|
||||
found, err := clusterRoleBindingLister.Get(username)
|
||||
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||
_, err = am.kubeClient.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||
if err != nil {
|
||||
klog.Errorln("create cluster role binding", err)
|
||||
return err
|
||||
@@ -575,12 +580,12 @@ func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName
|
||||
deletePolicy := metav1.DeletePropagationBackground
|
||||
gracePeriodSeconds := int64(0)
|
||||
deleteOption := &metav1.DeleteOptions{PropagationPolicy: &deletePolicy, GracePeriodSeconds: &gracePeriodSeconds}
|
||||
err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Delete(found.Name, deleteOption)
|
||||
err = am.kubeClient.RbacV1().ClusterRoleBindings().Delete(found.Name, deleteOption)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
_, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||
_, err = am.kubeClient.RbacV1().ClusterRoleBindings().Create(clusterRoleBinding)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
@@ -590,7 +595,7 @@ func (am *amOperator) CreateClusterRoleBinding(username string, clusterRoleName
|
||||
|
||||
if !ContainsUser(found.Subjects, username) {
|
||||
found.Subjects = clusterRoleBinding.Subjects
|
||||
_, err = client.ClientSets().K8s().Kubernetes().RbacV1().ClusterRoleBindings().Update(found)
|
||||
_, err = am.kubeClient.RbacV1().ClusterRoleBindings().Update(found)
|
||||
if err != nil {
|
||||
klog.Errorln("update cluster role binding", err)
|
||||
return err
|
||||
|
||||
@@ -20,216 +20,96 @@ package iam
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-ldap/ldap"
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models"
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
ldappool "kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"kubesphere.io/kubesphere/pkg/utils/jwtutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IdentityManagementInterface interface {
|
||||
CreateUser(user *User) (*User, error)
|
||||
CreateUser(user *iam.User) (*iam.User, error)
|
||||
DeleteUser(username string) error
|
||||
DescribeUser(username string) (*User, error)
|
||||
DescribeUser(username string) (*iam.User, error)
|
||||
Login(username, password, ip string) (*oauth2.Token, error)
|
||||
ModifyUser(user *User) (*User, error)
|
||||
ModifyUser(user *iam.User) (*iam.User, error)
|
||||
ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
|
||||
GetUserRoles(username string) ([]*rbacv1.Role, error)
|
||||
GetUserRole(namespace string, username string) (*rbacv1.Role, error)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
authRateLimit string
|
||||
maxAuthFailed int
|
||||
authTimeInterval time.Duration
|
||||
tokenIdleTimeout time.Duration
|
||||
enableMultiLogin bool
|
||||
}
|
||||
|
||||
type imOperator struct {
|
||||
config Config
|
||||
ldap ldappool.Client
|
||||
redis redis.Client
|
||||
authenticateOptions *iam.AuthenticationOptions
|
||||
ldapClient ldap.Interface
|
||||
cacheClient cache.Interface
|
||||
}
|
||||
|
||||
const (
|
||||
authRateLimitRegex = `(\d+)/(\d+[s|m|h])`
|
||||
defaultMaxAuthFailed = 5
|
||||
defaultAuthTimeInterval = 30 * time.Minute
|
||||
mailAttribute = "mail"
|
||||
uidAttribute = "uid"
|
||||
descriptionAttribute = "description"
|
||||
preferredLanguageAttribute = "preferredLanguage"
|
||||
createTimestampAttribute = "createTimestampAttribute"
|
||||
dateTimeLayout = "20060102150405Z"
|
||||
)
|
||||
|
||||
var (
|
||||
AuthRateLimitExceeded = errors.New("user auth rate limit exceeded")
|
||||
UserAlreadyExists = errors.New("user already exists")
|
||||
UserNotExists = errors.New("user not exists")
|
||||
)
|
||||
|
||||
func NewIMOperator(ldap ldappool.Client, config Config) *imOperator {
|
||||
imOperator := &imOperator{ldap: ldap, config: config}
|
||||
return imOperator
|
||||
func NewIMOperator(ldapClient ldap.Interface, cacheClient cache.Interface, options *iam.AuthenticationOptions) *imOperator {
|
||||
return &imOperator{ldapClient: ldapClient, cacheClient: cacheClient, authenticateOptions: options}
|
||||
|
||||
}
|
||||
|
||||
// TODO init in controller
|
||||
func (im *imOperator) Init() error {
|
||||
|
||||
userSearchBase := &ldap.AddRequest{
|
||||
DN: im.ldap.UserSearchBase(),
|
||||
Attributes: []ldap.Attribute{{
|
||||
Type: "objectClass",
|
||||
Vals: []string{"organizationalUnit", "top"},
|
||||
}, {
|
||||
Type: "ou",
|
||||
Vals: []string{"Users"},
|
||||
}},
|
||||
Controls: nil,
|
||||
}
|
||||
|
||||
err := im.createIfNotExists(userSearchBase)
|
||||
|
||||
func (im *imOperator) ModifyUser(user *iam.User) (*iam.User, error) {
|
||||
err := im.ldapClient.Update(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
groupSearchBase := &ldap.AddRequest{
|
||||
DN: im.ldap.GroupSearchBase(),
|
||||
Attributes: []ldap.Attribute{{
|
||||
Type: "objectClass",
|
||||
Vals: []string{"organizationalUnit", "top"},
|
||||
}, {
|
||||
Type: "ou",
|
||||
Vals: []string{"Groups"},
|
||||
}},
|
||||
Controls: nil,
|
||||
}
|
||||
|
||||
err = im.createIfNotExists(groupSearchBase)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (im *imOperator) createIfNotExists(createRequest *ldap.AddRequest) error {
|
||||
conn, err := im.ldap.NewConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
createRequest.DN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
"(objectClass=*)",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
_, err = conn.Search(searchRequest)
|
||||
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultNoSuchObject) {
|
||||
err = conn.Add(createRequest)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (im *imOperator) ModifyUser(user *User) (*User, error) {
|
||||
conn, err := im.ldap.NewConn()
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
dn := fmt.Sprintf("uid=%s,%s", user.Username, im.ldap.UserSearchBase())
|
||||
userModifyRequest := ldap.NewModifyRequest(dn, nil)
|
||||
|
||||
if user.Description != "" {
|
||||
userModifyRequest.Replace("description", []string{user.Description})
|
||||
}
|
||||
|
||||
if user.Lang != "" {
|
||||
userModifyRequest.Replace("preferredLanguage", []string{user.Lang})
|
||||
}
|
||||
|
||||
if user.Password != "" {
|
||||
userModifyRequest.Replace("userPassword", []string{user.Password})
|
||||
}
|
||||
|
||||
err = conn.Modify(userModifyRequest)
|
||||
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// clear auth failed record
|
||||
if user.Password != "" {
|
||||
|
||||
records, err := im.redis.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", user.Username)).Result()
|
||||
|
||||
records, err := im.cacheClient.Keys(authenticationFailedKeyForUsername(user.Username, "*"))
|
||||
if err == nil {
|
||||
im.redis.Del(records...)
|
||||
im.cacheClient.Del(records...)
|
||||
}
|
||||
}
|
||||
|
||||
return im.DescribeUser(user.Username)
|
||||
return im.ldapClient.Get(user.Username)
|
||||
}
|
||||
|
||||
func authenticationFailedKeyForUsername(username, failedTimestamp string) string {
|
||||
return fmt.Sprintf("kubesphere:authfailed:%s:%s", username, failedTimestamp)
|
||||
}
|
||||
|
||||
func tokenKeyForUsername(username, token string) string {
|
||||
return fmt.Sprintf("kubesphere:users:%s:token:%s", username, token)
|
||||
}
|
||||
|
||||
func loginKeyForUsername(username, loginTimestamp, ip string) string {
|
||||
return fmt.Sprintf("kubesphere:users:%s:login-log:%s:%s", username, loginTimestamp, ip)
|
||||
}
|
||||
|
||||
func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error) {
|
||||
|
||||
records, err := im.redis.Keys(fmt.Sprintf("kubesphere:authfailed:%s:*", username)).Result()
|
||||
|
||||
records, err := im.cacheClient.Keys(authenticationFailedKeyForUsername(username, "*"))
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(records) >= im.config.maxAuthFailed {
|
||||
if len(records) >= im.authenticateOptions.MaxAuthenticateRetries {
|
||||
return nil, AuthRateLimitExceeded
|
||||
}
|
||||
|
||||
user, err := im.DescribeUser(username)
|
||||
|
||||
conn, err := im.ldap.NewConn()
|
||||
user, err := im.ldapClient.Get(username)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dn := fmt.Sprintf("uid=%s,%s", user.Username, im.ldap.UserSearchBase())
|
||||
|
||||
// bind as the user to verify their password
|
||||
err = conn.Bind(dn, password)
|
||||
|
||||
err = im.ldapClient.Verify(user.Username, password)
|
||||
if err != nil {
|
||||
if ldap.IsErrorWithCode(err, ldap.LDAPResultInvalidCredentials) {
|
||||
cacheKey := fmt.Sprintf("kubesphere:authfailed:%s:%d", user.Username, time.Now().UnixNano())
|
||||
im.redis.Set(cacheKey, "", im.config.authTimeInterval)
|
||||
if err == ldap.ErrInvalidCredentials {
|
||||
im.cacheClient.Set(authenticationFailedKeyForUsername(username, fmt.Sprintf("%d", time.Now().UnixNano())), "", 30*time.Minute)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -243,30 +123,25 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
|
||||
}
|
||||
token := jwtutil.MustSigned(claims)
|
||||
|
||||
if !im.config.enableMultiLogin {
|
||||
tokenKey := tokenKeyForUsername(user.Username, "*")
|
||||
if !im.authenticateOptions.MultipleLogin {
|
||||
// multi login not allowed, remove the previous token
|
||||
cacheKey := fmt.Sprintf("kubesphere:users:%s:token:*", user.Username)
|
||||
sessions, err := im.redis.Keys(cacheKey).Result()
|
||||
|
||||
sessions, err := im.cacheClient.Keys(tokenKey)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(sessions) > 0 {
|
||||
klog.V(4).Infoln("revoke token", sessions)
|
||||
err = im.redis.Del(sessions...).Err()
|
||||
err = im.cacheClient.Del(sessions...)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache token with expiration time
|
||||
cacheKey := fmt.Sprintf("kubesphere:users:%s:token:%s", user.Username, token)
|
||||
if err = im.redis.Set(cacheKey, token, im.config.tokenIdleTimeout).Err(); err != nil {
|
||||
klog.Errorln(err)
|
||||
if err = im.cacheClient.Set(tokenKey, token, im.authenticateOptions.TokenExpiration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -277,153 +152,39 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
|
||||
|
||||
func (im *imOperator) loginRecord(username, ip string, loginTime time.Time) {
|
||||
if ip != "" {
|
||||
im.redis.RPush(fmt.Sprintf("kubesphere:users:%s:login-log", username), fmt.Sprintf("%s,%s", loginTime.UTC().Format("2006-01-02T15:04:05Z"), ip))
|
||||
im.redis.LTrim(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1)
|
||||
_ = im.cacheClient.Set(loginKeyForUsername(username, loginTime.UTC().Format("2006-01-02T15:04:05Z"), ip), "", 30*24*time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
func (im *imOperator) LoginHistory(username string) ([]string, error) {
|
||||
data, err := im.redis.LRange(fmt.Sprintf("kubesphere:users:%s:login-log", username), -10, -1).Result()
|
||||
|
||||
keys, err := im.cacheClient.Keys(loginKeyForUsername(username, "*", "*"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func (im *imOperator) ListUsers(conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (im *imOperator) DescribeUser(username string) (*User, error) {
|
||||
conn, err := im.ldap.NewConn()
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
filter := fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=%s)(mail=%s)))", username, username)
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
im.ldap.UserSearchBase(),
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
filter,
|
||||
[]string{mailAttribute, descriptionAttribute, preferredLanguageAttribute, createTimestampAttribute},
|
||||
nil,
|
||||
)
|
||||
|
||||
result, err := conn.Search(searchRequest)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result.Entries) != 1 {
|
||||
return nil, UserNotExists
|
||||
}
|
||||
|
||||
entry := result.Entries[0]
|
||||
|
||||
return convertLdapEntryToUser(entry), nil
|
||||
}
|
||||
|
||||
func convertLdapEntryToUser(entry *ldap.Entry) *User {
|
||||
username := entry.GetAttributeValue(uidAttribute)
|
||||
email := entry.GetAttributeValue(mailAttribute)
|
||||
description := entry.GetAttributeValue(descriptionAttribute)
|
||||
lang := entry.GetAttributeValue(preferredLanguageAttribute)
|
||||
createTimestamp, err := time.Parse(dateTimeLayout, entry.GetAttributeValue(createTimestampAttribute))
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
}
|
||||
return &User{Username: username, Email: email, Description: description, Lang: lang, CreateTime: createTimestamp}
|
||||
func (im *imOperator) DescribeUser(username string) (*iam.User, error) {
|
||||
return im.ldapClient.Get(username)
|
||||
}
|
||||
|
||||
func (im *imOperator) getLastLoginTime(username string) string {
|
||||
cacheKey := fmt.Sprintf("kubesphere:users:%s:login-log", username)
|
||||
lastLogin, err := im.redis.LRange(cacheKey, -1, -1).Result()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(lastLogin) > 0 {
|
||||
return strings.Split(lastLogin[0], ",")[0]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (im *imOperator) DeleteUser(username string) error {
|
||||
conn, err := im.ldap.NewConn()
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
deleteRequest := ldap.NewDelRequest(fmt.Sprintf("uid=%s,%s", username, im.ldap.UserSearchBase()), nil)
|
||||
|
||||
if err = conn.Del(deleteRequest); err != nil {
|
||||
klog.Errorln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return im.ldapClient.Delete(username)
|
||||
}
|
||||
|
||||
func (im *imOperator) CreateUser(user *User) (*User, error) {
|
||||
user.Username = strings.TrimSpace(user.Username)
|
||||
user.Email = strings.TrimSpace(user.Email)
|
||||
user.Password = strings.TrimSpace(user.Password)
|
||||
user.Description = strings.TrimSpace(user.Description)
|
||||
|
||||
existed, err := im.DescribeUser(user.Username)
|
||||
|
||||
func (im *imOperator) CreateUser(user *iam.User) (*iam.User, error) {
|
||||
err := im.ldapClient.Create(user)
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed != nil {
|
||||
return nil, UserAlreadyExists
|
||||
}
|
||||
|
||||
uidNumber := im.uidNumberNext()
|
||||
|
||||
createRequest := ldap.NewAddRequest(fmt.Sprintf("uid=%s,%s", user.Username, im.ldap.UserSearchBase()), nil)
|
||||
createRequest.Attribute("objectClass", []string{"inetOrgPerson", "posixAccount", "top"})
|
||||
createRequest.Attribute("cn", []string{user.Username}) // RFC4519: common name(s) for which the entity is known by
|
||||
createRequest.Attribute("sn", []string{" "}) // RFC2256: last (family) name(s) for which the entity is known by
|
||||
createRequest.Attribute("gidNumber", []string{"500"}) // RFC2307: An integer uniquely identifying a group in an administrative domain
|
||||
createRequest.Attribute("homeDirectory", []string{"/home/" + user.Username}) // The absolute path to the home directory
|
||||
createRequest.Attribute("uid", []string{user.Username}) // RFC4519: user identifier
|
||||
createRequest.Attribute("uidNumber", []string{strconv.Itoa(uidNumber)}) // RFC2307: An integer uniquely identifying a user in an administrative domain
|
||||
createRequest.Attribute("mail", []string{user.Email}) // RFC1274: RFC822 Mailbox
|
||||
createRequest.Attribute("userPassword", []string{user.Password}) // RFC4519/2307: password of user
|
||||
if user.Lang != "" {
|
||||
createRequest.Attribute("preferredLanguage", []string{user.Lang})
|
||||
}
|
||||
if user.Description != "" {
|
||||
createRequest.Attribute("description", []string{user.Description}) // RFC4519: descriptive information
|
||||
}
|
||||
|
||||
conn, err := im.ldap.NewConn()
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = conn.Add(createRequest)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -17,39 +17,3 @@
|
||||
*/
|
||||
|
||||
package iam
|
||||
|
||||
import (
|
||||
"github.com/golang/mock/gomock"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/ldap"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIMOperator(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
ldappool, err := ldap.NewMockClient(&ldap.Options{
|
||||
Host: "192.168.0.7:30389",
|
||||
ManagerDN: "cn=admin,dc=kubesphere,dc=io",
|
||||
ManagerPassword: "admin",
|
||||
UserSearchBase: "ou=Users,dc=kubesphere,dc=io",
|
||||
GroupSearchBase: "ou=Groups,dc=kubesphere,dc=io",
|
||||
InitialCap: 8,
|
||||
MaxCap: 64,
|
||||
}, ctrl, func(client *ldap.MockClient) {
|
||||
client.EXPECT().Search(gomock.Any()).AnyTimes()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer ldappool.Close()
|
||||
|
||||
im := NewIMOperator(ldappool, Config{})
|
||||
|
||||
err = im.Init()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +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/simple/client"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func convertDNToPath(dn string) string {
|
||||
|
||||
paths := regexp.MustCompile("cn=[a-z0-9]([-a-z0-9]*[a-z0-9])?").FindAllString(dn, -1)
|
||||
|
||||
if len(paths) > 1 {
|
||||
for i := 0; i < len(paths); i++ {
|
||||
paths[i] = strings.Replace(paths[i], "cn=", "", 1)
|
||||
}
|
||||
for i, j := 0, len(paths)-1; i < j; i, j = i+1, j-1 {
|
||||
paths[i], paths[j] = paths[j], paths[i]
|
||||
}
|
||||
return strings.Join(paths, ":")
|
||||
} else if len(paths) == 1 {
|
||||
return strings.Replace(paths[0], "cn=", "", -1)
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func splitPath(path string) (searchBase string, cn string) {
|
||||
ldapClient, err := client.ClientSets().Ldap()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
paths := strings.Split(path, ":")
|
||||
length := len(paths)
|
||||
if length > 2 {
|
||||
|
||||
cn = paths[length-1]
|
||||
basePath := paths[:length-1]
|
||||
|
||||
for i := 0; i < len(basePath); i++ {
|
||||
basePath[i] = fmt.Sprintf("cn=%s", basePath[i])
|
||||
}
|
||||
|
||||
for i, j := 0, length-2; i < j; i, j = i+1, j-1 {
|
||||
basePath[i], basePath[j] = basePath[j], basePath[i]
|
||||
}
|
||||
|
||||
searchBase = fmt.Sprintf("%s,%s", strings.Join(basePath, ","), ldapClient.GroupSearchBase())
|
||||
} else if length == 2 {
|
||||
searchBase = fmt.Sprintf("cn=%s,%s", paths[0], ldapClient.GroupSearchBase())
|
||||
cn = paths[1]
|
||||
} else {
|
||||
searchBase = ldapClient.GroupSearchBase()
|
||||
if paths[0] == "" {
|
||||
cn = "*"
|
||||
} else {
|
||||
cn = paths[0]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2020 The KubeSphere Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* /
|
||||
*/
|
||||
|
||||
package iam
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
KindTokenReview = "TokenReview"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Lang string `json:"lang,omitempty"`
|
||||
Description string `json:"description"`
|
||||
CreateTime time.Time `json:"create_time"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package iam
|
||||
|
||||
import (
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models/iam/policy"
|
||||
"strings"
|
||||
)
|
||||
@@ -194,15 +195,15 @@ func ContainsUser(subjects interface{}, username string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
case []User:
|
||||
for _, u := range subjects.([]User) {
|
||||
case []iam.User:
|
||||
for _, u := range subjects.([]iam.User) {
|
||||
if u.Username == username {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case []*User:
|
||||
for _, u := range subjects.([]*User) {
|
||||
case []*iam.User:
|
||||
for _, u := range subjects.([]*iam.User) {
|
||||
if u.Username == username {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ func NewResourceGetter(factory informers.InformerFactory) *ResourceGetter {
|
||||
resourceGetters[v1alpha2.StorageClasses] = storageclass.NewStorageClassesSearcher(factory.KubernetesSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.HorizontalPodAutoscalers] = hpa.NewHpaSearcher(factory.KubernetesSharedInformerFactory())
|
||||
|
||||
resourceGetters[v1alpha2.S2iBuilders] = s2ibuilder.NewS2iBuilderSearcher(factory.S2iSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.S2iRuns] = s2irun.NewS2iRunSearcher(factory.S2iSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.S2iBuilderTemplates] = s2buildertemplate.NewS2iBuidlerTemplateSearcher(factory.S2iSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.S2iBuilders] = s2ibuilder.NewS2iBuilderSearcher(factory.KubeSphereSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.S2iRuns] = s2irun.NewS2iRunSearcher(factory.KubeSphereSharedInformerFactory())
|
||||
resourceGetters[v1alpha2.S2iBuilderTemplates] = s2buildertemplate.NewS2iBuidlerTemplateSearcher(factory.KubeSphereSharedInformerFactory())
|
||||
|
||||
resourceGetters[v1alpha2.Workspaces] = workspace.NewWorkspaceSearcher(factory.KubeSphereSharedInformerFactory())
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package s2buildertemplate
|
||||
|
||||
import (
|
||||
"github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1"
|
||||
"github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
package s2ibuilder
|
||||
|
||||
import (
|
||||
"github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1"
|
||||
"github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
|
||||
|
||||
@@ -19,19 +19,15 @@
|
||||
package s2irun
|
||||
|
||||
import (
|
||||
"github.com/kubesphere/s2ioperator/pkg/client/informers/externalversions"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
|
||||
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/kubesphere/s2ioperator/pkg/apis/devops/v1alpha1"
|
||||
)
|
||||
|
||||
type s2iRunSearcher struct {
|
||||
|
||||
@@ -108,7 +108,7 @@ func TestListDeployments(t *testing.T) {
|
||||
},
|
||||
api.ListResult{
|
||||
Items: []interface{}{
|
||||
v1.Deployment{
|
||||
&v1.Deployment{
|
||||
ObjectMeta: metaV1.ObjectMeta{
|
||||
Name: "foo-2",
|
||||
Namespace: "bar",
|
||||
@@ -141,7 +141,7 @@ func TestListDeployments(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(got, test.expected); diff != "" {
|
||||
if diff := cmp.Diff(got.Items, test.expected.Items); diff != "" {
|
||||
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -77,7 +77,7 @@ func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
|
||||
}
|
||||
|
||||
func New(client kubernetes.Interface, informers k8sinformers.SharedInformerFactory, ksinformers ksinformers.SharedInformerFactory, db *mysql.Database) Interface {
|
||||
amOperator := iam.NewAMOperator(informers)
|
||||
amOperator := iam.NewAMOperator(client, informers)
|
||||
return &tenantOperator{
|
||||
workspaces: newWorkspaceOperator(client, informers, ksinformers, amOperator, db),
|
||||
namespaces: newNamespaceOperator(client, informers, amOperator),
|
||||
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/models/iam"
|
||||
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/server/params"
|
||||
clientset "kubesphere.io/kubesphere/pkg/simple/client"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
|
||||
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
|
||||
"sort"
|
||||
@@ -43,10 +42,12 @@ import (
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
iamapi "kubesphere.io/kubesphere/pkg/api/iam"
|
||||
)
|
||||
|
||||
type InWorkspaceUser struct {
|
||||
*iam.User
|
||||
*iamapi.User
|
||||
WorkspaceRole string `json:"workspaceRole"`
|
||||
}
|
||||
|
||||
@@ -203,10 +204,6 @@ func (w *workspaceOperator) deleteWorkspaceRoleBinding(workspace, username strin
|
||||
}
|
||||
|
||||
func (w *workspaceOperator) CountDevopsProjectsInWorkspace(workspaceName string) (int, error) {
|
||||
if w.db == nil {
|
||||
return 0, clientset.ErrClientSetNotEnabled
|
||||
}
|
||||
|
||||
query := w.db.Select(devops.DevOpsProjectIdColumn).
|
||||
From(devops.DevOpsProjectTableName).
|
||||
Where(db.And(db.Eq(devops.DevOpsProjectWorkSpaceColumn, workspaceName),
|
||||
|
||||
Reference in New Issue
Block a user