1717package authn
1818
1919import (
20- "bytes"
2120 "crypto/tls"
2221 "crypto/x509"
2322 "fmt"
@@ -28,17 +27,21 @@ import (
2827 "github.com/cesanta/glog"
2928)
3029
30+ type LabelMap struct {
31+ Attribute string `yaml:"attribute,omitempty"`
32+ ParseCN bool `yaml:"parse_cn,omitempty"`
33+ }
34+
3135type LDAPAuthConfig struct {
32- Addr string `yaml:"addr,omitempty"`
33- TLS string `yaml:"tls,omitempty"`
34- InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"`
35- CACertificate string `yaml:"ca_certificate,omitempty"`
36- Base string `yaml:"base,omitempty"`
37- Filter string `yaml:"filter,omitempty"`
38- BindDN string `yaml:"bind_dn,omitempty"`
39- BindPasswordFile string `yaml:"bind_password_file,omitempty"`
40- GroupBaseDN string `yaml:"group_base_dn,omitempty"`
41- GroupFilter string `yaml:"group_filter,omitempty"`
36+ Addr string `yaml:"addr,omitempty"`
37+ TLS string `yaml:"tls,omitempty"`
38+ InsecureTLSSkipVerify bool `yaml:"insecure_tls_skip_verify,omitempty"`
39+ CACertificate string `yaml:"ca_certificate,omitempty"`
40+ Base string `yaml:"base,omitempty"`
41+ Filter string `yaml:"filter,omitempty"`
42+ BindDN string `yaml:"bind_dn,omitempty"`
43+ BindPasswordFile string `yaml:"bind_password_file,omitempty"`
44+ LabelMaps map [string ]LabelMap `yaml:"labels,omitempty"`
4245}
4346
4447type LDAPAuth struct {
@@ -73,13 +76,20 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool,
7376 account = la .escapeAccountInput (account )
7477
7578 filter := la .getFilter (account )
76- accountEntryDN , uSearchErr := la .ldapSearch (l , & la .config .Base , & filter , & []string {})
79+
80+ labelAttributes , labelsConfigErr := la .getLabelAttributes ()
81+ if labelsConfigErr != nil {
82+ return false , nil , labelsConfigErr
83+ }
84+
85+ accountEntryDN , entryAttrMap , uSearchErr := la .ldapSearch (l , & la .config .Base , & filter , & labelAttributes )
7786 if uSearchErr != nil {
7887 return false , nil , uSearchErr
7988 }
8089 if accountEntryDN == "" {
8190 return false , nil , NoMatch // User does not exist
8291 }
92+
8393 // Bind as the user to verify their password
8494 if len (accountEntryDN ) > 0 {
8595 err := l .Bind (accountEntryDN , string (password ))
@@ -95,7 +105,13 @@ func (la *LDAPAuth) Authenticate(account string, password PasswordString) (bool,
95105 return false , nil , bindErr
96106 }
97107
98- return true , nil , nil
108+ // Extract labels from the attribute values
109+ labels , labelsExtractErr := la .getLabelsFromMap (entryAttrMap )
110+ if labelsExtractErr != nil {
111+ return false , nil , labelsExtractErr
112+ }
113+
114+ return true , labels , nil
99115}
100116
101117func (la * LDAPAuth ) bindReadOnlyUser (l * ldap.Conn ) error {
@@ -185,9 +201,9 @@ func (la *LDAPAuth) getFilter(account string) string {
185201
186202//ldap search and return required attributes' value from searched entries
187203//default return entry's DN value if you leave attrs array empty
188- func (la * LDAPAuth ) ldapSearch (l * ldap.Conn , baseDN * string , filter * string , attrs * []string ) (string , error ) {
204+ func (la * LDAPAuth ) ldapSearch (l * ldap.Conn , baseDN * string , filter * string , attrs * []string ) (string , map [ string ][] string , error ) {
189205 if l == nil {
190- return "" , fmt .Errorf ("No ldap connection!" )
206+ return "" , nil , fmt .Errorf ("No ldap connection!" )
191207 }
192208 glog .V (2 ).Infof ("Searching...basedDN:%s, filter:%s" , * baseDN , * filter )
193209 searchRequest := ldap .NewSearchRequest (
@@ -198,30 +214,82 @@ func (la *LDAPAuth) ldapSearch(l *ldap.Conn, baseDN *string, filter *string, att
198214 nil )
199215 sr , err := l .Search (searchRequest )
200216 if err != nil {
201- return "" , err
217+ return "" , nil , err
202218 }
203219
204220 if len (sr .Entries ) == 0 {
205- return "" , nil // User does not exist
221+ return "" , nil , nil // User does not exist
206222 } else if len (sr .Entries ) > 1 {
207- return "" , fmt .Errorf ("Too many entries returned." )
223+ return "" , nil , fmt .Errorf ("Too many entries returned." )
208224 }
209225
210- var buffer bytes.Buffer
226+ attributes := make (map [string ][]string )
227+ var entryDn string
211228 for _ , entry := range sr .Entries {
229+ entryDn = entry .DN
212230 if len (* attrs ) == 0 {
213- glog .V (2 ).Infof ("Entry DN = %s" , entry .DN )
214- buffer .WriteString (entry .DN )
231+ glog .V (2 ).Infof ("Entry DN = %s" , entryDn )
215232 } else {
216233 for _ , attr := range * attrs {
217- values := strings .Join (entry .GetAttributeValues (attr ), " " )
218- glog .V (2 ).Infof ("Entry %s = %s" , attr , values )
219- buffer .WriteString (values )
234+ values := entry .GetAttributeValues (attr )
235+ glog .V (2 ).Infof ("Entry %s = %s" , attr , strings .Join (values , "\n " ))
236+ attributes [attr ] = values
237+ }
238+ }
239+ }
240+
241+ return entryDn , attributes , nil
242+ }
243+
244+ func (la * LDAPAuth ) getLabelAttributes () ([]string , error ) {
245+ labelAttributes := make ([]string , len (la .config .LabelMaps ))
246+ i := 0
247+ for key , mapping := range la .config .LabelMaps {
248+ if mapping .Attribute == "" {
249+ return nil , fmt .Errorf ("Label %s is missing 'attribute' to map from" , key )
250+ }
251+ labelAttributes [i ] = mapping .Attribute
252+ i ++
253+ }
254+ return labelAttributes , nil
255+ }
256+
257+ func (la * LDAPAuth ) getLabelsFromMap (attrMap map [string ][]string ) (map [string ][]string , error ) {
258+ labels := make (map [string ][]string )
259+ for key , mapping := range la .config .LabelMaps {
260+ if mapping .Attribute == "" {
261+ return nil , fmt .Errorf ("Label %s is missing 'attribute' to map from" , key )
262+ }
263+
264+ mappingValues := attrMap [mapping .Attribute ]
265+ if mappingValues != nil {
266+ if mapping .ParseCN {
267+ // shorten attribute to its common name
268+ for i , value := range mappingValues {
269+ cn := la .getCNFromDN (value )
270+ mappingValues [i ] = cn
271+ }
272+ }
273+ labels [key ] = mappingValues
274+ }
275+ }
276+ return labels , nil
277+ }
278+
279+ func (la * LDAPAuth ) getCNFromDN (dn string ) string {
280+ parsedDN , err := ldap .ParseDN (dn )
281+ if err != nil || len (parsedDN .RDNs ) > 0 {
282+ for _ , rdn := range parsedDN .RDNs {
283+ for _ , rdnAttr := range rdn .Attributes {
284+ if rdnAttr .Type == "CN" {
285+ return rdnAttr .Value
286+ }
220287 }
221288 }
222289 }
223290
224- return buffer .String (), nil
291+ // else try using raw DN
292+ return dn
225293}
226294
227295func (la * LDAPAuth ) Stop () {
0 commit comments