ldap_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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. Username: "torkelo",
  53. Email: "my@email.com",
  54. MemberOf: []string{"cn=editor"},
  55. })
  56. So(err, ShouldBeNil)
  57. Convey("Should return new user", func() {
  58. So(result.Login, ShouldEqual, "torkelo")
  59. })
  60. /*
  61. Convey("Should create new user", func() {
  62. So(sc.getUserByAuthInfoQuery.Login, ShouldEqual, "torkelo")
  63. So(sc.getUserByAuthInfoQuery.Email, ShouldEqual, "my@email.com")
  64. So(sc.createUserCmd.Login, ShouldEqual, "torkelo")
  65. So(sc.createUserCmd.Email, ShouldEqual, "my@email.com")
  66. })
  67. */
  68. })
  69. })
  70. Convey("When calling SyncSignedInUser", t, func() {
  71. mockLdapConnection := &mockLdapConn{}
  72. ldapAuther := NewLdapAuthenticator(
  73. &LdapServerConf{
  74. Host: "",
  75. RootCACert: "",
  76. LdapGroups: []*LdapGroupToOrgRole{
  77. {GroupDN: "*", OrgRole: "Admin"},
  78. },
  79. Attr: LdapAttributeMap{
  80. Username: "username",
  81. Surname: "surname",
  82. Email: "email",
  83. Name: "name",
  84. MemberOf: "memberof",
  85. },
  86. SearchBaseDNs: []string{"BaseDNHere"},
  87. },
  88. )
  89. dialCalled := false
  90. ldapDial = func(network, addr string) (ILdapConn, error) {
  91. dialCalled = true
  92. return mockLdapConnection, nil
  93. }
  94. entry := ldap.Entry{
  95. DN: "dn", Attributes: []*ldap.EntryAttribute{
  96. {Name: "username", Values: []string{"roelgerrits"}},
  97. {Name: "surname", Values: []string{"Gerrits"}},
  98. {Name: "email", Values: []string{"roel@test.com"}},
  99. {Name: "name", Values: []string{"Roel"}},
  100. {Name: "memberof", Values: []string{"admins"}},
  101. }}
  102. result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
  103. mockLdapConnection.setSearchResult(&result)
  104. ldapAutherScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) {
  105. // arrange
  106. signedInUser := &m.SignedInUser{
  107. Email: "roel@test.net",
  108. UserId: 1,
  109. Name: "Roel Gerrits",
  110. Login: "roelgerrits",
  111. }
  112. sc.userQueryReturns(&m.User{
  113. Id: 1,
  114. Email: "roel@test.net",
  115. Name: "Roel Gerrits",
  116. Login: "roelgerrits",
  117. })
  118. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  119. // act
  120. syncErrResult := ldapAuther.SyncSignedInUser(nil, signedInUser)
  121. // assert
  122. So(dialCalled, ShouldBeTrue)
  123. So(syncErrResult, ShouldBeNil)
  124. // User should be searched in ldap
  125. So(mockLdapConnection.searchCalled, ShouldBeTrue)
  126. // Info should be updated (email differs)
  127. So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com")
  128. // User should have admin privileges
  129. So(sc.addOrgUserCmd.UserId, ShouldEqual, 1)
  130. So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
  131. })
  132. })
  133. }
  134. type mockLdapConn struct {
  135. result *ldap.SearchResult
  136. searchCalled bool
  137. }
  138. func (c *mockLdapConn) Bind(username, password string) error {
  139. return nil
  140. }
  141. func (c *mockLdapConn) Close() {}
  142. func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) {
  143. c.result = result
  144. }
  145. func (c *mockLdapConn) Search(*ldap.SearchRequest) (*ldap.SearchResult, error) {
  146. c.searchCalled = true
  147. return c.result, nil
  148. }
  149. func (c *mockLdapConn) StartTLS(*tls.Config) error {
  150. return nil
  151. }
  152. func ldapAutherScenario(desc string, fn scenarioFunc) {
  153. Convey(desc, func() {
  154. defer bus.ClearBusHandlers()
  155. sc := &scenarioContext{}
  156. bus.AddHandler("test", UpsertUser)
  157. bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
  158. sc.getUserByAuthInfoQuery = cmd
  159. sc.getUserByAuthInfoQuery.User = &m.User{Login: cmd.Login}
  160. return nil
  161. })
  162. bus.AddHandler("test", func(cmd *m.GetUserOrgListQuery) error {
  163. sc.getUserOrgListQuery = cmd
  164. return nil
  165. })
  166. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  167. sc.createUserCmd = cmd
  168. sc.createUserCmd.Result = m.User{Login: cmd.Login}
  169. return nil
  170. })
  171. bus.AddHandler("test", func(cmd *m.AddOrgUserCommand) error {
  172. sc.addOrgUserCmd = cmd
  173. return nil
  174. })
  175. bus.AddHandler("test", func(cmd *m.UpdateOrgUserCommand) error {
  176. sc.updateOrgUserCmd = cmd
  177. return nil
  178. })
  179. bus.AddHandler("test", func(cmd *m.RemoveOrgUserCommand) error {
  180. sc.removeOrgUserCmd = cmd
  181. return nil
  182. })
  183. bus.AddHandler("test", func(cmd *m.UpdateUserCommand) error {
  184. sc.updateUserCmd = cmd
  185. return nil
  186. })
  187. fn(sc)
  188. })
  189. }
  190. type scenarioContext struct {
  191. getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
  192. getUserOrgListQuery *m.GetUserOrgListQuery
  193. createUserCmd *m.CreateUserCommand
  194. addOrgUserCmd *m.AddOrgUserCommand
  195. updateOrgUserCmd *m.UpdateOrgUserCommand
  196. removeOrgUserCmd *m.RemoveOrgUserCommand
  197. updateUserCmd *m.UpdateUserCommand
  198. }
  199. func (sc *scenarioContext) userQueryReturns(user *m.User) {
  200. bus.AddHandler("test", func(query *m.GetUserByAuthInfoQuery) error {
  201. if user == nil {
  202. return m.ErrUserNotFound
  203. } else {
  204. query.User = user
  205. return nil
  206. }
  207. })
  208. bus.AddHandler("test", func(query *m.SetAuthInfoCommand) error {
  209. return nil
  210. })
  211. }
  212. func (sc *scenarioContext) userOrgsQueryReturns(orgs []*m.UserOrgDTO) {
  213. bus.AddHandler("test", func(query *m.GetUserOrgListQuery) error {
  214. query.Result = orgs
  215. return nil
  216. })
  217. }
  218. type scenarioFunc func(c *scenarioContext)