mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add redirect_back
for safer referrer redirects
`redirect_to :back` is a somewhat common pattern in Rails apps, but it is not completely safe. There are a number of circumstances where HTTP referrer information is not available on the request. This happens often with bot traffic and occasionally to user traffic depending on browser security settings. When there is no referrer available on the request, `redirect_to :back` will raise `ActionController::RedirectBackError`, usually resulting in an application error. `redirect_back` takes a required `fallback_location` keyword argument that specifies the redirect when the referrer information is not available. This prevents 500 errors caused by `ActionController::RedirectBackError`.
This commit is contained in:
parent
d953512360
commit
13fd5586ce
5 changed files with 59 additions and 1 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
* Add `redirect_back` method to `ActionController::Redirecting` to provide a
|
||||||
|
way to safely redirect to the `HTTP_REFERER` if it is present, falling back
|
||||||
|
to a provided redirect otherwise.
|
||||||
|
|
||||||
|
*Derek Prior*
|
||||||
|
|
||||||
* `ActionController::TestCase` will be moved to it's own gem in Rails 5.1
|
* `ActionController::TestCase` will be moved to it's own gem in Rails 5.1
|
||||||
|
|
||||||
With the speed improvements made to `ActionDispatch::IntegrationTest` we no
|
With the speed improvements made to `ActionDispatch::IntegrationTest` we no
|
||||||
|
|
|
@ -75,6 +75,29 @@ module ActionController
|
||||||
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Redirects the browser to the page that issued the request if possible,
|
||||||
|
# otherwise redirects to provided default fallback location.
|
||||||
|
#
|
||||||
|
# This avoids the <tt>ActionController::RedirectBackError</tt> that can
|
||||||
|
# occur if the request has no associated <tt>HTTP_REFERER</tt>.
|
||||||
|
#
|
||||||
|
# redirect_back fallback_location: { action: "show", id: 5 }
|
||||||
|
# redirect_back fallback_location: post
|
||||||
|
# redirect_back fallback_location: "http://www.rubyonrails.org"
|
||||||
|
# redirect_back fallback_location: "/images/screenshot.jpg"
|
||||||
|
# redirect_back fallback_location: articles_url
|
||||||
|
# redirect_back fallback_location: proc { edit_post_url(@post) }
|
||||||
|
#
|
||||||
|
# All options that can be passed to <tt>redirect_to</tt> are accepted as
|
||||||
|
# options and the behavior is indetical.
|
||||||
|
def redirect_back(fallback_location:, **args)
|
||||||
|
if referer = request.headers["Referer"]
|
||||||
|
redirect_to referer, **args
|
||||||
|
else
|
||||||
|
redirect_to fallback_location, **args
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def _compute_redirect_to_location(request, options) #:nodoc:
|
def _compute_redirect_to_location(request, options) #:nodoc:
|
||||||
case options
|
case options
|
||||||
# The scheme name consist of a letter followed by any combination of
|
# The scheme name consist of a letter followed by any combination of
|
||||||
|
|
|
@ -42,6 +42,10 @@ class RedirectController < ActionController::Base
|
||||||
redirect_to :back, :status => 307
|
redirect_to :back, :status => 307
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def redirect_back_with_status
|
||||||
|
redirect_back(fallback_location: "/things/stuff", status: 307)
|
||||||
|
end
|
||||||
|
|
||||||
def host_redirect
|
def host_redirect
|
||||||
redirect_to :action => "other_host", :only_path => false, :host => 'other.test.host'
|
redirect_to :action => "other_host", :only_path => false, :host => 'other.test.host'
|
||||||
end
|
end
|
||||||
|
@ -248,6 +252,23 @@ class RedirectTest < ActionController::TestCase
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_redirect_back
|
||||||
|
referer = "http://www.example.com/coming/from"
|
||||||
|
@request.env["HTTP_REFERER"] = referer
|
||||||
|
|
||||||
|
get :redirect_back_with_status
|
||||||
|
|
||||||
|
assert_response 307
|
||||||
|
assert_equal referer, redirect_to_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_redirect_back_with_no_referer
|
||||||
|
get :redirect_back_with_status
|
||||||
|
|
||||||
|
assert_response 307
|
||||||
|
assert_equal "http://test.host/things/stuff", redirect_to_url
|
||||||
|
end
|
||||||
|
|
||||||
def test_redirect_to_record
|
def test_redirect_to_record
|
||||||
with_routing do |set|
|
with_routing do |set|
|
||||||
set.draw do
|
set.draw do
|
||||||
|
|
|
@ -1150,7 +1150,7 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
def user_not_authorized
|
def user_not_authorized
|
||||||
flash[:error] = "You don't have access to this section."
|
flash[:error] = "You don't have access to this section."
|
||||||
redirect_to :back
|
redirect_back(fallback_location: root_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -628,6 +628,14 @@ You can use `redirect_to` with any arguments that you could use with `link_to` o
|
||||||
redirect_to :back
|
redirect_to :back
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will raise `ActionController::RedirectBackError` if the request had no
|
||||||
|
`HTTP_REFERER` information set. To guard against this case, you can provide a
|
||||||
|
fall back redirect URL by using `redirect_back`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
redirect_back(fallback_location: root_path)
|
||||||
|
```
|
||||||
|
|
||||||
#### Getting a Different Redirect Status Code
|
#### Getting a Different Redirect Status Code
|
||||||
|
|
||||||
Rails uses HTTP status code 302, a temporary redirect, when you call `redirect_to`. If you'd like to use a different status code, perhaps 301, a permanent redirect, you can use the `:status` option:
|
Rails uses HTTP status code 302, a temporary redirect, when you call `redirect_to`. If you'd like to use a different status code, perhaps 301, a permanent redirect, you can use the `:status` option:
|
||||||
|
|
Loading…
Reference in a new issue