diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc
index 4b7ecdcf..8c355db6 100644
--- a/CHANGELOG.rdoc
+++ b/CHANGELOG.rdoc
@@ -1,3 +1,7 @@
+Master
+
+* Adds support for strong_parameters (bryanrite)
+
1.6.10 (May 7, 2013)
* fix matches_conditons_hash for string values on 1.8 (thanks rrosen)
diff --git a/README.rdoc b/README.rdoc
index 9b3baf8d..a65ae004 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -1,6 +1,6 @@
= CanCan
-{}[http://badge.fury.io/rb/cancan]
-{}[http://travis-ci.org/ryanb/cancan]
+{}[http://badge.fury.io/rb/cancan]
+{}[http://travis-ci.org/ryanb/cancan]
{}[https://codeclimate.com/github/ryanb/cancan]
Wiki[https://github.com/ryanb/cancan/wiki] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
@@ -34,7 +34,7 @@ User permissions are defined in an +Ability+ class. CanCan 1.5 includes a Rails
rails g cancan:ability
-In Rails 2.3, just add a new class in `app/models/ability.rb` with the following contents:
+In Rails 2.3, just add a new class in app/models/ability.rb with the following contents:
class Ability
include CanCan::Ability
@@ -76,6 +76,36 @@ Setting this for every action can be tedious, therefore the +load_and_authorize_
See {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/authorizing-controller-actions] for more information.
+==== Strong Parameters
+
+When using strong_parameters or Rails 4+, you have to sanitize inputs before saving the record, in actions such as :create and :update.
+
+By default, CanCan will try to sanitize the input on :create and :update routes by seeing if your controller will respond to the following methods (in order):
+
+* create_params or update_params (depending on the action you are performing)
+* _params such as article_params (this is the default convention in rails for naming your param method)
+* resource_params (a generically named method you could specify in each controller)
+
+Additionally, load_and_authorize_resource can now take a param_method option to specify a custom method in the controller to run to sanitize input.
+
+ class ArticlesController < ApplicationController
+ load_and_authorize_resource param_method: :my_sanitizer
+
+ def create
+ if @article.save
+ # hurray
+ else
+ render :new
+ end
+ end
+
+ private
+
+ def my_sanitizer
+ params.require(:article).permit(:name)
+ end
+ end
+
=== 3. Handle Unauthorized Access
If the user authorization fails, a CanCan::AccessDenied exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.
diff --git a/lib/cancan/controller_resource.rb b/lib/cancan/controller_resource.rb
index d7ca4bfb..d03ea748 100644
--- a/lib/cancan/controller_resource.rb
+++ b/lib/cancan/controller_resource.rb
@@ -220,7 +220,9 @@ def name
end
def resource_params
- if @options[:class]
+ if param_actions.include?(@params[:action].to_sym) && params_method.present?
+ return @controller.send(params_method)
+ elsif @options[:class]
params_key = extract_key(@options[:class])
return @params[params_key] if @params[params_key]
end
@@ -232,6 +234,19 @@ def resource_params_by_namespaced_name
@params[extract_key(namespaced_name)]
end
+ def params_method
+ params_methods.each do |method|
+ return method if @controller.respond_to?(method, true)
+ end
+ nil
+ end
+
+ def params_methods
+ methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
+ methods.unshift(@options[:param_method]) if @options[:param_method].present?
+ methods
+ end
+
def namespace
@params[:controller].split(/::|\//)[0..-2]
end
@@ -258,6 +273,10 @@ def new_actions
[:new, :create] + Array(@options[:new])
end
+ def param_actions
+ [:create, :update]
+ end
+
private
def extract_key(value)
diff --git a/spec/cancan/controller_resource_spec.rb b/spec/cancan/controller_resource_spec.rb
index 2b88dddb..1e3223c9 100644
--- a/spec/cancan/controller_resource_spec.rb
+++ b/spec/cancan/controller_resource_spec.rb
@@ -492,4 +492,54 @@ class Section
expect { resource.load_and_authorize_resource }.not_to raise_error
expect(@controller.instance_variable_get(:@project)).to be_nil
end
+
+ context "with a strong parameters method" do
+
+ it "only calls the santitize method with actions matching param_actions" do
+ @params.merge!(:controller => "project", :action => "update")
+ @controller.stub(:resource_params).and_return(resource: 'params')
+ resource = CanCan::ControllerResource.new(@controller)
+ resource.stub(param_actions: [:create])
+
+ @controller.should_not_receive(:send).with(:resource_params)
+ resource.send("resource_params")
+ end
+
+ it "uses the specified option for santitizing input" do
+ @params.merge!(:controller => "project", :action => "create")
+ @controller.stub(:resource_params).and_return(resource: 'params')
+ @controller.stub(:project_params).and_return(model: 'params')
+ @controller.stub(:create_params).and_return(create: 'params')
+ @controller.stub(:custom_params).and_return(custom: 'params')
+ resource = CanCan::ControllerResource.new(@controller, {param_method: :custom_params})
+ expect(resource.send("resource_params")).to eq(custom: 'params')
+ end
+
+ it "prefers to use the create_params method for santitizing input" do
+ @params.merge!(:controller => "project", :action => "create")
+ @controller.stub(:resource_params).and_return(resource: 'params')
+ @controller.stub(:project_params).and_return(model: 'params')
+ @controller.stub(:create_params).and_return(create: 'params')
+ @controller.stub(:custom_params).and_return(custom: 'params')
+ resource = CanCan::ControllerResource.new(@controller)
+ expect(resource.send("resource_params")).to eq(create: 'params')
+ end
+
+ it "prefers to use the _params method for santitizing input if create is not found" do
+ @params.merge!(:controller => "project", :action => "create")
+ @controller.stub(:resource_params).and_return(resource: 'params')
+ @controller.stub(:project_params).and_return(model: 'params')
+ @controller.stub(:custom_params).and_return(custom: 'params')
+ resource = CanCan::ControllerResource.new(@controller)
+ expect(resource.send("resource_params")).to eq(model: 'params')
+ end
+
+ it "prefers to use the resource_params method for santitizing input if create or model is not found" do
+ @params.merge!(:controller => "project", :action => "create")
+ @controller.stub(:resource_params).and_return(resource: 'params')
+ @controller.stub(:custom_params).and_return(custom: 'params')
+ resource = CanCan::ControllerResource.new(@controller)
+ expect(resource.send("resource_params")).to eq(resource: 'params')
+ end
+ end
end