@@ -26,6 +26,7 @@ import (
2626 "net/url"
2727 "strconv"
2828 "strings"
29+ "sync"
2930 "time"
3031
3132 "github.com/go-ldap/ldap/v3"
@@ -71,6 +72,9 @@ type LDAPIdentifierBackend struct {
7172
7273 timeout int
7374 limiter * rate.Limiter
75+
76+ connLock sync.Mutex
77+ conn * ldap.Conn
7478}
7579
7680type ldapAttributeMapping map [string ]string
@@ -115,7 +119,7 @@ func newLdapUser(entryID string, mapping ldapAttributeMapping, entry *ldap.Entry
115119 for n , mapped := range mapping {
116120 // LDAP attribute descriptors / short names are case insensitive. See
117121 // https://tools.ietf.org/html/rfc4512#page-4.
118- if strings .ToLower (attribute .Name ) == strings . ToLower ( mapped ) {
122+ if strings .EqualFold (attribute .Name , mapped ) {
119123 // Check if we need conversion.
120124 switch mapping [fmt .Sprintf ("%s_type" , n )] {
121125 case AttributeValueTypeBinary :
@@ -370,9 +374,8 @@ func (b *LDAPIdentifierBackend) Logon(ctx context.Context, audience, username, p
370374
371375 l , err := b .connect (ctx )
372376 if err != nil {
373- return false , nil , nil , nil , fmt .Errorf ("ldap identifier backend logon connect error: %v" , err )
377+ return false , nil , nil , nil , fmt .Errorf ("ldap identifier backend logon global connect error: %v" , err )
374378 }
375- defer l .Close ()
376379
377380 // Search for the given username.
378381 entry , err := b .searchUsername (l , username , b .attributeMapping .attributes ())
@@ -387,8 +390,15 @@ func (b *LDAPIdentifierBackend) Logon(ctx context.Context, audience, username, p
387390 return false , nil , nil , nil , fmt .Errorf ("ldap identifier backend logon search returned wrong user" )
388391 }
389392
390- // Bind as the user to verify the password.
391- err = l .Bind (entry .DN , password )
393+ // use a temporary connection for the user Bind to verify the password.
394+ ul , err := b .createConnection (ctx , entry .DN , password )
395+ if err != nil {
396+ return false , nil , nil , nil , fmt .Errorf ("ldap identifier backend logon user connect error: %v" , err )
397+ }
398+ // always close the temporary connection
399+ defer ul .Close ()
400+
401+ err = ul .Bind (entry .DN , password )
392402 switch {
393403 case ldap .IsErrorWithCode (err , ldap .LDAPResultInvalidCredentials ):
394404 return false , nil , nil , nil , nil
@@ -431,7 +441,6 @@ func (b *LDAPIdentifierBackend) ResolveUserByUsername(ctx context.Context, usern
431441 if err != nil {
432442 return nil , fmt .Errorf ("ldap identifier backend resolve connect error: %v" , err )
433443 }
434- defer l .Close ()
435444
436445 // Search for the given username.
437446 entry , err := b .searchUsername (l , username , b .attributeMapping .attributes ())
@@ -464,7 +473,6 @@ func (b *LDAPIdentifierBackend) GetUser(ctx context.Context, entryID string, ses
464473 if err != nil {
465474 return nil , fmt .Errorf ("ldap identifier backend get user connect error: %v" , err )
466475 }
467- defer l .Close ()
468476
469477 entry , err := b .getUser (l , entryID , b .attributeMapping .attributes ())
470478 if err != nil {
@@ -517,7 +525,7 @@ func (b *LDAPIdentifierBackend) Name() string {
517525 return ldapIdentifierBackendName
518526}
519527
520- func (b * LDAPIdentifierBackend ) connect (parentCtx context.Context ) (* ldap.Conn , error ) {
528+ func (b * LDAPIdentifierBackend ) createConnection (parentCtx context.Context , bindDN , bindPassword string ) (* ldap.Conn , error ) {
521529 // A timeout for waiting for a limiter slot. The timeout also includes the
522530 // time to connect to the LDAP server which as a consequence means that both
523531 // getting a free slot and establishing the connection are one timeout.
@@ -550,9 +558,8 @@ func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn,
550558
551559 l .Start ()
552560
553- // Bind with general user (which is preferably read only).
554- if b .bindDN != "" {
555- err = l .Bind (b .bindDN , b .bindPassword )
561+ if bindDN != "" {
562+ err = l .Bind (bindDN , bindPassword )
556563 if err != nil {
557564 return nil , err
558565 }
@@ -561,6 +568,20 @@ func (b *LDAPIdentifierBackend) connect(parentCtx context.Context) (*ldap.Conn,
561568 return l , nil
562569}
563570
571+ // connect establishes a global connection that is used for all requests except the user Bind to check the password in the Logon call.
572+ func (b * LDAPIdentifierBackend ) connect (parentCtx context.Context ) (* ldap.Conn , error ) {
573+ b .connLock .Lock ()
574+ defer b .connLock .Unlock ()
575+
576+ if b .conn != nil && ! b .conn .IsClosing () {
577+ return b .conn , nil
578+ }
579+ var err error
580+ b .conn , err = b .createConnection (parentCtx , b .bindDN , b .bindPassword )
581+
582+ return b .conn , err
583+ }
584+
564585func (b * LDAPIdentifierBackend ) searchUsername (l * ldap.Conn , username string , attributes []string ) (* ldap.Entry , error ) {
565586 base , filter := b .baseAndSearchFilterFromUsername (username )
566587 // Search for the given username.
0 commit comments