Skip to content

Commit

Permalink
filling in some inline documentation for 1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanb committed Sep 3, 2010
1 parent 1af6c6f commit bf9b8ad
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 36 deletions.
19 changes: 19 additions & 0 deletions 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
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -10,4 +10,4 @@ Spec::Rake::SpecTask.new do |t|
t.spec_opts = ["-c"]
end

task :default => :spec
task :default => :spec
59 changes: 32 additions & 27 deletions lib/cancan/ability.rb
Expand Up @@ -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.
#
Expand Down Expand Up @@ -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
Expand Down
56 changes: 49 additions & 7 deletions lib/cancan/controller_additions.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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.
#
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion lib/cancan/exceptions.rb
Expand Up @@ -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
Expand Down

0 comments on commit bf9b8ad

Please sign in to comment.