Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

use I18n for unauthorization messages - closes #103

  • Loading branch information...
commit a5f838a964f8717c3243eb6e2d5cd3a949a8986a 1 parent 66314a8
@ryanb authored
View
40 lib/cancan/ability.rb
@@ -49,7 +49,6 @@ module Ability
#
# Also see the RSpec Matchers to aid in testing.
def can?(action, subject, *extra_args)
- raise Error, "Nom nom nom. I eated it." if action == :has && subject == :cheezburger
match = relevant_can_definitions(action, subject).detect do |can_definition|
can_definition.matches_conditions?(action, subject, extra_args)
end
@@ -189,9 +188,36 @@ def query(action, subject)
Query.new(subject, relevant_can_definitions_for_query(action, subject))
end
+ # See ControllerAdditions#authorize! for documentation.
+ def authorize!(action, subject, *args)
+ message = nil
+ if args.last.kind_of?(Hash) && args.last.has_key?(:message)
+ message = args.pop[:message]
+ end
+ if cannot?(action, subject, *args)
+ message ||= unauthorized_message(action, subject)
+ raise AccessDenied.new(message, action, subject)
+ end
+ end
+
+ def unauthorized_message(action, subject)
+ keys = unauthorized_message_keys(action, subject)
+ message = I18n.translate(nil, :scope => :unauthorized, :default => keys + [""])
+ message.blank? ? nil : message
+ end
+
private
- # Accepts a hash of aliased actions and returns an array of actions which match.
+ def unauthorized_message_keys(action, subject)
+ subject = (subject.class == Class ? subject : subject.class).name.underscore unless subject.kind_of? Symbol
+ [subject, :all].map do |try_subject|
+ [aliases_for_action(action), :manage].flatten.map do |try_action|
+ :"#{try_action}.#{try_subject}"
+ end
+ end.flatten
+ end
+
+ # Accepts an array of actions and returns an array of actions which match.
# This should be called before "matches?" and other checking methods since they
# rely on the actions to be expanded.
def expand_actions(actions)
@@ -200,6 +226,16 @@ def expand_actions(actions)
end.flatten
end
+ # Given an action, it will try to find all of the actions which are aliased to it.
+ # This does the opposite kind of lookup as expand_actions.
+ def aliases_for_action(action)
+ results = [action]
+ aliased_actions.each do |aliased_action, actions|
+ results += aliases_for_action(aliased_action) if actions.include? action
+ end
+ results
+ end
+
def can_definitions
@can_definitions ||= []
end
View
2  lib/cancan/can_definition.rb
@@ -94,7 +94,7 @@ def matches_conditions_hash?(subject, conditions = @conditions)
end
end
end
-
+
def call_block_with_all(action, subject, extra_args)
if subject.class == Class
@block.call(action, subject, nil, *extra_args)
View
8 lib/cancan/controller_additions.rb
@@ -185,12 +185,8 @@ def self.included(base)
#
# See the load_and_authorize_resource method to automatically add the authorize! behavior
# to the default RESTful actions.
- def authorize!(action, subject, *args)
- message = nil
- if args.last.kind_of?(Hash) && args.last.has_key?(:message)
- message = args.pop[:message]
- end
- raise AccessDenied.new(message, action, subject) if cannot?(action, subject, *args)
+ def authorize!(*args)
+ current_ability.authorize!(*args)
end
def unauthorized!(message = nil)
View
50 spec/cancan/ability_spec.rb
@@ -157,18 +157,7 @@
@ability.can?(:read, 123).should be_false
end
- it "should support block on 'cannot' method" do
- @ability.can :read, :all
- @ability.cannot :read, Integer do |int|
- int > 5
- end
- @ability.can?(:read, "foo").should be_true
- @ability.can?(:read, 3).should be_true
- @ability.can?(:read, 123).should be_false
- end
-
it "should pass to previous can definition, if block returns false or nil" do
- #same as previous
@ability.can :read, :all
@ability.cannot :read, Integer do |int|
int > 10 ? nil : ( int > 5 )
@@ -177,7 +166,6 @@
@ability.can?(:read, 3).should be_true
@ability.can?(:read, 8).should be_false
@ability.can?(:read, 123).should be_true
-
end
it "should always return `false` for single cannot definition" do
@@ -262,9 +250,39 @@
@ability.can?(:read, Array).should be_true
end
- it "should has eated cheezburger" do
- lambda {
- @ability.can? :has, :cheezburger
- }.should raise_error(CanCan::Error, "Nom nom nom. I eated it.")
+ describe "unauthorized message" do
+ after(:each) do
+ I18n.backend = nil
+ end
+
+ it "should use action/subject in i18n" do
+ I18n.backend.store_translations :en, :unauthorized => {:update => {:array => "foo"}}
+ @ability.unauthorized_message(:update, Array).should == "foo"
+ @ability.unauthorized_message(:update, [1, 2, 3]).should == "foo"
+ @ability.unauthorized_message(:update, :missing).should be_nil
+ end
+
+ it "should use symbol as subject directly" do
+ I18n.backend.store_translations :en, :unauthorized => {:has => {:cheezburger => "Nom nom nom. I eated it."}}
+ @ability.unauthorized_message(:has, :cheezburger).should == "Nom nom nom. I eated it."
+ end
+
+ it "should fall back to 'manage' and 'all'" do
+ I18n.backend.store_translations :en, :unauthorized => {
+ :manage => {:all => "manage all", :array => "manage array"},
+ :update => {:all => "update all", :array => "update array"}
+ }
+ @ability.unauthorized_message(:update, Array).should == "update array"
+ @ability.unauthorized_message(:update, Hash).should == "update all"
+ @ability.unauthorized_message(:foo, Array).should == "manage array"
+ @ability.unauthorized_message(:foo, Hash).should == "manage all"
+ end
+
+ it "should follow aliased actions" do
+ I18n.backend.store_translations :en, :unauthorized => {:modify => {:array => "modify array"}}
+ @ability.alias_action :update, :to => :modify
+ @ability.unauthorized_message(:update, Array).should == "modify array"
+ @ability.unauthorized_message(:edit, Array).should == "modify array"
+ end
end
end
View
1  spec/cancan/controller_additions_spec.rb
@@ -15,6 +15,7 @@
end
it "should raise access denied exception if ability us unauthorized to perform a certain action" do
+ # TODO this should probably be moved into Ability spec
begin
@controller.authorize! :read, :foo, 1, 2, 3, :message => "Access denied!"
rescue CanCan::AccessDenied => e
Please sign in to comment.
Something went wrong with that request. Please try again.