merb-auth-core 0.9.9
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/LICENSE +20 -0
- data/README.textile +338 -0
- data/Rakefile +65 -0
- data/TODO +0 -0
- data/lib/merb-auth-core/authenticated_helper.rb +42 -0
- data/lib/merb-auth-core/authentication.rb +130 -0
- data/lib/merb-auth-core/bootloader.rb +10 -0
- data/lib/merb-auth-core/customizations.rb +24 -0
- data/lib/merb-auth-core/errors.rb +66 -0
- data/lib/merb-auth-core/merbtasks.rb +6 -0
- data/lib/merb-auth-core/responses.rb +36 -0
- data/lib/merb-auth-core/router_helper.rb +25 -0
- data/lib/merb-auth-core/session_mixin.rb +57 -0
- data/lib/merb-auth-core/strategy.rb +206 -0
- data/lib/merb-auth-core.rb +26 -0
- data/spec/helpers/authentication_helper_spec.rb +111 -0
- data/spec/merb-auth-core/activation_fixture.rb +2 -0
- data/spec/merb-auth-core/authentication_spec.rb +318 -0
- data/spec/merb-auth-core/customizations_spec.rb +22 -0
- data/spec/merb-auth-core/errors_spec.rb +51 -0
- data/spec/merb-auth-core/merb-auth-core_spec.rb +15 -0
- data/spec/merb-auth-core/router_helper_spec.rb +114 -0
- data/spec/merb-auth-core/strategy_spec.rb +274 -0
- data/spec/spec_helper.rb +93 -0
- metadata +100 -0
@@ -0,0 +1,318 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe "Merb::Authentication Session" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@session_class = Merb::CookieSession
|
7
|
+
@session = @session_class.generate
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "module methods" do
|
11
|
+
before(:each) do
|
12
|
+
@m = mock("mock")
|
13
|
+
clear_strategies!
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:all) do
|
17
|
+
clear_strategies!
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "store_user" do
|
21
|
+
it{@session.authentication.should respond_to(:store_user)}
|
22
|
+
|
23
|
+
it "should raise a NotImplemented error by default" do
|
24
|
+
pending "How to spec this when we need to overwrite it for the specs to work?"
|
25
|
+
lambda do
|
26
|
+
@session.authentication.store_user("THE USER")
|
27
|
+
end.should raise_error(Merb::Authentication::NotImplemented)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "fetch_user" do
|
32
|
+
it{@session.authentication.should respond_to(:fetch_user)}
|
33
|
+
|
34
|
+
it "should raise a NotImplemented error by defualt" do
|
35
|
+
pending "How to spec this when we need to overwrite it for the specs to work?"
|
36
|
+
lambda do
|
37
|
+
@session.authentication.fetch_user
|
38
|
+
end.should raise_error(Merb::Authentication::NotImplemented)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "error_message" do
|
44
|
+
|
45
|
+
before(:each) do
|
46
|
+
@request = fake_request
|
47
|
+
@auth = Merb::Authentication.new(@request.session)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should be 'Could not log in' by default" do
|
51
|
+
@auth.error_message.should == "Could not log in"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow a user to set the error message" do
|
55
|
+
@auth.error_message = "No You Don't"
|
56
|
+
@auth.error_message.should == "No You Don't"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "user" do
|
61
|
+
it "should call fetch_user with the session contents to load the user" do
|
62
|
+
@session[:user] = 42
|
63
|
+
@session.authentication.should_receive(:fetch_user).with(42)
|
64
|
+
@session.user
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should set the @user instance variable" do
|
68
|
+
@session[:user] = 42
|
69
|
+
@session.authentication.should_receive(:fetch_user).and_return("THE USER")
|
70
|
+
@session.user
|
71
|
+
@session.authentication.assigns(:user).should == "THE USER"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should cache the user in an instance variable" do
|
75
|
+
@session[:user] = 42
|
76
|
+
@session.authentication.should_receive(:fetch_user).once.and_return("THE USER")
|
77
|
+
@session.user
|
78
|
+
@session.authentication.assigns(:user).should == "THE USER"
|
79
|
+
@session.user
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should set the ivar to nil if the session is nil" do
|
83
|
+
@session[:user] = nil
|
84
|
+
@session.user.should be_nil
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "user=" do
|
90
|
+
before(:each) do
|
91
|
+
@user = mock("user")
|
92
|
+
@session.authentication.stub!(:fetch_user).and_return(@user)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should call store_user on the session to get the value to store in the session" do
|
96
|
+
@session.authentication.should_receive(:store_user).with(@user)
|
97
|
+
@session.user = @user
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should set the instance variable to nil if the return of store_user is nil" do
|
101
|
+
@session.authentication.should_receive(:store_user).and_return(nil)
|
102
|
+
@session.user = @user
|
103
|
+
@session.user.should be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should set the instance varaible to nil if the return of store_user is false" do
|
107
|
+
@session.authentication.should_receive(:store_user).and_return(false)
|
108
|
+
@session.user = @user
|
109
|
+
@session.user.should be_nil
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should set the instance variable to the value of user if store_user is not nil or false" do
|
113
|
+
@session.authentication.should_receive(:store_user).and_return(42)
|
114
|
+
@session.user = @user
|
115
|
+
@session.user.should == @user
|
116
|
+
@session[:user].should == 42
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "abandon!" do
|
121
|
+
|
122
|
+
before(:each) do
|
123
|
+
@user = mock("user")
|
124
|
+
@session.authentication.stub!(:fetch_user).and_return(@user)
|
125
|
+
@session.authentication.stub!(:store_user).and_return(42)
|
126
|
+
@session[:user] = 42
|
127
|
+
@session.user
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should delete the session" do
|
131
|
+
@session.should_receive(:clear)
|
132
|
+
@session.abandon!
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should not have a user after it is abandoned" do
|
136
|
+
@session.user.should == @user
|
137
|
+
@session.abandon!
|
138
|
+
@session.user.should be_nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "Merb::Authentication" do
|
143
|
+
it "Should be hookable" do
|
144
|
+
Merb::Authentication.should include(Extlib::Hook)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#authenticate" do
|
150
|
+
|
151
|
+
before(:all) do
|
152
|
+
clear_strategies!
|
153
|
+
end
|
154
|
+
|
155
|
+
after(:all) do
|
156
|
+
clear_strategies!
|
157
|
+
end
|
158
|
+
|
159
|
+
before(:each) do
|
160
|
+
class Sone < Merb::Authentication::Strategy
|
161
|
+
def run!
|
162
|
+
Viking.capture(Sone)
|
163
|
+
params[:pass_1]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
class Stwo < Merb::Authentication::Strategy
|
167
|
+
def run!
|
168
|
+
Viking.capture(Stwo)
|
169
|
+
params[:pass_2]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
class Sthree < Merb::Authentication::Strategy
|
173
|
+
def run!
|
174
|
+
Viking.capture(Sthree)
|
175
|
+
params[:pass_3]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
class Sfour < Merb::Authentication::Strategy
|
179
|
+
abstract!
|
180
|
+
|
181
|
+
def run!
|
182
|
+
"BAD MAN"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
Sfour.should_not_receive(:run!)
|
187
|
+
@request = Users.new(fake_request)
|
188
|
+
@auth = Merb::Authentication.new(@request.session)
|
189
|
+
Viking.captures.clear
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should execute the strategies in the default order" do
|
193
|
+
@request.params[:pass_3] = true
|
194
|
+
@auth.authenticate!(@request, @request.params)
|
195
|
+
Viking.captures.should == %w( Sone Stwo Sthree )
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should run the strategeis until if finds a non nil non false" do
|
199
|
+
@request.params[:pass_2] = true
|
200
|
+
@auth.authenticate!(@request, @request.params)
|
201
|
+
Viking.captures.should == %w( Sone Stwo )
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should raise an Unauthenticated exception if no 'user' is found" do
|
205
|
+
lambda do
|
206
|
+
@auth.authenticate!(@request, @request.params)
|
207
|
+
end.should raise_error(Merb::Controller::Unauthenticated)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should store the user into the session if one is found" do
|
211
|
+
@auth.should_receive(:user=).with("WINNA")
|
212
|
+
@request.params[:pass_1] = "WINNA"
|
213
|
+
@auth.authenticate!(@request, @request.params)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should use the Authentiation#error_message as the error message" do
|
217
|
+
@auth.should_receive(:error_message).and_return("BAD BAD BAD")
|
218
|
+
lambda do
|
219
|
+
@auth.authenticate!(@request, @request.params)
|
220
|
+
end.should raise_error(Merb::Controller::Unauthenticated, "BAD BAD BAD")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should execute the strategies as passed into the authenticate! method" do
|
224
|
+
@request.params[:pass_1] = true
|
225
|
+
@auth.authenticate!(@request, @request.params, Stwo, Sone)
|
226
|
+
Viking.captures.should == ["Stwo", "Sone"]
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
describe "user_class" do
|
232
|
+
it "should have User as the default user class if requested" do
|
233
|
+
Merb::Authentication.user_class.should == User
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "redirection" do
|
238
|
+
|
239
|
+
before(:all) do
|
240
|
+
class FooController < Merb::Controller
|
241
|
+
before :ensure_authenticated
|
242
|
+
def index; "FooController#index" end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
before(:each) do
|
247
|
+
class MyStrategy < Merb::Authentication::Strategy
|
248
|
+
def run!
|
249
|
+
if params[:url]
|
250
|
+
self.body = "this is the body"
|
251
|
+
params[:status] ? redirect!(params[:url], :status => params[:status]) : redirect!(params[:url])
|
252
|
+
else
|
253
|
+
"WINNA"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end # MyStrategy
|
257
|
+
|
258
|
+
class FailStrategy < Merb::Authentication::Strategy
|
259
|
+
def run!
|
260
|
+
request.params[:should_not_be_here] = true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
Merb::Router.reset!
|
265
|
+
Merb::Router.prepare{ match("/").to(:controller => "foo_controller")}
|
266
|
+
@request = mock_request("/")
|
267
|
+
@s = MyStrategy.new(@request, @request.params)
|
268
|
+
@a = Merb::Authentication.new(@request.session)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "should answer redirected false if the strategy did not redirect" do
|
272
|
+
@a.authenticate! @request, @request.params
|
273
|
+
@a.should_not be_redirected
|
274
|
+
end
|
275
|
+
|
276
|
+
it "should answer redirected true if the strategy did redirect" do
|
277
|
+
@request.params[:url] = "/some/url"
|
278
|
+
@a.authenticate! @request, @request.params
|
279
|
+
@a.halted?
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should provide access to the Headers" do
|
283
|
+
@request.params[:url] = "/some/url"
|
284
|
+
@a.authenticate! @request, @request.params
|
285
|
+
@a.headers.should == {"Location" => "/some/url"}
|
286
|
+
end
|
287
|
+
|
288
|
+
it "should provide access to the status" do
|
289
|
+
@request.params[:url] = "/some/url"
|
290
|
+
@request.params[:status] = 401
|
291
|
+
@a.authenticate! @request, @request.params
|
292
|
+
@a.should be_halted
|
293
|
+
@a.status.should == 401
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should stop processing the strategies if one redirects" do
|
297
|
+
@request.params[:url] = "/some/url"
|
298
|
+
lambda do
|
299
|
+
@a.authenticate! @request, @request.params, MyStrategy, FailStrategy
|
300
|
+
end.should_not raise_error(Merb::Controller::NotFound)
|
301
|
+
@a.should be_halted
|
302
|
+
@request.params[:should_not_be_here].should be_nil
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should allow you to set the body" do
|
306
|
+
@a.body = "body"
|
307
|
+
@a.body.should == "body"
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should put the body of the strategy as the response body of the controller" do
|
311
|
+
controller = request "/", :params => {:url => "/some/url"}
|
312
|
+
controller.should redirect_to("/some/url")
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
|
318
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe "Merb::Authentication.customizations" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Merb::Authentication.default_customizations.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should allow addition to the customizations" do
|
10
|
+
Merb::Authentication.customize_default { "ONE" }
|
11
|
+
Merb::Authentication.default_customizations.first.call.should == "ONE"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should allow multiple additions to the customizations" do
|
15
|
+
Merb::Authentication.customize_default {"ONE"}
|
16
|
+
Merb::Authentication.customize_default {"TWO"}
|
17
|
+
|
18
|
+
Merb::Authentication.default_customizations.first.call.should == "ONE"
|
19
|
+
Merb::Authentication.default_customizations.last.call.should == "TWO"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Merb::Authentication::Errors do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@errors = Merb::Authentication::Errors.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should report that it is empty on first creation" do
|
10
|
+
@errors.empty?.should == true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should continue to report that it is empty even after being checked" do
|
14
|
+
@errors.on(:foo)
|
15
|
+
@errors.empty?.should == true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should add an error" do
|
19
|
+
@errors.add(:login, "Login or password incorrect")
|
20
|
+
@errors[:login].should == ["Login or password incorrect"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should allow many errors to be added to the same field" do
|
24
|
+
@errors.add(:login, "bad 1")
|
25
|
+
@errors.add(:login, "bad 2")
|
26
|
+
@errors.on(:login).should == ["bad 1", "bad 2"]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should give the full messages for an error" do
|
30
|
+
@errors.add(:login, "login wrong")
|
31
|
+
@errors.add(:password, "password wrong")
|
32
|
+
["password wrong", "login wrong"].each do |msg|
|
33
|
+
@errors.full_messages.should include(msg)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return the error for a specific field / label" do
|
38
|
+
@errors.add(:login, "wrong")
|
39
|
+
@errors.on(:login).should == ["wrong"]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return nil for a specific field if it's not been set" do
|
43
|
+
@errors.on(:not_there).should be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should provide an errors instance method on the Authenticaiton instance" do
|
47
|
+
a = Merb::Authentication.new(Merb::CookieSession.generate)
|
48
|
+
a.errors.should be_a_kind_of(Merb::Authentication::Errors)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "merb-auth-core" do
|
4
|
+
it "should ensure_authentication" do
|
5
|
+
dispatch_to(Users, :index) do |controller|
|
6
|
+
controller.should_receive(:ensure_authenticated)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not ensure_authenticated when skipped" do
|
11
|
+
dispatch_to(Dingbats, :index) do |controller|
|
12
|
+
controller.should_not_receive(:ensure_authenticated)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
|
2
|
+
require 'ruby-debug'
|
3
|
+
|
4
|
+
describe "router protection" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
class Foo < Merb::Controller
|
8
|
+
def index; "INDEX"; end
|
9
|
+
end
|
10
|
+
|
11
|
+
clear_strategies!
|
12
|
+
|
13
|
+
Object.class_eval do
|
14
|
+
remove_const("Mone") if defined?(Mone)
|
15
|
+
remove_const("Mtwo") if defined?(Mtwo)
|
16
|
+
remove_const("Mthree") if defined?(Mthree)
|
17
|
+
end
|
18
|
+
|
19
|
+
Viking.captures.clear
|
20
|
+
|
21
|
+
class Mone < Merb::Authentication::Strategy
|
22
|
+
def run!
|
23
|
+
Viking.capture self.class
|
24
|
+
if request.params[self.class.name]
|
25
|
+
request.params[self.class.name]
|
26
|
+
elsif request.params[:url]
|
27
|
+
redirect!(request.params[:url])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Mthree < Mone; end
|
33
|
+
class Mtwo < Mone; end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
Merb::Router.prepare do
|
38
|
+
to(:controller => "foo") do
|
39
|
+
authenticate do
|
40
|
+
match("/single_level_default").register
|
41
|
+
|
42
|
+
authenticate(Mtwo) do
|
43
|
+
match("/nested_specific").register
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
authenticate(Mtwo, Mone) do
|
48
|
+
match("/single_level_specific").register
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "single level default" do
|
55
|
+
|
56
|
+
it "should allow access to the controller if the strategy passes" do
|
57
|
+
result = request("/single_level_default", :params => {"Mtwo" => true})
|
58
|
+
result.body.should == "INDEX"
|
59
|
+
Viking.captures.should == %w(Mone Mthree Mtwo)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should fail if no strategies match" do
|
63
|
+
result = request("/single_level_default")
|
64
|
+
result.status.should == Merb::Controller::Unauthenticated.status
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should set return a rack array if the strategy redirects" do
|
68
|
+
result = request("/single_level_default", :params => {"url" => "/some/url"})
|
69
|
+
result.status.should == 302
|
70
|
+
result.body.should_not =="INDEX"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "nested_specific" do
|
75
|
+
|
76
|
+
it "should allow access to the controller if the strategy passes" do
|
77
|
+
result = request("/nested_specific", :params => {"Mtwo" => true})
|
78
|
+
result.body.should == "INDEX"
|
79
|
+
Viking.captures.should == %w(Mone Mthree Mtwo)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should fail if no strategies match" do
|
83
|
+
result = request("/nested_specific")
|
84
|
+
result.status.should == Merb::Controller::Unauthenticated.status
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should set return a rack array if the strategy redirects" do
|
88
|
+
result = request("/nested_specific", :params => {"url" => "/some/url"})
|
89
|
+
result.status.should == 302
|
90
|
+
result.body.should_not =="INDEX"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "single_level_specific" do
|
95
|
+
|
96
|
+
it "should allow access to the controller if the strategy passes" do
|
97
|
+
result = request("/single_level_specific", :params => {"Mone" => true})
|
98
|
+
result.body.should == "INDEX"
|
99
|
+
Viking.captures.should == %w(Mtwo Mone)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should fail if no strategies match" do
|
103
|
+
result = request("/single_level_specific")
|
104
|
+
result.status.should == Merb::Controller::Unauthenticated.status
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should set return a rack array if the strategy redirects" do
|
108
|
+
result = request("/single_level_specific", :params => {"url" => "/some/url"})
|
109
|
+
result.status.should == 302
|
110
|
+
result.body.should_not =="INDEX"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|