mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
LDAP: Adds bind before searching LDAP for non-login cases. (#18023)
This commit is contained in:
parent
5f0a7f43c3
commit
5d3a60d46e
@ -31,7 +31,8 @@ type IConnection interface {
|
|||||||
type IServer interface {
|
type IServer interface {
|
||||||
Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error)
|
Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error)
|
||||||
Users([]string) ([]*models.ExternalUserInfo, error)
|
Users([]string) ([]*models.ExternalUserInfo, error)
|
||||||
Auth(string, string) error
|
Bind() error
|
||||||
|
UserBind(string, string) error
|
||||||
Dial() error
|
Dial() error
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
@ -43,6 +44,23 @@ type Server struct {
|
|||||||
log log.Logger
|
log log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind authenticates the connection with the LDAP server
|
||||||
|
// - with the username and password setup in the config
|
||||||
|
// - or, anonymously
|
||||||
|
func (server *Server) Bind() error {
|
||||||
|
if server.shouldAuthAdmin() {
|
||||||
|
if err := server.AuthAdmin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := server.Connection.UnauthenticatedBind(server.Config.BindDN)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// UsersMaxRequest is a max amount of users we can request via Users().
|
// UsersMaxRequest is a max amount of users we can request via Users().
|
||||||
// Since many LDAP servers has limitations
|
// Since many LDAP servers has limitations
|
||||||
// on how much items can we return in one request
|
// on how much items can we return in one request
|
||||||
@ -149,7 +167,7 @@ func (server *Server) Login(query *models.LoginUserQuery) (
|
|||||||
}
|
}
|
||||||
} else if server.shouldSingleBind() {
|
} else if server.shouldSingleBind() {
|
||||||
authAndBind = true
|
authAndBind = true
|
||||||
err = server.Auth(server.singleBindDN(query.Username), query.Password)
|
err = server.UserBind(server.singleBindDN(query.Username), query.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -179,7 +197,7 @@ func (server *Server) Login(query *models.LoginUserQuery) (
|
|||||||
|
|
||||||
if !authAndBind {
|
if !authAndBind {
|
||||||
// Authenticate user
|
// Authenticate user
|
||||||
err = server.Auth(user.AuthId, query.Password)
|
err = server.UserBind(user.AuthId, query.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -380,9 +398,9 @@ func (server *Server) shouldAuthAdmin() bool {
|
|||||||
return server.Config.BindPassword != ""
|
return server.Config.BindPassword != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth authentificates user in LDAP
|
// UserBind authenticates the connection with the LDAP server
|
||||||
func (server *Server) Auth(username, password string) error {
|
func (server *Server) UserBind(username, password string) error {
|
||||||
err := server.auth(username, password)
|
err := server.userBind(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.log.Error(
|
server.log.Error(
|
||||||
fmt.Sprintf("Cannot authentificate user %s in LDAP", username),
|
fmt.Sprintf("Cannot authentificate user %s in LDAP", username),
|
||||||
@ -397,7 +415,7 @@ func (server *Server) Auth(username, password string) error {
|
|||||||
|
|
||||||
// AuthAdmin authentificates LDAP admin user
|
// AuthAdmin authentificates LDAP admin user
|
||||||
func (server *Server) AuthAdmin() error {
|
func (server *Server) AuthAdmin() error {
|
||||||
err := server.auth(server.Config.BindDN, server.Config.BindPassword)
|
err := server.userBind(server.Config.BindDN, server.Config.BindPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
server.log.Error(
|
server.log.Error(
|
||||||
"Cannot authentificate admin user in LDAP",
|
"Cannot authentificate admin user in LDAP",
|
||||||
@ -410,8 +428,8 @@ func (server *Server) AuthAdmin() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// auth is helper for several types of LDAP authentification
|
// userBind authenticates the connection with the LDAP server
|
||||||
func (server *Server) auth(path, password string) error {
|
func (server *Server) userBind(path, password string) error {
|
||||||
err := server.Connection.Bind(path, password)
|
err := server.Connection.Bind(path, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ldapErr, ok := err.(*ldap.Error); ok {
|
if ldapErr, ok := err.(*ldap.Error); ok {
|
||||||
|
@ -19,7 +19,7 @@ func TestLDAPLogin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Convey("Login()", t, func() {
|
Convey("Login()", t, func() {
|
||||||
Convey("Should get invalid credentials when auth fails", func() {
|
Convey("Should get invalid credentials when userBind fails", func() {
|
||||||
connection := &MockConnection{}
|
connection := &MockConnection{}
|
||||||
entry := ldap.Entry{}
|
entry := ldap.Entry{}
|
||||||
result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
|
result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
|
||||||
|
@ -145,7 +145,7 @@ func TestLDAPPrivateMethods(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("shouldAuthAdmin()", t, func() {
|
Convey("shouldAuthAdmin()", t, func() {
|
||||||
Convey("it should require admin auth", func() {
|
Convey("it should require admin userBind", func() {
|
||||||
server := &Server{
|
server := &Server{
|
||||||
Config: &ServerConfig{
|
Config: &ServerConfig{
|
||||||
BindPassword: "test",
|
BindPassword: "test",
|
||||||
@ -156,7 +156,7 @@ func TestLDAPPrivateMethods(t *testing.T) {
|
|||||||
So(result, ShouldBeTrue)
|
So(result, ShouldBeTrue)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("it should not require admin auth", func() {
|
Convey("it should not require admin userBind", func() {
|
||||||
server := &Server{
|
server := &Server{
|
||||||
Config: &ServerConfig{
|
Config: &ServerConfig{
|
||||||
BindPassword: "",
|
BindPassword: "",
|
||||||
|
@ -102,7 +102,7 @@ func TestPublicAPI(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Auth()", t, func() {
|
Convey("UserBind()", t, func() {
|
||||||
Convey("Should use provided DN and password", func() {
|
Convey("Should use provided DN and password", func() {
|
||||||
connection := &MockConnection{}
|
connection := &MockConnection{}
|
||||||
var actualUsername, actualPassword string
|
var actualUsername, actualPassword string
|
||||||
@ -119,7 +119,7 @@ func TestPublicAPI(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dn := "cn=user,ou=users,dc=grafana,dc=org"
|
dn := "cn=user,ou=users,dc=grafana,dc=org"
|
||||||
err := server.Auth(dn, "pwd")
|
err := server.UserBind(dn, "pwd")
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(actualUsername, ShouldEqual, dn)
|
So(actualUsername, ShouldEqual, dn)
|
||||||
@ -141,7 +141,7 @@ func TestPublicAPI(t *testing.T) {
|
|||||||
},
|
},
|
||||||
log: log.New("test-logger"),
|
log: log.New("test-logger"),
|
||||||
}
|
}
|
||||||
err := server.Auth("user", "pwd")
|
err := server.UserBind("user", "pwd")
|
||||||
So(err, ShouldEqual, expected)
|
So(err, ShouldEqual, expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -109,6 +109,10 @@ func (multiples *MultiLDAP) User(login string) (
|
|||||||
|
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
|
if err := server.Bind(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
users, err := server.Users(search)
|
users, err := server.Users(search)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -142,6 +146,10 @@ func (multiples *MultiLDAP) Users(logins []string) (
|
|||||||
|
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
|
if err := server.Bind(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
users, err := server.Users(logins)
|
users, err := server.Users(logins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -11,12 +11,15 @@ type MockLDAP struct {
|
|||||||
loginCalledTimes int
|
loginCalledTimes int
|
||||||
closeCalledTimes int
|
closeCalledTimes int
|
||||||
usersCalledTimes int
|
usersCalledTimes int
|
||||||
|
bindCalledTimes int
|
||||||
|
|
||||||
dialErrReturn error
|
dialErrReturn error
|
||||||
|
|
||||||
loginErrReturn error
|
loginErrReturn error
|
||||||
loginReturn *models.ExternalUserInfo
|
loginReturn *models.ExternalUserInfo
|
||||||
|
|
||||||
|
bindErrReturn error
|
||||||
|
|
||||||
usersErrReturn error
|
usersErrReturn error
|
||||||
usersFirstReturn []*models.ExternalUserInfo
|
usersFirstReturn []*models.ExternalUserInfo
|
||||||
usersRestReturn []*models.ExternalUserInfo
|
usersRestReturn []*models.ExternalUserInfo
|
||||||
@ -40,8 +43,8 @@ func (mock *MockLDAP) Users([]string) ([]*models.ExternalUserInfo, error) {
|
|||||||
return mock.usersRestReturn, mock.usersErrReturn
|
return mock.usersRestReturn, mock.usersErrReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth test fn
|
// UserBind test fn
|
||||||
func (mock *MockLDAP) Auth(string, string) error {
|
func (mock *MockLDAP) UserBind(string, string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +59,11 @@ func (mock *MockLDAP) Close() {
|
|||||||
mock.closeCalledTimes = mock.closeCalledTimes + 1
|
mock.closeCalledTimes = mock.closeCalledTimes + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mock *MockLDAP) Bind() error {
|
||||||
|
mock.bindCalledTimes++
|
||||||
|
return mock.bindErrReturn
|
||||||
|
}
|
||||||
|
|
||||||
// MockMultiLDAP represents testing struct for multildap testing
|
// MockMultiLDAP represents testing struct for multildap testing
|
||||||
type MockMultiLDAP struct {
|
type MockMultiLDAP struct {
|
||||||
LoginCalledTimes int
|
LoginCalledTimes int
|
||||||
|
Loading…
Reference in New Issue
Block a user