treequel 1.2.2 → 1.3.0pre384
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.tar.gz.sig +0 -0
- data/ChangeLog +3374 -0
- data/History.md +39 -0
- data/LICENSE +27 -0
- data/README.md +25 -2
- data/Rakefile +64 -29
- data/bin/treequel +16 -25
- data/bin/treewhat +318 -0
- data/lib/treequel.rb +13 -3
- data/lib/treequel/behavior/control.rb +40 -0
- data/lib/treequel/branch.rb +56 -28
- data/lib/treequel/branchset.rb +10 -2
- data/lib/treequel/controls/pagedresults.rb +4 -2
- data/lib/treequel/directory.rb +40 -50
- data/lib/treequel/exceptions.rb +18 -0
- data/lib/treequel/mixins.rb +44 -14
- data/lib/treequel/model.rb +338 -21
- data/lib/treequel/model/errors.rb +79 -0
- data/lib/treequel/model/objectclass.rb +26 -2
- data/lib/treequel/model/schemavalidations.rb +69 -0
- data/lib/treequel/monkeypatches.rb +99 -17
- data/lib/treequel/schema.rb +19 -5
- data/spec/lib/constants.rb +20 -2
- data/spec/lib/helpers.rb +25 -8
- data/spec/treequel/branch_spec.rb +73 -10
- data/spec/treequel/controls/contentsync_spec.rb +2 -11
- data/spec/treequel/controls/pagedresults_spec.rb +25 -9
- data/spec/treequel/controls/sortedresults_spec.rb +8 -10
- data/spec/treequel/directory_spec.rb +74 -63
- data/spec/treequel/model/errors_spec.rb +77 -0
- data/spec/treequel/model/objectclass_spec.rb +107 -35
- data/spec/treequel/model/schemavalidations_spec.rb +112 -0
- data/spec/treequel/model_spec.rb +294 -81
- data/spec/treequel/monkeypatches_spec.rb +49 -3
- metadata +28 -16
- metadata.gz.sig +0 -0
- data/spec/lib/control_behavior.rb +0 -47
data/lib/treequel.rb
CHANGED
@@ -25,7 +25,7 @@ end
|
|
25
25
|
|
26
26
|
# A library for interacting with LDAP modelled after Sequel.
|
27
27
|
#
|
28
|
-
# @version 1.
|
28
|
+
# @version 1.3.0
|
29
29
|
#
|
30
30
|
# @example
|
31
31
|
# # Connect to the directory at the specified URL
|
@@ -53,10 +53,10 @@ end
|
|
53
53
|
module Treequel
|
54
54
|
|
55
55
|
# Library version
|
56
|
-
VERSION = '1.
|
56
|
+
VERSION = '1.3.0'
|
57
57
|
|
58
58
|
# VCS revision
|
59
|
-
REVISION = %q$Revision:
|
59
|
+
REVISION = %q$Revision: 554028334395 $
|
60
60
|
|
61
61
|
# Common paths for ldap.conf
|
62
62
|
COMMON_LDAP_CONF_PATHS = %w[
|
@@ -80,6 +80,16 @@ module Treequel
|
|
80
80
|
|
81
81
|
|
82
82
|
### Logging
|
83
|
+
# Log levels
|
84
|
+
LOG_LEVELS = {
|
85
|
+
'debug' => Logger::DEBUG,
|
86
|
+
'info' => Logger::INFO,
|
87
|
+
'warn' => Logger::WARN,
|
88
|
+
'error' => Logger::ERROR,
|
89
|
+
'fatal' => Logger::FATAL,
|
90
|
+
}.freeze
|
91
|
+
LOG_LEVEL_NAMES = LOG_LEVELS.invert.freeze
|
92
|
+
|
83
93
|
@default_logger = Logger.new( $stderr )
|
84
94
|
@default_logger.level = $DEBUG ? Logger::DEBUG : Logger::WARN
|
85
95
|
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
|
5
|
+
require 'treequel'
|
6
|
+
require 'treequel/control'
|
7
|
+
|
8
|
+
|
9
|
+
# This is a shared behavior for specs which different Treequel::Controls share in
|
10
|
+
# common. If you're creating a Treequel::Control implementation, you can test
|
11
|
+
# its conformity to the expectations placed on them by adding this to your spec:
|
12
|
+
#
|
13
|
+
# require 'treequel/behavior/control'
|
14
|
+
#
|
15
|
+
# describe YourControl do
|
16
|
+
#
|
17
|
+
# it_should_behave_like "A Treequel::Control"
|
18
|
+
#
|
19
|
+
# end
|
20
|
+
|
21
|
+
shared_examples_for "A Treequel::Control" do
|
22
|
+
|
23
|
+
let( :control ) do
|
24
|
+
described_class
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
it "implements one of either #get_client_controls or #get_server_controls" do
|
29
|
+
methods = [
|
30
|
+
'get_client_controls', # 1.8.x
|
31
|
+
'get_server_controls',
|
32
|
+
:get_client_controls, # 1.9.x
|
33
|
+
:get_server_controls
|
34
|
+
]
|
35
|
+
(control.instance_methods( false ) | methods).should_not be_empty()
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
data/lib/treequel/branch.rb
CHANGED
@@ -38,11 +38,17 @@ class Treequel::Branch
|
|
38
38
|
# [Boolean] Whether or not to include operational attributes by default.
|
39
39
|
@include_operational_attrs = false
|
40
40
|
|
41
|
+
# [Boolean] Whether or not to freeze values cached in @values. This helps
|
42
|
+
# prevent you from accidentally doing branch[:attr] << 'value', which
|
43
|
+
# modifies the cached values, but not the entry.
|
44
|
+
@freeze_converted_values = true
|
45
|
+
|
41
46
|
# Whether or not to include operational attributes when fetching the
|
42
47
|
# entry for branches.
|
43
48
|
class << self
|
44
49
|
extend Treequel::AttributeDeclarations
|
45
50
|
predicate_attr :include_operational_attrs
|
51
|
+
predicate_attr :freeze_converted_values
|
46
52
|
end
|
47
53
|
|
48
54
|
|
@@ -54,8 +60,12 @@ class Treequel::Branch
|
|
54
60
|
### @return [Treequel::Branch] The new branch object.
|
55
61
|
def self::new_from_entry( entry, directory )
|
56
62
|
entry = Treequel::HashUtilities.stringify_keys( entry )
|
57
|
-
|
58
|
-
|
63
|
+
dnvals = entry.delete( 'dn' ) or
|
64
|
+
raise ArgumentError, "no 'dn' attribute for entry"
|
65
|
+
|
66
|
+
Treequel.logger.debug "Creating Branch from entry: %p in directory: %s" %
|
67
|
+
[ dnvals.first, directory ]
|
68
|
+
return self.new( directory, dnvals.first, entry )
|
59
69
|
end
|
60
70
|
|
61
71
|
|
@@ -72,6 +82,7 @@ class Treequel::Branch
|
|
72
82
|
### @param [String] dn The DN of the entry the Branch is wrapping.
|
73
83
|
### @param [LDAP::Entry, Hash] entry The entry object if it's already been fetched.
|
74
84
|
def initialize( directory, dn, entry=nil )
|
85
|
+
raise ArgumentError, "nil DN" unless dn
|
75
86
|
raise ArgumentError, "invalid DN" unless
|
76
87
|
dn.match( Patterns::DISTINGUISHED_NAME ) || dn.empty?
|
77
88
|
raise ArgumentError, "can't cast a %s to an LDAP::Entry" % [entry.class.name] unless
|
@@ -93,7 +104,7 @@ class Treequel::Branch
|
|
93
104
|
######
|
94
105
|
|
95
106
|
# Delegate some other methods to a new Branchset via the #branchset method
|
96
|
-
def_method_delegators :branchset, :filter, :scope, :select, :limit, :timeout, :order
|
107
|
+
def_method_delegators :branchset, :filter, :scope, :select, :limit, :timeout, :order, :as
|
97
108
|
|
98
109
|
# Delegate some methods to the Branch's directory via its accessor
|
99
110
|
def_method_delegators :directory, :controls, :referrals
|
@@ -204,7 +215,8 @@ class Treequel::Branch
|
|
204
215
|
### Return the Branch's immediate parent node.
|
205
216
|
### @return [Treequel::Branch]
|
206
217
|
def parent
|
207
|
-
|
218
|
+
pardn = self.parent_dn or return nil
|
219
|
+
return self.class.new( self.directory, pardn )
|
208
220
|
end
|
209
221
|
|
210
222
|
|
@@ -267,7 +279,7 @@ class Treequel::Branch
|
|
267
279
|
self.log.debug " making LDIF from an entry: %p" % [ entry ]
|
268
280
|
|
269
281
|
entry.keys.reject {|k| k == 'dn' }.each do |attribute|
|
270
|
-
entry[
|
282
|
+
Array( entry[attribute] ).each do |val|
|
271
283
|
ldif << ldif_for_attr( attribute, val, width )
|
272
284
|
end
|
273
285
|
end
|
@@ -299,14 +311,16 @@ class Treequel::Branch
|
|
299
311
|
def []( attrname )
|
300
312
|
attrsym = attrname.to_sym
|
301
313
|
|
302
|
-
|
314
|
+
if @values.key?( attrsym )
|
315
|
+
self.log.debug " value for %p is cached (%p)." % [ attrname, @values[attrsym] ]
|
316
|
+
else
|
303
317
|
self.log.debug " value for %p is NOT cached." % [ attrsym ]
|
304
318
|
value = self.get_converted_object( attrsym )
|
305
319
|
self.log.debug " converted value is: %p" % [ value ]
|
306
|
-
value.freeze if
|
320
|
+
value.freeze if
|
321
|
+
self.class.freeze_converted_values? &&
|
322
|
+
value.respond_to?( :freeze )
|
307
323
|
@values[ attrsym ] = value
|
308
|
-
else
|
309
|
-
self.log.debug " value for %p is cached." % [ attrname ]
|
310
324
|
end
|
311
325
|
|
312
326
|
return @values[ attrsym ]
|
@@ -366,23 +380,33 @@ class Treequel::Branch
|
|
366
380
|
###
|
367
381
|
### @return [TrueClass] if the delete succeeded
|
368
382
|
def delete( *attributes )
|
383
|
+
|
384
|
+
# If no attributes are given, delete the whole entry
|
369
385
|
if attributes.empty?
|
370
386
|
self.log.info "No attributes specified; deleting entire entry for %s" % [ self.dn ]
|
371
387
|
self.directory.delete( self )
|
388
|
+
|
389
|
+
# Otherwise, gather up the LDAP::Mod objects that will delete the given attributes
|
372
390
|
else
|
373
391
|
self.log.debug "Deleting attributes: %p" % [ attributes ]
|
374
392
|
mods = attributes.flatten.collect do |attribute|
|
393
|
+
|
394
|
+
# Delete particular values of the attribute
|
375
395
|
if attribute.is_a?( Hash )
|
376
396
|
attribute.collect do |key,vals|
|
377
|
-
vals =
|
397
|
+
vals = [ vals ] unless vals.is_a?( Array )
|
398
|
+
vals.collect! {|val| self.get_converted_attribute(key, val) }
|
378
399
|
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, key.to_s, vals )
|
379
400
|
end
|
401
|
+
|
402
|
+
# Delete all values of the attribute
|
380
403
|
else
|
381
404
|
LDAP::Mod.new( LDAP::LDAP_MOD_DELETE, attribute.to_s, [] )
|
382
405
|
end
|
383
|
-
end.flatten
|
384
406
|
|
385
|
-
|
407
|
+
end
|
408
|
+
|
409
|
+
self.directory.modify( self, mods.flatten )
|
386
410
|
end
|
387
411
|
|
388
412
|
self.clear_caches
|
@@ -394,9 +418,10 @@ class Treequel::Branch
|
|
394
418
|
### Create the entry for this Branch with the specified +attributes+. The +attributes+ should,
|
395
419
|
### at a minimum, contain the pair `:objectClass => :someStructuralObjectClass`.
|
396
420
|
###
|
397
|
-
### @
|
421
|
+
### @see Treequel::Directory#create
|
398
422
|
def create( attributes={} )
|
399
423
|
self.directory.create( self, attributes )
|
424
|
+
self.clear_caches
|
400
425
|
return self
|
401
426
|
end
|
402
427
|
|
@@ -432,7 +457,10 @@ class Treequel::Branch
|
|
432
457
|
### @param [Hash<String, Symbol => Object>] attributes
|
433
458
|
def move( rdn )
|
434
459
|
self.log.debug "Asking the directory to move me to an entry called %p" % [ rdn ]
|
435
|
-
|
460
|
+
self.directory.move( self, rdn )
|
461
|
+
self.clear_caches
|
462
|
+
|
463
|
+
return self
|
436
464
|
end
|
437
465
|
|
438
466
|
|
@@ -493,9 +521,8 @@ class Treequel::Branch
|
|
493
521
|
schema = self.directory.schema
|
494
522
|
|
495
523
|
oc_oids = self[:objectClass] || []
|
496
|
-
self.log.debug " objectClass OIDs are: %p" % [ oc_oids ]
|
497
524
|
oc_oids |= additional_classes.collect {|str| str.to_sym }
|
498
|
-
oc_oids <<
|
525
|
+
oc_oids << 'top' if oc_oids.empty?
|
499
526
|
|
500
527
|
oclasses = []
|
501
528
|
oc_oids.each do |oid|
|
@@ -504,7 +531,7 @@ class Treequel::Branch
|
|
504
531
|
oclasses << oc
|
505
532
|
end
|
506
533
|
|
507
|
-
self.log.debug " found %d objectClasses: %p" % [ oclasses.length, oclasses ]
|
534
|
+
self.log.debug " found %d objectClasses: %p" % [ oclasses.length, oclasses.map(&:name) ]
|
508
535
|
return oclasses.uniq
|
509
536
|
end
|
510
537
|
|
@@ -539,7 +566,7 @@ class Treequel::Branch
|
|
539
566
|
[ oclasses.map(&:name) ]
|
540
567
|
|
541
568
|
oclasses.each do |oc|
|
542
|
-
self.log.debug " adding %p from %p" % [ oc.must, oc ]
|
569
|
+
self.log.debug " adding %p from %p" % [ oc.must.map(&:name), oc.name ]
|
543
570
|
types |= oc.must
|
544
571
|
end
|
545
572
|
|
@@ -571,10 +598,10 @@ class Treequel::Branch
|
|
571
598
|
attrhash = {}
|
572
599
|
|
573
600
|
self.must_attribute_types( *additional_object_classes ).each do |attrtype|
|
574
|
-
self.log.debug " adding attrtype %p to the MUST attributes hash" % [ attrtype ]
|
601
|
+
# self.log.debug " adding attrtype %p to the MUST attributes hash" % [ attrtype.name ]
|
575
602
|
|
576
603
|
if attrtype.name == :objectClass
|
577
|
-
attrhash[ :objectClass ] = [
|
604
|
+
attrhash[ :objectClass ] = ['top'] | additional_object_classes
|
578
605
|
elsif attrtype.single?
|
579
606
|
attrhash[ attrtype.name ] = ''
|
580
607
|
else
|
@@ -625,7 +652,7 @@ class Treequel::Branch
|
|
625
652
|
attrhash = {}
|
626
653
|
|
627
654
|
self.may_attribute_types( *additional_object_classes ).each do |attrtype|
|
628
|
-
self.log.debug " adding attrtype %p to the MAY attributes hash" % [ attrtype ]
|
655
|
+
# self.log.debug " adding attrtype %p to the MAY attributes hash" % [ attrtype.named ]
|
629
656
|
|
630
657
|
if attrtype.single?
|
631
658
|
attrhash[ attrtype.name ] = nil
|
@@ -772,15 +799,16 @@ class Treequel::Branch
|
|
772
799
|
### Get the value associated with +attrsym+, convert it to a Ruby object if the Branch's
|
773
800
|
### directory has a conversion rule, and return it.
|
774
801
|
def get_converted_object( attrsym )
|
775
|
-
|
776
|
-
value = self.entry[ attrsym.to_s ] or return nil
|
802
|
+
value = self.entry ? self.entry[ attrsym.to_s ] : nil
|
777
803
|
|
778
804
|
if attribute = self.directory.schema.attribute_types[ attrsym ]
|
805
|
+
syntax_oid = attribute.syntax.oid
|
806
|
+
|
779
807
|
if attribute.single?
|
780
|
-
value = self.directory.convert_to_object(
|
808
|
+
value = self.directory.convert_to_object( syntax_oid, value.first ) if value
|
781
809
|
else
|
782
|
-
value = value.collect do |raw|
|
783
|
-
self.directory.convert_to_object(
|
810
|
+
value = Array( value ).collect do |raw|
|
811
|
+
self.directory.convert_to_object( syntax_oid, raw )
|
784
812
|
end
|
785
813
|
end
|
786
814
|
else
|
@@ -795,8 +823,8 @@ class Treequel::Branch
|
|
795
823
|
### and return it.
|
796
824
|
def get_converted_attribute( attrsym, object )
|
797
825
|
if attribute = self.directory.schema.attribute_types[ attrsym ]
|
798
|
-
self.log.debug "converting %p object to a %p attribute" %
|
799
|
-
[ attrsym, attribute.syntax.desc ]
|
826
|
+
self.log.debug "converting %p object (a %p) to a %p attribute" %
|
827
|
+
[ attrsym, object.class, attribute.syntax.desc ]
|
800
828
|
return self.directory.convert_to_attribute( attribute.syntax.oid, object )
|
801
829
|
else
|
802
830
|
self.log.info "no attributeType for %p" % [ attrsym ]
|
data/lib/treequel/branchset.rb
CHANGED
@@ -77,9 +77,17 @@ class Treequel::Branchset
|
|
77
77
|
def initialize( branch, options={} )
|
78
78
|
@branch = branch
|
79
79
|
@options = DEFAULT_OPTIONS.merge( options )
|
80
|
+
self.log.debug "Setting up %p for branch %p with options: %p" %
|
81
|
+
[ self.class, @branch, @options ]
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
+
if @branch.directory.registered_controls.empty?
|
84
|
+
self.log.debug " no registered controls."
|
85
|
+
else
|
86
|
+
@branch.directory.registered_controls.each do |control|
|
87
|
+
self.log.debug " extending with %p" % [ control ]
|
88
|
+
self.extend( control )
|
89
|
+
end
|
90
|
+
end
|
83
91
|
|
84
92
|
super()
|
85
93
|
end
|
@@ -47,7 +47,7 @@ module Treequel::PagedResultsControl
|
|
47
47
|
|
48
48
|
### Add the control's instance variables to including Branchsets.
|
49
49
|
def initialize
|
50
|
-
@paged_results_cookie =
|
50
|
+
@paged_results_cookie = nil
|
51
51
|
@paged_results_setsize = nil
|
52
52
|
end
|
53
53
|
|
@@ -75,8 +75,10 @@ module Treequel::PagedResultsControl
|
|
75
75
|
newset = self.clone
|
76
76
|
|
77
77
|
if setsize.nil? || setsize.zero?
|
78
|
+
self.log.debug "Removing paged results control."
|
78
79
|
newset.paged_results_setsize = nil
|
79
80
|
else
|
81
|
+
self.log.debug "Adding paged results control with page size = %d." % [ setsize ]
|
80
82
|
newset.paged_results_setsize = setsize
|
81
83
|
end
|
82
84
|
|
@@ -96,7 +98,7 @@ module Treequel::PagedResultsControl
|
|
96
98
|
### Remove any paging control associated with the receiving Branchset.
|
97
99
|
### @return [void]
|
98
100
|
def without_paging!
|
99
|
-
self.paged_results_cookie =
|
101
|
+
self.paged_results_cookie = nil
|
100
102
|
self.paged_results_setsize = nil
|
101
103
|
end
|
102
104
|
|
data/lib/treequel/directory.rb
CHANGED
@@ -99,7 +99,7 @@ class Treequel::Directory
|
|
99
99
|
|
100
100
|
|
101
101
|
#################################################################
|
102
|
-
###
|
102
|
+
### I N S T A N C E M E T H O D S
|
103
103
|
#################################################################
|
104
104
|
|
105
105
|
### Create a new Treequel::Directory with the given +options+. Options is a hash with one
|
@@ -132,14 +132,14 @@ class Treequel::Directory
|
|
132
132
|
@conn = nil
|
133
133
|
@bound_user = nil
|
134
134
|
|
135
|
-
@base_dn = options[:base_dn] || self.get_default_base_dn
|
136
|
-
|
137
|
-
@base = nil
|
138
135
|
|
139
136
|
@object_conversions = DEFAULT_OBJECT_CONVERSIONS.dup
|
140
137
|
@attribute_conversions = DEFAULT_ATTRIBUTE_CONVERSIONS.dup
|
141
138
|
@registered_controls = []
|
142
139
|
|
140
|
+
@base_dn = options[:base_dn] || self.get_default_base_dn
|
141
|
+
@base = nil
|
142
|
+
|
143
143
|
# Immediately bind if credentials are passed to the initializer.
|
144
144
|
if ( options[:bind_dn] && options[:pass] )
|
145
145
|
self.bind( options[:bind_dn], options[:pass] )
|
@@ -155,7 +155,7 @@ class Treequel::Directory
|
|
155
155
|
def_method_delegators :base, *DELEGATED_BRANCH_METHODS
|
156
156
|
|
157
157
|
# Delegate some methods to the connection via the #conn method
|
158
|
-
def_method_delegators :conn, :controls, :referrals
|
158
|
+
def_method_delegators :conn, :controls, :referrals
|
159
159
|
|
160
160
|
|
161
161
|
# The host to connect to.
|
@@ -187,6 +187,12 @@ class Treequel::Directory
|
|
187
187
|
attr_reader :bound_user
|
188
188
|
|
189
189
|
|
190
|
+
### Fetch the root DSE as a Treequel::Branch.
|
191
|
+
def root_dse
|
192
|
+
return self.search( '', :base, '(objectClass=*)', :selectattrs => ['+'] ).first
|
193
|
+
end
|
194
|
+
|
195
|
+
|
190
196
|
### Fetch the Branch for the base node of the directory.
|
191
197
|
### @return [Treequel::Branch]
|
192
198
|
def base
|
@@ -231,6 +237,23 @@ class Treequel::Directory
|
|
231
237
|
end
|
232
238
|
|
233
239
|
|
240
|
+
### Drop the existing connection and establish a new one.
|
241
|
+
### @return [Boolean] +true+ if the connection was re-established
|
242
|
+
### @raise [RuntimeError] if the re-connection failed
|
243
|
+
def reconnect
|
244
|
+
self.log.info "Reconnecting to %s..." % [ self.uri ]
|
245
|
+
@conn = self.connect
|
246
|
+
self.log.info "...reconnected."
|
247
|
+
|
248
|
+
return true
|
249
|
+
rescue LDAP::ResultError => err
|
250
|
+
self.log.error "%s while attempting to reconnect to %s: %s" %
|
251
|
+
[ err.class.name, self.uri, err.message ]
|
252
|
+
raise "Couldn't reconnect to %s: %s: %s" %
|
253
|
+
[ self.uri, err.class.name, err.message ]
|
254
|
+
end
|
255
|
+
|
256
|
+
|
234
257
|
### Return the URI object that corresponds to the directory.
|
235
258
|
### @return [URI::LDAP]
|
236
259
|
def uri
|
@@ -457,7 +480,7 @@ class Treequel::Directory
|
|
457
480
|
self.log.debug "Modifying %s with LDAP mod objects: %p" % [ branch.dn, mods ]
|
458
481
|
self.conn.modify( branch.dn, mods )
|
459
482
|
else
|
460
|
-
normattrs =
|
483
|
+
normattrs = normalize_attributes( mods )
|
461
484
|
self.log.debug "Modifying %s with: %p" % [ branch.dn, normattrs ]
|
462
485
|
self.conn.modify( branch.dn, normattrs )
|
463
486
|
end
|
@@ -472,28 +495,13 @@ class Treequel::Directory
|
|
472
495
|
|
473
496
|
|
474
497
|
### Create the entry for the given +branch+, setting its attributes to +newattrs+.
|
498
|
+
### @param [Treequel::Branch, #to_s] branch the branch to create (or a DN string)
|
499
|
+
### @param [Hash, Array<LDAP::Mod>] newattrs the attributes to create the entry with. This
|
500
|
+
### can be either a Hash of attributes, or an Array of
|
501
|
+
### LDAP::Mod objects.
|
475
502
|
def create( branch, newattrs={} )
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
# Merge RDN attributes with existing ones, combining any that exist in both
|
480
|
-
self.log.debug "Smushing rdn attributes %p into %p" % [ branch.rdn_attributes, newdn ]
|
481
|
-
newattrs.merge!( branch.rdn_attributes ) do |key, *values|
|
482
|
-
values.flatten.uniq
|
483
|
-
end
|
484
|
-
|
485
|
-
normattrs = self.normalize_attributes( newattrs )
|
486
|
-
raise ArgumentError, "Can't create an entry with no objectClasses" unless
|
487
|
-
normattrs.key?( 'objectClass' )
|
488
|
-
normattrs['objectClass'].each do |oc|
|
489
|
-
raise ArgumentError, "No such objectClass #{oc.inspect}" unless
|
490
|
-
schema.object_classes.key?(oc.to_sym)
|
491
|
-
end
|
492
|
-
raise ArgumentError, "Can't create an entry with no structural objectClass" unless
|
493
|
-
normattrs['objectClass'].any? {|oc| schema.object_classes[oc.to_sym].structural? }
|
494
|
-
|
495
|
-
self.log.debug "Creating an entry at %s with the attributes: %p" % [ newdn, normattrs ]
|
496
|
-
self.conn.add( newdn, normattrs )
|
503
|
+
newattrs = normalize_attributes( newattrs ) if newattrs.is_a?( Hash )
|
504
|
+
self.conn.add( branch.to_s, newattrs )
|
497
505
|
|
498
506
|
return true
|
499
507
|
end
|
@@ -565,6 +573,7 @@ class Treequel::Directory
|
|
565
573
|
end
|
566
574
|
end
|
567
575
|
end
|
576
|
+
alias_method :register_control, :register_controls
|
568
577
|
|
569
578
|
|
570
579
|
### Map the specified LDAP +attribute+ to its Ruby datatype if one is registered for the given
|
@@ -605,7 +614,7 @@ class Treequel::Directory
|
|
605
614
|
### Return an Array of OID strings representing the controls supported by the Directory,
|
606
615
|
### as listed in the directory's root DSE.
|
607
616
|
def supported_control_oids
|
608
|
-
return self.
|
617
|
+
return self.root_dse[:supportedControl]
|
609
618
|
end
|
610
619
|
|
611
620
|
|
@@ -620,7 +629,7 @@ class Treequel::Directory
|
|
620
629
|
### Return an Array of OID strings representing the extensions supported by the Directory,
|
621
630
|
### as listed in the directory's root DSE.
|
622
631
|
def supported_extension_oids
|
623
|
-
return self.
|
632
|
+
return self.root_dse[:supportedExtension]
|
624
633
|
end
|
625
634
|
|
626
635
|
|
@@ -635,7 +644,7 @@ class Treequel::Directory
|
|
635
644
|
### Return an Array of OID strings representing the features supported by the Directory,
|
636
645
|
### as listed in the directory's root DSE.
|
637
646
|
def supported_feature_oids
|
638
|
-
return self.
|
647
|
+
return self.root_dse[:supportedFeatures]
|
639
648
|
end
|
640
649
|
|
641
650
|
|
@@ -675,9 +684,7 @@ class Treequel::Directory
|
|
675
684
|
|
676
685
|
### Fetch the default base dn for the server from the server's Root DSE.
|
677
686
|
def get_default_base_dn
|
678
|
-
|
679
|
-
return '' if dse.nil? || dse.empty?
|
680
|
-
return dse.first['namingContexts'].first
|
687
|
+
return self.root_dse[:namingContexts].first.dn
|
681
688
|
end
|
682
689
|
|
683
690
|
|
@@ -694,23 +701,6 @@ class Treequel::Directory
|
|
694
701
|
end
|
695
702
|
|
696
703
|
|
697
|
-
### Normalize the attributes in +hash+ to be of the form expected by the
|
698
|
-
### LDAP library (i.e., keys as Strings, values as Arrays of Strings)
|
699
|
-
def normalize_attributes( hash )
|
700
|
-
normhash = {}
|
701
|
-
hash.each do |key,val|
|
702
|
-
val = [ val ] unless val.is_a?( Array )
|
703
|
-
val.collect! {|obj| obj.to_s }
|
704
|
-
|
705
|
-
normhash[ key.to_s ] = val
|
706
|
-
end
|
707
|
-
|
708
|
-
normhash.delete( 'dn' )
|
709
|
-
|
710
|
-
return normhash
|
711
|
-
end
|
712
|
-
|
713
|
-
|
714
704
|
### Normalize the parameters to the #search method into the format expected by
|
715
705
|
### the LDAP::Conn#Search_ext2 method and return them as a Hash.
|
716
706
|
def normalize_search_parameters( base, scope, filter, parameters )
|