ldap_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. package login
  2. import (
  3. "crypto/tls"
  4. "testing"
  5. "github.com/go-ldap/ldap"
  6. "github.com/grafana/grafana/pkg/bus"
  7. m "github.com/grafana/grafana/pkg/models"
  8. . "github.com/smartystreets/goconvey/convey"
  9. )
  10. func TestLdapAuther(t *testing.T) {
  11. Convey("When translating ldap user to grafana user", t, func() {
  12. Convey("Given no ldap group map match", func() {
  13. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  14. LdapGroups: []*LdapGroupToOrgRole{{}},
  15. })
  16. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{})
  17. So(err, ShouldEqual, ErrInvalidCredentials)
  18. })
  19. var user1 = &m.User{}
  20. ldapAutherScenario("Given wildcard group match", func(sc *scenarioContext) {
  21. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  22. LdapGroups: []*LdapGroupToOrgRole{
  23. {GroupDN: "*", OrgRole: "Admin"},
  24. },
  25. })
  26. sc.userQueryReturns(user1)
  27. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{})
  28. So(err, ShouldBeNil)
  29. So(result, ShouldEqual, user1)
  30. })
  31. ldapAutherScenario("Given exact group match", func(sc *scenarioContext) {
  32. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  33. LdapGroups: []*LdapGroupToOrgRole{
  34. {GroupDN: "cn=users", OrgRole: "Admin"},
  35. },
  36. })
  37. sc.userQueryReturns(user1)
  38. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{MemberOf: []string{"cn=users"}})
  39. So(err, ShouldBeNil)
  40. So(result, ShouldEqual, user1)
  41. })
  42. ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) {
  43. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  44. LdapGroups: []*LdapGroupToOrgRole{
  45. {GroupDN: "cn=admin", OrgRole: "Admin"},
  46. {GroupDN: "cn=editor", OrgRole: "Editor"},
  47. {GroupDN: "*", OrgRole: "Viewer"},
  48. },
  49. })
  50. sc.userQueryReturns(nil)
  51. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  52. DN: "torkelo",
  53. Username: "torkelo",
  54. Email: "my@email.com",
  55. MemberOf: []string{"cn=editor"},
  56. })
  57. So(err, ShouldBeNil)
  58. Convey("Should return new user", func() {
  59. So(result.Login, ShouldEqual, "torkelo")
  60. })
  61. /*
  62. Convey("Should create new user", func() {
  63. So(sc.getUserByAuthInfoQuery.Login, ShouldEqual, "torkelo")
  64. So(sc.getUserByAuthInfoQuery.Email, ShouldEqual, "my@email.com")
  65. So(sc.createUserCmd.Login, ShouldEqual, "torkelo")
  66. So(sc.createUserCmd.Email, ShouldEqual, "my@email.com")
  67. })
  68. */
  69. })
  70. })
  71. Convey("When calling SyncSignedInUser", t, func() {
  72. mockLdapConnection := &mockLdapConn{}
  73. ldapAuther := NewLdapAuthenticator(
  74. &LdapServerConf{
  75. Host: "",
  76. RootCACert: "",
  77. LdapGroups: []*LdapGroupToOrgRole{
  78. {GroupDN: "*", OrgRole: "Admin"},
  79. },
  80. Attr: LdapAttributeMap{
  81. Username: "username",
  82. Surname: "surname",
  83. Email: "email",
  84. Name: "name",
  85. MemberOf: "memberof",
  86. },
  87. SearchBaseDNs: []string{"BaseDNHere"},
  88. },
  89. )
  90. dialCalled := false
  91. ldapDial = func(network, addr string) (ILdapConn, error) {
  92. dialCalled = true
  93. return mockLdapConnection, nil
  94. }
  95. entry := ldap.Entry{
  96. DN: "dn", Attributes: []*ldap.EntryAttribute{
  97. {Name: "username", Values: []string{"roelgerrits"}},
  98. {Name: "surname", Values: []string{"Gerrits"}},
  99. {Name: "email", Values: []string{"roel@test.com"}},
  100. {Name: "name", Values: []string{"Roel"}},
  101. {Name: "memberof", Values: []string{"admins"}},
  102. }}
  103. result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
  104. mockLdapConnection.setSearchResult(&result)
  105. ldapAutherScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) {
  106. // arrange
  107. signedInUser := &m.SignedInUser{
  108. Email: "roel@test.net",
  109. UserId: 1,
  110. Name: "Roel Gerrits",
  111. Login: "roelgerrits",
  112. }
  113. sc.userQueryReturns(&m.User{
  114. Id: 1,
  115. Email: "roel@test.net",
  116. Name: "Roel Gerrits",
  117. Login: "roelgerrits",
  118. })
  119. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  120. // act
  121. syncErrResult := ldapAuther.SyncSignedInUser(nil, signedInUser)
  122. // assert
  123. So(dialCalled, ShouldBeTrue)
  124. So(syncErrResult, ShouldBeNil)
  125. // User should be searched in ldap
  126. So(mockLdapConnection.searchCalled, ShouldBeTrue)
  127. // Info should be updated (email differs)
  128. So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com")
  129. // User should have admin privileges
  130. So(sc.addOrgUserCmd.UserId, ShouldEqual, 1)
  131. So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
  132. })
  133. })
  134. }
  135. type mockLdapConn struct {
  136. result *ldap.SearchResult
  137. searchCalled bool
  138. }
  139. func (c *mockLdapConn) Bind(username, password string) error {
  140. return nil
  141. }
  142. func (c *mockLdapConn) Close() {}
  143. func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) {
  144. c.result = result
  145. }
  146. func (c *mockLdapConn) Search(*ldap.SearchRequest) (*ldap.SearchResult, error) {
  147. c.searchCalled = true
  148. return c.result, nil
  149. }
  150. func (c *mockLdapConn) StartTLS(*tls.Config) error {
  151. return nil
  152. }
  153. func ldapAutherScenario(desc string, fn scenarioFunc) {
  154. Convey(desc, func() {
  155. defer bus.ClearBusHandlers()
  156. sc := &scenarioContext{}
  157. bus.AddHandler("test", UpsertUser)
  158. bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
  159. sc.getUserByAuthInfoQuery = cmd
  160. sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login}
  161. return nil
  162. })
  163. bus.AddHandler("test", func(cmd *m.GetUserOrgListQuery) error {
  164. sc.getUserOrgListQuery = cmd
  165. return nil
  166. })
  167. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  168. sc.createUserCmd = cmd
  169. sc.createUserCmd.Result = m.User{Login: cmd.Login}
  170. return nil
  171. })
  172. bus.AddHandler("test", func(cmd *m.AddOrgUserCommand) error {
  173. sc.addOrgUserCmd = cmd
  174. return nil
  175. })
  176. bus.AddHandler("test", func(cmd *m.UpdateOrgUserCommand) error {
  177. sc.updateOrgUserCmd = cmd
  178. return nil
  179. })
  180. bus.AddHandler("test", func(cmd *m.RemoveOrgUserCommand) error {
  181. sc.removeOrgUserCmd = cmd
  182. return nil
  183. })
  184. bus.AddHandler("test", func(cmd *m.UpdateUserCommand) error {
  185. sc.updateUserCmd = cmd
  186. return nil
  187. })
  188. fn(sc)
  189. })
  190. }
  191. type scenarioContext struct {
  192. getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
  193. getUserOrgListQuery *m.GetUserOrgListQuery
  194. createUserCmd *m.CreateUserCommand
  195. addOrgUserCmd *m.AddOrgUserCommand
  196. updateOrgUserCmd *m.UpdateOrgUserCommand
  197. removeOrgUserCmd *m.RemoveOrgUserCommand
  198. updateUserCmd *m.UpdateUserCommand
  199. }
  200. func (sc *scenarioContext) userQueryReturns(user *m.User) {
  201. bus.AddHandler("test", func(query *m.GetUserByAuthInfoQuery) error {
  202. if user == nil {
  203. return m.ErrUserNotFound
  204. } else {
  205. query.Result = user
  206. return nil
  207. }
  208. })
  209. bus.AddHandler("test", func(query *m.SetAuthInfoCommand) error {
  210. return nil
  211. })
  212. }
  213. func (sc *scenarioContext) userOrgsQueryReturns(orgs []*m.UserOrgDTO) {
  214. bus.AddHandler("test", func(query *m.GetUserOrgListQuery) error {
  215. query.Result = orgs
  216. return nil
  217. })
  218. }
  219. type scenarioFunc func(c *scenarioContext)