ruby-activeldap 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|