cancancan 1.13.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/cancancan.gemspec +18 -18
- data/init.rb +2 -0
- data/lib/cancan.rb +9 -11
- data/lib/cancan/ability.rb +93 -194
- data/lib/cancan/ability/actions.rb +93 -0
- data/lib/cancan/ability/rules.rb +93 -0
- data/lib/cancan/ability/strong_parameter_support.rb +41 -0
- data/lib/cancan/conditions_matcher.rb +106 -0
- data/lib/cancan/controller_additions.rb +38 -41
- data/lib/cancan/controller_resource.rb +52 -211
- data/lib/cancan/controller_resource_builder.rb +26 -0
- data/lib/cancan/controller_resource_finder.rb +42 -0
- data/lib/cancan/controller_resource_loader.rb +120 -0
- data/lib/cancan/controller_resource_name_finder.rb +23 -0
- data/lib/cancan/controller_resource_sanitizer.rb +32 -0
- data/lib/cancan/exceptions.rb +17 -5
- data/lib/cancan/matchers.rb +12 -3
- data/lib/cancan/model_adapters/abstract_adapter.rb +10 -8
- data/lib/cancan/model_adapters/active_record_4_adapter.rb +39 -13
- data/lib/cancan/model_adapters/active_record_5_adapter.rb +68 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +77 -82
- data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
- data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
- data/lib/cancan/model_adapters/default_adapter.rb +2 -0
- data/lib/cancan/model_additions.rb +2 -1
- data/lib/cancan/parameter_validators.rb +9 -0
- data/lib/cancan/relevant.rb +29 -0
- data/lib/cancan/rule.rb +76 -105
- data/lib/cancan/rules_compressor.rb +23 -0
- data/lib/cancan/unauthorized_message_resolver.rb +24 -0
- data/lib/cancan/version.rb +3 -1
- data/lib/cancancan.rb +2 -0
- data/lib/generators/cancan/ability/ability_generator.rb +4 -2
- data/lib/generators/cancan/ability/templates/ability.rb +2 -0
- metadata +66 -56
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.travis.yml +0 -28
- data/Appraisals +0 -81
- data/CHANGELOG.rdoc +0 -518
- data/CONTRIBUTING.md +0 -23
- data/Gemfile +0 -3
- data/LICENSE +0 -22
- data/README.md +0 -214
- data/Rakefile +0 -9
- data/gemfiles/activerecord_3.2.gemfile +0 -16
- data/gemfiles/activerecord_4.0.gemfile +0 -17
- data/gemfiles/activerecord_4.1.gemfile +0 -17
- data/gemfiles/activerecord_4.2.gemfile +0 -18
- data/gemfiles/mongoid_2.x.gemfile +0 -16
- data/gemfiles/sequel_3.x.gemfile +0 -16
- data/lib/cancan/inherited_resource.rb +0 -20
- data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
- data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -54
- data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
- data/spec/README.rdoc +0 -27
- data/spec/cancan/ability_spec.rb +0 -521
- data/spec/cancan/controller_additions_spec.rb +0 -141
- data/spec/cancan/controller_resource_spec.rb +0 -632
- data/spec/cancan/exceptions_spec.rb +0 -58
- data/spec/cancan/inherited_resource_spec.rb +0 -71
- data/spec/cancan/matchers_spec.rb +0 -29
- data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -85
- data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -384
- data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
- data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -227
- data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -132
- data/spec/cancan/rule_spec.rb +0 -52
- data/spec/matchers.rb +0 -13
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -27
- data/spec/support/ability.rb +0 -7
@@ -1,58 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe CanCan::AccessDenied do
|
4
|
-
describe "with action and subject" do
|
5
|
-
before(:each) do
|
6
|
-
@exception = CanCan::AccessDenied.new(nil, :some_action, :some_subject)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "has action and subject accessors" do
|
10
|
-
expect(@exception.action).to eq(:some_action)
|
11
|
-
expect(@exception.subject).to eq(:some_subject)
|
12
|
-
end
|
13
|
-
|
14
|
-
it "has a changable default message" do
|
15
|
-
expect(@exception.message).to eq("You are not authorized to access this page.")
|
16
|
-
@exception.default_message = "Unauthorized!"
|
17
|
-
expect(@exception.message).to eq("Unauthorized!")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
describe "with only a message" do
|
22
|
-
before(:each) do
|
23
|
-
@exception = CanCan::AccessDenied.new("Access denied!")
|
24
|
-
end
|
25
|
-
|
26
|
-
it "has nil action and subject" do
|
27
|
-
expect(@exception.action).to be_nil
|
28
|
-
expect(@exception.subject).to be_nil
|
29
|
-
end
|
30
|
-
|
31
|
-
it "has passed message" do
|
32
|
-
expect(@exception.message).to eq("Access denied!")
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe "i18n in the default message" do
|
37
|
-
after(:each) do
|
38
|
-
I18n.backend = nil
|
39
|
-
end
|
40
|
-
|
41
|
-
it "uses i18n for the default message" do
|
42
|
-
I18n.backend.store_translations :en, :unauthorized => {:default => "This is a different message"}
|
43
|
-
@exception = CanCan::AccessDenied.new
|
44
|
-
expect(@exception.message).to eq("This is a different message")
|
45
|
-
end
|
46
|
-
|
47
|
-
it "defaults to a nice message" do
|
48
|
-
@exception = CanCan::AccessDenied.new
|
49
|
-
expect(@exception.message).to eq("You are not authorized to access this page.")
|
50
|
-
end
|
51
|
-
|
52
|
-
it "does not use translation if a message is given" do
|
53
|
-
@exception = CanCan::AccessDenied.new("Hey! You're not welcome here")
|
54
|
-
expect(@exception.message).to eq("Hey! You're not welcome here")
|
55
|
-
expect(@exception.message).to_not eq("You are not authorized to access this page.")
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe CanCan::InheritedResource do
|
4
|
-
let(:ability) { Ability.new(nil) }
|
5
|
-
let(:params) { HashWithIndifferentAccess.new(:controller => "models") }
|
6
|
-
let(:controller_class) { Class.new }
|
7
|
-
let(:controller) { controller_class.new }
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
class Model
|
11
|
-
attr_accessor :name
|
12
|
-
|
13
|
-
def initialize(attributes={})
|
14
|
-
attributes.each do |attribute, value|
|
15
|
-
send("#{attribute}=", value)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
allow(controller).to receive(:params) { params }
|
21
|
-
allow(controller).to receive(:current_ability) { ability }
|
22
|
-
allow(controller_class).to receive(:cancan_skipper) { {:authorize => {}, :load => {}} }
|
23
|
-
end
|
24
|
-
|
25
|
-
it "show loads resource through controller.resource" do
|
26
|
-
params.merge!(:action => "show", :id => 123)
|
27
|
-
allow(controller).to receive(:resource) { :model_resource }
|
28
|
-
CanCan::InheritedResource.new(controller).load_resource
|
29
|
-
expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
|
30
|
-
end
|
31
|
-
|
32
|
-
it "new loads through controller.build_resource" do
|
33
|
-
params[:action] = "new"
|
34
|
-
allow(controller).to receive(:build_resource) { :model_resource }
|
35
|
-
CanCan::InheritedResource.new(controller).load_resource
|
36
|
-
expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
|
37
|
-
end
|
38
|
-
|
39
|
-
it "index loads through controller.association_chain when parent" do
|
40
|
-
params[:action] = "index"
|
41
|
-
allow(controller).to receive(:association_chain) { controller.instance_variable_set(:@model, :model_resource) }
|
42
|
-
CanCan::InheritedResource.new(controller, :parent => true).load_resource
|
43
|
-
expect(controller.instance_variable_get(:@model)).to eq(:model_resource)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "index loads through controller.end_of_association_chain" do
|
47
|
-
params[:action] = "index"
|
48
|
-
allow(Model).to receive(:accessible_by).with(ability, :index) { :projects }
|
49
|
-
allow(controller).to receive(:end_of_association_chain) { Model }
|
50
|
-
CanCan::InheritedResource.new(controller).load_resource
|
51
|
-
expect(controller.instance_variable_get(:@models)).to eq(:projects)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "builds a new resource with attributes from current ability" do
|
55
|
-
params[:action] = "new"
|
56
|
-
ability.can(:create, Model, :name => "from conditions")
|
57
|
-
allow(controller).to receive(:build_resource) { Struct.new(:name).new }
|
58
|
-
resource = CanCan::InheritedResource.new(controller)
|
59
|
-
resource.load_resource
|
60
|
-
expect(controller.instance_variable_get(:@model).name).to eq("from conditions")
|
61
|
-
end
|
62
|
-
|
63
|
-
it "overrides initial attributes with params" do
|
64
|
-
params.merge!(:action => "new", :model => {:name => "from params"})
|
65
|
-
ability.can(:create, Model, :name => "from conditions")
|
66
|
-
allow(controller).to receive(:build_resource) { Struct.new(:name).new }
|
67
|
-
resource = CanCan::ControllerResource.new(controller)
|
68
|
-
resource.load_resource
|
69
|
-
expect(controller.instance_variable_get(:@model).name).to eq("from params")
|
70
|
-
end
|
71
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "be_able_to" do
|
4
|
-
it "delegates to can?" do
|
5
|
-
expect(object = double).to receive(:can?).with(:read, 123) { true }
|
6
|
-
expect(object).to be_able_to(:read, 123)
|
7
|
-
end
|
8
|
-
|
9
|
-
it "reports a nice failure message for should" do
|
10
|
-
expect(object = double).to receive(:can?).with(:read, 123) { false }
|
11
|
-
expect {
|
12
|
-
expect(object).to be_able_to(:read, 123)
|
13
|
-
}.to raise_error('expected to be able to :read 123')
|
14
|
-
end
|
15
|
-
|
16
|
-
it "reports a nice failure message for should not" do
|
17
|
-
expect(object = double).to receive(:can?).with(:read, 123) { true }
|
18
|
-
expect {
|
19
|
-
expect(object).to_not be_able_to(:read, 123)
|
20
|
-
}.to raise_error('expected not to be able to :read 123')
|
21
|
-
end
|
22
|
-
|
23
|
-
it "delegates additional arguments to can? and reports in failure message" do
|
24
|
-
expect(object = double).to receive(:can?).with(:read, 123, 456) { false }
|
25
|
-
expect {
|
26
|
-
expect(object).to be_able_to(:read, 123, 456)
|
27
|
-
}.to raise_error('expected to be able to :read 123 456')
|
28
|
-
end
|
29
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
if defined? CanCan::ModelAdapters::ActiveRecord4Adapter
|
4
|
-
describe CanCan::ModelAdapters::ActiveRecord4Adapter do
|
5
|
-
context 'with sqlite3' do
|
6
|
-
before :each do
|
7
|
-
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
8
|
-
ActiveRecord::Migration.verbose = false
|
9
|
-
ActiveRecord::Schema.define do
|
10
|
-
create_table(:parents) do |t|
|
11
|
-
t.timestamps :null => false
|
12
|
-
end
|
13
|
-
|
14
|
-
create_table(:children) do |t|
|
15
|
-
t.timestamps :null => false
|
16
|
-
t.integer :parent_id
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class Parent < ActiveRecord::Base
|
21
|
-
has_many :children, lambda { order(:id => :desc) }
|
22
|
-
end
|
23
|
-
|
24
|
-
class Child < ActiveRecord::Base
|
25
|
-
belongs_to :parent
|
26
|
-
end
|
27
|
-
|
28
|
-
(@ability = double).extend(CanCan::Ability)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "respects scope on included associations" do
|
32
|
-
@ability.can :read, [Parent, Child]
|
33
|
-
|
34
|
-
parent = Parent.create!
|
35
|
-
child1 = Child.create!(:parent => parent, :created_at => 1.hours.ago)
|
36
|
-
child2 = Child.create!(:parent => parent, :created_at => 2.hours.ago)
|
37
|
-
|
38
|
-
expect(Parent.accessible_by(@ability).order(:created_at => :asc).includes(:children).first.children).to eq [child2, child1]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
if Gem::Specification.find_all_by_name('pg').any?
|
43
|
-
context 'with postgresql' do
|
44
|
-
before :each do
|
45
|
-
ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "postgres", :schema_search_path => 'public')
|
46
|
-
ActiveRecord::Base.connection.drop_database('cancan_postgresql_spec')
|
47
|
-
ActiveRecord::Base.connection.create_database 'cancan_postgresql_spec', 'encoding' => 'utf-8', 'adapter' => 'postgresql'
|
48
|
-
ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "cancan_postgresql_spec")
|
49
|
-
ActiveRecord::Migration.verbose = false
|
50
|
-
ActiveRecord::Schema.define do
|
51
|
-
create_table(:parents) do |t|
|
52
|
-
t.timestamps :null => false
|
53
|
-
end
|
54
|
-
|
55
|
-
create_table(:children) do |t|
|
56
|
-
t.timestamps :null => false
|
57
|
-
t.integer :parent_id
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class Parent < ActiveRecord::Base
|
62
|
-
has_many :children, lambda { order(:id => :desc) }
|
63
|
-
end
|
64
|
-
|
65
|
-
class Child < ActiveRecord::Base
|
66
|
-
belongs_to :parent
|
67
|
-
end
|
68
|
-
|
69
|
-
(@ability = double).extend(CanCan::Ability)
|
70
|
-
end
|
71
|
-
|
72
|
-
it "allows overlapping conditions in SQL and merge with hash conditions" do
|
73
|
-
@ability.can :read, Parent, :children => {:parent_id => 1}
|
74
|
-
@ability.can :read, Parent, :children => {:parent_id => 1}
|
75
|
-
|
76
|
-
parent = Parent.create!
|
77
|
-
child1 = Child.create!(:parent => parent, :created_at => 1.hours.ago)
|
78
|
-
child2 = Child.create!(:parent => parent, :created_at => 2.hours.ago)
|
79
|
-
|
80
|
-
expect(Parent.accessible_by(@ability)).to eq([parent])
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,384 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
if defined? CanCan::ModelAdapters::ActiveRecordAdapter
|
4
|
-
|
5
|
-
describe CanCan::ModelAdapters::ActiveRecordAdapter do
|
6
|
-
|
7
|
-
before :each do
|
8
|
-
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
9
|
-
ActiveRecord::Migration.verbose = false
|
10
|
-
ActiveRecord::Schema.define do
|
11
|
-
create_table(:categories) do |t|
|
12
|
-
t.string :name
|
13
|
-
t.boolean :visible
|
14
|
-
t.timestamps :null => false
|
15
|
-
end
|
16
|
-
|
17
|
-
create_table(:projects) do |t|
|
18
|
-
t.string :name
|
19
|
-
t.timestamps :null => false
|
20
|
-
end
|
21
|
-
|
22
|
-
create_table(:articles) do |t|
|
23
|
-
t.string :name
|
24
|
-
t.timestamps :null => false
|
25
|
-
t.boolean :published
|
26
|
-
t.boolean :secret
|
27
|
-
t.integer :priority
|
28
|
-
t.integer :category_id
|
29
|
-
t.integer :user_id
|
30
|
-
end
|
31
|
-
|
32
|
-
create_table(:comments) do |t|
|
33
|
-
t.boolean :spam
|
34
|
-
t.integer :article_id
|
35
|
-
t.timestamps :null => false
|
36
|
-
end
|
37
|
-
|
38
|
-
create_table(:legacy_mentions) do |t|
|
39
|
-
t.integer :user_id
|
40
|
-
t.integer :article_id
|
41
|
-
t.timestamps :null => false
|
42
|
-
end
|
43
|
-
|
44
|
-
create_table(:users) do |t|
|
45
|
-
t.timestamps :null => false
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class Project < ActiveRecord::Base
|
50
|
-
end
|
51
|
-
|
52
|
-
class Category < ActiveRecord::Base
|
53
|
-
has_many :articles
|
54
|
-
end
|
55
|
-
|
56
|
-
class Article < ActiveRecord::Base
|
57
|
-
belongs_to :category
|
58
|
-
has_many :comments
|
59
|
-
has_many :mentions
|
60
|
-
has_many :mentioned_users, :through => :mentions, :source => :user
|
61
|
-
belongs_to :user
|
62
|
-
end
|
63
|
-
|
64
|
-
class Mention < ActiveRecord::Base
|
65
|
-
self.table_name = 'legacy_mentions'
|
66
|
-
belongs_to :user
|
67
|
-
belongs_to :article
|
68
|
-
end
|
69
|
-
|
70
|
-
class Comment < ActiveRecord::Base
|
71
|
-
belongs_to :article
|
72
|
-
end
|
73
|
-
|
74
|
-
class User < ActiveRecord::Base
|
75
|
-
has_many :articles
|
76
|
-
end
|
77
|
-
|
78
|
-
(@ability = double).extend(CanCan::Ability)
|
79
|
-
@article_table = Article.table_name
|
80
|
-
@comment_table = Comment.table_name
|
81
|
-
end
|
82
|
-
|
83
|
-
it "is for only active record classes" do
|
84
|
-
if ActiveRecord.respond_to?(:version) &&
|
85
|
-
ActiveRecord.version > Gem::Version.new("4")
|
86
|
-
expect(CanCan::ModelAdapters::ActiveRecord4Adapter).to_not be_for_class(Object)
|
87
|
-
expect(CanCan::ModelAdapters::ActiveRecord4Adapter).to be_for_class(Article)
|
88
|
-
expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq(CanCan::ModelAdapters::ActiveRecord4Adapter)
|
89
|
-
else
|
90
|
-
expect(CanCan::ModelAdapters::ActiveRecord3Adapter).to_not be_for_class(Object)
|
91
|
-
expect(CanCan::ModelAdapters::ActiveRecord3Adapter).to be_for_class(Article)
|
92
|
-
expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq(CanCan::ModelAdapters::ActiveRecord3Adapter)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
it "finds record" do
|
97
|
-
article = Article.create!
|
98
|
-
adapter = CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)
|
99
|
-
expect(adapter.find(Article, article.id)).to eq(article)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "does not fetch any records when no abilities are defined" do
|
103
|
-
Article.create!
|
104
|
-
expect(Article.accessible_by(@ability)).to be_empty
|
105
|
-
end
|
106
|
-
|
107
|
-
it "fetches all articles when one can read all" do
|
108
|
-
@ability.can :read, Article
|
109
|
-
article = Article.create!
|
110
|
-
expect(Article.accessible_by(@ability)).to eq([article])
|
111
|
-
end
|
112
|
-
|
113
|
-
it "fetches only the articles that are published" do
|
114
|
-
@ability.can :read, Article, :published => true
|
115
|
-
article1 = Article.create!(:published => true)
|
116
|
-
article2 = Article.create!(:published => false)
|
117
|
-
expect(Article.accessible_by(@ability)).to eq([article1])
|
118
|
-
end
|
119
|
-
|
120
|
-
it "fetches any articles which are published or secret" do
|
121
|
-
@ability.can :read, Article, :published => true
|
122
|
-
@ability.can :read, Article, :secret => true
|
123
|
-
article1 = Article.create!(:published => true, :secret => false)
|
124
|
-
article2 = Article.create!(:published => true, :secret => true)
|
125
|
-
article3 = Article.create!(:published => false, :secret => true)
|
126
|
-
article4 = Article.create!(:published => false, :secret => false)
|
127
|
-
expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
|
128
|
-
end
|
129
|
-
|
130
|
-
it "fetches any articles which we are cited in" do
|
131
|
-
user = User.create!
|
132
|
-
cited = Article.create!
|
133
|
-
not_cited = Article.create!
|
134
|
-
cited.mentioned_users << user
|
135
|
-
@ability.can :read, Article, { :mentioned_users => { :id => user.id } }
|
136
|
-
@ability.can :read, Article, { :mentions => { :user_id => user.id } }
|
137
|
-
expect(Article.accessible_by(@ability)).to eq([cited])
|
138
|
-
end
|
139
|
-
|
140
|
-
it "fetches only the articles that are published and not secret" do
|
141
|
-
@ability.can :read, Article, :published => true
|
142
|
-
@ability.cannot :read, Article, :secret => true
|
143
|
-
article1 = Article.create!(:published => true, :secret => false)
|
144
|
-
article2 = Article.create!(:published => true, :secret => true)
|
145
|
-
article3 = Article.create!(:published => false, :secret => true)
|
146
|
-
article4 = Article.create!(:published => false, :secret => false)
|
147
|
-
expect(Article.accessible_by(@ability)).to eq([article1])
|
148
|
-
end
|
149
|
-
|
150
|
-
it "only reads comments for articles which are published" do
|
151
|
-
@ability.can :read, Comment, :article => { :published => true }
|
152
|
-
comment1 = Comment.create!(:article => Article.create!(:published => true))
|
153
|
-
comment2 = Comment.create!(:article => Article.create!(:published => false))
|
154
|
-
expect(Comment.accessible_by(@ability)).to eq([comment1])
|
155
|
-
end
|
156
|
-
|
157
|
-
it "should only read articles which are published or in visible categories" do
|
158
|
-
@ability.can :read, Article, :category => { :visible => true }
|
159
|
-
@ability.can :read, Article, :published => true
|
160
|
-
article1 = Article.create!(:published => true)
|
161
|
-
article2 = Article.create!(:published => false)
|
162
|
-
article3 = Article.create!(:published => false, :category => Category.create!(:visible => true))
|
163
|
-
expect(Article.accessible_by(@ability)).to eq([article1, article3])
|
164
|
-
end
|
165
|
-
|
166
|
-
it "should only read categories once even if they have multiple articles" do
|
167
|
-
@ability.can :read, Category, :articles => { :published => true }
|
168
|
-
@ability.can :read, Article, :published => true
|
169
|
-
category = Category.create!
|
170
|
-
Article.create!(:published => true, :category => category)
|
171
|
-
Article.create!(:published => true, :category => category)
|
172
|
-
expect(Category.accessible_by(@ability)).to eq([category])
|
173
|
-
end
|
174
|
-
|
175
|
-
it "only reads comments for visible categories through articles" do
|
176
|
-
@ability.can :read, Comment, :article => { :category => { :visible => true } }
|
177
|
-
comment1 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => true)))
|
178
|
-
comment2 = Comment.create!(:article => Article.create!(:category => Category.create!(:visible => false)))
|
179
|
-
expect(Comment.accessible_by(@ability)).to eq([comment1])
|
180
|
-
end
|
181
|
-
|
182
|
-
it "allows conditions in SQL and merge with hash conditions" do
|
183
|
-
@ability.can :read, Article, :published => true
|
184
|
-
@ability.can :read, Article, ["secret=?", true]
|
185
|
-
article1 = Article.create!(:published => true, :secret => false)
|
186
|
-
article2 = Article.create!(:published => true, :secret => true)
|
187
|
-
article3 = Article.create!(:published => false, :secret => true)
|
188
|
-
article4 = Article.create!(:published => false, :secret => false)
|
189
|
-
expect(Article.accessible_by(@ability)).to eq([article1, article2, article3])
|
190
|
-
end
|
191
|
-
|
192
|
-
it "allows a scope for conditions" do
|
193
|
-
@ability.can :read, Article, Article.where(:secret => true)
|
194
|
-
article1 = Article.create!(:secret => true)
|
195
|
-
article2 = Article.create!(:secret => false)
|
196
|
-
expect(Article.accessible_by(@ability)).to eq([article1])
|
197
|
-
end
|
198
|
-
|
199
|
-
it "fetches only associated records when using with a scope for conditions" do
|
200
|
-
@ability.can :read, Article, Article.where(:secret => true)
|
201
|
-
category1 = Category.create!(:visible => false)
|
202
|
-
category2 = Category.create!(:visible => true)
|
203
|
-
article1 = Article.create!(:secret => true, :category => category1)
|
204
|
-
article2 = Article.create!(:secret => true, :category => category2)
|
205
|
-
expect(category1.articles.accessible_by(@ability)).to eq([article1])
|
206
|
-
end
|
207
|
-
|
208
|
-
it "raises an exception when trying to merge scope with other conditions" do
|
209
|
-
@ability.can :read, Article, :published => true
|
210
|
-
@ability.can :read, Article, Article.where(:secret => true)
|
211
|
-
expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error, "Unable to merge an Active Record scope with other conditions. Instead use a hash or SQL for read Article ability.")
|
212
|
-
end
|
213
|
-
|
214
|
-
it "does not allow to fetch records when ability with just block present" do
|
215
|
-
@ability.can :read, Article do
|
216
|
-
false
|
217
|
-
end
|
218
|
-
expect(lambda { Article.accessible_by(@ability) }).to raise_error(CanCan::Error)
|
219
|
-
end
|
220
|
-
|
221
|
-
it "should support more than one deeply nested conditions" do
|
222
|
-
@ability.can :read, Comment, :article => {
|
223
|
-
:category => {
|
224
|
-
:name => 'foo', :visible => true
|
225
|
-
}
|
226
|
-
}
|
227
|
-
expect { Comment.accessible_by(@ability) }.to_not raise_error
|
228
|
-
end
|
229
|
-
|
230
|
-
it "does not allow to check ability on object against SQL conditions without block" do
|
231
|
-
@ability.can :read, Article, ["secret=?", true]
|
232
|
-
expect(lambda { @ability.can? :read, Article.new }).to raise_error(CanCan::Error)
|
233
|
-
end
|
234
|
-
|
235
|
-
it "has false conditions if no abilities match" do
|
236
|
-
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
237
|
-
end
|
238
|
-
|
239
|
-
it "returns false conditions for cannot clause" do
|
240
|
-
@ability.cannot :read, Article
|
241
|
-
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
242
|
-
end
|
243
|
-
|
244
|
-
it "returns SQL for single `can` definition in front of default `cannot` condition" do
|
245
|
-
@ability.cannot :read, Article
|
246
|
-
@ability.can :read, Article, :published => false, :secret => true
|
247
|
-
expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't'])
|
248
|
-
end
|
249
|
-
|
250
|
-
it "returns true condition for single `can` definition in front of default `can` condition" do
|
251
|
-
@ability.can :read, Article
|
252
|
-
@ability.can :read, Article, :published => false, :secret => true
|
253
|
-
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
|
254
|
-
end
|
255
|
-
|
256
|
-
it "returns `false condition` for single `cannot` definition in front of default `cannot` condition" do
|
257
|
-
@ability.cannot :read, Article
|
258
|
-
@ability.cannot :read, Article, :published => false, :secret => true
|
259
|
-
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='f'")
|
260
|
-
end
|
261
|
-
|
262
|
-
it "returns `not (sql)` for single `cannot` definition in front of default `can` condition" do
|
263
|
-
@ability.can :read, Article
|
264
|
-
@ability.cannot :read, Article, :published => false, :secret => true
|
265
|
-
expect(@ability.model_adapter(Article, :read).conditions).to orderlessly_match(%Q["not (#{@article_table}"."published" = 'f' AND "#{@article_table}"."secret" = 't')])
|
266
|
-
end
|
267
|
-
|
268
|
-
it "returns appropriate sql conditions in complex case" do
|
269
|
-
@ability.can :read, Article
|
270
|
-
@ability.can :manage, Article, :id => 1
|
271
|
-
@ability.can :update, Article, :published => true
|
272
|
-
@ability.cannot :update, Article, :secret => true
|
273
|
-
expect(@ability.model_adapter(Article, :update).conditions).to eq(%Q[not ("#{@article_table}"."secret" = 't') AND (("#{@article_table}"."published" = 't') OR ("#{@article_table}"."id" = 1))])
|
274
|
-
expect(@ability.model_adapter(Article, :manage).conditions).to eq({:id => 1})
|
275
|
-
expect(@ability.model_adapter(Article, :read).conditions).to eq("'t'='t'")
|
276
|
-
end
|
277
|
-
|
278
|
-
it "returns appropriate sql conditions in complex case with nested joins" do
|
279
|
-
@ability.can :read, Comment, :article => { :category => { :visible => true } }
|
280
|
-
expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Category.table_name.to_sym => { :visible => true } })
|
281
|
-
end
|
282
|
-
|
283
|
-
it "returns appropriate sql conditions in complex case with nested joins of different depth" do
|
284
|
-
@ability.can :read, Comment, :article => { :published => true, :category => { :visible => true } }
|
285
|
-
expect(@ability.model_adapter(Comment, :read).conditions).to eq({ Article.table_name.to_sym => { :published => true }, Category.table_name.to_sym => { :visible => true } })
|
286
|
-
end
|
287
|
-
|
288
|
-
it "does not forget conditions when calling with SQL string" do
|
289
|
-
@ability.can :read, Article, :published => true
|
290
|
-
@ability.can :read, Article, ['secret=?', false]
|
291
|
-
adapter = @ability.model_adapter(Article, :read)
|
292
|
-
2.times do
|
293
|
-
expect(adapter.conditions).to eq(%Q[(secret='f') OR ("#{@article_table}"."published" = 't')])
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
it "has nil joins if no rules" do
|
298
|
-
expect(@ability.model_adapter(Article, :read).joins).to be_nil
|
299
|
-
end
|
300
|
-
|
301
|
-
it "has nil joins if no nested hashes specified in conditions" do
|
302
|
-
@ability.can :read, Article, :published => false
|
303
|
-
@ability.can :read, Article, :secret => true
|
304
|
-
expect(@ability.model_adapter(Article, :read).joins).to be_nil
|
305
|
-
end
|
306
|
-
|
307
|
-
it "merges separate joins into a single array" do
|
308
|
-
@ability.can :read, Article, :project => { :blocked => false }
|
309
|
-
@ability.can :read, Article, :company => { :admin => true }
|
310
|
-
expect(@ability.model_adapter(Article, :read).joins.inspect).to orderlessly_match([:company, :project].inspect)
|
311
|
-
end
|
312
|
-
|
313
|
-
it "merges same joins into a single array" do
|
314
|
-
@ability.can :read, Article, :project => { :blocked => false }
|
315
|
-
@ability.can :read, Article, :project => { :admin => true }
|
316
|
-
expect(@ability.model_adapter(Article, :read).joins).to eq([:project])
|
317
|
-
end
|
318
|
-
|
319
|
-
it "merges nested and non-nested joins" do
|
320
|
-
@ability.can :read, Article, :project => { :blocked => false }
|
321
|
-
@ability.can :read, Article, :project => { :comments => { :spam => true } }
|
322
|
-
expect(@ability.model_adapter(Article, :read).joins).to eq([{:project=>[:comments]}])
|
323
|
-
end
|
324
|
-
|
325
|
-
it "merges :all conditions with other conditions" do
|
326
|
-
user = User.create!
|
327
|
-
article = Article.create!(:user => user)
|
328
|
-
ability = Ability.new(user)
|
329
|
-
ability.can :manage, :all
|
330
|
-
ability.can :manage, Article, :user_id => user.id
|
331
|
-
expect(Article.accessible_by(ability)).to eq([article])
|
332
|
-
end
|
333
|
-
|
334
|
-
it 'should not execute a scope when checking ability on the class' do
|
335
|
-
relation = Article.where(:secret => true)
|
336
|
-
@ability.can :read, Article, relation do |article|
|
337
|
-
article.secret == true
|
338
|
-
end
|
339
|
-
|
340
|
-
allow(relation).to receive(:count).and_raise('Unexpected scope execution.')
|
341
|
-
|
342
|
-
expect { @ability.can? :read, Article }.not_to raise_error
|
343
|
-
end
|
344
|
-
|
345
|
-
context "with namespaced models" do
|
346
|
-
before :each do
|
347
|
-
ActiveRecord::Schema.define do
|
348
|
-
create_table( :table_xes ) do |t|
|
349
|
-
t.timestamps :null => false
|
350
|
-
end
|
351
|
-
|
352
|
-
create_table( :table_zs ) do |t|
|
353
|
-
t.integer :table_x_id
|
354
|
-
t.integer :user_id
|
355
|
-
t.timestamps :null => false
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
module Namespace
|
360
|
-
end
|
361
|
-
|
362
|
-
class Namespace::TableX < ActiveRecord::Base
|
363
|
-
has_many :table_zs
|
364
|
-
end
|
365
|
-
|
366
|
-
class Namespace::TableZ < ActiveRecord::Base
|
367
|
-
belongs_to :table_x
|
368
|
-
belongs_to :user
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
it "fetches all namespace::table_x when one is related by table_y" do
|
373
|
-
user = User.create!
|
374
|
-
|
375
|
-
ability = Ability.new(user)
|
376
|
-
ability.can :read, Namespace::TableX, :table_zs => { :user_id => user.id }
|
377
|
-
|
378
|
-
table_x = Namespace::TableX.create!
|
379
|
-
table_z = table_x.table_zs.create( :user => user )
|
380
|
-
expect(Namespace::TableX.accessible_by(ability)).to eq([table_x])
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|