Class: ActiveLdap::Base

Inherits:
Object
  • Object
show all
Includes:
GetTextSupport, Enumerable, Reloadable::Deprecated, Reloadable::Subclasses
Defined in:
lib/active_ldap/base.rb

Overview

Base

Base is the primary class which contains all of the core ActiveLdap functionality. It is meant to only ever be subclassed by extension classes.

Constant Summary collapse

VALID_LDAP_MAPPING_OPTIONS =
[:dn_attribute, :prefix, :scope,
:classes, :recommended_classes]
@@configurations =
{}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from GetTextSupport

included

Constructor Details

#initialize(attributes = nil) {|_self| ... } ⇒ Base

new

Creates a new instance of Base initializing all class and all initialization. Defines local defaults. See examples If multiple values exist for dn_attribute, the first one put here will be authoritative

Yields:

  • (_self)

Yield Parameters:



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/active_ldap/base.rb', line 479

def initialize(attributes=nil)
  init_base
  @new_entry = true
  initial_classes = required_classes | recommended_classes
  if attributes.nil?
    apply_object_class(initial_classes)
  elsif attributes.is_a?(String) or attributes.is_a?(Array)
    apply_object_class(initial_classes)
    self.dn = attributes
  elsif attributes.is_a?(Hash)
    classes, attributes = extract_object_class(attributes)
    apply_object_class(classes | initial_classes)
    normalized_attributes = {}
    attributes.each do |key, value|
      real_key = to_real_attribute_name(key) || key
      normalized_attributes[real_key] = value
    end
    self.dn = normalized_attributes[dn_attribute]
    self.attributes = normalized_attributes
  else
    message = _("'%s' must be either nil, DN value as String or Array " \
                "or attributes as Hash") % attributes.inspect
    raise ArgumentError, message
  end
  yield self if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

method_missing

If a given method matches an attribute or an attribute alias then call the appropriate method. TODO: Determine if it would be better to define each allowed method

using class_eval instead of using method_missing.  This would
give tab completion in irb.


644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
# File 'lib/active_ldap/base.rb', line 644

def method_missing(name, *args, &block)
  ensure_apply_object_class

  key = name.to_s
  case key
  when /=$/
    real_key = $PREMATCH
    if have_attribute?(real_key, ['objectClass'])
      if args.size != 1
        raise ArgumentError,
                _("wrong number of arguments (%d for 1)") % args.size
      end
      return set_attribute(real_key, *args, &block)
    end
  when /(?:(_before_type_cast)|(\?))?$/
    real_key = $PREMATCH
    before_type_cast = !$1.nil?
    query = !$2.nil?
    if have_attribute?(real_key, ['objectClass'])
      if args.size > 1
        raise ArgumentError,
          _("wrong number of arguments (%d for 1)") % args.size
      end
      if before_type_cast
        return get_attribute_before_type_cast(real_key, *args)
      elsif query
        return get_attribute_as_query(real_key, *args)
      else
        return get_attribute(real_key, *args)
      end
    end
  end
  super
end

Class Method Details

.baseObject

Base.base

This method when included into Base provides an inheritable, overwritable configuration setting

This should be a string with the base of the ldap server such as ‘dc=example,dc=com’, and it should be overwritten by including configuration.rb into this class. When subclassing, the specified prefix will be concatenated.



305
306
307
308
309
310
311
312
# File 'lib/active_ldap/base.rb', line 305

def base
  _base = base_inheritable
  _base = configuration[:base] if _base.nil? and configuration
  _base ||= base_inheritable(true)
  [prefix, _base].find_all do |component|
    component and !component.empty?
  end.join(",")
end

.base_classObject



327
328
329
330
331
332
333
# File 'lib/active_ldap/base.rb', line 327

def base_class
  if self == Base or superclass == Base
    self
  else
    superclass.base_class
  end
end

.base_inheritableObject



294
# File 'lib/active_ldap/base.rb', line 294

alias_method :base_inheritable, :base

.class_local_attr_accessor(search_ancestors, *syms) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/active_ldap/base.rb', line 184

def self.class_local_attr_accessor(search_ancestors, *syms)
  syms.flatten.each do |sym|
    class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      def self.#{sym}(search_superclasses=#{search_ancestors})
        @#{sym} ||= nil
        return @#{sym} if @#{sym}
        if search_superclasses
          target = superclass
          value = nil
          loop do
            break nil unless target.respond_to?(:#{sym})
            value = target.#{sym}
            break if value
            target = target.superclass
          end
          value
        else
          nil
        end
      end
      def #{sym}; self.class.#{sym}; end
      def self.#{sym}=(value); @#{sym} = value; end
      def #{sym}=(value); self.class.#{sym} = value; end
    EOS
  end
end

.create(attributes = nil, &block) ⇒ Object



260
261
262
263
264
265
266
267
268
# File 'lib/active_ldap/base.rb', line 260

def create(attributes=nil, &block)
  if attributes.is_a?(Array)
    attributes.collect {|attrs| create(attrs, &block)}
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

.establish_connection(config = nil) ⇒ Object

Connect and bind to LDAP creating a class variable for use by all ActiveLdap objects.

config

config must be a hash that may contain any of the following fields: :password_block, :logger, :host, :port, :base, :bind_dn, :try_sasl, :allow_anonymous :bind_dn specifies the DN to bind with. :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 sets the LDAP server hostname :port sets the LDAP server port :base overwrites Base.base - this affects EVERYTHING :try_sasl indicates that a SASL bind should be attempted when binding

to the server (default: false)

:sasl_mechanisms is an array of SASL mechanism to try

(default: ["GSSAPI", "CRAM-MD5", "EXTERNAL"])

:allow_anonymous indicates that a true anonymous bind is allowed when

trying to bind to the server (default: true)

:retries - indicates the number of attempts to reconnect that will be

undertaken when a stale connection occurs. -1 means infinite.

:sasl_quiet - if true, sets @sasl_quiet on the Ruby/LDAP connection :method - whether to use :ssl, :tls, or :plain (unencrypted) :retry_wait - seconds to wait before retrying a connection :scope - dictates how to find objects. ONELEVEL by default to

avoid dn_attr collisions across OUs. Think before changing.

:timeout - time in seconds - defaults to disabled. This CAN interrupt

search() requests. Be warned.

:retry_on_timeout - whether to reconnect when timeouts occur. Defaults

to true

See lib/configuration.rb for defaults for each option



252
253
254
255
256
257
258
# File 'lib/active_ldap/base.rb', line 252

def establish_connection(config=nil)
  super
  ensure_logger
  connection.connect
  # Make irb users happy with a 'true'
  true
end

.human_attribute_description(attribute_or_name) ⇒ Object



357
358
359
360
361
# File 'lib/active_ldap/base.rb', line 357

def human_attribute_description(attribute_or_name)
  msgid = human_attribute_description_msgid(attribute_or_name)
  return nil if msgid.nil?
  s_(msgid)
end

.human_attribute_description_msgid(attribute_or_name) ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
# File 'lib/active_ldap/base.rb', line 363

def human_attribute_description_msgid(attribute_or_name)
  if attribute_or_name.is_a?(Schema::Attribute)
    attribute = attribute_or_name
  else
    attribute = schema.attribute(attribute_or_name)
    return nil if attribute.nil?
  end
  description = attribute.description
  return nil if description.nil?
  "LDAP|Description|Attribute|#{attribute.name}|#{description}"
end

.human_attribute_name(attribute_or_name) ⇒ Object



335
336
337
338
339
# File 'lib/active_ldap/base.rb', line 335

def human_attribute_name(attribute_or_name)
  msgid = human_attribute_name_msgid(attribute_or_name)
  msgid ||= human_attribute_name_with_gettext(attribute_or_name)
  s_(msgid)
end

.human_attribute_name_msgid(attribute_or_name) ⇒ Object



341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/active_ldap/base.rb', line 341

def human_attribute_name_msgid(attribute_or_name)
  if attribute_or_name.is_a?(Schema::Attribute)
    name = attribute_or_name.name
  else
    attribute = schema.attribute(attribute_or_name)
    return nil if attribute.id.nil?
    if attribute.name == attribute_or_name or
        attribute.aliases.include?(attribute_or_name)
      name = attribute_or_name
    else
      return nil
    end
  end
  "LDAP|Attribute|#{name}"
end

.human_object_class_description(object_class_or_name) ⇒ Object



388
389
390
391
392
# File 'lib/active_ldap/base.rb', line 388

def human_object_class_description(object_class_or_name)
  msgid = human_object_class_description_msgid(object_class_or_name)
  return nil if msgid.nil?
  s_(msgid)
end

.human_object_class_description_msgid(object_class_or_name) ⇒ Object



394
395
396
397
398
399
400
401
402
403
404
# File 'lib/active_ldap/base.rb', line 394

def human_object_class_description_msgid(object_class_or_name)
  if object_class_or_name.is_a?(Schema::ObjectClass)
    object_class = object_class_or_name
  else
    object_class = schema.object_class(object_class_or_name)
    return nil if object_class.nil?
  end
  description = object_class.description
  return nil if description.nil?
  "LDAP|Description|ObjectClass|#{object_class.name}|#{description}"
end

.human_object_class_name(object_class_or_name) ⇒ Object



375
376
377
# File 'lib/active_ldap/base.rb', line 375

def human_object_class_name(object_class_or_name)
  s_(human_object_class_name_msgid(object_class_or_name))
end

.human_object_class_name_msgid(object_class_or_name) ⇒ Object



379
380
381
382
383
384
385
386
# File 'lib/active_ldap/base.rb', line 379

def human_object_class_name_msgid(object_class_or_name)
  if object_class_or_name.is_a?(Schema::ObjectClass)
    name = object_class_or_name.name
  else
    name = object_class_or_name
  end
  "LDAP|ObjectClass|#{name}"
end

.ldap_mapping(options = {}) ⇒ Object

This class function is used to setup all mappings between the subclass and ldap for use in activeldap

Example:

ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People',
             :classes => ['top', 'posixAccount'],
             :scope => :sub


277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/active_ldap/base.rb', line 277

def ldap_mapping(options={})
  validate_ldap_mapping_options(options)
  dn_attribute = options[:dn_attribute] || default_dn_attribute
  prefix = options[:prefix] || default_prefix
  classes = options[:classes]
  recommended_classes = options[:recommended_classes]
  scope = options[:scope]

  self.dn_attribute = dn_attribute
  self.prefix = prefix
  self.scope = scope
  self.required_classes = classes
  self.recommended_classes = recommended_classes

  public_class_method :new
end

.scope=(scope) ⇒ Object



315
316
317
318
# File 'lib/active_ldap/base.rb', line 315

def scope=(scope)
  validate_scope(scope)
  self.scope_without_validation = scope
end

.scope_without_validation=Object



314
# File 'lib/active_ldap/base.rb', line 314

alias_method :scope_without_validation=, :scope=

.validate_scope(scope) ⇒ Object

Raises:



320
321
322
323
324
325
# File 'lib/active_ldap/base.rb', line 320

def validate_scope(scope)
  scope = scope.to_sym if scope.is_a?(String)
  return if scope.nil? or scope.is_a?(Symbol)
  raise ConfigurationError,
          _("scope '%s' must be a Symbol") % scope.inspect
end

Instance Method Details

#==(comparison_object) ⇒ Object

Returns true if the comparison_object is the same object, or is of the same type and has the same dn.



508
509
510
511
512
513
# File 'lib/active_ldap/base.rb', line 508

def ==(comparison_object)
  comparison_object.equal?(self) or
    (comparison_object.instance_of?(self.class) and
     comparison_object.dn == dn and
     !comparison_object.new_entry?)
end

#[](name, force_array = false) ⇒ Object



792
793
794
795
796
797
798
# File 'lib/active_ldap/base.rb', line 792

def [](name, force_array=false)
  if name == "dn"
    array_of(dn, force_array)
  else
    get_attribute(name, force_array)
  end
end

#[]=(name, value) ⇒ Object



800
801
802
# File 'lib/active_ldap/base.rb', line 800

def []=(name, value)
  set_attribute(name, value)
end

#attribute_names(normalize = false) ⇒ Object

attributes

Return attribute methods so that a program can determine available attributes dynamically without schema awareness



542
543
544
545
546
547
548
549
550
551
552
# File 'lib/active_ldap/base.rb', line 542

def attribute_names(normalize=false)
  ensure_apply_object_class
  names = @attr_methods.keys
  if normalize
    names.collect do |name|
      to_real_attribute_name(name)
    end.uniq
  else
    names
  end
end

#attribute_present?(name) ⇒ Boolean

Returns:

  • (Boolean)


554
555
556
557
# File 'lib/active_ldap/base.rb', line 554

def attribute_present?(name)
  values = get_attribute(name, true)
  !values.empty? or values.any? {|x| not (x and x.empty?)}
end

#attributesObject

This returns the key value pairs in @data with all values cloned



717
718
719
# File 'lib/active_ldap/base.rb', line 717

def attributes
  Marshal.load(Marshal.dump(@data))
end

#attributes=(hash_or_assoc) ⇒ Object

This allows a bulk update to the attributes of a record without forcing an immediate save or validation.

It is unwise to attempt objectClass updates this way. Also be sure to only pass in key-value pairs of your choosing. Do not let URL/form hackers supply the keys.



727
728
729
730
731
732
733
734
735
736
737
738
739
740
# File 'lib/active_ldap/base.rb', line 727

def attributes=(hash_or_assoc)
  _schema = nil
  targets = remove_attributes_protected_from_mass_assignment(hash_or_assoc)
  targets.each do |key, value|
    setter = "#{key}="
    unless respond_to?(setter)
      _schema ||= schema
      attribute = _schema.attribute(key)
      next if attribute.id.nil?
      define_attribute_methods(attribute)
    end
    send(setter, value)
  end
end

#delete(options = {}) ⇒ Object



618
619
620
# File 'lib/active_ldap/base.rb', line 618

def delete(options={})
  super(dn, options)
end

#destroyObject

destroy

Delete this entry from LDAP



609
610
611
612
613
614
615
616
# File 'lib/active_ldap/base.rb', line 609

def destroy
  begin
    self.class.delete(dn)
    @new_entry = true
  rescue Error
    raise DeleteError.new(_("Failed to delete LDAP entry: %s") % dn)
  end
end

#dnObject

dn

Return the authoritative dn



577
578
579
580
581
582
583
584
585
586
# File 'lib/active_ldap/base.rb', line 577

def dn
  dn_value = id
  if dn_value.nil?
    raise DistinguishedNameNotSetError.new,
            _("%s's DN attribute (%s) isn't set") % [self, dn_attribute]
  end
  _base = base
  _base = nil if _base.empty?
  ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
end

#dn=(value) ⇒ Object Also known as: id=



596
597
598
# File 'lib/active_ldap/base.rb', line 596

def dn=(value)
  set_attribute(dn_attribute, value)
end

#dn_attributeObject



602
603
604
# File 'lib/active_ldap/base.rb', line 602

def dn_attribute
  @dn_attribute || dn_attribute_of_class
end

#dn_attribute_of_classObject



601
# File 'lib/active_ldap/base.rb', line 601

alias_method(:dn_attribute_of_class, :dn_attribute)

#eachObject



804
805
806
807
808
# File 'lib/active_ldap/base.rb', line 804

def each
  @data.each do |key, values|
    yield(key.dup, values.dup)
  end
end

#eql?(comparison_object) ⇒ Boolean

Delegates to ==

Returns:

  • (Boolean)


516
517
518
# File 'lib/active_ldap/base.rb', line 516

def eql?(comparison_object)
  self == (comparison_object)
end

#establish_connection(config = {}) ⇒ Object



810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
# File 'lib/active_ldap/base.rb', line 810

def establish_connection(config={})
  if config.is_a?(Hash)
    config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
  end
  super(config)
  before_connection = @connection
  begin
    @connection = nil
    connection.connect
    @connection = connection
    @schema = nil
    clear_association_cache
  rescue ActiveLdap::Error
    remove_connection
    @connection = before_connection
    raise
  end
  true
end

#exist?Boolean Also known as: exists?

exist?

Return whether the entry exists in LDAP or not

Returns:

  • (Boolean)


562
563
564
# File 'lib/active_ldap/base.rb', line 562

def exist?
  self.class.exists?(dn)
end

#hashObject

Delegates to id in order to allow two records of the same type and id to work with something like:

[ User.find("a"), User.find("b"), User.find("c") ] &
  [ User.find("a"), User.find("d") ] # => [ User.find("a") ]


524
525
526
# File 'lib/active_ldap/base.rb', line 524

def hash
  dn.hash
end

#have_attribute?(name, except = []) ⇒ Boolean Also known as: has_attribute?

Returns:

  • (Boolean)


769
770
771
772
# File 'lib/active_ldap/base.rb', line 769

def have_attribute?(name, except=[])
  real_name = to_real_attribute_name(name)
  real_name and !except.include?(real_name)
end

#idObject



588
589
590
# File 'lib/active_ldap/base.rb', line 588

def id
  get_attribute(dn_attribute)
end

#inspectObject



834
835
836
837
838
839
840
841
842
843
844
845
846
# File 'lib/active_ldap/base.rb', line 834

def inspect
  schema, @schema = @schema, nil
  must, may = @must, @may
  object_classes = @object_classes
  @must, @may = @must.collect(&:name), @may.collect(&:name)
  @object_classes = @object_classes.collect(&:name)
  super
ensure
  @schema = schema
  @must = must
  @may = may
  @object_classes = object_classes
end

#mayObject



528
529
530
531
# File 'lib/active_ldap/base.rb', line 528

def may
  ensure_apply_object_class
  @may
end

#methods(inherited_too = true) ⇒ Object

Add available attributes to the methods



680
681
682
683
684
685
686
687
# File 'lib/active_ldap/base.rb', line 680

def methods(inherited_too=true)
  ensure_apply_object_class
  target_names = @attr_methods.keys + @attr_aliases.keys
  target_names -= ['objectClass', Inflector.underscore('objectClass')]
  super + target_names.uniq.collect do |x|
    [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
  end.flatten
end

#mustObject



533
534
535
536
# File 'lib/active_ldap/base.rb', line 533

def must
  ensure_apply_object_class
  @must
end

#new_entry?Boolean

new_entry?

Return whether the entry is new entry in LDAP or not

Returns:

  • (Boolean)


570
571
572
# File 'lib/active_ldap/base.rb', line 570

def new_entry?
  @new_entry
end

#reloadObject



775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
# File 'lib/active_ldap/base.rb', line 775

def reload
  clear_association_cache
  _, attributes = search(:value => id).find do |_dn, _attributes|
    dn == _dn
  end
  if attributes.nil?
    raise EntryNotFound, _("Can't find DN '%s' to reload") % dn
  end

  @ldap_data.update(attributes)
  classes, attributes = extract_object_class(attributes)
  apply_object_class(classes)
  self.attributes = attributes
  @new_entry = false
  self
end

#respond_to?(name, include_priv = false) ⇒ Boolean

Returns:

  • (Boolean)


690
691
692
693
694
695
# File 'lib/active_ldap/base.rb', line 690

def respond_to?(name, include_priv=false)
  have_attribute?(name.to_s) or
    (/(?:=|\?|_before_type_cast)$/ =~ name.to_s and
     have_attribute?($PREMATCH)) or
    super
end

#respond_to_without_attributes?Object



689
# File 'lib/active_ldap/base.rb', line 689

alias_method :respond_to_without_attributes?, :respond_to?

#saveObject

save

Save and validate this object into LDAP either adding or replacing attributes TODO: Relative DN support



627
628
629
# File 'lib/active_ldap/base.rb', line 627

def save
  create_or_update
end

#save!Object



631
632
633
634
635
# File 'lib/active_ldap/base.rb', line 631

def save!
  unless create_or_update
    raise EntryNotSaved, _("entry %s can't be saved") % dn
  end
end

#schemaObject



830
831
832
# File 'lib/active_ldap/base.rb', line 830

def schema
  @schema ||= super
end

#to_ldifObject



742
743
744
# File 'lib/active_ldap/base.rb', line 742

def to_ldif
  super(dn, normalize_data(@data))
end

#to_paramObject



592
593
594
# File 'lib/active_ldap/base.rb', line 592

def to_param
  id
end

#to_xml(options = {}) ⇒ Object



746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
# File 'lib/active_ldap/base.rb', line 746

def to_xml(options={})
  root = options[:root] || Inflector.underscore(self.class.name)
  result = "<#{root}>\n"
  result << "  <dn>#{dn}</dn>\n"
  normalize_data(@data).sort_by {|key, values| key}.each do |key, values|
    targets = []
    values.each do |value|
      if value.is_a?(Hash)
        value.each do |option, real_value|
          targets << [real_value, " #{option}=\"true\""]
        end
      else
        targets << [value]
      end
    end
    targets.sort_by {|value, attr| value}.each do |value, attr|
      result << "  <#{key}#{attr}>#{value}</#{key}>\n"
    end
  end
  result << "</#{root}>\n"
  result
end

#update_attribute(name, value) ⇒ Object

Updates a given attribute and saves immediately



698
699
700
701
# File 'lib/active_ldap/base.rb', line 698

def update_attribute(name, value)
  send("#{name}=", value)
  save
end

#update_attributes(attrs) ⇒ Object

This performs a bulk update of attributes and immediately calls #save.



705
706
707
708
# File 'lib/active_ldap/base.rb', line 705

def update_attributes(attrs)
  self.attributes = attrs
  save
end

#update_attributes!(attrs) ⇒ Object



710
711
712
713
# File 'lib/active_ldap/base.rb', line 710

def update_attributes!(attrs)
  self.attributes = attrs
  save!
end