aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/CHANGELOG.md2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb24
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb6
-rw-r--r--actionpack/lib/action_view/railtie.rb8
-rw-r--r--actionpack/test/controller/request_forgery_protection_test.rb58
-rw-r--r--actionpack/test/template/form_helper_test.rb35
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb12
8 files changed, 129 insertions, 23 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 465d19e50d..d275569cf6 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 3.2.3 (unreleased) ##
+* Add `config.action_view.embed_authenticity_token_in_remote_forms` (defaults to true) which allows to set if authenticity token will be included by default in remote forms. If you change it to false, you can still force authenticity token by passing `:authenticity_token => true` in form options *Piotr Sarnacki*
+
* Do not include the authenticity token in forms where remote: true as ajax forms use the meta-tag value *DHH*
* Turn off verbose mode of rack-cache, we still have X-Rack-Cache to
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 2267f3c185..252fe1ed03 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -656,15 +656,16 @@ module ActionView
# 'Accept <a href="/terms">Terms</a>.'.html_safe
# end
def label(object_name, method, content_or_options = nil, options = nil, &block)
+ options ||= {}
+
content_is_options = content_or_options.is_a?(Hash)
if content_is_options || block_given?
- options = content_or_options if content_is_options
+ options.merge!(content_or_options) if content_is_options
text = nil
else
text = content_or_options
end
- options ||= {}
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
end
@@ -1072,7 +1073,7 @@ module ActionView
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
end
- content_tag("textarea", "\n#{options.delete('value') || value_before_type_cast(object)}", options)
+ content_tag("textarea", options.delete('value') || value_before_type_cast(object), options)
end
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 4ce878f26a..066b98d4a2 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -2,6 +2,7 @@ require 'cgi'
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionView
# = Action View Form Tag Helpers
@@ -17,6 +18,9 @@ module ActionView
include UrlHelper
include TextHelper
+ mattr_accessor :embed_authenticity_token_in_remote_forms
+ self.embed_authenticity_token_in_remote_forms = true
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
@@ -27,9 +31,11 @@ module ActionView
# is added to simulate the verb over post.
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
# pass custom authenticity token string, or to not add authenticity_token field at all
- # (by passing <tt>false</tt>). If this is a remote form, the authenticity_token will by default
- # not be included as the ajax handler will get it from the meta-tag (but you can force it to be
- # rendered anyway in that case by passing <tt>true</tt>).
+ # (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
+ # by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
+ # This is helpful when you're fragment-caching the form. Remote forms get the
+ # authenticity from the <tt>meta</tt> tag, so embedding is unnecessary unless you
+ # support browsers without JavaScript.
# * A list of parameters to feed to the URL the form will be posted to.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behavior. By default this behavior is an ajax submit.
@@ -611,16 +617,18 @@ module ActionView
# responsibility of the caller to escape all the values.
html_options["action"] = url_for(url_for_options)
html_options["accept-charset"] = "UTF-8"
-
+
html_options["data-remote"] = true if html_options.delete("remote")
- if html_options["data-remote"] && html_options["authenticity_token"] == true
+ if html_options["data-remote"] &&
+ !embed_authenticity_token_in_remote_forms &&
+ html_options["authenticity_token"].blank?
+ # The authenticity token is taken from the meta tag in this case
+ html_options["authenticity_token"] = false
+ elsif html_options["authenticity_token"] == true
# Include the default authenticity_token, which is only generated when its set to nil,
# but we needed the true value to override the default of no authenticity_token on data-remote.
html_options["authenticity_token"] = nil
- elsif html_options["data-remote"]
- # The authenticity token is taken from the meta tag in this case
- html_options["authenticity_token"] = false
end
end
end
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 8c33ef09fa..2b0dd96435 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -17,6 +17,10 @@ module ActionView
autofocus novalidate formnovalidate open pubdate).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
+ PRE_CONTENT_STRINGS = {
+ :textarea => "\n"
+ }
+
# Returns an empty HTML tag of type +name+ which by default is XHTML
# compliant. Set +open+ to true to create an open tag compatible
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
@@ -125,7 +129,7 @@ module ActionView
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
- "<#{name}#{tag_options}>#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{escape ? ERB::Util.h(content) : content}</#{name}>".html_safe
end
def tag_options(options, escape = true)
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 80391d72cc..5086fdc6a3 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -7,6 +7,14 @@ module ActionView
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.stylesheet_expansions = {}
config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
+ config.action_view.embed_authenticity_token_in_remote_forms = true
+
+ initializer "action_view.embed_authenticity_token_in_remote_forms" do |app|
+ ActiveSupport.on_load(:action_view) do
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
+ app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
+ end
+ end
initializer "action_view.cache_asset_ids" do |app|
unless app.config.cache_classes
diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb
index b8b14f3a24..144003a59b 100644
--- a/actionpack/test/controller/request_forgery_protection_test.rb
+++ b/actionpack/test/controller/request_forgery_protection_test.rb
@@ -45,6 +45,14 @@ module RequestForgeryProtectionActions
render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => true ) {} %>"
end
+ def form_for_with_token
+ render :inline => "<%= form_for(:some_resource, :authenticity_token => true ) {} %>"
+ end
+
+ def form_for_remote_with_external_token
+ render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => 'external_token') {} %>"
+ end
+
def rescue_action(e) raise e end
end
@@ -111,11 +119,48 @@ module RequestForgeryProtectionTests
assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token
end
- def test_should_render_form_without_token_tag_if_remote
+ def test_should_render_form_with_token_tag_if_remote
+ assert_not_blocked do
+ get :form_for_remote
+ end
+ assert_match(/authenticity_token/, response.body)
+ end
+
+ def test_should_render_form_without_token_tag_if_remote_and_embedding_token_is_off
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = false
assert_not_blocked do
get :form_for_remote
end
assert_no_match(/authenticity_token/, response.body)
+ ensure
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = true
+ end
+
+ def test_should_render_form_with_token_tag_if_remote_and_embedding_token_is_off_but_true_option_passed
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = false
+ assert_not_blocked do
+ get :form_for_remote_with_token
+ end
+ assert_match(/authenticity_token/, response.body)
+ ensure
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = true
+ end
+
+ def test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested_and_embedding_is_off
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = false
+ assert_not_blocked do
+ get :form_for_remote_with_external_token
+ end
+ assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token'
+ ensure
+ ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms = true
+ end
+
+ def test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested
+ assert_not_blocked do
+ get :form_for_remote_with_external_token
+ end
+ assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', 'external_token'
end
def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested
@@ -125,6 +170,13 @@ module RequestForgeryProtectionTests
assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token
end
+ def test_should_render_form_with_token_tag_with_authenticity_token_requested
+ assert_not_blocked do
+ get :form_for_with_token
+ end
+ assert_select 'form>div>input[name=?][value=?]', 'custom_authenticity_token', @token
+ end
+
def test_should_allow_get
assert_not_blocked { get :index }
end
@@ -270,10 +322,6 @@ class FreeCookieControllerTest < ActionController::TestCase
end
end
-
-
-
-
class CustomAuthenticityParamControllerTest < ActionController::TestCase
def setup
ActionController::Base.request_forgery_protection_token = :custom_token_name
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index f9940ead58..7eeede7087 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -943,6 +943,41 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+ def test_form_for_label_error_wrapping
+ form_for(@post) do |f|
+ concat f.label(:author_name, :class => 'label')
+ concat f.text_field(:author_name)
+ concat f.submit('Create post')
+ end
+
+ expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => "put") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' size='30' type='text' id='post_author_name' value='' /></div>" +
+ "<input name='commit' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
+
+ def test_form_for_label_error_wrapping_without_conventional_instance_variable
+ post = remove_instance_variable :@post
+
+ form_for(post) do |f|
+ concat f.label(:author_name, :class => 'label')
+ concat f.text_field(:author_name)
+ concat f.submit('Create post')
+ end
+
+ expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => "put") do
+ "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" +
+ "<div class='field_with_errors'><input name='post[author_name]' size='30' type='text' id='post_author_name' value='' /></div>" +
+ "<input name='commit' type='submit' value='Create post' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_for_with_namespace
form_for(@post, :namespace => 'namespace') do |f|
concat f.text_field(:title)
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index 233907d07a..dcc6dd0411 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -216,19 +216,19 @@ class FormTagHelperTest < ActionView::TestCase
def test_text_area_tag_size_string
actual = text_area_tag "body", "hello world", "size" => "20x40"
- expected = %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>)
+ expected = %(<textarea cols="20" id="body" name="body" rows="40">\nhello world</textarea>)
assert_dom_equal expected, actual
end
def test_text_area_tag_size_symbol
actual = text_area_tag "body", "hello world", :size => "20x40"
- expected = %(<textarea cols="20" id="body" name="body" rows="40">hello world</textarea>)
+ expected = %(<textarea cols="20" id="body" name="body" rows="40">\nhello world</textarea>)
assert_dom_equal expected, actual
end
def test_text_area_tag_should_disregard_size_if_its_given_as_an_integer
actual = text_area_tag "body", "hello world", :size => 20
- expected = %(<textarea id="body" name="body">hello world</textarea>)
+ expected = %(<textarea id="body" name="body">\nhello world</textarea>)
assert_dom_equal expected, actual
end
@@ -239,19 +239,19 @@ class FormTagHelperTest < ActionView::TestCase
def test_text_area_tag_escape_content
actual = text_area_tag "body", "<b>hello world</b>", :size => "20x40"
- expected = %(<textarea cols="20" id="body" name="body" rows="40">&lt;b&gt;hello world&lt;/b&gt;</textarea>)
+ expected = %(<textarea cols="20" id="body" name="body" rows="40">\n&lt;b&gt;hello world&lt;/b&gt;</textarea>)
assert_dom_equal expected, actual
end
def test_text_area_tag_unescaped_content
actual = text_area_tag "body", "<b>hello world</b>", :size => "20x40", :escape => false
- expected = %(<textarea cols="20" id="body" name="body" rows="40"><b>hello world</b></textarea>)
+ expected = %(<textarea cols="20" id="body" name="body" rows="40">\n<b>hello world</b></textarea>)
assert_dom_equal expected, actual
end
def test_text_area_tag_unescaped_nil_content
actual = text_area_tag "body", nil, :escape => false
- expected = %(<textarea id="body" name="body"></textarea>)
+ expected = %(<textarea id="body" name="body">\n</textarea>)
assert_dom_equal expected, actual
end