Class ActiveLdap::Adapter::Base
In: lib/active_ldap/adapter/base.rb
lib/active_ldap/adapter/ldap.rb
lib/active_ldap/adapter/net_ldap.rb
Parent: Object
Error DeleteError LdapError AdapterNotSpecified OperationNotPermitted RequiredAttributeMissed LdifInvalid AttributeAssignmentError RequiredObjectClassMissed DistinguishedNameNotSetError StrongAuthenticationRequired ConnectionError SaveError EntryNotFound AuthenticationError EntryNotSaved UnknownAttribute ConnectionNotEstablished TimeoutError ConfigurationError AdapterNotFound DistinguishedNameInvalid ObjectClassError EntryInvalid EntryAlreadyExist Base DistinguishedName Ldif Reloadable::Deprecated Reloadable::Subclasses Enumerable Collection StandardError Children HasMany HasManyWrap BelongsToMany Proxy BelongsTo Common Find LDIF Delete Update GetText Parser GetTextSupport Base\n[lib/active_ldap/adapter/base.rb\nlib/active_ldap/adapter/ldap.rb\nlib/active_ldap/adapter/net_ldap.rb] Ldap NetLdap Normalize ActiveRecord::Callbacks ActiveRecord::Validations Schema\n[lib/active_ldap/schema.rb\nlib/active_ldap/schema/syntaxes.rb] lib/active_ldap/base.rb lib/active_ldap/schema.rb lib/active_ldap/ldif.rb lib/active_ldap/distinguished_name.rb lib/active_ldap/ldap_error.rb ClassMethods Associations ClassMethods HumanReadable lib/active_ldap/association/has_many_wrap.rb lib/active_ldap/association/has_many.rb lib/active_ldap/association/proxy.rb lib/active_ldap/association/children.rb lib/active_ldap/association/collection.rb lib/active_ldap/association/belongs_to_many.rb lib/active_ldap/association/belongs_to.rb HasManyUtils Association ClassMethods Tree Acts Common LDIF Delete Find Update Operations lib/active_ldap/get_text/parser.rb GetText ClassMethods Configuration Command lib/active_ldap/adapter/net_ldap.rb lib/active_ldap/adapter/ldap.rb Adapter GetTextSupport Normalize ClassMethods Attributes Escape Callbacks ClassMethods ObjectClass Helper Validations ClassMethods Connection GetTextFallback Populate Salt UserPassword ActiveLdap dot/m_40_0.png

Methods

Included Modules

GetTextSupport

Constants

VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :timeout, :retry_on_timeout, :retry_limit, :retry_wait, :bind_dn, :password, :password_block, :try_sasl, :sasl_mechanisms, :sasl_quiet, :allow_anonymous, :store_password, :scope]
LOGICAL_OPERATORS = [:and, :or, :not, :&, :|]

Public Class methods

[Source]

    # File lib/active_ldap/adapter/ldap.rb, line 7
 7:         def ldap_connection(options)
 8:           require 'active_ldap/adapter/ldap_ext'
 9:           Ldap.new(options)
10:         end

[Source]

    # File lib/active_ldap/adapter/net_ldap.rb, line 9
 9:         def net_ldap_connection(options)
10:           require 'active_ldap/adapter/net_ldap_ext'
11:           NetLdap.new(options)
12:         end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 16
16:       def initialize(configuration={})
17:         @connection = nil
18:         @disconnected = false
19:         @configuration = configuration.dup
20:         @logger = @configuration.delete(:logger)
21:         @configuration.assert_valid_keys(VALID_ADAPTER_CONFIGURATION_KEYS)
22:         VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
23:           instance_variable_set("@#{name}", configuration[name])
24:         end
25:       end

Public Instance methods

[Source]

     # File lib/active_ldap/adapter/base.rb, line 166
166:       def add(dn, entries, options={})
167:         begin
168:           operation(options) do
169:             yield(dn, entries)
170:           end
171:         rescue LdapError::NoSuchObject
172:           raise EntryNotFound, _("No such entry: %s") % dn
173:         rescue LdapError::InvalidDnSyntax
174:           raise DistinguishedNameInvalid.new(dn)
175:         rescue LdapError::AlreadyExists
176:           raise EntryAlreadyExist, _("%s: %s") % [$!.message, dn]
177:         rescue LdapError::StrongAuthRequired
178:           raise StrongAuthenticationRequired, _("%s: %s") % [$!.message, dn]
179:         rescue LdapError::ObjectClassViolation
180:           raise RequiredAttributeMissed, _("%s: %s") % [$!.message, dn]
181:         rescue LdapError::UnwillingToPerform
182:           raise OperationNotPermitted, _("%s: %s") % [$!.message, dn]
183:         end
184:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 48
48:       def bind(options={})
49:         bind_dn = options[:bind_dn] || @bind_dn
50:         try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
51:         if options.has_key?(:allow_anonymous)
52:           allow_anonymous = options[:allow_anonymous]
53:         else
54:           allow_anonymous = @allow_anonymous
55:         end
56: 
57:         # Rough bind loop:
58:         # Attempt 1: SASL if available
59:         # Attempt 2: SIMPLE with credentials if password block
60:         # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
61:         if try_sasl and sasl_bind(bind_dn, options)
62:           @logger.info {_('Bound by SASL as %s') % bind_dn}
63:         elsif simple_bind(bind_dn, options)
64:           @logger.info {_('Bound by simple as %s') % bind_dn}
65:         elsif allow_anonymous and bind_as_anonymous(options)
66:           @logger.info {_('Bound as anonymous')}
67:         else
68:           message = yield if block_given?
69:           message ||= _('All authentication methods exhausted.')
70:           raise AuthenticationError, message
71:         end
72: 
73:         bound?
74:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 76
76:       def bind_as_anonymous(options={})
77:         operation(options) do
78:           yield
79:         end
80:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 27
27:       def connect(options={})
28:         host = options[:host] || @host
29:         port = options[:port] || @port
30:         method = ensure_method(options[:method] || @method)
31:         @disconnected = false
32:         @connection = yield(host, port, method)
33:         prepare_connection(options)
34:         bind(options)
35:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 82
82:       def connecting?
83:         !@connection.nil? and !@disconnected
84:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 151
151:       def delete(targets, options={})
152:         targets = [targets] unless targets.is_a?(Array)
153:         return if targets.empty?
154:         target = nil
155:         begin
156:           operation(options) do
157:             targets.each do |target|
158:               yield(target)
159:             end
160:           end
161:         rescue LdapError::NoSuchObject
162:           raise EntryNotFound, _("No such entry: %s") % target
163:         end
164:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 37
37:       def disconnect!(options={})
38:         return if @connection.nil?
39:         unbind(options)
40:         @connection = nil
41:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 112
112:       def load(ldifs, options={})
113:         operation(options) do
114:           ldifs.split(/(?:\r?\n){2,}/).each do |ldif|
115:             yield(ldif)
116:           end
117:         end
118:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 186
186:       def modify(dn, entries, options={})
187:         begin
188:           operation(options) do
189:             yield(dn, entries)
190:           end
191:         rescue LdapError::UndefinedType
192:           raise
193:         rescue LdapError::ObjectClassViolation
194:           raise RequiredAttributeMissed, _("%s: %s") % [$!.message, dn]
195:         end
196:       end

[Source]

    # File lib/active_ldap/adapter/base.rb, line 43
43:       def rebind(options={})
44:         unbind(options) if bound?
45:         connect(options)
46:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 86
 86:       def schema(options={})
 87:         @schema ||= operation(options) do
 88:           base = options[:base]
 89:           attrs = options[:attributes]
 90: 
 91:           attrs ||= [
 92:             'objectClasses',
 93:             'attributeTypes',
 94:             'matchingRules',
 95:             'matchingRuleUse',
 96:             'dITStructureRules',
 97:             'dITContentRules',
 98:             'nameForms',
 99:             'ldapSyntaxes',
100:             #'extendedAttributeInfo', # if we need RANGE-LOWER/UPPER.
101:           ]
102:           base ||= root_dse_values('subschemaSubentry', options)[0]
103:           base ||= 'cn=schema'
104:           dn, attributes = search(:base => base,
105:                                   :scope => :base,
106:                                   :filter => '(objectClass=subschema)',
107:                                   :attributes => attrs).first
108:           Schema.new(attributes)
109:         end
110:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 120
120:       def search(options={})
121:         filter = parse_filter(options[:filter]) || 'objectClass=*'
122:         attrs = options[:attributes] || []
123:         scope = ensure_scope(options[:scope] || @scope)
124:         base = options[:base]
125:         limit = options[:limit] || 0
126:         limit = nil if limit <= 0
127: 
128:         attrs = attrs.to_a # just in case
129: 
130:         values = []
131:         callback = Proc.new do |value, block|
132:           value = block.call(value) if block
133:           values << value
134:         end
135: 
136:         begin
137:           operation(options) do
138:             yield(base, scope, filter, attrs, limit, callback)
139:           end
140:         rescue LdapError
141:           # Do nothing on failure
142:           @logger.info do
143:             args = [$!.class, $!.message, filter, attrs.inspect]
144:             _("Ignore error %s(%s): filter %s: attributes: %s") % args
145:           end
146:         end
147: 
148:         values
149:       end

Private Instance methods

[Source]

     # File lib/active_ldap/adapter/base.rb, line 461
461:       def assert_filter_logical_operator(operator)
462:         return if operator.nil?
463:         unless filter_logical_operator?(operator)
464:           raise ArgumentError,
465:                 _("invalid logical operator: %s: available operators: %s") %
466:                   [operator.inspect, LOGICAL_OPERATORS.inspect]
467:         end
468:       end

Determine if we have exceed the retry limit or not. True is reconnecting is allowed - False if not.

[Source]

     # File lib/active_ldap/adapter/base.rb, line 517
517:       def can_reconnect?(options={})
518:         retry_limit = options[:retry_limit] || @retry_limit
519:         reconnect_attempts = options[:reconnect_attempts] || 0
520: 
521:         retry_limit < 0 or reconnect_attempts < (retry_limit - 1)
522:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 440
440:       def collection?(object)
441:         !object.is_a?(String) and object.respond_to?(:each)
442:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 380
380:       def construct_component(key, value, operator=nil)
381:         value, options = extract_filter_value_options(value)
382:         if collection?(value)
383:           values = []
384:           value.each do |val|
385:             if collection?(val)
386:               values.concat(val.collect {|v| [key, v]})
387:             else
388:               values << [key, val]
389:             end
390:           end
391:           values[0] = values[0][1] if filter_logical_operator?(values[0][1])
392:           parse_filter(values, operator)
393:         else
394:           [
395:            "(",
396:            escape_filter_key(key),
397:            options[:operator] || "=",
398:            escape_filter_value(value, options),
399:            ")"
400:           ].join
401:         end
402:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 425
425:       def construct_filter(components, operator=nil)
426:         operator = normalize_filter_logical_operator(operator)
427:         components = components.compact
428:         case components.size
429:         when 0
430:           nil
431:         when 1
432:           filter = components[0]
433:           filter = "(!#{filter})" if operator == :not
434:           filter
435:         else
436:           "(#{operator == :and ? '&' : '|'}#{components.join})"
437:         end
438:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 404
404:       def escape_filter_key(key)
405:         escape_filter_value(key.to_s)
406:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 408
408:       def escape_filter_value(value, options={})
409:         case value
410:         when Numeric, DN
411:           value = value.to_s
412:         when Time
413:           value = Schema::GeneralizedTime.new.normalize_value(value)
414:         end
415:         value.gsub(/(?:[()\\\0]|\*\*?)/) do |s|
416:           if s == "*"
417:             s
418:           else
419:             s = "*" if s == "**"
420:             "\\%02X" % s[0]
421:           end
422:         end
423:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 365
365:       def extract_filter_value_options(value)
366:         options = {}
367:         if value.is_a?(Array)
368:           case value[0]
369:           when Hash
370:             options = value[0]
371:             value = value[1]
372:           when "=", "~=", "<=", "=>"
373:             options[:operator] = value[1]
374:             value = value[1]
375:           end
376:         end
377:         [value, options]
378:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 445
445:       def filter_logical_operator?(operator)
446:         LOGICAL_OPERATORS.include?(operator)
447:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 222
222:       def need_credential_sasl_mechanism?(mechanism)
223:         not %(GSSAPI EXTERNAL ANONYMOUS).include?(mechanism)
224:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 355
355:       def normalize_array_filter(filter, operator=nil)
356:         filter_operator, *components = filter
357:         if filter_logical_operator?(filter_operator)
358:           operator = filter_operator
359:         else
360:           components.unshift(filter_operator)
361:         end
362:         [operator, components]
363:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 449
449:       def normalize_filter_logical_operator(operator)
450:         assert_filter_logical_operator(operator)
451:         case (operator || :and)
452:         when :and, :&
453:           :and
454:         when :or, :|
455:           :or
456:         else
457:           :not
458:         end
459:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 202
202:       def operation(options)
203:         retried = false
204:         begin
205:           reconnect_if_need
206:           try_reconnect = !options.has_key?(:try_reconnect) ||
207:                              options[:try_reconnect]
208:           with_timeout(try_reconnect, options) do
209:             yield
210:           end
211:         rescue Errno::EPIPE
212:           if retried or !try_reconnect
213:             raise
214:           else
215:             retried = true
216:             @disconnected = true
217:             retry
218:           end
219:         end
220:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 305
305:       def parse_filter(filter, operator=nil)
306:         return nil if filter.nil?
307:         if !filter.is_a?(String) and !filter.respond_to?(:collect)
308:           filter = filter.to_s
309:         end
310: 
311:         case filter
312:         when String
313:           parse_filter_string(filter)
314:         when Hash
315:           components = filter.sort_by {|k, v| k.to_s}.collect do |key, value|
316:             construct_component(key, value, operator)
317:           end
318:           construct_filter(components, operator)
319:         else
320:           operator, components = normalize_array_filter(filter, operator)
321: 
322:           components = components.collect do |component|
323:             if component.is_a?(Array) and component.size == 2
324:               key, value = component
325:               if filter_logical_operator?(key)
326:                 parse_filter(component)
327:               elsif value.is_a?(Hash)
328:                 parse_filter(value, key)
329:               else
330:                 construct_component(key, value, operator)
331:               end
332:             elsif component.is_a?(Symbol)
333:               assert_filter_logical_operator(component)
334:               nil
335:             else
336:               parse_filter(component, operator)
337:             end
338:           end
339:           construct_filter(components, operator)
340:         end
341:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 343
343:       def parse_filter_string(filter)
344:         if /\A\s*\z/.match(filter)
345:           nil
346:         else
347:           if filter[0, 1] == "("
348:             filter
349:           else
350:             "(#{filter})"
351:           end
352:         end
353:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 226
226:       def password(bind_dn, options={})
227:         passwd = options[:password] || @password
228:         return passwd if passwd
229: 
230:         password_block = options[:password_block] || @password_block
231:         # TODO: Give a warning to reconnect users with password clearing
232:         # Get the passphrase for the first time, or anew if we aren't storing
233:         if password_block.respond_to?(:call)
234:           passwd = password_block.call(bind_dn)
235:         else
236:           @logger.error {_('password_block not nil or Proc object. Ignoring.')}
237:           return nil
238:         end
239: 
240:         # Store the password for quick reference later
241:         if options.has_key?(:store_password)
242:           store_password = options[:store_password]
243:         else
244:           store_password = @store_password
245:         end
246:         @password = store_password ? passwd : nil
247: 
248:         passwd
249:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 199
199:       def prepare_connection(options)
200:       end

Attempts to reconnect up to the number of times allowed If forced, try once then fail with ConnectionError if not connected.

[Source]

     # File lib/active_ldap/adapter/base.rb, line 472
472:       def reconnect(options={})
473:         options = options.dup
474:         force = options[:force]
475:         retry_limit = options[:retry_limit] || @retry_limit
476:         retry_wait = options[:retry_wait] || @retry_wait
477:         options[:reconnect_attempts] ||= 0
478: 
479:         loop do
480:           unless can_reconnect?(options)
481:             raise ConnectionError,
482:                   _('Giving up trying to reconnect to LDAP server.')
483:           end
484: 
485:           @logger.debug {_('Attempting to reconnect')}
486:           disconnect!
487: 
488:           # Reset the attempts if this was forced.
489:           options[:reconnect_attempts] = 0 if force
490:           options[:reconnect_attempts] += 1 if retry_limit >= 0
491:           begin
492:             connect(options)
493:             break
494:           rescue => detail
495:             @logger.error do
496:               _("Reconnect to server failed: %s\n" \
497:                 "Reconnect to server failed backtrace:\n" \
498:                 "%s") % [detail.exception, detail.backtrace.join("\n")]
499:             end
500:             # Do not loop if forced
501:             raise ConnectionError, detail.message if force
502:           end
503: 
504:           # Sleep before looping
505:           sleep retry_wait
506:         end
507: 
508:         true
509:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 511
511:       def reconnect_if_need(options={})
512:         reconnect(options) if !connecting? and can_reconnect?(options)
513:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 524
524:       def root_dse_values(key, options={})
525:         dse = root_dse([key], options)[0]
526:         return [] if dse.nil?
527:         dse[key] || dse[key.downcase] || []
528:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 262
262:       def sasl_bind(bind_dn, options={})
263:         return false unless bind_dn
264: 
265:         # Get all SASL mechanisms
266:         mechanisms = operation(options) do
267:           root_dse_values("supportedSASLMechanisms")
268:         end
269: 
270:         if options.has_key?(:sasl_quiet)
271:           sasl_quiet = options[:sasl_quiet]
272:         else
273:           sasl_quiet = @sasl_quiet
274:         end
275: 
276:         sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
277:         sasl_mechanisms.each do |mechanism|
278:           next unless mechanisms.include?(mechanism)
279:           operation(options) do
280:             yield(bind_dn, mechanism, sasl_quiet)
281:             return true if bound?
282:           end
283:         end
284:         false
285:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 287
287:       def simple_bind(bind_dn, options={})
288:         return false unless bind_dn
289: 
290:         passwd = password(bind_dn, options)
291:         return false unless passwd
292: 
293:         begin
294:           operation(options) do
295:             yield(bind_dn, passwd)
296:             bound?
297:           end
298:         rescue LdapError::InvalidDnSyntax
299:           raise DistinguishedNameInvalid.new(bind_dn)
300:         rescue LdapError::InvalidCredentials
301:           false
302:         end
303:       end

[Source]

     # File lib/active_ldap/adapter/base.rb, line 251
251:       def with_timeout(try_reconnect=true, options={}, &block)
252:         begin
253:           Timeout.alarm(@timeout, &block)
254:         rescue Timeout::Error => e
255:           @logger.error {_('Requested action timed out.')}
256:           retry if try_reconnect and @retry_on_timeout and reconnect(options)
257:           @logger.error {e.message}
258:           raise TimeoutError, e.message
259:         end
260:       end

[Validate]