Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26b40f2b49 | ||
|
|
98326394f0 | ||
|
|
b3fc5add34 | ||
|
|
610d7e3ec4 | ||
|
|
cfb801ed8d | ||
|
|
0edd310c41 | ||
|
|
5ab7dea2f0 | ||
|
|
916f97fbf3 | ||
|
|
2be3f98e11 | ||
|
|
0343f8f0b5 | ||
|
|
987dce0dc2 | ||
|
|
e0492f5d75 | ||
|
|
0fc67e4d56 | ||
|
|
d24ef454e0 | ||
|
|
e561532bab | ||
|
|
1ab4e2d48c | ||
|
|
7937a282a3 | ||
|
|
d35419fa4c | ||
|
|
6c497b8dd9 | ||
|
|
3b33b36de9 | ||
|
|
600a3e16a5 | ||
|
|
ccd7a94d40 | ||
|
|
8f815c422a | ||
|
|
1c3e61725f | ||
|
|
71f60bc4ac | ||
|
|
596ad235a9 | ||
|
|
613ab1c1ab | ||
|
|
c031f82dd2 | ||
|
|
6a01427317 | ||
|
|
843fe89c63 | ||
|
|
74c9d582b2 | ||
|
|
4e4c5a9a7f | ||
|
|
dde88c92b7 | ||
|
|
cb9777be5f | ||
|
|
0882450232 | ||
|
|
ad62d60b20 | ||
|
|
ff13a82dda | ||
|
|
16bdb8d42e | ||
|
|
d6851debd4 | ||
|
|
6d39b0ae07 | ||
|
|
a6af47d213 | ||
|
|
17c52a7983 | ||
|
|
18c1007d3f | ||
|
|
2b6204117f | ||
|
|
b1424dfa49 | ||
|
|
6aaab9e440 | ||
|
|
a10243a569 | ||
|
|
81f00f9024 | ||
|
|
7bcfd3d295 | ||
|
|
e96cf5bea4 | ||
|
|
fb8e9bde57 | ||
|
|
89e40987d8 | ||
|
|
1ac8099f7a | ||
|
|
5d97cfb236 | ||
|
|
7688025404 | ||
|
|
3efa069349 | ||
|
|
b0c1646fee | ||
|
|
3f6cecbfcf | ||
|
|
fdd5ad022d | ||
|
|
3639ca90eb | ||
|
|
efa3ff1c0f | ||
|
|
9bee4a8d4b | ||
|
|
eb2826f135 | ||
|
|
a49269175e | ||
|
|
0de43c445b | ||
|
|
f9b181af05 | ||
|
|
80f1ab20fb | ||
|
|
37102fe6f8 | ||
|
|
ba999970b1 | ||
|
|
951d70e057 | ||
|
|
3a07d62782 | ||
|
|
2c2fa306cc | ||
|
|
28a9a0ac07 | ||
|
|
bcf2756ad2 | ||
|
|
c53ed1e497 | ||
|
|
07088a0cdc | ||
|
|
ff5aaf543b | ||
|
|
52435e97d9 | ||
|
|
e3eab13b86 | ||
|
|
79995e4309 | ||
|
|
8722fbc7a5 | ||
|
|
3901cbe499 | ||
|
|
471d54ce01 | ||
|
|
f23bbe04ef | ||
|
|
f1ea21b2a6 | ||
|
|
b2028c8aa7 |
24
.rvmrc
24
.rvmrc
@@ -1,23 +1 @@
|
|||||||
#!/usr/bin/env bash
|
rvm use 1.8.7@cancan --create
|
||||||
|
|
||||||
# adapted from: http://rvm.beginrescueend.com/workflow/rvmrc/
|
|
||||||
|
|
||||||
ruby_string="1.8.7"
|
|
||||||
gemset_name="cancan"
|
|
||||||
|
|
||||||
if rvm list strings | grep -q "${ruby_string}" ; then
|
|
||||||
|
|
||||||
# Load or create the specified environment
|
|
||||||
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
|
||||||
&& -s "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}" ]] ; then
|
|
||||||
\. "${rvm_path:-$HOME/.rvm}/environments/${ruby_string}@${gemset_name}"
|
|
||||||
else
|
|
||||||
rvm --create "${ruby_string}@${gemset_name}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
# Notify the user to install the desired interpreter before proceeding.
|
|
||||||
echo "${ruby_string} was not found, please run 'rvm install ${ruby_string}' and then cd back into the project directory."
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|||||||
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
rvm:
|
||||||
|
- 1.8.7
|
||||||
|
- ree
|
||||||
|
notifications:
|
||||||
|
recipients:
|
||||||
|
- graf.otodrakula@gmail.com
|
||||||
|
- ryan@railscasts.com
|
||||||
@@ -1,3 +1,86 @@
|
|||||||
|
1.6.6 (September 28, 2011)
|
||||||
|
|
||||||
|
* correct "return cant jump across threads" error when using check_authorization (thanks codeprimate) - issues #463, #469
|
||||||
|
|
||||||
|
* fixing tests in development by specifying with_model version (thanks kirkconnell) - issue #476
|
||||||
|
|
||||||
|
* added travis.yml file for TravisCI support (thanks bai) - issue #427
|
||||||
|
|
||||||
|
* better support for namespaced models (thanks whilefalse) - issues #424
|
||||||
|
|
||||||
|
* adding :id_param option to load_and_authorize_resource (thanks skhisma) - issue #425
|
||||||
|
|
||||||
|
* make default unauthorized message translatable text (thanks nhocki) - issue #409
|
||||||
|
|
||||||
|
* improving DataMapper behavior (thanks psanford, maxsum-corin) - issue #410, #373
|
||||||
|
|
||||||
|
* allow :find_by option to be full find method name - issue #335
|
||||||
|
|
||||||
|
|
||||||
|
1.6.5 (May 18, 2011)
|
||||||
|
|
||||||
|
* pass action and subject through AccessDenied exception when :through isn't found - issue #366
|
||||||
|
|
||||||
|
* many Mongoid adapter improvements (thanks rahearn, cardagin) - issues #363, #352, #343
|
||||||
|
|
||||||
|
* allow :through option to work with private controller methods - issue #360
|
||||||
|
|
||||||
|
* ensure Mongoid::Document is defined before loading Mongoid adapter - issue #359
|
||||||
|
|
||||||
|
* many DataMapper adapter improvements (thanks emmanuel) - issue #355
|
||||||
|
|
||||||
|
* handle checking nil attributes through associations (thanks thatothermitch) - issue #330
|
||||||
|
|
||||||
|
* improve scope merging - issue #328
|
||||||
|
|
||||||
|
|
||||||
|
1.6.4 (March 29, 2011)
|
||||||
|
|
||||||
|
* Fixed mongoid 'or' error - see issue #322
|
||||||
|
|
||||||
|
|
||||||
|
1.6.3 (March 25, 2011)
|
||||||
|
|
||||||
|
* Make sure ActiveRecord::Relation is defined before checking conditions against it so Rails 2 is supported again - see issue #312
|
||||||
|
|
||||||
|
* Return subject passed to authorize! - see issue #314
|
||||||
|
|
||||||
|
|
||||||
|
1.6.2 (March 18, 2011)
|
||||||
|
|
||||||
|
* Fixed instance loading when :singleton option is used - see issue #310
|
||||||
|
|
||||||
|
|
||||||
|
1.6.1 (March 15, 2011)
|
||||||
|
|
||||||
|
* Use Item.new instead of build_item for singleton resource so it doesn't effect database - see issue #304
|
||||||
|
|
||||||
|
* Made accessible_by action default to :index and parent action default to :show instead of :read - see issue #302
|
||||||
|
|
||||||
|
* Reverted Inherited Resources "collection" override since it doesn't seem to be working - see issue #305
|
||||||
|
|
||||||
|
|
||||||
|
1.6.0 (March 11, 2011)
|
||||||
|
|
||||||
|
* Added MetaWhere support - see issue #194 and #261
|
||||||
|
|
||||||
|
* Allow Active Record scopes in Ability conditions - see issue #257
|
||||||
|
|
||||||
|
* Added :if and :unless options to check_authorization - see issue #284
|
||||||
|
|
||||||
|
* Several Inherited Resources fixes (thanks aq1018, tanordheim and stefanoverna)
|
||||||
|
|
||||||
|
* Pass action name to accessible_by call when loading a collection (thanks amw)
|
||||||
|
|
||||||
|
* Added :prepend option to load_and_authorize_resource to load before other filters - see issue #290
|
||||||
|
|
||||||
|
* Fixed spacing issue in I18n message for multi-word model names - see issue #292
|
||||||
|
|
||||||
|
* Load resource collection for any action which doesn't have an "id" parameter - see issue #296
|
||||||
|
|
||||||
|
* Raise an exception when trying to make a Ability condition with both a hash of conditions and a block - see issue #269
|
||||||
|
|
||||||
|
|
||||||
1.5.1 (January 20, 2011)
|
1.5.1 (January 20, 2011)
|
||||||
|
|
||||||
* Fixing deeply nested conditions in Active Record adapter - see issue #246
|
* Fixing deeply nested conditions in Active Record adapter - see issue #246
|
||||||
|
|||||||
7
Gemfile
7
Gemfile
@@ -2,9 +2,10 @@ source "http://rubygems.org"
|
|||||||
|
|
||||||
case ENV["MODEL_ADAPTER"]
|
case ENV["MODEL_ADAPTER"]
|
||||||
when nil, "active_record"
|
when nil, "active_record"
|
||||||
gem "sqlite3-ruby", :require => "sqlite3"
|
gem "sqlite3"
|
||||||
gem "activerecord", :require => "active_record"
|
gem "activerecord", '~> 3.0.9', :require => "active_record"
|
||||||
gem "with_model"
|
gem "with_model", '~> 0.1.5'
|
||||||
|
gem "meta_where"
|
||||||
when "data_mapper"
|
when "data_mapper"
|
||||||
gem "dm-core", "~> 1.0.2"
|
gem "dm-core", "~> 1.0.2"
|
||||||
gem "dm-sqlite-adapter", "~> 1.0.2"
|
gem "dm-sqlite-adapter", "~> 1.0.2"
|
||||||
|
|||||||
16
README.rdoc
16
README.rdoc
@@ -70,17 +70,27 @@ If the user authorization fails, a <tt>CanCan::AccessDenied</tt> exception will
|
|||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
rescue_from CanCan::AccessDenied do |exception|
|
rescue_from CanCan::AccessDenied do |exception|
|
||||||
flash[:alert] = exception.message
|
redirect_to root_url, :alert => exception.message
|
||||||
redirect_to root_url
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
See {Exception Handling}[https://github.com/ryanb/cancan/wiki/exception-handling] for more information.
|
See {Exception Handling}[https://github.com/ryanb/cancan/wiki/exception-handling] for more information.
|
||||||
|
|
||||||
|
|
||||||
|
=== 4. Lock It Down
|
||||||
|
|
||||||
|
If you want to ensure authorization happens on every action in your application, add +check_authorization+ to your ApplicationController.
|
||||||
|
|
||||||
|
class ApplicationController < ActionController::Base
|
||||||
|
check_authorization
|
||||||
|
end
|
||||||
|
|
||||||
|
This will raise an exception if authorization is not performed in an action. If you want to skip this add +skip_authorization_check+ to a controller subclass. See {Ensure Authorization}[https://github.com/ryanb/cancan/wiki/Ensure-Authorization] for more information.
|
||||||
|
|
||||||
|
|
||||||
== Wiki Docs
|
== Wiki Docs
|
||||||
|
|
||||||
* {Upgrading to 1.5}[https://github.com/ryanb/cancan/wiki/Upgrading-to-1.5]
|
* {Upgrading to 1.6}[https://github.com/ryanb/cancan/wiki/Upgrading-to-1.6]
|
||||||
* {Defining Abilities}[https://github.com/ryanb/cancan/wiki/Defining-Abilities]
|
* {Defining Abilities}[https://github.com/ryanb/cancan/wiki/Defining-Abilities]
|
||||||
* {Checking Abilities}[https://github.com/ryanb/cancan/wiki/Checking-Abilities]
|
* {Checking Abilities}[https://github.com/ryanb/cancan/wiki/Checking-Abilities]
|
||||||
* {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/Authorizing-Controller-Actions]
|
* {Authorizing Controller Actions}[https://github.com/ryanb/cancan/wiki/Authorizing-Controller-Actions]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = "cancan"
|
s.name = "cancan"
|
||||||
s.version = "1.5.1"
|
s.version = "1.6.6"
|
||||||
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"
|
||||||
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|||||||
s.require_path = "lib"
|
s.require_path = "lib"
|
||||||
|
|
||||||
s.add_development_dependency 'rspec', '~> 2.1.0'
|
s.add_development_dependency 'rspec', '~> 2.1.0'
|
||||||
s.add_development_dependency 'rails', '~> 3.0.0'
|
s.add_development_dependency 'rails', '~> 3.0.9'
|
||||||
s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43
|
s.add_development_dependency 'rr', '~> 0.10.11' # 1.0.0 has respond_to? issues: http://github.com/btakita/rr/issues/issue/43
|
||||||
s.add_development_dependency 'supermodel', '~> 0.1.4'
|
s.add_development_dependency 'supermodel', '~> 0.1.4'
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ require 'cancan/model_adapters/abstract_adapter'
|
|||||||
require 'cancan/model_adapters/default_adapter'
|
require 'cancan/model_adapters/default_adapter'
|
||||||
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
|
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
|
||||||
require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
|
require 'cancan/model_adapters/data_mapper_adapter' if defined? DataMapper
|
||||||
require 'cancan/model_adapters/mongoid_adapter' if defined? Mongoid
|
require 'cancan/model_adapters/mongoid_adapter' if defined?(Mongoid) && defined?(Mongoid::Document)
|
||||||
|
|||||||
@@ -201,12 +201,13 @@ module CanCan
|
|||||||
message ||= unauthorized_message(action, subject)
|
message ||= unauthorized_message(action, subject)
|
||||||
raise AccessDenied.new(message, action, subject)
|
raise AccessDenied.new(message, action, subject)
|
||||||
end
|
end
|
||||||
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
def unauthorized_message(action, subject)
|
def unauthorized_message(action, subject)
|
||||||
keys = unauthorized_message_keys(action, subject)
|
keys = unauthorized_message_keys(action, subject)
|
||||||
variables = {:action => action.to_s}
|
variables = {:action => action.to_s}
|
||||||
variables[:subject] = (subject.class == Class ? subject : subject.class).to_s.downcase
|
variables[:subject] = (subject.class == Class ? subject : subject.class).to_s.underscore.humanize.downcase
|
||||||
message = I18n.translate(nil, variables.merge(:scope => :unauthorized, :default => keys + [""]))
|
message = I18n.translate(nil, variables.merge(:scope => :unauthorized, :default => keys + [""]))
|
||||||
message.blank? ? nil : message
|
message.blank? ? nil : message
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ module CanCan
|
|||||||
#
|
#
|
||||||
# load_resource :new => :build
|
# load_resource :new => :build
|
||||||
#
|
#
|
||||||
|
# [:+prepend+]
|
||||||
|
# Passing +true+ will use prepend_before_filter instead of a normal before_filter.
|
||||||
|
#
|
||||||
def load_resource(*args)
|
def load_resource(*args)
|
||||||
cancan_resource_class.add_before_filter(self, :load_resource, *args)
|
cancan_resource_class.add_before_filter(self, :load_resource, *args)
|
||||||
end
|
end
|
||||||
@@ -162,6 +165,9 @@ module CanCan
|
|||||||
# [:+through+]
|
# [:+through+]
|
||||||
# Authorize conditions on this parent resource when instance isn't available.
|
# Authorize conditions on this parent resource when instance isn't available.
|
||||||
#
|
#
|
||||||
|
# [:+prepend+]
|
||||||
|
# Passing +true+ will use prepend_before_filter instead of a normal before_filter.
|
||||||
|
#
|
||||||
def authorize_resource(*args)
|
def authorize_resource(*args)
|
||||||
cancan_resource_class.add_before_filter(self, :authorize_resource, *args)
|
cancan_resource_class.add_before_filter(self, :authorize_resource, *args)
|
||||||
end
|
end
|
||||||
@@ -180,7 +186,7 @@ module CanCan
|
|||||||
skip_authorize_resource(*args)
|
skip_authorize_resource(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Skip both the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
# Skip the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
||||||
# only do authorization on certain actions. You can pass :only and :except options to specify which actions to
|
# only do authorization on certain actions. You can pass :only and :except options to specify which actions to
|
||||||
# skip the effects on. It will apply to all actions by default.
|
# skip the effects on. It will apply to all actions by default.
|
||||||
#
|
#
|
||||||
@@ -196,7 +202,7 @@ module CanCan
|
|||||||
cancan_skipper[:load][name] = options
|
cancan_skipper[:load][name] = options
|
||||||
end
|
end
|
||||||
|
|
||||||
# Skip both the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
# Skip the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
||||||
# only do loading on certain actions. You can pass :only and :except options to specify which actions to
|
# only do loading on certain actions. You can pass :only and :except options to specify which actions to
|
||||||
# skip the effects on. It will apply to all actions by default.
|
# skip the effects on. It will apply to all actions by default.
|
||||||
#
|
#
|
||||||
@@ -220,16 +226,33 @@ module CanCan
|
|||||||
# check_authorization
|
# check_authorization
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# Any arguments are passed to the +after_filter+ it triggers.
|
|
||||||
#
|
|
||||||
# See skip_authorization_check to bypass this check on specific controller actions.
|
# See skip_authorization_check to bypass this check on specific controller actions.
|
||||||
def check_authorization(*args)
|
#
|
||||||
self.after_filter(*args) do |controller|
|
# Options:
|
||||||
unless controller.instance_variable_defined?(:@_authorized)
|
# [:+only+]
|
||||||
|
# Only applies to given actions.
|
||||||
|
#
|
||||||
|
# [:+except+]
|
||||||
|
# Does not apply to given actions.
|
||||||
|
#
|
||||||
|
# [:+if+]
|
||||||
|
# Supply the name of a controller method to be called. The authorization check only takes place if this returns true.
|
||||||
|
#
|
||||||
|
# check_authorization :if => :admin_controller?
|
||||||
|
#
|
||||||
|
# [:+unless+]
|
||||||
|
# Supply the name of a controller method to be called. The authorization check only takes place if this returns false.
|
||||||
|
#
|
||||||
|
# check_authorization :unless => :devise_controller?
|
||||||
|
#
|
||||||
|
def check_authorization(options = {})
|
||||||
|
self.after_filter(options.slice(:only, :except)) do |controller|
|
||||||
|
next if controller.instance_variable_defined?(:@_authorized)
|
||||||
|
next if options[:if] && !controller.send(options[:if])
|
||||||
|
next if options[:unless] && controller.send(options[:unless])
|
||||||
raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check."
|
raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization_check to bypass this check."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
# Call this in the class of a controller to skip the check_authorization behavior on the actions.
|
# Call this in the class of a controller to skip the check_authorization behavior on the actions.
|
||||||
#
|
#
|
||||||
@@ -263,7 +286,7 @@ module CanCan
|
|||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.extend ClassMethods
|
base.extend ClassMethods
|
||||||
base.helper_method :can?, :cannot?
|
base.helper_method :can?, :cannot?, :current_ability
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raises a CanCan::AccessDenied exception if the current_ability cannot
|
# Raises a CanCan::AccessDenied exception if the current_ability cannot
|
||||||
@@ -294,8 +317,7 @@ module CanCan
|
|||||||
#
|
#
|
||||||
# class ApplicationController < ActionController::Base
|
# class ApplicationController < ActionController::Base
|
||||||
# rescue_from CanCan::AccessDenied do |exception|
|
# rescue_from CanCan::AccessDenied do |exception|
|
||||||
# flash[:alert] = exception.message
|
# redirect_to root_url, :alert => exception.message
|
||||||
# redirect_to root_url
|
|
||||||
# end
|
# end
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ module CanCan
|
|||||||
def self.add_before_filter(controller_class, method, *args)
|
def self.add_before_filter(controller_class, method, *args)
|
||||||
options = args.extract_options!
|
options = args.extract_options!
|
||||||
resource_name = args.first
|
resource_name = args.first
|
||||||
controller_class.before_filter(options.slice(:only, :except)) do |controller|
|
before_filter_method = options.delete(:prepend) ? :prepend_before_filter : :before_filter
|
||||||
|
controller_class.send(before_filter_method, options.slice(:only, :except)) do |controller|
|
||||||
controller.class.cancan_resource_class.new(controller, resource_name, options.except(:only, :except)).send(method)
|
controller.class.cancan_resource_class.new(controller, resource_name, options.except(:only, :except)).send(method)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -77,14 +78,14 @@ module CanCan
|
|||||||
end
|
end
|
||||||
|
|
||||||
def load_collection
|
def load_collection
|
||||||
resource_base.accessible_by(current_ability)
|
resource_base.accessible_by(current_ability, authorization_action)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_resource
|
def build_resource
|
||||||
method_name = @options[:singleton] && resource_base.respond_to?("build_#{name}") ? "build_#{name}" : "new"
|
resource = resource_base.new(@params[name] || {})
|
||||||
resource = resource_base.send(method_name, @params[name] || {})
|
resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
|
||||||
initial_attributes.each do |name, value|
|
initial_attributes.each do |attr_name, value|
|
||||||
resource.send("#{name}=", value)
|
resource.send("#{attr_name}=", value)
|
||||||
end
|
end
|
||||||
resource
|
resource
|
||||||
end
|
end
|
||||||
@@ -96,23 +97,39 @@ module CanCan
|
|||||||
end
|
end
|
||||||
|
|
||||||
def find_resource
|
def find_resource
|
||||||
if @options[:singleton] && resource_base.respond_to?(name)
|
if @options[:singleton] && parent_resource.respond_to?(name)
|
||||||
resource_base.send(name)
|
parent_resource.send(name)
|
||||||
else
|
else
|
||||||
@options[:find_by] ? resource_base.send("find_by_#{@options[:find_by]}!", id_param) : resource_base.find(id_param)
|
if @options[:find_by]
|
||||||
|
if resource_base.respond_to? "find_by_#{@options[:find_by]}!"
|
||||||
|
resource_base.send("find_by_#{@options[:find_by]}!", id_param)
|
||||||
|
else
|
||||||
|
resource_base.send(@options[:find_by], id_param)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
adapter.find(resource_base, id_param)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def adapter
|
||||||
|
ModelAdapters::AbstractAdapter.adapter_class(resource_class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorization_action
|
def authorization_action
|
||||||
parent? ? :read : @params[:action].to_sym
|
parent? ? :show : @params[:action].to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
def id_param
|
def id_param
|
||||||
|
if @options[:id_param]
|
||||||
|
@params[@options[:id_param]]
|
||||||
|
else
|
||||||
@params[parent? ? :"#{name}_id" : :id]
|
@params[parent? ? :"#{name}_id" : :id]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def member_action?
|
def member_action?
|
||||||
!collection_actions.include? @params[:action].to_sym
|
new_actions.include?(@params[:action].to_sym) || @options[:singleton] || ( (@params[:id] || @params[@options[:id_param]]) && !collection_actions.include?(@params[:action].to_sym))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the class used for this resource. This can be overriden by the :class option.
|
# Returns the class used for this resource. This can be overriden by the :class option.
|
||||||
@@ -121,7 +138,7 @@ module CanCan
|
|||||||
def resource_class
|
def resource_class
|
||||||
case @options[:class]
|
case @options[:class]
|
||||||
when false then name.to_sym
|
when false then name.to_sym
|
||||||
when nil then name.to_s.camelize.constantize
|
when nil then namespaced_name.to_s.camelize.constantize
|
||||||
when String then @options[:class].constantize
|
when String then @options[:class].constantize
|
||||||
else @options[:class]
|
else @options[:class]
|
||||||
end
|
end
|
||||||
@@ -154,26 +171,30 @@ module CanCan
|
|||||||
def resource_base
|
def resource_base
|
||||||
if @options[:through]
|
if @options[:through]
|
||||||
if parent_resource
|
if parent_resource
|
||||||
@options[:singleton] ? parent_resource : parent_resource.send(@options[:through_association] || name.to_s.pluralize)
|
@options[:singleton] ? resource_class : parent_resource.send(@options[:through_association] || name.to_s.pluralize)
|
||||||
elsif @options[:shallow]
|
elsif @options[:shallow]
|
||||||
resource_class
|
resource_class
|
||||||
else
|
else
|
||||||
raise AccessDenied # maybe this should be a record not found error instead?
|
raise AccessDenied.new(nil, authorization_action, resource_class) # maybe this should be a record not found error instead?
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
resource_class
|
resource_class
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parent_name
|
||||||
|
@options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
|
||||||
|
end
|
||||||
|
|
||||||
# The object to load this resource through.
|
# The object to load this resource through.
|
||||||
def parent_resource
|
def parent_resource
|
||||||
@options[:through] && [@options[:through]].flatten.map { |i| fetch_parent(i) }.compact.first
|
parent_name && fetch_parent(parent_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_parent(name)
|
def fetch_parent(name)
|
||||||
if @controller.instance_variable_defined? "@#{name}"
|
if @controller.instance_variable_defined? "@#{name}"
|
||||||
@controller.instance_variable_get("@#{name}")
|
@controller.instance_variable_get("@#{name}")
|
||||||
elsif @controller.respond_to? name
|
elsif @controller.respond_to?(name, true)
|
||||||
@controller.send(name)
|
@controller.send(name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -186,6 +207,12 @@ module CanCan
|
|||||||
@name || name_from_controller
|
@name || name_from_controller
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def namespaced_name
|
||||||
|
@params[:controller].sub("Controller", "").singularize.camelize.constantize
|
||||||
|
rescue NameError
|
||||||
|
name
|
||||||
|
end
|
||||||
|
|
||||||
def name_from_controller
|
def name_from_controller
|
||||||
@params[:controller].sub("Controller", "").underscore.split('/').last.singularize
|
@params[:controller].sub("Controller", "").underscore.split('/').last.singularize
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ module CanCan
|
|||||||
@message = message
|
@message = message
|
||||||
@action = action
|
@action = action
|
||||||
@subject = subject
|
@subject = subject
|
||||||
@default_message = "You are not authorized to access this page."
|
@default_message = I18n.t(:"unauthorized.default", :default => "You are not authorized to access this page.")
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ module CanCan
|
|||||||
class InheritedResource < ControllerResource # :nodoc:
|
class InheritedResource < ControllerResource # :nodoc:
|
||||||
def load_resource_instance
|
def load_resource_instance
|
||||||
if parent?
|
if parent?
|
||||||
@controller.send :parent
|
@controller.send :association_chain
|
||||||
|
@controller.instance_variable_get("@#{instance_name}")
|
||||||
elsif new_actions.include? @params[:action].to_sym
|
elsif new_actions.include? @params[:action].to_sym
|
||||||
@controller.send :build_resource
|
@controller.send :build_resource
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
RSpec = Spec unless defined? RSpec # for RSpec 1 compatability
|
rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # for RSpec 1 compatability
|
||||||
RSpec::Matchers.define :be_able_to do |*args|
|
Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
|
||||||
match do |ability|
|
match do |ability|
|
||||||
ability.can?(*args)
|
ability.can?(*args)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ module CanCan
|
|||||||
false # override in subclass
|
false # override in subclass
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Override if you need custom find behavior
|
||||||
|
def self.find(model_class, id)
|
||||||
|
model_class.find(id)
|
||||||
|
end
|
||||||
|
|
||||||
# Used to determine if this model adapter will override the matching behavior for a hash of conditions.
|
# Used to determine if this model adapter will override the matching behavior for a hash of conditions.
|
||||||
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
||||||
def self.override_conditions_hash_matching?(subject, conditions)
|
def self.override_conditions_hash_matching?(subject, conditions)
|
||||||
@@ -26,6 +31,17 @@ module CanCan
|
|||||||
raise NotImplemented, "This model adapter does not support matching on a conditions hash."
|
raise NotImplemented, "This model adapter does not support matching on a conditions hash."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Used to determine if this model adapter will override the matching behavior for a specific condition.
|
||||||
|
# If this returns true then matches_condition? will be called. See Rule#matches_conditions_hash
|
||||||
|
def self.override_condition_matching?(subject, name, value)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Override if override_condition_matching? returns true
|
||||||
|
def self.matches_condition?(subject, name, value)
|
||||||
|
raise NotImplemented, "This model adapter does not support matching on a specific condition."
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(model_class, rules)
|
def initialize(model_class, rules)
|
||||||
@model_class = model_class
|
@model_class = model_class
|
||||||
@rules = rules
|
@rules = rules
|
||||||
|
|||||||
@@ -5,6 +5,37 @@ module CanCan
|
|||||||
model_class <= ActiveRecord::Base
|
model_class <= ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.override_condition_matching?(subject, name, value)
|
||||||
|
name.kind_of?(MetaWhere::Column) if defined? MetaWhere
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.matches_condition?(subject, name, value)
|
||||||
|
subject_value = subject.send(name.column)
|
||||||
|
if name.method.to_s.ends_with? "_any"
|
||||||
|
value.any? { |v| meta_where_match? subject_value, name.method.to_s.sub("_any", ""), v }
|
||||||
|
elsif name.method.to_s.ends_with? "_all"
|
||||||
|
value.all? { |v| meta_where_match? subject_value, name.method.to_s.sub("_all", ""), v }
|
||||||
|
else
|
||||||
|
meta_where_match? subject_value, name.method, value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.meta_where_match?(subject_value, method, value)
|
||||||
|
case method.to_sym
|
||||||
|
when :eq then subject_value == value
|
||||||
|
when :not_eq then subject_value != value
|
||||||
|
when :in then value.include?(subject_value)
|
||||||
|
when :not_in then !value.include?(subject_value)
|
||||||
|
when :lt then subject_value < value
|
||||||
|
when :lteq then subject_value <= value
|
||||||
|
when :gt then subject_value > value
|
||||||
|
when :gteq then subject_value >= value
|
||||||
|
when :matches then subject_value =~ Regexp.new("^" + Regexp.escape(value).gsub("%", ".*") + "$", true)
|
||||||
|
when :does_not_match then !meta_where_match?(subject_value, :matches, value)
|
||||||
|
else raise NotImplemented, "The #{method} MetaWhere condition is not supported."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Returns conditions intended to be used inside a database query. Normally you will not call this
|
# Returns conditions intended to be used inside a database query. Normally you will not call this
|
||||||
# method directly, but instead go through ModelAdditions#accessible_by.
|
# method directly, but instead go through ModelAdditions#accessible_by.
|
||||||
#
|
#
|
||||||
@@ -36,7 +67,7 @@ module CanCan
|
|||||||
conditions.inject({}) do |result_hash, (name, value)|
|
conditions.inject({}) do |result_hash, (name, value)|
|
||||||
if value.kind_of? Hash
|
if value.kind_of? Hash
|
||||||
association_class = model_class.reflect_on_association(name).class_name.constantize
|
association_class = model_class.reflect_on_association(name).class_name.constantize
|
||||||
name = model_class.reflect_on_association(name).table_name
|
name = model_class.reflect_on_association(name).table_name.to_sym
|
||||||
value = tableized_conditions(value, association_class)
|
value = tableized_conditions(value, association_class)
|
||||||
end
|
end
|
||||||
result_hash[name] = value
|
result_hash[name] = value
|
||||||
@@ -55,7 +86,9 @@ module CanCan
|
|||||||
end
|
end
|
||||||
|
|
||||||
def database_records
|
def database_records
|
||||||
if @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
|
if override_scope
|
||||||
|
@model_class.scoped.merge(override_scope)
|
||||||
|
elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
|
||||||
@model_class.where(conditions).joins(joins)
|
@model_class.where(conditions).joins(joins)
|
||||||
else
|
else
|
||||||
@model_class.scoped(:conditions => conditions, :joins => joins)
|
@model_class.scoped(:conditions => conditions, :joins => joins)
|
||||||
@@ -64,6 +97,18 @@ module CanCan
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def override_scope
|
||||||
|
conditions = @rules.map(&:conditions).compact
|
||||||
|
if defined?(ActiveRecord::Relation) && conditions.any? { |c| c.kind_of?(ActiveRecord::Relation) }
|
||||||
|
if conditions.size == 1
|
||||||
|
conditions.first
|
||||||
|
else
|
||||||
|
rule = @rules.detect { |rule| rule.conditions.kind_of?(ActiveRecord::Relation) }
|
||||||
|
raise Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for #{rule.actions.first} #{rule.subjects.first} ability."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def merge_conditions(sql, conditions_hash, behavior)
|
def merge_conditions(sql, conditions_hash, behavior)
|
||||||
if conditions_hash.blank?
|
if conditions_hash.blank?
|
||||||
behavior ? true_sql : false_sql
|
behavior ? true_sql : false_sql
|
||||||
|
|||||||
@@ -5,29 +5,30 @@ module CanCan
|
|||||||
model_class <= DataMapper::Resource
|
model_class <= DataMapper::Resource
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.find(model_class, id)
|
||||||
|
model_class.get(id)
|
||||||
|
end
|
||||||
|
|
||||||
def self.override_conditions_hash_matching?(subject, conditions)
|
def self.override_conditions_hash_matching?(subject, conditions)
|
||||||
conditions.any? { |k,v| !k.kind_of?(Symbol) }
|
conditions.any? { |k,v| !k.kind_of?(Symbol) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.matches_conditions_hash?(subject, conditions)
|
def self.matches_conditions_hash?(subject, conditions)
|
||||||
subject.class.all(:conditions => conditions).include?(subject) # TODO don't use a database query here for performance and other instances
|
collection = DataMapper::Collection.new(subject.query, [ subject ])
|
||||||
|
!!collection.first(conditions)
|
||||||
end
|
end
|
||||||
|
|
||||||
def database_records
|
def database_records
|
||||||
scope = @model_class.all(:conditions => ["0 = 1"])
|
scope = @model_class.all(:conditions => ["0 = 1"])
|
||||||
conditions.each do |condition|
|
cans, cannots = @rules.partition { |r| r.base_behavior }
|
||||||
scope += @model_class.all(:conditions => condition)
|
return scope if cans.empty?
|
||||||
end
|
# apply unions first, then differences. this mean cannot overrides can
|
||||||
|
cans.each { |r| scope += @model_class.all(:conditions => r.conditions) }
|
||||||
|
cannots.each { |r| scope -= @model_class.all(:conditions => r.conditions) }
|
||||||
scope
|
scope
|
||||||
end
|
end
|
||||||
|
end # class DataMapper
|
||||||
|
end # module ModelAdapters
|
||||||
|
end # module CanCan
|
||||||
|
|
||||||
def conditions
|
DataMapper::Model.append_extensions(CanCan::ModelAdditions::ClassMethods)
|
||||||
@rules.map(&:conditions)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
DataMapper::Model.class_eval do
|
|
||||||
include CanCan::ModelAdditions::ClassMethods
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -6,7 +6,14 @@ module CanCan
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.override_conditions_hash_matching?(subject, conditions)
|
def self.override_conditions_hash_matching?(subject, conditions)
|
||||||
conditions.any? { |k,v| !k.kind_of?(Symbol) }
|
conditions.any? do |k,v|
|
||||||
|
key_is_not_symbol = lambda { !k.kind_of?(Symbol) }
|
||||||
|
subject_value_is_array = lambda do
|
||||||
|
subject.respond_to?(k) && subject.send(k).is_a?(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
key_is_not_symbol.call || subject_value_is_array.call
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.matches_conditions_hash?(subject, conditions)
|
def self.matches_conditions_hash?(subject, conditions)
|
||||||
@@ -18,12 +25,20 @@ module CanCan
|
|||||||
def database_records
|
def database_records
|
||||||
if @rules.size == 0
|
if @rules.size == 0
|
||||||
@model_class.where(:_id => {'$exists' => false, '$type' => 7}) # return no records in Mongoid
|
@model_class.where(:_id => {'$exists' => false, '$type' => 7}) # return no records in Mongoid
|
||||||
|
elsif @rules.size == 1 && @rules[0].conditions.is_a?(Mongoid::Criteria)
|
||||||
|
@rules[0].conditions
|
||||||
else
|
else
|
||||||
@rules.inject(@model_class.all) do |records, rule|
|
# we only need to process can rules if
|
||||||
if rule.base_behavior
|
# there are no rules with empty conditions
|
||||||
records.or(rule.conditions)
|
rules = @rules.reject { |rule| rule.conditions.empty? }
|
||||||
|
process_can_rules = @rules.count == rules.count
|
||||||
|
rules.inject(@model_class.all) do |records, rule|
|
||||||
|
if process_can_rules && rule.base_behavior
|
||||||
|
records.or rule.conditions
|
||||||
|
elsif !rule.base_behavior
|
||||||
|
records.excludes rule.conditions
|
||||||
else
|
else
|
||||||
records.excludes(rule.conditions)
|
records
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ module CanCan
|
|||||||
module ModelAdditions
|
module ModelAdditions
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
# Returns a scope which fetches only the records that the passed ability
|
# Returns a scope which fetches only the records that the passed ability
|
||||||
# can perform a given action on. The action defaults to :read. This
|
# can perform a given action on. The action defaults to :index. This
|
||||||
# is usually called from a controller and passed the +current_ability+.
|
# is usually called from a controller and passed the +current_ability+.
|
||||||
#
|
#
|
||||||
# @articles = Article.accessible_by(current_ability)
|
# @articles = Article.accessible_by(current_ability)
|
||||||
@@ -19,7 +19,7 @@ module CanCan
|
|||||||
# @articles = Article.accessible_by(current_ability, :update)
|
# @articles = Article.accessible_by(current_ability, :update)
|
||||||
#
|
#
|
||||||
# Here only the articles which the user can update are returned.
|
# Here only the articles which the user can update are returned.
|
||||||
def accessible_by(ability, action = :read)
|
def accessible_by(ability, action = :index)
|
||||||
ability.model_adapter(self, action).database_records
|
ability.model_adapter(self, action).database_records
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ module CanCan
|
|||||||
# it holds the information about a "can" call made on Ability and provides
|
# it holds the information about a "can" call made on Ability and provides
|
||||||
# helpful methods to determine permission checking and conditions hash generation.
|
# helpful methods to determine permission checking and conditions hash generation.
|
||||||
class Rule # :nodoc:
|
class Rule # :nodoc:
|
||||||
attr_reader :base_behavior, :actions, :conditions
|
attr_reader :base_behavior, :subjects, :actions, :conditions
|
||||||
attr_writer :expanded_actions
|
attr_writer :expanded_actions
|
||||||
|
|
||||||
# The first argument when initializing is the base_behavior which is a true/false
|
# The first argument when initializing is the base_behavior which is a true/false
|
||||||
@@ -11,6 +11,7 @@ module CanCan
|
|||||||
# and subject respectively (such as :read, @project). The third argument is a hash
|
# and subject respectively (such as :read, @project). The third argument is a hash
|
||||||
# of conditions and the last one is the block passed to the "can" call.
|
# of conditions and the last one is the block passed to the "can" call.
|
||||||
def initialize(base_behavior, action, subject, conditions, block)
|
def initialize(base_behavior, action, subject, conditions, block)
|
||||||
|
raise Error, "You are not able to supply a block with a hash of conditions in #{action} #{subject} ability. Use either one." if conditions.kind_of?(Hash) && !block.nil?
|
||||||
@match_all = action.nil? && subject.nil?
|
@match_all = action.nil? && subject.nil?
|
||||||
@base_behavior = base_behavior
|
@base_behavior = base_behavior
|
||||||
@actions = [action].flatten
|
@actions = [action].flatten
|
||||||
@@ -21,7 +22,7 @@ module CanCan
|
|||||||
|
|
||||||
# Matches both the subject and action, not necessarily the conditions
|
# Matches both the subject and action, not necessarily the conditions
|
||||||
def relevant?(action, subject)
|
def relevant?(action, subject)
|
||||||
subject = subject.values.first if subject.kind_of? Hash
|
subject = subject.values.first if subject.class == Hash
|
||||||
@match_all || (matches_action?(action) && matches_subject?(subject))
|
@match_all || (matches_action?(action) && matches_subject?(subject))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ module CanCan
|
|||||||
call_block_with_all(action, subject, extra_args)
|
call_block_with_all(action, subject, extra_args)
|
||||||
elsif @block && !subject_class?(subject)
|
elsif @block && !subject_class?(subject)
|
||||||
@block.call(subject, *extra_args)
|
@block.call(subject, *extra_args)
|
||||||
elsif @conditions.kind_of?(Hash) && subject.kind_of?(Hash)
|
elsif @conditions.kind_of?(Hash) && subject.class == Hash
|
||||||
nested_subject_matches_conditions?(subject)
|
nested_subject_matches_conditions?(subject)
|
||||||
elsif @conditions.kind_of?(Hash) && !subject_class?(subject)
|
elsif @conditions.kind_of?(Hash) && !subject_class?(subject)
|
||||||
matches_conditions_hash?(subject)
|
matches_conditions_hash?(subject)
|
||||||
@@ -100,12 +101,15 @@ module CanCan
|
|||||||
model_adapter(subject).matches_conditions_hash? subject, conditions
|
model_adapter(subject).matches_conditions_hash? subject, conditions
|
||||||
else
|
else
|
||||||
conditions.all? do |name, value|
|
conditions.all? do |name, value|
|
||||||
|
if model_adapter(subject).override_condition_matching? subject, name, value
|
||||||
|
model_adapter(subject).matches_condition? subject, name, value
|
||||||
|
else
|
||||||
attribute = subject.send(name)
|
attribute = subject.send(name)
|
||||||
if value.kind_of?(Hash)
|
if value.kind_of?(Hash)
|
||||||
if attribute.kind_of? Array
|
if attribute.kind_of? Array
|
||||||
attribute.any? { |element| matches_conditions_hash? element, value }
|
attribute.any? { |element| matches_conditions_hash? element, value }
|
||||||
else
|
else
|
||||||
matches_conditions_hash? attribute, value
|
!attribute.nil? && matches_conditions_hash?(attribute, value)
|
||||||
end
|
end
|
||||||
elsif value.kind_of?(Array) || value.kind_of?(Range)
|
elsif value.kind_of?(Array) || value.kind_of?(Range)
|
||||||
value.include? attribute
|
value.include? attribute
|
||||||
@@ -116,6 +120,7 @@ module CanCan
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def nested_subject_matches_conditions?(subject_hash)
|
def nested_subject_matches_conditions?(subject_hash)
|
||||||
parent, child = subject_hash.shift
|
parent, child = subject_hash.shift
|
||||||
|
|||||||
@@ -250,6 +250,13 @@ describe CanCan::Ability do
|
|||||||
@ability.can?(:read, 4..6).should be_false
|
@ability.can?(:read, 4..6).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should not match subjects return nil for methods that must match nested a nested conditions hash" do
|
||||||
|
mock(object_with_foo = Object.new).foo { :bar }
|
||||||
|
@ability.can :read, Array, :first => { :foo => :bar }
|
||||||
|
@ability.can?(:read, [object_with_foo]).should be_true
|
||||||
|
@ability.can?(:read, []).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
it "should not stop at cannot definition when comparing class" do
|
it "should not stop at cannot definition when comparing class" do
|
||||||
@ability.can :read, Range
|
@ability.can :read, Range
|
||||||
@ability.cannot :read, Range, :begin => 1
|
@ability.cannot :read, Range, :begin => 1
|
||||||
@@ -291,6 +298,12 @@ describe CanCan::Ability do
|
|||||||
@ability.can?(:read, 123 => Range).should be_true
|
@ability.can?(:read, 123 => Range).should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should allow to check ability on Hash-like object" do
|
||||||
|
class Container < Hash; end
|
||||||
|
@ability.can :read, Container
|
||||||
|
@ability.can?(:read, Container.new).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
it "should have initial attributes based on hash conditions of 'new' action" do
|
it "should have initial attributes based on hash conditions of 'new' action" do
|
||||||
@ability.can :manage, Range, :foo => "foo", :hash => {:skip => "hashes"}
|
@ability.can :manage, Range, :foo => "foo", :hash => {:skip => "hashes"}
|
||||||
@ability.can :create, Range, :bar => 123, :array => %w[skip arrays]
|
@ability.can :create, Range, :bar => 123, :array => %w[skip arrays]
|
||||||
@@ -311,9 +324,11 @@ describe CanCan::Ability do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not raise access denied exception if ability is authorized to perform an action" do
|
it "should not raise access denied exception if ability is authorized to perform an action and return subject" do
|
||||||
@ability.can :read, :foo
|
@ability.can :read, :foo
|
||||||
lambda { @ability.authorize!(:read, :foo) }.should_not raise_error
|
lambda {
|
||||||
|
@ability.authorize!(:read, :foo).should == :foo
|
||||||
|
}.should_not raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should know when block is used in conditions" do
|
it "should know when block is used in conditions" do
|
||||||
@@ -351,6 +366,14 @@ describe CanCan::Ability do
|
|||||||
@ability.model_adapter(model_class, :read).should == :adapter_instance
|
@ability.model_adapter(model_class, :read).should == :adapter_instance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should raise an error when attempting to use a block with a hash condition since it's not likely what they want" do
|
||||||
|
lambda {
|
||||||
|
@ability.can :read, Array, :published => true do
|
||||||
|
false
|
||||||
|
end
|
||||||
|
}.should raise_error(CanCan::Error, "You are not able to supply a block with a hash of conditions in read Array ability. Use either one.")
|
||||||
|
end
|
||||||
|
|
||||||
describe "unauthorized message" do
|
describe "unauthorized message" do
|
||||||
after(:each) do
|
after(:each) do
|
||||||
I18n.backend = nil
|
I18n.backend = nil
|
||||||
@@ -389,6 +412,7 @@ describe CanCan::Ability do
|
|||||||
it "should have variables for action and subject" do
|
it "should have variables for action and subject" do
|
||||||
I18n.backend.store_translations :en, :unauthorized => {:manage => {:all => "%{action} %{subject}"}} # old syntax for now in case testing with old I18n
|
I18n.backend.store_translations :en, :unauthorized => {:manage => {:all => "%{action} %{subject}"}} # old syntax for now in case testing with old I18n
|
||||||
@ability.unauthorized_message(:update, Array).should == "update array"
|
@ability.unauthorized_message(:update, Array).should == "update array"
|
||||||
|
@ability.unauthorized_message(:update, ArgumentError).should == "update argument error"
|
||||||
@ability.unauthorized_message(:edit, 1..3).should == "edit range"
|
@ability.unauthorized_message(:edit, 1..3).should == "edit range"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ describe CanCan::ControllerAdditions do
|
|||||||
@controller = @controller_class.new
|
@controller = @controller_class.new
|
||||||
stub(@controller).params { {} }
|
stub(@controller).params { {} }
|
||||||
stub(@controller).current_user { :current_user }
|
stub(@controller).current_user { :current_user }
|
||||||
mock(@controller_class).helper_method(:can?, :cannot?)
|
mock(@controller_class).helper_method(:can?, :cannot?, :current_ability)
|
||||||
@controller_class.send(:include, CanCan::ControllerAdditions)
|
@controller_class.send(:include, CanCan::ControllerAdditions)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -42,6 +42,11 @@ describe CanCan::ControllerAdditions do
|
|||||||
@controller_class.load_and_authorize_resource :project, :foo => :bar
|
@controller_class.load_and_authorize_resource :project, :foo => :bar
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "load_and_authorize_resource with :prepend should prepend the before filter" do
|
||||||
|
mock(@controller_class).prepend_before_filter({})
|
||||||
|
@controller_class.load_and_authorize_resource :foo => :bar, :prepend => true
|
||||||
|
end
|
||||||
|
|
||||||
it "authorize_resource should setup a before filter which passes call to ControllerResource" do
|
it "authorize_resource should setup a before filter which passes call to ControllerResource" do
|
||||||
stub(CanCan::ControllerResource).new(@controller, nil, :foo => :bar).mock!.authorize_resource
|
stub(CanCan::ControllerResource).new(@controller, nil, :foo => :bar).mock!.authorize_resource
|
||||||
mock(@controller_class).before_filter(:except => :show) { |options, block| block.call(@controller) }
|
mock(@controller_class).before_filter(:except => :show) { |options, block| block.call(@controller) }
|
||||||
@@ -61,17 +66,33 @@ describe CanCan::ControllerAdditions do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "check_authorization should trigger AuthorizationNotPerformed in after filter" do
|
it "check_authorization should trigger AuthorizationNotPerformed in after filter" do
|
||||||
mock(@controller_class).after_filter(:some_options) { |options, block| block.call(@controller) }
|
mock(@controller_class).after_filter(:only => [:test]) { |options, block| block.call(@controller) }
|
||||||
lambda {
|
lambda {
|
||||||
@controller_class.check_authorization(:some_options)
|
@controller_class.check_authorization(:only => [:test])
|
||||||
}.should raise_error(CanCan::AuthorizationNotPerformed)
|
}.should raise_error(CanCan::AuthorizationNotPerformed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "check_authorization should not trigger AuthorizationNotPerformed when :if is false" do
|
||||||
|
stub(@controller).check_auth? { false }
|
||||||
|
mock(@controller_class).after_filter({}) { |options, block| block.call(@controller) }
|
||||||
|
lambda {
|
||||||
|
@controller_class.check_authorization(:if => :check_auth?)
|
||||||
|
}.should_not raise_error(CanCan::AuthorizationNotPerformed)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "check_authorization should not trigger AuthorizationNotPerformed when :unless is true" do
|
||||||
|
stub(@controller).engine_controller? { true }
|
||||||
|
mock(@controller_class).after_filter({}) { |options, block| block.call(@controller) }
|
||||||
|
lambda {
|
||||||
|
@controller_class.check_authorization(:unless => :engine_controller?)
|
||||||
|
}.should_not raise_error(CanCan::AuthorizationNotPerformed)
|
||||||
|
end
|
||||||
|
|
||||||
it "check_authorization should not raise error when @_authorized is set" do
|
it "check_authorization should not raise error when @_authorized is set" do
|
||||||
@controller.instance_variable_set(:@_authorized, true)
|
@controller.instance_variable_set(:@_authorized, true)
|
||||||
mock(@controller_class).after_filter(:some_options) { |options, block| block.call(@controller) }
|
mock(@controller_class).after_filter(:only => [:test]) { |options, block| block.call(@controller) }
|
||||||
lambda {
|
lambda {
|
||||||
@controller_class.check_authorization(:some_options)
|
@controller_class.check_authorization(:only => [:test])
|
||||||
}.should_not raise_error(CanCan::AuthorizationNotPerformed)
|
}.should_not raise_error(CanCan::AuthorizationNotPerformed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,18 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@project).should == project
|
@controller.instance_variable_get(:@project).should == project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should attempt to load a resource with the same namespace as the controller when using :: for namespace" do
|
||||||
|
module MyEngine
|
||||||
|
class Project < ::Project; end
|
||||||
|
end
|
||||||
|
|
||||||
|
project = MyEngine::Project.create!
|
||||||
|
@params.merge!(:controller => "MyEngine::ProjectsController", :action => "show", :id => project.id)
|
||||||
|
resource = CanCan::ControllerResource.new(@controller)
|
||||||
|
resource.load_resource
|
||||||
|
@controller.instance_variable_get(:@project).should == project
|
||||||
|
end
|
||||||
|
|
||||||
it "should properly load resource for namespaced controller when using '::' for namespace" do
|
it "should properly load resource for namespaced controller when using '::' for namespace" do
|
||||||
project = Project.create!
|
project = Project.create!
|
||||||
@params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
|
@params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
|
||||||
@@ -67,7 +79,7 @@ describe CanCan::ControllerResource do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "should build a collection when on index action when class responds to accessible_by" do
|
it "should build a collection when on index action when class responds to accessible_by" do
|
||||||
stub(Project).accessible_by(@ability) { :found_projects }
|
stub(Project).accessible_by(@ability, :index) { :found_projects }
|
||||||
@params[:action] = "index"
|
@params[:action] = "index"
|
||||||
resource = CanCan::ControllerResource.new(@controller, :project)
|
resource = CanCan::ControllerResource.new(@controller, :project)
|
||||||
resource.load_resource
|
resource.load_resource
|
||||||
@@ -104,13 +116,13 @@ describe CanCan::ControllerResource do
|
|||||||
it "should authorize parent resource in collection action" do
|
it "should authorize parent resource in collection action" do
|
||||||
@params[:action] = "index"
|
@params[:action] = "index"
|
||||||
@controller.instance_variable_set(:@category, :some_category)
|
@controller.instance_variable_set(:@category, :some_category)
|
||||||
stub(@controller).authorize!(:read, :some_category) { raise CanCan::AccessDenied }
|
stub(@controller).authorize!(:show, :some_category) { raise CanCan::AccessDenied }
|
||||||
resource = CanCan::ControllerResource.new(@controller, :category, :parent => true)
|
resource = CanCan::ControllerResource.new(@controller, :category, :parent => true)
|
||||||
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should perform authorization using controller action and loaded model" do
|
it "should perform authorization using controller action and loaded model" do
|
||||||
@params[:action] = "show"
|
@params.merge!(:action => "show", :id => 123)
|
||||||
@controller.instance_variable_set(:@project, :some_project)
|
@controller.instance_variable_set(:@project, :some_project)
|
||||||
stub(@controller).authorize!(:show, :some_project) { raise CanCan::AccessDenied }
|
stub(@controller).authorize!(:show, :some_project) { raise CanCan::AccessDenied }
|
||||||
resource = CanCan::ControllerResource.new(@controller)
|
resource = CanCan::ControllerResource.new(@controller)
|
||||||
@@ -118,27 +130,36 @@ describe CanCan::ControllerResource do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "should perform authorization using controller action and non loaded model" do
|
it "should perform authorization using controller action and non loaded model" do
|
||||||
@params[:action] = "show"
|
@params.merge!(:action => "show", :id => 123)
|
||||||
stub(@controller).authorize!(:show, Project) { raise CanCan::AccessDenied }
|
stub(@controller).authorize!(:show, Project) { raise CanCan::AccessDenied }
|
||||||
resource = CanCan::ControllerResource.new(@controller)
|
resource = CanCan::ControllerResource.new(@controller)
|
||||||
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
lambda { resource.authorize_resource }.should raise_error(CanCan::AccessDenied)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should call load_resource and authorize_resource for load_and_authorize_resource" do
|
it "should call load_resource and authorize_resource for load_and_authorize_resource" do
|
||||||
@params[:action] = "show"
|
@params.merge!(:action => "show", :id => 123)
|
||||||
resource = CanCan::ControllerResource.new(@controller)
|
resource = CanCan::ControllerResource.new(@controller)
|
||||||
mock(resource).load_resource
|
mock(resource).load_resource
|
||||||
mock(resource).authorize_resource
|
mock(resource).authorize_resource
|
||||||
resource.load_and_authorize_resource
|
resource.load_and_authorize_resource
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should not build a resource when on custom collection action" do
|
it "should not build a single resource when on custom collection action even with id" do
|
||||||
@params[:action] = "sort"
|
@params.merge!(:action => "sort", :id => 123)
|
||||||
resource = CanCan::ControllerResource.new(@controller, :collection => [:sort, :list])
|
resource = CanCan::ControllerResource.new(@controller, :collection => [:sort, :list])
|
||||||
resource.load_resource
|
resource.load_resource
|
||||||
@controller.instance_variable_get(:@project).should be_nil
|
@controller.instance_variable_get(:@project).should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should load a collection resource when on custom action with no id param" do
|
||||||
|
stub(Project).accessible_by(@ability, :sort) { :found_projects }
|
||||||
|
@params[:action] = "sort"
|
||||||
|
resource = CanCan::ControllerResource.new(@controller)
|
||||||
|
resource.load_resource
|
||||||
|
@controller.instance_variable_get(:@project).should be_nil
|
||||||
|
@controller.instance_variable_get(:@projects).should == :found_projects
|
||||||
|
end
|
||||||
|
|
||||||
it "should build a resource when on custom new action even when params[:id] exists" do
|
it "should build a resource when on custom new action even when params[:id] exists" do
|
||||||
@params.merge!(:action => "build", :id => 123)
|
@params.merge!(:action => "build", :id => 123)
|
||||||
stub(Project).new { :some_project }
|
stub(Project).new { :some_project }
|
||||||
@@ -226,7 +247,10 @@ describe CanCan::ControllerResource do
|
|||||||
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
||||||
lambda {
|
lambda {
|
||||||
resource.load_resource
|
resource.load_resource
|
||||||
}.should raise_error(CanCan::AccessDenied)
|
}.should raise_error(CanCan::AccessDenied) { |exception|
|
||||||
|
exception.action.should == :show
|
||||||
|
exception.subject.should == Project
|
||||||
|
}
|
||||||
@controller.instance_variable_get(:@project).should be_nil
|
@controller.instance_variable_get(:@project).should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -249,8 +273,8 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@project).should == :some_project
|
@controller.instance_variable_get(:@project).should == :some_project
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find record through has_one association with :singleton option" do
|
it "should find record through has_one association with :singleton option without id param" do
|
||||||
@params.merge!(:action => "show")
|
@params.merge!(:action => "show", :id => nil)
|
||||||
category = Object.new
|
category = Object.new
|
||||||
@controller.instance_variable_set(:@category, category)
|
@controller.instance_variable_set(:@category, category)
|
||||||
stub(category).project { :some_project }
|
stub(category).project { :some_project }
|
||||||
@@ -259,14 +283,14 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@project).should == :some_project
|
@controller.instance_variable_get(:@project).should == :some_project
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should build record through has_one association with :singleton option" do
|
it "should not build record through has_one association with :singleton option because it can cause it to delete it in the database" do
|
||||||
@params.merge!(:action => "create", :project => {:name => "foobar"})
|
@params.merge!(:action => "create", :project => {:name => "foobar"})
|
||||||
category = Object.new
|
category = Category.new
|
||||||
@controller.instance_variable_set(:@category, category)
|
@controller.instance_variable_set(:@category, category)
|
||||||
stub(category).build_project { |attributes| Project.new(attributes) }
|
|
||||||
resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
|
resource = CanCan::ControllerResource.new(@controller, :through => :category, :singleton => true)
|
||||||
resource.load_resource
|
resource.load_resource
|
||||||
@controller.instance_variable_get(:@project).name.should == "foobar"
|
@controller.instance_variable_get(:@project).name.should == "foobar"
|
||||||
|
@controller.instance_variable_get(:@project).category.should == category
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should find record through has_one association with :singleton and :shallow options" do
|
it "should find record through has_one association with :singleton and :shallow options" do
|
||||||
@@ -284,10 +308,10 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@project).name.should == "foobar"
|
@controller.instance_variable_get(:@project).name.should == "foobar"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should only authorize :read action on parent resource" do
|
it "should only authorize :show action on parent resource" do
|
||||||
project = Project.create!
|
project = Project.create!
|
||||||
@params.merge!(:action => "new", :project_id => project.id)
|
@params.merge!(:action => "new", :project_id => project.id)
|
||||||
stub(@controller).authorize!(:read, project) { raise CanCan::AccessDenied }
|
stub(@controller).authorize!(:show, project) { raise CanCan::AccessDenied }
|
||||||
resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
|
resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
|
||||||
lambda { resource.load_and_authorize_resource }.should raise_error(CanCan::AccessDenied)
|
lambda { resource.load_and_authorize_resource }.should raise_error(CanCan::AccessDenied)
|
||||||
end
|
end
|
||||||
@@ -316,6 +340,14 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@custom_project).should == project
|
@controller.instance_variable_get(:@custom_project).should == project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should load resource using custom ID param" do
|
||||||
|
project = Project.create!
|
||||||
|
@params.merge!(:action => "show", :the_project => project.id)
|
||||||
|
resource = CanCan::ControllerResource.new(@controller, :id_param => :the_project)
|
||||||
|
resource.load_resource
|
||||||
|
@controller.instance_variable_get(:@project).should == project
|
||||||
|
end
|
||||||
|
|
||||||
it "should load resource using custom find_by attribute" do
|
it "should load resource using custom find_by attribute" do
|
||||||
project = Project.create!(:name => "foo")
|
project = Project.create!(:name => "foo")
|
||||||
@params.merge!(:action => "show", :id => "foo")
|
@params.merge!(:action => "show", :id => "foo")
|
||||||
@@ -324,6 +356,14 @@ describe CanCan::ControllerResource do
|
|||||||
@controller.instance_variable_get(:@project).should == project
|
@controller.instance_variable_get(:@project).should == project
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should allow full find method to be passed into find_by option" do
|
||||||
|
project = Project.create!(:name => "foo")
|
||||||
|
@params.merge!(:action => "show", :id => "foo")
|
||||||
|
resource = CanCan::ControllerResource.new(@controller, :find_by => :find_by_name)
|
||||||
|
resource.load_resource
|
||||||
|
@controller.instance_variable_get(:@project).should == project
|
||||||
|
end
|
||||||
|
|
||||||
it "should raise ImplementationRemoved when adding :name option" do
|
it "should raise ImplementationRemoved when adding :name option" do
|
||||||
lambda {
|
lambda {
|
||||||
CanCan::ControllerResource.new(@controller, :name => :foo)
|
CanCan::ControllerResource.new(@controller, :name => :foo)
|
||||||
|
|||||||
@@ -32,4 +32,27 @@ describe CanCan::AccessDenied do
|
|||||||
@exception.message.should == "Access denied!"
|
@exception.message.should == "Access denied!"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "i18n in the default message" do
|
||||||
|
after(:each) do
|
||||||
|
I18n.backend = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses i18n for the default message" do
|
||||||
|
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
|
||||||
|
@exception = CanCan::AccessDenied.new
|
||||||
|
@exception.message.should == "This is a different message"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "defaults to a nice message" do
|
||||||
|
@exception = CanCan::AccessDenied.new
|
||||||
|
@exception.message.should == "You are not authorized to access this page."
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not use translation if a message is given" do
|
||||||
|
@exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
|
||||||
|
@exception.message.should == "Hey! You're not welcome here"
|
||||||
|
@exception.message.should_not == "You are not authorized to access this page."
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe CanCan::InheritedResource do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "show should load resource through @controller.resource" do
|
it "show should load resource through @controller.resource" do
|
||||||
@params[:action] = "show"
|
@params.merge!(:action => "show", :id => 123)
|
||||||
stub(@controller).resource { :project_resource }
|
stub(@controller).resource { :project_resource }
|
||||||
CanCan::InheritedResource.new(@controller).load_resource
|
CanCan::InheritedResource.new(@controller).load_resource
|
||||||
@controller.instance_variable_get(:@project).should == :project_resource
|
@controller.instance_variable_get(:@project).should == :project_resource
|
||||||
@@ -25,16 +25,16 @@ describe CanCan::InheritedResource do
|
|||||||
@controller.instance_variable_get(:@project).should == :project_resource
|
@controller.instance_variable_get(:@project).should == :project_resource
|
||||||
end
|
end
|
||||||
|
|
||||||
it "index should load through @controller.parent when parent" do
|
it "index should load through @controller.association_chain when parent" do
|
||||||
@params[:action] = "index"
|
@params[:action] = "index"
|
||||||
stub(@controller).parent { :project_resource }
|
stub(@controller).association_chain { @controller.instance_variable_set(:@project, :project_resource) }
|
||||||
CanCan::InheritedResource.new(@controller, :parent => true).load_resource
|
CanCan::InheritedResource.new(@controller, :parent => true).load_resource
|
||||||
@controller.instance_variable_get(:@project).should == :project_resource
|
@controller.instance_variable_get(:@project).should == :project_resource
|
||||||
end
|
end
|
||||||
|
|
||||||
it "index should load through @controller.end_of_association_chain" do
|
it "index should load through @controller.end_of_association_chain" do
|
||||||
@params[:action] = "index"
|
@params[:action] = "index"
|
||||||
stub(Project).accessible_by(@ability) { :projects }
|
stub(Project).accessible_by(@ability, :index) { :projects }
|
||||||
stub(@controller).end_of_association_chain { Project }
|
stub(@controller).end_of_association_chain { Project }
|
||||||
CanCan::InheritedResource.new(@controller).load_resource
|
CanCan::InheritedResource.new(@controller).load_resource
|
||||||
@controller.instance_variable_get(:@projects).should == :projects
|
@controller.instance_variable_get(:@projects).should == :projects
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|||||||
|
|
||||||
with_model :article do
|
with_model :article do
|
||||||
table do |t|
|
table do |t|
|
||||||
|
t.string "name"
|
||||||
t.boolean "published"
|
t.boolean "published"
|
||||||
t.boolean "secret"
|
t.boolean "secret"
|
||||||
|
t.integer "priority"
|
||||||
t.integer "category_id"
|
t.integer "category_id"
|
||||||
end
|
end
|
||||||
model do
|
model do
|
||||||
@@ -54,6 +56,11 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|||||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should find record" do
|
||||||
|
article = Article.create!
|
||||||
|
CanCan::ModelAdapters::ActiveRecordAdapter.find(Article, article.id).should == article
|
||||||
|
end
|
||||||
|
|
||||||
it "should not fetch any records when no abilities are defined" do
|
it "should not fetch any records when no abilities are defined" do
|
||||||
Article.create!
|
Article.create!
|
||||||
Article.accessible_by(@ability).should be_empty
|
Article.accessible_by(@ability).should be_empty
|
||||||
@@ -110,10 +117,34 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|||||||
@ability.can :read, Article, :published => true
|
@ability.can :read, Article, :published => true
|
||||||
@ability.can :read, Article, ["secret=?", true]
|
@ability.can :read, Article, ["secret=?", true]
|
||||||
article1 = Article.create!(:published => true, :secret => false)
|
article1 = Article.create!(:published => true, :secret => false)
|
||||||
|
article2 = Article.create!(:published => true, :secret => true)
|
||||||
|
article3 = Article.create!(:published => false, :secret => true)
|
||||||
article4 = Article.create!(:published => false, :secret => false)
|
article4 = Article.create!(:published => false, :secret => false)
|
||||||
|
Article.accessible_by(@ability).should == [article1, article2, article3]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should allow a scope for conditions" do
|
||||||
|
@ability.can :read, Article, Article.where(:secret => true)
|
||||||
|
article1 = Article.create!(:secret => true)
|
||||||
|
article2 = Article.create!(:secret => false)
|
||||||
Article.accessible_by(@ability).should == [article1]
|
Article.accessible_by(@ability).should == [article1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should fetch only associated records when using with a scope for conditions" do
|
||||||
|
@ability.can :read, Article, Article.where(:secret => true)
|
||||||
|
category1 = Category.create!(:visible => false)
|
||||||
|
category2 = Category.create!(:visible => true)
|
||||||
|
article1 = Article.create!(:secret => true, :category => category1)
|
||||||
|
article2 = Article.create!(:secret => true, :category => category2)
|
||||||
|
category1.articles.accessible_by(@ability).should == [article1]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise an exception when trying to merge scope with other conditions" do
|
||||||
|
@ability.can :read, Article, :published => true
|
||||||
|
@ability.can :read, Article, Article.where(:secret => true)
|
||||||
|
lambda { Article.accessible_by(@ability) }.should raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Article ability.")
|
||||||
|
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, Article do
|
@ability.can :read, Article do
|
||||||
false
|
false
|
||||||
@@ -199,5 +230,49 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
|||||||
@ability.can :read, Article, :project => { :admin => true }
|
@ability.can :read, Article, :project => { :admin => true }
|
||||||
@ability.model_adapter(Article, :read).joins.should == [:project]
|
@ability.model_adapter(Article, :read).joins.should == [:project]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should restrict articles given a MetaWhere condition" do
|
||||||
|
@ability.can :read, Article, :priority.lt => 2
|
||||||
|
article1 = Article.create!(:priority => 1)
|
||||||
|
article2 = Article.create!(:priority => 3)
|
||||||
|
Article.accessible_by(@ability).should == [article1]
|
||||||
|
@ability.should be_able_to(:read, article1)
|
||||||
|
@ability.should_not be_able_to(:read, article2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should match any MetaWhere condition" do
|
||||||
|
adapter = CanCan::ModelAdapters::ActiveRecordAdapter
|
||||||
|
article1 = Article.new(:priority => 1, :name => "Hello World")
|
||||||
|
adapter.matches_condition?(article1, :priority.eq, 1).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.eq, 2).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.eq_any, [1, 2]).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.eq_any, [2, 3]).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.eq_all, [1, 1]).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.eq_all, [1, 2]).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.ne, 2).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.ne, 1).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.in, [1, 2]).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.in, [2, 3]).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.nin, [2, 3]).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.nin, [1, 2]).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.lt, 2).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.lt, 1).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.lteq, 1).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.lteq, 0).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.gt, 0).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.gt, 1).should be_false
|
||||||
|
adapter.matches_condition?(article1, :priority.gteq, 1).should be_true
|
||||||
|
adapter.matches_condition?(article1, :priority.gteq, 2).should be_false
|
||||||
|
adapter.matches_condition?(article1, :name.like, "%ello worl%").should be_true
|
||||||
|
adapter.matches_condition?(article1, :name.like, "hello world").should be_true
|
||||||
|
adapter.matches_condition?(article1, :name.like, "hello%").should be_true
|
||||||
|
adapter.matches_condition?(article1, :name.like, "h%d").should be_true
|
||||||
|
adapter.matches_condition?(article1, :name.like, "%helo%").should be_false
|
||||||
|
adapter.matches_condition?(article1, :name.like, "hello").should be_false
|
||||||
|
adapter.matches_condition?(article1, :name.like, "hello.world").should be_false
|
||||||
|
# For some reason this is reporting "The not_matches MetaWhere condition is not supported."
|
||||||
|
# adapter.matches_condition?(article1, :name.nlike, "%helo%").should be_true
|
||||||
|
# adapter.matches_condition?(article1, :name.nlike, "%ello worl%").should be_false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
|||||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should find record" do
|
||||||
|
article = Article.create
|
||||||
|
CanCan::ModelAdapters::DataMapperAdapter.find(Article, article.id).should == article
|
||||||
|
end
|
||||||
|
|
||||||
it "should not fetch any records when no abilities are defined" do
|
it "should not fetch any records when no abilities are defined" do
|
||||||
Article.create
|
Article.create
|
||||||
Article.accessible_by(@ability).should be_empty
|
Article.accessible_by(@ability).should be_empty
|
||||||
@@ -65,7 +70,6 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "should fetch only the articles that are published and not secret" do
|
it "should fetch only the articles that are published and not secret" do
|
||||||
pending "the `cannot` may require some custom SQL, maybe abstract out from Active Record adapter"
|
|
||||||
@ability.can :read, Article, :published => true
|
@ability.can :read, Article, :published => true
|
||||||
@ability.cannot :read, Article, :secret => true
|
@ability.cannot :read, Article, :secret => true
|
||||||
article1 = Article.create(:published => true, :secret => false)
|
article1 = Article.create(:published => true, :secret => false)
|
||||||
|
|||||||
@@ -36,12 +36,26 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|||||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject).should == CanCan::ModelAdapters::MongoidAdapter
|
CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject).should == CanCan::ModelAdapters::MongoidAdapter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should find record" do
|
||||||
|
project = MongoidProject.create
|
||||||
|
CanCan::ModelAdapters::MongoidAdapter.find(MongoidProject, project.id).should == project
|
||||||
|
end
|
||||||
|
|
||||||
it "should compare properties on mongoid documents with the conditions hash" do
|
it "should compare properties on mongoid documents with the conditions hash" do
|
||||||
model = MongoidProject.new
|
model = MongoidProject.new
|
||||||
@ability.can :read, MongoidProject, :id => model.id
|
@ability.can :read, MongoidProject, :id => model.id
|
||||||
@ability.should be_able_to(:read, model)
|
@ability.should be_able_to(:read, model)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should be able to read hashes when field is array" do
|
||||||
|
one_to_three = MongoidProject.create(:numbers => ['one', 'two', 'three'])
|
||||||
|
two_to_five = MongoidProject.create(:numbers => ['two', 'three', 'four', 'five'])
|
||||||
|
|
||||||
|
@ability.can :foo, MongoidProject, :numbers => 'one'
|
||||||
|
@ability.should be_able_to(:foo, one_to_three)
|
||||||
|
@ability.should_not be_able_to(:foo, two_to_five)
|
||||||
|
end
|
||||||
|
|
||||||
it "should return [] when no ability is defined so no records are found" do
|
it "should return [] when no ability is defined so no records are found" do
|
||||||
MongoidProject.create(:title => 'Sir')
|
MongoidProject.create(:title => 'Sir')
|
||||||
MongoidProject.create(:title => 'Lord')
|
MongoidProject.create(:title => 'Lord')
|
||||||
@@ -59,6 +73,15 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|||||||
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should be able to mix empty conditions and hashes" do
|
||||||
|
@ability.can :read, MongoidProject
|
||||||
|
@ability.can :read, MongoidProject, :title => 'Sir'
|
||||||
|
sir = MongoidProject.create(:title => 'Sir')
|
||||||
|
lord = MongoidProject.create(:title => 'Lord')
|
||||||
|
|
||||||
|
MongoidProject.accessible_by(@ability, :read).count.should == 2
|
||||||
|
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
|
||||||
@ability.can :manage, :all
|
@ability.can :manage, :all
|
||||||
sir = MongoidProject.create(:title => 'Sir')
|
sir = MongoidProject.create(:title => 'Sir')
|
||||||
@@ -68,6 +91,14 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
|||||||
MongoidProject.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
|
MongoidProject.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should allow a scope for conditions" do
|
||||||
|
@ability.can :read, MongoidProject, MongoidProject.where(:title => 'Sir')
|
||||||
|
sir = MongoidProject.create(:title => 'Sir')
|
||||||
|
lord = MongoidProject.create(:title => 'Lord')
|
||||||
|
dude = MongoidProject.create(:title => 'Dude')
|
||||||
|
|
||||||
|
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
||||||
|
end
|
||||||
|
|
||||||
describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
|
describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
|
||||||
it "should handle :field.in" do
|
it "should handle :field.in" do
|
||||||
|
|||||||
@@ -29,4 +29,13 @@ end
|
|||||||
|
|
||||||
class Project < SuperModel::Base
|
class Project < SuperModel::Base
|
||||||
belongs_to :category
|
belongs_to :category
|
||||||
|
attr_accessor :category # why doesn't SuperModel do this automatically?
|
||||||
|
|
||||||
|
def self.respond_to?(method, include_private = false)
|
||||||
|
if method.to_s == "find_by_name!" # hack to simulate ActiveRecord
|
||||||
|
true
|
||||||
|
else
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user