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.

auth_source_ldap.rb 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # Redmine - project management software
  2. # Copyright (C) 2006-2011 Jean-Philippe Lang
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. require 'iconv'
  18. require 'net/ldap'
  19. require 'net/ldap/dn'
  20. class AuthSourceLdap < AuthSource
  21. validates_presence_of :host, :port, :attr_login
  22. validates_length_of :name, :host, :maximum => 60, :allow_nil => true
  23. validates_length_of :account, :account_password, :base_dn, :filter, :maximum => 255, :allow_blank => true
  24. validates_length_of :attr_login, :attr_firstname, :attr_lastname, :attr_mail, :maximum => 30, :allow_nil => true
  25. validates_numericality_of :port, :only_integer => true
  26. validate :validate_filter
  27. before_validation :strip_ldap_attributes
  28. def self.human_attribute_name(attribute_key_name, *args)
  29. attr_name = attribute_key_name.to_s
  30. if attr_name == "filter"
  31. attr_name = "ldap_filter"
  32. end
  33. super(attr_name, *args)
  34. end
  35. def initialize(attributes=nil, *args)
  36. super
  37. self.port = 389 if self.port == 0
  38. end
  39. def authenticate(login, password)
  40. return nil if login.blank? || password.blank?
  41. attrs = get_user_dn(login, password)
  42. if attrs && attrs[:dn] && authenticate_dn(attrs[:dn], password)
  43. logger.debug "Authentication successful for '#{login}'" if logger && logger.debug?
  44. return attrs.except(:dn)
  45. end
  46. rescue Net::LDAP::LdapError => e
  47. raise AuthSourceException.new(e.message)
  48. end
  49. # test the connection to the LDAP
  50. def test_connection
  51. ldap_con = initialize_ldap_con(self.account, self.account_password)
  52. ldap_con.open { }
  53. rescue Net::LDAP::LdapError => e
  54. raise "LdapError: " + e.message
  55. end
  56. def auth_method_name
  57. "LDAP"
  58. end
  59. private
  60. def ldap_filter
  61. if filter.present?
  62. Net::LDAP::Filter.construct(filter)
  63. end
  64. rescue Net::LDAP::LdapError
  65. nil
  66. end
  67. def validate_filter
  68. if filter.present? && ldap_filter.nil?
  69. errors.add(:filter, :invalid)
  70. end
  71. end
  72. def strip_ldap_attributes
  73. [:attr_login, :attr_firstname, :attr_lastname, :attr_mail].each do |attr|
  74. write_attribute(attr, read_attribute(attr).strip) unless read_attribute(attr).nil?
  75. end
  76. end
  77. def initialize_ldap_con(ldap_user, ldap_password)
  78. options = { :host => self.host,
  79. :port => self.port,
  80. :encryption => (self.tls ? :simple_tls : nil)
  81. }
  82. options.merge!(:auth => { :method => :simple, :username => ldap_user, :password => ldap_password }) unless ldap_user.blank? && ldap_password.blank?
  83. Net::LDAP.new options
  84. end
  85. def get_user_attributes_from_ldap_entry(entry)
  86. {
  87. :dn => entry.dn,
  88. :firstname => AuthSourceLdap.get_attr(entry, self.attr_firstname),
  89. :lastname => AuthSourceLdap.get_attr(entry, self.attr_lastname),
  90. :mail => AuthSourceLdap.get_attr(entry, self.attr_mail),
  91. :auth_source_id => self.id
  92. }
  93. end
  94. # Return the attributes needed for the LDAP search. It will only
  95. # include the user attributes if on-the-fly registration is enabled
  96. def search_attributes
  97. if onthefly_register?
  98. ['dn', self.attr_firstname, self.attr_lastname, self.attr_mail]
  99. else
  100. ['dn']
  101. end
  102. end
  103. # Check if a DN (user record) authenticates with the password
  104. def authenticate_dn(dn, password)
  105. if dn.present? && password.present?
  106. initialize_ldap_con(dn, password).bind
  107. end
  108. end
  109. # Get the user's dn and any attributes for them, given their login
  110. def get_user_dn(login, password)
  111. ldap_con = nil
  112. if self.account && self.account.include?("$login")
  113. ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(login)), password)
  114. else
  115. ldap_con = initialize_ldap_con(self.account, self.account_password)
  116. end
  117. login_filter = Net::LDAP::Filter.eq( self.attr_login, login )
  118. object_filter = Net::LDAP::Filter.eq( "objectClass", "*" )
  119. attrs = {}
  120. search_filter = object_filter & login_filter
  121. if f = ldap_filter
  122. search_filter = search_filter & f
  123. end
  124. ldap_con.search( :base => self.base_dn,
  125. :filter => search_filter,
  126. :attributes=> search_attributes) do |entry|
  127. if onthefly_register?
  128. attrs = get_user_attributes_from_ldap_entry(entry)
  129. else
  130. attrs = {:dn => entry.dn}
  131. end
  132. logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug?
  133. end
  134. attrs
  135. end
  136. def self.get_attr(entry, attr_name)
  137. if !attr_name.blank?
  138. entry[attr_name].is_a?(Array) ? entry[attr_name].first : entry[attr_name]
  139. end
  140. end
  141. end