lib/activeldap/base.rb in ruby-activeldap-debug-0.5.8 vs lib/activeldap/base.rb in ruby-activeldap-debug-0.5.9

- old
+ new

@@ -32,11 +32,11 @@ require 'ldap' require 'ldap/schema' require 'log4r' module ActiveLDAP - # OO-interface to LDAP assuming pam/nss_ldap-style comanization with Active specifics + # OO-interface to LDAP assuming pam/nss_ldap-style organization with Active specifics # Each subclass does a ldapsearch for the matching entry. # If no exact match, raise an error. # If match, change all LDAP attributes in accessor attributes on the object. # -- these are ACTUALLY populated from schema - see subschema.rb example # -- @conn.schema().each{|k,vs| vs.each{|v| print("#{k}: #{v}\n")}} @@ -165,11 +165,11 @@ # # == +config+ # +config+ must be a hash that may contain any of the following fields: # :user, :password_block, :logger, :host, :port, :base, :bind_format, :try_sasl, :allow_anonymous # :user specifies the username to bind with. - # :bind_format specifies the string to substitute the username into on bind. e.g. uid=%s,ou=People,dc=example,dc=com. Overrides @@bind_format. + # :bind_format specifies the string to substitute the username into on bind. e.g. uid=%s,ou=People,dc=dataspill,dc=org. Overrides @@bind_format. # :password_block specifies a Proc object that will yield a String to be used as the password when called. # :logger specifies a preconfigured Log4r::Logger to be used for all logging # :host overrides the configuration.rb @@host setting with the LDAP server hostname # :port overrides the configuration.rb @@port setting for the LDAP server port # :base overwrites Base.base - this affects EVERYTHING @@ -292,11 +292,11 @@ values.push(res) end rescue RuntimeError => detail #TODO# Check for 'No message' when retrying # The connection may have gone stale. Let's reconnect and retry. - if tries > @max_retries + if tries > @@config[:retries] # Do nothing on failure @@logger.debug "No matches for #{config[:filter]} and attrs #{config[:attrs]}" end tries += 1 # Reconnect and rebind. @@ -344,24 +344,24 @@ matches = [] tries = 0 begin # Get some attributes - @@conn.search(base(), LDAP::LDAP_SCOPE_SUBTREE, "(#{attr}=#{val})") do |m| + @@conn.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{attr}=#{val})") do |m| # Extract the dnattr value dnval = m.dn.split(/,/)[0].split(/=/)[1] if objects - return eval("#{real_klass}.new(m)") + return real_klass.new(m) else return dnval end end rescue RuntimeError => detail #todo# check for 'no message' when retrying # the connection may have gone stale. let's reconnect and retry. - if tries > @max_retries + if tries > @@config[:retries] # do nothing on failure @@logger.debug "no matches for #{attr}=#{val}" end tries += 1 # reconnect and rebind. @@ -392,37 +392,41 @@ real_klass = self.class end # Allow a single string argument val = config + attr = dnattr() objects = false # Or a hash if config.respond_to?"has_key?" - attr = config[:attribute] || dnattr() val = config[:value] || '*' + attr = config[:attribute] || dnattr() objects = config[:objects] end matches = [] tries = 0 begin # Get some attributes - @@conn.search(base(), LDAP::LDAP_SCOPE_SUBTREE, "(#{attr}=#{val})") do |m| + @@conn.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{attr}=#{val})") do |m| # Extract the dnattr value dnval = m.dn.split(/,/)[0].split(/=/)[1] if objects - matches.push(eval("#{real_klass}.new(m)")) + matches.push(real_klass.new(m)) else matches.push(dnval) end end rescue RuntimeError => detail #todo# check for 'no message' when retrying + + #TODO# This is broken because search gives bad messages. + # the connection may have gone stale. let's reconnect and retry. - if tries > @max_retries + if tries > @@config[:retries] # do nothing on failure @@logger.debug "no matches for #{attr}=#{val}" end tries += 1 # reconnect and rebind. @@ -506,11 +510,14 @@ end @data = {} # where the r/w entry data is stored @ldap_data = {} # original ldap entry data @attr_methods = {} # list of valid method calls for attributes used for dereferencing - @last_oc = nil # for use in other methods for "caching" + @last_oc = false # for use in other methods for "caching" + if dnattr().empty? + raise RuntimeError, "dnattr() not set for this class." + end # Break val apart if it is a dn if val.match(/^#{dnattr()}=([^,=]+),#{base()}$/i) val = $1 elsif val.match(/[=,]/) @@ -527,11 +534,11 @@ else # do a search then # Search for the existing entry tries = 0 begin # Get some attributes - Base.connection.search("#{dnattr()}=#{val},#{base()}", LDAP::LDAP_SCOPE_SUBTREE, "objectClass=*") do |m| + Base.connection.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{dnattr()}=#{val})") do |m| # Save DN @dn = m.dn # Load up data into tmp @@logger.debug("loading entry: #{@dn}") m.attrs.each do |attr| @@ -555,28 +562,28 @@ # Populate real data now that we have the schema with aliases @ldap_data.each do |pair| send(:attribute_method=, pair[0], pair[1].dup) end - rescue RuntimeError => detail - #todo# check for 'no message' when retrying - # the connection may have gone stale. let's reconnect and retry. - if tries > @max_retries - @exists = false - # Create what should be the authoritative DN - @dn = "#{dnattr()}=#{val},#{base()}" - send(:apply_objectclass, required_classes()) + rescue RuntimeError => detail + #todo# check for 'no message' when retrying + # the connection may have gone stale. let's reconnect and retry. + if tries > @@config[:retries] + @exists = false + # Create what should be the authoritative DN + @dn = "#{dnattr()}=#{val},#{base()}" + send(:apply_objectclass, required_classes()) - # Setup dn attribute (later rdn too!) - attr_sym = "#{dnattr()}=".to_sym - @@logger.debug("new: setting dnattr: #{dnattr()} = #{val}") - send(attr_sym, val) - end - tries += 1 - # reconnect and rebind. - do_connect() - retry + # Setup dn attribute (later rdn too!) + attr_sym = "#{dnattr()}=".to_sym + @@logger.debug("new: setting dnattr: #{dnattr()} = #{val}") + send(attr_sym, val) + end + tries += 1 + # reconnect and rebind. + do_connect() + retry rescue LDAP::ResultError @exists = false # Create what should be the authoritative DN @dn = "#{dnattr()}=#{val},#{base()}" send(:apply_objectclass, required_classes()) @@ -670,20 +677,20 @@ @@logger.debug("stub: delete called") tries = 0 begin @@conn.delete(@dn) @exists = false - rescue RuntimeError => detail - #todo# check for 'no message' when retrying - # the connection may have gone stale. let's reconnect and retry. - if tries > @max_retries + rescue RuntimeError => detail + #todo# check for 'no message' when retrying + # the connection may have gone stale. let's reconnect and retry. + if tries > @@config[:retries] raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'" - end - tries += 1 - # reconnect and rebind. - do_connect() - retry + end + tries += 1 + # reconnect and rebind. + do_connect() + retry rescue LDAP::ResultError => detail raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'" end end @@ -810,20 +817,20 @@ tries = 0 begin @@logger.debug("#write: modifying #{@dn}") @@conn.modify(@dn, entry) @@logger.debug("#write: modify successful") - rescue RuntimeError => detail - #todo# check for 'no message' when retrying - # the connection may have gone stale. let's reconnect and retry. - if tries > @max_retries - raise WriteError, "Could not update LDAP entry: #{detail}" - end - tries += 1 - # reconnect and rebind. - do_connect() - retry + rescue RuntimeError => detail + #todo# check for 'no message' when retrying + # the connection may have gone stale. let's reconnect and retry. + if tries > @@config[:retries] + raise WriteError, "Could not update LDAP entry: #{detail}" + end + tries += 1 + # reconnect and rebind. + do_connect() + retry rescue => detail raise WriteError, "Could not update LDAP entry: #{detail}" end else # add everything! @@logger.debug("#write: adding all attribute value pairs") @@ -851,11 +858,11 @@ @@conn.add(@dn, entry) @@logger.debug("#write: add successful") @exists = true rescue RuntimeError => e # The connection may have gone stale. Let's reconnect and retry. - if tries > @max_retries + if tries > @@config[:retries] raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}" end tries += 1 # Reconnect and rebind. do_connect() @@ -982,10 +989,13 @@ # given the new objectclasses. def apply_objectclass(val) @@logger.debug("stub: objectClass=(#{val.inspect}) called") new_oc = val new_oc = [val] if new_oc.class != Array + if defined?(@last_oc).nil? + @last_oc = false + end return new_oc if @last_oc == new_oc # Store for caching purposes @last_oc = new_oc.dup @@ -994,20 +1004,20 @@ @data['objectClass'] = new_oc.uniq # Build |data| from schema # clear attr_method mapping first @attr_methods = {} - @must = [] - @may = [] + @must = [] + @may = [] new_oc.each do |objc| # get all attributes for the class attributes = Base.schema.class_attributes(objc.to_s) - @must += attributes[:must] - @may += attributes[:may] + @must += attributes[:must] + @may += attributes[:may] end - @must.uniq! - @may.uniq! + @must.uniq! + @may.uniq! (@must+@may).each do |attr| # Update attr_method with appropriate define_attribute_methods(attr) end @@ -1114,35 +1124,44 @@ # Performs the actually connection. This separate so that it may # be called to refresh stale connections. def Base.do_connect() - # Connect to LDAP - begin - # SSL using START_TLS - @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], true) - rescue - @@logger.warn "Warning: Failed to connect using TLS!" - begin - @@logger.warn "Warning: Attempting SSL connection . . ." - @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], false) - # HACK: Load the schema here because otherwise you can't tell if the - # HACK: SSLConn is a real SSL connection. - @@schema = @@conn.schema() if @@schema.nil? - rescue - @@logger.warn "Warning: Attempting unencrypted connection . . ." - @@conn = LDAP::Conn.new(@@config[:host], @@config[:port]) - end - end - @@logger.debug "Connected to #{@@config[:host]}:#{@@config[:port]}." + # Wrap the whole thing an try retries times + tries = 0 + begin + # Connect to LDAP + begin + # SSL using START_TLS + @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], true) + rescue + @@logger.warn "Warning: Failed to connect using TLS!" + begin + @@logger.warn "Warning: Attempting SSL connection . . ." + @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], false) + # HACK: Load the schema here because otherwise you can't tell if the + # HACK: SSLConn is a real SSL connection. + @@schema = @@conn.schema() if @@schema.nil? + rescue + @@logger.warn "Warning: Attempting unencrypted connection . . ." + @@conn = LDAP::Conn.new(@@config[:host], @@config[:port]) + end + end + @@logger.debug "Connected to #{@@config[:host]}:#{@@config[:port]}." - # Enforce LDAPv3 - @@conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) + # Enforce LDAPv3 + @@conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) - # Authenticate - do_bind - end + # Authenticate + do_bind + rescue => e + # Retry + tries += 1 + raise e if tries > @@config[:retries] + retry + end + end # Wrapper all bind activity def Base.do_bind() bind_dn = @@config[:bind_format] % [@@config[:user]] @@ -1203,10 +1222,10 @@ def Base.do_sasl_bind(bind_dn) # Get all SASL mechanisms mechanisms = @@conn.root_dse[0]['supportedSASLMechanisms'] # Use GSSAPI if available # Currently only GSSAPI is supported with Ruby/LDAP from - # http://caliban.com/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm + # http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm # TODO: Investigate further SASL support if mechanisms.respond_to? :member? and mechanisms.member? 'GSSAPI' begin @@conn.sasl_bind(bind_dn, 'GSSAPI') return true