From 12602720a538f2678562580a6f3167055e9c7e09 Mon Sep 17 00:00:00 2001 From: Andrey Novikov Date: Mon, 13 Jul 2015 16:56:55 +0300 Subject: [PATCH 1/2] Add autodetection and override of param key for `permitted_attributes` helper. First, trying to get param key for ActiveModel objects, as Rails' `form_for` helper uses it to generate key for parameters hash. See https://github.com/rails/rails/blob/9f44aa08636dfbba9261f4350ec14684425c4b7b/actionview/lib/action_view/helpers/form_helper.rb#L433 for reference. Second, allowing to redefine it with `param_key` option (as element in optional second argument hash), it is useful in case of single table inheritance, when the param key is differs from actual class (as it is getting defined at runtime). And last, use previous behaviour otherwise. Fixes https://github.com/elabs/pundit/issues/286 --- lib/pundit.rb | 11 +++++++++-- spec/pundit_spec.rb | 15 +++++++++++++++ spec/spec_helper.rb | 10 ++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/pundit.rb b/lib/pundit.rb index 610419f..865bc0f 100644 --- a/lib/pundit.rb +++ b/lib/pundit.rb @@ -139,8 +139,15 @@ module Pundit policies[record] ||= Pundit.policy!(pundit_user, record) end - def permitted_attributes(record) - name = record.class.to_s.demodulize.underscore + def permitted_attributes(record, options={}) + name = options.delete(:param_key) + name ||= if record.class.respond_to?(:model_name) # ActiveModel and ActiveRecord + record.class.model_name.param_key + elsif record.respond_to?(:model_name) + record.model_name.param_key + else + record.class.to_s.demodulize.underscore + end params.require(name).permit(policy(record).permitted_attributes) end diff --git a/spec/pundit_spec.rb b/spec/pundit_spec.rb index 7de6fbf..c48e005 100644 --- a/spec/pundit_spec.rb +++ b/spec/pundit_spec.rb @@ -3,6 +3,7 @@ require "spec_helper" describe Pundit do let(:user) { double } let(:post) { Post.new(user) } + let(:customer_post) { Customer::Post.new(user) } let(:comment) { Comment.new } let(:article) { Article.new } let(:controller) { Controller.new(user, { :action => 'update' }) } @@ -343,6 +344,20 @@ describe Pundit do expect(Controller.new(user, params).permitted_attributes(post)).to eq({ 'title' => 'Hello', 'votes' => 5 }) expect(Controller.new(double, params).permitted_attributes(post)).to eq({ 'votes' => 5 }) end + + it "checks policy for permitted attributes for record of a ActiveModel type" do + params = ActionController::Parameters.new({ action: 'update', customer_post: { title: 'Hello', votes: 5, admin: true } }) + + expect(Controller.new(user, params).permitted_attributes(customer_post)).to eq({ 'title' => 'Hello', 'votes' => 5 }) + expect(Controller.new(double, params).permitted_attributes(customer_post)).to eq({ 'votes' => 5 }) + end + + it "checks policy for permitted attributes for record with manually specified param key" do + params = ActionController::Parameters.new({ action: 'update', custom_post: { title: 'Hello', votes: 5, admin: true } }) + + expect(Controller.new(user, params).permitted_attributes(post, param_key: 'custom_post')).to eq({ 'title' => 'Hello', 'votes' => 5 }) + expect(Controller.new(double, params).permitted_attributes(post, param_key: 'custom_post')).to eq({ 'votes' => 5 }) + end end describe "Pundit::NotAuthorizedError" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index cdbf0dd..d5be5ae 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -56,6 +56,16 @@ class Post < Struct.new(:user) def inspect; "#"; end end +module Customer + class Post < Post + def self.model_name + OpenStruct.new(param_key: 'customer_post') + end + def policy_class + PostPolicy + end + end +end class CommentPolicy < Struct.new(:user, :comment); end class CommentPolicy::Scope < Struct.new(:user, :scope) def resolve From db74632a907c757f13eb8a3f59b3acdb60415ac8 Mon Sep 17 00:00:00 2001 From: Andrey Novikov Date: Wed, 15 Jul 2015 12:58:54 +0300 Subject: [PATCH 2/2] Fixes and simplifications for previous commit (to be squashed) --- CHANGELOG.md | 2 ++ lib/pundit.rb | 6 +++--- spec/spec_helper.rb | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 734f5c6..d922506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Pundit +- Add autodetection of param key to `permitted_attributes` helper with ability to override it with `param_key` option. + ## 1.0.1 (2015-05-27) - Fixed a regression where NotAuthorizedError could not be ininitialized with a string. diff --git a/lib/pundit.rb b/lib/pundit.rb index 865bc0f..2edacf4 100644 --- a/lib/pundit.rb +++ b/lib/pundit.rb @@ -141,10 +141,10 @@ module Pundit def permitted_attributes(record, options={}) name = options.delete(:param_key) - name ||= if record.class.respond_to?(:model_name) # ActiveModel and ActiveRecord - record.class.model_name.param_key - elsif record.respond_to?(:model_name) + name ||= if record.respond_to?(:model_name) # ActiveModel and ActiveRecord record.model_name.param_key + elsif record.is_a?(Class) + record.to_s.demodulize.underscore else record.class.to_s.demodulize.underscore end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5be5ae..31acaa1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -58,7 +58,8 @@ end module Customer class Post < Post - def self.model_name + # In ActiveRecord this method is accessible at both object and class level + def model_name OpenStruct.new(param_key: 'customer_post') end def policy_class