drbservice 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/spec
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'rspec'
14
+ require 'spec/lib/helpers'
15
+ require 'drb/authsslprotocol'
16
+
17
+
18
+ describe DRb::DRbAuthenticatedSSLSocket do
19
+
20
+ before( :all ) do
21
+ setup_logging( :fatal )
22
+ end
23
+
24
+ describe "URI parsing method" do
25
+ it "parses a valid drb authenticated SSL URI string into an Array of [host, port, option]" do
26
+ DRb::DRbAuthenticatedSSLSocket.parse_uri( VALID_SERVICE_URISTRING ).should ==
27
+ [ VALID_SERVICE_URI.host, VALID_SERVICE_URI.port, VALID_SERVICE_URI.query ]
28
+ end
29
+
30
+ it "parses a valid drb authenticated SSL URI object into an Array of [host, port, option]" do
31
+ DRb::DRbAuthenticatedSSLSocket.parse_uri( VALID_SERVICE_URI ).should ==
32
+ [ VALID_SERVICE_URI.host, VALID_SERVICE_URI.port, VALID_SERVICE_URI.query ]
33
+ end
34
+
35
+ it "parses the 'query' part of the URI as the 'option' return value" do
36
+ DRb::DRbAuthenticatedSSLSocket.parse_uri( VALID_SERVICE_URISTRING + '?an_option' ).
37
+ should == [ VALID_SERVICE_URI.host, VALID_SERVICE_URI.port, 'an_option' ]
38
+ end
39
+
40
+ it "raises an exception if the URI scheme to be parsed isn't supported by this protocol" do
41
+ expect {
42
+ DRb::DRbAuthenticatedSSLSocket.parse_uri( 'drb://localhost:1718' )
43
+ }.to raise_exception( DRb::DRbBadScheme, /not a drbauthssl/i )
44
+ end
45
+
46
+ it "raises an exception if the port isn't specified by the URI to be parsed" do
47
+ expect {
48
+ DRb::DRbAuthenticatedSSLSocket.parse_uri( 'drbauthssl://localhost' )
49
+ }.to raise_exception( DRb::DRbBadURI, /missing the port/i )
50
+ end
51
+
52
+ end
53
+
54
+
55
+ # [open(uri, config)] Open a client connection to the server at +uri+,
56
+ # using configuration +config+. Return a protocol
57
+ # instance for this connection.
58
+ describe "client-open method" do
59
+ end
60
+
61
+
62
+ # [open_server(uri, config)] Open a server listening at +uri+,
63
+ # using configuration +config+. Return a
64
+ # protocol instance for this listener.
65
+ describe "server-open method" do
66
+ end
67
+
68
+
69
+ # [uri_option(uri, config)] Take a URI, possibly containing an option
70
+ # component (e.g. a trailing '?param=val'),
71
+ # and return a [uri, option] tuple.
72
+ describe "client-open method" do
73
+ end
74
+
75
+
76
+ end
@@ -0,0 +1,382 @@
1
+ #!/usr/bin/spec
2
+
3
+ BEGIN {
4
+ require 'pathname'
5
+
6
+ basedir = Pathname( __FILE__ ).dirname.parent.parent
7
+ libdir = basedir + 'lib'
8
+
9
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
10
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
11
+ }
12
+
13
+ require 'treequel'
14
+
15
+ require 'rspec'
16
+ require 'spec/lib/helpers'
17
+
18
+ require 'drbservice'
19
+ require 'drbservice/ldapauth'
20
+
21
+
22
+ describe DRbService::LDAPAuthentication do
23
+ include DRbService::SpecHelpers
24
+
25
+ TEST_URI = 'ldap://ldap.acme.com/dc=acme,dc=com'
26
+ TEST_DN_PATTERN = 'uid=%s,ou=people,dc=acme,dc=com'
27
+ TEST_FILTER_PATTERN = '(&(uid=%s)(objectClass=posixAccount))'
28
+ TEST_BASE = 'ou=employees,dc=acme,dc=com'
29
+
30
+
31
+ before( :all ) do
32
+ setup_logging( :fatal )
33
+ end
34
+
35
+ after( :all ) do
36
+ reset_logging()
37
+ end
38
+
39
+
40
+ describe "mixed into a DRbService" do
41
+
42
+ it "provides a declarative to set the LDAP URI for the service" do
43
+ serviceclass = Class.new( DRbService ) do
44
+ include DRbService::LDAPAuthentication
45
+ ldap_uri TEST_URI
46
+ end
47
+ serviceclass.ldap_uri.should == TEST_URI
48
+ end
49
+
50
+ it "provides a declarative to set a string pattern for the DN of the binding user" do
51
+ serviceclass = Class.new( DRbService ) do
52
+ include DRbService::LDAPAuthentication
53
+ ldap_dn TEST_DN_PATTERN
54
+ end
55
+ serviceclass.ldap_dn.should == TEST_DN_PATTERN
56
+ end
57
+
58
+ it "provides a declarative to set a search filter for finding the DN of the binding user" do
59
+ serviceclass = Class.new( DRbService ) do
60
+ include DRbService::LDAPAuthentication
61
+ ldap_dn_search TEST_FILTER_PATTERN
62
+ end
63
+ serviceclass.ldap_dn_search.should == {
64
+ :scope => :sub,
65
+ :filter => TEST_FILTER_PATTERN,
66
+ :base => nil,
67
+ }
68
+ end
69
+
70
+ it "accepts an optional search base dn as an argument to the search filter declarative" do
71
+ serviceclass = Class.new( DRbService ) do
72
+ include DRbService::LDAPAuthentication
73
+ ldap_dn_search TEST_FILTER_PATTERN, :base => TEST_BASE
74
+ end
75
+ serviceclass.ldap_dn_search.should == {
76
+ :filter => TEST_FILTER_PATTERN,
77
+ :base => TEST_BASE,
78
+ :scope => :sub,
79
+ }
80
+ end
81
+
82
+ it "accepts an optional scope as an argument to the search filter declarative" do
83
+ serviceclass = Class.new( DRbService ) do
84
+ include DRbService::LDAPAuthentication
85
+ ldap_dn_search TEST_FILTER_PATTERN, :scope => :one
86
+ end
87
+ serviceclass.ldap_dn_search.should == {
88
+ :filter => TEST_FILTER_PATTERN,
89
+ :scope => :one,
90
+ :base => nil,
91
+ }
92
+ end
93
+
94
+ it "accepts both a scope and a base as arguments to the search filter declarative" do
95
+ serviceclass = Class.new( DRbService ) do
96
+ include DRbService::LDAPAuthentication
97
+ ldap_dn_search TEST_FILTER_PATTERN, :scope => :sub, :base => 'dc=acme,dc=com'
98
+ end
99
+ serviceclass.ldap_dn_search.should == {
100
+ :filter => TEST_FILTER_PATTERN,
101
+ :scope => :sub,
102
+ :base => 'dc=acme,dc=com'
103
+ }
104
+ end
105
+
106
+ it "provides a declarative to set an optional authorization callback for the service" do
107
+ serviceclass = Class.new( DRbService ) do
108
+ include DRbService::LDAPAuthentication
109
+ ldap_authz_callback do |user, directory|
110
+ # noop
111
+ end
112
+ end
113
+ serviceclass.ldap_authz_callback.should be_a( Proc )
114
+ end
115
+
116
+ it "accepts the name of a method to call as the authorization callback for the service" do
117
+ serviceclass = Class.new( DRbService ) do
118
+ include DRbService::LDAPAuthentication
119
+ ldap_authz_callback :authorize_user
120
+ end
121
+ serviceclass.ldap_authz_callback.should == :authorize_user
122
+ end
123
+
124
+
125
+ describe "instances without an ldap URI set" do
126
+
127
+ before( :all ) do
128
+ @serviceclass = Class.new( DRbService ) do
129
+ def do_some_guarded_stuff; return "Ronk."; end
130
+ unguarded do
131
+ def do_some_unguarded_stuff; return "Adonk."; end
132
+ end
133
+ end
134
+ end
135
+
136
+ before( :each ) do
137
+ @serviceobj = @serviceclass.new
138
+ end
139
+
140
+
141
+ it "raise an exception without calling the block on any authentication" do
142
+ block_called = false
143
+ expect {
144
+ @serviceobj.authenticate( '' ) do
145
+ block_called = true
146
+ end
147
+ }.should raise_exception( SecurityError, /authentication failure/i )
148
+ block_called.should == false
149
+ end
150
+
151
+ it "don't allow access to guarded methods" do
152
+ expect {
153
+ @serviceobj.do_some_guarded_stuff
154
+ }.to raise_exception( SecurityError, /not authenticated/i )
155
+ end
156
+
157
+ it "allow access to unguarded methods" do
158
+ @serviceobj.do_some_unguarded_stuff.should == 'Adonk.'
159
+ end
160
+
161
+ end
162
+
163
+ describe "instances with an ldap_dn pattern set" do
164
+
165
+ before( :each ) do
166
+ @serviceclass = Class.new( DRbService ) do
167
+ include DRbService::LDAPAuthentication
168
+
169
+ ldap_uri TEST_URI
170
+ ldap_dn 'uid=%s,ou=people,dc=acme,dc=com'
171
+
172
+ def do_some_guarded_stuff; return "Ronk."; end
173
+ unguarded do
174
+ def do_some_unguarded_stuff; return "Adonk."; end
175
+ end
176
+ end
177
+ end
178
+
179
+ before( :each ) do
180
+ @serviceobj = @serviceclass.new
181
+ end
182
+
183
+
184
+ it "uses the ldap_dn as a pattern for authentication" do
185
+ directory = mock( "directory object" )
186
+ user_branch = mock( "user branch" )
187
+
188
+ Treequel.should_receive( :directory ).with( TEST_URI ).
189
+ and_return( directory )
190
+ Treequel::Branch.should_receive( :new ).
191
+ with( directory, 'uid=user,ou=people,dc=acme,dc=com' ).
192
+ and_return( user_branch )
193
+ user_branch.should_receive( :exists? ).and_return( true )
194
+
195
+ directory.should_receive( :bind_as ).
196
+ with( user_branch, 'pass' ).
197
+ and_raise( LDAP::ResultError.new('Invalid credentials') )
198
+
199
+ block_called = false
200
+ expect {
201
+ @serviceobj.authenticate( 'user', 'pass' ) do
202
+ block_called = true
203
+ end
204
+ }.should raise_exception( SecurityError, /authentication fail/i )
205
+ block_called.should == false
206
+ end
207
+
208
+ it "fails if the DN isn't valid" do
209
+ directory = mock( "directory object" )
210
+ user_branch = mock( "user branch" )
211
+
212
+ Treequel.should_receive( :directory ).with( TEST_URI ).
213
+ and_return( directory )
214
+ Treequel::Branch.should_receive( :new ).
215
+ with( directory, 'uid=user,ou=people,dc=acme,dc=com' ).
216
+ and_return( user_branch )
217
+ user_branch.should_receive( :exists? ).and_return( false )
218
+
219
+ directory.should_not_receive( :bind_as )
220
+
221
+ block_called = false
222
+ expect {
223
+ @serviceobj.authenticate( 'user', 'pass' ) do
224
+ block_called = true
225
+ end
226
+ }.should raise_exception( SecurityError, /authentication fail/i )
227
+ block_called.should == false
228
+ end
229
+
230
+ end
231
+
232
+ describe "instances with an ldap_dn_search set" do
233
+
234
+ before( :each ) do
235
+ @serviceclass = Class.new( DRbService ) do
236
+ include DRbService::LDAPAuthentication
237
+
238
+ ldap_uri TEST_URI
239
+ ldap_dn_search TEST_FILTER_PATTERN,
240
+ :base => TEST_BASE,
241
+ :scope => :one
242
+
243
+ def do_some_guarded_stuff; return "Ronk."; end
244
+ unguarded do
245
+ def do_some_unguarded_stuff; return "Adonk."; end
246
+ end
247
+ end
248
+ end
249
+
250
+ before( :each ) do
251
+ @serviceobj = @serviceclass.new
252
+ end
253
+
254
+
255
+ it "use the configured search criteria to find the user to bind as" do
256
+ directory = mock( "directory object" )
257
+ base_branch = mock( "base branch" )
258
+ user_branch = mock( "user branch" )
259
+ search_branchset = mock( "search branchset" )
260
+
261
+ Treequel.should_receive( :directory ).with( TEST_URI ).
262
+ and_return( directory )
263
+ Treequel::Branch.should_receive( :new ).with( directory, TEST_BASE ).
264
+ and_return( base_branch )
265
+ base_branch.should_receive( :scope ).with( :one ).and_return( search_branchset )
266
+
267
+ expected_filter = TEST_FILTER_PATTERN % [ 'user' ]
268
+ search_branchset.should_receive( :filter ).with( expected_filter ).
269
+ and_return( search_branchset )
270
+ search_branchset.should_receive( :first ).and_return( user_branch )
271
+
272
+ directory.should_receive( :bind_as ).with( user_branch, 'pass' ).
273
+ and_raise( LDAP::ResultError.new('Invalid credentials') )
274
+
275
+ block_called = false
276
+ expect {
277
+ @serviceobj.authenticate( 'user', 'pass' ) do
278
+ block_called = true
279
+ end
280
+ }.should raise_exception( SecurityError, /authentication fail/i )
281
+ block_called.should == false
282
+ end
283
+
284
+ it "fails if the search can't find a valid user" do
285
+ directory = mock( "directory object" )
286
+ base_branch = mock( "base branch" )
287
+ search_branchset = mock( "search branchset" )
288
+
289
+ Treequel.should_receive( :directory ).with( TEST_URI ).
290
+ and_return( directory )
291
+ Treequel::Branch.should_receive( :new ).with( directory, TEST_BASE ).
292
+ and_return( base_branch )
293
+ base_branch.should_receive( :scope ).with( :one ).and_return( search_branchset )
294
+
295
+ expected_filter = TEST_FILTER_PATTERN % [ 'user' ]
296
+ search_branchset.should_receive( :filter ).with( expected_filter ).
297
+ and_return( search_branchset )
298
+ search_branchset.should_receive( :first ).and_return( nil )
299
+
300
+ directory.should_not_receive( :bound_as )
301
+
302
+ block_called = false
303
+ expect {
304
+ @serviceobj.authenticate( 'user', 'pass' ) do
305
+ block_called = true
306
+ end
307
+ }.should raise_exception( SecurityError, /authentication fail/i )
308
+ block_called.should == false
309
+ end
310
+ end
311
+
312
+ describe "instances with an ldap_authz_callback set to a Proc" do
313
+ before( :each ) do
314
+ @serviceclass = Class.new( DRbService ) do
315
+ include DRbService::LDAPAuthentication
316
+
317
+ ldap_uri TEST_URI
318
+ ldap_dn TEST_DN_PATTERN
319
+ ldap_authz_callback do |user, directory|
320
+ user.is_authorized?
321
+ end
322
+
323
+ def do_some_guarded_stuff; return "Ronk."; end
324
+ unguarded do
325
+ def do_some_unguarded_stuff; return "Adonk."; end
326
+ end
327
+ end
328
+ end
329
+
330
+ before( :each ) do
331
+ @serviceobj = @serviceclass.new
332
+ end
333
+
334
+ it "raises a SecurityError if the authorization callback returns false" do
335
+ directory = mock( "directory object" )
336
+ user = mock( "user branch" )
337
+
338
+ Treequel.should_receive( :directory ).with( TEST_URI ).
339
+ and_return( directory )
340
+ expected_dn = TEST_DN_PATTERN % [ 'user' ]
341
+ Treequel::Branch.should_receive( :new ).with( directory, expected_dn ).
342
+ and_return( user )
343
+ user.should_receive( :exists? ).and_return( true )
344
+ directory.should_receive( :bind_as ).with( user, 'pass' ).
345
+ and_return( expected_dn )
346
+ user.should_receive( :is_authorized? ).and_return( false )
347
+
348
+ block_called = false
349
+ expect {
350
+ @serviceobj.authenticate( 'user', 'pass' ) do
351
+ block_called = true
352
+ end
353
+ }.should raise_exception( SecurityError, /authorization fail/i )
354
+ block_called.should == false
355
+ end
356
+
357
+ it "yields to the remote caller if the authorization callback returns true" do
358
+ directory = mock( "directory object" )
359
+ user = mock( "user branch" )
360
+
361
+ Treequel.should_receive( :directory ).with( TEST_URI ).
362
+ and_return( directory )
363
+ expected_dn = TEST_DN_PATTERN % [ 'user' ]
364
+ Treequel::Branch.should_receive( :new ).with( directory, expected_dn ).
365
+ and_return( user )
366
+ user.should_receive( :exists? ).and_return( true )
367
+ directory.should_receive( :bind_as ).with( user, 'pass' ).
368
+ and_return( expected_dn )
369
+ user.should_receive( :is_authorized? ).and_return( true )
370
+
371
+ block_called = false
372
+ @serviceobj.authenticate( 'user', 'pass' ) do
373
+ block_called = true
374
+ end
375
+ block_called.should == true
376
+ end
377
+ end
378
+
379
+ end
380
+
381
+ end
382
+