Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embed auth token in remote forms #5633

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions 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
Expand Down
24 changes: 16 additions & 8 deletions actionpack/lib/action_view/helpers/form_tag_helper.rb
Expand Up @@ -2,6 +2,7 @@
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
Expand All @@ -17,6 +18,9 @@ module FormTagHelper
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.
#
Expand All @@ -27,9 +31,11 @@ module FormTagHelper
# 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.
Expand Down Expand Up @@ -611,16 +617,18 @@ def html_options_for_form(url_for_options, options)
# 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"] != true
# 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
Expand Down
8 changes: 8 additions & 0 deletions actionpack/lib/action_view/railtie.rb
Expand Up @@ -7,6 +7,14 @@ class Railtie < Rails::Railtie
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
Expand Down
54 changes: 48 additions & 6 deletions actionpack/test/controller/request_forgery_protection_test.rb
Expand Up @@ -45,6 +45,14 @@ def form_for_remote_with_token
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

Expand Down Expand Up @@ -111,11 +119,42 @@ def test_should_render_button_to_with_token_tag
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_no_match(/authenticity_token/, response.body)
assert_match(/authenticity_token/, response.body)
end

def test_should_render_form_without_token_tag_if_remote_and_embedding_token_is_off
begin
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
end

def test_should_render_form_with_token_tag_if_remote_and_embedding_token_is_off_but_true_option_passed
begin
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
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
Expand All @@ -125,6 +164,13 @@ def test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requ
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
Expand Down Expand Up @@ -270,10 +316,6 @@ def test_should_allow_all_methods_without_token
end
end





class CustomAuthenticityParamControllerTest < ActionController::TestCase
def setup
ActionController::Base.request_forgery_protection_token = :custom_token_name
Expand Down
2 changes: 2 additions & 0 deletions railties/guides/source/configuring.textile
Expand Up @@ -375,6 +375,8 @@ And can reference in the view with the following code:

* +config.action_view.cache_asset_ids+ With the cache enabled, the asset tag helper methods will make fewer expensive file system calls (the default implementation checks the file system timestamp). However this prevents you from modifying any asset files while the server is running.

* +config.action_view.embed_authenticity_token_in_remote_forms+ This is by default set to true. If you set it to false, authenticity_token will not be added to forms with +:remote => true+ by default. You can force +authenticity_token+ to be added to such remote form by passing +:authenticity_token => true+ option.

h4. Configuring Action Mailer

There are a number of settings available on +config.action_mailer+:
Expand Down