ldap.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. package ldap
  2. import (
  3. "crypto/tls"
  4. "crypto/x509"
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "strings"
  9. "github.com/davecgh/go-spew/spew"
  10. LDAP "gopkg.in/ldap.v3"
  11. "github.com/grafana/grafana/pkg/bus"
  12. "github.com/grafana/grafana/pkg/log"
  13. models "github.com/grafana/grafana/pkg/models"
  14. "github.com/grafana/grafana/pkg/setting"
  15. )
  16. // IConnection is interface for LDAP connection manipulation
  17. type IConnection interface {
  18. Bind(username, password string) error
  19. UnauthenticatedBind(username string) error
  20. Search(*LDAP.SearchRequest) (*LDAP.SearchResult, error)
  21. StartTLS(*tls.Config) error
  22. Close()
  23. }
  24. // IAuth is interface for LDAP authorization
  25. type IAuth interface {
  26. Login(query *models.LoginUserQuery) error
  27. SyncUser(query *models.LoginUserQuery) error
  28. GetGrafanaUserFor(
  29. ctx *models.ReqContext,
  30. user *UserInfo,
  31. ) (*models.User, error)
  32. Users() ([]*UserInfo, error)
  33. }
  34. // Auth is basic struct of LDAP authorization
  35. type Auth struct {
  36. server *ServerConfig
  37. conn IConnection
  38. requireSecondBind bool
  39. log log.Logger
  40. }
  41. var (
  42. // ErrInvalidCredentials is returned if username and password do not match
  43. ErrInvalidCredentials = errors.New("Invalid Username or Password")
  44. )
  45. var dial = func(network, addr string) (IConnection, error) {
  46. return LDAP.Dial(network, addr)
  47. }
  48. // New creates the new LDAP auth
  49. func New(server *ServerConfig) IAuth {
  50. return &Auth{
  51. server: server,
  52. log: log.New("ldap"),
  53. }
  54. }
  55. // Dial dials in the LDAP
  56. func (auth *Auth) Dial() error {
  57. if hookDial != nil {
  58. return hookDial(auth)
  59. }
  60. var err error
  61. var certPool *x509.CertPool
  62. if auth.server.RootCACert != "" {
  63. certPool = x509.NewCertPool()
  64. for _, caCertFile := range strings.Split(auth.server.RootCACert, " ") {
  65. pem, err := ioutil.ReadFile(caCertFile)
  66. if err != nil {
  67. return err
  68. }
  69. if !certPool.AppendCertsFromPEM(pem) {
  70. return errors.New("Failed to append CA certificate " + caCertFile)
  71. }
  72. }
  73. }
  74. var clientCert tls.Certificate
  75. if auth.server.ClientCert != "" && auth.server.ClientKey != "" {
  76. clientCert, err = tls.LoadX509KeyPair(auth.server.ClientCert, auth.server.ClientKey)
  77. if err != nil {
  78. return err
  79. }
  80. }
  81. for _, host := range strings.Split(auth.server.Host, " ") {
  82. address := fmt.Sprintf("%s:%d", host, auth.server.Port)
  83. if auth.server.UseSSL {
  84. tlsCfg := &tls.Config{
  85. InsecureSkipVerify: auth.server.SkipVerifySSL,
  86. ServerName: host,
  87. RootCAs: certPool,
  88. }
  89. if len(clientCert.Certificate) > 0 {
  90. tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
  91. }
  92. if auth.server.StartTLS {
  93. auth.conn, err = dial("tcp", address)
  94. if err == nil {
  95. if err = auth.conn.StartTLS(tlsCfg); err == nil {
  96. return nil
  97. }
  98. }
  99. } else {
  100. auth.conn, err = LDAP.DialTLS("tcp", address, tlsCfg)
  101. }
  102. } else {
  103. auth.conn, err = dial("tcp", address)
  104. }
  105. if err == nil {
  106. return nil
  107. }
  108. }
  109. return err
  110. }
  111. // Login logs in the user
  112. func (auth *Auth) Login(query *models.LoginUserQuery) error {
  113. // connect to ldap server
  114. if err := auth.Dial(); err != nil {
  115. return err
  116. }
  117. defer auth.conn.Close()
  118. // perform initial authentication
  119. if err := auth.initialBind(query.Username, query.Password); err != nil {
  120. return err
  121. }
  122. // find user entry & attributes
  123. user, err := auth.searchForUser(query.Username)
  124. if err != nil {
  125. return err
  126. }
  127. auth.log.Debug("Ldap User found", "info", spew.Sdump(user))
  128. // check if a second user bind is needed
  129. if auth.requireSecondBind {
  130. err = auth.secondBind(user, query.Password)
  131. if err != nil {
  132. return err
  133. }
  134. }
  135. grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user)
  136. if err != nil {
  137. return err
  138. }
  139. query.User = grafanaUser
  140. return nil
  141. }
  142. // SyncUser syncs user with Grafana
  143. func (auth *Auth) SyncUser(query *models.LoginUserQuery) error {
  144. // connect to ldap server
  145. err := auth.Dial()
  146. if err != nil {
  147. return err
  148. }
  149. defer auth.conn.Close()
  150. err = auth.serverBind()
  151. if err != nil {
  152. return err
  153. }
  154. // find user entry & attributes
  155. user, err := auth.searchForUser(query.Username)
  156. if err != nil {
  157. auth.log.Error("Failed searching for user in ldap", "error", err)
  158. return err
  159. }
  160. auth.log.Debug("Ldap User found", "info", spew.Sdump(user))
  161. grafanaUser, err := auth.GetGrafanaUserFor(query.ReqContext, user)
  162. if err != nil {
  163. return err
  164. }
  165. query.User = grafanaUser
  166. return nil
  167. }
  168. func (auth *Auth) GetGrafanaUserFor(
  169. ctx *models.ReqContext,
  170. user *UserInfo,
  171. ) (*models.User, error) {
  172. extUser := &models.ExternalUserInfo{
  173. AuthModule: "ldap",
  174. AuthId: user.DN,
  175. Name: fmt.Sprintf("%s %s", user.FirstName, user.LastName),
  176. Login: user.Username,
  177. Email: user.Email,
  178. Groups: user.MemberOf,
  179. OrgRoles: map[int64]models.RoleType{},
  180. }
  181. for _, group := range auth.server.Groups {
  182. // only use the first match for each org
  183. if extUser.OrgRoles[group.OrgId] != "" {
  184. continue
  185. }
  186. if user.isMemberOf(group.GroupDN) {
  187. extUser.OrgRoles[group.OrgId] = group.OrgRole
  188. if extUser.IsGrafanaAdmin == nil || !*extUser.IsGrafanaAdmin {
  189. extUser.IsGrafanaAdmin = group.IsGrafanaAdmin
  190. }
  191. }
  192. }
  193. // validate that the user has access
  194. // if there are no ldap group mappings access is true
  195. // otherwise a single group must match
  196. if len(auth.server.Groups) > 0 && len(extUser.OrgRoles) < 1 {
  197. auth.log.Info(
  198. "Ldap Auth: user does not belong in any of the specified ldap groups",
  199. "username", user.Username,
  200. "groups", user.MemberOf,
  201. )
  202. return nil, ErrInvalidCredentials
  203. }
  204. // add/update user in grafana
  205. upsertUserCmd := &models.UpsertUserCommand{
  206. ReqContext: ctx,
  207. ExternalUser: extUser,
  208. SignupAllowed: setting.LdapAllowSignup,
  209. }
  210. err := bus.Dispatch(upsertUserCmd)
  211. if err != nil {
  212. return nil, err
  213. }
  214. return upsertUserCmd.Result, nil
  215. }
  216. func (auth *Auth) serverBind() error {
  217. bindFn := func() error {
  218. return auth.conn.Bind(auth.server.BindDN, auth.server.BindPassword)
  219. }
  220. if auth.server.BindPassword == "" {
  221. bindFn = func() error {
  222. return auth.conn.UnauthenticatedBind(auth.server.BindDN)
  223. }
  224. }
  225. // bind_dn and bind_password to bind
  226. if err := bindFn(); err != nil {
  227. auth.log.Info("LDAP initial bind failed, %v", err)
  228. if ldapErr, ok := err.(*LDAP.Error); ok {
  229. if ldapErr.ResultCode == 49 {
  230. return ErrInvalidCredentials
  231. }
  232. }
  233. return err
  234. }
  235. return nil
  236. }
  237. func (auth *Auth) secondBind(user *UserInfo, userPassword string) error {
  238. if err := auth.conn.Bind(user.DN, userPassword); err != nil {
  239. auth.log.Info("Second bind failed", "error", err)
  240. if ldapErr, ok := err.(*LDAP.Error); ok {
  241. if ldapErr.ResultCode == 49 {
  242. return ErrInvalidCredentials
  243. }
  244. }
  245. return err
  246. }
  247. return nil
  248. }
  249. func (auth *Auth) initialBind(username, userPassword string) error {
  250. if auth.server.BindPassword != "" || auth.server.BindDN == "" {
  251. userPassword = auth.server.BindPassword
  252. auth.requireSecondBind = true
  253. }
  254. bindPath := auth.server.BindDN
  255. if strings.Contains(bindPath, "%s") {
  256. bindPath = fmt.Sprintf(auth.server.BindDN, username)
  257. }
  258. bindFn := func() error {
  259. return auth.conn.Bind(bindPath, userPassword)
  260. }
  261. if userPassword == "" {
  262. bindFn = func() error {
  263. return auth.conn.UnauthenticatedBind(bindPath)
  264. }
  265. }
  266. if err := bindFn(); err != nil {
  267. auth.log.Info("Initial bind failed", "error", err)
  268. if ldapErr, ok := err.(*LDAP.Error); ok {
  269. if ldapErr.ResultCode == 49 {
  270. return ErrInvalidCredentials
  271. }
  272. }
  273. return err
  274. }
  275. return nil
  276. }
  277. func (auth *Auth) searchForUser(username string) (*UserInfo, error) {
  278. var searchResult *LDAP.SearchResult
  279. var err error
  280. for _, searchBase := range auth.server.SearchBaseDNs {
  281. attributes := make([]string, 0)
  282. inputs := auth.server.Attr
  283. attributes = appendIfNotEmpty(attributes,
  284. inputs.Username,
  285. inputs.Surname,
  286. inputs.Email,
  287. inputs.Name,
  288. inputs.MemberOf)
  289. searchReq := LDAP.SearchRequest{
  290. BaseDN: searchBase,
  291. Scope: LDAP.ScopeWholeSubtree,
  292. DerefAliases: LDAP.NeverDerefAliases,
  293. Attributes: attributes,
  294. Filter: strings.Replace(
  295. auth.server.SearchFilter,
  296. "%s", LDAP.EscapeFilter(username),
  297. -1,
  298. ),
  299. }
  300. auth.log.Debug("Ldap Search For User Request", "info", spew.Sdump(searchReq))
  301. searchResult, err = auth.conn.Search(&searchReq)
  302. if err != nil {
  303. return nil, err
  304. }
  305. if len(searchResult.Entries) > 0 {
  306. break
  307. }
  308. }
  309. if len(searchResult.Entries) == 0 {
  310. return nil, ErrInvalidCredentials
  311. }
  312. if len(searchResult.Entries) > 1 {
  313. return nil, errors.New("Ldap search matched more than one entry, please review your filter setting")
  314. }
  315. var memberOf []string
  316. if auth.server.GroupSearchFilter == "" {
  317. memberOf = getLdapAttrArray(auth.server.Attr.MemberOf, searchResult)
  318. } else {
  319. // If we are using a POSIX LDAP schema it won't support memberOf, so we manually search the groups
  320. var groupSearchResult *LDAP.SearchResult
  321. for _, groupSearchBase := range auth.server.GroupSearchBaseDNs {
  322. var filter_replace string
  323. if auth.server.GroupSearchFilterUserAttribute == "" {
  324. filter_replace = getLdapAttr(auth.server.Attr.Username, searchResult)
  325. } else {
  326. filter_replace = getLdapAttr(auth.server.GroupSearchFilterUserAttribute, searchResult)
  327. }
  328. filter := strings.Replace(
  329. auth.server.GroupSearchFilter, "%s",
  330. LDAP.EscapeFilter(filter_replace),
  331. -1,
  332. )
  333. auth.log.Info("Searching for user's groups", "filter", filter)
  334. // support old way of reading settings
  335. groupIdAttribute := auth.server.Attr.MemberOf
  336. // but prefer dn attribute if default settings are used
  337. if groupIdAttribute == "" || groupIdAttribute == "memberOf" {
  338. groupIdAttribute = "dn"
  339. }
  340. groupSearchReq := LDAP.SearchRequest{
  341. BaseDN: groupSearchBase,
  342. Scope: LDAP.ScopeWholeSubtree,
  343. DerefAliases: LDAP.NeverDerefAliases,
  344. Attributes: []string{groupIdAttribute},
  345. Filter: filter,
  346. }
  347. groupSearchResult, err = auth.conn.Search(&groupSearchReq)
  348. if err != nil {
  349. return nil, err
  350. }
  351. if len(groupSearchResult.Entries) > 0 {
  352. for i := range groupSearchResult.Entries {
  353. memberOf = append(memberOf, getLdapAttrN(groupIdAttribute, groupSearchResult, i))
  354. }
  355. break
  356. }
  357. }
  358. }
  359. return &UserInfo{
  360. DN: searchResult.Entries[0].DN,
  361. LastName: getLdapAttr(auth.server.Attr.Surname, searchResult),
  362. FirstName: getLdapAttr(auth.server.Attr.Name, searchResult),
  363. Username: getLdapAttr(auth.server.Attr.Username, searchResult),
  364. Email: getLdapAttr(auth.server.Attr.Email, searchResult),
  365. MemberOf: memberOf,
  366. }, nil
  367. }
  368. func (ldap *Auth) Users() ([]*UserInfo, error) {
  369. var result *LDAP.SearchResult
  370. var err error
  371. server := ldap.server
  372. if err := ldap.Dial(); err != nil {
  373. return nil, err
  374. }
  375. defer ldap.conn.Close()
  376. for _, base := range server.SearchBaseDNs {
  377. attributes := make([]string, 0)
  378. inputs := server.Attr
  379. attributes = appendIfNotEmpty(
  380. attributes,
  381. inputs.Username,
  382. inputs.Surname,
  383. inputs.Email,
  384. inputs.Name,
  385. inputs.MemberOf,
  386. )
  387. req := LDAP.SearchRequest{
  388. BaseDN: base,
  389. Scope: LDAP.ScopeWholeSubtree,
  390. DerefAliases: LDAP.NeverDerefAliases,
  391. Attributes: attributes,
  392. // Doing a star here to get all the users in one go
  393. Filter: strings.Replace(server.SearchFilter, "%s", "*", -1),
  394. }
  395. result, err = ldap.conn.Search(&req)
  396. if err != nil {
  397. return nil, err
  398. }
  399. if len(result.Entries) > 0 {
  400. break
  401. }
  402. }
  403. return ldap.serializeUsers(result), nil
  404. }
  405. func (ldap *Auth) serializeUsers(users *LDAP.SearchResult) []*UserInfo {
  406. var serialized []*UserInfo
  407. for index := range users.Entries {
  408. serialize := &UserInfo{
  409. DN: getLdapAttrN(
  410. "dn",
  411. users,
  412. index,
  413. ),
  414. LastName: getLdapAttrN(
  415. ldap.server.Attr.Surname,
  416. users,
  417. index,
  418. ),
  419. FirstName: getLdapAttrN(
  420. ldap.server.Attr.Name,
  421. users,
  422. index,
  423. ),
  424. Username: getLdapAttrN(
  425. ldap.server.Attr.Username,
  426. users,
  427. index,
  428. ),
  429. Email: getLdapAttrN(
  430. ldap.server.Attr.Email,
  431. users,
  432. index,
  433. ),
  434. MemberOf: getLdapAttrArrayN(
  435. ldap.server.Attr.MemberOf,
  436. users,
  437. index,
  438. ),
  439. }
  440. serialized = append(serialized, serialize)
  441. }
  442. return serialized
  443. }
  444. func appendIfNotEmpty(slice []string, values ...string) []string {
  445. for _, v := range values {
  446. if v != "" {
  447. slice = append(slice, v)
  448. }
  449. }
  450. return slice
  451. }
  452. func getLdapAttr(name string, result *LDAP.SearchResult) string {
  453. return getLdapAttrN(name, result, 0)
  454. }
  455. func getLdapAttrN(name string, result *LDAP.SearchResult, n int) string {
  456. if strings.ToLower(name) == "dn" {
  457. return result.Entries[n].DN
  458. }
  459. for _, attr := range result.Entries[n].Attributes {
  460. if attr.Name == name {
  461. if len(attr.Values) > 0 {
  462. return attr.Values[0]
  463. }
  464. }
  465. }
  466. return ""
  467. }
  468. func getLdapAttrArray(name string, result *LDAP.SearchResult) []string {
  469. return getLdapAttrArrayN(name, result, 0)
  470. }
  471. func getLdapAttrArrayN(name string, result *LDAP.SearchResult, n int) []string {
  472. for _, attr := range result.Entries[n].Attributes {
  473. if attr.Name == name {
  474. return attr.Values
  475. }
  476. }
  477. return []string{}
  478. }