| Class | ActiveLdap::Base |
| In: |
lib/active_ldap/base.rb
|
| Parent: | Object |
Base is the primary class which contains all of the core ActiveLdap functionality. It is meant to only ever be subclassed by extension classes.
| VALID_LDAP_MAPPING_OPTIONS | = | [:dn_attribute, :prefix, :scope, :classes, :recommended_classes, :excluded_classes, :sort_by, :order] |
| base | -> | parsed_base |
| scope= | -> | scope_without_validation= |
| dn_attribute | -> | dn_attribute_of_class |
| respond_to? | -> | respond_to_without_attributes? |
| scope | -> | scope_of_class |
| abstract_class | [RW] |
# File lib/active_ldap/base.rb, line 507
507: def abstract_class?
508: defined?(@abstract_class) && @abstract_class
509: end
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.
# File lib/active_ldap/base.rb, line 434
434: def base
435: @base ||= compute_base
436: end
# File lib/active_ldap/base.rb, line 439
439: def base=(value)
440: self.inheritable_base = value
441: @base = nil
442: end
# File lib/active_ldap/base.rb, line 467
467: def base_class
468: if self == Base or superclass == Base
469: self
470: else
471: superclass.base_class
472: end
473: end
# File lib/active_ldap/base.rb, line 298
298: def self.class_local_attr_accessor(search_ancestors, *syms)
299: syms.flatten.each do |sym|
300: class_eval("def self.\#{sym}(search_superclasses=\#{search_ancestors})\n@\#{sym} ||= nil\nreturn @\#{sym} if @\#{sym}\nif search_superclasses\ntarget = superclass\nvalue = nil\nloop do\nbreak nil unless target.respond_to?(:\#{sym})\nvalue = target.\#{sym}\nbreak if value\ntarget = target.superclass\nend\nvalue\nelse\nnil\nend\nend\ndef \#{sym}; self.class.\#{sym}; end\ndef self.\#{sym}=(value); @\#{sym} = value; end\n", __FILE__, __LINE__ + 1)
301: end
302: end
# File lib/active_ldap/base.rb, line 511
511: def class_of_active_ldap_descendant(klass)
512: if klass.superclass == Base or klass.superclass.abstract_class?
513: klass
514: elsif klass.superclass.nil?
515: raise Error, _("%s doesn't belong in a hierarchy descending " \
516: "from ActiveLdap") % (name || to_s)
517: else
518: class_of_active_ldap_descendant(klass.superclass)
519: end
520: end
# File lib/active_ldap/base.rb, line 390
390: def create(attributes=nil, &block)
391: if attributes.is_a?(Array)
392: attributes.collect {|attrs| create(attrs, &block)}
393: else
394: object = new(attributes, &block)
395: object.save
396: object
397: end
398: end
# File lib/active_ldap/base.rb, line 475
475: def default_search_attribute
476: dn_attribute
477: end
establish_connection is deprecated since 1.1.0. Please use setup_connection() instead.
# File lib/active_ldap/base.rb, line 381
381: def establish_connection(config=nil)
382: message =
383: _("ActiveLdap::Base.establish_connection has been deprecated " \
384: "since 1.1.0. " \
385: "Please use ActiveLdap::Base.setup_connection instead.")
386: ActiveSupport::Deprecation.warn(message)
387: setup_connection(config)
388: end
# File lib/active_ldap/base.rb, line 541
541: def human_name(options={})
542: defaults = self_and_descendants_from_active_ldap.collect do |klass|
543: if klass.name.blank?
544: nil
545: else
546: "#{klass.name.underscore}""#{klass.name.underscore}"
547: end
548: end
549: defaults << name.humanize
550: defaults = defaults.compact
551: defaults.first || name || to_s
552: end
# File lib/active_ldap/base.rb, line 479
479: def inspect
480: if self == Base
481: super
482: elsif abstract_class?
483: "#{super}(abstract)"
484: else
485: detail = nil
486: begin
487: must = []
488: may = []
489: class_names = classes.collect do |object_class|
490: must.concat(object_class.must)
491: may.concat(object_class.may)
492: object_class.name
493: end
494: detail = ["objectClass:<#{class_names.join(', ')}>",
495: "must:<#{inspect_attributes(must)}>",
496: "may:<#{inspect_attributes(may)}>"].join(", ")
497: rescue ActiveLdap::ConnectionNotSetup
498: detail = "not-connected"
499: rescue ActiveLdap::Error
500: detail = "connection-failure"
501: end
502: "#{super}(#{detail})"
503: end
504: end
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
# File lib/active_ldap/base.rb, line 407
407: def ldap_mapping(options={})
408: options = options.symbolize_keys
409: validate_ldap_mapping_options(options)
410:
411: self.dn_attribute = options[:dn_attribute] || default_dn_attribute
412: self.dn_attribute = dn_attribute.to_s if dn_attribute.is_a?(Symbol)
413: self.prefix = options[:prefix] || default_prefix
414: self.scope = options[:scope]
415: self.required_classes = options[:classes]
416: self.recommended_classes = options[:recommended_classes]
417: self.excluded_classes = options[:excluded_classes]
418: self.sort_by = options[:sort_by]
419: self.order = options[:order]
420:
421: public_class_method :new
422: end
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
# File lib/active_ldap/base.rb, line 671
671: def initialize(attributes=nil)
672: init_base
673: @new_entry = true
674: initial_classes = required_classes | recommended_classes
675: case attributes
676: when nil
677: self.classes = initial_classes
678: when String, Array, DN
679: self.classes = initial_classes
680: self.dn = attributes
681: when Hash
682: classes, attributes = extract_object_class(attributes)
683: self.classes = classes | initial_classes
684: normalized_attributes = {}
685: attributes.each do |key, value|
686: real_key = to_real_attribute_name(key) || key
687: normalized_attributes[real_key] = value
688: end
689: self.dn = normalized_attributes.delete(dn_attribute)
690: self.attributes = normalized_attributes
691: else
692: format = _("'%s' must be either nil, DN value as ActiveLdap::DN, " \
693: "String or Array or attributes as Hash")
694: raise ArgumentError, format % attributes.inspect
695: end
696: yield self if block_given?
697: end
# File lib/active_ldap/base.rb, line 444
444: def prefix
445: @prefix ||= inheritable_prefix and DN.parse(inheritable_prefix)
446: end
# File lib/active_ldap/base.rb, line 448
448: def prefix=(value)
449: self.inheritable_prefix = value
450: @prefix = nil
451: @base = nil
452: end
# File lib/active_ldap/base.rb, line 455
455: def scope=(scope)
456: validate_scope(scope)
457: self.scope_without_validation = scope
458: end
# File lib/active_ldap/base.rb, line 522
522: def self_and_descendants_from_active_ldap
523: klass = self
524: classes = [klass]
525: while klass != klass.base_class
526: classes << klass = klass.superclass
527: end
528: classes
529: rescue
530: [self]
531: end
Connect and bind to LDAP creating a class variable for use by all ActiveLdap objects.
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/active_ldap/configuration.rb for defaults for each option
# File lib/active_ldap/base.rb, line 373
373: def setup_connection(config=nil)
374: super
375: ensure_logger
376: nil
377: end
# File lib/active_ldap/base.rb, line 460
460: def validate_scope(scope)
461: scope = scope.to_sym if scope.is_a?(String)
462: return if scope.nil? or scope.is_a?(Symbol)
463: raise ConfigurationError,
464: _("scope '%s' must be a Symbol") % scope.inspect
465: end
# File lib/active_ldap/base.rb, line 634
634: def compute_base
635: _base = inheritable_base
636: _base = configuration[:base] if _base.nil? and configuration
637: if _base.nil?
638: target = superclass
639: loop do
640: break unless target.respond_to?(:base)
641: _base = target.base
642: break if _base
643: target = target.superclass
644: end
645: end
646: _prefix = prefix
647:
648: _base ||= connection.naming_contexts.first
649: return _prefix if _base.blank?
650:
651: _base = DN.parse(_base)
652: _base = _prefix + _base if _prefix
653: _base
654: end
# File lib/active_ldap/base.rb, line 617
617: def default_dn_attribute
618: dn_attribute = nil
619: parent_class = ancestors[1]
620: if parent_class.respond_to?(:dn_attribute)
621: dn_attribute = parent_class.dn_attribute
622: end
623: dn_attribute || "cn"
624: end
# File lib/active_ldap/base.rb, line 626
626: def default_prefix
627: if name.blank?
628: nil
629: else
630: "ou=#{name.demodulize.pluralize}"
631: end
632: end
# File lib/active_ldap/base.rb, line 585
585: def ensure_logger
586: @@logger ||= configuration[:logger]
587: # Setup default logger to console
588: if @@logger.nil?
589: require 'logger'
590: @@logger = Logger.new(STDERR)
591: @@logger.progname = 'ActiveLdap'
592: @@logger.level = Logger::ERROR
593: end
594: configuration[:logger] ||= @@logger
595: end
# File lib/active_ldap/base.rb, line 567
567: def inspect_attribute(attribute)
568: syntax = attribute.syntax
569: result = "#{attribute.name}"
570: if syntax and !syntax.description.blank?
571: result << ": #{syntax.description}"
572: end
573: properties = []
574: properties << "read-only" if attribute.read_only?
575: properties << "binary" if attribute.binary?
576: properties << "binary-required" if attribute.binary_required?
577: result << "(#{properties.join(', ')})" unless properties.empty?
578: result
579: end
# File lib/active_ldap/base.rb, line 555
555: def inspect_attributes(attributes)
556: inspected_attribute_names = {}
557: attributes.collect do |attribute|
558: if inspected_attribute_names.has_key?(attribute.name)
559: nil
560: else
561: inspected_attribute_names[attribute.name] = true
562: inspect_attribute(attribute)
563: end
564: end.compact.join(', ')
565: end
# File lib/active_ldap/base.rb, line 597
597: def instantiate(args)
598: dn, attributes, options = args
599: options ||= {}
600: if self.class == Class
601: klass = self.ancestors[0].to_s.split(':').last
602: real_klass = self.ancestors[0]
603: else
604: klass = self.class.to_s.split(':').last
605: real_klass = self.class
606: end
607:
608: obj = real_klass.allocate
609: conn = options[:connection] || connection
610: obj.connection = conn if conn != connection
611: obj.instance_eval do
612: initialize_by_ldap_data(dn, attributes)
613: end
614: obj
615: end
# File lib/active_ldap/base.rb, line 581
581: def validate_ldap_mapping_options(options)
582: options.assert_valid_keys(VALID_LDAP_MAPPING_OPTIONS)
583: end
Returns true if the comparison_object is the same object, or is of the same type and has the same dn.
# File lib/active_ldap/base.rb, line 701
701: def ==(comparison_object)
702: comparison_object.equal?(self) or
703: (comparison_object.instance_of?(self.class) and
704: comparison_object.dn == dn and
705: !comparison_object.new_entry?)
706: end
# File lib/active_ldap/base.rb, line 984
984: def [](name, force_array=false)
985: if name == "dn"
986: array_of(dn, force_array)
987: else
988: get_attribute(name, force_array)
989: end
990: end
# File lib/active_ldap/base.rb, line 992
992: def []=(name, value)
993: set_attribute(name, value)
994: end
Return attribute methods so that a program can determine available attributes dynamically without schema awareness
# File lib/active_ldap/base.rb, line 743
743: def attribute_names(normalize=false)
744: entry_attribute.names(normalize)
745: end
# File lib/active_ldap/base.rb, line 747
747: def attribute_present?(name)
748: values = get_attribute(name, true)
749: !values.empty? or values.any? {|x| !(x and x.empty?)}
750: end
This returns the key value pairs in @data with all values cloned
# File lib/active_ldap/base.rb, line 904
904: def attributes
905: @simplified_data ||= simplify_data(@data)
906: @simplified_data.clone
907: end
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.
# File lib/active_ldap/base.rb, line 915
915: def attributes=(new_attributes)
916: return if new_attributes.blank?
917: _schema = _local_entry_attribute = nil
918: targets = remove_attributes_protected_from_mass_assignment(new_attributes)
919: targets.each do |key, value|
920: setter = "#{key}="
921: unless respond_to?(setter)
922: _schema ||= schema
923: attribute = _schema.attribute(key)
924: next if attribute.id.nil?
925: _local_entry_attribute ||= local_entry_attribute
926: _local_entry_attribute.register(attribute)
927: end
928: send(setter, value)
929: end
930: end
# File lib/active_ldap/base.rb, line 1046
1046: def base=(object_local_base)
1047: ensure_update_dn
1048: @dn = nil
1049: @base = nil
1050: @base_value = object_local_base
1051: end
# File lib/active_ldap/base.rb, line 1002
1002: def bind(config_or_password={}, config_or_ignore=nil, &block)
1003: if config_or_password.is_a?(String)
1004: config = (config_or_ignore || {}).merge(:password => config_or_password)
1005: else
1006: config = config_or_password
1007: end
1008: config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
1009: config[:password_block] ||= block if block_given?
1010: setup_connection(config)
1011:
1012: before_connection = @connection
1013: begin
1014: @connection = nil
1015: connection.connect
1016: @connection = connection
1017: clear_connection_based_cache
1018: clear_association_cache
1019: rescue ActiveLdap::Error
1020: remove_connection
1021: @connection = before_connection
1022: raise
1023: end
1024: true
1025: end
# File lib/active_ldap/base.rb, line 1027
1027: def clear_connection_based_cache
1028: @schema = nil
1029: @local_entry_attribute = nil
1030: clear_object_class_based_cache
1031: end
# File lib/active_ldap/base.rb, line 1033
1033: def clear_object_class_based_cache
1034: @entry_attribute = nil
1035: @real_names = {}
1036: end
# File lib/active_ldap/base.rb, line 794
794: def default_search_attribute
795: self.class.default_search_attribute
796: end
# File lib/active_ldap/base.rb, line 806
806: def delete(options={})
807: super(dn, options)
808: end
# File lib/active_ldap/base.rb, line 1063
1063: def delete_all(options={})
1064: super({:base => dn}.merge(options || {}))
1065: end
# File lib/active_ldap/base.rb, line 1067
1067: def destroy_all(options={})
1068: super({:base => dn}.merge(options || {}))
1069: end
# File lib/active_ldap/base.rb, line 782
782: def dn=(value)
783: set_attribute(dn_attribute_with_fallback, value)
784: end
# File lib/active_ldap/base.rb, line 788
788: def dn_attribute
789: ensure_update_dn
790: _dn_attribute = @dn_attribute || dn_attribute_of_class
791: to_real_attribute_name(_dn_attribute) || _dn_attribute
792: end
# File lib/active_ldap/base.rb, line 996
996: def each
997: @data.each do |key, values|
998: yield(key.dup, values.dup)
999: end
1000: end
Delegates to ==
# File lib/active_ldap/base.rb, line 709
709: def eql?(comparison_object)
710: self == (comparison_object)
711: end
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") ]
# File lib/active_ldap/base.rb, line 717
717: def hash
718: return super if @_hashing # workaround for GetText :<
719: _dn = nil
720: begin
721: @_hashing = true
722: _dn = dn
723: rescue DistinguishedNameInvalid, DistinguishedNameNotSetError
724: return super
725: ensure
726: @_hashing = false
727: end
728: _dn.hash
729: end
# File lib/active_ldap/base.rb, line 961
961: def have_attribute?(name, except=[])
962: real_name = to_real_attribute_name(name)
963: !real_name.nil? and !except.include?(real_name)
964: end
# File lib/active_ldap/base.rb, line 774
774: def id
775: get_attribute(dn_attribute_with_fallback)
776: end
# File lib/active_ldap/base.rb, line 1071
1071: def inspect
1072: object_classes = entry_attribute.object_classes
1073: inspected_object_classes = object_classes.collect do |object_class|
1074: object_class.name
1075: end.join(', ')
1076: must_attributes = must.collect(&:name).sort.join(', ')
1077: may_attributes = may.collect(&:name).sort.join(', ')
1078: inspected_attributes = attribute_names.sort.collect do |name|
1079: inspect_attribute(name)
1080: end.join(', ')
1081: result = "\#<#{self.class} objectClass:<#{inspected_object_classes}>, "
1082: result << "must:<#{must_attributes}>, may:<#{may_attributes}>, "
1083: result << "#{inspected_attributes}>"
1084: result
1085: end
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.
# File lib/active_ldap/base.rb, line 832
832: def method_missing(name, *args, &block)
833: key = name.to_s
834: case key
835: when /=$/
836: real_key = $PREMATCH
837: if have_attribute?(real_key, ['objectClass'])
838: if args.size != 1
839: raise ArgumentError,
840: _("wrong number of arguments (%d for 1)") % args.size
841: end
842: return set_attribute(real_key, *args, &block)
843: end
844: when /(?:(_before_type_cast)|(\?))?$/
845: real_key = $PREMATCH
846: before_type_cast = !$1.nil?
847: query = !$2.nil?
848: if have_attribute?(real_key, ['objectClass'])
849: if args.size > 1
850: raise ArgumentError,
851: _("wrong number of arguments (%d for 1)") % args.size
852: end
853: if before_type_cast
854: return get_attribute_before_type_cast(real_key, *args)[1]
855: elsif query
856: return get_attribute_as_query(real_key, *args)
857: else
858: return get_attribute(real_key, *args)
859: end
860: end
861: end
862: super
863: end
Add available attributes to the methods
# File lib/active_ldap/base.rb, line 866
866: def methods(inherited_too=true)
867: target_names = entry_attribute.all_names
868: target_names -= ['objectClass', 'objectClass'.underscore]
869: super + target_names.uniq.collect do |x|
870: [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
871: end.flatten
872: end
# File lib/active_ldap/base.rb, line 967
967: def reload
968: clear_association_cache
969: _, attributes = search(:value => id).find do |_dn, _attributes|
970: dn == _dn
971: end
972: if attributes.nil?
973: raise EntryNotFound, _("Can't find DN '%s' to reload") % dn
974: end
975:
976: @ldap_data.update(attributes)
977: classes, attributes = extract_object_class(attributes)
978: self.classes = classes
979: self.attributes = attributes
980: @new_entry = false
981: self
982: end
# File lib/active_ldap/base.rb, line 875
875: def respond_to?(name, include_priv=false)
876: return true if super
877:
878: name = name.to_s
879: return true if have_attribute?(name, ["objectClass"])
880: return false if /(?:=|\?|_before_type_cast)$/ !~ name
881: have_attribute?($PREMATCH, ["objectClass"])
882: end
Save and validate this object into LDAP either adding or replacing attributes TODO: Relative DN support
# File lib/active_ldap/base.rb, line 815
815: def save
816: create_or_update
817: end
# File lib/active_ldap/base.rb, line 819
819: def save!
820: unless create_or_update
821: raise EntryNotSaved, _("entry %s can't be saved") % dn
822: end
823: end
# File lib/active_ldap/base.rb, line 1058
1058: def scope=(scope)
1059: self.class.validate_scope(scope)
1060: @scope = scope
1061: end
# File lib/active_ldap/base.rb, line 936
936: def to_ldif
937: Ldif.new([to_ldif_record]).to_s
938: end
# File lib/active_ldap/base.rb, line 932
932: def to_ldif_record
933: super(dn, normalize_data(@data))
934: end
# File lib/active_ldap/base.rb, line 940
940: def to_xml(options={})
941: options = options.dup
942: options[:root] ||= (self.class.name || '').underscore
943: options[:root] = 'anonymous' if options[:root].blank?
944: except = options[:except]
945: if except
946: options[:except] = except.collect do |name|
947: if name.to_s.downcase == "dn"
948: "dn"
949: else
950: to_real_attribute_name(name)
951: end
952: end.compact
953: end
954: XML.new(dn, normalize_data(@data), schema).to_s(options)
955: end
Updates a given attribute and saves immediately
# File lib/active_ldap/base.rb, line 885
885: def update_attribute(name, value)
886: send("#{name}=", value)
887: save
888: end
This performs a bulk update of attributes and immediately calls save.
# File lib/active_ldap/base.rb, line 892
892: def update_attributes(attrs)
893: self.attributes = attrs
894: save
895: end
# File lib/active_ldap/base.rb, line 897
897: def update_attributes!(attrs)
898: self.attributes = attrs
899: save!
900: end
Returns the array form of a value, or not an array if false is passed in.
# File lib/active_ldap/base.rb, line 1414
1414: def array_of(value, to_a=true)
1415: case value
1416: when Array
1417: if to_a or value.size > 1
1418: value.collect {|v| array_of(v, false)}.compact
1419: else
1420: if value.empty?
1421: nil
1422: else
1423: array_of(value.first, to_a)
1424: end
1425: end
1426: when Hash
1427: if to_a
1428: [value]
1429: else
1430: result = {}
1431: value.each {|k, v| result[k] = array_of(v, to_a)}
1432: result
1433: end
1434: else
1435: to_a ? [value] : value
1436: end
1437: end
# File lib/active_ldap/base.rb, line 1117
1117: def attribute_name_resolvable_without_connection?
1118: @entry_attribute and @local_entry_attribute
1119: end
# File lib/active_ldap/base.rb, line 1511
1511: def collect_all_attributes(data)
1512: dn_attr = dn_attribute
1513: dn_value = data[dn_attr]
1514:
1515: attributes = []
1516: attributes.push([dn_attr, dn_value])
1517:
1518: oc_value = data['objectClass']
1519: attributes.push(['objectClass', oc_value])
1520: except_keys = ['objectClass', dn_attr].collect(&:downcase)
1521: data.each do |key, value|
1522: next if except_keys.include?(key.downcase)
1523: value = self.class.remove_blank_value(value)
1524: next if self.class.blank_value?(value)
1525:
1526: attributes.push([key, value])
1527: end
1528:
1529: attributes
1530: end
# File lib/active_ldap/base.rb, line 1467
1467: def collect_modified_attributes(ldap_data, data)
1468: klass = self.class
1469: _dn_attribute = dn_attribute
1470: new_dn_value = nil
1471: attributes = []
1472:
1473: # Now that all the options will be treated as unique attributes
1474: # we can see what's changed and add anything that is brand-spankin'
1475: # new.
1476: ldap_data.each do |k, v|
1477: value = data[k] || []
1478:
1479: next if v == value
1480:
1481: value = klass.remove_blank_value(value) || []
1482: next if v == value
1483:
1484: if klass.blank_value?(value) and
1485: schema.attribute(k).binary_required?
1486: value = [{'binary' => []}]
1487: end
1488: if k == _dn_attribute
1489: new_dn_value = value[0]
1490: else
1491: attributes.push([:replace, k, value])
1492: end
1493: end
1494:
1495: data.each do |k, v|
1496: value = v || []
1497: next if ldap_data.has_key?(k)
1498:
1499: value = klass.remove_blank_value(value) || []
1500: next if klass.blank_value?(value)
1501:
1502: # Detect subtypes and account for them
1503: # REPLACE will function like ADD, but doesn't hit EQUALITY problems
1504: # TODO: Added equality(attr) to Schema
1505: attributes.push([:replace, k, value])
1506: end
1507:
1508: [new_dn_value, attributes]
1509: end
# File lib/active_ldap/base.rb, line 1399
1399: def compute_base
1400: base_of_class = self.class.base
1401: if @base_value.nil?
1402: base_of_class
1403: else
1404: base_of_object = DN.parse(@base_value)
1405: base_of_object += base_of_class if base_of_class
1406: base_of_object
1407: end
1408: end
# File lib/active_ldap/base.rb, line 1383
1383: def compute_dn
1384: return base if @dn_is_base
1385:
1386: ensure_update_dn
1387: dn_value = id
1388: if dn_value.nil?
1389: format = _("%s's DN attribute (%s) isn't set")
1390: message = format % [self.inspect, dn_attribute]
1391: raise DistinguishedNameNotSetError.new, message
1392: end
1393: dn_value = DN.escape_value(dn_value.to_s)
1394: _base = base
1395: _base = nil if _base.blank?
1396: DN.parse(["#{dn_attribute}=#{dn_value}", _base].compact.join(","))
1397: end
# File lib/active_ldap/base.rb, line 1564
1564: def create
1565: prepare_data_for_saving do |data, ldap_data|
1566: attributes = collect_all_attributes(data)
1567: add_entry(dn, attributes)
1568: @new_entry = false
1569: true
1570: end
1571: end
# File lib/active_ldap/base.rb, line 1532
1532: def create_or_update
1533: new_entry? ? create : update
1534: end
# File lib/active_ldap/base.rb, line 1088
1088: def dn_attribute_with_fallback
1089: begin
1090: dn_attribute
1091: rescue DistinguishedNameInvalid
1092: _dn_attribute = @dn_attribute || dn_attribute_of_class
1093: _dn_attribute = to_real_attribute_name(_dn_attribute) || _dn_attribute
1094: raise if _dn_attribute.nil?
1095: _dn_attribute
1096: end
1097: end
enforce_type applies your changes without attempting to write to LDAP. This means that if you set userCertificate to somebinary value, it will wrap it up correctly.
# File lib/active_ldap/base.rb, line 1195
1195: def enforce_type(key, value)
1196: # Enforce attribute value formatting
1197: normalize_attribute(key, value)[1]
1198: end
# File lib/active_ldap/base.rb, line 1373
1373: def ensure_update_dn
1374: return unless need_update_dn?
1375: @mutex.synchronize do
1376: if @dn_split_value
1377: update_dn(*@dn_split_value)
1378: @dn_split_value = nil
1379: end
1380: end
1381: end
# File lib/active_ldap/base.rb, line 1121
1121: def entry_attribute
1122: @entry_attribute ||=
1123: connection.entry_attribute(find_object_class_values(@data) || [])
1124: end
# File lib/active_ldap/base.rb, line 1130
1130: def extract_object_class(attributes)
1131: classes = []
1132: attrs = {}
1133: attributes.each do |key, value|
1134: key = key.to_s
1135: if /\Aobject_?class\z/i =~ key
1136: classes.concat(value.to_a)
1137: else
1138: attrs[key] = value
1139: end
1140: end
1141: [classes, attributes]
1142: end
# File lib/active_ldap/base.rb, line 1263
1263: def false_value?(value)
1264: value.nil? or value == false or value == [] or
1265: value == "false" or value == "FALSE" or value == ""
1266: end
# File lib/active_ldap/base.rb, line 1113
1113: def find_object_class_values(data)
1114: data["objectClass"] || data["objectclass"]
1115: end
Return the value of the attribute called by method_missing?
# File lib/active_ldap/base.rb, line 1218
1218: def get_attribute(name, force_array=false)
1219: name, value = get_attribute_before_type_cast(name, force_array)
1220: return value if name.nil?
1221: attribute = schema.attribute(name)
1222: type_cast(attribute, value)
1223: end
# File lib/active_ldap/base.rb, line 1254
1254: def get_attribute_as_query(name, force_array=false)
1255: name, value = get_attribute_before_type_cast(name, force_array)
1256: if force_array
1257: value.collect {|x| !false_value?(x)}
1258: else
1259: !false_value?(value)
1260: end
1261: end
# File lib/active_ldap/base.rb, line 1246
1246: def get_attribute_before_type_cast(name, force_array=false)
1247: name = to_real_attribute_name(name)
1248:
1249: value = @data[name]
1250: value = [] if value.nil?
1251: [name, array_of(value, force_array)]
1252: end
# File lib/active_ldap/base.rb, line 1144
1144: def init_base
1145: init_instance_variables
1146: end
# File lib/active_ldap/base.rb, line 1200
1200: def init_instance_variables
1201: @mutex = Mutex.new
1202: @data = {} # where the r/w entry data is stored
1203: @ldap_data = {} # original ldap entry data
1204: @dn_attribute = nil
1205: @base = nil
1206: @scope = nil
1207: @dn = nil
1208: @dn_is_base = false
1209: @dn_split_value = nil
1210: @connection ||= nil
1211: @_hashing = false
1212: clear_connection_based_cache
1213: end
# File lib/active_ldap/base.rb, line 1148
1148: def initialize_by_ldap_data(dn, attributes)
1149: init_base
1150: dn = Compatible.convert_to_utf8_encoded_object(dn)
1151: attributes = Compatible.convert_to_utf8_encoded_object(attributes)
1152: @original_dn = dn.clone
1153: @dn = dn
1154: @base = nil
1155: @base_value = nil
1156: @new_entry = false
1157: @dn_is_base = false
1158: @ldap_data = attributes
1159: classes, attributes = extract_object_class(attributes)
1160: self.classes = classes
1161: self.dn = dn
1162: self.attributes = attributes
1163: yield self if block_given?
1164: end
# File lib/active_ldap/base.rb, line 1099
1099: def inspect_attribute(name)
1100: values = get_attribute(name, true)
1101: values.collect do |value|
1102: if value.is_a?(String) and value.length > 50
1103: "#{value[0, 50]}...".inspect
1104: elsif value.is_a?(Date) || value.is_a?(Time)
1105: "#{value.to_s(:db)}"
1106: else
1107: value.inspect
1108: end
1109: end
1110: "#{name}: #{values.inspect}"
1111: end
# File lib/active_ldap/base.rb, line 1166
1166: def instantiate(args)
1167: dn, attributes, options = args
1168: options ||= {}
1169:
1170: obj = self.class.allocate
1171: obj.connection = options[:connection] || @connection
1172: obj.instance_eval do
1173: initialize_by_ldap_data(dn, attributes)
1174: end
1175: obj
1176: end
# File lib/active_ldap/base.rb, line 1126
1126: def local_entry_attribute
1127: @local_entry_attribute ||= connection.entry_attribute([])
1128: end
# File lib/active_ldap/base.rb, line 1369
1369: def need_update_dn?
1370: not @dn_split_value.nil?
1371: end
# File lib/active_ldap/base.rb, line 1439
1439: def normalize_data(data, except=[])
1440: _schema = schema
1441: result = {}
1442: data.each do |key, values|
1443: next if except.include?(key)
1444: real_name = to_real_attribute_name(key)
1445: next if real_name and except.include?(real_name)
1446: real_name ||= key
1447: next if _schema.attribute(real_name).id.nil?
1448: result[real_name] ||= []
1449: result[real_name].concat(enforce_type(real_name, values))
1450: end
1451: result
1452: end
# File lib/active_ldap/base.rb, line 1536
1536: def prepare_data_for_saving
1537: # Expand subtypes to real ldap_data attributes
1538: # We can't reuse @ldap_data because an exception would leave
1539: # an object in an unknown state
1540: ldap_data = normalize_data(@ldap_data)
1541:
1542: # Expand subtypes to real data attributes, but leave @data alone
1543: object_classes = find_object_class_values(@ldap_data) || []
1544: original_attributes =
1545: connection.entry_attribute(object_classes).names
1546: bad_attrs = original_attributes - entry_attribute.names
1547: data = normalize_data(@data, bad_attrs)
1548:
1549: success = yield(data, ldap_data)
1550:
1551: if success
1552: @ldap_data = data.clone
1553: # Delete items disallowed by objectclasses.
1554: # They should have been removed from ldap.
1555: bad_attrs.each do |remove_me|
1556: @ldap_data.delete(remove_me)
1557: end
1558: @original_dn = dn.clone
1559: end
1560:
1561: success
1562: end
# File lib/active_ldap/base.rb, line 1289
1289: def register_new_dn_attribute(name, value)
1290: @dn = nil
1291: @dn_is_base = false
1292: if value.blank?
1293: @dn_split_value = nil
1294: [name, nil]
1295: else
1296: new_name, new_value, raw_new_value, new_bases = split_dn_value(value)
1297: @dn_split_value = [new_name, new_value, new_bases]
1298: if new_name.nil? and new_value.nil?
1299: new_name, raw_new_value = new_bases[0].to_a[0]
1300: end
1301: [to_real_attribute_name(new_name) || name,
1302: raw_new_value || value]
1303: end
1304: end
Set the value of the attribute called by method_missing?
# File lib/active_ldap/base.rb, line 1271
1271: def set_attribute(name, value)
1272: real_name = to_real_attribute_name(name)
1273: _dn_attribute = nil
1274: valid_dn_attribute = true
1275: begin
1276: _dn_attribute = dn_attribute
1277: rescue DistinguishedNameInvalid
1278: valid_dn_attribute = false
1279: end
1280: if valid_dn_attribute and real_name == _dn_attribute
1281: real_name, value = register_new_dn_attribute(real_name, value)
1282: end
1283: raise UnknownAttribute.new(name) if real_name.nil?
1284:
1285: @data[real_name] = value
1286: @simplified_data = nil
1287: end
# File lib/active_ldap/base.rb, line 1454
1454: def simplify_data(data)
1455: _schema = schema
1456: result = {}
1457: data.each do |key, values|
1458: attribute = _schema.attribute(key)
1459: if attribute.single_value? and values.is_a?(Array) and values.size == 1
1460: values = values[0]
1461: end
1462: result[key] = type_cast(attribute, values)
1463: end
1464: result
1465: end
# File lib/active_ldap/base.rb, line 1332
1332: def split_dn_value(value)
1333: dn_value = relative_dn_value = nil
1334: begin
1335: dn_value = value if value.is_a?(DN)
1336: dn_value ||= DN.parse(value)
1337: rescue DistinguishedNameInvalid
1338: begin
1339: dn_value = DN.parse("#{dn_attribute}=#{value}")
1340: rescue DistinguishedNameInvalid
1341: return [nil, value, value, []]
1342: end
1343: end
1344:
1345: val = bases = nil
1346: begin
1347: relative_dn_value = dn_value
1348: base_of_class = self.class.base
1349: relative_dn_value -= base_of_class if base_of_class
1350: if relative_dn_value.rdns.empty?
1351: val = []
1352: bases = dn_value.rdns
1353: else
1354: val, *bases = relative_dn_value.rdns
1355: end
1356: rescue ArgumentError
1357: val, *bases = dn_value.rdns
1358: end
1359:
1360: dn_attribute_name, dn_attribute_value = val.to_a[0]
1361: escaped_dn_attribute_value = nil
1362: unless dn_attribute_value.nil?
1363: escaped_dn_attribute_value = DN.escape_value(dn_attribute_value)
1364: end
1365: [dn_attribute_name, escaped_dn_attribute_value,
1366: dn_attribute_value, bases]
1367: end
# File lib/active_ldap/base.rb, line 1178
1178: def to_real_attribute_name(name, allow_normalized_name=true)
1179: return name if name.nil?
1180: if allow_normalized_name
1181: entry_attribute.normalize(name, allow_normalized_name) ||
1182: local_entry_attribute.normalize(name, allow_normalized_name)
1183: else
1184: @real_names[name] ||=
1185: entry_attribute.normalize(name, false) ||
1186: local_entry_attribute.normalize(name, false)
1187: end
1188: end
# File lib/active_ldap/base.rb, line 1225
1225: def type_cast(attribute, value)
1226: case value
1227: when Hash
1228: result = {}
1229: value.each do |option, val|
1230: result[option] = type_cast(attribute, val)
1231: end
1232: if result.size == 1 and result.has_key?("binary")
1233: result["binary"]
1234: else
1235: result
1236: end
1237: when Array
1238: value.collect do |val|
1239: type_cast(attribute, val)
1240: end
1241: else
1242: attribute.type_cast(value)
1243: end
1244: end
# File lib/active_ldap/base.rb, line 1573
1573: def update
1574: prepare_data_for_saving do |data, ldap_data|
1575: new_dn_value, attributes = collect_modified_attributes(ldap_data, data)
1576: modify_entry(@original_dn, attributes)
1577: if new_dn_value
1578: old_dn_base = DN.parse(@original_dn).parent
1579: new_dn_base = dn.clone.parent
1580: if old_dn_base == new_dn_base
1581: new_superior = nil
1582: else
1583: new_superior = new_dn_base
1584: end
1585: modify_rdn_entry(@original_dn,
1586: "#{dn_attribute}=#{DN.escape_value(new_dn_value)}",
1587: true,
1588: new_superior)
1589: end
1590: true
1591: end
1592: end
# File lib/active_ldap/base.rb, line 1306
1306: def update_dn(new_name, new_value, bases)
1307: if new_name.nil? and new_value.nil?
1308: @dn_is_base = true
1309: @base = nil
1310: @base_value = nil
1311: attr, value = bases[0].to_a[0]
1312: @dn_attribute = attr
1313: else
1314: new_name ||= @dn_attribute || dn_attribute_of_class
1315: new_name = to_real_attribute_name(new_name)
1316: if new_name.nil?
1317: new_name = @dn_attribute || dn_attribute_of_class
1318: new_name = to_real_attribute_name(new_name)
1319: end
1320: new_bases = bases.empty? ? nil : DN.new(*bases).to_s
1321: dn_components = ["#{new_name}=#{new_value}",
1322: new_bases,
1323: self.class.base.to_s]
1324: dn_components = dn_components.find_all {|component| !component.blank?}
1325: DN.parse(dn_components.join(','))
1326: @base = nil
1327: @base_value = new_bases
1328: @dn_attribute = new_name
1329: end
1330: end