forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ldapinterface.go
156 lines (125 loc) · 5.27 KB
/
ldapinterface.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package ad
import (
"gopkg.in/ldap.v2"
"k8s.io/apimachinery/pkg/util/sets"
"github.com/openshift/origin/pkg/auth/ldaputil"
"github.com/openshift/origin/pkg/auth/ldaputil/ldapclient"
"github.com/openshift/origin/pkg/oc/admin/groups/sync/groupdetector"
"github.com/openshift/origin/pkg/oc/admin/groups/sync/interfaces"
)
// NewADLDAPInterface builds a new ADLDAPInterface using a schema-appropriate config
func NewADLDAPInterface(clientConfig ldapclient.Config,
userQuery ldaputil.LDAPQuery,
groupMembershipAttributes []string,
userNameAttributes []string) *ADLDAPInterface {
return &ADLDAPInterface{
clientConfig: clientConfig,
userQuery: userQuery,
userNameAttributes: userNameAttributes,
groupMembershipAttributes: groupMembershipAttributes,
ldapGroupToLDAPMembers: map[string][]*ldap.Entry{},
}
}
// 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.
type ADLDAPInterface struct {
// clientConfig holds LDAP connection information
clientConfig ldapclient.Config
// userQuery holds the information necessary to make an LDAP query for all first-class user entries on the LDAP server
userQuery ldaputil.LDAPQuery
// groupMembershipAttributes defines which attributes on an LDAP user entry will be interpreted as its ldapGroupUID
groupMembershipAttributes []string
// UserNameAttributes defines which attributes on an LDAP user entry will be interpreted as its name
userNameAttributes []string
// cacheFullyPopulated determines if the cache has been fully populated
// populateCache() will populate it fully, specific calls to ExtractMembers() will not
cacheFullyPopulated bool
// ldapGroupToLDAPMembers holds the result of user queries for later reference, indexed on group UID
// e.g. this will map all LDAP users to the LDAP group UID whose entry returned them
ldapGroupToLDAPMembers map[string][]*ldap.Entry
}
// The LDAPInterface must conform to the following interfaces
var _ interfaces.LDAPMemberExtractor = &ADLDAPInterface{}
var _ interfaces.LDAPGroupLister = &ADLDAPInterface{}
// ExtractMembers returns the LDAP member entries for a group specified with a ldapGroupUID
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
}
// This happens in cases where we did not list out every group. In that case, we're going to be asked about specific groups.
usersInGroup := []*ldap.Entry{}
// check for all users with ldapGroupUID in any of the allowed member attributes
for _, currAttribute := range e.groupMembershipAttributes {
currQuery := ldaputil.LDAPQueryOnAttribute{LDAPQuery: e.userQuery, QueryAttribute: currAttribute}
searchRequest, err := currQuery.NewSearchRequest(ldapGroupUID, e.requiredUserAttributes())
if err != nil {
return nil, err
}
currEntries, err := ldaputil.QueryForEntries(e.clientConfig, searchRequest)
if err != nil {
return nil, err
}
for _, currEntry := range currEntries {
if !isEntryPresent(usersInGroup, currEntry) {
usersInGroup = append(usersInGroup, currEntry)
}
}
}
e.ldapGroupToLDAPMembers[ldapGroupUID] = usersInGroup
return usersInGroup, nil
}
// 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 *ADLDAPInterface) ListGroups() ([]string, error) {
if err := e.populateCache(); err != nil {
return nil, err
}
return sets.StringKeySet(e.ldapGroupToLDAPMembers).List(), nil
}
// 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 *ADLDAPInterface) populateCache() error {
if e.cacheFullyPopulated {
return nil
}
searchRequest := e.userQuery.NewSearchRequest(e.requiredUserAttributes())
userEntries, err := ldaputil.QueryForEntries(e.clientConfig, searchRequest)
if err != nil {
return err
}
for _, userEntry := range userEntries {
if userEntry == nil {
continue
}
for _, groupAttribute := range e.groupMembershipAttributes {
for _, groupUID := range userEntry.GetAttributeValues(groupAttribute) {
if _, exists := e.ldapGroupToLDAPMembers[groupUID]; !exists {
e.ldapGroupToLDAPMembers[groupUID] = []*ldap.Entry{}
}
if !isEntryPresent(e.ldapGroupToLDAPMembers[groupUID], userEntry) {
e.ldapGroupToLDAPMembers[groupUID] = append(e.ldapGroupToLDAPMembers[groupUID], userEntry)
}
}
}
}
e.cacheFullyPopulated = true
return nil
}
func isEntryPresent(haystack []*ldap.Entry, needle *ldap.Entry) bool {
for _, curr := range haystack {
if curr.DN == needle.DN {
return true
}
}
return false
}
func (e *ADLDAPInterface) requiredUserAttributes() []string {
allAttributes := sets.NewString(e.userNameAttributes...)
allAttributes.Insert(e.groupMembershipAttributes...)
return allAttributes.List()
}
// Exists determines if a group idenified with its LDAP group UID exists on the LDAP server
func (e *ADLDAPInterface) Exists(ldapGrouUID string) (bool, error) {
return groupdetector.NewMemberBasedDetector(e).Exists(ldapGrouUID)
}