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

485 lines
14 KiB

10 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
9 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
Better logging (#6038) (#6095) * Panic don't fatal on create new logger Fixes #5854 Signed-off-by: Andrew Thornton <art27@cantab.net> * partial broken * Update the logging infrastrcture Signed-off-by: Andrew Thornton <art27@cantab.net> * Reset the skip levels for Fatal and Error Signed-off-by: Andrew Thornton <art27@cantab.net> * broken ncsa * More log.Error fixes Signed-off-by: Andrew Thornton <art27@cantab.net> * Remove nal * set log-levels to lowercase * Make console_test test all levels * switch to lowercased levels * OK now working * Fix vetting issues * Fix lint * Fix tests * change default logging to match current gitea * Improve log testing Signed-off-by: Andrew Thornton <art27@cantab.net> * reset error skip levels to 0 * Update documentation and access logger configuration * Redirect the router log back to gitea if redirect macaron log but also allow setting the log level - i.e. TRACE * Fix broken level caching * Refactor the router log * Add Router logger * Add colorizing options * Adjust router colors * Only create logger if they will be used * update app.ini.sample * rename Attribute ColorAttribute * Change from white to green for function * Set fatal/error levels * Restore initial trace logger * Fix Trace arguments in modules/auth/auth.go * Properly handle XORMLogger * Improve admin/config page * fix fmt * Add auto-compression of old logs * Update error log levels * Remove the unnecessary skip argument from Error, Fatal and Critical * Add stacktrace support * Fix tests * Remove x/sync from vendors? * Add stderr option to console logger * Use filepath.ToSlash to protect against Windows in tests * Remove prefixed underscores from names in colors.go * Remove not implemented database logger This was removed from Gogs on 4 Mar 2016 but left in the configuration since then. * Ensure that log paths are relative to ROOT_PATH * use path.Join * rename jsonConfig to logConfig * Rename "config" to "jsonConfig" to make it clearer * Requested changes * Requested changes: XormLogger * Try to color the windows terminal If successful default to colorizing the console logs * fixup * Colorize initially too * update vendor * Colorize logs on default and remove if this is not a colorizing logger * Fix documentation * fix test * Use go-isatty to detect if on windows we are on msys or cygwin * Fix spelling mistake * Add missing vendors * More changes * Rationalise the ANSI writer protection * Adjust colors on advice from @0x5c * Make Flags a comma separated list * Move to use the windows constant for ENABLE_VIRTUAL_TERMINAL_PROCESSING * Ensure matching is done on the non-colored message - to simpify EXPRESSION
5 years ago
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2020 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. // Package ldap provide functions & structure to query a LDAP ldap directory
  6. // For now, it's mainly tested again an MS Active Directory service, see README.md for more information
  7. package ldap
  8. import (
  9. "crypto/tls"
  10. "fmt"
  11. "strings"
  12. "code.gitea.io/gitea/modules/log"
  13. "gopkg.in/ldap.v3"
  14. )
  15. // SecurityProtocol protocol type
  16. type SecurityProtocol int
  17. // Note: new type must be added at the end of list to maintain compatibility.
  18. const (
  19. SecurityProtocolUnencrypted SecurityProtocol = iota
  20. SecurityProtocolLDAPS
  21. SecurityProtocolStartTLS
  22. )
  23. // Source Basic LDAP authentication service
  24. type Source struct {
  25. Name string // canonical name (ie. corporate.ad)
  26. Host string // LDAP host
  27. Port int // port number
  28. SecurityProtocol SecurityProtocol
  29. SkipVerify bool
  30. BindDN string // DN to bind with
  31. BindPassword string // Bind DN password
  32. UserBase string // Base search path for users
  33. UserDN string // Template for the DN of the user for simple auth
  34. AttributeUsername string // Username attribute
  35. AttributeName string // First name attribute
  36. AttributeSurname string // Surname attribute
  37. AttributeMail string // E-mail attribute
  38. AttributesInBind bool // fetch attributes in bind context (not user)
  39. AttributeSSHPublicKey string // LDAP SSH Public Key attribute
  40. SearchPageSize uint32 // Search with paging page size
  41. Filter string // Query filter to validate entry
  42. AdminFilter string // Query filter to check if user is admin
  43. RestrictedFilter string // Query filter to check if user is restricted
  44. Enabled bool // if this source is disabled
  45. AllowDeactivateAll bool // Allow an empty search response to deactivate all users from this source
  46. GroupsEnabled bool // if the group checking is enabled
  47. GroupDN string // Group Search Base
  48. GroupFilter string // Group Name Filter
  49. GroupMemberUID string // Group Attribute containing array of UserUID
  50. UserUID string // User Attribute listed in Group
  51. }
  52. // SearchResult : user data
  53. type SearchResult struct {
  54. Username string // Username
  55. Name string // Name
  56. Surname string // Surname
  57. Mail string // E-mail address
  58. SSHPublicKey []string // SSH Public Key
  59. IsAdmin bool // if user is administrator
  60. IsRestricted bool // if user is restricted
  61. }
  62. func (ls *Source) sanitizedUserQuery(username string) (string, bool) {
  63. // See http://tools.ietf.org/search/rfc4515
  64. badCharacters := "\x00()*\\"
  65. if strings.ContainsAny(username, badCharacters) {
  66. log.Debug("'%s' contains invalid query characters. Aborting.", username)
  67. return "", false
  68. }
  69. return fmt.Sprintf(ls.Filter, username), true
  70. }
  71. func (ls *Source) sanitizedUserDN(username string) (string, bool) {
  72. // See http://tools.ietf.org/search/rfc4514: "special characters"
  73. badCharacters := "\x00()*\\,='\"#+;<>"
  74. if strings.ContainsAny(username, badCharacters) {
  75. log.Debug("'%s' contains invalid DN characters. Aborting.", username)
  76. return "", false
  77. }
  78. return fmt.Sprintf(ls.UserDN, username), true
  79. }
  80. func (ls *Source) sanitizedGroupFilter(group string) (string, bool) {
  81. // See http://tools.ietf.org/search/rfc4515
  82. badCharacters := "\x00*\\"
  83. if strings.ContainsAny(group, badCharacters) {
  84. log.Trace("Group filter invalid query characters: %s", group)
  85. return "", false
  86. }
  87. return group, true
  88. }
  89. func (ls *Source) sanitizedGroupDN(groupDn string) (string, bool) {
  90. // See http://tools.ietf.org/search/rfc4514: "special characters"
  91. badCharacters := "\x00()*\\'\"#+;<>"
  92. if strings.ContainsAny(groupDn, badCharacters) || strings.HasPrefix(groupDn, " ") || strings.HasSuffix(groupDn, " ") {
  93. log.Trace("Group DN contains invalid query characters: %s", groupDn)
  94. return "", false
  95. }
  96. return groupDn, true
  97. }
  98. func (ls *Source) findUserDN(l *ldap.Conn, name string) (string, bool) {
  99. log.Trace("Search for LDAP user: %s", name)
  100. // A search for the user.
  101. userFilter, ok := ls.sanitizedUserQuery(name)
  102. if !ok {
  103. return "", false
  104. }
  105. log.Trace("Searching for DN using filter %s and base %s", userFilter, ls.UserBase)
  106. search := ldap.NewSearchRequest(
  107. ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0,
  108. false, userFilter, []string{}, nil)
  109. // Ensure we found a user
  110. sr, err := l.Search(search)
  111. if err != nil || len(sr.Entries) < 1 {
  112. log.Debug("Failed search using filter[%s]: %v", userFilter, err)
  113. return "", false
  114. } else if len(sr.Entries) > 1 {
  115. log.Debug("Filter '%s' returned more than one user.", userFilter)
  116. return "", false
  117. }
  118. userDN := sr.Entries[0].DN
  119. if userDN == "" {
  120. log.Error("LDAP search was successful, but found no DN!")
  121. return "", false
  122. }
  123. return userDN, true
  124. }
  125. func dial(ls *Source) (*ldap.Conn, error) {
  126. log.Trace("Dialing LDAP with security protocol (%v) without verifying: %v", ls.SecurityProtocol, ls.SkipVerify)
  127. tlsCfg := &tls.Config{
  128. ServerName: ls.Host,
  129. InsecureSkipVerify: ls.SkipVerify,
  130. }
  131. if ls.SecurityProtocol == SecurityProtocolLDAPS {
  132. return ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port), tlsCfg)
  133. }
  134. conn, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ls.Host, ls.Port))
  135. if err != nil {
  136. return nil, fmt.Errorf("Dial: %v", err)
  137. }
  138. if ls.SecurityProtocol == SecurityProtocolStartTLS {
  139. if err = conn.StartTLS(tlsCfg); err != nil {
  140. conn.Close()
  141. return nil, fmt.Errorf("StartTLS: %v", err)
  142. }
  143. }
  144. return conn, nil
  145. }
  146. func bindUser(l *ldap.Conn, userDN, passwd string) error {
  147. log.Trace("Binding with userDN: %s", userDN)
  148. err := l.Bind(userDN, passwd)
  149. if err != nil {
  150. log.Debug("LDAP auth. failed for %s, reason: %v", userDN, err)
  151. return err
  152. }
  153. log.Trace("Bound successfully with userDN: %s", userDN)
  154. return err
  155. }
  156. func checkAdmin(l *ldap.Conn, ls *Source, userDN string) bool {
  157. if len(ls.AdminFilter) == 0 {
  158. return false
  159. }
  160. log.Trace("Checking admin with filter %s and base %s", ls.AdminFilter, userDN)
  161. search := ldap.NewSearchRequest(
  162. userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.AdminFilter,
  163. []string{ls.AttributeName},
  164. nil)
  165. sr, err := l.Search(search)
  166. if err != nil {
  167. log.Error("LDAP Admin Search failed unexpectedly! (%v)", err)
  168. } else if len(sr.Entries) < 1 {
  169. log.Trace("LDAP Admin Search found no matching entries.")
  170. } else {
  171. return true
  172. }
  173. return false
  174. }
  175. func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool {
  176. if len(ls.RestrictedFilter) == 0 {
  177. return false
  178. }
  179. if ls.RestrictedFilter == "*" {
  180. return true
  181. }
  182. log.Trace("Checking restricted with filter %s and base %s", ls.RestrictedFilter, userDN)
  183. search := ldap.NewSearchRequest(
  184. userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, ls.RestrictedFilter,
  185. []string{ls.AttributeName},
  186. nil)
  187. sr, err := l.Search(search)
  188. if err != nil {
  189. log.Error("LDAP Restrictred Search failed unexpectedly! (%v)", err)
  190. } else if len(sr.Entries) < 1 {
  191. log.Trace("LDAP Restricted Search found no matching entries.")
  192. } else {
  193. return true
  194. }
  195. return false
  196. }
  197. // SearchEntry : search an LDAP source if an entry (name, passwd) is valid and in the specific filter
  198. func (ls *Source) SearchEntry(name, passwd string, directBind bool) *SearchResult {
  199. // See https://tools.ietf.org/search/rfc4513#section-5.1.2
  200. if len(passwd) == 0 {
  201. log.Debug("Auth. failed for %s, password cannot be empty", name)
  202. return nil
  203. }
  204. l, err := dial(ls)
  205. if err != nil {
  206. log.Error("LDAP Connect error, %s:%v", ls.Host, err)
  207. ls.Enabled = false
  208. return nil
  209. }
  210. defer l.Close()
  211. var userDN string
  212. if directBind {
  213. log.Trace("LDAP will bind directly via UserDN template: %s", ls.UserDN)
  214. var ok bool
  215. userDN, ok = ls.sanitizedUserDN(name)
  216. if !ok {
  217. return nil
  218. }
  219. err = bindUser(l, userDN, passwd)
  220. if err != nil {
  221. return nil
  222. }
  223. if ls.UserBase != "" {
  224. // not everyone has a CN compatible with input name so we need to find
  225. // the real userDN in that case
  226. userDN, ok = ls.findUserDN(l, name)
  227. if !ok {
  228. return nil
  229. }
  230. }
  231. } else {
  232. log.Trace("LDAP will use BindDN.")
  233. var found bool
  234. if ls.BindDN != "" && ls.BindPassword != "" {
  235. err := l.Bind(ls.BindDN, ls.BindPassword)
  236. if err != nil {
  237. log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
  238. return nil
  239. }
  240. log.Trace("Bound as BindDN %s", ls.BindDN)
  241. } else {
  242. log.Trace("Proceeding with anonymous LDAP search.")
  243. }
  244. userDN, found = ls.findUserDN(l, name)
  245. if !found {
  246. return nil
  247. }
  248. }
  249. if !ls.AttributesInBind {
  250. // binds user (checking password) before looking-up attributes in user context
  251. err = bindUser(l, userDN, passwd)
  252. if err != nil {
  253. return nil
  254. }
  255. }
  256. userFilter, ok := ls.sanitizedUserQuery(name)
  257. if !ok {
  258. return nil
  259. }
  260. var isAttributeSSHPublicKeySet = len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
  261. attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
  262. if len(strings.TrimSpace(ls.UserUID)) > 0 {
  263. attribs = append(attribs, ls.UserUID)
  264. }
  265. if isAttributeSSHPublicKeySet {
  266. attribs = append(attribs, ls.AttributeSSHPublicKey)
  267. }
  268. log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v', '%v' with filter '%s' and base '%s'", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, ls.UserUID, userFilter, userDN)
  269. search := ldap.NewSearchRequest(
  270. userDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
  271. attribs, nil)
  272. sr, err := l.Search(search)
  273. if err != nil {
  274. log.Error("LDAP Search failed unexpectedly! (%v)", err)
  275. return nil
  276. } else if len(sr.Entries) < 1 {
  277. if directBind {
  278. log.Trace("User filter inhibited user login.")
  279. } else {
  280. log.Trace("LDAP Search found no matching entries.")
  281. }
  282. return nil
  283. }
  284. var sshPublicKey []string
  285. username := sr.Entries[0].GetAttributeValue(ls.AttributeUsername)
  286. firstname := sr.Entries[0].GetAttributeValue(ls.AttributeName)
  287. surname := sr.Entries[0].GetAttributeValue(ls.AttributeSurname)
  288. mail := sr.Entries[0].GetAttributeValue(ls.AttributeMail)
  289. uid := sr.Entries[0].GetAttributeValue(ls.UserUID)
  290. // Check group membership
  291. if ls.GroupsEnabled {
  292. groupFilter, ok := ls.sanitizedGroupFilter(ls.GroupFilter)
  293. if !ok {
  294. return nil
  295. }
  296. groupDN, ok := ls.sanitizedGroupDN(ls.GroupDN)
  297. if !ok {
  298. return nil
  299. }
  300. log.Trace("Fetching groups '%v' with filter '%s' and base '%s'", ls.GroupMemberUID, groupFilter, groupDN)
  301. groupSearch := ldap.NewSearchRequest(
  302. groupDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, groupFilter,
  303. []string{ls.GroupMemberUID},
  304. nil)
  305. srg, err := l.Search(groupSearch)
  306. if err != nil {
  307. log.Error("LDAP group search failed: %v", err)
  308. return nil
  309. } else if len(srg.Entries) < 1 {
  310. log.Error("LDAP group search failed: 0 entries")
  311. return nil
  312. }
  313. isMember := false
  314. Entries:
  315. for _, group := range srg.Entries {
  316. for _, member := range group.GetAttributeValues(ls.GroupMemberUID) {
  317. if (ls.UserUID == "dn" && member == sr.Entries[0].DN) || member == uid {
  318. isMember = true
  319. break Entries
  320. }
  321. }
  322. }
  323. if !isMember {
  324. log.Error("LDAP group membership test failed")
  325. return nil
  326. }
  327. }
  328. if isAttributeSSHPublicKeySet {
  329. sshPublicKey = sr.Entries[0].GetAttributeValues(ls.AttributeSSHPublicKey)
  330. }
  331. isAdmin := checkAdmin(l, ls, userDN)
  332. var isRestricted bool
  333. if !isAdmin {
  334. isRestricted = checkRestricted(l, ls, userDN)
  335. }
  336. if !directBind && ls.AttributesInBind {
  337. // binds user (checking password) after looking-up attributes in BindDN context
  338. err = bindUser(l, userDN, passwd)
  339. if err != nil {
  340. return nil
  341. }
  342. }
  343. return &SearchResult{
  344. Username: username,
  345. Name: firstname,
  346. Surname: surname,
  347. Mail: mail,
  348. SSHPublicKey: sshPublicKey,
  349. IsAdmin: isAdmin,
  350. IsRestricted: isRestricted,
  351. }
  352. }
  353. // UsePagedSearch returns if need to use paged search
  354. func (ls *Source) UsePagedSearch() bool {
  355. return ls.SearchPageSize > 0
  356. }
  357. // SearchEntries : search an LDAP source for all users matching userFilter
  358. func (ls *Source) SearchEntries() ([]*SearchResult, error) {
  359. l, err := dial(ls)
  360. if err != nil {
  361. log.Error("LDAP Connect error, %s:%v", ls.Host, err)
  362. ls.Enabled = false
  363. return nil, err
  364. }
  365. defer l.Close()
  366. if ls.BindDN != "" && ls.BindPassword != "" {
  367. err := l.Bind(ls.BindDN, ls.BindPassword)
  368. if err != nil {
  369. log.Debug("Failed to bind as BindDN[%s]: %v", ls.BindDN, err)
  370. return nil, err
  371. }
  372. log.Trace("Bound as BindDN %s", ls.BindDN)
  373. } else {
  374. log.Trace("Proceeding with anonymous LDAP search.")
  375. }
  376. userFilter := fmt.Sprintf(ls.Filter, "*")
  377. var isAttributeSSHPublicKeySet = len(strings.TrimSpace(ls.AttributeSSHPublicKey)) > 0
  378. attribs := []string{ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail}
  379. if isAttributeSSHPublicKeySet {
  380. attribs = append(attribs, ls.AttributeSSHPublicKey)
  381. }
  382. log.Trace("Fetching attributes '%v', '%v', '%v', '%v', '%v' with filter %s and base %s", ls.AttributeUsername, ls.AttributeName, ls.AttributeSurname, ls.AttributeMail, ls.AttributeSSHPublicKey, userFilter, ls.UserBase)
  383. search := ldap.NewSearchRequest(
  384. ls.UserBase, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, userFilter,
  385. attribs, nil)
  386. var sr *ldap.SearchResult
  387. if ls.UsePagedSearch() {
  388. sr, err = l.SearchWithPaging(search, ls.SearchPageSize)
  389. } else {
  390. sr, err = l.Search(search)
  391. }
  392. if err != nil {
  393. log.Error("LDAP Search failed unexpectedly! (%v)", err)
  394. return nil, err
  395. }
  396. result := make([]*SearchResult, len(sr.Entries))
  397. for i, v := range sr.Entries {
  398. result[i] = &SearchResult{
  399. Username: v.GetAttributeValue(ls.AttributeUsername),
  400. Name: v.GetAttributeValue(ls.AttributeName),
  401. Surname: v.GetAttributeValue(ls.AttributeSurname),
  402. Mail: v.GetAttributeValue(ls.AttributeMail),
  403. IsAdmin: checkAdmin(l, ls, v.DN),
  404. }
  405. if !result[i].IsAdmin {
  406. result[i].IsRestricted = checkRestricted(l, ls, v.DN)
  407. }
  408. if isAttributeSSHPublicKeySet {
  409. result[i].SSHPublicKey = v.GetAttributeValues(ls.AttributeSSHPublicKey)
  410. }
  411. }
  412. return result, nil
  413. }