diff --git a/pkg/services/ldap/ldap.go b/pkg/services/ldap/ldap.go index 0e4404340b5..c35f274b7e3 100644 --- a/pkg/services/ldap/ldap.go +++ b/pkg/services/ldap/ldap.go @@ -140,15 +140,19 @@ func (server *Server) Login(query *models.LoginUserQuery) ( *models.ExternalUserInfo, error, ) { var err error + var authAndBind bool - // Do we need to authenticate the "admin" user first? - // Admin user should have access for the user search in LDAP server + // Check if we can use a search user if server.shouldAuthAdmin() { if err := server.AuthAdmin(); err != nil { return nil, err } - - // Or if anyone can perform the search in LDAP? + } else if server.shouldSingleBind() { + authAndBind = true + err = server.Auth(server.singleBindDN(query.Username), query.Password) + if err != nil { + return nil, err + } } else { err := server.Connection.UnauthenticatedBind(server.Config.BindDN) if err != nil { @@ -173,15 +177,25 @@ func (server *Server) Login(query *models.LoginUserQuery) ( return nil, err } - // Authenticate user - err = server.Auth(user.AuthId, query.Password) - if err != nil { - return nil, err + if !authAndBind { + // Authenticate user + err = server.Auth(user.AuthId, query.Password) + if err != nil { + return nil, err + } } return user, nil } +func (server *Server) singleBindDN(username string) string { + return fmt.Sprintf(server.Config.BindDN, username) +} + +func (server *Server) shouldSingleBind() bool { + return strings.Contains(server.Config.BindDN, "%s") +} + // getUsersIteration is a helper function for Users() method. // It divides the users by equal parts for the anticipated requests func getUsersIteration(logins []string, fn func(int, int) error) error { diff --git a/pkg/services/ldap/ldap_login_test.go b/pkg/services/ldap/ldap_login_test.go index 1828eeb9d7b..6aee77e4863 100644 --- a/pkg/services/ldap/ldap_login_test.go +++ b/pkg/services/ldap/ldap_login_test.go @@ -198,5 +198,37 @@ func TestLDAPLogin(t *testing.T) { So(username, ShouldEqual, "test") So(password, ShouldEqual, "pwd") }) + Convey("Should bind with user if %s exists in the bind_dn", func() { + connection := &MockConnection{} + entry := ldap.Entry{ + DN: "test", + } + connection.setSearchResult(&ldap.SearchResult{Entries: []*ldap.Entry{&entry}}) + + authBindUser := "" + authBindPassword := "" + + connection.BindProvider = func(name, pass string) error { + authBindUser = name + authBindPassword = pass + return nil + } + server := &Server{ + Config: &ServerConfig{ + BindDN: "cn=%s,ou=users,dc=grafana,dc=org", + SearchBaseDNs: []string{"BaseDNHere"}, + }, + Connection: connection, + log: log.New("test-logger"), + } + + _, err := server.Login(defaultLogin) + + So(err, ShouldBeNil) + + So(authBindUser, ShouldEqual, "cn=user,ou=users,dc=grafana,dc=org") + So(authBindPassword, ShouldEqual, "pwd") + So(connection.BindCalled, ShouldBeTrue) + }) }) }