Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Augmented AD ldap sync #5086

Merged
merged 3 commits into from
Oct 13, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
enhanced active directory ldap sync
  • Loading branch information
deads2k committed Oct 13, 2015
commit dca6bb689b8168cd5d9d9d6e138680f01fc2d47d
94 changes: 94 additions & 0 deletions pkg/cmd/experimental/syncgroups/ad/augmented_ldapinterface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package ad

import (
"k8s.io/kubernetes/pkg/util/sets"

"github.com/go-ldap/ldap"

"github.com/openshift/origin/pkg/auth/ldaputil"
ldapinterfaces "github.com/openshift/origin/pkg/cmd/experimental/syncgroups/interfaces"
)

// NewLDAPInterface builds a new LDAPInterface using a schema-appropriate config
<<<<<<< HEAD:pkg/cmd/experimental/syncgroups/ad/enhanced_ldapinterface.go
func NewEnhancedADLDAPInterface(clientConfig ldaputil.LDAPClientConfig,
=======
func NewAugmentedADLDAPInterface(clientConfig *ldaputil.LDAPClientConfig,
>>>>>>> d67cc33... main:pkg/cmd/experimental/syncgroups/ad/augmented_ldapinterface.go
userQuery ldaputil.LDAPQueryOnAttribute,
groupMembershipAttributes []string,
userNameAttributes []string,
groupQuery ldaputil.LDAPQueryOnAttribute,
<<<<<<< HEAD:pkg/cmd/experimental/syncgroups/ad/enhanced_ldapinterface.go
groupNameAttributes []string) EnhancedADLDAPInterface {

return EnhancedADLDAPInterface{
=======
groupNameAttributes []string) *AugmentedADLDAPInterface {

return &AugmentedADLDAPInterface{
>>>>>>> d67cc33... main:pkg/cmd/experimental/syncgroups/ad/augmented_ldapinterface.go
ADLDAPInterface: NewADLDAPInterface(clientConfig, userQuery, groupMembershipAttributes, userNameAttributes),
groupQuery: groupQuery,
groupNameAttributes: groupNameAttributes,
cachedGroups: map[string]*ldap.Entry{},
}
}

// LDAPInterface extracts the member list of an LDAP user entry from an LDAP server
// with first-class LDAP entries for users and group. Group membership is on the user. The LDAPInterface is *NOT* thread-safe.
// The LDAPInterface satisfies:
// - LDAPMemberExtractor
// - LDAPGroupGetter
// - LDAPGroupLister
<<<<<<< HEAD:pkg/cmd/experimental/syncgroups/ad/enhanced_ldapinterface.go
type EnhancedADLDAPInterface struct {
ADLDAPInterface
=======
type AugmentedADLDAPInterface struct {
*ADLDAPInterface
>>>>>>> d67cc33... main:pkg/cmd/experimental/syncgroups/ad/augmented_ldapinterface.go

// groupQuery holds the information necessary to make an LDAP query for a specific
// first-class group entry on the LDAP server
groupQuery ldaputil.LDAPQueryOnAttribute
// groupNameAttributes defines which attributes on an LDAP group entry will be interpreted as its name to use for an OpenShift group
groupNameAttributes []string

cachedGroups map[string]*ldap.Entry
}

var _ ldapinterfaces.LDAPMemberExtractor = &AugmentedADLDAPInterface{}
var _ ldapinterfaces.LDAPGroupGetter = &AugmentedADLDAPInterface{}
var _ ldapinterfaces.LDAPGroupLister = &AugmentedADLDAPInterface{}

// GroupFor returns an LDAP group entry for the given group UID by searching the internal cache
// of the LDAPInterface first, then sending an LDAP query if the cache did not contain the entry.
// This also satisfies the LDAPGroupGetter interface
func (e *AugmentedADLDAPInterface) GroupEntryFor(ldapGroupUID string) (*ldap.Entry, error) {
group, exists := e.cachedGroups[ldapGroupUID]
if exists {
return group, nil
}

searchRequest, err := e.groupQuery.NewSearchRequest(ldapGroupUID, e.requiredGroupAttributes())
if err != nil {
return nil, err
}

group, err = ldaputil.QueryForUniqueEntry(e.clientConfig, searchRequest)
if err != nil {
return nil, err
}

// cache for annotation extraction
e.cachedGroups[ldapGroupUID] = group
return group, nil
}

func (e *AugmentedADLDAPInterface) requiredGroupAttributes() []string {
allAttributes := sets.NewString(e.groupNameAttributes...) // these attributes will be used for a future openshift group name mapping
allAttributes.Insert(e.groupQuery.QueryAttribute) // this is used for extracting the group UID (otherwise an entry isn't self-describing)

return allAttributes.List()
}
29 changes: 15 additions & 14 deletions pkg/cmd/experimental/syncgroups/ad/ldapinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import (
ldapinterfaces "github.com/openshift/origin/pkg/cmd/experimental/syncgroups/interfaces"
)

// NewLDAPInterface builds a new LDAPInterface using a schema-appropriate config
func NewLDAPInterface(clientConfig ldaputil.LDAPClientConfig,
// NewADLDAPInterface builds a new ADLDAPInterface using a schema-appropriate config
func NewADLDAPInterface(clientConfig ldaputil.LDAPClientConfig,
userQuery ldaputil.LDAPQueryOnAttribute,
groupMembershipAttributes []string,
userNameAttributes []string) LDAPInterface {
return LDAPInterface{
userNameAttributes []string) ADLDAPInterface {

return ADLDAPInterface{
clientConfig: clientConfig,
userQuery: userQuery,
userNameAttributes: userNameAttributes,
Expand All @@ -25,12 +26,12 @@ func NewLDAPInterface(clientConfig ldaputil.LDAPClientConfig,
}
}

// LDAPInterface extracts the member list of an LDAP group entry from an LDAP server
// with first-class LDAP entries for user only. The LDAPInterface is *NOT* thread-safe.
// The LDAPInterface satisfies:
// ADLDAPInterface extracts the member list of an LDAP group entry from an LDAP server
// with first-class LDAP entries for user only. The ADLDAPInterface is *NOT* thread-safe.
// The ADLDAPInterface satisfies:
// - LDAPMemberExtractor
// - LDAPGroupLister
type LDAPInterface struct {
type ADLDAPInterface struct {
// clientConfig holds LDAP connection information
clientConfig ldaputil.LDAPClientConfig

Expand All @@ -46,11 +47,11 @@ type LDAPInterface struct {
ldapGroupToLDAPMembers map[string][]*ldap.Entry
}

var _ ldapinterfaces.LDAPMemberExtractor = &LDAPInterface{}
var _ ldapinterfaces.LDAPGroupLister = &LDAPInterface{}
var _ ldapinterfaces.LDAPMemberExtractor = &ADLDAPInterface{}
var _ ldapinterfaces.LDAPGroupLister = &ADLDAPInterface{}

// ExtractMembers returns the LDAP member entries for a group specified with a ldapGroupUID
func (e *LDAPInterface) ExtractMembers(ldapGroupUID string) ([]*ldap.Entry, error) {
func (e *ADLDAPInterface) ExtractMembers(ldapGroupUID string) ([]*ldap.Entry, error) {
// if we already have it cached, return the cached value
if members, present := e.ldapGroupToLDAPMembers[ldapGroupUID]; present {
return members, nil
Expand Down Expand Up @@ -90,7 +91,7 @@ func (e *LDAPInterface) ExtractMembers(ldapGroupUID string) ([]*ldap.Entry, erro

// ListGroups queries for all groups as configured with the common group filter and returns their
// LDAP group UIDs. This also satisfies the LDAPGroupLister interface
func (e *LDAPInterface) ListGroups() ([]string, error) {
func (e *ADLDAPInterface) ListGroups() ([]string, error) {
if err := e.populateCache(); err != nil {
return nil, err
}
Expand All @@ -100,7 +101,7 @@ func (e *LDAPInterface) ListGroups() ([]string, error) {

// populateCache queries all users to build a map of all the groups. If the cache has already been
// populated, this is a no-op.
func (e *LDAPInterface) populateCache() error {
func (e *ADLDAPInterface) populateCache() error {
if e.cachePopulated {
return nil
}
Expand Down Expand Up @@ -145,7 +146,7 @@ func isEntryPresent(haystack []*ldap.Entry, needle *ldap.Entry) bool {
return false
}

func (e *LDAPInterface) requiredUserAttributes() []string {
func (e *ADLDAPInterface) requiredUserAttributes() []string {
attributes := sets.NewString(e.groupMembershipAttributes...)
attributes.Insert(e.userNameAttributes...)

Expand Down
43 changes: 39 additions & 4 deletions pkg/cmd/experimental/syncgroups/syncgroups.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ func (o *SyncGroupsOptions) Run(cmd *cobra.Command, f *clientcmd.Factory) error
Err: os.Stderr,
}

syncer.GroupNameMapper = &DNLDAPGroupNameMapper{}
if len(o.Config.LDAPGroupUIDToOpenShiftGroupNameMapping) > 0 {
syncer.GroupNameMapper = NewUserDefinedGroupNameMapper(o.Config.LDAPGroupUIDToOpenShiftGroupNameMapping)
}
Expand Down Expand Up @@ -297,25 +298,59 @@ func (o *SyncGroupsOptions) Run(cmd *cobra.Command, f *clientcmd.Factory) error
}

// the schema-specific ldapInterface is built from the config
ldapInterface := ad.NewLDAPInterface(clientConfig,
ldapInterface := ad.NewADLDAPInterface(clientConfig,
userQuery,
o.Config.ActiveDirectoryConfig.GroupMembershipAttributes,
o.Config.ActiveDirectoryConfig.UserNameAttributes)

// The LDAPInterface knows how to extract group members
syncer.GroupMemberExtractor = &ldapInterface

// In order to build the groupLister, we need to know about the group sync scope and source:
syncer.GroupLister = getGroupLister(o.Scope,
o.Source,
o.WhitelistContents,
o.GroupInterface,
clientConfig.Host,
&ldapInterface)

case o.Config.AugmentedActiveDirectoryConfig != nil:
syncer.UserNameMapper = NewUserNameMapper(o.Config.AugmentedActiveDirectoryConfig.UserNameAttributes)

userQuery, err := ldaputil.NewLDAPQueryOnAttribute(o.Config.AugmentedActiveDirectoryConfig.AllUsersQuery, "dn")
if err != nil {
return err
}

groupQuery, err := ldaputil.NewLDAPQueryOnAttribute(o.Config.AugmentedActiveDirectoryConfig.AllGroupsQuery, o.Config.RFC2307Config.GroupUIDAttribute)
if err != nil {
return err
}

// the schema-specific ldapInterface is built from the config
ldapInterface := ad.NewEnhancedADLDAPInterface(clientConfig,
userQuery,
o.Config.AugmentedActiveDirectoryConfig.GroupMembershipAttributes,
o.Config.AugmentedActiveDirectoryConfig.UserNameAttributes,
groupQuery,
o.Config.AugmentedActiveDirectoryConfig.GroupNameAttributes,
)

// The LDAPInterface knows how to extract group members
syncer.GroupMemberExtractor = &ldapInterface

// In order to build the GroupNameMapper, we need to know if the user defined a hard mapping
// or one based on LDAP group entry attributes
if syncer.GroupNameMapper == nil {
syncer.GroupNameMapper = &DNLDAPGroupNameMapper{}
if o.Config.AugmentedActiveDirectoryConfig.GroupNameAttributes == nil {
return errors.New("not enough information to build a group name mapper")
}
syncer.GroupNameMapper = NewEntryAttributeGroupNameMapper(o.Config.AugmentedActiveDirectoryConfig.GroupNameAttributes, &ldapInterface)
}

// In order to build the groupLister, we need to know about the group sync scope and source:
syncer.GroupLister = getGroupLister(o.Source, o.WhitelistContents, o.GroupInterface, clientConfig.Host, &ldapInterface)

case o.Config.AugmentedActiveDirectoryConfig != nil:
fallthrough
default:
return fmt.Errorf("invalid schema-specific query template type: %v", o.Config.RFC2307Config)
}
Expand Down
2 changes: 1 addition & 1 deletion test/extended/authentication.sh
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ oc login -u system:admin -n default

echo "[INFO] Running extended tests"

schema=('rfc2307' 'ad')
schema=('rfc2307' 'ad' 'augmented-ad')

for (( i=0; i<${#schema[@]}; i++ )); do
current_schema=${schema[$i]}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cn=group1,ou=groups,ou=adextended,dc=example,dc=com
cn=group2,ou=groups,ou=adextended,dc=example,dc=com
cn=group3,ou=groups,ou=adextended,dc=example,dc=com
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extended-group1
extended-group2
extended-group3
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
kind: LDAPSyncConfig
apiVersion: v1
url: ldap://LDAP_SERVICE_IP:389
insecure: true
augmentedActiveDirectory:
usersQuery:
baseDN: "ou=people,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=inetOrgPerson)
groupMembershipAttributes: [ testMemberOf ]
userNameAttributes: [ dn ]
groupsQuery:
baseDN: "ou=groups,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=groupOfNames)
groupUIDAttribute: dn
groupNameAttributes: [ dn ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
kind: LDAPSyncConfig
apiVersion: v1
url: ldap://LDAP_SERVICE_IP:389
insecure: true
groupUIDNameMapping:
cn=group1,ou=groups,ou=adextended,dc=example,dc=com: firstgroup
cn=group2,ou=groups,ou=adextended,dc=example,dc=com: secondgroup
cn=group3,ou=groups,ou=adextended,dc=example,dc=com: thirdgroup
augmentedActiveDirectory:
usersQuery:
baseDN: "ou=people,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=inetOrgPerson)
groupMembershipAttributes: [ testMemberOf ]
userNameAttributes: [ mail ]
groupsQuery:
baseDN: "ou=groups,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=groupOfNames)
groupUIDAttribute: dn
groupNameAttributes: [ cn ]
19 changes: 19 additions & 0 deletions test/extended/authentication/ldap/augmented-ad/sync-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
kind: LDAPSyncConfig
apiVersion: v1
url: ldap://LDAP_SERVICE_IP:389
insecure: true
augmentedActiveDirectory:
usersQuery:
baseDN: "ou=people,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=inetOrgPerson)
groupMembershipAttributes: [ testMemberOf ]
userNameAttributes: [ mail ]
groupsQuery:
baseDN: "ou=groups,ou=adextended,dc=example,dc=com"
scope: sub
derefAliases: never
filter: (objectclass=groupOfNames)
groupUIDAttribute: dn
groupNameAttributes: [ cn ]
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
apiVersion: v1
kind: Group
metadata:
annotations:
openshift.io/ldap.uid: cn=group1,ou=groups,ou=adextended,dc=example,dc=com
openshift.io/ldap.url: LDAP_SERVICE_IP:389
creationTimestamp: null
name: extended-group1
users:
- person1smith@example.com
- 'person2mith@example.com '
- person3smith@example.com
- person4smith@example.com
- person5smith@example.com
apiVersion: v1
kind: Group
metadata:
annotations:
openshift.io/ldap.uid: cn=group2,ou=groups,ou=adextended,dc=example,dc=com
openshift.io/ldap.url: LDAP_SERVICE_IP:389
creationTimestamp: null
name: extended-group2
users:
- person1smith@example.com
- 'person2mith@example.com '
- person3smith@example.com
apiVersion: v1
kind: Group
metadata:
annotations:
openshift.io/ldap.uid: cn=group3,ou=groups,ou=adextended,dc=example,dc=com
openshift.io/ldap.url: LDAP_SERVICE_IP:389
creationTimestamp: null
name: extended-group3
users:
- person1smith@example.com
- person5smith@example.com
Loading