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