diff options
Diffstat (limited to 'actionpack/lib/action_controller')
4 files changed, 132 insertions, 7 deletions
| diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 84e8c2a698..6b8d9384d4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -214,6 +214,7 @@ module ActionController        Caching,        MimeResponds,        ImplicitRender, +      StrongParameters,        Cookies,        Flash, diff --git a/actionpack/lib/action_controller/metal/params_wrapper.rb b/actionpack/lib/action_controller/metal/params_wrapper.rb index 2736948ce0..bbb711b49d 100644 --- a/actionpack/lib/action_controller/metal/params_wrapper.rb +++ b/actionpack/lib/action_controller/metal/params_wrapper.rb @@ -42,9 +42,7 @@ module ActionController    #     end    #    # On ActiveRecord models with no +:include+ or +:exclude+ option set, -  # if attr_accessible is set on that model, it will only wrap the accessible -  # parameters, else it will only wrap the parameters returned by the class -  # method attribute_names. +  # it will only wrap the parameters returned by the class method attribute_names.    #    # If you're going to pass the parameters to an +ActiveModel+ object (such as    # <tt>User.new(params[:user])</tt>), you might consider passing the model class to @@ -165,10 +163,7 @@ module ActionController          unless options[:include] || options[:exclude]            model ||= _default_wrap_model -          role = options.fetch(:as, :default) -          if model.respond_to?(:accessible_attributes) && model.accessible_attributes(role).present? -            options[:include] = model.accessible_attributes(role).to_a -          elsif model.respond_to?(:attribute_names) && model.attribute_names.present? +          if model.respond_to?(:attribute_names) && model.attribute_names.present?              options[:include] = model.attribute_names            end          end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb new file mode 100644 index 0000000000..c3c1921706 --- /dev/null +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -0,0 +1,125 @@ +require 'active_support/concern' +require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/rescuable' + +module ActionController +  class ParameterMissing < KeyError +    attr_reader :param + +    def initialize(param) +      @param = param +      super("key not found: #{param}") +    end +  end + +  class Parameters < ActiveSupport::HashWithIndifferentAccess +    cattr_accessor :permit_all_parameters, instance_accessor: false +    attr_accessor :permitted +    alias :permitted? :permitted + +    def initialize(attributes = nil) +      super(attributes) +      @permitted = self.class.permit_all_parameters +    end + +    def permit! +      @permitted = true +      self +    end + +    def require(key) +      self[key].presence || raise(ParameterMissing.new(key)) +    end + +    alias :required :require + +    def permit(*filters) +      params = self.class.new + +      filters.each do |filter| +        case filter +        when Symbol, String then +          params[filter] = self[filter] if has_key?(filter) +        when Hash then +          self.slice(*filter.keys).each do |key, value| +            return unless value + +            key = key.to_sym + +            params[key] = each_element(value) do |value| +              # filters are a Hash, so we expect value to be a Hash too +              next if filter.is_a?(Hash) && !value.is_a?(Hash) + +              value = self.class.new(value) if !value.respond_to?(:permit) + +              value.permit(*Array.wrap(filter[key])) +            end +          end +        end +      end + +      params.permit! +    end + +    def [](key) +      convert_hashes_to_parameters(key, super) +    end + +    def fetch(key, *args) +      convert_hashes_to_parameters(key, super) +    rescue KeyError +      raise ActionController::ParameterMissing.new(key) +    end + +    def slice(*keys) +      self.class.new(super) +    end + +    def dup +      super.tap do |duplicate| +        duplicate.instance_variable_set :@permitted, @permitted +      end +    end + +    private +      def convert_hashes_to_parameters(key, value) +        if value.is_a?(Parameters) || !value.is_a?(Hash) +          value +        else +          # Convert to Parameters on first access +          self[key] = self.class.new(value) +        end +      end + +      def each_element(object) +        if object.is_a?(Array) +          object.map { |el| yield el }.compact +        elsif object.is_a?(Hash) && object.keys.all? { |k| k =~ /\A-?\d+\z/ } +          hash = object.class.new +          object.each { |k,v| hash[k] = yield v } +          hash +        else +          yield object +        end +      end +  end + +  module StrongParameters +    extend ActiveSupport::Concern +    include ActiveSupport::Rescuable + +    included do +      rescue_from(ActionController::ParameterMissing) do |parameter_missing_exception| +        render text: "Required parameter missing: #{parameter_missing_exception.param}", status: :bad_request +      end +    end + +    def params +      @_params ||= Parameters.new(request.parameters) +    end + +    def params=(val) +      @_params = val.is_a?(Hash) ? Parameters.new(val) : val +    end +  end +end diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index 3ecc105e22..d7e8194bf6 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -19,6 +19,10 @@ module ActionController        ActionController::Helpers.helpers_path = app.helpers_paths      end +    initializer "action_controller.parameters_config" do |app| +      ActionController::Parameters.permit_all_parameters = app.config.action_controller.delete(:permit_all_parameters) +    end +      initializer "action_controller.set_configs" do |app|        paths   = app.config.paths        options = app.config.action_controller | 
