| 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 | -> | base_inheritable |
| base= | -> | base_without_parsed_cache_clear= |
| scope= | -> | scope_without_validation= |
| dn_attribute | -> | dn_attribute_of_class |
| respond_to? | -> | respond_to_without_attributes? |
| base | -> | base_of_class |
| scope | -> | scope_of_class |
| abstract_class | [RW] |
# File lib/active_ldap/base.rb, line 493
493: def abstract_class?
494: defined?(@abstract_class) && @abstract_class
495: 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 427
427: def base
428: _base = base_inheritable
429: _base = configuration[:base] if _base.nil? and configuration
430: _base ||= base_inheritable(true)
431: [prefix, _base].find_all do |component|
432: !component.blank?
433: end.join(",")
434: end
# File lib/active_ldap/base.rb, line 437
437: def base=(value)
438: self.base_without_parsed_cache_clear = value
439: @parsed_base = nil
440: end
# File lib/active_ldap/base.rb, line 459
459: def base_class
460: if self == Base or superclass == Base
461: self
462: else
463: superclass.base_class
464: end
465: end
# File lib/active_ldap/base.rb, line 290
290: def self.class_local_attr_accessor(search_ancestors, *syms)
291: syms.flatten.each do |sym|
292: 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)
293: end
294: end
# File lib/active_ldap/base.rb, line 497
497: def class_of_active_ldap_descendant(klass)
498: if klass.superclass == Base or klass.superclass.abstract_class?
499: klass
500: elsif klass.superclass.nil?
501: raise Error, _("%s doesn't belong in a hierarchy descending " \
502: "from ActiveLdap") % (name || to_s)
503: else
504: class_of_active_ldap_descendant(klass.superclass)
505: end
506: end
# File lib/active_ldap/base.rb, line 382
382: def create(attributes=nil, &block)
383: if attributes.is_a?(Array)
384: attributes.collect {|attrs| create(attrs, &block)}
385: else
386: object = new(attributes, &block)
387: object.save
388: object
389: end
390: end
# File lib/active_ldap/base.rb, line 467
467: def default_search_attribute
468: dn_attribute
469: end
establish_connection is deprecated since 1.1.0. Please use setup_connection() instead.
# File lib/active_ldap/base.rb, line 373
373: def establish_connection(config=nil)
374: message =
375: _("ActiveLdap::Base.establish_connection has been deprecated " \
376: "since 1.1.0. " \
377: "Please use ActiveLdap::Base.setup_connection instead.")
378: ActiveSupport::Deprecation.warn(message)
379: setup_connection(config)
380: end
# File lib/active_ldap/base.rb, line 527
527: def human_name(options={})
528: defaults = self_and_descendants_from_active_ldap.collect do |klass|
529: if klass.name.blank?
530: nil
531: else
532: "#{klass.name.underscore}""#{klass.name.underscore}"
533: end
534: end
535: defaults << name.humanize
536: defaults = defaults.compact
537: defaults.first || name || to_s
538: end
# File lib/active_ldap/base.rb, line 471
471: def inspect
472: if self == Base
473: super
474: elsif abstract_class?
475: "#{super}(abstract)"
476: else
477: class_names = []
478: must = []
479: may = []
480: class_names = classes.collect do |object_class|
481: must.concat(object_class.must)
482: may.concat(object_class.may)
483: object_class.name
484: end
485: detail = ["objectClass:<#{class_names.join(', ')}>",
486: "must:<#{inspect_attributes(must)}>",
487: "may:<#{inspect_attributes(may)}>"].join(", ")
488: "#{super}(#{detail})"
489: end
490: 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 399
399: def ldap_mapping(options={})
400: options = options.symbolize_keys
401: validate_ldap_mapping_options(options)
402:
403: self.dn_attribute = options[:dn_attribute] || default_dn_attribute
404: self.dn_attribute = dn_attribute.to_s if dn_attribute.is_a?(Symbol)
405: self.prefix = options[:prefix] || default_prefix
406: self.scope = options[:scope]
407: self.required_classes = options[:classes]
408: self.recommended_classes = options[:recommended_classes]
409: self.excluded_classes = options[:excluded_classes]
410: self.sort_by = options[:sort_by]
411: self.order = options[:order]
412:
413: public_class_method :new
414: 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 635
635: def initialize(attributes=nil)
636: init_base
637: @new_entry = true
638: initial_classes = required_classes | recommended_classes
639: case attributes
640: when nil
641: self.classes = initial_classes
642: when String, Array, DN
643: self.classes = initial_classes
644: self.dn = attributes
645: when Hash
646: classes, attributes = extract_object_class(attributes)
647: self.classes = classes | initial_classes
648: normalized_attributes = {}
649: attributes.each do |key, value|
650: real_key = to_real_attribute_name(key) || key
651: normalized_attributes[real_key] = value
652: end
653: self.dn = normalized_attributes.delete(dn_attribute)
654: self.attributes = normalized_attributes
655: else
656: format = _("'%s' must be either nil, DN value as ActiveLdap::DN, " \
657: "String or Array or attributes as Hash")
658: raise ArgumentError, format % attributes.inspect
659: end
660: yield self if block_given?
661: end
# File lib/active_ldap/base.rb, line 442
442: def parsed_base
443: @parsed_base ||= DN.parse(base)
444: end
# File lib/active_ldap/base.rb, line 447
447: def scope=(scope)
448: validate_scope(scope)
449: self.scope_without_validation = scope
450: end
# File lib/active_ldap/base.rb, line 508
508: def self_and_descendants_from_active_ldap
509: klass = self
510: classes = [klass]
511: while klass != klass.base_class
512: classes << klass = klass.superclass
513: end
514: classes
515: rescue
516: [self]
517: 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 365
365: def setup_connection(config=nil)
366: super
367: ensure_logger
368: nil
369: end
# File lib/active_ldap/base.rb, line 452
452: def validate_scope(scope)
453: scope = scope.to_sym if scope.is_a?(String)
454: return if scope.nil? or scope.is_a?(Symbol)
455: raise ConfigurationError,
456: _("scope '%s' must be a Symbol") % scope.inspect
457: end
# File lib/active_ldap/base.rb, line 603
603: def default_dn_attribute
604: dn_attribute = nil
605: parent_class = ancestors[1]
606: if parent_class.respond_to?(:dn_attribute)
607: dn_attribute = parent_class.dn_attribute
608: end
609: dn_attribute || "cn"
610: end
# File lib/active_ldap/base.rb, line 612
612: def default_prefix
613: if name.blank?
614: nil
615: else
616: "ou=#{name.demodulize.pluralize}"
617: end
618: end
# File lib/active_ldap/base.rb, line 571
571: def ensure_logger
572: @@logger ||= configuration[:logger]
573: # Setup default logger to console
574: if @@logger.nil?
575: require 'logger'
576: @@logger = Logger.new(STDERR)
577: @@logger.progname = 'ActiveLdap'
578: @@logger.level = Logger::UNKNOWN
579: end
580: configuration[:logger] ||= @@logger
581: end
# File lib/active_ldap/base.rb, line 553
553: def inspect_attribute(attribute)
554: syntax = attribute.syntax
555: result = "#{attribute.name}"
556: if syntax and !syntax.description.blank?
557: result << ": #{syntax.description}"
558: end
559: properties = []
560: properties << "read-only" if attribute.read_only?
561: properties << "binary" if attribute.binary?
562: properties << "binary-required" if attribute.binary_required?
563: result << "(#{properties.join(', ')})" unless properties.empty?
564: result
565: end
# File lib/active_ldap/base.rb, line 541
541: def inspect_attributes(attributes)
542: inspected_attribute_names = {}
543: attributes.collect do |attribute|
544: if inspected_attribute_names.has_key?(attribute.name)
545: nil
546: else
547: inspected_attribute_names[attribute.name] = true
548: inspect_attribute(attribute)
549: end
550: end.compact.join(', ')
551: end
# File lib/active_ldap/base.rb, line 583
583: def instantiate(args)
584: dn, attributes, options = args
585: options ||= {}
586: if self.class == Class
587: klass = self.ancestors[0].to_s.split(':').last
588: real_klass = self.ancestors[0]
589: else
590: klass = self.class.to_s.split(':').last
591: real_klass = self.class
592: end
593:
594: obj = real_klass.allocate
595: conn = options[:connection] || connection
596: obj.connection = conn if conn != connection
597: obj.instance_eval do
598: initialize_by_ldap_data(dn, attributes)
599: end
600: obj
601: end
# File lib/active_ldap/base.rb, line 567
567: def validate_ldap_mapping_options(options)
568: options.assert_valid_keys(VALID_LDAP_MAPPING_OPTIONS)
569: 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 665
665: def ==(comparison_object)
666: comparison_object.equal?(self) or
667: (comparison_object.instance_of?(self.class) and
668: comparison_object.dn == dn and
669: !comparison_object.new_entry?)
670: end
# File lib/active_ldap/base.rb, line 941
941: def [](name, force_array=false)
942: if name == "dn"
943: array_of(dn, force_array)
944: else
945: get_attribute(name, force_array)
946: end
947: end
# File lib/active_ldap/base.rb, line 949
949: def []=(name, value)
950: set_attribute(name, value)
951: end
Return attribute methods so that a program can determine available attributes dynamically without schema awareness
# File lib/active_ldap/base.rb, line 697
697: def attribute_names(normalize=false)
698: entry_attribute.names(normalize)
699: end
# File lib/active_ldap/base.rb, line 701
701: def attribute_present?(name)
702: values = get_attribute(name, true)
703: !values.empty? or values.any? {|x| !(x and x.empty?)}
704: end
This returns the key value pairs in @data with all values cloned
# File lib/active_ldap/base.rb, line 863
863: def attributes
864: Marshal.load(Marshal.dump(@data))
865: 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 873
873: def attributes=(new_attributes)
874: return if new_attributes.blank?
875: _schema = _local_entry_attribute = nil
876: targets = remove_attributes_protected_from_mass_assignment(new_attributes)
877: targets.each do |key, value|
878: setter = "#{key}="
879: unless respond_to?(setter)
880: _schema ||= schema
881: attribute = _schema.attribute(key)
882: next if attribute.id.nil?
883: _local_entry_attribute ||= local_entry_attribute
884: _local_entry_attribute.register(attribute)
885: end
886: send(setter, value)
887: end
888: end
# File lib/active_ldap/base.rb, line 1000
1000: def base
1001: ensure_update_dn
1002: [@base, base_of_class].find_all do |component|
1003: not component.blank?
1004: end.join(",")
1005: end
# File lib/active_ldap/base.rb, line 1007
1007: def base=(object_local_base)
1008: ensure_update_dn
1009: @dn = nil
1010: @base = object_local_base
1011: end
# File lib/active_ldap/base.rb, line 959
959: def bind(config_or_password={}, config_or_ignore=nil, &block)
960: if config_or_password.is_a?(String)
961: config = (config_or_ignore || {}).merge(:password => config_or_password)
962: else
963: config = config_or_password
964: end
965: config = {:bind_dn => dn, :allow_anonymous => false}.merge(config)
966: config[:password_block] ||= block if block_given?
967: setup_connection(config)
968:
969: before_connection = @connection
970: begin
971: @connection = nil
972: connection.connect
973: @connection = connection
974: clear_connection_based_cache
975: clear_association_cache
976: rescue ActiveLdap::Error
977: remove_connection
978: @connection = before_connection
979: raise
980: end
981: true
982: end
# File lib/active_ldap/base.rb, line 984
984: def clear_connection_based_cache
985: @schema = nil
986: @local_entry_attribute = nil
987: clear_object_class_based_cache
988: end
# File lib/active_ldap/base.rb, line 990
990: def clear_object_class_based_cache
991: @entry_attribute = nil
992: @real_names = {}
993: end
# File lib/active_ldap/base.rb, line 749
749: def default_search_attribute
750: self.class.default_search_attribute
751: end
# File lib/active_ldap/base.rb, line 765
765: def delete(options={})
766: super(dn, options)
767: end
# File lib/active_ldap/base.rb, line 736
736: def dn=(value)
737: set_attribute(dn_attribute_with_fallback, value)
738: @dn = nil
739: end
# File lib/active_ldap/base.rb, line 743
743: def dn_attribute
744: ensure_update_dn
745: _dn_attribute = @dn_attribute || dn_attribute_of_class
746: to_real_attribute_name(_dn_attribute) || _dn_attribute
747: end
# File lib/active_ldap/base.rb, line 953
953: def each
954: @data.each do |key, values|
955: yield(key.dup, values.dup)
956: end
957: end
Delegates to ==
# File lib/active_ldap/base.rb, line 673
673: def eql?(comparison_object)
674: self == (comparison_object)
675: end
# File lib/active_ldap/base.rb, line 918
918: def have_attribute?(name, except=[])
919: real_name = to_real_attribute_name(name)
920: !real_name.nil? and !except.include?(real_name)
921: end
# File lib/active_ldap/base.rb, line 728
728: def id
729: get_attribute(dn_attribute_with_fallback)
730: end
# File lib/active_ldap/base.rb, line 1023
1023: def inspect
1024: object_classes = entry_attribute.object_classes
1025: inspected_object_classes = object_classes.collect do |object_class|
1026: object_class.name
1027: end.join(', ')
1028: must_attributes = must.collect(&:name).sort.join(', ')
1029: may_attributes = may.collect(&:name).sort.join(', ')
1030: inspected_attributes = attribute_names.sort.collect do |name|
1031: inspect_attribute(name)
1032: end.join(', ')
1033: result = "\#<#{self.class} objectClass:<#{inspected_object_classes}>, "
1034: result << "must:<#{must_attributes}>, may:<#{may_attributes}>, "
1035: result << "#{inspected_attributes}>"
1036: result
1037: 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 791
791: def method_missing(name, *args, &block)
792: key = name.to_s
793: case key
794: when /=$/
795: real_key = $PREMATCH
796: if have_attribute?(real_key, ['objectClass'])
797: if args.size != 1
798: raise ArgumentError,
799: _("wrong number of arguments (%d for 1)") % args.size
800: end
801: return set_attribute(real_key, *args, &block)
802: end
803: when /(?:(_before_type_cast)|(\?))?$/
804: real_key = $PREMATCH
805: before_type_cast = !$1.nil?
806: query = !$2.nil?
807: if have_attribute?(real_key, ['objectClass'])
808: if args.size > 1
809: raise ArgumentError,
810: _("wrong number of arguments (%d for 1)") % args.size
811: end
812: if before_type_cast
813: return get_attribute_before_type_cast(real_key, *args)[1]
814: elsif query
815: return get_attribute_as_query(real_key, *args)
816: else
817: return get_attribute(real_key, *args)
818: end
819: end
820: end
821: super
822: end
Add available attributes to the methods
# File lib/active_ldap/base.rb, line 825
825: def methods(inherited_too=true)
826: target_names = entry_attribute.all_names
827: target_names -= ['objectClass', 'objectClass'.underscore]
828: super + target_names.uniq.collect do |x|
829: [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
830: end.flatten
831: end
# File lib/active_ldap/base.rb, line 924
924: def reload
925: clear_association_cache
926: _, attributes = search(:value => id).find do |_dn, _attributes|
927: dn == _dn
928: end
929: if attributes.nil?
930: raise EntryNotFound, _("Can't find DN '%s' to reload") % dn
931: end
932:
933: @ldap_data.update(attributes)
934: classes, attributes = extract_object_class(attributes)
935: self.classes = classes
936: self.attributes = attributes
937: @new_entry = false
938: self
939: end
# File lib/active_ldap/base.rb, line 834
834: def respond_to?(name, include_priv=false)
835: return true if super
836:
837: name = name.to_s
838: return true if have_attribute?(name)
839: return false if /(?:=|\?|_before_type_cast)$/ !~ name
840: have_attribute?($PREMATCH)
841: end
Save and validate this object into LDAP either adding or replacing attributes TODO: Relative DN support
# File lib/active_ldap/base.rb, line 774
774: def save
775: create_or_update
776: end
# File lib/active_ldap/base.rb, line 778
778: def save!
779: unless create_or_update
780: raise EntryNotSaved, _("entry %s can't be saved") % dn
781: end
782: end
# File lib/active_ldap/base.rb, line 1018
1018: def scope=(scope)
1019: self.class.validate_scope(scope)
1020: @scope = scope
1021: end
# File lib/active_ldap/base.rb, line 894
894: def to_ldif
895: Ldif.new([to_ldif_record]).to_s
896: end
# File lib/active_ldap/base.rb, line 890
890: def to_ldif_record
891: super(dn, normalize_data(@data))
892: end
# File lib/active_ldap/base.rb, line 898
898: def to_xml(options={})
899: options = options.dup
900: options[:root] ||= (self.class.name || '').underscore
901: except = options[:except]
902: if except
903: options[:except] = except.collect do |name|
904: if name.to_s.downcase == "dn"
905: "dn"
906: else
907: to_real_attribute_name(name)
908: end
909: end.compact
910: end
911: XML.new(dn, normalize_data(@data), schema).to_s(options)
912: end
Updates a given attribute and saves immediately
# File lib/active_ldap/base.rb, line 844
844: def update_attribute(name, value)
845: send("#{name}=", value)
846: save
847: end
This performs a bulk update of attributes and immediately calls save.
# File lib/active_ldap/base.rb, line 851
851: def update_attributes(attrs)
852: self.attributes = attrs
853: save
854: end
# File lib/active_ldap/base.rb, line 856
856: def update_attributes!(attrs)
857: self.attributes = attrs
858: save!
859: end
Returns the array form of a value, or not an array if false is passed in.
# File lib/active_ldap/base.rb, line 1344
1344: def array_of(value, to_a=true)
1345: case value
1346: when Array
1347: if to_a or value.size > 1
1348: value.collect {|v| array_of(v, false)}.compact
1349: else
1350: if value.empty?
1351: nil
1352: else
1353: array_of(value.first, to_a)
1354: end
1355: end
1356: when Hash
1357: if to_a
1358: [value]
1359: else
1360: result = {}
1361: value.each {|k, v| result[k] = array_of(v, to_a)}
1362: result
1363: end
1364: else
1365: to_a ? [value] : value
1366: end
1367: end
# File lib/active_ldap/base.rb, line 1065
1065: def attribute_name_resolvable_without_connection?
1066: @entry_attribute and @local_entry_attribute
1067: end
# File lib/active_ldap/base.rb, line 1429
1429: def collect_all_attributes(data)
1430: dn_attr = dn_attribute
1431: dn_value = data[dn_attr]
1432:
1433: attributes = []
1434: attributes.push([dn_attr, dn_value])
1435:
1436: oc_value = data['objectClass']
1437: attributes.push(['objectClass', oc_value])
1438: data.each do |key, value|
1439: next if key == 'objectClass' or key == dn_attr
1440: value = self.class.remove_blank_value(value)
1441: next if self.class.blank_value?(value)
1442:
1443: attributes.push([key, value])
1444: end
1445:
1446: attributes
1447: end
# File lib/active_ldap/base.rb, line 1384
1384: def collect_modified_attributes(ldap_data, data)
1385: attributes = []
1386: # Now that all the options will be treated as unique attributes
1387: # we can see what's changed and add anything that is brand-spankin'
1388: # new.
1389: ldap_data.each do |k, v|
1390: value = data[k] || []
1391:
1392: next if v == value
1393:
1394: x = value
1395: value = self.class.remove_blank_value(value) || []
1396: next if v == value
1397:
1398: # Create mod entries
1399: if self.class.blank_value?(value)
1400: # Since some types do not have equality matching rules,
1401: # delete doesn't work
1402: # Replacing with nothing is equivalent.
1403: if !data.has_key?(k) and schema.attribute(k).binary_required?
1404: value = [{'binary' => []}]
1405: end
1406: else
1407: # Ditched delete then replace because attribs with no equality
1408: # match rules will fails
1409: end
1410: attributes.push([:replace, k, value])
1411: end
1412: data.each do |k, v|
1413: value = v || []
1414: next if ldap_data.has_key?(k)
1415:
1416: value = self.class.remove_blank_value(value) || []
1417: next if self.class.blank_value?(value)
1418:
1419:
1420: # Detect subtypes and account for them
1421: # REPLACE will function like ADD, but doesn't hit EQUALITY problems
1422: # TODO: Added equality(attr) to Schema
1423: attributes.push([:replace, k, value])
1424: end
1425:
1426: attributes
1427: end
# File lib/active_ldap/base.rb, line 1320
1320: def compute_dn(escape_dn_value=false)
1321: return base if @dn_is_base
1322:
1323: ensure_update_dn
1324: dn_value = id
1325: if dn_value.nil?
1326: format =_("%s's DN attribute (%s) isn't set")
1327: message = format % [self.inspect, dn_attribute]
1328: raise DistinguishedNameNotSetError.new, message
1329: end
1330: dn_value = DN.escape_value(dn_value.to_s) if escape_dn_value
1331: _base = base
1332: _base = nil if _base.blank?
1333: ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
1334: end
# File lib/active_ldap/base.rb, line 1477
1477: def create
1478: prepare_data_for_saving do |data, ldap_data|
1479: attributes = collect_all_attributes(data)
1480: add_entry(escaped_dn, attributes)
1481: @new_entry = false
1482: true
1483: end
1484: end
# File lib/active_ldap/base.rb, line 1449
1449: def create_or_update
1450: new_entry? ? create : update
1451: end
# File lib/active_ldap/base.rb, line 1040
1040: def dn_attribute_with_fallback
1041: begin
1042: dn_attribute
1043: rescue DistinguishedNameInvalid
1044: _dn_attribute = @dn_attribute || dn_attribute_of_class
1045: _dn_attribute = to_real_attribute_name(_dn_attribute) || _dn_attribute
1046: raise if _dn_attribute.nil?
1047: _dn_attribute
1048: end
1049: 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 1139
1139: def enforce_type(key, value)
1140: # Enforce attribute value formatting
1141: normalize_attribute(key, value)[1]
1142: end
# File lib/active_ldap/base.rb, line 1310
1310: def ensure_update_dn
1311: return unless need_update_dn?
1312: @mutex.synchronize do
1313: if @dn_split_value
1314: update_dn(*@dn_split_value)
1315: @dn_split_value = nil
1316: end
1317: end
1318: end
# File lib/active_ldap/base.rb, line 1069
1069: def entry_attribute
1070: @entry_attribute ||= connection.entry_attribute(@data["objectClass"] || [])
1071: end
# File lib/active_ldap/base.rb, line 1077
1077: def extract_object_class(attributes)
1078: classes = []
1079: attrs = {}
1080: attributes.each do |key, value|
1081: key = key.to_s
1082: if /\Aobject_?class\z/i =~ key
1083: classes.concat(value.to_a)
1084: else
1085: attrs[key] = value
1086: end
1087: end
1088: [classes, attributes]
1089: end
# File lib/active_ldap/base.rb, line 1205
1205: def false_value?(value)
1206: value.nil? or value == false or value == [] or
1207: value == "false" or value == "FALSE" or value == ""
1208: end
Return the value of the attribute called by method_missing?
# File lib/active_ldap/base.rb, line 1161
1161: def get_attribute(name, force_array=false)
1162: name, value = get_attribute_before_type_cast(name, force_array)
1163: return value if name.nil?
1164: attribute = schema.attribute(name)
1165: type_cast(attribute, value)
1166: end
# File lib/active_ldap/base.rb, line 1196
1196: def get_attribute_as_query(name, force_array=false)
1197: name, value = get_attribute_before_type_cast(name, force_array)
1198: if force_array
1199: value.collect {|x| !false_value?(x)}
1200: else
1201: !false_value?(value)
1202: end
1203: end
# File lib/active_ldap/base.rb, line 1189
1189: def get_attribute_before_type_cast(name, force_array=false)
1190: name = to_real_attribute_name(name)
1191:
1192: value = @data[name] || []
1193: [name, array_of(value, force_array)]
1194: end
# File lib/active_ldap/base.rb, line 1091
1091: def init_base
1092: init_instance_variables
1093: end
# File lib/active_ldap/base.rb, line 1144
1144: def init_instance_variables
1145: @mutex = Mutex.new
1146: @data = {} # where the r/w entry data is stored
1147: @ldap_data = {} # original ldap entry data
1148: @dn_attribute = nil
1149: @base = nil
1150: @scope = nil
1151: @dn = nil
1152: @dn_is_base = false
1153: @dn_split_value = nil
1154: @connection ||= nil
1155: clear_connection_based_cache
1156: end
# File lib/active_ldap/base.rb, line 1095
1095: def initialize_by_ldap_data(dn, attributes)
1096: init_base
1097: dn = Compatible.convert_to_utf8_encoded_object(dn)
1098: attributes = Compatible.convert_to_utf8_encoded_object(attributes)
1099: @dn = dn
1100: @new_entry = false
1101: @dn_is_base = false
1102: @ldap_data = attributes
1103: classes, attributes = extract_object_class(attributes)
1104: self.classes = classes
1105: self.dn = dn
1106: self.attributes = attributes
1107: yield self if block_given?
1108: end
# File lib/active_ldap/base.rb, line 1051
1051: def inspect_attribute(name)
1052: values = get_attribute(name, true)
1053: values.collect do |value|
1054: if value.is_a?(String) and value.length > 50
1055: "#{value[0, 50]}...".inspect
1056: elsif value.is_a?(Date) || value.is_a?(Time)
1057: "#{value.to_s(:db)}"
1058: else
1059: value.inspect
1060: end
1061: end
1062: "#{name}: #{values.inspect}"
1063: end
# File lib/active_ldap/base.rb, line 1110
1110: def instantiate(args)
1111: dn, attributes, options = args
1112: options ||= {}
1113:
1114: obj = self.class.allocate
1115: obj.connection = options[:connection] || @connection
1116: obj.instance_eval do
1117: initialize_by_ldap_data(dn, attributes)
1118: end
1119: obj
1120: end
# File lib/active_ldap/base.rb, line 1073
1073: def local_entry_attribute
1074: @local_entry_attribute ||= connection.entry_attribute([])
1075: end
# File lib/active_ldap/base.rb, line 1306
1306: def need_update_dn?
1307: not @dn_split_value.nil?
1308: end
# File lib/active_ldap/base.rb, line 1369
1369: def normalize_data(data, except=[])
1370: _schema = schema
1371: result = {}
1372: data.each do |key, values|
1373: next if except.include?(key)
1374: real_name = to_real_attribute_name(key)
1375: next if real_name and except.include?(real_name)
1376: real_name ||= key
1377: next if _schema.attribute(real_name).id.nil?
1378: result[real_name] ||= []
1379: result[real_name].concat(enforce_type(real_name, values))
1380: end
1381: result
1382: end
# File lib/active_ldap/base.rb, line 1453
1453: def prepare_data_for_saving
1454: # Expand subtypes to real ldap_data attributes
1455: # We can't reuse @ldap_data because an exception would leave
1456: # an object in an unknown state
1457: ldap_data = normalize_data(@ldap_data)
1458:
1459: # Expand subtypes to real data attributes, but leave @data alone
1460: bad_attrs = @data.keys - attribute_names
1461: data = normalize_data(@data, bad_attrs)
1462:
1463: success = yield(data, ldap_data)
1464:
1465: if success
1466: @ldap_data = Marshal.load(Marshal.dump(data))
1467: # Delete items disallowed by objectclasses.
1468: # They should have been removed from ldap.
1469: bad_attrs.each do |remove_me|
1470: @ldap_data.delete(remove_me)
1471: end
1472: end
1473:
1474: success
1475: end
# File lib/active_ldap/base.rb, line 1230
1230: def register_new_dn_attribute(name, value)
1231: @dn = nil
1232: @dn_is_base = false
1233: if value.blank?
1234: @dn_split_value = nil
1235: [name, nil]
1236: else
1237: new_name, new_value, raw_new_value, new_bases = split_dn_value(value)
1238: @dn_split_value = [new_name, new_value, new_bases]
1239: if new_name.nil? and new_value.nil?
1240: new_name, raw_new_value = new_bases[0].to_a[0]
1241: end
1242: [to_real_attribute_name(new_name) || name,
1243: raw_new_value || value]
1244: end
1245: end
Set the value of the attribute called by method_missing?
# File lib/active_ldap/base.rb, line 1213
1213: def set_attribute(name, value)
1214: real_name = to_real_attribute_name(name)
1215: _dn_attribute = nil
1216: valid_dn_attribute = true
1217: begin
1218: _dn_attribute = dn_attribute
1219: rescue DistinguishedNameInvalid
1220: valid_dn_attribute = false
1221: end
1222: if valid_dn_attribute and real_name == _dn_attribute
1223: real_name, value = register_new_dn_attribute(real_name, value)
1224: end
1225: raise UnknownAttribute.new(name) if real_name.nil?
1226:
1227: @data[real_name] = value
1228: end
# File lib/active_ldap/base.rb, line 1271
1271: def split_dn_value(value)
1272: dn_value = relative_dn_value = nil
1273: begin
1274: dn_value = value if value.is_a?(DN)
1275: dn_value ||= DN.parse(value)
1276: rescue DistinguishedNameInvalid
1277: begin
1278: dn_value = DN.parse("#{dn_attribute}=#{value}")
1279: rescue DistinguishedNameInvalid
1280: return [nil, value, value, []]
1281: end
1282: end
1283:
1284: val = bases = nil
1285: begin
1286: relative_dn_value = dn_value - self.class.parsed_base
1287: if relative_dn_value.rdns.empty?
1288: val = []
1289: bases = dn_value.rdns
1290: else
1291: val, *bases = relative_dn_value.rdns
1292: end
1293: rescue ArgumentError
1294: val, *bases = dn_value.rdns
1295: end
1296:
1297: dn_attribute_name, dn_attribute_value = val.to_a[0]
1298: escaped_dn_attribute_value = nil
1299: unless dn_attribute_value.nil?
1300: escaped_dn_attribute_value = DN.escape_value(dn_attribute_value)
1301: end
1302: [dn_attribute_name, escaped_dn_attribute_value,
1303: dn_attribute_value, bases]
1304: end
# File lib/active_ldap/base.rb, line 1122
1122: def to_real_attribute_name(name, allow_normalized_name=true)
1123: return name if name.nil?
1124: if allow_normalized_name
1125: entry_attribute.normalize(name, allow_normalized_name) ||
1126: local_entry_attribute.normalize(name, allow_normalized_name)
1127: else
1128: @real_names[name] ||=
1129: entry_attribute.normalize(name, false) ||
1130: local_entry_attribute.normalize(name, false)
1131: end
1132: end
# File lib/active_ldap/base.rb, line 1168
1168: def type_cast(attribute, value)
1169: case value
1170: when Hash
1171: result = {}
1172: value.each do |option, val|
1173: result[option] = type_cast(attribute, val)
1174: end
1175: if result.size == 1 and result.has_key?("binary")
1176: result["binary"]
1177: else
1178: result
1179: end
1180: when Array
1181: value.collect do |val|
1182: type_cast(attribute, val)
1183: end
1184: else
1185: attribute.type_cast(value)
1186: end
1187: end
# File lib/active_ldap/base.rb, line 1486
1486: def update
1487: prepare_data_for_saving do |data, ldap_data|
1488: attributes = collect_modified_attributes(ldap_data, data)
1489: modify_entry(escaped_dn, attributes)
1490: true
1491: end
1492: end
# File lib/active_ldap/base.rb, line 1247
1247: def update_dn(new_name, new_value, bases)
1248: if new_name.nil? and new_value.nil?
1249: @dn_is_base = true
1250: @base = nil
1251: attr, value = bases[0].to_a[0]
1252: @dn_attribute = attr
1253: else
1254: new_name ||= @dn_attribute || dn_attribute_of_class
1255: new_name = to_real_attribute_name(new_name)
1256: if new_name.nil?
1257: new_name = @dn_attribute || dn_attribute_of_class
1258: new_name = to_real_attribute_name(new_name)
1259: end
1260: new_bases = bases.empty? ? nil : DN.new(*bases).to_s
1261: dn_components = ["#{new_name}=#{new_value}",
1262: new_bases,
1263: base_of_class]
1264: dn_components = dn_components.find_all {|component| !component.blank?}
1265: DN.parse(dn_components.join(','))
1266: @base = new_bases
1267: @dn_attribute = new_name
1268: end
1269: end