9 Commits
1.5.0 ... 1.5.1

Author SHA1 Message Date
Ryan Bates
929579f03b releasing 1.5.1 2011-01-20 10:16:01 -08:00
Ryan Bates
f9ad4858f5 handle deeply nested conditions properly in active record adapter - closes #246 2011-01-20 10:12:46 -08:00
Ryan Bates
5c4c179c5a cleaning up mongoid adapter a little 2011-01-19 10:17:21 -08:00
Ryan Bates
78cbea5733 Merge branch 'master' of https://github.com/stellard/cancan into stellard-master 2011-01-19 09:25:08 -08:00
stellard
cff922915e improved test assertion 2011-01-18 21:47:33 +00:00
Ryan Bates
2012311c40 readme improvements 2011-01-18 11:55:46 -08:00
stellard
55c8a5045b added cannot support and multiple can support 2011-01-18 18:28:03 +00:00
stellard
344832d199 updated mongoid 2011-01-18 18:27:53 +00:00
Ryan Bates
52b33589dc changing flash[:error] to flash[:alert] in rdocs - closes #238 2011-01-18 09:19:22 -08:00
9 changed files with 76 additions and 27 deletions

View File

@@ -1,3 +1,10 @@
1.5.1 (January 20, 2011)
* Fixing deeply nested conditions in Active Record adapter - see issue #246
* Improving Mongoid support for multiple can and cannot definitions (thanks stellard) - see issue #239
1.5.0 (January 11, 2011) 1.5.0 (January 11, 2011)
* Added an Ability generator - see issue #170 * Added an Ability generator - see issue #170

View File

@@ -11,7 +11,7 @@ when "data_mapper"
gem "dm-migrations", "~> 1.0.2" gem "dm-migrations", "~> 1.0.2"
when "mongoid" when "mongoid"
gem "bson_ext", "~> 1.1" gem "bson_ext", "~> 1.1"
gem "mongoid", "~> 2.0.0.beta.19" gem "mongoid", "~> 2.0.0.beta.20"
else else
raise "Unknown model adapter: #{ENV["MODEL_ADAPTER"]}" raise "Unknown model adapter: #{ENV["MODEL_ADAPTER"]}"
end end

View File

@@ -7,7 +7,7 @@ CanCan is an authorization library for Ruby on Rails which restricts what resour
== Installation == Installation
In <b>Rails 3</b>, add this to your Gemfile. In <b>Rails 3</b>, add this to your Gemfile and run the +bundle+ command.
gem "cancan" gem "cancan"
@@ -22,13 +22,19 @@ Alternatively, you can install it as a plugin.
== Getting Started == Getting Started
CanCan expects a +current_user+ method to exist in controllers. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/ryanb/cancan/wiki/changing-defaults] if you need to customize this behavior. CanCan expects a +current_user+ method to exist in the controller. First, set up some authentication (such as Authlogic[https://github.com/binarylogic/authlogic] or Devise[https://github.com/plataformatec/devise]). See {Changing Defaults}[https://github.com/ryanb/cancan/wiki/changing-defaults] if you need different behavior.
Next, make an +Ability+ class. CanCan 1.5 includes a generator for this.
=== 1. Define Abilities
User permissions are defined in an +Ability+ class. CanCan 1.5 includes a Rails 3 generator for creating this class.
rails g cancan:ability rails g cancan:ability
This is where the user permission will be defined. See the comments in models/ability.rb and {Defining Abilities}[https://github.com/ryanb/cancan/wiki/defining-abilities] for details. See {Defining Abilities}[https://github.com/ryanb/cancan/wiki/defining-abilities] for details.
=== 2. Check Abilities & Authorization
The current user's permissions can then be checked using the <tt>can?</tt> and <tt>cannot?</tt> methods in the view and controller. The current user's permissions can then be checked using the <tt>can?</tt> and <tt>cannot?</tt> methods in the view and controller.
@@ -38,14 +44,14 @@ The current user's permissions can then be checked using the <tt>can?</tt> and <
See {Checking Abilities}[https://github.com/ryanb/cancan/wiki/checking-abilities] for more information See {Checking Abilities}[https://github.com/ryanb/cancan/wiki/checking-abilities] for more information
The "authorize!" method in the controller will raise an exception if the user is not able to perform the given action. The <tt>authorize!</tt> method in the controller will raise an exception if the user is not able to perform the given action.
def show def show
@article = Article.find(params[:id]) @article = Article.find(params[:id])
authorize! :read, @article authorize! :read, @article
end end
Setting this for every action can be tedious, therefore the +load_and_authorize_resource+ method is provided to automatically authorize all actions in a RESTful style resource controller. It will use a before filter to load the resource into an instance variable and authorize it for each action. Setting this for every action can be tedious, therefore the +load_and_authorize_resource+ method is provided to automatically authorize all actions in a RESTful style resource controller. It will use a before filter to load the resource into an instance variable and authorize it for every action.
class ArticlesController < ApplicationController class ArticlesController < ApplicationController
load_and_authorize_resource load_and_authorize_resource
@@ -57,6 +63,9 @@ 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. See {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/authorizing-controller-actions] for more information.
=== 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+. 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+.
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
@@ -82,9 +91,9 @@ See {Exception Handling}[https://github.com/ryanb/cancan/wiki/exception-handling
== Questions or Problems? == Questions or Problems?
If you have any issues with CanCan which you cannot find the solution to in the documentation, please add an {issue on GitHub}[https://github.com/ryanb/cancan/issues] or fork the project and send a pull request. If you have any issues with CanCan which you cannot find the solution to in the documentation[https://github.com/ryanb/cancan/wiki], please add an {issue on GitHub}[https://github.com/ryanb/cancan/issues] or fork the project and send a pull request.
To get the specs running you should call +bundle+ and then +rake+. Specs currently do not work in Ruby 1.9 due to the RR mocking framework. See the {spec/README}[https://github.com/ryanb/cancan/blob/master/spec/README.rdoc] for more information. To get the specs running you should call +bundle+ and then +rake+. See the {spec/README}[https://github.com/ryanb/cancan/blob/master/spec/README.rdoc] for more information.
== Special Thanks == Special Thanks

View File

@@ -1,6 +1,6 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "cancan" s.name = "cancan"
s.version = "1.5.0" s.version = "1.5.1"
s.author = "Ryan Bates" s.author = "Ryan Bates"
s.email = "ryan@railscasts.com" s.email = "ryan@railscasts.com"
s.homepage = "http://github.com/ryanb/cancan" s.homepage = "http://github.com/ryanb/cancan"

View File

@@ -294,7 +294,7 @@ module CanCan
# #
# class ApplicationController < ActionController::Base # class ApplicationController < ActionController::Base
# rescue_from CanCan::AccessDenied do |exception| # rescue_from CanCan::AccessDenied do |exception|
# flash[:error] = exception.message # flash[:alert] = exception.message
# redirect_to root_url # redirect_to root_url
# end # end
# end # end

View File

@@ -31,12 +31,13 @@ module CanCan
end end
end end
def tableized_conditions(conditions) def tableized_conditions(conditions, model_class = @model_class)
return conditions unless conditions.kind_of? Hash return conditions unless conditions.kind_of? Hash
conditions.inject({}) do |result_hash, (name, value)| conditions.inject({}) do |result_hash, (name, value)|
if value.kind_of? Hash if value.kind_of? Hash
name = @model_class.reflect_on_association(name).table_name association_class = model_class.reflect_on_association(name).class_name.constantize
value = tableized_conditions(value) name = model_class.reflect_on_association(name).table_name
value = tableized_conditions(value, association_class)
end end
result_hash[name] = value result_hash[name] = value
result_hash result_hash

View File

@@ -16,21 +16,18 @@ module CanCan
end end
def database_records def database_records
@model_class.where(conditions) if @rules.size == 0
end @model_class.where(:_id => {'$exists' => false, '$type' => 7}) # return no records in Mongoid
def conditions
if @rules.size == 0
false_query
else else
@rules.first.conditions @rules.inject(@model_class.all) do |records, rule|
if rule.base_behavior
records.or(rule.conditions)
else
records.excludes(rule.conditions)
end
end
end end
end end
def false_query
# this query is sure to return no results
{:_id => {'$exists' => false, '$type' => 7}} # type 7 is an ObjectID (default for _id)
end
end end
end end
end end

View File

@@ -8,12 +8,23 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:") ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
describe CanCan::ModelAdapters::ActiveRecordAdapter do describe CanCan::ModelAdapters::ActiveRecordAdapter do
with_model :category do
table do |t|
t.boolean "visible"
end
model do
has_many :articles
end
end
with_model :article do with_model :article do
table do |t| table do |t|
t.boolean "published" t.boolean "published"
t.boolean "secret" t.boolean "secret"
t.integer "category_id"
end end
model do model do
belongs_to :category
has_many :comments has_many :comments
end end
end end
@@ -88,6 +99,13 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
Comment.accessible_by(@ability).should == [comment1] Comment.accessible_by(@ability).should == [comment1]
end end
it "should only read comments for visible categories through articles" do
@ability.can :read, Comment, :article => { :category => { :visible => true } }
comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
Comment.accessible_by(@ability).should == [comment1]
end
it "should allow conditions in SQL and merge with hash conditions" do it "should allow conditions in SQL and merge with hash conditions" do
@ability.can :read, Article, :published => true @ability.can :read, Article, :published => true
@ability.can :read, Article, ["secret=?", true] @ability.can :read, Article, ["secret=?", true]

View File

@@ -56,7 +56,7 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
lord = MongoidProject.create(:title => 'Lord') lord = MongoidProject.create(:title => 'Lord')
dude = MongoidProject.create(:title => 'Dude') dude = MongoidProject.create(:title => 'Dude')
MongoidProject.accessible_by(@ability, :read).should == [sir] MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
end end
it "should return everything when the defined ability is manage all" do it "should return everything when the defined ability is manage all" do
@@ -154,7 +154,24 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
@ability.can :read, MongoidProject, :foo => {:bar => 1} @ability.can :read, MongoidProject, :foo => {:bar => 1}
MongoidProject.accessible_by(@ability, :read).entries.first.should == obj MongoidProject.accessible_by(@ability, :read).entries.first.should == obj
end end
it "should exclude from the result if set to cannot" do
obj = MongoidProject.create(:bar => 1)
obj2 = MongoidProject.create(:bar => 2)
@ability.can :read, MongoidProject
@ability.cannot :read, MongoidProject, :bar => 2
MongoidProject.accessible_by(@ability, :read).entries.should == [obj]
end
it "should combine the rules" do
obj = MongoidProject.create(:bar => 1)
obj2 = MongoidProject.create(:bar => 2)
obj3 = MongoidProject.create(:bar => 3)
@ability.can :read, MongoidProject, :bar => 1
@ability.can :read, MongoidProject, :bar => 2
MongoidProject.accessible_by(@ability, :read).entries.should =~ [obj, obj2]
end
it "should not allow to fetch records when ability with just block present" do it "should not allow to fetch records when ability with just block present" do
@ability.can :read, MongoidProject do @ability.can :read, MongoidProject do
false false