forked from openshift/oc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ldapinterface.go
193 lines (162 loc) · 7.24 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package rfc2307
import (
"fmt"
"gopkg.in/ldap.v2"
"k8s.io/apimachinery/pkg/util/sets"
"github.com/openshift/library-go/pkg/security/ldapclient"
ldapquery "github.com/openshift/library-go/pkg/security/ldapquery"
"github.com/openshift/library-go/pkg/security/ldaputil"
"github.com/openshift/oc/pkg/helpers/groupsync/groupdetector"
"github.com/openshift/oc/pkg/helpers/groupsync/interfaces"
"github.com/openshift/oc/pkg/helpers/groupsync/syncerror"
)
// NewLDAPInterface builds a new LDAPInterface using a schema-appropriate config
func NewLDAPInterface(clientConfig ldapclient.Config,
groupQuery ldapquery.LDAPQueryOnAttribute,
groupNameAttributes []string,
groupMembershipAttributes []string,
userQuery ldapquery.LDAPQueryOnAttribute,
userNameAttributes []string,
errorHandler syncerror.Handler) *LDAPInterface {
return &LDAPInterface{
clientConfig: clientConfig,
groupQuery: groupQuery,
groupNameAttributes: groupNameAttributes,
groupMembershipAttributes: groupMembershipAttributes,
userQuery: userQuery,
userNameAttributes: userNameAttributes,
cachedUsers: map[string]*ldap.Entry{},
cachedGroups: map[string]*ldap.Entry{},
errorHandler: errorHandler,
}
}
// LDAPInterface extracts the member list of an LDAP group entry from an LDAP server
// with first-class LDAP entries for groups. The LDAPInterface is *NOT* thread-safe.
type LDAPInterface struct {
// clientConfig holds LDAP connection information
clientConfig ldapclient.Config
// groupQuery holds the information necessary to make an LDAP query for a specific first-class group entry on the LDAP server
groupQuery ldapquery.LDAPQueryOnAttribute
// groupNameAttributes defines which attributes on an LDAP group entry will be interpreted as its name to use for an OpenShift group
groupNameAttributes []string
// groupMembershipAttributes defines which attributes on an LDAP group entry will be interpreted as its members ldapUserUID
groupMembershipAttributes []string
// userQuery holds the information necessary to make an LDAP query for a specific first-class user entry on the LDAP server
userQuery ldapquery.LDAPQueryOnAttribute
// UserNameAttributes defines which attributes on an LDAP user entry will be interpreted as its' name
userNameAttributes []string
// cachedGroups holds the result of group queries for later reference, indexed on group UID
// e.g. this will map an LDAP group UID to the LDAP entry returned from the query made using it
cachedGroups map[string]*ldap.Entry
// cachedUsers holds the result of user queries for later reference, indexed on user UID
// e.g. this will map an LDAP user UID to the LDAP entry returned from the query made using it
cachedUsers map[string]*ldap.Entry
// errorHandler handles errors that occur
errorHandler syncerror.Handler
}
// The LDAPInterface must conform to the following interfaces
var _ interfaces.LDAPMemberExtractor = &LDAPInterface{}
var _ interfaces.LDAPGroupGetter = &LDAPInterface{}
var _ interfaces.LDAPGroupLister = &LDAPInterface{}
// ExtractMembers returns the LDAP member entries for a group specified with a ldapGroupUID
func (e *LDAPInterface) ExtractMembers(ldapGroupUID string) ([]*ldap.Entry, error) {
// get group entry from LDAP
group, err := e.GroupEntryFor(ldapGroupUID)
if err != nil {
return nil, err
}
// extract member UIDs from group entry
var ldapMemberUIDs []string
for _, attribute := range e.groupMembershipAttributes {
ldapMemberUIDs = append(ldapMemberUIDs, group.GetAttributeValues(attribute)...)
}
members := []*ldap.Entry{}
// find members on LDAP server or in cache
for _, ldapMemberUID := range ldapMemberUIDs {
memberEntry, err := e.userEntryFor(ldapMemberUID)
if err == nil {
members = append(members, memberEntry)
continue
}
err = syncerror.NewMemberLookupError(ldapGroupUID, ldapMemberUID, err)
handled, fatalErr := e.errorHandler.HandleError(err)
if fatalErr != nil {
return nil, fatalErr
}
if !handled {
return nil, err
}
}
return members, nil
}
// GroupEntryFor 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 *LDAPInterface) 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 = ldapquery.QueryForUniqueEntry(e.clientConfig, searchRequest)
if err != nil {
return nil, err
}
e.cachedGroups[ldapGroupUID] = group
return group, 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 *LDAPInterface) ListGroups() ([]string, error) {
searchRequest := e.groupQuery.LDAPQuery.NewSearchRequest(e.requiredGroupAttributes())
groups, err := ldapquery.QueryForEntries(e.clientConfig, searchRequest)
if err != nil {
return nil, err
}
ldapGroupUIDs := []string{}
for _, group := range groups {
ldapGroupUID := ldaputil.GetAttributeValue(group, []string{e.groupQuery.QueryAttribute})
if len(ldapGroupUID) == 0 {
return nil, fmt.Errorf("unable to find LDAP group UID for %s", group.DN)
}
e.cachedGroups[ldapGroupUID] = group
ldapGroupUIDs = append(ldapGroupUIDs, ldapGroupUID)
}
return ldapGroupUIDs, nil
}
func (e *LDAPInterface) requiredGroupAttributes() []string {
allAttributes := sets.NewString(e.groupNameAttributes...) // these attributes will be used for a future openshift group name mapping
allAttributes.Insert(e.groupMembershipAttributes...) // these attribute are used for finding group members
allAttributes.Insert(e.groupQuery.QueryAttribute) // this is used for extracting the group UID (otherwise an entry isn't self-describing)
return allAttributes.List()
}
// userEntryFor 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
func (e *LDAPInterface) userEntryFor(ldapUserUID string) (user *ldap.Entry, err error) {
user, exists := e.cachedUsers[ldapUserUID]
if exists {
return user, nil
}
searchRequest, err := e.userQuery.NewSearchRequest(ldapUserUID, e.requiredUserAttributes())
if err != nil {
return nil, err
}
user, err = ldapquery.QueryForUniqueEntry(e.clientConfig, searchRequest)
if err != nil {
return nil, err
}
e.cachedUsers[ldapUserUID] = user
return user, nil
}
func (e *LDAPInterface) requiredUserAttributes() []string {
allAttributes := sets.NewString(e.userNameAttributes...) // these attributes will be used for a future openshift user name mapping
allAttributes.Insert(e.userQuery.QueryAttribute) // this is used for extracting the user UID (otherwise an entry isn't self-describing)
return allAttributes.List()
}
// Exists determines if a group idenified with its LDAP group UID exists on the LDAP server
func (e *LDAPInterface) Exists(ldapGroupUID string) (bool, error) {
return groupdetector.NewGroupBasedDetector(e).Exists(ldapGroupUID)
}