drbservice 1.0.4

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.
@@ -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
+