|
@@ -0,0 +1,214 @@
|
|
|
|
|
+package login
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "errors"
|
|
|
|
|
+ "testing"
|
|
|
|
|
+
|
|
|
|
|
+ m "github.com/grafana/grafana/pkg/models"
|
|
|
|
|
+ . "github.com/smartystreets/goconvey/convey"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+func TestAuthenticateUser(t *testing.T) {
|
|
|
|
|
+ Convey("Authenticate user", t, func() {
|
|
|
|
|
+ authScenario("When a user authenticates having too many login attempts", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(ErrTooManyLoginAttempts, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(nil, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, nil, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When grafana user authenticate with valid credentials", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(nil, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, ErrInvalidCredentials, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When grafana user authenticate and unexpected error occurs", func(sc *authScenarioContext) {
|
|
|
|
|
+ customErr := errors.New("custom")
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(customErr, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, ErrInvalidCredentials, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
|
|
|
|
|
+ mockLoginUsingLdap(false, nil, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ Convey("it should result in", func() {
|
|
|
|
|
+ So(err, ShouldEqual, ErrInvalidCredentials)
|
|
|
|
|
+ So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, ErrInvalidCredentials, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ Convey("it should result in", func() {
|
|
|
|
|
+ So(err, ShouldEqual, ErrInvalidCredentials)
|
|
|
|
|
+ So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, nil, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) {
|
|
|
|
|
+ customErr := errors.New("custom")
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(m.ErrUserNotFound, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, customErr, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ authScenario("When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) {
|
|
|
|
|
+ mockLoginAttemptValidation(nil, sc)
|
|
|
|
|
+ mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc)
|
|
|
|
|
+ mockLoginUsingLdap(true, ErrInvalidCredentials, sc)
|
|
|
|
|
+ mockSaveInvalidLoginAttempt(sc)
|
|
|
|
|
+
|
|
|
|
|
+ err := AuthenticateUser(sc.loginUserQuery)
|
|
|
|
|
+
|
|
|
|
|
+ Convey("it should result in", func() {
|
|
|
|
|
+ So(err, ShouldEqual, ErrInvalidCredentials)
|
|
|
|
|
+ So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.grafanaLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.ldapLoginWasCalled, ShouldBeTrue)
|
|
|
|
|
+ So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type authScenarioContext struct {
|
|
|
|
|
+ loginUserQuery *LoginUserQuery
|
|
|
|
|
+ grafanaLoginWasCalled bool
|
|
|
|
|
+ ldapLoginWasCalled bool
|
|
|
|
|
+ loginAttemptValidationWasCalled bool
|
|
|
|
|
+ saveInvalidLoginAttemptWasCalled bool
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+type authScenarioFunc func(sc *authScenarioContext)
|
|
|
|
|
+
|
|
|
|
|
+func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
|
|
|
|
|
+ loginUsingGrafanaDB = func(query *LoginUserQuery) error {
|
|
|
|
|
+ sc.grafanaLoginWasCalled = true
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func mockLoginUsingLdap(enabled bool, err error, sc *authScenarioContext) {
|
|
|
|
|
+ loginUsingLdap = func(query *LoginUserQuery) (bool, error) {
|
|
|
|
|
+ sc.ldapLoginWasCalled = true
|
|
|
|
|
+ return enabled, err
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func mockLoginAttemptValidation(err error, sc *authScenarioContext) {
|
|
|
|
|
+ validateLoginAttempts = func(username string) error {
|
|
|
|
|
+ sc.loginAttemptValidationWasCalled = true
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func mockSaveInvalidLoginAttempt(sc *authScenarioContext) {
|
|
|
|
|
+ saveInvalidLoginAttempt = func(query *LoginUserQuery) {
|
|
|
|
|
+ sc.saveInvalidLoginAttemptWasCalled = true
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func authScenario(desc string, fn authScenarioFunc) {
|
|
|
|
|
+ Convey(desc, func() {
|
|
|
|
|
+ origLoginUsingGrafanaDB := loginUsingGrafanaDB
|
|
|
|
|
+ origLoginUsingLdap := loginUsingLdap
|
|
|
|
|
+ origValidateLoginAttempts := validateLoginAttempts
|
|
|
|
|
+ origSaveInvalidLoginAttempt := saveInvalidLoginAttempt
|
|
|
|
|
+
|
|
|
|
|
+ sc := &authScenarioContext{
|
|
|
|
|
+ loginUserQuery: &LoginUserQuery{
|
|
|
|
|
+ Username: "user",
|
|
|
|
|
+ Password: "pwd",
|
|
|
|
|
+ IpAddress: "192.168.1.1:56433",
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ defer func() {
|
|
|
|
|
+ loginUsingGrafanaDB = origLoginUsingGrafanaDB
|
|
|
|
|
+ loginUsingLdap = origLoginUsingLdap
|
|
|
|
|
+ validateLoginAttempts = origValidateLoginAttempts
|
|
|
|
|
+ saveInvalidLoginAttempt = origSaveInvalidLoginAttempt
|
|
|
|
|
+ }()
|
|
|
|
|
+
|
|
|
|
|
+ fn(sc)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|