The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/multildap/multildap_test.go

522 lines
11 KiB

package multildap
import (
"errors"
"testing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/stretchr/testify/require"
//TODO(sh0rez): remove once import cycle resolved
_ "github.com/grafana/grafana/pkg/api/response"
)
func TestMultiLDAP(t *testing.T) {
t.Run("Ping()", func(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{})
_, err := multi.Ping()
require.Error(t, err)
require.Equal(t, ErrNoLDAPServers, err)
teardown()
})
t.Run("Should return an unavailable status on dial error", func(t *testing.T) {
mock := setup()
expectedErr := errors.New("Dial error")
mock.dialErrReturn = expectedErr
multi := New([]*ldap.ServerConfig{
{Host: "10.0.0.1", Port: 361},
})
statuses, err := multi.Ping()
require.Nil(t, err)
require.Equal(t, "10.0.0.1", statuses[0].Host)
require.Equal(t, 361, statuses[0].Port)
require.False(t, statuses[0].Available)
require.Equal(t, expectedErr, statuses[0].Error)
require.Equal(t, 0, mock.closeCalledTimes)
teardown()
})
t.Run("Should get the LDAP server statuses", func(t *testing.T) {
mock := setup()
multi := New([]*ldap.ServerConfig{
{Host: "10.0.0.1", Port: 361},
})
statuses, err := multi.Ping()
require.Nil(t, err)
require.Equal(t, "10.0.0.1", statuses[0].Host)
require.Equal(t, 361, statuses[0].Port)
require.True(t, statuses[0].Available)
require.Nil(t, statuses[0].Error)
require.Equal(t, 1, mock.closeCalledTimes)
teardown()
})
})
t.Run("Login()", func(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{})
_, err := multi.Login(&models.LoginUserQuery{})
require.Error(t, err)
require.Equal(t, ErrNoLDAPServers, err)
teardown()
})
t.Run("Should return a dial error", func(t *testing.T) {
mock := setup()
expected := errors.New("Dial error")
mock.dialErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Error(t, err)
require.Equal(t, expected, err)
teardown()
})
t.Run("Should call underlying LDAP methods", func(t *testing.T) {
mock := setup()
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.loginCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Equal(t, ErrInvalidCredentials, err)
teardown()
})
t.Run("Should get login result", func(t *testing.T) {
mock := setup()
mock.loginReturn = &models.ExternalUserInfo{
Login: "killa",
}
multi := New([]*ldap.ServerConfig{
{}, {},
})
result, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 1, mock.dialCalledTimes)
require.Equal(t, 1, mock.loginCalledTimes)
require.Equal(t, 1, mock.closeCalledTimes)
require.Equal(t, "killa", result.Login)
require.Nil(t, err)
teardown()
})
t.Run("Should still call a second error for invalid not found error", func(t *testing.T) {
mock := setup()
mock.loginErrReturn = ErrCouldNotFindUser
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.loginCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Equal(t, ErrInvalidCredentials, err)
teardown()
})
t.Run("Should still try to auth with the second server after receiving an invalid credentials error from the first", func(t *testing.T) {
mock := setup()
mock.loginErrReturn = ErrInvalidCredentials
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.loginCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Equal(t, ErrInvalidCredentials, err)
teardown()
})
t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
mock := setup()
expectedError := errors.New("Dial error")
mock.dialErrReturn = expectedError
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, expectedError, err)
teardown()
})
t.Run("Should return unknown error", func(t *testing.T) {
mock := setup()
expected := errors.New("Something unknown")
mock.loginErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Login(&models.LoginUserQuery{})
require.Equal(t, 1, mock.dialCalledTimes)
require.Equal(t, 1, mock.loginCalledTimes)
require.Equal(t, 1, mock.closeCalledTimes)
require.Equal(t, expected, err)
teardown()
})
})
t.Run("User()", func(t *testing.T) {
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{})
_, _, err := multi.User("test")
require.Error(t, err)
require.Equal(t, ErrNoLDAPServers, err)
teardown()
})
t.Run("Should return a dial error", func(t *testing.T) {
mock := setup()
expected := errors.New("Dial error")
mock.dialErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, _, err := multi.User("test")
require.Error(t, err)
require.Equal(t, expected, err)
teardown()
})
t.Run("Should call underlying LDAP methods", func(t *testing.T) {
mock := setup()
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, _, err := multi.User("test")
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.usersCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Equal(t, ErrDidNotFindUser, err)
teardown()
})
t.Run("Should return some error", func(t *testing.T) {
mock := setup()
expected := errors.New("Killa Gorilla")
mock.usersErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, _, err := multi.User("test")
require.Equal(t, 1, mock.dialCalledTimes)
require.Equal(t, 1, mock.usersCalledTimes)
require.Equal(t, 1, mock.closeCalledTimes)
require.Equal(t, expected, err)
teardown()
})
t.Run("Should get only one user", func(t *testing.T) {
mock := setup()
mock.usersFirstReturn = []*models.ExternalUserInfo{
{
Login: "one",
},
{
Login: "two",
},
}
multi := New([]*ldap.ServerConfig{
{}, {},
})
user, _, err := multi.User("test")
require.Equal(t, 1, mock.dialCalledTimes)
require.Equal(t, 1, mock.usersCalledTimes)
require.Equal(t, 1, mock.closeCalledTimes)
require.Nil(t, err)
require.Equal(t, "one", user.Login)
teardown()
})
t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
mock := setup()
expectedError := errors.New("Dial error")
mock.dialErrReturn = expectedError
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, _, err := multi.User("test")
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, expectedError, err)
teardown()
})
})
t.Run("Users()", func(t *testing.T) {
t.Run("Should still try to auth with the second server after receiving a dial error from the first", func(t *testing.T) {
mock := setup()
expectedError := errors.New("Dial error")
mock.dialErrReturn = expectedError
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, expectedError, err)
teardown()
})
t.Run("Should return error for absent config list", func(t *testing.T) {
setup()
multi := New([]*ldap.ServerConfig{})
_, err := multi.Users([]string{"test"})
require.Error(t, err)
require.Equal(t, ErrNoLDAPServers, err)
teardown()
})
t.Run("Should return a dial error", func(t *testing.T) {
mock := setup()
expected := errors.New("Dial error")
mock.dialErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Users([]string{"test"})
require.Error(t, err)
require.Equal(t, expected, err)
teardown()
})
t.Run("Should call underlying LDAP methods", func(t *testing.T) {
mock := setup()
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.usersCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Nil(t, err)
teardown()
})
t.Run("Should return some error", func(t *testing.T) {
mock := setup()
expected := errors.New("Killa Gorilla")
mock.usersErrReturn = expected
multi := New([]*ldap.ServerConfig{
{}, {},
})
_, err := multi.Users([]string{"test"})
require.Equal(t, 1, mock.dialCalledTimes)
require.Equal(t, 1, mock.usersCalledTimes)
require.Equal(t, 1, mock.closeCalledTimes)
require.Equal(t, expected, err)
teardown()
})
t.Run("Should get users", func(t *testing.T) {
mock := setup()
mock.usersFirstReturn = []*models.ExternalUserInfo{
{
Login: "one",
},
{
Login: "two",
},
}
mock.usersRestReturn = []*models.ExternalUserInfo{
{
Login: "three",
},
}
multi := New([]*ldap.ServerConfig{
{}, {},
})
users, err := multi.Users([]string{"test"})
require.Equal(t, 2, mock.dialCalledTimes)
require.Equal(t, 2, mock.usersCalledTimes)
require.Equal(t, 2, mock.closeCalledTimes)
require.Nil(t, err)
require.Equal(t, "one", users[0].Login)
require.Equal(t, "two", users[1].Login)
require.Equal(t, "three", users[2].Login)
teardown()
})
})
}
// mockLDAP represents testing struct for ldap testing
type mockLDAP struct {
dialCalledTimes int
loginCalledTimes int
closeCalledTimes int
usersCalledTimes int
bindCalledTimes int
dialErrReturn error
loginErrReturn error
loginReturn *models.ExternalUserInfo
bindErrReturn error
usersErrReturn error
usersFirstReturn []*models.ExternalUserInfo
usersRestReturn []*models.ExternalUserInfo
}
// Login test fn
func (mock *mockLDAP) Login(*models.LoginUserQuery) (*models.ExternalUserInfo, error) {
mock.loginCalledTimes++
return mock.loginReturn, mock.loginErrReturn
}
// Users test fn
func (mock *mockLDAP) Users([]string) ([]*models.ExternalUserInfo, error) {
mock.usersCalledTimes++
if mock.usersCalledTimes == 1 {
return mock.usersFirstReturn, mock.usersErrReturn
}
return mock.usersRestReturn, mock.usersErrReturn
}
// UserBind test fn
func (mock *mockLDAP) UserBind(string, string) error {
return nil
}
// Dial test fn
func (mock *mockLDAP) Dial() error {
mock.dialCalledTimes++
return mock.dialErrReturn
}
// Close test fn
func (mock *mockLDAP) Close() {
mock.closeCalledTimes++
}
func (mock *mockLDAP) Bind() error {
mock.bindCalledTimes++
return mock.bindErrReturn
}
func setup() *mockLDAP {
mock := &mockLDAP{}
newLDAP = func(config *ldap.ServerConfig) ldap.IServer {
return mock
}
return mock
}
func teardown() {
newLDAP = ldap.New
}