ldap.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package auth
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/go-ldap/ldap"
  6. "github.com/grafana/grafana/pkg/bus"
  7. "github.com/grafana/grafana/pkg/log"
  8. m "github.com/grafana/grafana/pkg/models"
  9. )
  10. var ldapServers []*LdapServerConf
  11. func init() {
  12. ldapServers = []*LdapServerConf{
  13. {
  14. UseSSL: false,
  15. Host: "127.0.0.1",
  16. Port: "389",
  17. BindDN: "cn=%s,dc=grafana,dc=org",
  18. AttrName: "givenName",
  19. AttrSurname: "sn",
  20. AttrUsername: "cn",
  21. AttrMemberOf: "memberOf",
  22. AttrEmail: "email",
  23. SearchFilter: "(cn=%s)",
  24. SearchBaseDNs: []string{"dc=grafana,dc=org"},
  25. LdapGroups: []*LdapGroupToOrgRole{
  26. {GroupDN: "cn=users,dc=grafana,dc=org", OrgRole: m.ROLE_EDITOR},
  27. },
  28. },
  29. }
  30. }
  31. type ldapAuther struct {
  32. server *LdapServerConf
  33. conn *ldap.Conn
  34. }
  35. func NewLdapAuthenticator(server *LdapServerConf) *ldapAuther {
  36. return &ldapAuther{server: server}
  37. }
  38. func (a *ldapAuther) Dial() error {
  39. address := fmt.Sprintf("%s:%s", a.server.Host, a.server.Port)
  40. var err error
  41. if a.server.UseSSL {
  42. a.conn, err = ldap.DialTLS("tcp", address, nil)
  43. } else {
  44. a.conn, err = ldap.Dial("tcp", address)
  45. }
  46. return err
  47. }
  48. func (a *ldapAuther) login(query *AuthenticateUserQuery) error {
  49. if err := a.Dial(); err != nil {
  50. return err
  51. }
  52. defer a.conn.Close()
  53. // perform initial authentication
  54. if err := a.initialBind(query.Username, query.Password); err != nil {
  55. return err
  56. }
  57. // find user entry & attributes
  58. if ldapUser, err := a.searchForUser(query.Username); err != nil {
  59. return err
  60. } else {
  61. log.Info("Surname: %s", ldapUser.LastName)
  62. log.Info("givenName: %s", ldapUser.FirstName)
  63. log.Info("email: %s", ldapUser.Email)
  64. log.Info("memberOf: %s", ldapUser.MemberOf)
  65. if grafanaUser, err := a.getGrafanaUserFor(ldapUser); err != nil {
  66. return err
  67. } else {
  68. // sync org roles
  69. if err := a.syncOrgRoles(grafanaUser, ldapUser); err != nil {
  70. return err
  71. }
  72. query.User = grafanaUser
  73. return nil
  74. }
  75. }
  76. }
  77. func (a *ldapAuther) getGrafanaUserFor(ldapUser *ldapUserInfo) (*m.User, error) {
  78. // validate that the user has access
  79. // if there are no ldap group mappings access is true
  80. // otherwise a single group must match
  81. access := len(a.server.LdapGroups) == 0
  82. for _, ldapGroup := range a.server.LdapGroups {
  83. if ldapUser.isMemberOf(ldapGroup.GroupDN) {
  84. access = true
  85. }
  86. }
  87. if !access {
  88. log.Info("Ldap Auth: user %s does not belong in any of the specified ldap groups", ldapUser.Username)
  89. return nil, ErrInvalidCredentials
  90. }
  91. // get user from grafana db
  92. userQuery := m.GetUserByLoginQuery{LoginOrEmail: ldapUser.Username}
  93. if err := bus.Dispatch(&userQuery); err != nil {
  94. if err == m.ErrUserNotFound {
  95. return a.createGrafanaUser(ldapUser)
  96. } else {
  97. return nil, err
  98. }
  99. }
  100. return userQuery.Result, nil
  101. }
  102. func (a *ldapAuther) createGrafanaUser(ldapUser *ldapUserInfo) (*m.User, error) {
  103. cmd := m.CreateUserCommand{
  104. Login: ldapUser.Username,
  105. Email: ldapUser.Email,
  106. Name: fmt.Sprintf("%s %s", ldapUser.FirstName, ldapUser.LastName),
  107. }
  108. if err := bus.Dispatch(&cmd); err != nil {
  109. return nil, err
  110. }
  111. return &cmd.Result, nil
  112. }
  113. func (a *ldapAuther) syncOrgRoles(user *m.User, ldapUser *ldapUserInfo) error {
  114. if len(a.server.LdapGroups) == 0 {
  115. return nil
  116. }
  117. orgsQuery := m.GetUserOrgListQuery{UserId: user.Id}
  118. if err := bus.Dispatch(&orgsQuery); err != nil {
  119. return err
  120. }
  121. // remove or update org roles
  122. for _, org := range orgsQuery.Result {
  123. for _, group := range a.server.LdapGroups {
  124. if group.OrgId == org.OrgId && ldapUser.isMemberOf(group.GroupDN) {
  125. if org.Role != group.OrgRole {
  126. // update role
  127. }
  128. } else {
  129. // remove role
  130. }
  131. }
  132. }
  133. for _, group := range a.server.LdapGroups {
  134. if !ldapUser.isMemberOf(group.GroupDN) {
  135. continue
  136. }
  137. match := false
  138. for _, org := range orgsQuery.Result {
  139. if group.OrgId == org.OrgId {
  140. match = true
  141. }
  142. }
  143. if !match {
  144. // add role
  145. cmd := m.AddOrgUserCommand{UserId: user.Id, Role: group.OrgRole, OrgId: group.OrgId}
  146. if err := bus.Dispatch(&cmd); err != nil {
  147. return err
  148. }
  149. }
  150. }
  151. return nil
  152. }
  153. func (a *ldapAuther) initialBind(username, userPassword string) error {
  154. if a.server.BindPassword != "" {
  155. userPassword = a.server.BindPassword
  156. }
  157. bindPath := fmt.Sprintf(a.server.BindDN, username)
  158. if err := a.conn.Bind(bindPath, userPassword); err != nil {
  159. if ldapErr, ok := err.(*ldap.Error); ok {
  160. if ldapErr.ResultCode == 49 {
  161. return ErrInvalidCredentials
  162. }
  163. }
  164. return err
  165. }
  166. return nil
  167. }
  168. func (a *ldapAuther) searchForUser(username string) (*ldapUserInfo, error) {
  169. var searchResult *ldap.SearchResult
  170. var err error
  171. for _, searchBase := range a.server.SearchBaseDNs {
  172. searchReq := ldap.SearchRequest{
  173. BaseDN: searchBase,
  174. Scope: ldap.ScopeWholeSubtree,
  175. DerefAliases: ldap.NeverDerefAliases,
  176. Attributes: []string{
  177. a.server.AttrUsername,
  178. a.server.AttrSurname,
  179. a.server.AttrEmail,
  180. a.server.AttrName,
  181. a.server.AttrMemberOf,
  182. },
  183. Filter: fmt.Sprintf(a.server.SearchFilter, username),
  184. }
  185. searchResult, err = a.conn.Search(&searchReq)
  186. if err != nil {
  187. return nil, err
  188. }
  189. if len(searchResult.Entries) > 0 {
  190. break
  191. }
  192. }
  193. if len(searchResult.Entries) == 0 {
  194. return nil, errors.New("Ldap search matched no entry, please review your filter setting.")
  195. }
  196. if len(searchResult.Entries) > 1 {
  197. return nil, errors.New("Ldap search matched mopre than one entry, please review your filter setting")
  198. }
  199. return &ldapUserInfo{
  200. LastName: getLdapAttr(a.server.AttrSurname, searchResult),
  201. FirstName: getLdapAttr(a.server.AttrName, searchResult),
  202. Username: getLdapAttr(a.server.AttrUsername, searchResult),
  203. Email: getLdapAttr(a.server.AttrEmail, searchResult),
  204. MemberOf: getLdapAttrArray(a.server.AttrMemberOf, searchResult),
  205. }, nil
  206. }
  207. func getLdapAttr(name string, result *ldap.SearchResult) string {
  208. for _, attr := range result.Entries[0].Attributes {
  209. if attr.Name == name {
  210. if len(attr.Values) > 0 {
  211. return attr.Values[0]
  212. }
  213. }
  214. }
  215. return ""
  216. }
  217. func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
  218. for _, attr := range result.Entries[0].Attributes {
  219. if attr.Name == name {
  220. return attr.Values
  221. }
  222. }
  223. return []string{}
  224. }
  225. func createUserFromLdapInfo() error {
  226. return nil
  227. }