ldap_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. package login
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "testing"
  6. "github.com/go-ldap/ldap"
  7. "github.com/grafana/grafana/pkg/bus"
  8. m "github.com/grafana/grafana/pkg/models"
  9. . "github.com/smartystreets/goconvey/convey"
  10. )
  11. func TestLdapAuther(t *testing.T) {
  12. Convey("When translating ldap user to grafana user", t, func() {
  13. var user1 = &m.User{}
  14. bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpsertUserCommand) error {
  15. cmd.Result = user1
  16. cmd.Result.Login = "torkelo"
  17. return nil
  18. })
  19. Convey("Given no ldap group map match", func() {
  20. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  21. LdapGroups: []*LdapGroupToOrgRole{{}},
  22. })
  23. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{})
  24. So(err, ShouldEqual, ErrInvalidCredentials)
  25. })
  26. ldapAutherScenario("Given wildcard group match", func(sc *scenarioContext) {
  27. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  28. LdapGroups: []*LdapGroupToOrgRole{
  29. {GroupDN: "*", OrgRole: "Admin"},
  30. },
  31. })
  32. sc.userQueryReturns(user1)
  33. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{})
  34. So(err, ShouldBeNil)
  35. So(result, ShouldEqual, user1)
  36. })
  37. ldapAutherScenario("Given exact group match", func(sc *scenarioContext) {
  38. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  39. LdapGroups: []*LdapGroupToOrgRole{
  40. {GroupDN: "cn=users", OrgRole: "Admin"},
  41. },
  42. })
  43. sc.userQueryReturns(user1)
  44. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{MemberOf: []string{"cn=users"}})
  45. So(err, ShouldBeNil)
  46. So(result, ShouldEqual, user1)
  47. })
  48. ldapAutherScenario("Given group match with different case", func(sc *scenarioContext) {
  49. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  50. LdapGroups: []*LdapGroupToOrgRole{
  51. {GroupDN: "cn=users", OrgRole: "Admin"},
  52. },
  53. })
  54. sc.userQueryReturns(user1)
  55. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{MemberOf: []string{"CN=users"}})
  56. So(err, ShouldBeNil)
  57. So(result, ShouldEqual, user1)
  58. })
  59. ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) {
  60. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  61. LdapGroups: []*LdapGroupToOrgRole{
  62. {GroupDN: "cn=admin", OrgRole: "Admin"},
  63. {GroupDN: "cn=editor", OrgRole: "Editor"},
  64. {GroupDN: "*", OrgRole: "Viewer"},
  65. },
  66. })
  67. sc.userQueryReturns(nil)
  68. result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  69. DN: "torkelo",
  70. Username: "torkelo",
  71. Email: "my@email.com",
  72. MemberOf: []string{"cn=editor"},
  73. })
  74. So(err, ShouldBeNil)
  75. Convey("Should return new user", func() {
  76. So(result.Login, ShouldEqual, "torkelo")
  77. })
  78. Convey("Should set isGrafanaAdmin to false by default", func() {
  79. So(result.IsAdmin, ShouldBeFalse)
  80. })
  81. })
  82. })
  83. Convey("When syncing ldap groups to grafana org roles", t, func() {
  84. ldapAutherScenario("given no current user orgs", func(sc *scenarioContext) {
  85. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  86. LdapGroups: []*LdapGroupToOrgRole{
  87. {GroupDN: "cn=users", OrgRole: "Admin"},
  88. },
  89. })
  90. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  91. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  92. MemberOf: []string{"cn=users"},
  93. })
  94. Convey("Should create new org user", func() {
  95. So(err, ShouldBeNil)
  96. So(sc.addOrgUserCmd, ShouldNotBeNil)
  97. So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  98. })
  99. })
  100. ldapAutherScenario("given different current org role", func(sc *scenarioContext) {
  101. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  102. LdapGroups: []*LdapGroupToOrgRole{
  103. {GroupDN: "cn=users", OrgId: 1, OrgRole: "Admin"},
  104. },
  105. })
  106. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
  107. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  108. MemberOf: []string{"cn=users"},
  109. })
  110. Convey("Should update org role", func() {
  111. So(err, ShouldBeNil)
  112. So(sc.updateOrgUserCmd, ShouldNotBeNil)
  113. So(sc.updateOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  114. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  115. })
  116. })
  117. ldapAutherScenario("given current org role is removed in ldap", func(sc *scenarioContext) {
  118. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  119. LdapGroups: []*LdapGroupToOrgRole{
  120. {GroupDN: "cn=users", OrgId: 2, OrgRole: "Admin"},
  121. },
  122. })
  123. sc.userOrgsQueryReturns([]*m.UserOrgDTO{
  124. {OrgId: 1, Role: m.ROLE_EDITOR},
  125. {OrgId: 2, Role: m.ROLE_EDITOR},
  126. })
  127. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  128. MemberOf: []string{"cn=users"},
  129. })
  130. Convey("Should remove org role", func() {
  131. So(err, ShouldBeNil)
  132. So(sc.removeOrgUserCmd, ShouldNotBeNil)
  133. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 2)
  134. })
  135. })
  136. ldapAutherScenario("given org role is updated in config", func(sc *scenarioContext) {
  137. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  138. LdapGroups: []*LdapGroupToOrgRole{
  139. {GroupDN: "cn=admin", OrgId: 1, OrgRole: "Admin"},
  140. {GroupDN: "cn=users", OrgId: 1, OrgRole: "Viewer"},
  141. },
  142. })
  143. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_EDITOR}})
  144. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  145. MemberOf: []string{"cn=users"},
  146. })
  147. Convey("Should update org role", func() {
  148. So(err, ShouldBeNil)
  149. So(sc.removeOrgUserCmd, ShouldBeNil)
  150. So(sc.updateOrgUserCmd, ShouldNotBeNil)
  151. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  152. })
  153. })
  154. ldapAutherScenario("given multiple matching ldap groups", func(sc *scenarioContext) {
  155. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  156. LdapGroups: []*LdapGroupToOrgRole{
  157. {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
  158. {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
  159. },
  160. })
  161. sc.userOrgsQueryReturns([]*m.UserOrgDTO{{OrgId: 1, Role: m.ROLE_ADMIN}})
  162. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  163. MemberOf: []string{"cn=admins"},
  164. })
  165. Convey("Should take first match, and ignore subsequent matches", func() {
  166. So(err, ShouldBeNil)
  167. So(sc.updateOrgUserCmd, ShouldBeNil)
  168. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  169. })
  170. })
  171. ldapAutherScenario("given multiple matching ldap groups and no existing groups", func(sc *scenarioContext) {
  172. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  173. LdapGroups: []*LdapGroupToOrgRole{
  174. {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin"},
  175. {GroupDN: "*", OrgId: 1, OrgRole: "Viewer"},
  176. },
  177. })
  178. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  179. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  180. MemberOf: []string{"cn=admins"},
  181. })
  182. Convey("Should take first match, and ignore subsequent matches", func() {
  183. So(err, ShouldBeNil)
  184. So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
  185. So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
  186. })
  187. Convey("Should not update permissions unless specified", func() {
  188. So(err, ShouldBeNil)
  189. So(sc.updateUserPermissionsCmd, ShouldBeNil)
  190. })
  191. })
  192. ldapAutherScenario("given ldap groups with grafana_admin=true", func(sc *scenarioContext) {
  193. trueVal := true
  194. ldapAuther := NewLdapAuthenticator(&LdapServerConf{
  195. LdapGroups: []*LdapGroupToOrgRole{
  196. {GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin", IsGrafanaAdmin: &trueVal},
  197. },
  198. })
  199. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  200. _, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
  201. MemberOf: []string{"cn=admins"},
  202. })
  203. Convey("Should create user with admin set to true", func() {
  204. So(err, ShouldBeNil)
  205. So(sc.updateUserPermissionsCmd.IsGrafanaAdmin, ShouldBeTrue)
  206. })
  207. })
  208. })
  209. Convey("When calling SyncUser", t, func() {
  210. mockLdapConnection := &mockLdapConn{}
  211. ldapAuther := NewLdapAuthenticator(
  212. &LdapServerConf{
  213. Host: "",
  214. RootCACert: "",
  215. LdapGroups: []*LdapGroupToOrgRole{
  216. {GroupDN: "*", OrgRole: "Admin"},
  217. },
  218. Attr: LdapAttributeMap{
  219. Username: "username",
  220. Surname: "surname",
  221. Email: "email",
  222. Name: "name",
  223. MemberOf: "memberof",
  224. },
  225. SearchBaseDNs: []string{"BaseDNHere"},
  226. },
  227. )
  228. dialCalled := false
  229. ldapDial = func(network, addr string) (ILdapConn, error) {
  230. dialCalled = true
  231. return mockLdapConnection, nil
  232. }
  233. entry := ldap.Entry{
  234. DN: "dn", Attributes: []*ldap.EntryAttribute{
  235. {Name: "username", Values: []string{"roelgerrits"}},
  236. {Name: "surname", Values: []string{"Gerrits"}},
  237. {Name: "email", Values: []string{"roel@test.com"}},
  238. {Name: "name", Values: []string{"Roel"}},
  239. {Name: "memberof", Values: []string{"admins"}},
  240. }}
  241. result := ldap.SearchResult{Entries: []*ldap.Entry{&entry}}
  242. mockLdapConnection.setSearchResult(&result)
  243. ldapAutherScenario("When ldapUser found call syncInfo and orgRoles", func(sc *scenarioContext) {
  244. // arrange
  245. query := &m.LoginUserQuery{
  246. Username: "roelgerrits",
  247. }
  248. sc.userQueryReturns(&m.User{
  249. Id: 1,
  250. Email: "roel@test.net",
  251. Name: "Roel Gerrits",
  252. Login: "roelgerrits",
  253. })
  254. sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
  255. // act
  256. syncErrResult := ldapAuther.SyncUser(query)
  257. // assert
  258. So(dialCalled, ShouldBeTrue)
  259. So(syncErrResult, ShouldBeNil)
  260. // User should be searched in ldap
  261. So(mockLdapConnection.searchCalled, ShouldBeTrue)
  262. // Info should be updated (email differs)
  263. So(sc.updateUserCmd.Email, ShouldEqual, "roel@test.com")
  264. // User should have admin privileges
  265. So(sc.addOrgUserCmd.UserId, ShouldEqual, 1)
  266. So(sc.addOrgUserCmd.Role, ShouldEqual, "Admin")
  267. })
  268. })
  269. }
  270. type mockLdapConn struct {
  271. result *ldap.SearchResult
  272. searchCalled bool
  273. }
  274. func (c *mockLdapConn) Bind(username, password string) error {
  275. return nil
  276. }
  277. func (c *mockLdapConn) Close() {}
  278. func (c *mockLdapConn) setSearchResult(result *ldap.SearchResult) {
  279. c.result = result
  280. }
  281. func (c *mockLdapConn) Search(*ldap.SearchRequest) (*ldap.SearchResult, error) {
  282. c.searchCalled = true
  283. return c.result, nil
  284. }
  285. func (c *mockLdapConn) StartTLS(*tls.Config) error {
  286. return nil
  287. }
  288. func ldapAutherScenario(desc string, fn scenarioFunc) {
  289. Convey(desc, func() {
  290. defer bus.ClearBusHandlers()
  291. sc := &scenarioContext{}
  292. bus.AddHandler("test", UpsertUser)
  293. bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.SyncTeamsCommand) error {
  294. return nil
  295. })
  296. bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpdateUserPermissionsCommand) error {
  297. sc.updateUserPermissionsCmd = cmd
  298. return nil
  299. })
  300. bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
  301. sc.getUserByAuthInfoQuery = cmd
  302. sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login}
  303. return nil
  304. })
  305. bus.AddHandler("test", func(cmd *m.GetUserOrgListQuery) error {
  306. sc.getUserOrgListQuery = cmd
  307. return nil
  308. })
  309. bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
  310. sc.createUserCmd = cmd
  311. sc.createUserCmd.Result = m.User{Login: cmd.Login}
  312. return nil
  313. })
  314. bus.AddHandler("test", func(cmd *m.AddOrgUserCommand) error {
  315. sc.addOrgUserCmd = cmd
  316. return nil
  317. })
  318. bus.AddHandler("test", func(cmd *m.UpdateOrgUserCommand) error {
  319. sc.updateOrgUserCmd = cmd
  320. return nil
  321. })
  322. bus.AddHandler("test", func(cmd *m.RemoveOrgUserCommand) error {
  323. sc.removeOrgUserCmd = cmd
  324. return nil
  325. })
  326. bus.AddHandler("test", func(cmd *m.UpdateUserCommand) error {
  327. sc.updateUserCmd = cmd
  328. return nil
  329. })
  330. bus.AddHandler("test", func(cmd *m.SetUsingOrgCommand) error {
  331. sc.setUsingOrgCmd = cmd
  332. return nil
  333. })
  334. fn(sc)
  335. })
  336. }
  337. type scenarioContext struct {
  338. getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
  339. getUserOrgListQuery *m.GetUserOrgListQuery
  340. createUserCmd *m.CreateUserCommand
  341. addOrgUserCmd *m.AddOrgUserCommand
  342. updateOrgUserCmd *m.UpdateOrgUserCommand
  343. removeOrgUserCmd *m.RemoveOrgUserCommand
  344. updateUserCmd *m.UpdateUserCommand
  345. setUsingOrgCmd *m.SetUsingOrgCommand
  346. updateUserPermissionsCmd *m.UpdateUserPermissionsCommand
  347. }
  348. func (sc *scenarioContext) userQueryReturns(user *m.User) {
  349. bus.AddHandler("test", func(query *m.GetUserByAuthInfoQuery) error {
  350. if user == nil {
  351. return m.ErrUserNotFound
  352. }
  353. query.Result = user
  354. return nil
  355. })
  356. bus.AddHandler("test", func(query *m.SetAuthInfoCommand) error {
  357. return nil
  358. })
  359. }
  360. func (sc *scenarioContext) userOrgsQueryReturns(orgs []*m.UserOrgDTO) {
  361. bus.AddHandler("test", func(query *m.GetUserOrgListQuery) error {
  362. query.Result = orgs
  363. return nil
  364. })
  365. }
  366. type scenarioFunc func(c *scenarioContext)