user.rb 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. class Ldap
  2. # Class for handling LDAP Groups.
  3. # ATTENTION: Make sure to add the following lines to your code if accessing this class.
  4. # Otherwise Rails will autoload the Group model or might throw parameter errors if crearing
  5. # an ::Ldap instance.
  6. #
  7. # @example
  8. # require 'ldap'
  9. # require 'ldap/user'
  10. class User
  11. include Ldap::FilterLookup
  12. BLACKLISTED = [
  13. :admincount,
  14. :accountexpires,
  15. :badpasswordtime,
  16. :badpwdcount,
  17. :countrycode,
  18. :distinguishedname,
  19. :dnshostname,
  20. :dscorepropagationdata,
  21. :instancetype,
  22. :iscriticalsystemobject,
  23. :useraccountcontrol,
  24. :usercertificate,
  25. :objectclass,
  26. :objectcategory,
  27. :objectguid,
  28. :objectsid,
  29. :primarygroupid,
  30. :pwdlastset,
  31. :lastlogoff,
  32. :lastlogon,
  33. :lastlogontimestamp,
  34. :localpolicyflags,
  35. :lockouttime,
  36. :logoncount,
  37. :logonhours,
  38. :'msdfsr-computerreferencebl',
  39. :'msds-supportedencryptiontypes',
  40. :ridsetreferences,
  41. :samaccounttype,
  42. :memberof,
  43. :serverreferencebl,
  44. :serviceprincipalname,
  45. :showinadvancedviewonly,
  46. :usnchanged,
  47. :usncreated,
  48. :whenchanged,
  49. :whencreated,
  50. ].freeze
  51. # Returns the uid attribute.
  52. #
  53. # @param attributes [Hash{Symbol=>Array<String>}] A list of LDAP User attributes which should get checked for available uids.
  54. #
  55. # @example
  56. # Ldap::User.uid_attribute(attributes)
  57. #
  58. # @return [String] The uid attribute.
  59. def self.uid_attribute(attributes)
  60. result = nil
  61. %i(samaccountname userprincipalname uid dn).each do |attribute|
  62. next if attributes[attribute].blank?
  63. result = attribute.to_s
  64. break
  65. end
  66. result
  67. end
  68. # Initializes a wrapper around Net::LDAP and ::Ldap to handle LDAP users.
  69. #
  70. # @param [Hash] config the configuration for establishing a LDAP connection. Default is Setting 'ldap_config'.
  71. # @option config [String] :uid_attribute The uid attribute. Default is determined automatically.
  72. # @option config [String] :filter The filter for LDAP users. Default is determined automatically.
  73. # @param ldap [Ldap] An optional existing Ldap class instance. Default is a new connection with given configuration.
  74. #
  75. # @example
  76. # require 'ldap'
  77. # require 'ldap/user'
  78. # ldap_user = Ldap::User.new
  79. #
  80. # @return [nil]
  81. def initialize(config = nil, ldap: nil)
  82. @ldap = ldap || ::Ldap.new(config)
  83. handle_config(config)
  84. end
  85. # Checks if given username and password combination is valid for the connected LDAP.
  86. #
  87. # @param username [String] The username.
  88. # @param password [String] The password.
  89. #
  90. # @example
  91. # ldap_user.valid?('example_user', 'pw1234')
  92. # #=> true
  93. #
  94. # @return [Boolean] The valid state of the username and password combination.
  95. def valid?(username, password)
  96. bind_success = @ldap.connection.bind_as(
  97. base: @ldap.base_dn,
  98. filter: "(#{uid_attribute}=#{username})",
  99. password: password
  100. )
  101. message = bind_success ? 'successful' : 'failed'
  102. Rails.logger.info "ldap authentication for user '#{username}' (#{uid_attribute}) #{message}!"
  103. bind_success.present?
  104. end
  105. # Determines possible User attributes with example values.
  106. #
  107. # @param filter [String] The filter for listing users. Default is initialization parameter.
  108. # @param base_dn [String] The applied base DN for listing users. Default is Ldap#base_dn.
  109. #
  110. # @example
  111. # ldap_user.attributes
  112. # #=> {:dn=>"dn (e. g. CN=Administrator,CN=Users,DC=domain,DC=tld)", ...}
  113. #
  114. # @return [Hash{Symbol=>String}] The available User attributes as key and the name and an example as value.
  115. def attributes(filter: nil, base_dn: nil)
  116. filter ||= filter()
  117. attributes = {}
  118. known_attributes = BLACKLISTED.dup
  119. lookup_counter = 1
  120. @ldap.search(filter, base: base_dn) do |entry|
  121. new_attributes = entry.attribute_names - known_attributes
  122. if new_attributes.blank?
  123. lookup_counter += 1
  124. # check max 50 entries with
  125. # the same attributes in a row
  126. break if lookup_counter == 50
  127. next
  128. end
  129. new_attributes.each do |attribute|
  130. value = entry[attribute]
  131. next if value.blank?
  132. next if value[0].blank?
  133. example_value = value[0].force_encoding('UTF-8').encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?')
  134. attributes[attribute] = "#{attribute} (e. g. #{example_value})"
  135. end
  136. known_attributes.concat(new_attributes)
  137. lookup_counter = 0
  138. end
  139. attributes
  140. end
  141. # The active filter of the instance. If none give on initialization an automatic lookup is performed.
  142. #
  143. # @example
  144. # ldap_user.filter
  145. # #=> '(objectClass=user)'
  146. #
  147. # @return [String, nil] The active or found filter or nil if none could be found.
  148. def filter
  149. @filter ||= lookup_filter(['(&(objectClass=user)(samaccountname=*)(!(samaccountname=*$)))', '(objectClass=user)', '(objectClass=posixaccount)', '(objectClass=person)'])
  150. end
  151. # The active uid attribute of the instance. If none give on initialization an automatic lookup is performed.
  152. #
  153. # @example
  154. # ldap_user.uid_attribute
  155. # #=> 'samaccountname'
  156. #
  157. # @return [String, nil] The active or found uid attribute or nil if none could be found.
  158. def uid_attribute
  159. @uid_attribute ||= self.class.uid_attribute(attributes)
  160. end
  161. private
  162. def handle_config(config)
  163. return if config.blank?
  164. @uid_attribute = config[:uid_attribute]
  165. @filter = config[:filter]
  166. end
  167. end
  168. end