auth_test.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package login
  2. import (
  3. "errors"
  4. "testing"
  5. . "github.com/smartystreets/goconvey/convey"
  6. "github.com/grafana/grafana/pkg/models"
  7. "github.com/grafana/grafana/pkg/services/ldap"
  8. )
  9. func TestAuthenticateUser(t *testing.T) {
  10. Convey("Authenticate user", t, func() {
  11. authScenario("When a user authenticates without setting a password", func(sc *authScenarioContext) {
  12. mockLoginAttemptValidation(nil, sc)
  13. mockLoginUsingGrafanaDB(nil, sc)
  14. mockLoginUsingLDAP(false, nil, sc)
  15. loginQuery := models.LoginUserQuery{
  16. Username: "user",
  17. Password: "",
  18. }
  19. err := AuthenticateUser(&loginQuery)
  20. Convey("login should fail", func() {
  21. So(sc.grafanaLoginWasCalled, ShouldBeFalse)
  22. So(sc.ldapLoginWasCalled, ShouldBeFalse)
  23. So(err, ShouldEqual, ErrPasswordEmpty)
  24. })
  25. })
  26. authScenario("When a user authenticates having too many login attempts", func(sc *authScenarioContext) {
  27. mockLoginAttemptValidation(ErrTooManyLoginAttempts, sc)
  28. mockLoginUsingGrafanaDB(nil, sc)
  29. mockLoginUsingLDAP(true, nil, sc)
  30. mockSaveInvalidLoginAttempt(sc)
  31. err := AuthenticateUser(sc.loginUserQuery)
  32. Convey("it should result in", func() {
  33. So(err, ShouldEqual, ErrTooManyLoginAttempts)
  34. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  35. So(sc.grafanaLoginWasCalled, ShouldBeFalse)
  36. So(sc.ldapLoginWasCalled, ShouldBeFalse)
  37. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  38. })
  39. })
  40. authScenario("When grafana user authenticate with valid credentials", func(sc *authScenarioContext) {
  41. mockLoginAttemptValidation(nil, sc)
  42. mockLoginUsingGrafanaDB(nil, sc)
  43. mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
  44. mockSaveInvalidLoginAttempt(sc)
  45. err := AuthenticateUser(sc.loginUserQuery)
  46. Convey("it should result in", func() {
  47. So(err, ShouldEqual, nil)
  48. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  49. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  50. So(sc.ldapLoginWasCalled, ShouldBeFalse)
  51. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  52. })
  53. })
  54. authScenario("When grafana user authenticate and unexpected error occurs", func(sc *authScenarioContext) {
  55. customErr := errors.New("custom")
  56. mockLoginAttemptValidation(nil, sc)
  57. mockLoginUsingGrafanaDB(customErr, sc)
  58. mockLoginUsingLDAP(true, ErrInvalidCredentials, sc)
  59. mockSaveInvalidLoginAttempt(sc)
  60. err := AuthenticateUser(sc.loginUserQuery)
  61. Convey("it should result in", func() {
  62. So(err, ShouldEqual, customErr)
  63. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  64. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  65. So(sc.ldapLoginWasCalled, ShouldBeFalse)
  66. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  67. })
  68. })
  69. authScenario("When a non-existing grafana user authenticate and ldap disabled", func(sc *authScenarioContext) {
  70. mockLoginAttemptValidation(nil, sc)
  71. mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
  72. mockLoginUsingLDAP(false, nil, sc)
  73. mockSaveInvalidLoginAttempt(sc)
  74. err := AuthenticateUser(sc.loginUserQuery)
  75. Convey("it should result in", func() {
  76. So(err, ShouldEqual, ErrInvalidCredentials)
  77. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  78. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  79. So(sc.ldapLoginWasCalled, ShouldBeTrue)
  80. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  81. })
  82. })
  83. authScenario("When a non-existing grafana user authenticate and invalid ldap credentials", func(sc *authScenarioContext) {
  84. mockLoginAttemptValidation(nil, sc)
  85. mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
  86. mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
  87. mockSaveInvalidLoginAttempt(sc)
  88. err := AuthenticateUser(sc.loginUserQuery)
  89. Convey("it should result in", func() {
  90. So(err, ShouldEqual, ErrInvalidCredentials)
  91. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  92. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  93. So(sc.ldapLoginWasCalled, ShouldBeTrue)
  94. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
  95. })
  96. })
  97. authScenario("When a non-existing grafana user authenticate and valid ldap credentials", func(sc *authScenarioContext) {
  98. mockLoginAttemptValidation(nil, sc)
  99. mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
  100. mockLoginUsingLDAP(true, nil, sc)
  101. mockSaveInvalidLoginAttempt(sc)
  102. err := AuthenticateUser(sc.loginUserQuery)
  103. Convey("it should result in", func() {
  104. So(err, ShouldBeNil)
  105. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  106. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  107. So(sc.ldapLoginWasCalled, ShouldBeTrue)
  108. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  109. })
  110. })
  111. authScenario("When a non-existing grafana user authenticate and ldap returns unexpected error", func(sc *authScenarioContext) {
  112. customErr := errors.New("custom")
  113. mockLoginAttemptValidation(nil, sc)
  114. mockLoginUsingGrafanaDB(models.ErrUserNotFound, sc)
  115. mockLoginUsingLDAP(true, customErr, sc)
  116. mockSaveInvalidLoginAttempt(sc)
  117. err := AuthenticateUser(sc.loginUserQuery)
  118. Convey("it should result in", func() {
  119. So(err, ShouldEqual, customErr)
  120. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  121. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  122. So(sc.ldapLoginWasCalled, ShouldBeTrue)
  123. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeFalse)
  124. })
  125. })
  126. authScenario("When grafana user authenticate with invalid credentials and invalid ldap credentials", func(sc *authScenarioContext) {
  127. mockLoginAttemptValidation(nil, sc)
  128. mockLoginUsingGrafanaDB(ErrInvalidCredentials, sc)
  129. mockLoginUsingLDAP(true, ldap.ErrInvalidCredentials, sc)
  130. mockSaveInvalidLoginAttempt(sc)
  131. err := AuthenticateUser(sc.loginUserQuery)
  132. Convey("it should result in", func() {
  133. So(err, ShouldEqual, ErrInvalidCredentials)
  134. So(sc.loginAttemptValidationWasCalled, ShouldBeTrue)
  135. So(sc.grafanaLoginWasCalled, ShouldBeTrue)
  136. So(sc.ldapLoginWasCalled, ShouldBeTrue)
  137. So(sc.saveInvalidLoginAttemptWasCalled, ShouldBeTrue)
  138. })
  139. })
  140. })
  141. }
  142. type authScenarioContext struct {
  143. loginUserQuery *models.LoginUserQuery
  144. grafanaLoginWasCalled bool
  145. ldapLoginWasCalled bool
  146. loginAttemptValidationWasCalled bool
  147. saveInvalidLoginAttemptWasCalled bool
  148. }
  149. type authScenarioFunc func(sc *authScenarioContext)
  150. func mockLoginUsingGrafanaDB(err error, sc *authScenarioContext) {
  151. loginUsingGrafanaDB = func(query *models.LoginUserQuery) error {
  152. sc.grafanaLoginWasCalled = true
  153. return err
  154. }
  155. }
  156. func mockLoginUsingLDAP(enabled bool, err error, sc *authScenarioContext) {
  157. loginUsingLDAP = func(query *models.LoginUserQuery) (bool, error) {
  158. sc.ldapLoginWasCalled = true
  159. return enabled, err
  160. }
  161. }
  162. func mockLoginAttemptValidation(err error, sc *authScenarioContext) {
  163. validateLoginAttempts = func(username string) error {
  164. sc.loginAttemptValidationWasCalled = true
  165. return err
  166. }
  167. }
  168. func mockSaveInvalidLoginAttempt(sc *authScenarioContext) {
  169. saveInvalidLoginAttempt = func(query *models.LoginUserQuery) {
  170. sc.saveInvalidLoginAttemptWasCalled = true
  171. }
  172. }
  173. func authScenario(desc string, fn authScenarioFunc) {
  174. Convey(desc, func() {
  175. origLoginUsingGrafanaDB := loginUsingGrafanaDB
  176. origLoginUsingLDAP := loginUsingLDAP
  177. origValidateLoginAttempts := validateLoginAttempts
  178. origSaveInvalidLoginAttempt := saveInvalidLoginAttempt
  179. sc := &authScenarioContext{
  180. loginUserQuery: &models.LoginUserQuery{
  181. Username: "user",
  182. Password: "pwd",
  183. IpAddress: "192.168.1.1:56433",
  184. },
  185. }
  186. defer func() {
  187. loginUsingGrafanaDB = origLoginUsingGrafanaDB
  188. loginUsingLDAP = origLoginUsingLDAP
  189. validateLoginAttempts = origValidateLoginAttempts
  190. saveInvalidLoginAttempt = origSaveInvalidLoginAttempt
  191. }()
  192. fn(sc)
  193. })
  194. }