user.rb 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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 = %i[
  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. objectsid
  28. primarygroupid
  29. pwdlastset
  30. lastlogoff
  31. lastlogon
  32. lastlogontimestamp
  33. localpolicyflags
  34. lockouttime
  35. logoncount
  36. logonhours
  37. msdfsr-computerreferencebl
  38. msds-supportedencryptiontypes
  39. ridsetreferences
  40. samaccounttype
  41. memberof
  42. serverreferencebl
  43. serviceprincipalname
  44. showinadvancedviewonly
  45. usnchanged
  46. usncreated
  47. whenchanged
  48. whencreated
  49. ].freeze
  50. # Returns the uid attribute.
  51. #
  52. # @param attributes [Hash{Symbol=>Array<String>}] A list of LDAP User attributes which should get checked for available uids.
  53. #
  54. # @example
  55. # Ldap::User.uid_attribute(attributes)
  56. #
  57. # @return [String] The uid attribute.
  58. def self.uid_attribute(attributes)
  59. result = nil
  60. %i[objectguid entryuuid samaccountname userprincipalname uid dn].each do |attribute|
  61. next if attributes[attribute].blank?
  62. result = attribute.to_s
  63. break
  64. end
  65. result
  66. end
  67. # Initializes a wrapper around Net::LDAP and ::Ldap to handle LDAP users.
  68. #
  69. # @param [Hash] config the configuration for establishing a LDAP connection. Default is Setting 'ldap_config'.
  70. # @option config [String] :uid_attribute The uid attribute. Default is determined automatically.
  71. # @option config [String] :filter The filter for LDAP users. Default is determined automatically.
  72. # @param ldap [Ldap] An optional existing Ldap class instance. Default is a new connection with given configuration.
  73. #
  74. # @example
  75. # require 'ldap'
  76. # require 'ldap/user'
  77. # ldap_user = Ldap::User.new
  78. #
  79. # @return [nil]
  80. def initialize(config = nil, ldap: nil)
  81. @config = config || Setting.get('ldap_config')
  82. @ldap = ldap || ::Ldap.new(@config)
  83. handle_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: "(#{login_attribute}=#{username})",
  99. password: password
  100. )
  101. message = bind_success ? 'successful' : 'failed'
  102. Rails.logger.info "ldap authentication for user '#{username}' (#{login_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. attr_reader :config
  163. def login_attribute
  164. @login_attribute ||= config[:user_attributes]&.key('login') || uid_attribute
  165. end
  166. def handle_config
  167. return if config.blank?
  168. @uid_attribute = config[:uid_attribute]
  169. @filter = config[:filter]
  170. end
  171. end
  172. end