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,
:excluded_classes, :sort_by, :order]
@@colorize_logging =
true
@@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:



495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/active_ldap/base.rb', line 495

def initialize(attributes=nil)
  init_base
  @new_entry = true
  initial_classes = required_classes | recommended_classes
  case attributes
  when nil
    self.classes = initial_classes
  when String, Array, DN
    self.classes = initial_classes
    self.dn = attributes
  when Hash
    classes, attributes = extract_object_class(attributes)
    self.classes = 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.delete(dn_attribute)
    self.attributes = normalized_attributes
  else
    format = _("'%s' must be either nil, DN value as ActiveLdap::DN, " \
               "String or Array or attributes as Hash")
    raise ArgumentError, format % attributes.inspect
  end
  yield self if block_given?
  assert_dn_attribute
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.


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
678
679
680
681
# File 'lib/active_ldap/base.rb', line 650

def method_missing(name, *args, &block)
  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)[1]
      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.



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

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

.base=(value) ⇒ Object



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

def base=(value)
  self.base_without_parsed_cache_clear = value
  @parsed_base = nil
end

.base_classObject



410
411
412
413
414
415
416
# File 'lib/active_ldap/base.rb', line 410

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

.base_inheritableObject



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

alias_method :base_inheritable, :base

.base_without_parsed_cache_clear=Object



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

alias_method :base_without_parsed_cache_clear=, :base=

.class_local_attr_accessor(search_ancestors, *syms) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/active_ldap/base.rb', line 260

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



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

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

.default_search_attributeObject



418
419
420
# File 'lib/active_ldap/base.rb', line 418

def default_search_attribute
  dn_attribute
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 logger object (Logger, Log4r::Logger and s on) :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



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

def establish_connection(config=nil)
  super
  ensure_logger
  nil
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


351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/active_ldap/base.rb', line 351

def ldap_mapping(options={})
  options = options.symbolize_keys
  validate_ldap_mapping_options(options)

  self.dn_attribute = options[:dn_attribute] || default_dn_attribute
  self.prefix = options[:prefix] || default_prefix
  self.scope = options[:scope]
  self.required_classes = options[:classes]
  self.recommended_classes = options[:recommended_classes]
  self.excluded_classes = options[:excluded_classes]
  self.sort_by = options[:sort_by]
  self.order = options[:order]

  public_class_method :new
end

.parsed_baseObject



393
394
395
# File 'lib/active_ldap/base.rb', line 393

def parsed_base
  @parsed_base ||= DN.parse(base)
end

.scope=(scope) ⇒ Object



398
399
400
401
# File 'lib/active_ldap/base.rb', line 398

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

.scope_without_validation=Object



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

alias_method :scope_without_validation=, :scope=

.validate_scope(scope) ⇒ Object

Raises:



403
404
405
406
407
408
# File 'lib/active_ldap/base.rb', line 403

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.



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

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



799
800
801
802
803
804
805
# File 'lib/active_ldap/base.rb', line 799

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

#[]=(name, value) ⇒ Object



807
808
809
# File 'lib/active_ldap/base.rb', line 807

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



558
559
560
# File 'lib/active_ldap/base.rb', line 558

def attribute_names(normalize=false)
  entry_attribute.names(normalize)
end

#attribute_present?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

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



722
723
724
# File 'lib/active_ldap/base.rb', line 722

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

#attributes=(new_attributes) ⇒ 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.



732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
# File 'lib/active_ldap/base.rb', line 732

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

#baseObject



858
859
860
# File 'lib/active_ldap/base.rb', line 858

def base
  [@base, base_of_class].compact.join(",")
end

#base=(object_local_base) ⇒ Object



863
864
865
866
# File 'lib/active_ldap/base.rb', line 863

def base=(object_local_base)
  @dn = nil
  @base = object_local_base
end

#base_of_classObject



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

alias_method :base_of_class, :base

#bind(config_or_password = {}, config_or_ignore = nil, &block) ⇒ Object



817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
# File 'lib/active_ldap/base.rb', line 817

def bind(config_or_password={}, config_or_ignore=nil, &block)
  if config_or_password.is_a?(String)
    config = (config_or_ignore || {}).merge(:password => config_or_password)
  else
    config = config_or_password
  end
  config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
  config[:password_block] ||= block if block_given?
  establish_connection(config)

  before_connection = @connection
  begin
    @connection = nil
    connection.connect
    @connection = connection
    clear_connection_based_cache
    clear_association_cache
  rescue ActiveLdap::Error
    remove_connection
    @connection = before_connection
    raise
  end
  true
end

#clear_connection_based_cacheObject



842
843
844
845
846
# File 'lib/active_ldap/base.rb', line 842

def clear_connection_based_cache
  @schema = nil
  @local_entry_attribute = nil
  clear_object_class_based_cache
end

#clear_object_class_based_cacheObject



848
849
850
851
# File 'lib/active_ldap/base.rb', line 848

def clear_object_class_based_cache
  @entry_attribute = nil
  @real_names = {}
end

#default_search_attributeObject



608
609
610
# File 'lib/active_ldap/base.rb', line 608

def default_search_attribute
  self.class.default_search_attribute
end

#delete(options = {}) ⇒ Object



624
625
626
# File 'lib/active_ldap/base.rb', line 624

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

#destroyObject

destroy

Delete this entry from LDAP



615
616
617
618
619
620
621
622
# File 'lib/active_ldap/base.rb', line 615

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



585
586
587
# File 'lib/active_ldap/base.rb', line 585

def dn
  @dn ||= compute_dn
end

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



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

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

#dn_attributeObject



604
605
606
# File 'lib/active_ldap/base.rb', line 604

def dn_attribute
  @dn_attribute || dn_attribute_of_class
end

#dn_attribute_of_classObject



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

alias_method(:dn_attribute_of_class, :dn_attribute)

#eachObject



811
812
813
814
815
# File 'lib/active_ldap/base.rb', line 811

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

#eql?(comparison_object) ⇒ Boolean

Delegates to ==

Returns:

  • (Boolean)


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

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

#exist?Boolean Also known as: exists?

exist?

Return whether the entry exists in LDAP or not

Returns:

  • (Boolean)


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

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") ]


542
543
544
# File 'lib/active_ldap/base.rb', line 542

def hash
  dn.hash
end

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

Returns:

  • (Boolean)


776
777
778
779
# File 'lib/active_ldap/base.rb', line 776

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

#idObject



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

def id
  get_attribute(dn_attribute)
end

#inspectObject



879
880
881
882
883
# File 'lib/active_ldap/base.rb', line 879

def inspect
  abbreviate_instance_variables do
    super
  end
end

#mayObject



546
547
548
# File 'lib/active_ldap/base.rb', line 546

def may
  entry_attribute.may
end

#methods(inherited_too = true) ⇒ Object

Add available attributes to the methods



684
685
686
687
688
689
690
# File 'lib/active_ldap/base.rb', line 684

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

#mustObject



550
551
552
# File 'lib/active_ldap/base.rb', line 550

def must
  entry_attribute.must
end

#new_entry?Boolean

new_entry?

Return whether the entry is new entry in LDAP or not

Returns:

  • (Boolean)


578
579
580
# File 'lib/active_ldap/base.rb', line 578

def new_entry?
  @new_entry
end

#pretty_print(q) ⇒ Object



885
886
887
888
889
# File 'lib/active_ldap/base.rb', line 885

def pretty_print(q)
  abbreviate_instance_variables do
    q.pp_object(self)
  end
end

#reloadObject



782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
# File 'lib/active_ldap/base.rb', line 782

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)
  self.classes = classes
  self.attributes = attributes
  @new_entry = false
  self
end

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

Returns:

  • (Boolean)


693
694
695
696
697
698
699
700
# File 'lib/active_ldap/base.rb', line 693

def respond_to?(name, include_priv=false)
  return true if super

  name = name.to_s
  return true if have_attribute?(name)
  return false if /(?:=|\?|_before_type_cast)$/ !~ name
  have_attribute?($PREMATCH)
end

#respond_to_without_attributes?Object



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

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



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

def save
  create_or_update
end

#save!Object



637
638
639
640
641
# File 'lib/active_ldap/base.rb', line 637

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

#schemaObject



853
854
855
# File 'lib/active_ldap/base.rb', line 853

def schema
  @schema ||= super
end

#scopeObject



869
870
871
# File 'lib/active_ldap/base.rb', line 869

def scope
  @scope || scope_of_class
end

#scope=(scope) ⇒ Object



874
875
876
877
# File 'lib/active_ldap/base.rb', line 874

def scope=(scope)
  self.class.validate_scope(scope)
  @scope = scope
end

#scope_of_classObject



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

alias_method :scope_of_class, :scope

#to_ldifObject



749
750
751
# File 'lib/active_ldap/base.rb', line 749

def to_ldif
  super(dn, @data)
end

#to_paramObject



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

def to_param
  id
end

#to_xml(options = {}) ⇒ Object



753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
# File 'lib/active_ldap/base.rb', line 753

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



703
704
705
706
# File 'lib/active_ldap/base.rb', line 703

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.



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

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

#update_attributes!(attrs) ⇒ Object



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

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