class ActiveLdap::Adapter::Base
Constants
- LOGICAL_OPERATORS
- VALID_ADAPTER_CONFIGURATION_KEYS
Public Class Methods
new(configuration={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 21 def initialize(configuration={}) @connection = nil @disconnected = false @bound = false @bind_tried = false @entry_attributes = {} @configuration = configuration.dup @logger = @configuration.delete(:logger) @configuration.assert_valid_keys(VALID_ADAPTER_CONFIGURATION_KEYS) VALID_ADAPTER_CONFIGURATION_KEYS.each do |name| instance_variable_set("@#{name}", configuration[name]) end @instrumenter = ActiveSupport::Notifications.instrumenter end
Public Instance Methods
add(dn, entries, options={}) { |dn, entries| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 195 def add(dn, entries, options={}) dn = ensure_dn_string(dn) begin operation(options) do yield(dn, entries) end rescue LdapError::NoSuchObject raise EntryNotFound, _("No such entry: %s") % dn rescue LdapError::InvalidDnSyntax raise DistinguishedNameInvalid.new(dn) rescue LdapError::AlreadyExists raise EntryAlreadyExist, _("%s: %s") % [$!.message, dn] rescue LdapError::StrongAuthRequired raise StrongAuthenticationRequired, _("%s: %s") % [$!.message, dn] rescue LdapError::ObjectClassViolation raise RequiredAttributeMissed, _("%s: %s") % [$!.message, dn] rescue LdapError::UnwillingToPerform raise OperationNotPermitted, _("%s: %s") % [$!.message, dn] end end
bind(options={}) { || ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 60 def bind(options={}) @bind_tried = true bind_dn = ensure_dn_string(options[:bind_dn] || @bind_dn) try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl if options.has_key?(:allow_anonymous) allow_anonymous = options[:allow_anonymous] else allow_anonymous = @allow_anonymous end options = options.merge(:allow_anonymous => allow_anonymous) # Rough bind loop: # Attempt 1: SASL if available # Attempt 2: SIMPLE with credentials if password block # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '') if try_sasl and sasl_bind(bind_dn, options) @logger.info {_('Bound to %s by SASL as %s') % [target, bind_dn]} elsif simple_bind(bind_dn, options) @logger.info {_('Bound to %s by simple as %s') % [target, bind_dn]} elsif allow_anonymous and bind_as_anonymous(options) @logger.info {_('Bound to %s as anonymous') % target} else message = yield if block_given? message ||= _('All authentication methods for %s exhausted.') % target raise AuthenticationError, message end @bound = true @bound end
bind_as_anonymous(options={}) { || ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 97 def bind_as_anonymous(options={}) yield end
bound?()
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 105 def bound? connecting? and @bound end
connect(options={}) { |host, port, method| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 36 def connect(options={}) host = options[:host] || @host method = options[:method] || @method || :plain port = options[:port] || @port || ensure_port(method) method = ensure_method(method) @disconnected = false @bound = false @bind_tried = false @connection, @uri, @with_start_tls = yield(host, port, method) prepare_connection(options) bind(options) end
connecting?()
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 101 def connecting? !@connection.nil? and !@disconnected end
delete(targets, options={}) { |target| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 176 def delete(targets, options={}) targets = [targets] unless targets.is_a?(Array) return if targets.empty? begin operation(options) do targets.each do |target| target = ensure_dn_string(target) begin yield(target) rescue LdapError::UnwillingToPerform, LdapError::InsufficientAccess raise OperationNotPermitted, _("%s: %s") % [$!.message, target] end end end rescue LdapError::NoSuchObject raise EntryNotFound, _("No such entry: %s") % target end end
disconnect!(options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 49 def disconnect!(options={}) unbind(options) @connection = @uri = @with_start_tls = nil @disconnected = true end
entry_attribute(object_classes)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 148 def entry_attribute(object_classes) @entry_attributes[object_classes.uniq.sort] ||= EntryAttribute.new(schema, object_classes) end
jndi_connection(options)
click to toggle source
# File lib/active_ldap/adapter/jndi.rb, line 7 def jndi_connection(options) require 'active_ldap/adapter/jndi_connection' Jndi.new(options) end
ldap_connection(options)
click to toggle source
# File lib/active_ldap/adapter/ldap.rb, line 7 def ldap_connection(options) require 'active_ldap/adapter/ldap_ext' Ldap.new(options) end
modify(dn, entries, options={}) { |dn, entries| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 216 def modify(dn, entries, options={}) dn = ensure_dn_string(dn) begin operation(options) do begin yield(dn, entries) rescue LdapError::UnwillingToPerform, LdapError::InsufficientAccess raise OperationNotPermitted, _("%s: %s") % [$!.message, target] end end rescue LdapError::UndefinedType raise rescue LdapError::ObjectClassViolation raise RequiredAttributeMissed, _("%s: %s") % [$!.message, dn] end end
modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={}) { |dn, new_rdn, delete_old_rdn, new_superior| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 233 def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={}) dn = ensure_dn_string(dn) operation(options) do yield(dn, new_rdn, delete_old_rdn, new_superior) end end
naming_contexts()
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 139 def naming_contexts root_dse_values('namingContexts') end
net_ldap_connection(options)
click to toggle source
# File lib/active_ldap/adapter/net_ldap.rb, line 9 def net_ldap_connection(options) require 'active_ldap/adapter/net_ldap_ext' NetLdap.new(options) end
rebind(options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 55 def rebind(options={}) unbind(options) if bound? connect(options) end
schema(options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 109 def schema(options={}) @schema ||= operation(options) do base = options[:base] attrs = options[:attributes] attrs ||= [ 'objectClasses', 'attributeTypes', 'matchingRules', 'matchingRuleUse', 'dITStructureRules', 'dITContentRules', 'nameForms', 'ldapSyntaxes', #'extendedAttributeInfo', # if we need RANGE-LOWER/UPPER. ] base ||= root_dse_values('subschemaSubentry', options)[0] base ||= 'cn=schema' schema = nil search(:base => base, :scope => :base, :filter => '(objectClass=subschema)', :attributes => attrs, :limit => 1) do |dn, attributes| schema = Schema.new(attributes) end schema || Schema.new([]) end end
search(options={}) { |base, scope, filter, attrs, limit| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 153 def search(options={}) filter = parse_filter(options[:filter]) || 'objectClass=*' attrs = options[:attributes] || [] scope = ensure_scope(options[:scope] || @scope) base = options[:base] limit = options[:limit] || 0 limit = nil if limit <= 0 attrs = attrs.to_a # just in case base = ensure_dn_string(base) begin operation(options) do yield(base, scope, filter, attrs, limit) end rescue LdapError::NoSuchObject, LdapError::InvalidDnSyntax # Do nothing on failure @logger.info do args = [$!.class, $!.message, filter, attrs.inspect] _("Ignore error %s(%s): filter %s: attributes: %s") % args end end end
supported_control()
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 143 def supported_control @supported_control ||= SupportedControl.new(root_dse_values("supportedControl")) end
unbind(options={}) { || ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 92 def unbind(options={}) yield if @connection and (@bind_tried or bound?) @bind_tried = @bound = false end
Private Instance Methods
assert_filter_logical_operator(operator)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 547 def assert_filter_logical_operator(operator) return if operator.nil? unless filter_logical_operator?(operator) raise ArgumentError, _("invalid logical operator: %s: available operators: %s") % [operator.inspect, LOGICAL_OPERATORS.inspect] end end
can_reconnect?(options={})
click to toggle source
Determine if we have exceed the retry limit or not. True is reconnecting is allowed - False if not.
# File lib/active_ldap/adapter/base.rb, line 609 def can_reconnect?(options={}) retry_limit = options[:retry_limit] || @retry_limit reconnect_attempts = options[:reconnect_attempts] || 0 retry_limit < 0 or reconnect_attempts <= retry_limit end
collection?(object)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 526 def collection?(object) !object.is_a?(String) and object.respond_to?(:each) end
construct_component(key, value, operator=nil)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 459 def construct_component(key, value, operator=nil) value, options = extract_filter_value_options(value) comparison_operator = options[:operator] || "=" if collection?(value) return nil if value.empty? operator, value = normalize_array_filter(value, operator) values = [] value.each do |val| if collection?(val) values.concat(val.collect {|v| [key, comparison_operator, v]}) else values << [key, comparison_operator, val] end end values[0] = values[0][1] if filter_logical_operator?(values[0][1]) parse_filter(values, operator) else [ "(", escape_filter_key(key), comparison_operator, escape_filter_value(value, options), ")" ].join end end
construct_components(components, operator)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 435 def construct_components(components, operator) components.collect do |component| if component.is_a?(Array) if filter_logical_operator?(component[0]) parse_filter(component) elsif component.size == 2 key, value = component if value.is_a?(Hash) parse_filter(value, key) else construct_component(key, value, operator) end else construct_component(component[0], component[1..-1], operator) end elsif component.is_a?(Symbol) assert_filter_logical_operator(component) nil else parse_filter(component, operator) end end end
construct_filter(components, operator=nil)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 511 def construct_filter(components, operator=nil) operator = normalize_filter_logical_operator(operator) components = components.compact case components.size when 0 nil when 1 filter = components[0] filter = "(!#{filter})" if operator == :not filter else "(#{operator == :and ? '&' : '|'}#{components.join})" end end
construct_uri(host, port, ssl)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 645 def construct_uri(host, port, ssl) protocol = ssl ? "ldaps" : "ldap" URI.parse("#{protocol}://#{host}:#{port}").to_s end
do_in_timeout(timeout, &block)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 323 def do_in_timeout(timeout, &block) Timeout.alarm(timeout, &block) end
ensure_dn_string(dn)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 669 def ensure_dn_string(dn) if dn.is_a?(DN) dn.to_s else dn end end
ensure_port(method)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 241 def ensure_port(method) if method == :ssl URI::LDAPS::DEFAULT_PORT else URI::LDAP::DEFAULT_PORT end end
escape_filter_key(key)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 486 def escape_filter_key(key) escape_filter_value(key.to_s) end
escape_filter_value(value, options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 490 def escape_filter_value(value, options={}) case value when Numeric, DN value = value.to_s when Time value = Schema::GeneralizedTime.new.normalize_value(value) end value.gsub(/(?:[:()\\0]|\*\*?)/) do |s| if s == "*" s else s = "*" if s == "**" if s.respond_to?(:getbyte) "\\%02X" % s.getbyte(0) else "\\%02X" % s[0] end end end end
extract_filter_value_options(value)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 416 def extract_filter_value_options(value) options = {} if value.is_a?(Array) case value[0] when Hash options = value[0] value = value[1] when "=", "~=", "<=", ">=" options[:operator] = value[0] if value.size > 2 value = value[1..-1] else value = value[1] end end end [value, options] end
filter_logical_operator?(operator)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 531 def filter_logical_operator?(operator) LOGICAL_OPERATORS.include?(operator) end
log(name, info=nil) { || ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 659 def log(name, info=nil) result = nil payload = {:name => name} payload[:info] = info if info @instrumenter.instrument("log_info.active_ldap", payload) do result = yield if block_given? end result end
need_credential_sasl_mechanism?(mechanism)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 274 def need_credential_sasl_mechanism?(mechanism) not %Q(GSSAPI EXTERNAL ANONYMOUS).include?(mechanism) end
normalize_array_filter(filter, operator=nil)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 405 def normalize_array_filter(filter, operator=nil) filter_operator, *components = filter if filter_logical_operator?(filter_operator) operator = filter_operator else components.unshift(filter_operator) components = [components] unless filter_operator.is_a?(Array) end [operator, components] end
normalize_filter_logical_operator(operator)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 535 def normalize_filter_logical_operator(operator) assert_filter_logical_operator(operator) case (operator || :and) when :and, :& :and when :or, :| :or else :not end end
operation(options) { || ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 252 def operation(options) retried = false options = options.dup options[:try_reconnect] = true unless options.has_key?(:try_reconnect) try_reconnect = false begin reconnect_if_need(options) try_reconnect = options[:try_reconnect] with_timeout(try_reconnect, options) do yield end rescue ConnectionError if try_reconnect and !retried retried = true @disconnected = true retry else raise end end end
parse_filter(filter, operator=nil)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 372 def parse_filter(filter, operator=nil) return nil if filter.nil? if !filter.is_a?(String) and !filter.respond_to?(:collect) filter = filter.to_s end case filter when String parse_filter_string(filter) when Hash components = filter.sort_by {|k, v| k.to_s}.collect do |key, value| construct_component(key, value, operator) end construct_filter(components, operator) else operator, components = normalize_array_filter(filter, operator) components = construct_components(components, operator) construct_filter(components, operator) end end
parse_filter_string(filter)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 393 def parse_filter_string(filter) if /\A\s*\z/.match(filter) nil else if filter[0, 1] == "(" filter else "(#{filter})" end end end
password(bind_dn, options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 278 def password(bind_dn, options={}) passwd = options[:password] || @password return passwd if passwd password_block = options[:password_block] || @password_block # TODO: Give a warning to reconnect users with password clearing # Get the passphrase for the first time, or anew if we aren't storing if password_block.respond_to?(:call) passwd = password_block.call(bind_dn) else @logger.error {_('password_block not nil or Proc object. Ignoring.')} return nil end # Store the password for quick reference later if options.has_key?(:store_password) store_password = options[:store_password] else store_password = @store_password end @password = store_password ? passwd : nil passwd end
prepare_connection(options)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 249 def prepare_connection(options) end
reconnect(options={})
click to toggle source
Attempts to reconnect up to the number of times allowed If forced, try once then fail with ConnectionError if not connected.
# File lib/active_ldap/adapter/base.rb, line 558 def reconnect(options={}) options = options.dup force = options[:force] retry_limit = options[:retry_limit] || @retry_limit retry_wait = options[:retry_wait] || @retry_wait options[:reconnect_attempts] ||= 0 loop do @logger.debug {_('Attempting to reconnect')} disconnect! # Reset the attempts if this was forced. options[:reconnect_attempts] = 0 if force options[:reconnect_attempts] += 1 if retry_limit >= 0 begin options[:try_reconnect] = false connect(options) break rescue AuthenticationError, Timeout::Error raise rescue => detail @logger.error do _("Reconnect to server failed: %s\n" "Reconnect to server failed backtrace:\n" "%s") % [detail.message, detail.backtrace.join("\n")] end # Do not loop if forced raise ConnectionError, detail.message if force end unless can_reconnect?(options) raise ConnectionError, _('Giving up trying to reconnect to LDAP server.') end # Sleep before looping sleep retry_wait end true end
reconnect_if_need(options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 600 def reconnect_if_need(options={}) return if connecting? with_timeout(false, options) do reconnect(options) end end
root_dse(attrs, options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 626 def root_dse(attrs, options={}) found_attributes = nil if options.has_key?(:try_reconnect) try_reconnect = options[:try_reconnect] else try_reconnect = true end search(:base => "", :scope => :base, :attributes => attrs, :limit => 1, :try_reconnect => try_reconnect, :use_paged_results => false) do |dn, attributes| found_attributes = attributes end found_attributes end
root_dse_values(key, options={})
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 616 def root_dse_values(key, options={}) dse = root_dse([key], options) return [] if dse.nil? normalized_key = key.downcase dse.each do |_key, _value| return _value if _key.downcase == normalized_key end [] end
sasl_bind(bind_dn, options={}) { |bind_dn, mechanism, sasl_quiet| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 327 def sasl_bind(bind_dn, options={}) # Get all SASL mechanisms mechanisms = operation(options) do root_dse_values("supportedSASLMechanisms", options) end if options.has_key?(:sasl_quiet) sasl_quiet = options[:sasl_quiet] else sasl_quiet = @sasl_quiet end sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms sasl_mechanisms.each do |mechanism| next unless mechanisms.include?(mechanism) return true if yield(bind_dn, mechanism, sasl_quiet) end false end
simple_bind(bind_dn, options={}) { |bind_dn, passwd| ... }
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 347 def simple_bind(bind_dn, options={}) return false unless bind_dn passwd = password(bind_dn, options) return false unless passwd if passwd.empty? if options[:allow_anonymous] @logger.info {_("Skip simple bind with empty password.")} return false else raise AuthenticationError, _("Can't use empty password for simple bind.") end end begin yield(bind_dn, passwd) rescue LdapError::InvalidDnSyntax raise DistinguishedNameInvalid.new(bind_dn) rescue LdapError::InvalidCredentials false end end
target()
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 650 def target return nil if @uri.nil? if @with_start_tls "#{@uri}(StartTLS)" else @uri end end
with_timeout(try_reconnect=true, options={}, &block)
click to toggle source
# File lib/active_ldap/adapter/base.rb, line 303 def with_timeout(try_reconnect=true, options={}, &block) n_retries = 0 retry_limit = options[:retry_limit] || @retry_limit begin do_in_timeout(@timeout, &block) rescue Timeout::Error => e @logger.error {_('Requested action timed out.')} if @retry_on_timeout and (retry_limit < 0 or n_retries <= retry_limit) n_retries += 1 if connecting? retry elsif try_reconnect retry if with_timeout(false, options) {reconnect(options)} end end @logger.error {e.message} raise TimeoutError, e.message end end