Permalink
Browse files

filling in some inline documentation for 1.4

  • Loading branch information...
1 parent 1af6c6f commit bf9b8ad1a6dd3f4dc5de8ba431104f9dc98e9529 @ryanb committed Sep 3, 2010
Showing with 103 additions and 36 deletions.
  1. +19 −0 CHANGELOG.rdoc
  2. +1 −1 Rakefile
  3. +32 −27 lib/cancan/ability.rb
  4. +49 −7 lib/cancan/controller_additions.rb
  5. +2 −1 lib/cancan/exceptions.rb
View
19 CHANGELOG.rdoc
@@ -1,3 +1,22 @@
+1.4.0 (not yet released)
+
+* Adding check_authorization and skip_authorization controller class methods to ensure authorization is performed (thanks justinko) - see issue #135
+
+* Setting initial attributes based on ability conditions in new/create actions - see issue #114
+
+* Check parent attributes for nested association in index action - see issue #121
+
+* Supporting nesting in can? method using hash - see issue #121
+
+* Adding I18n support for Access Denied messages (thanks EppO) - see issue #103
+
+* Passing no arguments to +can+ definition will pass action, class, and object to block - see issue #129
+
+* Don't pass action to block in +can+ definition when using :+manage+ option - see issue #129
+
+* No longer calling block in +can+ definition when checking on class - see issue #116
+
+
1.3.4 (August 31, 2010)
* Don't stop at +cannot+ with hash conditions when checking class (thanks tamoya) - see issue #131
View
2 Rakefile
@@ -10,4 +10,4 @@ Spec::Rake::SpecTask.new do |t|
t.spec_opts = ["-c"]
end
-task :default => :spec
+task :default => :spec
View
59 lib/cancan/ability.rb
@@ -16,14 +16,19 @@ module CanCan
# end
#
module Ability
- # Use to check if the user has permission to perform a given action on an object.
+ # Check if the user has permission to perform a given action on an object.
#
# can? :destroy, @project
#
# You can also pass the class instead of an instance (if you don't have one handy).
#
# can? :create, Project
#
+ # Nested resources can be passed through a hash, this way conditions which are
+ # dependent upon the association will work when using a class.
+ #
+ # can? :create, @category => Project
+ #
# Any additional arguments will be passed into the "can" block definition. This
# can be used to pass more information about the user's request for example.
#
@@ -69,53 +74,53 @@ def cannot?(*args)
# can :update, Article
#
# You can pass an array for either of these parameters to match any one.
+ # Here the user has the ability to update or destroy both articles and comments.
#
# can [:update, :destroy], [Article, Comment]
#
- # In this case the user has the ability to update or destroy both articles and comments.
+ # You can pass :all to match any object and :manage to match any action. Here are some examples.
+ #
+ # can :manage, :all
+ # can :update, :all
+ # can :manage, Project
#
- # You can pass a hash of conditions as the third argument.
+ # You can pass a hash of conditions as the third argument. Here the user can only see active projects which he owns.
#
# can :read, Project, :active => true, :user_id => user.id
#
- # Here the user can only see active projects which he owns. See ActiveRecordAdditions#accessible_by
- # for how to use this in database queries.
+ # See ActiveRecordAdditions#accessible_by for how to use this in database queries. These conditions
+ # are also used for initial attributes when building a record in ControllerAdditions#load_resource.
#
- # If the conditions hash does not give you enough control over defining abilities, you can use a block to
- # write any Ruby code you want.
+ # If the conditions hash does not give you enough control over defining abilities, you can use a block
+ # along with any Ruby code you want.
#
# can :update, Project do |project|
- # project && project.groups.include?(user.group)
+ # project.groups.include?(user.group)
# end
#
# If the block returns true then the user has that :update ability for that project, otherwise he
- # will be denied access. It's possible for the passed in model to be nil if one isn't specified,
- # so be sure to take that into consideration.
+ # will be denied access. The downside to using a block is that it cannot be used to generate
+ # conditions for database queries.
#
- # The downside to using a block is that it cannot be used to generate conditions for database queries.
+ # You can pass custom objects into this "can" method, this is usually done with a symbol
+ # and is useful if a class isn't available to define permissions on.
#
- # You can pass :all to reference every type of object. In this case the object type will be passed
- # into the block as well (just in case object is nil).
+ # can :read, :stats
+ # can? :read, :stats # => true
#
- # can :read, :all do |object_class, object|
- # object_class != Order
- # end
+ # IMPORTANT: Neither a hash of conditions or a block will be used when checking permission on a class.
#
- # Here the user has permission to read all objects except orders.
+ # can :update, Project, :priority => 3
+ # can? :update, Project # => true
#
- # You can also pass :manage as the action which will match any action. In this case the action is
- # passed to the block.
+ # If you pass no arguments to +can+, the action, class, and object will be passed to the block and the
+ # block will always be executed. This allows you to override the full behavior if the permissions are
+ # defined in an external source such as the database.
#
- # can :manage, Comment do |action, comment|
- # action != :destroy
+ # can do |action, object_class, object|
+ # # check the database and return true/false
# end
#
- # You can pass custom objects into this "can" method, this is usually done through a symbol
- # and is useful if a class isn't available to define permissions on.
- #
- # can :read, :stats
- # can? :read, :stats # => true
- #
def can(action = nil, subject = nil, conditions = nil, &block)
can_definitions << CanDefinition.new(true, action, subject, conditions, block)
end
View
56 lib/cancan/controller_additions.rb
@@ -21,6 +21,10 @@ def load_and_authorize_resource(*args)
# Article.new(params[:article]) depending upon the action. It does nothing for the "index"
# action.
#
+ # If a conditions hash is used in the Ability, the +new+ and +create+ actions will set
+ # the initial attributes based on these conditions. This way these actions will satisfy
+ # the ability restrictions.
+ #
# Call this method directly on the controller class.
#
# class BooksController < ApplicationController
@@ -148,23 +152,44 @@ def load_resource(*args)
# [:+instance_name+]
# The name of the instance variable for this resource.
#
+ # [:+through+]
+ # Authorize conditions on this parent resource when instance isn't available.
+ #
def authorize_resource(*args)
ControllerResource.add_before_filter(self, :authorize_resource, *args)
end
- def skip_authorization(*args)
- self.before_filter(*args) do |controller|
- controller.instance_variable_set(:@_authorized, true)
- end
- end
-
+ # Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
+ # If neither of these authorization methods are called, a CanCan::AuthorizationNotPerformed exception will be raised.
+ # This is normally added to the ApplicationController to ensure all controller actions do authorization.
+ #
+ # class ApplicationController < ActionController::Base
+ # check_authorization
+ # end
+ #
+ # Any arguments are passed to the +after_filter+ it triggers.
+ #
+ # See skip_authorization to bypass this check on specific controller actions.
def check_authorization(*args)
self.after_filter(*args) do |controller|
unless controller.instance_variable_defined?(:@_authorized)
- raise AuthorizationNotPerformed, "This action does not authorize the user. Add authorize! or authorize_resource to the controller."
+ raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization to bypass this check."
end
end
end
+
+ # Call this in the class of a controller to skip the check_authorization behavior on the actions.
+ #
+ # class HomeController < ApplicationController
+ # skip_authorization :only => :index
+ # end
+ #
+ # Any arguments are passed to the +before_filter+ it triggers.
+ def skip_authorization(*args)
+ self.before_filter(*args) do |controller|
+ controller.instance_variable_set(:@_authorized, true)
+ end
+ end
end
def self.included(base)
@@ -185,6 +210,16 @@ def self.included(base)
#
# authorize! :read, @article, :message => "Not authorized to read #{@article.name}"
#
+ # You can also use I18n to customize the message. Action aliases defined in Ability work here.
+ #
+ # en:
+ # unauthorized:
+ # manage:
+ # all: "Not authorized to perform that action."
+ # user: "Not allowed to manage other user accounts."
+ # update:
+ # project: "Not allowed to update this project."
+ #
# You can rescue from the exception in the controller to customize how unauthorized
# access is displayed to the user.
#
@@ -234,6 +269,13 @@ def current_ability
# <%= link_to "New Project", new_project_path %>
# <% end %>
#
+ # If it's a nested resource, you can pass the parent instance in a hash. This way it will
+ # check conditions which reach through that association.
+ #
+ # <% if can? :create, @category => Project %>
+ # <%= link_to "New Project", new_project_path %>
+ # <% end %>
+ #
# This simply calls "can?" on the current_ability. See Ability#can?.
def can?(*args)
current_ability.can?(*args)
View
3 lib/cancan/exceptions.rb
@@ -27,7 +27,8 @@ class AuthorizationNotPerformed < Error; end
# exception.default_message = "Default error message"
# exception.message # => "Default error message"
#
- # See ControllerAdditions#authorized! for more information on rescuing from this exception.
+ # See ControllerAdditions#authorized! for more information on rescuing from this exception
+ # and customizing the message using I18n.
class AccessDenied < Error
attr_reader :action, :subject
attr_writer :default_message

0 comments on commit bf9b8ad

Please sign in to comment.