@@ -1,7 +1,9 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
)
|
||||
|
||||
// Interface defines CRUD behaviors of manipulating users
|
||||
@@ -20,4 +22,6 @@ type Interface interface {
|
||||
|
||||
// Authenticate checks if (name, password) is valid, return ErrInvalidCredentials if not
|
||||
Authenticate(name string, password string) error
|
||||
|
||||
List(query *query.Query) (*api.ListResult, error)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,13 @@ import (
|
||||
"github.com/go-ldap/ldap"
|
||||
"github.com/google/uuid"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -109,6 +114,7 @@ func (l *ldapInterfaceImpl) createSearchBase() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
createIfNotExistsFunc := func(request *ldap.AddRequest) error {
|
||||
searchRequest := &ldap.SearchRequest{
|
||||
@@ -165,10 +171,10 @@ func (l *ldapInterfaceImpl) newConn() (ldap.Client, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Bind(l.managerDN, l.managerPassword)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
@@ -357,6 +363,7 @@ func (l *ldapInterfaceImpl) Authenticate(username, password string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dn := l.dnForUsername(username)
|
||||
err = conn.Bind(dn, password)
|
||||
@@ -365,3 +372,106 @@ func (l *ldapInterfaceImpl) Authenticate(username, password string) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *ldapInterfaceImpl) List(query *query.Query) (*api.ListResult, error) {
|
||||
conn, err := l.newConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
pageControl := ldap.NewControlPaging(1000)
|
||||
|
||||
users := make([]iamv1alpha2.User, 0)
|
||||
|
||||
filter := "(&(objectClass=inetOrgPerson))"
|
||||
|
||||
if keyword := query.Filters["keyword"]; keyword != "" {
|
||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|(uid=*%s*)(mail=*%s*)(description=*%s*)))", keyword, keyword, keyword)
|
||||
}
|
||||
|
||||
if username := query.Filters["username"]; username != "" {
|
||||
uidFilter := ""
|
||||
for _, username := range strings.Split(string(username), "|") {
|
||||
uidFilter += fmt.Sprintf("(uid=%s)", username)
|
||||
}
|
||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", uidFilter)
|
||||
}
|
||||
|
||||
if email := query.Filters["email"]; email != "" {
|
||||
emailFilter := ""
|
||||
for _, username := range strings.Split(string(email), "|") {
|
||||
emailFilter += fmt.Sprintf("(mail=%s)", username)
|
||||
}
|
||||
filter = fmt.Sprintf("(&(objectClass=inetOrgPerson)(|%s))", emailFilter)
|
||||
}
|
||||
|
||||
for {
|
||||
userSearchRequest := ldap.NewSearchRequest(
|
||||
l.userSearchBase,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
filter,
|
||||
[]string{"uid", "mail", "description", "preferredLanguage", "createTimestamp"},
|
||||
[]ldap.Control{pageControl},
|
||||
)
|
||||
|
||||
response, err := conn.Search(userSearchRequest)
|
||||
|
||||
if err != nil {
|
||||
klog.Errorln("search user", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, entry := range response.Entries {
|
||||
|
||||
uid := entry.GetAttributeValue("uid")
|
||||
email := entry.GetAttributeValue("mail")
|
||||
description := entry.GetAttributeValue("description")
|
||||
lang := entry.GetAttributeValue("preferredLanguage")
|
||||
createTimestamp, _ := time.Parse("20060102150405Z", entry.GetAttributeValue("createTimestamp"))
|
||||
|
||||
user := iamv1alpha2.User{ObjectMeta: metav1.ObjectMeta{Name: uid, CreationTimestamp: metav1.Time{Time: createTimestamp}}, Spec: iamv1alpha2.UserSpec{
|
||||
Email: email,
|
||||
Lang: lang,
|
||||
Description: description,
|
||||
}}
|
||||
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
sort.Slice(users, func(i, j int) bool {
|
||||
if !query.Ascending {
|
||||
i, j = j, i
|
||||
}
|
||||
switch query.SortBy {
|
||||
case "username":
|
||||
return strings.Compare(users[i].Name, users[j].Name) <= 0
|
||||
case "createTime":
|
||||
fallthrough
|
||||
default:
|
||||
return users[i].CreationTimestamp.Before(&users[j].CreationTimestamp)
|
||||
}
|
||||
})
|
||||
|
||||
items := make([]interface{}, 0)
|
||||
|
||||
for i, user := range users {
|
||||
if i >= query.Pagination.Offset && len(items) < query.Pagination.Limit {
|
||||
items = append(items, user)
|
||||
}
|
||||
}
|
||||
|
||||
return &api.ListResult{
|
||||
Items: items,
|
||||
TotalItems: len(users),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package ldap
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
iamv1alpha2 "kubesphere.io/kubesphere/pkg/apis/iam/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/query"
|
||||
)
|
||||
|
||||
// simpleLdap is a implementation of ldap.Interface, you should never use this in production env!
|
||||
@@ -74,3 +76,16 @@ func (s simpleLdap) Authenticate(name string, password string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *simpleLdap) List(query *query.Query) (*api.ListResult, error) {
|
||||
items := make([]interface{}, 0)
|
||||
|
||||
for _, user := range l.store {
|
||||
items = append(items, user)
|
||||
}
|
||||
|
||||
return &api.ListResult{
|
||||
Items: items,
|
||||
TotalItems: len(items),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -4,80 +4,8 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/logging"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMainBool(t *testing.T) {
|
||||
var tests = []struct {
|
||||
description string
|
||||
searchFilter logging.SearchFilter
|
||||
expected *bodyBuilder
|
||||
}{
|
||||
{
|
||||
description: "filter 2 namespaces",
|
||||
searchFilter: logging.SearchFilter{
|
||||
NamespaceFilter: map[string]time.Time{
|
||||
"kubesphere-system": time.Unix(1582000000, 0),
|
||||
"kubesphere-logging-system": time.Unix(1582969999, 0),
|
||||
},
|
||||
},
|
||||
expected: &bodyBuilder{Body{
|
||||
Query: &Query{
|
||||
Bool: Bool{
|
||||
Filter: []Match{
|
||||
{
|
||||
Bool: &Bool{
|
||||
Should: []Match{
|
||||
{
|
||||
Bool: &Bool{
|
||||
Filter: []Match{
|
||||
{
|
||||
MatchPhrase: map[string]string{"kubernetes.namespace_name.keyword": "kubesphere-system"},
|
||||
},
|
||||
{
|
||||
Range: &Range{&Time{Gte: func() *time.Time { t := time.Unix(1582000000, 0); return &t }()}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Bool: &Bool{
|
||||
Filter: []Match{
|
||||
{
|
||||
MatchPhrase: map[string]string{"kubernetes.namespace_name.keyword": "kubesphere-logging-system"},
|
||||
},
|
||||
{
|
||||
Range: &Range{&Time{Gte: func() *time.Time { t := time.Unix(1582969999, 0); return &t }()}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MinimumShouldMatch: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
body, err := newBodyBuilder().mainBool(test.searchFilter).bytes()
|
||||
expected, _ := test.expected.bytes()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(body, expected); diff != "" {
|
||||
t.Fatalf("%T differ (-got, +want): %s", expected, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCardinalityAggregation(t *testing.T) {
|
||||
var test = struct {
|
||||
description string
|
||||
|
||||
Reference in New Issue
Block a user