Skip to content

Commit

Permalink
Merge pull request #1 from bryanrite/yet_another_strong_params_support
Browse files Browse the repository at this point in the history
Support for strong_parameters
  • Loading branch information
bryanrite committed Jan 28, 2014
2 parents 3c97fb3 + 23b2ed6 commit 535e5db
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 4 deletions.
4 changes: 4 additions & 0 deletions 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)
Expand Down
36 changes: 33 additions & 3 deletions README.rdoc
@@ -1,6 +1,6 @@
= CanCan
{<img src="https://fury-badge.herokuapp.com/rb/cancan.png" alt="Gem Version" />}[http://badge.fury.io/rb/cancan]
{<img src="https://secure.travis-ci.org/ryanb/cancan.png?branch=master" />}[http://travis-ci.org/ryanb/cancan]
{<img src="https://fury-badge.herokuapp.com/rb/cancan.png" alt="Gem Version" />}[http://badge.fury.io/rb/cancan]
{<img src="https://secure.travis-ci.org/ryanb/cancan.png?branch=master" />}[http://travis-ci.org/ryanb/cancan]
{<img src="https://codeclimate.com/github/ryanb/cancan.png" />}[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]
Expand Down Expand Up @@ -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 <tt>app/models/ability.rb</tt> with the following contents:

class Ability
include CanCan::Ability
Expand Down Expand Up @@ -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 <tt>strong_parameters</tt> or Rails 4+, you have to sanitize inputs before saving the record, in actions such as <tt>:create</tt> and <tt>:update</tt>.

By default, CanCan will try to sanitize the input on <tt>:create</tt> and <tt>:update</tt> routes by seeing if your controller will respond to the following methods (in order):

* <tt>create_params</tt> or <tt>update_params</tt> (depending on the action you are performing)
* <tt><model_name>_params</tt> such as <tt>article_params</tt> (this is the default convention in rails for naming your param method)
* <tt>resource_params</tt> (a generically named method you could specify in each controller)

Additionally, <tt>load_and_authorize_resource</tt> can now take a <tt>param_method</tt> 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 <tt>CanCan::AccessDenied</tt> exception will be raised. You can catch this and modify its behavior in the +ApplicationController+.
Expand Down
21 changes: 20 additions & 1 deletion lib/cancan/controller_resource.rb
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -258,6 +273,10 @@ def new_actions
[:new, :create] + Array(@options[:new])
end

def param_actions
[:create, :update]
end

private

def extract_key(value)
Expand Down
50 changes: 50 additions & 0 deletions spec/cancan/controller_resource_spec.rb
Expand Up @@ -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 <model_name>_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

0 comments on commit 535e5db

Please sign in to comment.