You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ldap.go 4.6 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. // Package ldap provide functions & structure to query a LDAP ldap directory
  5. // For now, it's mainly tested again an MS Active Directory service, see README.md for more information
  6. package ldap
  7. import (
  8. "fmt"
  9. "github.com/gogits/gogs/modules/ldap"
  10. "github.com/gogits/gogs/modules/log"
  11. )
  12. // Basic LDAP authentication service
  13. type Ldapsource struct {
  14. Name string // canonical name (ie. corporate.ad)
  15. Host string // LDAP host
  16. Port int // port number
  17. UseSSL bool // Use SSL
  18. BindDN string // DN to bind with
  19. BindPassword string // Bind DN password
  20. UserBase string // Base search path for users
  21. AttributeName string // First name attribute
  22. AttributeSurname string // Surname attribute
  23. AttributeMail string // E-mail attribute
  24. Filter string // Query filter to validate entry
  25. AdminFilter string // Query filter to check if user is admin
  26. Enabled bool // if this source is disabled
  27. }
  28. func (ls Ldapsource) FindUserDN(name string) (string, bool) {
  29. l, err := ldapDial(ls)
  30. if err != nil {
  31. log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
  32. ls.Enabled = false
  33. return "", false
  34. }
  35. defer l.Close()
  36. log.Trace("Search for LDAP user: %s", name)
  37. if ls.BindDN != "" && ls.BindPassword != "" {
  38. err = l.Bind(ls.BindDN, ls.BindPassword)
  39. if err != nil {
  40. log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
  41. return "", false
  42. }
  43. log.Trace("Bound as BindDN %s", ls.BindDN)
  44. } else {
  45. log.Trace("Proceeding with anonymous LDAP search.")
  46. }
  47. // A search for the user.
  48. userFilter := fmt.Sprintf(ls.Filter, name)
  49. log.Trace("Searching using filter %s", userFilter)
  50. search := ldap.NewSearchRequest(
  51. ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
  52. false, userFilter, []string{}, nil)
  53. // Ensure we found a user
  54. sr, err := l.Search(search)
  55. if err != nil || len(sr.Entries) < 1 {
  56. log.Debug("Failed search using filter[%s]: %v", userFilter, err)
  57. return "", false
  58. } else if len(sr.Entries) > 1 {
  59. log.Debug("Filter '%s' returned more than one user.", userFilter)
  60. return "", false
  61. }
  62. userDN := sr.Entries[0].DN
  63. if userDN == "" {
  64. log.Error(4, "LDAP search was succesful, but found no DN!")
  65. return "", false
  66. }
  67. return userDN, true
  68. }
  69. // searchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
  70. func (ls Ldapsource) SearchEntry(name, passwd string) (string, string, string, bool, bool) {
  71. userDN, found := ls.FindUserDN(name)
  72. if !found {
  73. return "", "", "", false, false
  74. }
  75. l, err := ldapDial(ls)
  76. if err != nil {
  77. log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err)
  78. ls.Enabled = false
  79. return "", "", "", false, false
  80. }
  81. defer l.Close()
  82. log.Trace("Binding with userDN: %s", userDN)
  83. err = l.Bind(userDN, passwd)
  84. if err != nil {
  85. log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err)
  86. return "", "", "", false, false
  87. }
  88. log.Trace("Bound successfully with userDN: %s", userDN)
  89. userFilter := fmt.Sprintf(ls.Filter, name)
  90. search := ldap.NewSearchRequest(
  91. userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
  92. []string{ls.AttributeName, ls.AttributeSurname, ls.AttributeMail},
  93. nil)
  94. sr, err := l.Search(search)
  95. if err != nil {
  96. log.Error(4, "LDAP Search failed unexpectedly! (%v)", err)
  97. return "", "", "", false, false
  98. } else if len(sr.Entries) < 1 {
  99. log.Error(4, "LDAP Search failed unexpectedly! (0 entries)")
  100. return "", "", "", false, false
  101. }
  102. name_attr := sr.Entries[0].GetAttributeValue(ls.AttributeName)
  103. sn_attr := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
  104. mail_attr := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
  105. search = ldap.NewSearchRequest(
  106. userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter,
  107. []string{ls.AttributeName},
  108. nil)
  109. sr, err = l.Search(search)
  110. admin_attr := false
  111. if err != nil {
  112. log.Error(4, "LDAP Admin Search failed unexpectedly! (%v)", err)
  113. } else if len(sr.Entries) < 1 {
  114. log.Error(4, "LDAP Admin Search failed")
  115. } else {
  116. admin_attr = true
  117. }
  118. return name_attr, sn_attr, mail_attr, admin_attr, true
  119. }
  120. func ldapDial(ls Ldapsource) (*ldap.Conn, error) {
  121. if ls.UseSSL {
  122. log.Debug("Using TLS for LDAP")
  123. return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), nil)
  124. } else {
  125. return ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
  126. }
  127. }