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.

login.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. // Copyright (c) 2020 Matt Ouille
  2. // Copyright (c) 2020 Shivaram Lingamneni
  3. // released under the MIT license
  4. // Portions of this code copyright Grafana Labs and contributors
  5. // and released under the Apache 2.0 license
  6. // Copying Grafana's original comment on the different cases for LDAP:
  7. // There are several cases -
  8. // 1. "admin" user
  9. // Bind the "admin" user (defined in Grafana config file) which has the search privileges
  10. // in LDAP server, then we search the targeted user through that bind, then the second
  11. // perform the bind via passed login/password.
  12. // 2. Single bind
  13. // // If all the users meant to be used with Grafana have the ability to search in LDAP server
  14. // then we bind with LDAP server with targeted login/password
  15. // and then search for the said user in order to retrive all the information about them
  16. // 3. Unauthenticated bind
  17. // For some LDAP configurations it is allowed to search the
  18. // user without login/password binding with LDAP server, in such case
  19. // we will perform "unauthenticated bind", then search for the
  20. // targeted user and then perform the bind with passed login/password.
  21. // Note: the only validation we do on users is to check RequiredGroups.
  22. // If RequiredGroups is not set and we can do a single bind, we don't
  23. // even need to search. So our case 2 is not restricted
  24. // to setups where all the users have search privileges: we only need to
  25. // be able to do DN resolution via pure string substitution.
  26. package ldap
  27. import (
  28. "errors"
  29. "fmt"
  30. ldap "github.com/go-ldap/ldap/v3"
  31. "github.com/oragono/oragono/irc/logger"
  32. )
  33. var (
  34. ErrUserNotInRequiredGroup = errors.New("User is not a member of any required groups")
  35. )
  36. // equivalent of Grafana's `Server`, but unexported
  37. // also, `log` was renamed to `logger`, since the APIs are slightly different
  38. // and this way the compiler will catch any unchanged references to Grafana's `Server.log`
  39. type serverConn struct {
  40. Config *ServerConfig
  41. Connection *ldap.Conn
  42. logger *logger.Manager
  43. }
  44. func CheckLDAPPassphrase(config ServerConfig, accountName, passphrase string, log *logger.Manager) (err error) {
  45. defer func() {
  46. if err != nil {
  47. log.Debug("ldap", "failed passphrase check", err.Error())
  48. }
  49. }()
  50. server := serverConn{
  51. Config: &config,
  52. logger: log,
  53. }
  54. err = server.Dial()
  55. if err != nil {
  56. return
  57. }
  58. defer server.Close()
  59. server.Connection.SetTimeout(config.Timeout)
  60. passphraseChecked := false
  61. if server.shouldSingleBind() {
  62. log.Debug("ldap", "attempting single bind to", accountName)
  63. err = server.userBind(server.singleBindDN(accountName), passphrase)
  64. passphraseChecked = (err == nil)
  65. } else if server.shouldAdminBind() {
  66. log.Debug("ldap", "attempting admin bind to", config.BindDN)
  67. err = server.userBind(config.BindDN, config.BindPassword)
  68. } else {
  69. log.Debug("ldap", "attempting unauthenticated bind")
  70. err = server.Connection.UnauthenticatedBind(config.BindDN)
  71. }
  72. if err != nil {
  73. return
  74. }
  75. if passphraseChecked && len(config.RequireGroups) == 0 {
  76. return nil
  77. }
  78. users, err := server.users([]string{accountName})
  79. if err != nil {
  80. log.Debug("ldap", "failed user lookup")
  81. return err
  82. }
  83. if len(users) == 0 {
  84. return ErrCouldNotFindUser
  85. }
  86. user := users[0]
  87. log.Debug("ldap", "looked up user", user.DN)
  88. err = server.validateGroupMembership(user)
  89. if err != nil {
  90. return err
  91. }
  92. if !passphraseChecked {
  93. log.Debug("ldap", "rebinding", user.DN)
  94. err = server.userBind(user.DN, passphrase)
  95. }
  96. return err
  97. }
  98. func (server *serverConn) validateGroupMembership(user *ldap.Entry) (err error) {
  99. if len(server.Config.RequireGroups) == 0 {
  100. return
  101. }
  102. var memberOf []string
  103. memberOf, err = server.getMemberOf(user)
  104. if err != nil {
  105. server.logger.Debug("ldap", "could not retrieve group memberships", err.Error())
  106. return
  107. }
  108. server.logger.Debug("ldap", fmt.Sprintf("found group memberships: %v", memberOf))
  109. foundGroup := false
  110. for _, inGroup := range memberOf {
  111. for _, acceptableGroup := range server.Config.RequireGroups {
  112. if inGroup == acceptableGroup {
  113. foundGroup = true
  114. break
  115. }
  116. }
  117. if foundGroup {
  118. break
  119. }
  120. }
  121. if foundGroup {
  122. return nil
  123. } else {
  124. return ErrUserNotInRequiredGroup
  125. }
  126. }