Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
929579f03b | ||
|
|
f9ad4858f5 | ||
|
|
5c4c179c5a | ||
|
|
78cbea5733 | ||
|
|
cff922915e | ||
|
|
2012311c40 | ||
|
|
55c8a5045b | ||
|
|
344832d199 | ||
|
|
52b33589dc |
@@ -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
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@@ -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
|
||||||
|
|||||||
25
README.rdoc
25
README.rdoc
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user