actionpack 2.3.3 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +12 -1
- data/Rakefile +9 -8
- data/lib/action_controller/assertions/response_assertions.rb +3 -1
- data/lib/action_controller/base.rb +6 -2
- data/lib/action_controller/cookies.rb +1 -1
- data/lib/action_controller/dispatcher.rb +21 -6
- data/lib/action_controller/http_authentication.rb +3 -2
- data/lib/action_controller/params_parser.rb +6 -0
- data/lib/action_controller/reloader.rb +30 -21
- data/lib/action_controller/request_forgery_protection.rb +2 -1
- data/lib/action_controller/resources.rb +17 -13
- data/lib/action_controller/response.rb +6 -0
- data/lib/action_controller/routing.rb +3 -0
- data/lib/action_controller/routing/route_set.rb +18 -5
- data/lib/action_controller/streaming.rb +3 -1
- data/lib/action_controller/url_rewriter.rb +1 -1
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +3 -2
- data/lib/action_view/helpers/form_options_helper.rb +69 -1
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +20 -11
- data/lib/action_view/helpers/url_helper.rb +1 -1
- data/lib/action_view/locale/en.yml +4 -0
- data/test/abstract_unit.rb +16 -0
- data/test/controller/caching_test.rb +1 -1
- data/test/controller/cookie_test.rb +6 -0
- data/test/controller/dispatcher_test.rb +50 -11
- data/test/controller/filter_params_test.rb +2 -1
- data/test/controller/http_basic_authentication_test.rb +25 -0
- data/test/controller/http_digest_authentication_test.rb +29 -6
- data/test/controller/rack_test.rb +18 -1
- data/test/controller/redirect_test.rb +1 -1
- data/test/controller/reloader_test.rb +47 -20
- data/test/controller/request/json_params_parsing_test.rb +24 -4
- data/test/controller/request/xml_params_parsing_test.rb +15 -0
- data/test/controller/request_forgery_protection_test.rb +6 -5
- data/test/controller/resources_test.rb +44 -0
- data/test/controller/routing_test.rb +7 -2
- data/test/controller/send_file_test.rb +11 -1
- data/test/controller/url_rewriter_test.rb +29 -3
- data/test/fixtures/public/absolute/test.css +23 -0
- data/test/fixtures/public/absolute/test.js +63 -0
- data/test/template/atom_feed_helper_test.rb +29 -0
- data/test/template/form_helper_test.rb +26 -0
- data/test/template/form_options_helper_i18n_test.rb +27 -0
- data/test/template/form_options_helper_test.rb +34 -0
- data/test/template/text_helper_test.rb +23 -0
- data/test/template/url_helper_test.rb +8 -0
- metadata +10 -94
@@ -184,7 +184,7 @@ module ActionController
|
|
184
184
|
path = rewrite_path(options)
|
185
185
|
rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
|
186
186
|
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
|
187
|
-
rewritten_url << "##{options[:anchor]}" if options[:anchor]
|
187
|
+
rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
|
188
188
|
|
189
189
|
rewritten_url
|
190
190
|
end
|
data/lib/action_pack/version.rb
CHANGED
@@ -98,7 +98,7 @@ module ActionView
|
|
98
98
|
options[:schema_date] = "2005" # The Atom spec copyright date
|
99
99
|
end
|
100
100
|
|
101
|
-
xml = options
|
101
|
+
xml = options.delete(:xml) || eval("xml", block.binding)
|
102
102
|
xml.instruct!
|
103
103
|
if options[:instruct]
|
104
104
|
options[:instruct].each do |target,attrs|
|
@@ -726,6 +726,7 @@ module ActionView
|
|
726
726
|
options = options.stringify_keys
|
727
727
|
tag_value = options.delete("value")
|
728
728
|
name_and_id = options.dup
|
729
|
+
name_and_id["id"] = name_and_id["for"]
|
729
730
|
add_default_name_and_id_for_value(tag_value, name_and_id)
|
730
731
|
options.delete("index")
|
731
732
|
options["for"] ||= name_and_id["id"]
|
@@ -860,8 +861,8 @@ module ActionView
|
|
860
861
|
|
861
862
|
private
|
862
863
|
def add_default_name_and_id_for_value(tag_value, options)
|
863
|
-
|
864
|
-
pretty_tag_value
|
864
|
+
unless tag_value.nil?
|
865
|
+
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
|
865
866
|
specified_id = options["id"]
|
866
867
|
add_default_name_and_id(options)
|
867
868
|
options["id"] += "_#{pretty_tag_value}" unless specified_id
|
@@ -162,6 +162,60 @@ module ActionView
|
|
162
162
|
InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
|
163
163
|
end
|
164
164
|
|
165
|
+
|
166
|
+
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
|
167
|
+
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
|
168
|
+
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
|
169
|
+
# or <tt>:include_blank</tt> in the +options+ hash.
|
170
|
+
#
|
171
|
+
# Parameters:
|
172
|
+
# * +object+ - The instance of the class to be used for the select tag
|
173
|
+
# * +method+ - The attribute of +object+ corresponding to the select tag
|
174
|
+
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
|
175
|
+
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
|
176
|
+
# array of child objects representing the <tt><option></tt> tags.
|
177
|
+
# * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
|
178
|
+
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
|
179
|
+
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
|
180
|
+
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
|
181
|
+
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
|
182
|
+
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
|
183
|
+
#
|
184
|
+
# Example object structure for use with this method:
|
185
|
+
# class Continent < ActiveRecord::Base
|
186
|
+
# has_many :countries
|
187
|
+
# # attribs: id, name
|
188
|
+
# end
|
189
|
+
# class Country < ActiveRecord::Base
|
190
|
+
# belongs_to :continent
|
191
|
+
# # attribs: id, name, continent_id
|
192
|
+
# end
|
193
|
+
# class City < ActiveRecord::Base
|
194
|
+
# belongs_to :country
|
195
|
+
# # attribs: id, name, country_id
|
196
|
+
# end
|
197
|
+
#
|
198
|
+
# Sample usage:
|
199
|
+
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
|
200
|
+
#
|
201
|
+
# Possible output:
|
202
|
+
# <select name="city[country_id]">
|
203
|
+
# <optgroup label="Africa">
|
204
|
+
# <option value="1">South Africa</option>
|
205
|
+
# <option value="3">Somalia</option>
|
206
|
+
# </optgroup>
|
207
|
+
# <optgroup label="Europe">
|
208
|
+
# <option value="7" selected="selected">Denmark</option>
|
209
|
+
# <option value="2">Ireland</option>
|
210
|
+
# </optgroup>
|
211
|
+
# </select>
|
212
|
+
#
|
213
|
+
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
214
|
+
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
|
165
219
|
# Return select and option tags for the given object and method, using
|
166
220
|
# #time_zone_options_for_select to generate the list of option tags.
|
167
221
|
#
|
@@ -490,6 +544,15 @@ module ActionView
|
|
490
544
|
)
|
491
545
|
end
|
492
546
|
|
547
|
+
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
548
|
+
html_options = html_options.stringify_keys
|
549
|
+
add_default_name_and_id(html_options)
|
550
|
+
value = value(object)
|
551
|
+
content_tag(
|
552
|
+
"select", add_options(option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value), options, value), html_options
|
553
|
+
)
|
554
|
+
end
|
555
|
+
|
493
556
|
def to_time_zone_select_tag(priority_zones, options, html_options)
|
494
557
|
html_options = html_options.stringify_keys
|
495
558
|
add_default_name_and_id(html_options)
|
@@ -508,7 +571,8 @@ module ActionView
|
|
508
571
|
option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
|
509
572
|
end
|
510
573
|
if value.blank? && options[:prompt]
|
511
|
-
|
574
|
+
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('support.select.prompt', :default => 'Please select')
|
575
|
+
"<option value=\"\">#{prompt}</option>\n" + option_tags
|
512
576
|
else
|
513
577
|
option_tags
|
514
578
|
end
|
@@ -524,6 +588,10 @@ module ActionView
|
|
524
588
|
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
525
589
|
end
|
526
590
|
|
591
|
+
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
592
|
+
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
|
593
|
+
end
|
594
|
+
|
527
595
|
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
|
528
596
|
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
|
529
597
|
end
|
@@ -103,7 +103,7 @@ module ActionView
|
|
103
103
|
# escape_once("<< Accept & Checkout")
|
104
104
|
# # => "<< Accept & Checkout"
|
105
105
|
def escape_once(html)
|
106
|
-
html.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
|
106
|
+
ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
|
107
107
|
end
|
108
108
|
|
109
109
|
private
|
@@ -33,30 +33,31 @@ module ActionView
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
|
36
|
-
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
|
36
|
+
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
|
37
|
+
# for a total length not exceeding <tt>:length</tt>.
|
37
38
|
#
|
38
39
|
# ==== Examples
|
39
40
|
#
|
40
41
|
# truncate("Once upon a time in a world far far away")
|
41
|
-
# # => Once upon a time in a world
|
42
|
+
# # => Once upon a time in a world...
|
42
43
|
#
|
43
44
|
# truncate("Once upon a time in a world far far away", :length => 14)
|
44
45
|
# # => Once upon a...
|
45
46
|
#
|
46
47
|
# truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)")
|
47
|
-
# # => And they found
|
48
|
+
# # => And they found t(clipped)
|
48
49
|
#
|
49
|
-
# truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length =>
|
50
|
-
# # => And they
|
50
|
+
# truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 25)
|
51
|
+
# # => And they f... (continued)
|
51
52
|
#
|
52
53
|
# You can still use <tt>truncate</tt> with the old API that accepts the
|
53
54
|
# +length+ as its optional second and the +ellipsis+ as its
|
54
55
|
# optional third parameter:
|
55
56
|
# truncate("Once upon a time in a world far far away", 14)
|
56
|
-
# # => Once upon a
|
57
|
+
# # => Once upon a...
|
57
58
|
#
|
58
|
-
# truncate("And they found that many people were sleeping better.",
|
59
|
-
# # => And they
|
59
|
+
# truncate("And they found that many people were sleeping better.", 25, "... (continued)")
|
60
|
+
# # => And they f... (continued)
|
60
61
|
def truncate(text, *args)
|
61
62
|
options = args.extract_options!
|
62
63
|
unless args.empty?
|
@@ -234,12 +235,20 @@ module ActionView
|
|
234
235
|
#
|
235
236
|
# textilize("Visit the Rails website "here":http://www.rubyonrails.org/.)
|
236
237
|
# # => "<p>Visit the Rails website <a href="http://www.rubyonrails.org/">here</a>.</p>"
|
237
|
-
|
238
|
+
#
|
239
|
+
# textilize("This is worded <strong>strongly</strong>")
|
240
|
+
# # => "<p>This is worded <strong>strongly</strong></p>"
|
241
|
+
#
|
242
|
+
# textilize("This is worded <strong>strongly</strong>", :filter_html)
|
243
|
+
# # => "<p>This is worded <strong>strongly</strong></p>"
|
244
|
+
#
|
245
|
+
def textilize(text, *options)
|
246
|
+
options ||= [:hard_breaks]
|
247
|
+
|
238
248
|
if text.blank?
|
239
249
|
""
|
240
250
|
else
|
241
|
-
textilized = RedCloth.new(text,
|
242
|
-
textilized.hard_breaks = true if textilized.respond_to?(:hard_breaks=)
|
251
|
+
textilized = RedCloth.new(text, options)
|
243
252
|
textilized.to_html
|
244
253
|
end
|
245
254
|
end
|
@@ -568,7 +568,7 @@ module ActionView
|
|
568
568
|
when confirm && popup
|
569
569
|
"if (#{confirm_javascript_function(confirm)}) { #{popup_javascript_function(popup)} };return false;"
|
570
570
|
when confirm && method
|
571
|
-
"if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method)} };return false;"
|
571
|
+
"if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method, url, href)} };return false;"
|
572
572
|
when confirm
|
573
573
|
"return #{confirm_javascript_function(confirm)};"
|
574
574
|
when method
|
data/test/abstract_unit.rb
CHANGED
@@ -43,3 +43,19 @@ ActionController::Base.view_paths = FIXTURE_LOAD_PATH
|
|
43
43
|
CACHED_VIEW_PATHS = ActionView::Base.cache_template_loading? ?
|
44
44
|
ActionController::Base.view_paths :
|
45
45
|
ActionController::Base.view_paths.map {|path| ActionView::Template::EagerPath.new(path.to_s)}
|
46
|
+
|
47
|
+
class DummyMutex
|
48
|
+
def lock
|
49
|
+
@locked = true
|
50
|
+
end
|
51
|
+
|
52
|
+
def unlock
|
53
|
+
@locked = false
|
54
|
+
end
|
55
|
+
|
56
|
+
def locked?
|
57
|
+
@locked
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
ActionController::Reloader.default_lock = DummyMutex.new
|
@@ -52,7 +52,7 @@ class PageCachingTest < ActionController::TestCase
|
|
52
52
|
ActionController::Base.perform_caching = true
|
53
53
|
|
54
54
|
ActionController::Routing::Routes.draw do |map|
|
55
|
-
map.main '', :controller => 'posts'
|
55
|
+
map.main '', :controller => 'posts', :format => nil
|
56
56
|
map.formatted_posts 'posts.:format', :controller => 'posts'
|
57
57
|
map.resources :posts
|
58
58
|
map.connect ':controller/:action/:id'
|
@@ -118,4 +118,10 @@ class CookieTest < ActionController::TestCase
|
|
118
118
|
get :delete_cookie_with_path
|
119
119
|
assert_equal ["user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"], @response.headers["Set-Cookie"]
|
120
120
|
end
|
121
|
+
|
122
|
+
def test_cookies_persist_throughout_request
|
123
|
+
get :authenticate
|
124
|
+
cookies = @controller.send(:cookies)
|
125
|
+
assert_equal 'david', cookies['user_name']
|
126
|
+
end
|
121
127
|
end
|
@@ -2,25 +2,17 @@ require 'abstract_unit'
|
|
2
2
|
|
3
3
|
class DispatcherTest < Test::Unit::TestCase
|
4
4
|
Dispatcher = ActionController::Dispatcher
|
5
|
+
Reloader = ActionController::Reloader
|
5
6
|
|
6
7
|
def setup
|
7
8
|
ENV['REQUEST_METHOD'] = 'GET'
|
8
|
-
|
9
|
-
Dispatcher.middleware = ActionController::MiddlewareStack.new do |middleware|
|
10
|
-
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/middlewares.rb"))
|
11
|
-
middleware.instance_eval(File.read(middlewares))
|
12
|
-
end
|
13
|
-
|
14
|
-
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
|
15
|
-
Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
16
|
-
Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
17
|
-
Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
18
|
-
|
9
|
+
reset_dispatcher
|
19
10
|
Dispatcher.stubs(:require_dependency)
|
20
11
|
end
|
21
12
|
|
22
13
|
def teardown
|
23
14
|
ENV.delete 'REQUEST_METHOD'
|
15
|
+
reset_dispatcher
|
24
16
|
end
|
25
17
|
|
26
18
|
def test_clears_dependencies_after_dispatch_if_in_loading_mode
|
@@ -41,6 +33,34 @@ class DispatcherTest < Test::Unit::TestCase
|
|
41
33
|
dispatch
|
42
34
|
end
|
43
35
|
|
36
|
+
def test_builds_middleware_stack_only_during_initialization_if_not_in_loading_mode
|
37
|
+
dispatcher = create_dispatcher
|
38
|
+
assert_not_nil dispatcher.instance_variable_get(:"@app")
|
39
|
+
dispatcher.instance_variable_set(:"@app", lambda { |env| })
|
40
|
+
dispatcher.expects(:build_middleware_stack).never
|
41
|
+
dispatcher.call(nil)
|
42
|
+
dispatcher.call(nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_rebuilds_middleware_stack_on_every_request_if_in_loading_mode
|
46
|
+
dispatcher = create_dispatcher(false)
|
47
|
+
dispatcher.instance_variable_set(:"@app", lambda { |env| })
|
48
|
+
dispatcher.expects(:build_middleware_stack).twice
|
49
|
+
dispatcher.call(nil)
|
50
|
+
Reloader.default_lock.unlock
|
51
|
+
dispatcher.call(nil)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_doesnt_wrap_call_in_reloader_if_not_in_loading_mode
|
55
|
+
Reloader.expects(:run).never
|
56
|
+
dispatch
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_wraps_call_in_reloader_if_in_loading_mode
|
60
|
+
Reloader.expects(:run).once
|
61
|
+
dispatch(false)
|
62
|
+
end
|
63
|
+
|
44
64
|
# Stub out dispatch error logger
|
45
65
|
class << Dispatcher
|
46
66
|
def log_failsafe_exception(status, exception); end
|
@@ -99,6 +119,25 @@ class DispatcherTest < Test::Unit::TestCase
|
|
99
119
|
Dispatcher.new.call({'rack.input' => StringIO.new('')})
|
100
120
|
end
|
101
121
|
|
122
|
+
def create_dispatcher(cache_classes = true)
|
123
|
+
Dispatcher.define_dispatcher_callbacks(cache_classes)
|
124
|
+
Dispatcher.new
|
125
|
+
end
|
126
|
+
|
127
|
+
def reset_dispatcher
|
128
|
+
Dispatcher.middleware = ActionController::MiddlewareStack.new do |middleware|
|
129
|
+
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/middlewares.rb"))
|
130
|
+
middleware.instance_eval(File.read(middlewares))
|
131
|
+
end
|
132
|
+
|
133
|
+
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
|
134
|
+
Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
135
|
+
Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
136
|
+
Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
137
|
+
|
138
|
+
Dispatcher.define_dispatcher_callbacks(true)
|
139
|
+
end
|
140
|
+
|
102
141
|
def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)
|
103
142
|
assert_equal howmany, klass.subclasses.size, message
|
104
143
|
end
|
@@ -24,7 +24,8 @@ class FilterParamTest < Test::Unit::TestCase
|
|
24
24
|
[{'foo'=>'bar', 'baz'=>'foo'},{'foo'=>'[FILTERED]', 'baz'=>'[FILTERED]'},%w'foo baz'],
|
25
25
|
[{'bar'=>{'foo'=>'bar','bar'=>'foo'}},{'bar'=>{'foo'=>'[FILTERED]','bar'=>'foo'}},%w'fo'],
|
26
26
|
[{'foo'=>{'foo'=>'bar','bar'=>'foo'}},{'foo'=>'[FILTERED]'},%w'f banana'],
|
27
|
-
[{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)]
|
27
|
+
[{'baz'=>[{'foo'=>'baz'}]}, {'baz'=>[{'foo'=>'[FILTERED]'}]}, %w(foo)],
|
28
|
+
[{'baz'=>[{'foo'=>'baz'}, 1, 2, 3]}, {'baz'=>[{'foo'=>'[FILTERED]'}, 1, 2, 3]}, %w(foo)]]
|
28
29
|
|
29
30
|
test_hashes.each do |before_filter, after_filter, filter_words|
|
30
31
|
FilterParamController.filter_parameter_logging(*filter_words)
|
@@ -4,6 +4,7 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
|
|
4
4
|
class DummyController < ActionController::Base
|
5
5
|
before_filter :authenticate, :only => :index
|
6
6
|
before_filter :authenticate_with_request, :only => :display
|
7
|
+
before_filter :authenticate_long_credentials, :only => :show
|
7
8
|
|
8
9
|
def index
|
9
10
|
render :text => "Hello Secret"
|
@@ -12,6 +13,10 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
|
|
12
13
|
def display
|
13
14
|
render :text => 'Definitely Maybe'
|
14
15
|
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
render :text => 'Only for loooooong credentials'
|
19
|
+
end
|
15
20
|
|
16
21
|
private
|
17
22
|
|
@@ -28,6 +33,12 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
|
|
28
33
|
request_http_basic_authentication("SuperSecret")
|
29
34
|
end
|
30
35
|
end
|
36
|
+
|
37
|
+
def authenticate_long_credentials
|
38
|
+
authenticate_or_request_with_http_basic do |username, password|
|
39
|
+
username == '1234567890123456789012345678901234567890' && password == '1234567890123456789012345678901234567890'
|
40
|
+
end
|
41
|
+
end
|
31
42
|
end
|
32
43
|
|
33
44
|
AUTH_HEADERS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION', 'REDIRECT_X_HTTP_AUTHORIZATION']
|
@@ -42,6 +53,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
|
|
42
53
|
assert_response :success
|
43
54
|
assert_equal 'Hello Secret', @response.body, "Authentication failed for request header #{header}"
|
44
55
|
end
|
56
|
+
test "successful authentication with #{header.downcase} and long credentials" do
|
57
|
+
@request.env[header] = encode_credentials('1234567890123456789012345678901234567890', '1234567890123456789012345678901234567890')
|
58
|
+
get :show
|
59
|
+
|
60
|
+
assert_response :success
|
61
|
+
assert_equal 'Only for loooooong credentials', @response.body, "Authentication failed for request header #{header} and long credentials"
|
62
|
+
end
|
45
63
|
end
|
46
64
|
|
47
65
|
AUTH_HEADERS.each do |header|
|
@@ -52,6 +70,13 @@ class HttpBasicAuthenticationTest < ActionController::TestCase
|
|
52
70
|
assert_response :unauthorized
|
53
71
|
assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header}"
|
54
72
|
end
|
73
|
+
test "unsuccessful authentication with #{header.downcase} and long credentials" do
|
74
|
+
@request.env[header] = encode_credentials('h4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0rh4x0r', 'worldworldworldworldworldworldworldworld')
|
75
|
+
get :show
|
76
|
+
|
77
|
+
assert_response :unauthorized
|
78
|
+
assert_equal "HTTP Basic: Access denied.\n", @response.body, "Authentication didn't fail for request header #{header} and long credentials"
|
79
|
+
end
|
55
80
|
end
|
56
81
|
|
57
82
|
test "authentication request without credential" do
|
@@ -129,7 +129,7 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
|
|
129
129
|
assert_equal 'Definitely Maybe', @response.body
|
130
130
|
end
|
131
131
|
|
132
|
-
|
132
|
+
test "authentication request with request-uri that doesn't match credentials digest-uri" do
|
133
133
|
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
|
134
134
|
@request.env['REQUEST_URI'] = "/http_digest_authentication_test/dummy_digest/altered/uri"
|
135
135
|
get :display
|
@@ -138,10 +138,33 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
|
|
138
138
|
assert_equal "Authentication Failed", @response.body
|
139
139
|
end
|
140
140
|
|
141
|
-
|
142
|
-
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:
|
141
|
+
test "authentication request with absolute request uri (as in webrick)" do
|
142
|
+
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please')
|
143
|
+
@request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest"
|
144
|
+
|
145
|
+
get :display
|
146
|
+
|
147
|
+
assert_response :success
|
148
|
+
assert assigns(:logged_in)
|
149
|
+
assert_equal 'Definitely Maybe', @response.body
|
150
|
+
end
|
151
|
+
|
152
|
+
test "authentication request with absolute uri in credentials (as in IE)" do
|
153
|
+
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest",
|
143
154
|
:username => 'pretty', :password => 'please')
|
144
|
-
|
155
|
+
|
156
|
+
get :display
|
157
|
+
|
158
|
+
assert_response :success
|
159
|
+
assert assigns(:logged_in)
|
160
|
+
assert_equal 'Definitely Maybe', @response.body
|
161
|
+
end
|
162
|
+
|
163
|
+
test "authentication request with absolute uri in both request and credentials (as in Webrick with IE)" do
|
164
|
+
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:url => "http://test.host/http_digest_authentication_test/dummy_digest",
|
165
|
+
:username => 'pretty', :password => 'please')
|
166
|
+
@request.env['REQUEST_URI'] = "http://test.host/http_digest_authentication_test/dummy_digest"
|
167
|
+
|
145
168
|
get :display
|
146
169
|
|
147
170
|
assert_response :success
|
@@ -199,11 +222,11 @@ class HttpDigestAuthenticationTest < ActionController::TestCase
|
|
199
222
|
|
200
223
|
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
|
201
224
|
credentials.merge!(options)
|
202
|
-
credentials.
|
225
|
+
credentials.merge!(:uri => @request.env['REQUEST_URI'].to_s)
|
203
226
|
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
|
204
227
|
end
|
205
228
|
|
206
229
|
def decode_credentials(header)
|
207
230
|
ActionController::HttpAuthentication::Digest.decode_credentials(@response.headers['WWW-Authenticate'])
|
208
231
|
end
|
209
|
-
end
|
232
|
+
end
|