ruby-activeldap 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/activeldap/associations.rb +2 -2
- data/lib/activeldap/base.rb +138 -109
- data/lib/activeldap/schema2.rb +67 -27
- data/lib/activeldap.rb +34 -28
- metadata +2 -2
@@ -75,7 +75,7 @@ module ActiveLDAP
|
|
75
75
|
key = options[:foreign_key] || association_id.to_s + "_id"
|
76
76
|
local_key = options[:local_key] || ''
|
77
77
|
class_eval <<-"end_eval"
|
78
|
-
def #{association_id}(objects =
|
78
|
+
def #{association_id}(objects = true)
|
79
79
|
local_key = "#{local_key}"
|
80
80
|
local_key = dnattr() if local_key.empty?
|
81
81
|
return #{klass}.find_all(:attribute => "#{key}", :value => attribute_method(local_key), :objects => objects)
|
@@ -100,7 +100,7 @@ module ActiveLDAP
|
|
100
100
|
key = options[:local_key] || association_id.to_s + "_id"
|
101
101
|
foreign_key = options[:foreign_key] || ''
|
102
102
|
class_eval <<-"end_eval"
|
103
|
-
def #{association_id}(objects =
|
103
|
+
def #{association_id}(objects = true)
|
104
104
|
foreign_key = "#{foreign_key}"
|
105
105
|
if foreign_key.empty?
|
106
106
|
foreign_key = dnattr()
|
data/lib/activeldap/base.rb
CHANGED
@@ -74,6 +74,12 @@ module ActiveLDAP
|
|
74
74
|
class ConnectionError < RuntimeError
|
75
75
|
end
|
76
76
|
|
77
|
+
# ObjectClassError
|
78
|
+
#
|
79
|
+
# An exception raised when an objectClass is not defined in the schema
|
80
|
+
class ObjectClassError < RuntimeError
|
81
|
+
end
|
82
|
+
|
77
83
|
|
78
84
|
# Base
|
79
85
|
#
|
@@ -189,7 +195,7 @@ module ActiveLDAP
|
|
189
195
|
# Setup default logger to console
|
190
196
|
if @@logger.nil?
|
191
197
|
@@logger = Log4r::Logger.new('activeldap')
|
192
|
-
@@logger.level = Log4r::
|
198
|
+
@@logger.level = Log4r::OFF
|
193
199
|
Log4r::StderrOutputter.new 'console'
|
194
200
|
@@logger.add('console')
|
195
201
|
end
|
@@ -240,9 +246,8 @@ module ActiveLDAP
|
|
240
246
|
raise ConnectionError, "#{detail.exception} - LDAP connection failure, or server does not support schema queries."
|
241
247
|
end
|
242
248
|
|
243
|
-
|
244
249
|
# Cleanly return
|
245
|
-
return
|
250
|
+
return true
|
246
251
|
end # Base.connect
|
247
252
|
|
248
253
|
# Base.close
|
@@ -387,7 +392,6 @@ module ActiveLDAP
|
|
387
392
|
end
|
388
393
|
|
389
394
|
# Allow a single string argument
|
390
|
-
attr = dnattr()
|
391
395
|
objects = false
|
392
396
|
val = config
|
393
397
|
# Or a hash
|
@@ -487,9 +491,6 @@ module ActiveLDAP
|
|
487
491
|
@ldap_data = {} # original ldap entry data
|
488
492
|
@ldap_data.default = []
|
489
493
|
@attr_methods = {} # list of valid method calls for attributes used for dereferencing
|
490
|
-
@must = {} # list of schema required attributes
|
491
|
-
@may = {} # list of schema optional attributes
|
492
|
-
@sup = {} # list of schema supplemental attributes
|
493
494
|
|
494
495
|
# Break val apart if it is a dn
|
495
496
|
if val.match(/^#{dnattr()}=([^,=]+),#{base()}$/i)
|
@@ -504,7 +505,7 @@ module ActiveLDAP
|
|
504
505
|
@exists = false
|
505
506
|
# Setup what should eb authoritative
|
506
507
|
@dn = "#{dnattr()}=#{val},#{base()}"
|
507
|
-
send(:
|
508
|
+
send(:apply_objectclass, required_classes())
|
508
509
|
else # do a search then
|
509
510
|
# Search for the existing entry
|
510
511
|
begin
|
@@ -521,9 +522,9 @@ module ActiveLDAP
|
|
521
522
|
@@logger.debug("finished make_subtypes for #{attr}")
|
522
523
|
# Add subtype to any existing values
|
523
524
|
if @ldap_data.has_key? safe_attr
|
524
|
-
|
525
|
+
value.each do |v|
|
525
526
|
@ldap_data[safe_attr].push(v)
|
526
|
-
|
527
|
+
end
|
527
528
|
else
|
528
529
|
@ldap_data[safe_attr] = value
|
529
530
|
end
|
@@ -531,7 +532,7 @@ module ActiveLDAP
|
|
531
532
|
end
|
532
533
|
@exists = true
|
533
534
|
# Populate schema data
|
534
|
-
send(:
|
535
|
+
send(:apply_objectclass, @ldap_data['objectClass'])
|
535
536
|
|
536
537
|
# Populate real data now that we have the schema with aliases
|
537
538
|
@ldap_data.each do |pair|
|
@@ -542,10 +543,11 @@ module ActiveLDAP
|
|
542
543
|
@exists = false
|
543
544
|
# Create what should be the authoritative DN
|
544
545
|
@dn = "#{dnattr()}=#{val},#{base()}"
|
545
|
-
send(:
|
546
|
+
send(:apply_objectclass, required_classes())
|
546
547
|
|
547
548
|
# Setup dn attribute (later rdn too!)
|
548
549
|
attr_sym = "#{dnattr()}=".to_sym
|
550
|
+
@@logger.debug("new: setting dnattr: #{dnattr()} = #{val}")
|
549
551
|
send(attr_sym, val)
|
550
552
|
end
|
551
553
|
end
|
@@ -560,79 +562,10 @@ module ActiveLDAP
|
|
560
562
|
# attributes dynamically without schema awareness
|
561
563
|
def attributes
|
562
564
|
@@logger.debug("stub: attributes called")
|
565
|
+
send(:apply_objectclass, @data['objectClass']) if @data['objectClass'] != @last_oc
|
563
566
|
return @attr_methods.keys
|
564
567
|
end
|
565
568
|
|
566
|
-
# objectClass=
|
567
|
-
#
|
568
|
-
# objectClass= special case for updating appropriately
|
569
|
-
# This updates the objectClass entry in @data. It also
|
570
|
-
# updating all required and allowed attributes while
|
571
|
-
# removing defined attributes that are no longer valid
|
572
|
-
# given the new objectclasses.
|
573
|
-
def objectClass=(val)
|
574
|
-
@@logger.debug("stub: objectClass=(#{val.inspect}) called")
|
575
|
-
if val.class != Array
|
576
|
-
raise TypeError, 'objectClass must be an Array'
|
577
|
-
end
|
578
|
-
|
579
|
-
val.each do |klass|
|
580
|
-
unless klass.class == String
|
581
|
-
raise TypeError, "Value in array is not a String. (#{klass.class})"
|
582
|
-
end
|
583
|
-
unless Base.schema.names("objectClasses").member? klass
|
584
|
-
# TODO MAKE OWN EXCEPTION
|
585
|
-
raise RuntimeError, "objectClass '#{klass}' unknown to LDAP server"
|
586
|
-
end
|
587
|
-
end
|
588
|
-
|
589
|
-
# make sure this doesn't drop any of the required objectclasses
|
590
|
-
required_classes().each do |oc|
|
591
|
-
unless val.member? oc
|
592
|
-
raise "'#{oc}' must be a defined objectClass for class '#{self.class}'"
|
593
|
-
end
|
594
|
-
end
|
595
|
-
|
596
|
-
# Set the actual objectClass data
|
597
|
-
define_attribute_methods('objectClass')
|
598
|
-
@data['objectClass'] = val.uniq
|
599
|
-
|
600
|
-
# Build |data| from schema
|
601
|
-
# clear attr_method mapping first
|
602
|
-
@attr_methods = {}
|
603
|
-
@must = {}
|
604
|
-
@may = {}
|
605
|
-
@sup = {}
|
606
|
-
val.each do |objc|
|
607
|
-
# Setup dependencies for validation pre-save
|
608
|
-
@must[objc] = Base.schema.attr('objectClasses', objc, 'MUST')
|
609
|
-
@may[objc] = Base.schema.attr('objectClasses', objc, 'MAY')
|
610
|
-
@sup[objc] = Base.schema.attr('objectClasses', objc, 'SUP')
|
611
|
-
@must[objc] = [] if @must[objc].nil?
|
612
|
-
@may[objc] = [] if @may[objc].nil?
|
613
|
-
@sup[objc] = [] if @sup[objc].nil?
|
614
|
-
|
615
|
-
# setup the alias/attr method mapping for @must
|
616
|
-
@must[objc].each do |attr|
|
617
|
-
# Update attr_method with appropriate
|
618
|
-
define_attribute_methods(attr)
|
619
|
-
end
|
620
|
-
|
621
|
-
# setup the alias/attr method mapping for @may
|
622
|
-
@may[objc].each do |attr|
|
623
|
-
define_attribute_methods(attr)
|
624
|
-
end
|
625
|
-
end
|
626
|
-
|
627
|
-
# Delete all now invalid attributes given the new objectClasses
|
628
|
-
@data.keys.each do |key|
|
629
|
-
# If it's not a proper aliased attribute, drop it
|
630
|
-
unless @attr_methods.has_key? key
|
631
|
-
@data.delete(key)
|
632
|
-
end
|
633
|
-
end
|
634
|
-
end
|
635
|
-
|
636
569
|
# exists?
|
637
570
|
#
|
638
571
|
# Return whether the entry exists in LDAP or not
|
@@ -654,15 +587,38 @@ module ActiveLDAP
|
|
654
587
|
# Basic validation:
|
655
588
|
# - Verify that every 'MUST' specified in the schema has a value defined
|
656
589
|
# - Enforcement of undefined attributes is handled in the objectClass= method
|
590
|
+
# Must call enforce_types() first before enforcement can be guaranteed
|
657
591
|
def validate
|
658
592
|
@@logger.debug("stub: validate called")
|
659
|
-
|
660
|
-
|
661
|
-
|
593
|
+
# Clean up attr values, etc
|
594
|
+
send(:enforce_types)
|
595
|
+
|
596
|
+
# Validate objectclass settings
|
597
|
+
@data['objectClass'].each do |klass|
|
598
|
+
unless klass.class == String
|
599
|
+
raise TypeError, "Value in objectClass array is not a String. (#{klass.class}:#{klass.inspect})"
|
600
|
+
end
|
601
|
+
unless Base.schema.names("objectClasses").member? klass
|
602
|
+
raise ObjectClassError, "objectClass '#{klass}' unknown to LDAP server."
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# make sure this doesn't drop any of the required objectclasses
|
607
|
+
required_classes().each do |oc|
|
608
|
+
unless @data['objectClass'].member? oc.to_s
|
609
|
+
raise ObjectClassError, "'#{oc}' must be a defined objectClass for class '#{self.class}' as set in the ldap_mapping"
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
# Make sure all MUST attributes have a value
|
614
|
+
@data['objectClass'].each do |objc|
|
615
|
+
must = Base.schema.attr('objectClasses', objc, 'MUST')
|
616
|
+
may = Base.schema.attr('objectClasses', objc, 'MAY')
|
617
|
+
must.each do |req_attr|
|
662
618
|
deref = @attr_methods[req_attr]
|
663
619
|
if @data[deref] == []
|
664
620
|
raise AttributeEmpty,
|
665
|
-
"objectClass '#{
|
621
|
+
"objectClass '#{objc}' requires attribute '#{Base.schema.attribute_aliases(req_attr).join(', ')}'"
|
666
622
|
end
|
667
623
|
end
|
668
624
|
end
|
@@ -836,7 +792,7 @@ module ActiveLDAP
|
|
836
792
|
@@logger.debug("#write: add successful")
|
837
793
|
@exists = true
|
838
794
|
rescue LDAP::ResultError => detail
|
839
|
-
raise WriteError, "Could not add LDAP entry[#{Base.
|
795
|
+
raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}"
|
840
796
|
end
|
841
797
|
end
|
842
798
|
@@logger.debug("#write: resetting @ldap_data to a dup of @data")
|
@@ -855,15 +811,25 @@ module ActiveLDAP
|
|
855
811
|
# give tab completion in irb.
|
856
812
|
def method_missing(name, *args)
|
857
813
|
@@logger.debug("stub: called method_missing(#{name.inspect}, #{args.inspect})")
|
814
|
+
|
815
|
+
# dynamically update the available attributes without requiring an
|
816
|
+
# explicit call. The cache 'last_oc' saves a lot of cpu time.
|
817
|
+
if @data['objectClass'] != @last_oc
|
818
|
+
@@logger.debug("method_missing(#{name.inspect}, #{args.inspect}): updating apply_objectclass(#{@data['objectClass'].inspect})")
|
819
|
+
send(:apply_objectclass, @data['objectClass'])
|
820
|
+
end
|
858
821
|
key = name.to_s
|
859
822
|
case key
|
860
823
|
when /^(\S+)=$/
|
861
824
|
real_key = $1
|
825
|
+
@@logger.debug("method_missing: attr_methods has_key? #{real_key}")
|
862
826
|
if @attr_methods.has_key? real_key
|
863
827
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size != 1
|
828
|
+
@@logger.debug("method_missing: calling :attribute_method=(#{real_key}, #{args[0]})")
|
864
829
|
return send(:attribute_method=, real_key, args[0])
|
865
830
|
end
|
866
831
|
else
|
832
|
+
@@logger.debug("method_missing: attr_methods has_key? #{key}")
|
867
833
|
if @attr_methods.has_key? key
|
868
834
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size > 1
|
869
835
|
return attribute_method(key, *args)
|
@@ -872,29 +838,97 @@ module ActiveLDAP
|
|
872
838
|
raise NoMethodError, "undefined method `#{key}' for #{self}"
|
873
839
|
end
|
874
840
|
|
841
|
+
# Add available attributes to the methods
|
842
|
+
alias_method :__methods, :methods
|
843
|
+
def methods
|
844
|
+
return __methods + attributes()
|
845
|
+
end
|
846
|
+
|
875
847
|
|
876
848
|
private
|
877
849
|
|
850
|
+
# enforce_types
|
851
|
+
#
|
852
|
+
# enforce_types applies your changes without attempting to write to LDAP. This means that
|
853
|
+
# if you set userCertificate to somebinary value, it will wrap it up correctly.
|
854
|
+
def enforce_types
|
855
|
+
@@logger.debug("stub: enforce_types called")
|
856
|
+
send(:apply_objectclass, @data['objectClass']) if @data['objectClass'] != @last_oc
|
857
|
+
# Enforce attribute value formatting
|
858
|
+
@data.keys.each do |key|
|
859
|
+
@data[key] = attribute_input_handler(key, @data[key])
|
860
|
+
end
|
861
|
+
@@logger.debug("stub: enforce_types done")
|
862
|
+
return true
|
863
|
+
end
|
864
|
+
|
865
|
+
|
866
|
+
|
867
|
+
# apply_objectclass
|
868
|
+
#
|
869
|
+
# objectClass= special case for updating appropriately
|
870
|
+
# This updates the objectClass entry in @data. It also
|
871
|
+
# updating all required and allowed attributes while
|
872
|
+
# removing defined attributes that are no longer valid
|
873
|
+
# given the new objectclasses.
|
874
|
+
def apply_objectclass(val)
|
875
|
+
@@logger.debug("stub: objectClass=(#{val.inspect}) called")
|
876
|
+
new_oc = val
|
877
|
+
new_oc = [val] if new_oc.class != Array
|
878
|
+
return new_oc if @last_oc == new_oc
|
879
|
+
|
880
|
+
# Store for caching purposes
|
881
|
+
@last_oc = new_oc.dup
|
882
|
+
|
883
|
+
# Set the actual objectClass data
|
884
|
+
define_attribute_methods('objectClass')
|
885
|
+
@data['objectClass'] = new_oc.uniq
|
886
|
+
|
887
|
+
# Build |data| from schema
|
888
|
+
# clear attr_method mapping first
|
889
|
+
@attr_methods = {}
|
890
|
+
attributes = []
|
891
|
+
new_oc.each do |objc|
|
892
|
+
# get all attributes for the class
|
893
|
+
attributes += Base.schema.class_attributes(objc.to_s)
|
894
|
+
end
|
895
|
+
attributes.uniq
|
896
|
+
attributes.each do |attr|
|
897
|
+
# Update attr_method with appropriate
|
898
|
+
define_attribute_methods(attr)
|
899
|
+
end
|
900
|
+
|
901
|
+
# Delete all now innew_ocid attributes given the new objectClasses
|
902
|
+
@data.keys.each do |key|
|
903
|
+
# If it's not a proper aliased attribute, drop it
|
904
|
+
unless @attr_methods.has_key? key
|
905
|
+
@data.delete(key)
|
906
|
+
end
|
907
|
+
end
|
908
|
+
end
|
909
|
+
|
910
|
+
|
911
|
+
|
878
912
|
# Enforce typing:
|
879
913
|
# Hashes are for subtypes
|
880
914
|
# Arrays are for multiple entries
|
881
915
|
def attribute_input_handler(attr, value)
|
882
916
|
@@logger.debug("stub: called attribute_input_handler(#{attr.inspect}, #{value.inspect})")
|
883
917
|
if attr.nil?
|
884
|
-
raise RuntimeError, '
|
918
|
+
raise RuntimeError, 'The first argument, attr, must not be nil. Please report this as a bug!'
|
885
919
|
end
|
886
920
|
binary = Base.schema.binary_required? attr
|
887
921
|
single = Base.schema.single_value? attr
|
888
922
|
case value.class.to_s
|
889
923
|
when 'Array'
|
890
924
|
if single and value.size > 1
|
891
|
-
raise TypeError, "
|
925
|
+
raise TypeError, "Attribute #{attr} can only have a single value"
|
892
926
|
end
|
893
927
|
value.map! do |entry|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
928
|
+
if entry.class != Hash
|
929
|
+
@@logger.debug("coercing value for #{attr} into a string because nested values exceeds a useful depth: #{entry.inspect} -> #{entry.to_s}")
|
930
|
+
entry = entry.to_s
|
931
|
+
end
|
898
932
|
entry = attribute_input_handler(attr, entry)[0]
|
899
933
|
end
|
900
934
|
when 'Hash'
|
@@ -1073,38 +1107,31 @@ module ActiveLDAP
|
|
1073
1107
|
# attribute_method
|
1074
1108
|
#
|
1075
1109
|
# Return the value of the attribute called by method_missing?
|
1076
|
-
def attribute_method(method,
|
1077
|
-
@@logger.debug("stub: called attribute_method(#{method.inspect}, #{
|
1110
|
+
def attribute_method(method, not_array = false)
|
1111
|
+
@@logger.debug("stub: called attribute_method(#{method.inspect}, #{not_array.inspect}")
|
1078
1112
|
attr = @attr_methods[method]
|
1079
1113
|
|
1080
1114
|
# Return a copy of the stored data
|
1081
|
-
return @data[attr].dup if
|
1082
|
-
return
|
1115
|
+
return array_of(@data[attr].dup, false) if not_array
|
1116
|
+
return @data[attr]
|
1083
1117
|
end
|
1084
1118
|
|
1085
1119
|
|
1086
1120
|
# attribute_method=
|
1087
1121
|
#
|
1088
1122
|
# Set the value of the attribute called by method_missing?
|
1089
|
-
def attribute_method=(method,
|
1090
|
-
@@logger.debug("stub: called attribute_method=(#{method.inspect}, #{
|
1091
|
-
# Copy input
|
1092
|
-
begin
|
1093
|
-
value = arg.dup
|
1094
|
-
rescue TypeError
|
1095
|
-
value = arg.to_s
|
1096
|
-
end
|
1097
|
-
|
1123
|
+
def attribute_method=(method, value)
|
1124
|
+
@@logger.debug("stub: called attribute_method=(#{method.inspect}, #{value.inspect})")
|
1098
1125
|
# Get the attr and clean up the input
|
1099
1126
|
attr = @attr_methods[method]
|
1100
|
-
|
1101
|
-
|
1127
|
+
@@logger.debug("attribute_method=(#{method.inspect}, #{value.inspect}): method maps to #{attr}")
|
1128
|
+
#value = attribute_input_handler(attr, value)
|
1102
1129
|
# Assign the value
|
1103
1130
|
@data[attr] = value
|
1104
1131
|
|
1105
1132
|
# Return a copy of what got saved
|
1106
1133
|
@@logger.debug("stub: exitting attribute_method=")
|
1107
|
-
return @data[attr]
|
1134
|
+
return @data[attr]
|
1108
1135
|
end
|
1109
1136
|
|
1110
1137
|
|
@@ -1119,8 +1146,10 @@ module ActiveLDAP
|
|
1119
1146
|
end
|
1120
1147
|
aliases = Base.schema.attribute_aliases(attr)
|
1121
1148
|
aliases.each do |ali|
|
1149
|
+
@@logger.debug("associating #{ali} --> #{attr}")
|
1122
1150
|
@attr_methods[ali] = attr
|
1123
1151
|
end
|
1152
|
+
@@logger.debug("stub: leaving define_attribute_methods(#{attr.inspect})")
|
1124
1153
|
end
|
1125
1154
|
|
1126
1155
|
# array_of
|
data/lib/activeldap/schema2.rb
CHANGED
@@ -3,8 +3,9 @@ require 'ldap/schema'
|
|
3
3
|
|
4
4
|
module LDAP
|
5
5
|
class Schema2 < Schema
|
6
|
-
@@
|
7
|
-
|
6
|
+
@@attr_cache = {}
|
7
|
+
@@class_cache = {}
|
8
|
+
|
8
9
|
# attr
|
9
10
|
#
|
10
11
|
# This is just like LDAP::Schema#attr except that it allows
|
@@ -18,19 +19,19 @@ module LDAP
|
|
18
19
|
return '' if at.empty?
|
19
20
|
|
20
21
|
# Check already parsed options first
|
21
|
-
if @@
|
22
|
-
and @@
|
23
|
-
and @@
|
24
|
-
return @@
|
22
|
+
if @@attr_cache.has_key? sub \
|
23
|
+
and @@attr_cache[sub].has_key? type \
|
24
|
+
and @@attr_cache[sub][type].has_key? at
|
25
|
+
return @@attr_cache[sub][type][at].dup
|
25
26
|
end
|
26
27
|
|
27
28
|
# Initialize anything that is required
|
28
|
-
unless @@
|
29
|
-
@@
|
29
|
+
unless @@attr_cache.has_key? sub
|
30
|
+
@@attr_cache[sub] = {}
|
30
31
|
end
|
31
32
|
|
32
|
-
unless @@
|
33
|
-
@@
|
33
|
+
unless @@attr_cache[sub].has_key? type
|
34
|
+
@@attr_cache[sub][type] = {}
|
34
35
|
end
|
35
36
|
|
36
37
|
at = at.upcase
|
@@ -47,31 +48,31 @@ module LDAP
|
|
47
48
|
multi = ''
|
48
49
|
case line
|
49
50
|
when /#{at}\s+[\)A-Z]/
|
50
|
-
@@
|
51
|
+
@@attr_cache[sub][type][at] = ['TRUE']
|
51
52
|
return ['TRUE']
|
52
53
|
when /#{at}\s+'(.+?)'/
|
53
|
-
@@
|
54
|
+
@@attr_cache[sub][type][at] = [$1]
|
54
55
|
return [$1]
|
55
56
|
when /#{at}\s+\((.+?)\)/
|
56
57
|
multi = $1
|
57
58
|
when /#{at}\s+\(([\w\d\s\.]+)\)/
|
58
59
|
multi = $1
|
59
60
|
when /#{at}\s+([\w\d\.]+)/
|
60
|
-
@@
|
61
|
+
@@attr_cache[sub][type][at] = [$1]
|
61
62
|
return [$1]
|
62
63
|
end
|
63
64
|
# Split up multiple matches
|
64
65
|
# if oc then it is sep'd by $
|
65
66
|
# if attr then bu spaces
|
66
67
|
if multi.match(/\$/)
|
67
|
-
@@
|
68
|
-
return @@
|
68
|
+
@@attr_cache[sub][type][at] = multi.split("$").collect{|attr| attr.strip}
|
69
|
+
return @@attr_cache[sub][type][at].dup
|
69
70
|
elsif not multi.empty?
|
70
|
-
@@
|
71
|
-
return @@
|
71
|
+
@@attr_cache[sub][type][at] = multi.gsub(/'/, '').split(' ').collect{|attr| attr.strip}
|
72
|
+
return @@attr_cache[sub][type][at].dup
|
72
73
|
end
|
73
74
|
end
|
74
|
-
@@
|
75
|
+
@@attr_cache[sub][type][at] = []
|
75
76
|
return []
|
76
77
|
end
|
77
78
|
|
@@ -140,24 +141,63 @@ module LDAP
|
|
140
141
|
|
141
142
|
return false
|
142
143
|
end # binary_required?
|
144
|
+
|
145
|
+
# class_attributes
|
146
|
+
#
|
147
|
+
# Returns an Array of all the valid attributes (but not with full aliases)
|
148
|
+
# for the given objectClass
|
149
|
+
def class_attributes(objc)
|
150
|
+
if @@class_cache.has_key? objc
|
151
|
+
return @@class_cache[objc]
|
152
|
+
end
|
153
|
+
|
154
|
+
# First get all the current level attributes
|
155
|
+
@@class_cache[objc] = attr('objectClasses', objc, 'MUST') + attr('objectClasses', objc, 'MAY')
|
156
|
+
|
157
|
+
# Now add all attributes from the parent object (SUPerclasses)
|
158
|
+
# Hopefully an iterative approach will be pretty speedy
|
159
|
+
# 1. build complete list of SUPs
|
160
|
+
# 2. Add attributes from each
|
161
|
+
sups = attr('objectClasses', objc, 'SUP')
|
162
|
+
loop do
|
163
|
+
start_size = sups.size
|
164
|
+
new_sups = []
|
165
|
+
sups.each do |sup|
|
166
|
+
new_sups += attr('objectClasses', sup, 'SUP')
|
167
|
+
end
|
168
|
+
sups += new_sups
|
169
|
+
sups = sups.uniq
|
170
|
+
break if sups.size == start_size
|
171
|
+
end
|
172
|
+
sups.each do |sup|
|
173
|
+
@@class_cache[objc] += attr('objectClasses', sup, 'MUST') + attr('objectClasses', sup, 'MAY')
|
174
|
+
end
|
175
|
+
|
176
|
+
# Clean out the dupes.
|
177
|
+
@@class_cache[objc] = @@class_cache[objc].uniq
|
178
|
+
|
179
|
+
# Return the cached value
|
180
|
+
return @@class_cache[objc].dup
|
181
|
+
end
|
182
|
+
|
143
183
|
end # Schema2
|
144
184
|
|
145
185
|
class Conn
|
146
186
|
def schema(base = nil, attrs = nil, sec = 0, usec = 0)
|
147
187
|
attrs ||= [
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
188
|
+
'objectClasses',
|
189
|
+
'attributeTypes',
|
190
|
+
'matchingRules',
|
191
|
+
'matchingRuleUse',
|
192
|
+
'dITStructureRules',
|
193
|
+
'dITContentRules',
|
194
|
+
'nameForms',
|
195
|
+
'ldapSyntaxes',
|
156
196
|
]
|
157
197
|
base ||= root_dse(['subschemaSubentry'], sec, usec)[0]['subschemaSubentry'][0]
|
158
198
|
base ||= 'cn=schema'
|
159
199
|
ent = search2(base, LDAP_SCOPE_BASE, '(objectClass=subschema)',
|
160
|
-
|
200
|
+
attrs, false, sec, usec)
|
161
201
|
return Schema2.new(ent[0])
|
162
202
|
end
|
163
203
|
end
|
data/lib/activeldap.rb
CHANGED
@@ -212,7 +212,8 @@
|
|
212
212
|
# what classes are required when creating a new object. Of course, you can leave
|
213
213
|
# that field out to default to ['top'] only. Then you can let each application
|
214
214
|
# choose what objectClasses their objects should have by calling the method e.g.
|
215
|
-
# Group#objectClass=(value)
|
215
|
+
# Group#objectClass=(value) or by modifying the value returned by the accessor,
|
216
|
+
# e.g. Group#objectClass.
|
216
217
|
#
|
217
218
|
# Note that is can be very important to define the default :classes value. Due to
|
218
219
|
# implementation choices with most LDAP servers, once an object is created, its
|
@@ -259,28 +260,26 @@
|
|
259
260
|
# Group objects that a user is in.
|
260
261
|
#
|
261
262
|
# irb> me = User.new('drewry')
|
262
|
-
# => ...
|
263
263
|
# irb> me.groups
|
264
|
-
# => ["cdrom", "audio", "develop"]
|
265
|
-
#
|
266
|
-
# Methods created with belongs_to also take an optional argument: objects. This
|
267
|
-
# argument specifies whether it will return the value of the 'dnattr' of the
|
268
|
-
# objects, or whether it will return Group objects.
|
269
|
-
#
|
270
|
-
# irb> me.groups(true)
|
271
264
|
# => [#<Group:0x000001 ...>, #<Group:0x000002 ...>, ...]
|
272
265
|
# irb> me.groups(true).each { |group| p group.cn };nil
|
273
266
|
# "cdrom"
|
274
267
|
# "audio"
|
275
268
|
# "develop"
|
276
269
|
# => nil
|
277
|
-
#
|
278
270
|
# (Note: nil is just there to make the output cleaner...)
|
279
271
|
#
|
272
|
+
# Methods created with belongs_to also take an optional argument: objects. This
|
273
|
+
# argument specifies whether it will return the value of the 'dnattr' of the
|
274
|
+
# objects, or whether it will return Group objects.
|
275
|
+
#
|
276
|
+
# irb> me.groups(false)
|
277
|
+
# => ["cdrom", "audio", "develop"]
|
278
|
+
#
|
280
279
|
# TIP: If you weren't sure what the distinguished name attribute was for Group,
|
281
280
|
# you could also do the following:
|
282
281
|
#
|
283
|
-
# irb> me.groups
|
282
|
+
# irb> me.groups.each { |group| p group.dnattr };nil
|
284
283
|
# "cdrom"
|
285
284
|
# "audio"
|
286
285
|
# "develop"
|
@@ -328,9 +327,9 @@
|
|
328
327
|
# irb> develop = Group.new('develop')
|
329
328
|
# => ...
|
330
329
|
# irb> develop.members
|
331
|
-
# => ["drewry", "builder"]
|
332
|
-
# irb> develop.members(true)
|
333
330
|
# => [#<User:0x000001 ...>, #<User:...>]
|
331
|
+
# irb> develop.members(false)
|
332
|
+
# => ["drewry", "builder"]
|
334
333
|
#
|
335
334
|
#
|
336
335
|
# The arguments for has_many follow the exact same idea that belongs_to's
|
@@ -392,11 +391,13 @@
|
|
392
391
|
# * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
|
393
392
|
# * :scope defaults to LDAP::LDAP_SCOPE_SUBTREE. Usually you won't need to change it
|
394
393
|
# * :attrs defaults to [] and is the list of attrs you want back. Empty means all of them.
|
395
|
-
#
|
394
|
+
#
|
396
395
|
# ==== #validate
|
397
396
|
#
|
398
397
|
# validate is a method that verifies that all attributes that are required by the
|
399
|
-
# objects current objectClasses are populated. This
|
398
|
+
# objects current objectClasses are populated. This also will call the
|
399
|
+
# private "#enforce_types" method. This will make sure that all values defined are
|
400
|
+
# valid to be written to LDAP. #validate is called by #write prior to
|
400
401
|
# performing any action. Its explicit use in an application is unnecessary, and
|
401
402
|
# it may become a private method in the future.
|
402
403
|
#
|
@@ -536,6 +537,11 @@
|
|
536
537
|
# LDAP server could be created. Check you configuration.rb, Base.connect
|
537
538
|
# arguments, and network connectivity! Also check your LDAP server logs to see
|
538
539
|
# if it ever saw the request.
|
540
|
+
#
|
541
|
+
# ==== ObjectClassError
|
542
|
+
#
|
543
|
+
# This exception is raised when an object class is used that is not defined
|
544
|
+
# in the schema.
|
539
545
|
#
|
540
546
|
# === Others
|
541
547
|
#
|
@@ -752,26 +758,26 @@
|
|
752
758
|
# and everything should work well.
|
753
759
|
#
|
754
760
|
#
|
755
|
-
# ====
|
761
|
+
# ==== Non-array results for single values
|
756
762
|
#
|
757
|
-
# Even though Ruby/ActiveLDAP
|
758
|
-
#
|
759
|
-
#
|
760
|
-
#
|
763
|
+
# Even though Ruby/ActiveLDAP attempts to maintain programmatic ease by
|
764
|
+
# returning Array values only. By specifying 'false' as an argument to
|
765
|
+
# any attribute method you will get back a String if it is single value.
|
766
|
+
# This is useful when you are just dumping values for human reading.
|
767
|
+
# Here's an example:
|
761
768
|
#
|
762
769
|
# irb> user = User.new('drewry')
|
763
770
|
# => ...
|
764
|
-
# irb> user.cn(
|
765
|
-
# =>
|
771
|
+
# irb> user.cn(false)
|
772
|
+
# => "Will Drewry"
|
766
773
|
#
|
767
|
-
# That's it. Now you can
|
768
|
-
# wrappers.
|
774
|
+
# That's it. Now you can make human-readable output faster.
|
769
775
|
#
|
770
776
|
# ==== Dynamic attribute crawling
|
771
777
|
#
|
772
|
-
# If you use tab completion in irb, you'll notice that you can
|
773
|
-
#
|
774
|
-
# #attributes
|
778
|
+
# If you use tab completion in irb, you'll notice that you /can/ tab complete the dynamic
|
779
|
+
# attribute methods. You can still see which methods are for attributes using
|
780
|
+
# Base#attributes:
|
775
781
|
#
|
776
782
|
# irb> d = Group.new('develop')
|
777
783
|
# => ...
|
@@ -892,7 +898,7 @@ require 'activeldap/configuration'
|
|
892
898
|
require 'activeldap/schema2'
|
893
899
|
|
894
900
|
module ActiveLDAP
|
895
|
-
VERSION = "0.
|
901
|
+
VERSION = "0.5.0"
|
896
902
|
end
|
897
903
|
|
898
904
|
ActiveLDAP::Base.class_eval do
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-activeldap
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2004-10-
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2004-10-21
|
8
8
|
summary: Ruby/ActiveLDAP is a object-oriented API to LDAP
|
9
9
|
require_paths:
|
10
10
|
- lib
|