Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e89b31bad | ||
|
|
5f1be25419 | ||
|
|
6d7bce78fc | ||
|
|
a0200166cc | ||
|
|
112a995327 | ||
|
|
944804183e | ||
|
|
2b89dbbdfa | ||
|
|
aff8ca60e4 | ||
|
|
33e33c584e | ||
|
|
a1254ca1c6 | ||
|
|
88aba4664a | ||
|
|
b965f5bab4 | ||
|
|
c2c0b86c3a | ||
|
|
d5baed6281 | ||
|
|
7bf683d8f4 | ||
|
|
da663aaed1 | ||
|
|
80a8c39a93 | ||
|
|
b3f9ffe93b | ||
|
|
c27ead5b9f | ||
|
|
0c21831b4d | ||
|
|
b347c7b78c | ||
|
|
1cdd7b3c18 | ||
|
|
7f4f469e58 | ||
|
|
14e1f5cad4 | ||
|
|
8e46ccad8c | ||
|
|
0bbe2e1802 | ||
|
|
10cbfbb923 | ||
|
|
a8a85f13a3 | ||
|
|
c1f7181336 | ||
|
|
70515de8c1 | ||
|
|
b73bd062a8 | ||
|
|
d1176245e6 | ||
|
|
78e1a17258 | ||
|
|
6e8bc851be | ||
|
|
65bbf0e354 | ||
|
|
aed37cda03 | ||
|
|
51702e0f7b | ||
|
|
f166b5945a | ||
|
|
ba01349eb0 | ||
|
|
37a42e3666 | ||
|
|
83e2dcebd0 | ||
|
|
baadcb923b | ||
|
|
e65f9bd4fd | ||
|
|
f18f53c9ce | ||
|
|
98312940e4 | ||
|
|
7797b37c7e | ||
|
|
80ceaf8e17 | ||
|
|
9eebeb2155 | ||
|
|
67a3038628 | ||
|
|
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 |
1
.rbenv-version
Normal file
1
.rbenv-version
Normal file
@@ -0,0 +1 @@
|
||||
1.8.7-p357
|
||||
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
|
||||
125
CHANGELOG.rdoc
125
CHANGELOG.rdoc
@@ -1,3 +1,90 @@
|
||||
1.6.8 (June 25, 2012)
|
||||
|
||||
* improved support for namespaced controllers and models
|
||||
|
||||
* pass :if and :unless options for load and authorize resource (thanks mauriciozaffari)
|
||||
|
||||
* Travis CI badge (thanks plentz)
|
||||
|
||||
* adding Ability#merge for combining multiple abilities (thanks rogercampos)
|
||||
|
||||
* support for multiple MetaWhere rules (thanks andhapp)
|
||||
|
||||
* various fixes for DataMapper, Mongoid, and Inherited Resource integration
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.7...1.6.8]
|
||||
|
||||
|
||||
1.6.7 (October 4, 2011)
|
||||
|
||||
* fixing nested resource problem caused by namespace addition - issue #482
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.6...1.6.7]
|
||||
|
||||
|
||||
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
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.5...1.6.6]
|
||||
|
||||
|
||||
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
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.4...1.6.5]
|
||||
|
||||
|
||||
1.6.4 (March 29, 2011)
|
||||
|
||||
* Fixed mongoid 'or' error - see issue #322
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.3...1.6.4]
|
||||
|
||||
|
||||
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
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.2...1.6.3]
|
||||
|
||||
|
||||
1.6.2 (March 18, 2011)
|
||||
|
||||
* Fixed instance loading when :singleton option is used - see issue #310
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.1...1.6.2]
|
||||
|
||||
|
||||
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
|
||||
@@ -6,6 +93,8 @@
|
||||
|
||||
* Reverted Inherited Resources "collection" override since it doesn't seem to be working - see issue #305
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.6.0...1.6.1]
|
||||
|
||||
|
||||
1.6.0 (March 11, 2011)
|
||||
|
||||
@@ -27,6 +116,8 @@
|
||||
|
||||
* Raise an exception when trying to make a Ability condition with both a hash of conditions and a block - see issue #269
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.5.1...1.6.0]
|
||||
|
||||
|
||||
1.5.1 (January 20, 2011)
|
||||
|
||||
@@ -34,6 +125,8 @@
|
||||
|
||||
* Improving Mongoid support for multiple can and cannot definitions (thanks stellard) - see issue #239
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.5.0...1.5.1]
|
||||
|
||||
|
||||
1.5.0 (January 11, 2011)
|
||||
|
||||
@@ -55,6 +148,8 @@
|
||||
|
||||
* Internal: added .rvmrc to auto-switch to 1.8.7 with gemset - see issue #231
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.4.1...1.5.0]
|
||||
|
||||
|
||||
1.4.1 (November 12, 2010)
|
||||
|
||||
@@ -68,6 +163,8 @@
|
||||
|
||||
* Fix odd behavior when "cache_classes = false" (thanks mphalliday) - see issue #174
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.4.0...1.4.1]
|
||||
|
||||
|
||||
1.4.0 (October 5, 2010)
|
||||
|
||||
@@ -107,11 +204,15 @@
|
||||
|
||||
* No longer calling block in +can+ definition when checking on class - see issue #116
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.4...1.4.0]
|
||||
|
||||
|
||||
1.3.4 (August 31, 2010)
|
||||
|
||||
* Don't stop at +cannot+ with hash conditions when checking class (thanks tamoya) - see issue #131
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.3...1.3.4]
|
||||
|
||||
|
||||
1.3.3 (August 20, 2010)
|
||||
|
||||
@@ -119,16 +220,22 @@
|
||||
|
||||
* Pluralize nested associations for conditions in accessible_by (thanks mlooney) - see issue #123
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.2...1.3.3]
|
||||
|
||||
|
||||
1.3.2 (August 7, 2010)
|
||||
|
||||
* Fixing slice error when passing in custom resource name - see issue #112
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.1...1.3.2]
|
||||
|
||||
|
||||
1.3.1 (August 6, 2010)
|
||||
|
||||
* Fixing protected sanitize_sql error - see issue #111
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.3.0...1.3.1]
|
||||
|
||||
|
||||
1.3.0 (August 6, 2010)
|
||||
|
||||
@@ -156,6 +263,8 @@
|
||||
|
||||
* Supporting deeply nested aliases - see issue #98
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.2.0...1.3.0]
|
||||
|
||||
|
||||
1.2.0 (July 16, 2010)
|
||||
|
||||
@@ -171,11 +280,15 @@
|
||||
|
||||
* Adding joins clause to accessible_by when conditions are across associations
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.1.1...1.2.0]
|
||||
|
||||
|
||||
1.1.1 (April 17, 2010)
|
||||
|
||||
* Fixing behavior in Rails 3 by properly initializing ResourceAuthorization
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.1...1.1.1]
|
||||
|
||||
|
||||
1.1.0 (April 17, 2010)
|
||||
|
||||
@@ -199,6 +312,8 @@
|
||||
|
||||
* Support additional arguments to can? which get passed to the block - see issue #48
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.2...1.1]
|
||||
|
||||
|
||||
1.0.2 (Dec 30, 2009)
|
||||
|
||||
@@ -208,6 +323,8 @@
|
||||
|
||||
* Adding custom message argument to unauthorized! method (thanks tjwallace) - see issue #18
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.1...1.0.2]
|
||||
|
||||
|
||||
1.0.1 (Dec 14, 2009)
|
||||
|
||||
@@ -215,6 +332,8 @@
|
||||
|
||||
* Don't fetch parent of nested resource if *_id parameter is missing so it works with shallow nested routes - see issue #14
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/1.0.0...1.0.1]
|
||||
|
||||
|
||||
1.0.0 (Dec 13, 2009)
|
||||
|
||||
@@ -230,6 +349,8 @@
|
||||
|
||||
* BACKWARDS INCOMPATIBLE: turning load and authorize resource methods into class methods which set up the before filter so they can accept additional arguments.
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.2.1...1.0.0]
|
||||
|
||||
|
||||
0.2.1 (Nov 26, 2009)
|
||||
|
||||
@@ -239,6 +360,8 @@
|
||||
|
||||
* support custom objects (usually symbols) in can definition - see issue #8
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.2.0...0.2.1]
|
||||
|
||||
|
||||
0.2.0 (Nov 17, 2009)
|
||||
|
||||
@@ -250,6 +373,8 @@
|
||||
|
||||
* BACKWARDS INCOMPATIBLE: use Ability#initialize instead of 'prepare' to set up abilities - see issue #4
|
||||
|
||||
* {see the full list of changes}[https://github.com/ryanb/cancan/compare/0.1.0...0.2.0]
|
||||
|
||||
|
||||
0.1.0 (Nov 16, 2009)
|
||||
|
||||
|
||||
4
Gemfile
4
Gemfile
@@ -3,8 +3,8 @@ source "http://rubygems.org"
|
||||
case ENV["MODEL_ADAPTER"]
|
||||
when nil, "active_record"
|
||||
gem "sqlite3"
|
||||
gem "activerecord", :require => "active_record"
|
||||
gem "with_model"
|
||||
gem "activerecord", '~> 3.0.9', :require => "active_record"
|
||||
gem "with_model", "~> 0.2.5"
|
||||
gem "meta_where"
|
||||
when "data_mapper"
|
||||
gem "dm-core", "~> 1.0.2"
|
||||
|
||||
11
README.rdoc
11
README.rdoc
@@ -1,4 +1,4 @@
|
||||
= CanCan
|
||||
= CanCan {<img src="https://secure.travis-ci.org/ryanb/cancan.png" />}[http://travis-ci.org/ryanb/cancan]
|
||||
|
||||
Wiki[https://github.com/ryanb/cancan/wiki] | RDocs[http://rdoc.info/projects/ryanb/cancan] | Screencast[http://railscasts.com/episodes/192-authorization-with-cancan]
|
||||
|
||||
@@ -31,6 +31,15 @@ User permissions are defined in an +Ability+ class. CanCan 1.5 includes a Rails
|
||||
|
||||
rails g cancan:ability
|
||||
|
||||
In Rails 2.3, just add a new class in `app/models/ability.rb` with the folowing contents:
|
||||
|
||||
class Ability
|
||||
include CanCan::Ability
|
||||
|
||||
def initialize(user)
|
||||
end
|
||||
end
|
||||
|
||||
See {Defining Abilities}[https://github.com/ryanb/cancan/wiki/defining-abilities] for details.
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "cancan"
|
||||
s.version = "1.6.1"
|
||||
s.version = "1.6.8"
|
||||
s.author = "Ryan Bates"
|
||||
s.email = "ryan@railscasts.com"
|
||||
s.homepage = "http://github.com/ryanb/cancan"
|
||||
@@ -10,8 +10,8 @@ Gem::Specification.new do |s|
|
||||
s.files = Dir["{lib,spec}/**/*", "[A-Z]*", "init.rb"] - ["Gemfile.lock"]
|
||||
s.require_path = "lib"
|
||||
|
||||
s.add_development_dependency 'rspec', '~> 2.1.0'
|
||||
s.add_development_dependency 'rails', '~> 3.0.0'
|
||||
s.add_development_dependency 'rspec', '~> 2.6.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 'supermodel', '~> 0.1.4'
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@ require 'cancan/model_adapters/abstract_adapter'
|
||||
require 'cancan/model_adapters/default_adapter'
|
||||
require 'cancan/model_adapters/active_record_adapter' if defined? ActiveRecord
|
||||
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,6 +201,7 @@ module CanCan
|
||||
message ||= unauthorized_message(action, subject)
|
||||
raise AccessDenied.new(message, action, subject)
|
||||
end
|
||||
subject
|
||||
end
|
||||
|
||||
def unauthorized_message(action, subject)
|
||||
@@ -227,6 +228,13 @@ module CanCan
|
||||
relevant_rules(action, subject).any?(&:only_raw_sql?)
|
||||
end
|
||||
|
||||
def merge(ability)
|
||||
ability.send(:rules).each do |rule|
|
||||
rules << rule.dup
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unauthorized_message_keys(action, subject)
|
||||
|
||||
@@ -94,7 +94,7 @@ module CanCan
|
||||
# [:+find_by+]
|
||||
# Find using a different attribute other than id. For example.
|
||||
#
|
||||
# load_resource :find_by => :permalink # will use find_by_permlink!(params[:id])
|
||||
# load_resource :find_by => :permalink # will use find_by_permalink!(params[:id])
|
||||
#
|
||||
# [:+collection+]
|
||||
# Specify which actions are resource collection actions in addition to :+index+. This
|
||||
@@ -151,6 +151,9 @@ module CanCan
|
||||
# [:+except+]
|
||||
# Does not apply before filter to given actions.
|
||||
#
|
||||
# [:+singleton+]
|
||||
# Pass +true+ if this is a singleton resource through a +has_one+ association.
|
||||
#
|
||||
# [:+parent+]
|
||||
# True or false depending on if the resource is considered a parent resource. This defaults to +true+ if a resource
|
||||
# name is given which does not match the controller.
|
||||
@@ -186,7 +189,7 @@ module CanCan
|
||||
skip_authorize_resource(*args)
|
||||
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
|
||||
# skip the effects on. It will apply to all actions by default.
|
||||
#
|
||||
@@ -202,7 +205,7 @@ module CanCan
|
||||
cancan_skipper[:load][name] = options
|
||||
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
|
||||
# skip the effects on. It will apply to all actions by default.
|
||||
#
|
||||
@@ -247,9 +250,9 @@ module CanCan
|
||||
#
|
||||
def check_authorization(options = {})
|
||||
self.after_filter(options.slice(:only, :except)) do |controller|
|
||||
return if controller.instance_variable_defined?(:@_authorized)
|
||||
return if options[:if] && !controller.send(options[:if])
|
||||
return if options[:unless] && controller.send(options[:unless])
|
||||
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."
|
||||
end
|
||||
end
|
||||
@@ -286,7 +289,7 @@ module CanCan
|
||||
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
base.helper_method :can?, :cannot?
|
||||
base.helper_method :can?, :cannot?, :current_ability
|
||||
end
|
||||
|
||||
# Raises a CanCan::AccessDenied exception if the current_ability cannot
|
||||
@@ -382,7 +385,7 @@ module CanCan
|
||||
end
|
||||
end
|
||||
|
||||
if defined? ActionController
|
||||
if defined? ActionController::Base
|
||||
ActionController::Base.class_eval do
|
||||
include CanCan::ControllerAdditions
|
||||
end
|
||||
|
||||
@@ -6,8 +6,8 @@ module CanCan
|
||||
options = args.extract_options!
|
||||
resource_name = args.first
|
||||
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.send(before_filter_method, options.slice(:only, :except, :if, :unless)) do |controller|
|
||||
controller.class.cancan_resource_class.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -82,7 +82,11 @@ module CanCan
|
||||
end
|
||||
|
||||
def build_resource
|
||||
resource = resource_base.new(@params[name] || {})
|
||||
resource = resource_base.new(resource_params || {})
|
||||
assign_attributes(resource)
|
||||
end
|
||||
|
||||
def assign_attributes(resource)
|
||||
resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
|
||||
initial_attributes.each do |attr_name, value|
|
||||
resource.send("#{attr_name}=", value)
|
||||
@@ -92,7 +96,7 @@ module CanCan
|
||||
|
||||
def initial_attributes
|
||||
current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, value|
|
||||
@params[name] && @params[name].include?(key)
|
||||
resource_params && resource_params.include?(key)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,8 +104,20 @@ module CanCan
|
||||
if @options[:singleton] && parent_resource.respond_to?(name)
|
||||
parent_resource.send(name)
|
||||
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
|
||||
else
|
||||
adapter.find(resource_base, id_param)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def adapter
|
||||
ModelAdapters::AbstractAdapter.adapter_class(resource_class)
|
||||
end
|
||||
|
||||
def authorization_action
|
||||
@@ -109,11 +125,15 @@ module CanCan
|
||||
end
|
||||
|
||||
def id_param
|
||||
if @options[:id_param]
|
||||
@params[@options[:id_param]]
|
||||
else
|
||||
@params[parent? ? :"#{name}_id" : :id]
|
||||
end
|
||||
end
|
||||
|
||||
def member_action?
|
||||
new_actions.include?(@params[:action].to_sym) || (@params[:id] && !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
|
||||
|
||||
# Returns the class used for this resource. This can be overriden by the :class option.
|
||||
@@ -122,7 +142,7 @@ module CanCan
|
||||
def resource_class
|
||||
case @options[:class]
|
||||
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
|
||||
else @options[:class]
|
||||
end
|
||||
@@ -159,7 +179,7 @@ module CanCan
|
||||
elsif @options[:shallow]
|
||||
resource_class
|
||||
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
|
||||
else
|
||||
resource_class
|
||||
@@ -178,7 +198,7 @@ module CanCan
|
||||
def fetch_parent(name)
|
||||
if @controller.instance_variable_defined? "@#{name}"
|
||||
@controller.instance_variable_get("@#{name}")
|
||||
elsif @controller.respond_to? name
|
||||
elsif @controller.respond_to?(name, true)
|
||||
@controller.send(name)
|
||||
end
|
||||
end
|
||||
@@ -191,6 +211,24 @@ module CanCan
|
||||
@name || name_from_controller
|
||||
end
|
||||
|
||||
def resource_params
|
||||
if @options[:class]
|
||||
@params[@options[:class].to_s.underscore.gsub('/', '_')]
|
||||
else
|
||||
@params[namespaced_name.to_s.underscore.gsub("/", "_")]
|
||||
end
|
||||
end
|
||||
|
||||
def namespace
|
||||
@params[:controller].split("::")[0..-2]
|
||||
end
|
||||
|
||||
def namespaced_name
|
||||
[namespace, name.camelize].join('::').singularize.camelize.constantize
|
||||
rescue NameError
|
||||
name
|
||||
end
|
||||
|
||||
def name_from_controller
|
||||
@params[:controller].sub("Controller", "").underscore.split('/').last.singularize
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ module CanCan
|
||||
@message = message
|
||||
@action = action
|
||||
@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
|
||||
|
||||
def to_s
|
||||
|
||||
@@ -6,7 +6,8 @@ module CanCan
|
||||
@controller.send :association_chain
|
||||
@controller.instance_variable_get("@#{instance_name}")
|
||||
elsif new_actions.include? @params[:action].to_sym
|
||||
@controller.send :build_resource
|
||||
resource = @controller.send :build_resource
|
||||
assign_attributes(resource)
|
||||
else
|
||||
@controller.send :resource
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
RSpec = Spec unless defined? RSpec # for RSpec 1 compatability
|
||||
RSpec::Matchers.define :be_able_to do |*args|
|
||||
rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # for RSpec 1 compatability
|
||||
Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
|
||||
match do |ability|
|
||||
ability.can?(*args)
|
||||
end
|
||||
|
||||
@@ -15,6 +15,11 @@ module CanCan
|
||||
false # override in subclass
|
||||
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.
|
||||
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
||||
def self.override_conditions_hash_matching?(subject, conditions)
|
||||
|
||||
@@ -87,9 +87,14 @@ module CanCan
|
||||
|
||||
def database_records
|
||||
if override_scope
|
||||
override_scope
|
||||
@model_class.scoped.merge(override_scope)
|
||||
elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
|
||||
mergeable_conditions = @rules.select {|rule| rule.unmergeable? }.blank?
|
||||
if mergeable_conditions
|
||||
@model_class.where(conditions).joins(joins)
|
||||
else
|
||||
@model_class.where(*(@rules.map(&:conditions))).joins(joins)
|
||||
end
|
||||
else
|
||||
@model_class.scoped(:conditions => conditions, :joins => joins)
|
||||
end
|
||||
@@ -99,7 +104,7 @@ module CanCan
|
||||
|
||||
def override_scope
|
||||
conditions = @rules.map(&:conditions).compact
|
||||
if conditions.any? { |c| c.kind_of?(ActiveRecord::Relation) }
|
||||
if defined?(ActiveRecord::Relation) && conditions.any? { |c| c.kind_of?(ActiveRecord::Relation) }
|
||||
if conditions.size == 1
|
||||
conditions.first
|
||||
else
|
||||
|
||||
@@ -5,29 +5,30 @@ module CanCan
|
||||
model_class <= DataMapper::Resource
|
||||
end
|
||||
|
||||
def self.find(model_class, id)
|
||||
model_class.get(id)
|
||||
end
|
||||
|
||||
def self.override_conditions_hash_matching?(subject, conditions)
|
||||
conditions.any? { |k,v| !k.kind_of?(Symbol) }
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def database_records
|
||||
scope = @model_class.all(:conditions => ["0 = 1"])
|
||||
conditions.each do |condition|
|
||||
scope += @model_class.all(:conditions => condition)
|
||||
end
|
||||
cans, cannots = @rules.partition { |r| r.base_behavior }
|
||||
return scope if cans.empty?
|
||||
# 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
|
||||
end
|
||||
end # class DataMapper
|
||||
end # module ModelAdapters
|
||||
end # module CanCan
|
||||
|
||||
def conditions
|
||||
@rules.map(&:conditions)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DataMapper::Model.class_eval do
|
||||
include CanCan::ModelAdditions::ClassMethods
|
||||
end
|
||||
DataMapper::Model.append_extensions(CanCan::ModelAdditions::ClassMethods)
|
||||
|
||||
@@ -6,7 +6,14 @@ module CanCan
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def self.matches_conditions_hash?(subject, conditions)
|
||||
@@ -18,12 +25,21 @@ module CanCan
|
||||
def database_records
|
||||
if @rules.size == 0
|
||||
@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
|
||||
@rules.inject(@model_class.all) do |records, rule|
|
||||
if rule.base_behavior
|
||||
records.or(rule.conditions)
|
||||
# we only need to process can rules if
|
||||
# there are no rules with empty conditions
|
||||
rules = @rules.reject { |rule| rule.conditions.empty? && rule.base_behavior }
|
||||
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
|
||||
records.excludes(rule.conditions)
|
||||
records
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,6 +54,10 @@ module CanCan
|
||||
@conditions == {} || @conditions.nil?
|
||||
end
|
||||
|
||||
def unmergeable?
|
||||
@conditions.respond_to?(:keys) && (! @conditions.keys.first.kind_of? Symbol)
|
||||
end
|
||||
|
||||
def associations_hash(conditions = @conditions)
|
||||
hash = {}
|
||||
conditions.map do |name, value|
|
||||
@@ -109,9 +113,9 @@ module CanCan
|
||||
if attribute.kind_of? Array
|
||||
attribute.any? { |element| matches_conditions_hash? element, value }
|
||||
else
|
||||
matches_conditions_hash? attribute, value
|
||||
!attribute.nil? && matches_conditions_hash?(attribute, value)
|
||||
end
|
||||
elsif value.kind_of?(Array) || value.kind_of?(Range)
|
||||
elsif value.kind_of?(Enumerable)
|
||||
value.include? attribute
|
||||
else
|
||||
attribute == value
|
||||
@@ -123,7 +127,7 @@ module CanCan
|
||||
end
|
||||
|
||||
def nested_subject_matches_conditions?(subject_hash)
|
||||
parent, child = subject_hash.shift
|
||||
parent, child = subject_hash.first
|
||||
matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym] || {})
|
||||
end
|
||||
|
||||
@@ -136,7 +140,7 @@ module CanCan
|
||||
end
|
||||
|
||||
def model_adapter(subject)
|
||||
ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
|
||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -250,6 +250,21 @@ describe CanCan::Ability do
|
||||
@ability.can?(:read, 4..6).should be_false
|
||||
end
|
||||
|
||||
it "should accept a set as a condition value" do
|
||||
mock(object_with_foo_2 = Object.new).foo { 2 }
|
||||
mock(object_with_foo_3 = Object.new).foo { 3 }
|
||||
@ability.can :read, Object, :foo => [1, 2, 5].to_set
|
||||
@ability.can?(:read, object_with_foo_2).should be_true
|
||||
@ability.can?(:read, object_with_foo_3).should be_false
|
||||
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
|
||||
@ability.can :read, Range
|
||||
@ability.cannot :read, Range, :begin => 1
|
||||
@@ -291,6 +306,14 @@ describe CanCan::Ability do
|
||||
@ability.can?(:read, 123 => Range).should be_true
|
||||
end
|
||||
|
||||
it "passing a hash of subjects with multiple definitions should check permissions correctly" do
|
||||
@ability.can :read, Range, :string => {:length => 4}
|
||||
@ability.can [:create, :read], Range, :string => {:upcase => 'FOO'}
|
||||
@ability.can?(:read, "foo" => Range).should be_true
|
||||
@ability.can?(:read, "foobar" => Range).should be_false
|
||||
@ability.can?(:read, 1234 => Range).should be_true
|
||||
end
|
||||
|
||||
it "should allow to check ability on Hash-like object" do
|
||||
class Container < Hash; end
|
||||
@ability.can :read, Container
|
||||
@@ -317,9 +340,11 @@ describe CanCan::Ability do
|
||||
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
|
||||
lambda { @ability.authorize!(:read, :foo) }.should_not raise_error
|
||||
lambda {
|
||||
@ability.authorize!(:read, :foo).should == :foo
|
||||
}.should_not raise_error
|
||||
end
|
||||
|
||||
it "should know when block is used in conditions" do
|
||||
@@ -407,4 +432,17 @@ describe CanCan::Ability do
|
||||
@ability.unauthorized_message(:edit, 1..3).should == "edit range"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#merge" do
|
||||
it "should add the rules from the given ability" do
|
||||
@ability.can :use, :tools
|
||||
another_ability = Object.new
|
||||
another_ability.extend(CanCan::Ability)
|
||||
another_ability.can :use, :search
|
||||
|
||||
@ability.merge(another_ability)
|
||||
@ability.can?(:use, :search).should be_true
|
||||
@ability.send(:rules).size.should == 2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ describe CanCan::ControllerAdditions do
|
||||
@controller = @controller_class.new
|
||||
stub(@controller).params { {} }
|
||||
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)
|
||||
end
|
||||
|
||||
@@ -49,14 +49,14 @@ describe CanCan::ControllerAdditions 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
|
||||
mock(@controller_class).before_filter(:except => :show) { |options, block| block.call(@controller) }
|
||||
@controller_class.authorize_resource :foo => :bar, :except => :show
|
||||
mock(@controller_class).before_filter(:except => :show, :if => true) { |options, block| block.call(@controller) }
|
||||
@controller_class.authorize_resource :foo => :bar, :except => :show, :if => true
|
||||
end
|
||||
|
||||
it "load_resource should setup a before filter which passes call to ControllerResource" do
|
||||
stub(CanCan::ControllerResource).new(@controller, nil, :foo => :bar).mock!.load_resource
|
||||
mock(@controller_class).before_filter(:only => [:show, :index]) { |options, block| block.call(@controller) }
|
||||
@controller_class.load_resource :foo => :bar, :only => [:show, :index]
|
||||
mock(@controller_class).before_filter(:only => [:show, :index], :unless => false) { |options, block| block.call(@controller) }
|
||||
@controller_class.load_resource :foo => :bar, :only => [:show, :index], :unless => false
|
||||
end
|
||||
|
||||
it "skip_authorization_check should set up a before filter which sets @_authorized to true" do
|
||||
|
||||
@@ -35,6 +35,30 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@project).should == project
|
||||
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
|
||||
|
||||
# Rails includes namespace in params, see issue #349
|
||||
it "should create through the namespaced params" do
|
||||
module MyEngine
|
||||
class Project < ::Project; end
|
||||
end
|
||||
|
||||
@params.merge!(:controller => "MyEngine::ProjectsController", :action => "create", :my_engine_project => {:name => "foobar"})
|
||||
resource = CanCan::ControllerResource.new(@controller)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).name.should == "foobar"
|
||||
end
|
||||
|
||||
it "should properly load resource for namespaced controller when using '::' for namespace" do
|
||||
project = Project.create!
|
||||
@params.merge!(:controller => "Admin::ProjectsController", :action => "show", :id => project.id)
|
||||
@@ -50,6 +74,14 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@project).name.should == "foobar"
|
||||
end
|
||||
|
||||
it "should build a new resource for namespaced model with hash if params[:id] is not specified" do
|
||||
project = Sub::Project.create!
|
||||
@params.merge!(:action => "create", 'sub_project' => {:name => "foobar"})
|
||||
resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).name.should == "foobar"
|
||||
end
|
||||
|
||||
it "should build a new resource with attributes from current ability" do
|
||||
@params.merge!(:action => "new")
|
||||
@ability.can(:create, Project, :name => "from conditions")
|
||||
@@ -183,10 +215,18 @@ describe CanCan::ControllerResource do
|
||||
resource.should_not be_parent
|
||||
end
|
||||
|
||||
it "should have the specified resource_class if 'name' is passed to load_resource" do
|
||||
class Section
|
||||
end
|
||||
|
||||
resource = CanCan::ControllerResource.new(@controller, :section)
|
||||
resource.send(:resource_class).should == Section
|
||||
end
|
||||
|
||||
it "should load parent resource through proper id parameter" do
|
||||
project = Project.create!
|
||||
@params.merge!(:action => "index", :project_id => project.id)
|
||||
resource = CanCan::ControllerResource.new(@controller, :project, :parent => true)
|
||||
@params.merge!(:controller => "categories", :action => "index", :project_id => project.id)
|
||||
resource = CanCan::ControllerResource.new(@controller, :project)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).should == project
|
||||
end
|
||||
@@ -235,7 +275,10 @@ describe CanCan::ControllerResource do
|
||||
resource = CanCan::ControllerResource.new(@controller, :through => :category)
|
||||
lambda {
|
||||
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
|
||||
end
|
||||
|
||||
@@ -258,8 +301,8 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@project).should == :some_project
|
||||
end
|
||||
|
||||
it "should find record through has_one association with :singleton option" do
|
||||
@params.merge!(:action => "show", :id => 123)
|
||||
it "should find record through has_one association with :singleton option without id param" do
|
||||
@params.merge!(:action => "show", :id => nil)
|
||||
category = Object.new
|
||||
@controller.instance_variable_set(:@category, category)
|
||||
stub(category).project { :some_project }
|
||||
@@ -309,6 +352,14 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@project).should == project
|
||||
end
|
||||
|
||||
it "should load the model using a custom namespaced class" do
|
||||
project = Sub::Project.create!
|
||||
@params.merge!(:action => "show", :id => project.id)
|
||||
resource = CanCan::ControllerResource.new(@controller, :class => ::Sub::Project)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).should == project
|
||||
end
|
||||
|
||||
it "should authorize based on resource name if class is false" do
|
||||
@params.merge!(:action => "show", :id => 123)
|
||||
stub(@controller).authorize!(:show, :project) { raise CanCan::AccessDenied }
|
||||
@@ -325,6 +376,14 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@custom_project).should == project
|
||||
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
|
||||
project = Project.create!(:name => "foo")
|
||||
@params.merge!(:action => "show", :id => "foo")
|
||||
@@ -333,6 +392,14 @@ describe CanCan::ControllerResource do
|
||||
@controller.instance_variable_get(:@project).should == project
|
||||
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
|
||||
lambda {
|
||||
CanCan::ControllerResource.new(@controller, :name => :foo)
|
||||
|
||||
@@ -32,4 +32,27 @@ describe CanCan::AccessDenied do
|
||||
@exception.message.should == "Access denied!"
|
||||
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
|
||||
|
||||
@@ -39,4 +39,22 @@ describe CanCan::InheritedResource do
|
||||
CanCan::InheritedResource.new(@controller).load_resource
|
||||
@controller.instance_variable_get(:@projects).should == :projects
|
||||
end
|
||||
|
||||
it "should build a new resource with attributes from current ability" do
|
||||
@params[:action] = "new"
|
||||
@ability.can(:create, Project, :name => "from conditions")
|
||||
stub(@controller).build_resource { Struct.new(:name).new }
|
||||
resource = CanCan::InheritedResource.new(@controller)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).name.should == "from conditions"
|
||||
end
|
||||
|
||||
it "should override initial attributes with params" do
|
||||
@params.merge!(:action => "new", :project => {:name => "from params"})
|
||||
@ability.can(:create, Project, :name => "from conditions")
|
||||
stub(@controller).build_resource { Struct.new(:name).new }
|
||||
resource = CanCan::ControllerResource.new(@controller)
|
||||
resource.load_resource
|
||||
@controller.instance_variable_get(:@project).name.should == "from params"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.extend WithModel
|
||||
end
|
||||
|
||||
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
||||
|
||||
describe CanCan::ModelAdapters::ActiveRecordAdapter do
|
||||
@@ -56,6 +52,11 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::ActiveRecordAdapter
|
||||
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
|
||||
Article.create!
|
||||
Article.accessible_by(@ability).should be_empty
|
||||
@@ -125,6 +126,15 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
Article.accessible_by(@ability).should == [article1]
|
||||
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)
|
||||
@@ -226,6 +236,16 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
@ability.should_not be_able_to(:read, article2)
|
||||
end
|
||||
|
||||
it "should merge MetaWhere and non-MetaWhere conditions" do
|
||||
@ability.can :read, Article, :priority.lt => 2
|
||||
@ability.can :read, Article, :priority => 1
|
||||
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")
|
||||
@@ -256,8 +276,9 @@ if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
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
|
||||
adapter.matches_condition?(article1, :name.nlike, "%helo%").should be_true
|
||||
adapter.matches_condition?(article1, :name.nlike, "%ello worl%").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
|
||||
|
||||
@@ -36,6 +36,11 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
||||
CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article).should == CanCan::ModelAdapters::DataMapperAdapter
|
||||
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
|
||||
Article.create
|
||||
Article.accessible_by(@ability).should be_empty
|
||||
@@ -65,7 +70,6 @@ if ENV["MODEL_ADAPTER"] == "data_mapper"
|
||||
end
|
||||
|
||||
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.cannot :read, Article, :secret => true
|
||||
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
|
||||
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
|
||||
model = MongoidProject.new
|
||||
@ability.can :read, MongoidProject, :id => model.id
|
||||
@ability.should be_able_to(:read, model)
|
||||
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
|
||||
MongoidProject.create(:title => 'Sir')
|
||||
MongoidProject.create(:title => 'Lord')
|
||||
@@ -59,6 +73,26 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
||||
MongoidProject.accessible_by(@ability, :read).entries.should == [sir]
|
||||
end
|
||||
|
||||
it "should return the correct records when a mix of can and cannot rules in defined ability" do
|
||||
@ability.can :manage, MongoidProject, :title => 'Sir'
|
||||
@ability.cannot :destroy, MongoidProject
|
||||
|
||||
sir = MongoidProject.create(:title => 'Sir')
|
||||
lord = MongoidProject.create(:title => 'Lord')
|
||||
dude = MongoidProject.create(:title => 'Dude')
|
||||
|
||||
MongoidProject.accessible_by(@ability, :destroy).entries.should == [sir]
|
||||
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
|
||||
@ability.can :manage, :all
|
||||
sir = MongoidProject.create(:title => 'Sir')
|
||||
@@ -68,6 +102,14 @@ if ENV["MODEL_ADAPTER"] == "mongoid"
|
||||
MongoidProject.accessible_by(@ability, :read).entries.should == [sir, lord, dude]
|
||||
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
|
||||
it "should handle :field.in" do
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require "spec_helper"
|
||||
require "ostruct" # for OpenStruct below
|
||||
|
||||
# Most of Rule functionality is tested in Ability specs
|
||||
describe CanCan::Rule do
|
||||
@@ -36,4 +37,11 @@ describe CanCan::Rule do
|
||||
rule = CanCan::Rule.new(true, :read, Integer, nil, nil)
|
||||
rule.associations_hash.should == {}
|
||||
end
|
||||
|
||||
it "should not be mergeable if conditions are not simple hashes" do
|
||||
meta_where = OpenStruct.new(:name => 'metawhere', :column => 'test')
|
||||
@conditions[meta_where] = :bar
|
||||
|
||||
@rule.should be_unmergeable
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,11 +9,15 @@ require 'matchers'
|
||||
require 'cancan/matchers'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.filter_run :focus => true
|
||||
config.run_all_when_everything_filtered = true
|
||||
config.mock_with :rr
|
||||
config.before(:each) do
|
||||
Project.delete_all
|
||||
Category.delete_all
|
||||
end
|
||||
config.extend WithModel if ENV["MODEL_ADAPTER"].nil? || ENV["MODEL_ADAPTER"] == "active_record"
|
||||
end
|
||||
|
||||
class Ability
|
||||
@@ -27,7 +31,30 @@ class Category < SuperModel::Base
|
||||
has_many :projects
|
||||
end
|
||||
|
||||
module Sub
|
||||
class Project < SuperModel::Base
|
||||
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
|
||||
|
||||
class Project < SuperModel::Base
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user