2018-01-26 10:41:41 +01:00
|
|
|
package login
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
. "github.com/smartystreets/goconvey/convey"
|
2019-04-26 15:47:16 +03:00
|
|
|
|
2019-05-17 14:57:26 +03:00
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
|
|
|
"github.com/grafana/grafana/pkg/services/ldap"
|
2018-01-26 10:41:41 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestAuthenticateUser(t *testing.T) {
|
|
|
|
|
Convey("Authenticate user", t, func() {
|
2018-09-05 12:12:46 +02:00
|
|
|
authScenario("When a user authenticates without setting a password", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
mockLoginUsingGrafanaDB(nil, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(false, nil, sc)
|
2018-09-05 12:12:46 +02:00
|
|
|
|
2019-05-17 14:57:26 +03:00
|
|
|
loginQuery := models.LoginUserQuery{
|
2018-09-05 12:12:46 +02:00
|
|
|
Username: "user",
|
|
|
|
|
Password: "",
|
|
|
|
|
}
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(&loginQuery)
|
2018-09-05 12:12:46 +02:00
|
|
|
|
|
|
|
|
Convey("login should fail", func() {
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(err, ShouldEqual, ErrPasswordEmpty)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "")
|
2018-09-05 12:12:46 +02:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
2018-01-26 10:41:41 +01:00
|
|
|
authScenario("When a user authenticates having too many login attempts", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(ErrTooManyLoginAttempts, sc)
|
|
|
|
|
mockLoginUsingGrafanaDB(nil, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, nil, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
|
|
|
|
So(err, ShouldEqual, ErrTooManyLoginAttempts)
|
|
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When grafana user authenticate with valid credentials", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
mockLoginUsingGrafanaDB(nil, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
|
|
|
|
So(err, ShouldEqual, nil)
|
|
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "grafana")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When grafana user authenticate and unexpected error occurs", func(sc *authScenarioContext) {
|
|
|
|
|
customErr := errors.New("custom")
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
mockLoginUsingGrafanaDB(customErr, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
|
|
|
|
So(err, ShouldEqual, customErr)
|
|
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeFalse)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "grafana")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
2019-05-17 14:57:26 +03:00
|
|
|
mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(false, nil, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
2020-09-04 14:54:59 +02:00
|
|
|
So(err, ShouldEqual, models.ErrUserNotFound)
|
2018-01-26 10:41:41 +01:00
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
2019-05-17 14:57:26 +03:00
|
|
|
mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
2019-05-21 14:52:49 +03:00
|
|
|
So(err, ShouldEqual, ErrInvalidCredentials)
|
2018-01-26 10:41:41 +01:00
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "ldap")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
2019-05-17 14:57:26 +03:00
|
|
|
mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, nil, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
|
|
|
|
So(err, ShouldBeNil)
|
|
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "ldap")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) {
|
|
|
|
|
customErr := errors.New("custom")
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
2019-05-17 14:57:26 +03:00
|
|
|
mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, customErr, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
|
|
|
|
So(err, ShouldEqual, customErr)
|
|
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
2020-09-04 14:54:59 +02:00
|
|
|
So(sc.loginUserQuery.AuthModule, ShouldEqual, "ldap")
|
2018-01-26 10:41:41 +01:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
authScenario("When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc)
|
2019-05-22 15:30:03 +03:00
|
|
|
mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
|
2018-01-26 10:41:41 +01:00
|
|
|
mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
|
2020-12-11 11:44:44 +01:00
|
|
|
err := authenticateUser(sc.loginUserQuery)
|
2018-01-26 10:41:41 +01:00
|
|
|
|
|
|
|
|
Convey("it should result in", func() {
|
2019-05-21 14:52:49 +03:00
|
|
|
So(err, ShouldEqual, ErrInvalidCredentials)
|
2018-01-26 10:41:41 +01:00
|
|
|
So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type authScenarioContext struct {
|
2019-05-17 14:57:26 +03:00
|
|
|
loginUserQuery *models.LoginUserQuery
|
2018-01-26 10:41:41 +01:00
|
|
|
grafanaLoginWasCalled bool
|
|
|
|
|
ldapLoginWasCalled bool
|
|
|
|
|
loginAttemptValidationWasCalled bool
|
|
|
|
|
saveInvalidLoginAttemptWasCalled bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type authScenarioFunc func(sc *authScenarioContext)
|
|
|
|
|
|
|
|
|
|
func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
|
2019-05-17 14:57:26 +03:00
|
|
|
loginUsingGrafanaDB = func(query *models.LoginUserQuery) error {
|
2018-01-26 10:41:41 +01:00
|
|
|
sc.grafanaLoginWasCalled = true
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-22 15:30:03 +03:00
|
|
|
func mockLoginUsingLDAP(enabled bool, err error, sc *authScenarioContext) {
|
|
|
|
|
loginUsingLDAP = func(query *models.LoginUserQuery) (bool, error) {
|
2018-01-26 10:41:41 +01:00
|
|
|
sc.ldapLoginWasCalled = true
|
|
|
|
|
return enabled, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mockLoginAttemptValidation(err error, sc *authScenarioContext) {
|
2020-12-11 11:44:44 +01:00
|
|
|
validateLoginAttempts = func(*models.LoginUserQuery) error {
|
2018-01-26 10:41:41 +01:00
|
|
|
sc.loginAttemptValidationWasCalled = true
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mockSaveInvalidLoginAttempt(sc *authScenarioContext) {
|
2019-10-09 12:57:44 +02:00
|
|
|
saveInvalidLoginAttempt = func(query *models.LoginUserQuery) error {
|
2018-01-26 10:41:41 +01:00
|
|
|
sc.saveInvalidLoginAttemptWasCalled = true
|
2019-10-09 12:57:44 +02:00
|
|
|
return nil
|
2018-01-26 10:41:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func authScenario(desc string, fn authScenarioFunc) {
|
|
|
|
|
Convey(desc, func() {
|
|
|
|
|
origLoginUsingGrafanaDB := loginUsingGrafanaDB
|
2019-05-22 15:30:03 +03:00
|
|
|
origLoginUsingLDAP := loginUsingLDAP
|
2018-01-26 10:41:41 +01:00
|
|
|
origValidateLoginAttempts := validateLoginAttempts
|
|
|
|
|
origSaveInvalidLoginAttempt := saveInvalidLoginAttempt
|
|
|
|
|
|
|
|
|
|
sc := &authScenarioContext{
|
2019-05-17 14:57:26 +03:00
|
|
|
loginUserQuery: &models.LoginUserQuery{
|
2018-01-26 10:41:41 +01:00
|
|
|
Username: "user",
|
|
|
|
|
Password: "pwd",
|
|
|
|
|
IpAddress: "192.168.1.1:56433",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
loginUsingGrafanaDB = origLoginUsingGrafanaDB
|
2019-05-22 15:30:03 +03:00
|
|
|
loginUsingLDAP = origLoginUsingLDAP
|
2018-01-26 10:41:41 +01:00
|
|
|
validateLoginAttempts = origValidateLoginAttempts
|
|
|
|
|
saveInvalidLoginAttempt = origSaveInvalidLoginAttempt
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
fn(sc)
|
|
|
|
|
})
|
|
|
|
|
}
|