treequel 1.0.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/ChangeLog +354 -0
- data/LICENSE +27 -0
- data/README +66 -0
- data/Rakefile +345 -0
- data/Rakefile.local +43 -0
- data/bin/treeirb +14 -0
- data/bin/treequel +229 -0
- data/examples/company-directory.rb +112 -0
- data/examples/ldap-monitor.rb +143 -0
- data/examples/ldap-monitor/public/css/master.css +328 -0
- data/examples/ldap-monitor/public/images/card_small.png +0 -0
- data/examples/ldap-monitor/public/images/chain_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small.png +0 -0
- data/examples/ldap-monitor/public/images/globe_small_green.png +0 -0
- data/examples/ldap-monitor/public/images/plug.png +0 -0
- data/examples/ldap-monitor/public/images/shadows/large-30-down.png +0 -0
- data/examples/ldap-monitor/public/images/tick.png +0 -0
- data/examples/ldap-monitor/public/images/tick_circle.png +0 -0
- data/examples/ldap-monitor/public/images/treequel-favicon.png +0 -0
- data/examples/ldap-monitor/views/backends.erb +41 -0
- data/examples/ldap-monitor/views/connections.erb +74 -0
- data/examples/ldap-monitor/views/databases.erb +39 -0
- data/examples/ldap-monitor/views/dump_subsystem.erb +14 -0
- data/examples/ldap-monitor/views/index.erb +14 -0
- data/examples/ldap-monitor/views/layout.erb +35 -0
- data/examples/ldap-monitor/views/listeners.erb +30 -0
- data/examples/ldap_state.rb +62 -0
- data/lib/treequel.rb +145 -0
- data/lib/treequel/branch.rb +589 -0
- data/lib/treequel/branchcollection.rb +204 -0
- data/lib/treequel/branchset.rb +360 -0
- data/lib/treequel/constants.rb +604 -0
- data/lib/treequel/directory.rb +541 -0
- data/lib/treequel/exceptions.rb +32 -0
- data/lib/treequel/filter.rb +704 -0
- data/lib/treequel/mixins.rb +325 -0
- data/lib/treequel/schema.rb +245 -0
- data/lib/treequel/schema/attributetype.rb +252 -0
- data/lib/treequel/schema/ldapsyntax.rb +96 -0
- data/lib/treequel/schema/matchingrule.rb +124 -0
- data/lib/treequel/schema/matchingruleuse.rb +124 -0
- data/lib/treequel/schema/objectclass.rb +289 -0
- data/lib/treequel/sequel_integration.rb +26 -0
- data/lib/treequel/utils.rb +169 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/helpers.rb +434 -0
- data/rake/hg.rb +261 -0
- data/rake/manual.rb +782 -0
- data/rake/packaging.rb +135 -0
- data/rake/publishing.rb +318 -0
- data/rake/rdoc.rb +30 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/rake/win32.rb +190 -0
- data/spec/lib/constants.rb +93 -0
- data/spec/lib/helpers.rb +100 -0
- data/spec/treequel/branch_spec.rb +569 -0
- data/spec/treequel/branchcollection_spec.rb +213 -0
- data/spec/treequel/branchset_spec.rb +376 -0
- data/spec/treequel/directory_spec.rb +487 -0
- data/spec/treequel/filter_spec.rb +482 -0
- data/spec/treequel/mixins_spec.rb +330 -0
- data/spec/treequel/schema/attributetype_spec.rb +237 -0
- data/spec/treequel/schema/ldapsyntax_spec.rb +83 -0
- data/spec/treequel/schema/matchingrule_spec.rb +158 -0
- data/spec/treequel/schema/matchingruleuse_spec.rb +137 -0
- data/spec/treequel/schema/objectclass_spec.rb +262 -0
- data/spec/treequel/schema_spec.rb +118 -0
- data/spec/treequel/utils_spec.rb +49 -0
- data/spec/treequel_spec.rb +179 -0
- metadata +169 -0
@@ -0,0 +1,487 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
BEGIN {
|
4
|
+
require 'pathname'
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
6
|
+
|
7
|
+
libdir = basedir + "lib"
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
10
|
+
}
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'spec'
|
14
|
+
require 'spec/lib/constants'
|
15
|
+
require 'spec/lib/helpers'
|
16
|
+
|
17
|
+
require 'treequel/directory'
|
18
|
+
require 'treequel/branch'
|
19
|
+
rescue LoadError
|
20
|
+
unless Object.const_defined?( :Gem )
|
21
|
+
require 'rubygems'
|
22
|
+
retry
|
23
|
+
end
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
include Treequel::TestConstants
|
29
|
+
include Treequel::Constants
|
30
|
+
|
31
|
+
#####################################################################
|
32
|
+
### C O N T E X T S
|
33
|
+
#####################################################################
|
34
|
+
|
35
|
+
describe Treequel::Directory do
|
36
|
+
include Treequel::SpecHelpers
|
37
|
+
|
38
|
+
before( :all ) do
|
39
|
+
setup_logging( :fatal )
|
40
|
+
end
|
41
|
+
|
42
|
+
after( :all ) do
|
43
|
+
reset_logging()
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
before( :each ) do
|
48
|
+
@options = {
|
49
|
+
:host => TEST_HOST,
|
50
|
+
:port => TEST_PORT,
|
51
|
+
:base_dn => TEST_BASE_DN,
|
52
|
+
:connect_type => :plain,
|
53
|
+
}
|
54
|
+
@conn = mock( "LDAP connection", :set_option => true, :bound? => false )
|
55
|
+
LDAP::SSLConn.stub!( :new ).and_return( @conn )
|
56
|
+
@conn.stub!( :root_dse ).and_return( nil )
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
it "is created with reasonable default options if none are specified" do
|
61
|
+
dir = Treequel::Directory.new
|
62
|
+
|
63
|
+
dir.host.should == 'localhost'
|
64
|
+
dir.port.should == 389
|
65
|
+
dir.connect_type.should == :tls
|
66
|
+
dir.base_dn.should == ''
|
67
|
+
end
|
68
|
+
|
69
|
+
it "is created with the specified options if options are specified" do
|
70
|
+
dir = Treequel::Directory.new( @options )
|
71
|
+
|
72
|
+
dir.host.should == TEST_HOST
|
73
|
+
dir.port.should == TEST_PORT
|
74
|
+
dir.connect_type.should == @options[:connect_type]
|
75
|
+
dir.base_dn.should == TEST_BASE_DN
|
76
|
+
end
|
77
|
+
|
78
|
+
it "binds immediately if user/pass is included in the ldap URI" do
|
79
|
+
conn = mock( "LDAP connection", :set_option => true )
|
80
|
+
|
81
|
+
LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
|
82
|
+
and_return( conn )
|
83
|
+
conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
|
84
|
+
|
85
|
+
dir = Treequel::Directory.new( @options.merge( :bind_dn => TEST_BIND_DN, :pass => TEST_BIND_PASS ))
|
86
|
+
dir.instance_variable_get( :@bound_as ).should == TEST_BIND_DN
|
87
|
+
end
|
88
|
+
|
89
|
+
it "uses the first namingContext from the Root DSE if no base is specified" do
|
90
|
+
conn = mock( "LDAP connection", :set_option => true )
|
91
|
+
LDAP::Conn.stub!( :new ).and_return( conn )
|
92
|
+
conn.should_receive( :root_dse ).and_return( TEST_DSE )
|
93
|
+
|
94
|
+
@dir = Treequel::Directory.new( @options.merge(:base_dn => nil) )
|
95
|
+
@dir.base_dn.should == TEST_BASE_DN
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
describe "instances without existing connections" do
|
100
|
+
|
101
|
+
before( :each ) do
|
102
|
+
@dir = Treequel::Directory.new( @options )
|
103
|
+
@conn = mock( "ldap connection", :set_option => true )
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
it "stringifies as a description which includes the host, port, connection type and base" do
|
108
|
+
@dir.to_s.should =~ /#{Regexp.quote(TEST_HOST)}/
|
109
|
+
@dir.to_s.should =~ /#{TEST_PORT}/
|
110
|
+
@dir.to_s.should =~ /\b#{@dir.connect_type}\b/
|
111
|
+
@dir.to_s.should =~ /#{TEST_BASE_DN}/i
|
112
|
+
end
|
113
|
+
|
114
|
+
it "connects on demand to the configured directory server" do
|
115
|
+
LDAP::Conn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
|
116
|
+
and_return( @conn )
|
117
|
+
@dir.conn.should == @conn
|
118
|
+
end
|
119
|
+
|
120
|
+
it "connects with TLS on demand to the configured directory server if configured to do so" do
|
121
|
+
@dir.connect_type = :tls
|
122
|
+
LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT, true ).
|
123
|
+
and_return( @conn )
|
124
|
+
@dir.conn.should == @conn
|
125
|
+
end
|
126
|
+
|
127
|
+
it "connects over SSL on demand to the configured directory server if configured to do so" do
|
128
|
+
@dir.connect_type = :ssl
|
129
|
+
LDAP::SSLConn.should_receive( :new ).with( TEST_HOST, TEST_PORT ).
|
130
|
+
and_return( @conn )
|
131
|
+
@dir.conn.should == @conn
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "instances with a connection" do
|
136
|
+
|
137
|
+
before( :each ) do
|
138
|
+
@conn = mock( "ldap connection", :bound? => false )
|
139
|
+
|
140
|
+
@dir = Treequel::Directory.new( @options )
|
141
|
+
@dir.instance_variable_set( :@conn, @conn )
|
142
|
+
|
143
|
+
@schema = mock( "Directory schema" )
|
144
|
+
@conn.stub!( :schema ).and_return( :the_schema )
|
145
|
+
Treequel::Schema.stub!( :new ).with( :the_schema ).and_return( @schema )
|
146
|
+
@schema.stub!( :attribute_types ).and_return({ :cn => :a_value, :ou => :a_value })
|
147
|
+
end
|
148
|
+
|
149
|
+
it "can bind with the given user DN and password" do
|
150
|
+
@conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
|
151
|
+
@dir.bind( TEST_BIND_DN, TEST_BIND_PASS )
|
152
|
+
end
|
153
|
+
|
154
|
+
it "can bind with the DN of the given Branch (or a quack-alike) and password" do
|
155
|
+
branch = stub( "branch", :dn => TEST_BIND_DN )
|
156
|
+
@conn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
|
157
|
+
@dir.bind( branch, TEST_BIND_PASS )
|
158
|
+
end
|
159
|
+
|
160
|
+
it "can temporarily bind as another user for the duration of a block" do
|
161
|
+
dupconn = mock( "duplicate connection" )
|
162
|
+
@conn.should_receive( :dup ).and_return( dupconn )
|
163
|
+
dupconn.should_receive( :bind ).with( TEST_BIND_DN, TEST_BIND_PASS )
|
164
|
+
@conn.should_not_receive( :bind )
|
165
|
+
|
166
|
+
@dir.bound_as( TEST_BIND_DN, TEST_BIND_PASS ) do
|
167
|
+
@dir.conn.should == dupconn
|
168
|
+
end
|
169
|
+
|
170
|
+
@dir.conn.should == @conn
|
171
|
+
end
|
172
|
+
|
173
|
+
it "knows if its underlying connection is already bound" do
|
174
|
+
@conn.should_receive( :bound? ).and_return( false, true )
|
175
|
+
@dir.should_not be_bound()
|
176
|
+
@dir.should be_bound()
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
it "can be unbound, which replaces the bound connection with a duplicate that is unbound" do
|
181
|
+
dupconn = mock( "duplicate connection" )
|
182
|
+
@conn.should_receive( :bound? ).and_return( true )
|
183
|
+
@conn.should_receive( :dup ).and_return( dupconn )
|
184
|
+
@conn.should_receive( :unbind )
|
185
|
+
|
186
|
+
@dir.unbind
|
187
|
+
|
188
|
+
@dir.conn.should == dupconn
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
it "doesn't do anything if told to unbind but the current connection is not bound" do
|
193
|
+
@conn.should_receive( :bound? ).and_return( false )
|
194
|
+
@conn.should_not_receive( :dup )
|
195
|
+
@conn.should_not_receive( :unbind )
|
196
|
+
|
197
|
+
@dir.unbind
|
198
|
+
|
199
|
+
@dir.conn.should == @conn
|
200
|
+
end
|
201
|
+
|
202
|
+
it "can look up a Branch's corresponding LDAP::Entry hash" do
|
203
|
+
branch = mock( "branch" )
|
204
|
+
|
205
|
+
branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
|
206
|
+
|
207
|
+
@conn.should_receive( :search_ext2 ).
|
208
|
+
with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)' ).
|
209
|
+
and_return([ :the_entry ])
|
210
|
+
|
211
|
+
@dir.get_entry( branch ).should == :the_entry
|
212
|
+
end
|
213
|
+
|
214
|
+
it "can look up a Branch's corresponding LDAP::Entry hash with operational attributes included" do
|
215
|
+
branch = mock( "branch" )
|
216
|
+
|
217
|
+
branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
|
218
|
+
|
219
|
+
@conn.should_receive( :search_ext2 ).
|
220
|
+
with( TEST_PERSON_DN, LDAP::LDAP_SCOPE_BASE, '(objectClass=*)', ['*', '+'] ).
|
221
|
+
and_return([ :the_extended_entry ])
|
222
|
+
|
223
|
+
@dir.get_extended_entry( branch ).should == :the_extended_entry
|
224
|
+
end
|
225
|
+
|
226
|
+
it "can search for entries and return them as Sequel::Branch objects" do
|
227
|
+
base = TEST_PEOPLE_DN
|
228
|
+
filter = '(|(uid=jonlong)(uid=margento))'
|
229
|
+
branch = mock( "branch" )
|
230
|
+
|
231
|
+
found_branch1 = stub( "entry1 branch" )
|
232
|
+
found_branch2 = stub( "entry2 branch" )
|
233
|
+
|
234
|
+
# Do the search
|
235
|
+
entries = [
|
236
|
+
{ 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
|
237
|
+
{ 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
|
238
|
+
]
|
239
|
+
@conn.should_receive( :search_ext2 ).
|
240
|
+
with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
|
241
|
+
and_return( entries )
|
242
|
+
|
243
|
+
# Turn found entries into Branch objects
|
244
|
+
Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
|
245
|
+
and_return( found_branch1 )
|
246
|
+
Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
|
247
|
+
and_return( found_branch2 )
|
248
|
+
|
249
|
+
@dir.search( base, :base, filter ).should == [ found_branch1, found_branch2 ]
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
it "can search for entries and yield them as Sequel::Branch objects" do
|
254
|
+
base = TEST_PEOPLE_DN
|
255
|
+
filter = '(|(uid=jonlong)(uid=margento))'
|
256
|
+
branch = mock( "branch", :dn => "thedn" )
|
257
|
+
|
258
|
+
found_branch1 = stub( "entry1 branch" )
|
259
|
+
found_branch2 = stub( "entry2 branch" )
|
260
|
+
|
261
|
+
# Do the search
|
262
|
+
entries = [
|
263
|
+
{ 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
|
264
|
+
{ 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
|
265
|
+
]
|
266
|
+
@conn.should_receive( :search_ext2 ).
|
267
|
+
with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'], false, nil, nil, 0, 0, 0, '', nil ).
|
268
|
+
and_return( entries )
|
269
|
+
|
270
|
+
# Turn found entries into Branch objects
|
271
|
+
Treequel::Branch.should_receive( :new_from_entry ).with( entries[0], @dir ).
|
272
|
+
and_return( found_branch1 )
|
273
|
+
Treequel::Branch.should_receive( :new_from_entry ).with( entries[1], @dir ).
|
274
|
+
and_return( found_branch2 )
|
275
|
+
|
276
|
+
results = []
|
277
|
+
@dir.search( base, :base, filter ) do |branch|
|
278
|
+
results << branch
|
279
|
+
end
|
280
|
+
|
281
|
+
results.should == [ found_branch1, found_branch2 ]
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
it "catches plain RuntimeErrors raised by #search2 and re-casts them as " +
|
286
|
+
"more-interesting errors" do
|
287
|
+
@conn.should_receive( :search_ext2 ).
|
288
|
+
and_raise( RuntimeError.new('no result returned by search') )
|
289
|
+
@conn.should_receive( :err ).and_return( -1 )
|
290
|
+
|
291
|
+
expect {
|
292
|
+
@dir.search( TEST_BASE_DN, :base, '(objectClass=*)' )
|
293
|
+
}.to raise_error( LDAP::ResultError, /can't contact/i )
|
294
|
+
end
|
295
|
+
|
296
|
+
describe "and a custom search results class" do
|
297
|
+
|
298
|
+
before( :each ) do
|
299
|
+
@customclass = Class.new {
|
300
|
+
def self::new_from_entry( entry, directory )
|
301
|
+
new( entry, directory, 'a_dn' )
|
302
|
+
end
|
303
|
+
def initialize( entry, directory, dn )
|
304
|
+
@entry = entry
|
305
|
+
@directory = directory
|
306
|
+
@dn = dn
|
307
|
+
end
|
308
|
+
attr_reader :entry, :directory, :dn
|
309
|
+
}
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
it "can search for entries and return them as instances of a custom class" do
|
314
|
+
filter = '(|(uid=jonlong)(uid=margento))'
|
315
|
+
base = mock( "branch" )
|
316
|
+
|
317
|
+
found_branch1 = stub( "entry1 branch" )
|
318
|
+
found_branch2 = stub( "entry2 branch" )
|
319
|
+
|
320
|
+
# Do the search
|
321
|
+
entries = [
|
322
|
+
{ 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
|
323
|
+
{ 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
|
324
|
+
]
|
325
|
+
@conn.should_receive( :search_ext2 ).
|
326
|
+
with( base, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
|
327
|
+
false, nil, nil, 0, 0, 0, '', nil ).
|
328
|
+
and_return( entries )
|
329
|
+
|
330
|
+
rval = @dir.search( base, :base, filter, :results_class => @customclass )
|
331
|
+
|
332
|
+
rval[0].should be_an_instance_of( @customclass )
|
333
|
+
rval[0].entry.should == entries[0]
|
334
|
+
rval[0].directory.should == @dir
|
335
|
+
rval[1].should be_an_instance_of( @customclass )
|
336
|
+
rval[1].entry.should == entries[1]
|
337
|
+
rval[1].directory.should == @dir
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
it "returns instances of the base argument if it responds to new_from_entry and no " +
|
342
|
+
"custom class is specified" do
|
343
|
+
|
344
|
+
base = @customclass.new( nil, nil, TEST_PEOPLE_DN )
|
345
|
+
filter = '(|(uid=jonlong)(uid=margento))'
|
346
|
+
branch = mock( "branch" )
|
347
|
+
|
348
|
+
found_branch1 = stub( "entry1 branch" )
|
349
|
+
found_branch2 = stub( "entry2 branch" )
|
350
|
+
|
351
|
+
# Do the search
|
352
|
+
entries = [
|
353
|
+
{ 'dn' => ["uid=jonlong,#{TEST_PEOPLE_DN}"] },
|
354
|
+
{ 'dn' => ["uid=margento,#{TEST_PEOPLE_DN}"] },
|
355
|
+
]
|
356
|
+
@conn.should_receive( :search_ext2 ).
|
357
|
+
with( TEST_PEOPLE_DN, LDAP::LDAP_SCOPE_BASE, filter, ['*'],
|
358
|
+
false, nil, nil, 0, 0, 0, '', nil ).
|
359
|
+
and_return( entries )
|
360
|
+
|
361
|
+
rval = @dir.search( base, :base, filter )
|
362
|
+
|
363
|
+
rval[0].should be_an_instance_of( @customclass )
|
364
|
+
rval[0].entry.should == entries[0]
|
365
|
+
rval[0].directory.should == @dir
|
366
|
+
rval[1].should be_an_instance_of( @customclass )
|
367
|
+
rval[1].entry.should == entries[1]
|
368
|
+
rval[1].directory.should == @dir
|
369
|
+
end
|
370
|
+
|
371
|
+
end
|
372
|
+
|
373
|
+
it "can turn a DN string into an RDN string from its base" do
|
374
|
+
@dir.rdn_to( TEST_PERSON_DN ).should == TEST_PERSON_DN.sub( /,#{TEST_BASE_DN}$/, '' )
|
375
|
+
end
|
376
|
+
|
377
|
+
it "can fetch the server's schema" do
|
378
|
+
@conn.should_receive( :schema ).and_return( :the_schema )
|
379
|
+
Treequel::Schema.should_receive( :new ).with( :the_schema ).
|
380
|
+
and_return( :the_parsed_schema )
|
381
|
+
@dir.schema.should == :the_parsed_schema
|
382
|
+
end
|
383
|
+
|
384
|
+
it "creates branches for messages that match valid attributeType OIDs" do
|
385
|
+
@schema.should_receive( :attribute_types ).
|
386
|
+
and_return({ :cn => :a_value, :ou => :a_value })
|
387
|
+
|
388
|
+
@dir.stub!( :bound? ).and_return( false )
|
389
|
+
rval = @dir.ou( :people )
|
390
|
+
rval.dn.downcase.should == TEST_PEOPLE_DN.downcase
|
391
|
+
end
|
392
|
+
|
393
|
+
it "doesn't create branches for messages that don't match valid attributeType OIDs" do
|
394
|
+
@schema.should_receive( :attribute_types ).
|
395
|
+
and_return({ :cn => :a_value, :ou => :a_value })
|
396
|
+
|
397
|
+
expect { @dir.void('sbc') }.to raise_error( NoMethodError )
|
398
|
+
end
|
399
|
+
|
400
|
+
it "can modify the record corresponding to a Branch in the directory" do
|
401
|
+
branch = mock( "branch" )
|
402
|
+
branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
|
403
|
+
|
404
|
+
@conn.should_receive( :modify ).with( :the_branches_dn, 'cn' => ['nomblywob'] )
|
405
|
+
|
406
|
+
@dir.modify( branch, 'cn' => ['nomblywob'] )
|
407
|
+
end
|
408
|
+
|
409
|
+
it "can delete the record corresponding to a Branch from the directory" do
|
410
|
+
branch = mock( "branch" )
|
411
|
+
branch.should_receive( :dn ).at_least( :once ).and_return( :the_branches_dn )
|
412
|
+
|
413
|
+
@conn.should_receive( :delete ).once.with( :the_branches_dn )
|
414
|
+
|
415
|
+
@dir.delete( branch )
|
416
|
+
end
|
417
|
+
|
418
|
+
it "can create an entry for a Branch" do
|
419
|
+
newattrs = {
|
420
|
+
:cn => 'Chilly T',
|
421
|
+
:desc => 'Audi like Jetta',
|
422
|
+
:objectClass => :room,
|
423
|
+
}
|
424
|
+
rdn_attrs = {
|
425
|
+
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE]
|
426
|
+
}
|
427
|
+
addattrs = {
|
428
|
+
'cn' => ['Chilly T'],
|
429
|
+
'desc' => ['Audi like Jetta'],
|
430
|
+
'objectClass' => ['room'],
|
431
|
+
TEST_PERSON_DN_ATTR => [TEST_PERSON_DN_VALUE],
|
432
|
+
}
|
433
|
+
|
434
|
+
branch = mock( "new person branch" )
|
435
|
+
branch.should_receive( :dn ).and_return( TEST_PERSON_DN )
|
436
|
+
branch.should_receive( :rdn_attributes ).at_least( :once ).and_return( rdn_attrs )
|
437
|
+
|
438
|
+
room_objectclass = stub( 'room objectClass', :structural? => true )
|
439
|
+
@schema.should_receive( :object_classes ).at_least( :once ).and_return({
|
440
|
+
:room => room_objectclass,
|
441
|
+
})
|
442
|
+
|
443
|
+
@conn.should_receive( :add ).with( TEST_PERSON_DN, addattrs )
|
444
|
+
|
445
|
+
@dir.create( branch, newattrs )
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
it "can move a record to a new dn within the same branch" do
|
450
|
+
@dir.stub!( :bound? ).and_return( false )
|
451
|
+
branch = mock( "sibling branch obj" )
|
452
|
+
branch.should_receive( :dn ).at_least( :once ).and_return( TEST_PERSON_DN )
|
453
|
+
branch.should_receive( :split_dn ).at_least( :once ).
|
454
|
+
and_return([ TEST_PERSON_RDN, TEST_PEOPLE_DN ])
|
455
|
+
|
456
|
+
@conn.should_receive( :modrdn ).with( TEST_PERSON_DN, TEST_PERSON2_RDN, true )
|
457
|
+
branch.should_receive( :dn= ).with( TEST_PERSON2_DN )
|
458
|
+
|
459
|
+
@dir.move( branch, TEST_PERSON2_DN )
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
### Datatype conversion
|
464
|
+
|
465
|
+
it "allows a mapping to be overridden by a block for a valid syntax OID" do
|
466
|
+
@dir.add_syntax_mapping( OIDS::BIT_STRING_SYNTAX ) do |unconverted_value|
|
467
|
+
unconverted_value.to_sym
|
468
|
+
end
|
469
|
+
@dir.convert_syntax_value( OIDS::BIT_STRING_SYNTAX, 'a_value' ).should == :a_value
|
470
|
+
end
|
471
|
+
|
472
|
+
it "allows a mapping to be overridden by a Hash for a valid syntax OID" do
|
473
|
+
@dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
|
474
|
+
@dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == true
|
475
|
+
end
|
476
|
+
|
477
|
+
it "allows a mapping to be cleared by adding a nil mapping" do
|
478
|
+
@dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX, {'true' => true, 'false' => false} )
|
479
|
+
@dir.add_syntax_mapping( OIDS::BOOLEAN_SYNTAX )
|
480
|
+
@dir.convert_syntax_value( OIDS::BOOLEAN_SYNTAX, 'true' ).should == 'true'
|
481
|
+
end
|
482
|
+
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
|
487
|
+
# vim: set nosta noet ts=4 sw=4:
|