aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb10
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb18
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb2
-rw-r--r--actionpack/lib/action_controller/metal/force_ssl.rb4
-rw-r--r--actionpack/lib/action_controller/metal/head.rb2
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb3
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb12
-rw-r--r--actionpack/lib/action_controller/metal/live.rb4
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb7
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb2
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb30
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb76
-rw-r--r--actionpack/lib/action_controller/renderer.rb15
13 files changed, 130 insertions, 55 deletions
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 06b6a95ff8..d6911ee2b5 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -230,12 +230,20 @@ module ActionController
# This method will overwrite an existing Cache-Control header.
# See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
#
+ # HTTP Cache-Control Extensions for Stale Content. See https://tools.ietf.org/html/rfc5861
+ # It helps to cache an asset and serve it while is being revalidated and/or returning with an error.
+ #
+ # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds
+ # expires_in 3.hours, public: true, stale_while_revalidate: 60.seconds, stale_if_error: 5.minutes
+ #
# The method will also ensure an HTTP Date header for client compatibility.
def expires_in(seconds, options = {})
response.cache_control.merge!(
max_age: seconds,
public: options.delete(:public),
- must_revalidate: options.delete(:must_revalidate)
+ must_revalidate: options.delete(:must_revalidate),
+ stale_while_revalidate: options.delete(:stale_while_revalidate),
+ stale_if_error: options.delete(:stale_if_error),
)
options.delete(:private)
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index ce9eb209fe..30034be018 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -51,6 +51,24 @@ module ActionController
class UnknownFormat < ActionControllerError #:nodoc:
end
+ # Raised when a nested respond_to is triggered and the content types of each
+ # are incompatible. For exampe:
+ #
+ # respond_to do |outer_type|
+ # outer_type.js do
+ # respond_to do |inner_type|
+ # inner_type.html { render body: "HTML" }
+ # end
+ # end
+ # end
+ class RespondToMismatchError < ActionControllerError
+ DEFAULT_MESSAGE = "respond_to was called multiple times and matched with conflicting formats in this action. Please note that you may only call respond_to and match on a single format per action."
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
class MissingExactTemplate < UnknownFormat #:nodoc:
end
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 5115c2fadf..380f2e9591 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -36,7 +36,7 @@ module ActionController #:nodoc:
define_method(type) do
request.flash[type]
end
- helper_method type
+ helper_method(type) if respond_to?(:helper_method)
self._flash_types += [type]
end
diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb
index 8d53a30e93..26e6f72b66 100644
--- a/actionpack/lib/action_controller/metal/force_ssl.rb
+++ b/actionpack/lib/action_controller/metal/force_ssl.rb
@@ -5,8 +5,8 @@ require "active_support/core_ext/hash/slice"
module ActionController
# This module is deprecated in favor of +config.force_ssl+ in your environment
- # config file. This will ensure all communication to non-whitelisted endpoints
- # served by your application occurs over HTTPS.
+ # config file. This will ensure all endpoints not explicitly marked otherwise
+ # will have all communication served over HTTPS.
module ForceSSL # :nodoc:
extend ActiveSupport::Concern
include AbstractController::Callbacks
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index bac9bc5e5f..3c84bebb85 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -38,7 +38,7 @@ module ActionController
self.response_body = ""
if include_content?(response_code)
- self.content_type = content_type || (Mime[formats.first] if formats)
+ self.content_type = content_type || (Mime[formats.first] if formats) || Mime[:html]
response.charset = false
end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 22c84e440b..0faaac1ce4 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -100,8 +100,7 @@ module ActionController
# # => ["application", "chart", "rubygems"]
def all_helpers_from_path(path)
helpers = Array(path).flat_map do |_path|
- extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
- names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1'.freeze) }
+ names = Dir["#{_path}/**/*_helper.rb"].map { |file| file[_path.to_s.size + 1..-"_helper.rb".size - 1] }
names.sort!
end
helpers.uniq!
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 01676f3237..a871ccd533 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -56,8 +56,9 @@ module ActionController
# In your integration tests, you can do something like this:
#
# def test_access_granted_from_xml
- # @request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
- # get "/notes/1.xml"
+ # authorization = ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
+ #
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
#
# assert_equal 200, status
# end
@@ -389,10 +390,9 @@ module ActionController
# In your integration tests, you can do something like this:
#
# def test_access_granted_from_xml
- # get(
- # "/notes/1.xml", nil,
- # 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
- # )
+ # authorization = ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
+ #
+ # get "/notes/1.xml", headers: { 'HTTP_AUTHORIZATION' => authorization }
#
# assert_equal 200, status
# end
diff --git a/actionpack/lib/action_controller/metal/live.rb b/actionpack/lib/action_controller/metal/live.rb
index 2f4c8fb83c..b1c2391afe 100644
--- a/actionpack/lib/action_controller/metal/live.rb
+++ b/actionpack/lib/action_controller/metal/live.rb
@@ -86,7 +86,7 @@ module ActionController
# Note: SSEs are not currently supported by IE. However, they are supported
# by Chrome, Firefox, Opera, and Safari.
class SSE
- WHITELISTED_OPTIONS = %w( retry event id )
+ PERMITTED_OPTIONS = %w( retry event id )
def initialize(stream, options = {})
@stream = stream
@@ -111,7 +111,7 @@ module ActionController
def perform_write(json, options)
current_options = @options.merge(options).stringify_keys
- WHITELISTED_OPTIONS.each do |option_name|
+ PERMITTED_OPTIONS.each do |option_name|
if (option_value = current_options[option_name])
@stream.write "#{option_name}: #{option_value}\n"
end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 2233b93406..118da11990 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -11,7 +11,7 @@ module ActionController #:nodoc:
# @people = Person.all
# end
#
- # That action implicitly responds to all formats, but formats can also be whitelisted:
+ # That action implicitly responds to all formats, but formats can also be explicitly enumerated:
#
# def index
# @people = Person.all
@@ -105,7 +105,7 @@ module ActionController #:nodoc:
#
# Mime::Type.register "image/jpg", :jpg
#
- # Respond to also allows you to specify a common block for different formats by using +any+:
+ # +respond_to+ also allows you to specify a common block for different formats by using +any+:
#
# def index
# @people = Person.all
@@ -197,6 +197,9 @@ module ActionController #:nodoc:
yield collector if block_given?
if format = collector.negotiate_format(request)
+ if content_type && content_type != format
+ raise ActionController::RespondToMismatchError
+ end
_process_format(format)
_set_rendered_content_type format
response = collector.response
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 4c2b5120eb..2804a06a58 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -105,7 +105,7 @@ module ActionController
when String
request.protocol + request.host_with_port + options
when Proc
- _compute_redirect_to_location request, options.call
+ _compute_redirect_to_location request, instance_eval(&options)
else
url_for(options)
end.delete("\0\r\n")
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index fc9cf8aaff..cb109c6ad8 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -17,7 +17,7 @@ module ActionController #:nodoc:
# access. When a request reaches your application, \Rails verifies the received
# token with the token in the session. All requests are checked except GET requests
# as these should be idempotent. Keep in mind that all session-oriented requests
- # should be CSRF protected, including JavaScript and HTML requests.
+ # are CSRF protected by default, including JavaScript and HTML requests.
#
# Since HTML and JavaScript requests are typically made from the browser, we
# need to ensure to verify request authenticity for the web browser. We can
@@ -30,16 +30,23 @@ module ActionController #:nodoc:
# URL on your site. When your JavaScript response loads on their site, it executes.
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
- # Ajax) requests are allowed to make GET requests for JavaScript responses.
+ # Ajax) requests are allowed to make requests for JavaScript responses.
#
- # It's important to remember that XML or JSON requests are also affected and if
- # you're building an API you should change forgery protection method in
+ # It's important to remember that XML or JSON requests are also checked by default. If
+ # you're building an API or an SPA you could change forgery protection method in
# <tt>ApplicationController</tt> (by default: <tt>:exception</tt>):
#
# class ApplicationController < ActionController::Base
# protect_from_forgery unless: -> { request.format.json? }
# end
#
+ # It is generally safe to exclude XHR requests from CSRF protection
+ # (like the code snippet above does), because XHR requests can only be made from
+ # the same origin. Note however that any cross-origin third party domain
+ # allowed via {CORS}[https://en.wikipedia.org/wiki/Cross-origin_resource_sharing]
+ # will also be able to create XHR requests. Be sure to check your
+ # CORS configuration before disabling forgery protection for XHR.
+ #
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method.
# By default <tt>protect_from_forgery</tt> protects your session with
# <tt>:null_session</tt> method, which provides an empty session
@@ -54,7 +61,7 @@ module ActionController #:nodoc:
# <tt>csrf_meta_tags</tt> in the HTML +head+.
#
# Learn more about CSRF attacks and securing your application in the
- # {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
+ # {Ruby on Rails Security Guide}[https://guides.rubyonrails.org/security.html].
module RequestForgeryProtection
extend ActiveSupport::Concern
@@ -275,7 +282,7 @@ module ActionController #:nodoc:
# Check for cross-origin JavaScript responses.
def non_xhr_javascript_response? # :doc:
- content_type =~ %r(\Atext/javascript) && !request.xhr?
+ content_type =~ %r(\A(?:text|application)/javascript) && !request.xhr?
end
AUTHENTICITY_TOKEN_LENGTH = 32
@@ -400,9 +407,14 @@ module ActionController #:nodoc:
end
def xor_byte_strings(s1, s2) # :doc:
- s2_bytes = s2.bytes
- s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 }
- s2_bytes.pack("C*")
+ s2 = s2.dup
+ size = s1.bytesize
+ i = 0
+ while i < size
+ s2.setbyte(i, s1.getbyte(i) ^ s2.getbyte(i))
+ i += 1
+ end
+ s2
end
# The form's authenticity parameter. Override to provide your own.
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index 5a06bf86e3..a37f08d944 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -58,7 +58,7 @@ module ActionController
# == Action Controller \Parameters
#
- # Allows you to choose which attributes should be whitelisted for mass updating
+ # Allows you to choose which attributes should be permitted for mass updating
# and thus prevent accidentally exposing that which shouldn't be exposed.
# Provides two methods for this purpose: #require and #permit. The former is
# used to mark parameters as required. The latter is used to set the parameter
@@ -133,6 +133,15 @@ module ActionController
# Returns a hash that can be used as the JSON representation for the parameters.
##
+ # :method: each_key
+ #
+ # :call-seq:
+ # each_key()
+ #
+ # Calls block once for each key in the parameters, passing the key.
+ # If no block is given, an enumerator is returned instead.
+
+ ##
# :method: empty?
#
# :call-seq:
@@ -204,7 +213,7 @@ module ActionController
#
# Returns a new array of the values of the parameters.
delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
- :as_json, :to_s, to: :@parameters
+ :as_json, :to_s, :each_key, to: :@parameters
# By default, never raise an UnpermittedParameters exception if these
# params are present. The default includes both 'controller' and 'action'
@@ -505,7 +514,7 @@ module ActionController
#
# Note that if you use +permit+ in a key that points to a hash,
# it won't allow all the hash. You also need to specify which
- # attributes inside the hash should be whitelisted.
+ # attributes inside the hash should be permitted.
#
# params = ActionController::Parameters.new({
# person: {
@@ -560,12 +569,14 @@ module ActionController
# Returns a parameter for the given +key+. If the +key+
# can't be found, there are several options: With no other arguments,
# it will raise an <tt>ActionController::ParameterMissing</tt> error;
- # if more arguments are given, then that will be returned; if a block
+ # if a second argument is given, then that is returned (converted to an
+ # instance of ActionController::Parameters if possible); if a block
# is given, then that will be run and its result returned.
#
# params = ActionController::Parameters.new(person: { name: "Francesco" })
# params.fetch(:person) # => <ActionController::Parameters {"name"=>"Francesco"} permitted: false>
# params.fetch(:none) # => ActionController::ParameterMissing: param is missing or the value is empty: none
+ # params.fetch(:none, {}) # => <ActionController::Parameters {} permitted: false>
# params.fetch(:none, "Francesco") # => "Francesco"
# params.fetch(:none) { "Francesco" } # => "Francesco"
def fetch(key, *args)
@@ -637,20 +648,18 @@ module ActionController
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
# params.transform_values { |x| x * 2 }
# # => <ActionController::Parameters {"a"=>2, "b"=>4, "c"=>6} permitted: false>
- def transform_values(&block)
- if block
- new_instance_with_inherited_permitted_status(
- @parameters.transform_values(&block)
- )
- else
- @parameters.transform_values
- end
+ def transform_values
+ return to_enum(:transform_values) unless block_given?
+ new_instance_with_inherited_permitted_status(
+ @parameters.transform_values { |v| yield convert_value_to_parameters(v) }
+ )
end
# Performs values transformation and returns the altered
# <tt>ActionController::Parameters</tt> instance.
- def transform_values!(&block)
- @parameters.transform_values!(&block)
+ def transform_values!
+ return to_enum(:transform_values!) unless block_given?
+ @parameters.transform_values! { |v| yield convert_value_to_parameters(v) }
self
end
@@ -793,9 +802,7 @@ module ActionController
protected
attr_reader :parameters
- def permitted=(new_permitted)
- @permitted = new_permitted
- end
+ attr_writer :permitted
def fields_for_style?
@parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
@@ -906,15 +913,28 @@ module ActionController
PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) }
end
- def permitted_scalar_filter(params, key)
- if has_key?(key) && permitted_scalar?(self[key])
- params[key] = self[key]
+ # Adds existing keys to the params if their values are scalar.
+ #
+ # For example:
+ #
+ # puts self.keys #=> ["zipcode(90210i)"]
+ # params = {}
+ #
+ # permitted_scalar_filter(params, "zipcode")
+ #
+ # puts params.keys # => ["zipcode"]
+ def permitted_scalar_filter(params, permitted_key)
+ permitted_key = permitted_key.to_s
+
+ if has_key?(permitted_key) && permitted_scalar?(self[permitted_key])
+ params[permitted_key] = self[permitted_key]
end
- keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
- if permitted_scalar?(self[k])
- params[k] = self[k]
- end
+ each_key do |key|
+ next unless key =~ /\(\d+[if]?\)\z/
+ next unless $~.pre_match == permitted_key
+
+ params[key] = self[key] if permitted_scalar?(self[key])
end
end
@@ -999,8 +1019,8 @@ module ActionController
#
# It provides an interface for protecting attributes from end-user
# assignment. This makes Action Controller parameters forbidden
- # to be used in Active Model mass assignment until they have been
- # whitelisted.
+ # to be used in Active Model mass assignment until they have been explicitly
+ # enumerated.
#
# In addition, parameters can be marked as required and flow through a
# predefined raise/rescue flow to end up as a <tt>400 Bad Request</tt> with no
@@ -1036,7 +1056,7 @@ module ActionController
# end
#
# In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
- # will need to specify which nested attributes should be whitelisted. You might want
+ # will need to specify which nested attributes should be permitted. You might want
# to allow +:id+ and +:_destroy+, see ActiveRecord::NestedAttributes for more information.
#
# class Person
@@ -1054,7 +1074,7 @@ module ActionController
# private
#
# def person_params
- # # It's mandatory to specify the nested attributes that should be whitelisted.
+ # # It's mandatory to specify the nested attributes that should be permitted.
# # If you use `permit` with just the key that points to the nested attributes hash,
# # it will return an empty hash.
# params.require(:person).permit(:name, :age, pets_attributes: [ :id, :name, :category ])
diff --git a/actionpack/lib/action_controller/renderer.rb b/actionpack/lib/action_controller/renderer.rb
index 49c5b782f0..2b4559c760 100644
--- a/actionpack/lib/action_controller/renderer.rb
+++ b/actionpack/lib/action_controller/renderer.rb
@@ -71,6 +71,21 @@ module ActionController
end
# Render templates with any options from ActionController::Base#render_to_string.
+ #
+ # The primary options are:
+ # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt> for details.
+ # * <tt>:file</tt> - Renders an explicit template file. Add <tt>:locals</tt> to pass in, if so desired.
+ # It shouldn’t be used directly with unsanitized user input due to lack of validation.
+ # * <tt>:inline</tt> - Renders a ERB template string.
+ # * <tt>:plain</tt> - Renders provided text and sets the content type as <tt>text/plain</tt>.
+ # * <tt>:html</tt> - Renders the provided HTML safe string, otherwise
+ # performs HTML escape on the string first. Sets the content type as <tt>text/html</tt>.
+ # * <tt>:json</tt> - Renders the provided hash or object in JSON. You don't
+ # need to call <tt>.to_json</tt> on the object you want to render.
+ # * <tt>:body</tt> - Renders provided text and sets content type of <tt>text/plain</tt>.
+ #
+ # If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, the default is
+ # to render a partial and use the second parameter as the locals hash.
def render(*args)
raise "missing controller" unless controller