treequel 1.2.2 → 1.3.0pre384
Sign up to get free protection for your applications and to get access to all the features.
- 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 )
|