Skip to content

Commit

Permalink
Add sync 3rd-party idp groups with users
Browse files Browse the repository at this point in the history
  • Loading branch information
yadzhang authored and choujimmy committed Jan 17, 2020
1 parent 8ac9e51 commit ddd6081
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 14 deletions.
6 changes: 4 additions & 2 deletions api/auth/v1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ func AddFieldLabelConversionsForUser(scheme *runtime.Scheme) error {
return scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("User"),
func(label, value string) (string, string, error) {
switch label {
case "keyword":
case "keyword",
"spec.tenantID":
return label, value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
Expand All @@ -215,7 +216,8 @@ func AddFieldLabelConversionsForGroup(scheme *runtime.Scheme) error {
return scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("Group"),
func(label, value string) (string, string, error) {
switch label {
case "keyword":
case "keyword",
"spec.tenantID":
return label, value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
Expand Down
15 changes: 10 additions & 5 deletions pkg/auth/authentication/oidc/identityprovider/ldap/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,11 @@ func (c *identityProvider) ListUsers(ctx context.Context, options *internalversi
userList := auth.UserList{}
for _, entry := range ldapUsers {
user, err := c.userFromEntry(*entry)
if err == nil {
userList.Items = append(userList.Items, *user)
if err != nil {
continue
}
userList.Items = append(userList.Items, *user)

}

return &userList, nil
Expand Down Expand Up @@ -234,9 +236,11 @@ func (c *identityProvider) ListGroups(ctx context.Context, options *internalvers
groupList := auth.GroupList{}
for _, entry := range ldapGroups {
grp, err := c.groupFromEntry(*entry)
if err == nil {
groupList.Items = append(groupList.Items, *grp)
if err != nil {
continue
}
groupList.Items = append(groupList.Items, *grp)

}

return &groupList, nil
Expand Down Expand Up @@ -431,6 +435,7 @@ func (c *identityProvider) groupsEntry(conn *ldap.Conn, keyword string, limit in

log.Infof("performing ldap search %s %s %s",
req.BaseDN, scopeString(req.Scope), req.Filter)

resp, err := conn.SearchWithPaging(req, uint32(limit))
if err != nil {
return nil, fmt.Errorf("ldap: search with filter %q failed: %v", req.Filter, err)
Expand All @@ -457,7 +462,7 @@ func (c *identityProvider) groupFromEntry(group ldap.Entry) (authGroup *auth.Gro
members := getAttrs(group, c.GroupSearch.GroupAttr)
for _, dn := range members {
name := parseNameFromDN(dn, c.UserSearch.Username)
if name == "" {
if name != "" {
authGroup.Status.Users = append(authGroup.Status.Users, auth.Subject{
ID: dn,
Name: name,
Expand Down
6 changes: 4 additions & 2 deletions pkg/auth/authentication/oidc/identityprovider/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,10 +295,12 @@ func (c *DefaultIdentityProvider) ListGroups(ctx context.Context, options *metai
localGroupList = newList
}

items := localGroupList[0:min(len(localGroupList), limit)]
if limit > 0 {
localGroupList = localGroupList[0:min(len(localGroupList), limit)]
}

groupList := auth.GroupList{}
for _, item := range items {
for _, item := range localGroupList {
group := convertToGroup(item)
groupList.Items = append(groupList.Items, group)
}
Expand Down
74 changes: 70 additions & 4 deletions pkg/auth/controller/group/group_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ import (

"github.com/casbin/casbin/v2"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"

"tkestack.io/tke/api/auth"
v1 "tkestack.io/tke/api/auth/v1"
clientset "tkestack.io/tke/api/client/clientset/versioned"
authv1informer "tkestack.io/tke/api/client/informers/externalversions/auth/v1"
Expand All @@ -53,6 +57,8 @@ const (
groupDeletionGracePeriod = 5 * time.Second

controllerName = "group-controller"

groupSyncedPeriod = 1 * time.Minute
)

// Controller is responsible for performing actions dependent upon a group phase.
Expand Down Expand Up @@ -146,7 +152,8 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) {
log.Error("Failed to wait for group caches to sync")
}

//TODO sync groups(non-tke-local) for idp into casbin
// sync groups(include 3rd party) for identity providers into casbin
go c.pollThirdPartyGroup(stopCh)

for i := 0; i < workers; i++ {
go wait.Until(c.worker, time.Second, stopCh)
Expand Down Expand Up @@ -222,11 +229,28 @@ func (c *Controller) syncItem(key string) error {
}

func (c *Controller) processUpdate(group *v1.LocalGroup, key string) error {
return c.handleSubjects(key, group)
return c.handleSubjects(key, convertToGroup(group))
}

func (c *Controller) handleSubjects(key string, group *v1.LocalGroup) error {
rules := c.enforcer.GetFilteredGroupingPolicy(1, authutil.GroupPrefix(group.Spec.TenantID))
func convertToGroup(localGroup *v1.LocalGroup) *v1.Group {
return &v1.Group{
ObjectMeta: metav1.ObjectMeta{
Name: localGroup.ObjectMeta.Name,
},
Spec: v1.GroupSpec{
ID: localGroup.ObjectMeta.Name,
DisplayName: localGroup.Spec.DisplayName,
TenantID: localGroup.Spec.TenantID,
Description: localGroup.Spec.TenantID,
},
Status: v1.GroupStatus{
Users: localGroup.Status.Users,
},
}
}

func (c *Controller) handleSubjects(key string, group *v1.Group) error {
rules := c.enforcer.GetFilteredGroupingPolicy(1, authutil.GroupKey(group.Spec.TenantID, key))
log.Debugf("Get grouping rules for group: %s, %v", group.Name, rules)
var existMembers []string
for _, rule := range rules {
Expand Down Expand Up @@ -265,3 +289,45 @@ func (c *Controller) handleSubjects(key string, group *v1.LocalGroup) error {

return utilerrors.NewAggregate(errs)
}

// pollThirdPartyGroup syncs groups with members into storage
func (c *Controller) pollThirdPartyGroup(stopCh <-chan struct{}) {
timerC := time.NewTicker(groupSyncedPeriod)
for {
select {
case <-timerC.C:
c.resyncGroups()
case <-stopCh:
timerC.Stop()
return
}
}
}

func (c *Controller) resyncGroups() {
defer log.Info("Finished syncing groups with users")

idpList, err := c.client.AuthV1().IdentityProviders().List(metav1.ListOptions{})
if err != nil {
log.Error("List all identity providers failed", log.Err(err))
return
}

for _, idp := range idpList.Items {
tenantSelector := fields.AndSelectors(
fields.OneTermEqualSelector("spec.tenantID", idp.Name),
fields.OneTermEqualSelector(auth.QueryLimitTag, "0"),
)

groups, err := c.client.AuthV1().Groups().List(metav1.ListOptions{FieldSelector: tenantSelector.String()})
if err != nil {
log.Error("List groups for tenant failed", log.String("tenant", idp.Name), log.Err(err))
continue
}
log.Debug("syncing groups for tenantID", log.String("tenant", idp.Name), log.Any("groups", groups))
for _, grp := range groups.Items {
_ = c.handleSubjects(grp.Name, grp.DeepCopy())
}
}

}
7 changes: 7 additions & 0 deletions pkg/auth/registry/group/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions)
// List selects resources in the storage which match to the selector. 'options' can be nil.
func (r *REST) List(ctx context.Context, options *metainternal.ListOptions) (runtime.Object, error) {
_, tenantID := authentication.GetUsernameAndTenantID(ctx)

if tenantID == "" {
tenantID, _ = options.FieldSelector.RequiresExactMatch("spec.tenantID")
if tenantID == "" {
return nil, apierrors.NewBadRequest("List groups must specify tenantID")
}
}
log.Info("store", log.Any("store", identityprovider.IdentityProvidersStore))
idp, ok := identityprovider.IdentityProvidersStore[tenantID]
if !ok {
Expand Down
7 changes: 7 additions & 0 deletions pkg/auth/registry/user/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions)
// List selects resources in the storage which match to the selector. 'options' can be nil.
func (r *REST) List(ctx context.Context, options *metainternal.ListOptions) (runtime.Object, error) {
_, tenantID := authentication.GetUsernameAndTenantID(ctx)
if tenantID == "" {
tenantID, _ = options.FieldSelector.RequiresExactMatch("spec.tenantID")
if tenantID == "" {
return nil, apierrors.NewBadRequest("List groups must specify tenantID")
}
}

idp, ok := identityprovider.IdentityProvidersStore[tenantID]
if !ok {
log.Error("Tenant has no related identity providers", log.String("tenantID", tenantID))
Expand Down
2 changes: 1 addition & 1 deletion pkg/auth/util/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func ParseQueryKeywordAndLimit(options *metainternal.ListOptions) (string, int)
if options.FieldSelector != nil {
keyword, _ = options.FieldSelector.RequiresExactMatch(auth.KeywordQueryTag)
limitStr, _ := options.FieldSelector.RequiresExactMatch(auth.QueryLimitTag)
if li, err := strconv.Atoi(limitStr); err == nil && li > 0 {
if li, err := strconv.Atoi(limitStr); err == nil && li >= 0 {
limit = li
}
}
Expand Down

0 comments on commit ddd6081

Please sign in to comment.