ldap_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. })
  63. Convey("When syncing ldap groups to grafana org roles", t, func() {
  64. ldapAutherScenario("given no current user orgs", func(sc *scenarioContext) {
  65. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  66. LdapGroups: []*LdapGroupToOrgRole{
  67. {GroupDN: "cn=users", OrgRole: "Admin"},
  68. },
  69. })
  70. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  71. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  72. MemberOf: []string{"cn=users"},
  73. })
  74. Convey("Should create new org user", func() {
  75. So(err, ShouldBeNil)
  76. So(sc.addOrgUserCmd, ShouldNotBeNil)
  77. So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  78. })
  79. })
  80. ldapAutherScenario("given different current org role", func(sc *scenarioContext) {
  81. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  82. LdapGroups: []*LdapGroupToOrgRole{
  83. {GroupDN: "cn=users", OrgId: 1, OrgRole: "Admin"},
  84. },
  85. })
  86. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
  87. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  88. MemberOf: []string{"cn=users"},
  89. })
  90. Convey("Should update org role", func() {
  91. So(err, ShouldBeNil)
  92. So(sc.updateOrgUserCmd, ShouldNotBeNil)
  93. So(sc.updateOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  94. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  95. })
  96. })
  97. ldapAutherScenario("given current org role is removed in ldap", func(sc *scenarioContext) {
  98. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  99. LdapGroups: []*LdapGroupToOrgRole{
  100. {GroupDN: "cn=users", OrgId: 2, OrgRole: "Admin"},
  101. },
  102. })
  103. sc.userOrgsQueryReturns([]*m.UserOrgDTO{
  104. {OrgId: 1, Role: m.ROLE_EDITOR},
  105. {OrgId: 2, Role: m.ROLE_EDITOR},
  106. })
  107. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  108. MemberOf: []string{"cn=users"},
  109. })
  110. Convey("Should remove org role", func() {
  111. So(err, ShouldBeNil)
  112. So(sc.removeOrgUserCmd, ShouldNotBeNil)
  113. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 2)
  114. })
  115. })
  116. ldapAutherScenario("given org role is updated in config", func(sc *scenarioContext) {
  117. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  118. LdapGroups: []*LdapGroupToOrgRole{
  119. {GroupDN: "cn=admin", OrgId: 1, OrgRole: "Admin"},
  120. {GroupDN: "cn=users", OrgId: 1, OrgRole: "Viewer"},
  121. },
  122. })
  123. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
  124. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  125. MemberOf: []string{"cn=users"},
  126. })
  127. Convey("Should update org role", func() {
  128. So(err, ShouldBeNil)
  129. So(sc.removeOrgUserCmd, ShouldBeNil)
  130. So(sc.updateOrgUserCmd, ShouldNotBeNil)
  131. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  132. })
  133. })
  134. ldapAutherScenario("given multiple matching ldap groups", func(sc *scenarioContext) {
  135. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  136. LdapGroups: []*LdapGroupToOrgRole{
  137. {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
  138. {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
  139. },
  140. })
  141. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_ADMIN}})
  142. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  143. MemberOf: []string{"cn=admins"},
  144. })
  145. Convey("Should take first match, and ignore subsequent matches", func() {
  146. So(err, ShouldBeNil)
  147. So(sc.updateOrgUserCmd, ShouldBeNil)
  148. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  149. })
  150. })
  151. ldapAutherScenario("given multiple matching ldap groups and no existing groups", func(sc *scenarioContext) {
  152. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  153. LdapGroups: []*LdapGroupToOrgRole{
  154. {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
  155. {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
  156. },
  157. })
  158. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  159. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  160. MemberOf: []string{"cn=admins"},
  161. })
  162. Convey("Should take first match, and ignore subsequent matches", func() {
  163. So(err, ShouldBeNil)
  164. So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  165. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  166. })
  167. })
  168. })
  169. Convey("When calling SyncUser", t, func() {
  170. mockLdapConnection := &mockLdapConn{}
  171. ldapAuther := NewLdapAuthenticator(
  172. &LdapServerConf{
  173. Host: "",
  174. RootCACert: "",
  175. LdapGroups: []*LdapGroupToOrgRole{
  176. {GroupDN: "*", OrgRole: "Admin"},
  177. },
  178. Attr: LdapAttributeMap{
  179. Username: "username",
  180. Surname: "surname",
  181. Email: "email",
  182. Name: "name",
  183. MemberOf: "memberof",
  184. },
  185. SearchBaseDNs: []string{"BaseDNHere"},
  186. },
  187. )
  188. dialCalled := false
  189. ldapDial = func(network, addr string) (ILdapConn, error) {
  190. dialCalled = true
  191. return mockLdapConnection, nil
  192. }
  193. entry := ldap.Entry{
  194. DN: "dn", Attributes: []*ldap.EntryAttribute{
  195. {Name: "username", Values: []string{"roelgerrits"}},
  196. {Name: "surname", Values: []string{"Gerrits"}},
  197. {Name: "email", Values: []string{"roel@test.com"}},
  198. {Name: "name", Values: []string{"Roel"}},
  199. {Name: "memberof", Values: []string{"admins"}},
  200. }}
  201. result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
  202. mockLdapConnection.setSearchResult(&result)
  203. ldapAutherScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) {
  204. // arrange
  205. query := &m.LoginUserQuery{
  206. Username: "roelgerrits",
  207. }
  208. sc.userQueryReturns(&m.User{
  209. Id: 1,
  210. Email: "roel@test.net",
  211. Name: "Roel Gerrits",
  212. Login: "roelgerrits",
  213. })
  214. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  215. // act
  216. syncErrResult := ldapAuther.SyncUser(query)
  217. // assert
  218. So(dialCalled, ShouldBeTrue)
  219. So(syncErrResult, ShouldBeNil)
  220. // User should be searched in ldap
  221. So(mockLdapConnection.searchCalled, ShouldBeTrue)
  222. // Info should be updated (email differs)
  223. So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com")
  224. // User should have admin privileges
  225. So(sc.addOrgUserCmd.UserId, ShouldEqual, 1)
  226. So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
  227. })
  228. })
  229. }
  230. type mockLdapConn struct {
  231. result *ldap.SearchResult
  232. searchCalled bool
  233. }
  234. func (c *mockLdapConn) Bind(username, password string) error {
  235. return nil
  236. }
  237. func (c *mockLdapConn) Close() {}
  238. func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) {
  239. c.result = result
  240. }
  241. func (c *mockLdapConn) Search(*ldap.SearchRequest) (*ldap.SearchResult, error) {
  242. c.searchCalled = true
  243. return c.result, nil
  244. }
  245. func (c *mockLdapConn) StartTLS(*tls.Config) error {
  246. return nil
  247. }
  248. func ldapAutherScenario(desc string, fn scenarioFunc) {
  249. Convey(desc, func() {
  250. defer bus.ClearBusHandlers()
  251. sc := &scenarioContext{}
  252. bus.AddHandler("test", UpsertUser)
  253. bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
  254. sc.getUserByAuthInfoQuery = cmd
  255. sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login}
  256. return nil
  257. })
  258. bus.AddHandler("test", func(cmd *m.GetUserOrgListQuery) error {
  259. sc.getUserOrgListQuery = cmd
  260. return nil
  261. })
  262. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  263. sc.createUserCmd = cmd
  264. sc.createUserCmd.Result = m.User{Login: cmd.Login}
  265. return nil
  266. })
  267. bus.AddHandler("test", func(cmd *m.AddOrgUserCommand) error {
  268. sc.addOrgUserCmd = cmd
  269. return nil
  270. })
  271. bus.AddHandler("test", func(cmd *m.UpdateOrgUserCommand) error {
  272. sc.updateOrgUserCmd = cmd
  273. return nil
  274. })
  275. bus.AddHandler("test", func(cmd *m.RemoveOrgUserCommand) error {
  276. sc.removeOrgUserCmd = cmd
  277. return nil
  278. })
  279. bus.AddHandler("test", func(cmd *m.UpdateUserCommand) error {
  280. sc.updateUserCmd = cmd
  281. return nil
  282. })
  283. bus.AddHandler("test", func(cmd *m.SetUsingOrgCommand) error {
  284. sc.setUsingOrgCmd = cmd
  285. return nil
  286. })
  287. fn(sc)
  288. })
  289. }
  290. type scenarioContext struct {
  291. getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
  292. getUserOrgListQuery *m.GetUserOrgListQuery
  293. createUserCmd *m.CreateUserCommand
  294. addOrgUserCmd *m.AddOrgUserCommand
  295. updateOrgUserCmd *m.UpdateOrgUserCommand
  296. removeOrgUserCmd *m.RemoveOrgUserCommand
  297. updateUserCmd *m.UpdateUserCommand
  298. setUsingOrgCmd *m.SetUsingOrgCommand
  299. }
  300. func (sc *scenarioContext) userQueryReturns(user *m.User) {
  301. bus.AddHandler("test", func(query *m.GetUserByAuthInfoQuery) error {
  302. if user == nil {
  303. return m.ErrUserNotFound
  304. }
  305. query.Result = user
  306. return nil
  307. })
  308. bus.AddHandler("test", func(query *m.SetAuthInfoCommand) error {
  309. return nil
  310. })
  311. }
  312. func (sc *scenarioContext) userOrgsQueryReturns(orgs []*m.UserOrgDTO) {
  313. bus.AddHandler("test", func(query *m.GetUserOrgListQuery) error {
  314. query.Result = orgs
  315. return nil
  316. })
  317. }
  318. type scenarioFunc func(c *scenarioContext)