cancancan 1.15.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/cancancan.gemspec +18 -18
  3. data/init.rb +2 -0
  4. data/lib/cancan.rb +9 -11
  5. data/lib/cancan/ability.rb +90 -203
  6. data/lib/cancan/ability/actions.rb +93 -0
  7. data/lib/cancan/ability/rules.rb +93 -0
  8. data/lib/cancan/ability/strong_parameter_support.rb +41 -0
  9. data/lib/cancan/conditions_matcher.rb +106 -0
  10. data/lib/cancan/controller_additions.rb +29 -36
  11. data/lib/cancan/controller_resource.rb +46 -211
  12. data/lib/cancan/controller_resource_builder.rb +26 -0
  13. data/lib/cancan/controller_resource_finder.rb +42 -0
  14. data/lib/cancan/controller_resource_loader.rb +120 -0
  15. data/lib/cancan/controller_resource_name_finder.rb +23 -0
  16. data/lib/cancan/controller_resource_sanitizer.rb +32 -0
  17. data/lib/cancan/exceptions.rb +17 -5
  18. data/lib/cancan/matchers.rb +12 -3
  19. data/lib/cancan/model_adapters/abstract_adapter.rb +10 -8
  20. data/lib/cancan/model_adapters/active_record_4_adapter.rb +39 -43
  21. data/lib/cancan/model_adapters/active_record_5_adapter.rb +68 -0
  22. data/lib/cancan/model_adapters/active_record_adapter.rb +77 -82
  23. data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  24. data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  25. data/lib/cancan/model_adapters/default_adapter.rb +2 -0
  26. data/lib/cancan/model_additions.rb +2 -1
  27. data/lib/cancan/parameter_validators.rb +9 -0
  28. data/lib/cancan/relevant.rb +29 -0
  29. data/lib/cancan/rule.rb +76 -106
  30. data/lib/cancan/rules_compressor.rb +23 -0
  31. data/lib/cancan/unauthorized_message_resolver.rb +24 -0
  32. data/lib/cancan/version.rb +3 -1
  33. data/lib/cancancan.rb +2 -0
  34. data/lib/generators/cancan/ability/ability_generator.rb +4 -2
  35. data/lib/generators/cancan/ability/templates/ability.rb +2 -0
  36. metadata +66 -57
  37. data/.gitignore +0 -15
  38. data/.rspec +0 -1
  39. data/.travis.yml +0 -33
  40. data/Appraisals +0 -104
  41. data/CHANGELOG.rdoc +0 -527
  42. data/CONTRIBUTING.md +0 -23
  43. data/Gemfile +0 -3
  44. data/LICENSE +0 -22
  45. data/README.md +0 -217
  46. data/Rakefile +0 -9
  47. data/gemfiles/activerecord_3.2.gemfile +0 -17
  48. data/gemfiles/activerecord_4.0.gemfile +0 -18
  49. data/gemfiles/activerecord_4.1.gemfile +0 -18
  50. data/gemfiles/activerecord_4.2.gemfile +0 -19
  51. data/gemfiles/activerecord_5.0.gemfile +0 -19
  52. data/gemfiles/mongoid_2.x.gemfile +0 -17
  53. data/gemfiles/sequel_3.x.gemfile +0 -17
  54. data/lib/cancan/inherited_resource.rb +0 -20
  55. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  56. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -75
  57. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  58. data/spec/README.rdoc +0 -27
  59. data/spec/cancan/ability_spec.rb +0 -544
  60. data/spec/cancan/controller_additions_spec.rb +0 -151
  61. data/spec/cancan/controller_resource_spec.rb +0 -643
  62. data/spec/cancan/exceptions_spec.rb +0 -58
  63. data/spec/cancan/inherited_resource_spec.rb +0 -71
  64. data/spec/cancan/matchers_spec.rb +0 -29
  65. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -154
  66. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -405
  67. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  68. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -247
  69. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -132
  70. data/spec/cancan/rule_spec.rb +0 -52
  71. data/spec/matchers.rb +0 -13
  72. data/spec/spec.opts +0 -2
  73. data/spec/spec_helper.rb +0 -27
  74. 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,154 +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
-
41
- if ActiveRecord::VERSION::MINOR >= 1
42
- it "allows filters on enums" do
43
- ActiveRecord::Schema.define do
44
- create_table(:shapes) do |t|
45
- t.integer :color, default: 0, null: false
46
- end
47
- end
48
-
49
- class Shape < ActiveRecord::Base
50
- enum color: [:red, :green, :blue]
51
- end
52
-
53
- red = Shape.create!(color: :red)
54
- green = Shape.create!(color: :green)
55
- blue = Shape.create!(color: :blue)
56
-
57
- # A condition with a single value.
58
- @ability.can :read, Shape, color: Shape.colors[:green]
59
-
60
- expect(@ability.cannot? :read, red).to be true
61
- expect(@ability.can? :read, green).to be true
62
- expect(@ability.cannot? :read, blue).to be true
63
-
64
- accessible = Shape.accessible_by(@ability)
65
- expect(accessible).to contain_exactly(green)
66
-
67
- # A condition with multiple values.
68
- @ability.can :update, Shape, color: [Shape.colors[:red],
69
- Shape.colors[:blue]]
70
-
71
- expect(@ability.can? :update, red).to be true
72
- expect(@ability.cannot? :update, green).to be true
73
- expect(@ability.can? :update, blue).to be true
74
-
75
- accessible = Shape.accessible_by(@ability, :update)
76
- expect(accessible).to contain_exactly(red, blue)
77
- end
78
-
79
- it "allows dual filter on enums" do
80
- ActiveRecord::Schema.define do
81
- create_table(:discs) do |t|
82
- t.integer :color, default: 0, null: false
83
- t.integer :shape, default: 3, null: false
84
- end
85
- end
86
-
87
- class Disc < ActiveRecord::Base
88
- enum color: [:red, :green, :blue]
89
- enum shape: { triangle: 3, rectangle: 4 }
90
- end
91
-
92
- red_triangle = Disc.create!(color: Disc.colors[:red], shape: Disc.shapes[:triangle])
93
- green_triangle = Disc.create!(color: Disc.colors[:green], shape: Disc.shapes[:triangle])
94
- green_rectangle = Disc.create!(color: Disc.colors[:green], shape: Disc.shapes[:rectangle])
95
- blue_rectangle = Disc.create!(color: Disc.colors[:blue], shape: Disc.shapes[:rectangle])
96
-
97
- # A condition with a dual filter.
98
- @ability.can :read, Disc, color: Disc.colors[:green], shape: Disc.shapes[:rectangle]
99
-
100
- expect(@ability.cannot? :read, red_triangle).to be true
101
- expect(@ability.cannot? :read, green_triangle).to be true
102
- expect(@ability.can? :read, green_rectangle).to be true
103
- expect(@ability.cannot? :read, blue_rectangle).to be true
104
-
105
- accessible = Disc.accessible_by(@ability)
106
- expect(accessible).to contain_exactly(green_rectangle)
107
- end
108
- end
109
- end
110
-
111
- if Gem::Specification.find_all_by_name('pg').any?
112
- context 'with postgresql' do
113
- before :each do
114
- ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "postgres", :schema_search_path => 'public')
115
- ActiveRecord::Base.connection.drop_database('cancan_postgresql_spec')
116
- ActiveRecord::Base.connection.create_database 'cancan_postgresql_spec', 'encoding' => 'utf-8', 'adapter' => 'postgresql'
117
- ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "cancan_postgresql_spec")
118
- ActiveRecord::Migration.verbose = false
119
- ActiveRecord::Schema.define do
120
- create_table(:parents) do |t|
121
- t.timestamps :null => false
122
- end
123
-
124
- create_table(:children) do |t|
125
- t.timestamps :null => false
126
- t.integer :parent_id
127
- end
128
- end
129
-
130
- class Parent < ActiveRecord::Base
131
- has_many :children, lambda { order(:id => :desc) }
132
- end
133
-
134
- class Child < ActiveRecord::Base
135
- belongs_to :parent
136
- end
137
-
138
- (@ability = double).extend(CanCan::Ability)
139
- end
140
-
141
- it "allows overlapping conditions in SQL and merge with hash conditions" do
142
- @ability.can :read, Parent, :children => {:parent_id => 1}
143
- @ability.can :read, Parent, :children => {:parent_id => 1}
144
-
145
- parent = Parent.create!
146
- child1 = Child.create!(:parent => parent, :created_at => 1.hours.ago)
147
- child2 = Child.create!(:parent => parent, :created_at => 2.hours.ago)
148
-
149
- expect(Parent.accessible_by(@ability)).to eq([parent])
150
- end
151
- end
152
- end
153
- end
154
- end
@@ -1,405 +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
-
384
- context 'when conditions are non iterable ranges' do
385
- before :each do
386
- ActiveRecord::Schema.define do
387
- create_table( :courses ) do |t|
388
- t.datetime :start_at
389
- end
390
- end
391
-
392
- class Course < ActiveRecord::Base
393
- end
394
- end
395
-
396
- it 'fetches only the valid records' do
397
- @ability.can :read, Course, :start_at => 1.day.ago..1.day.from_now
398
- Course.create!(:start_at => 10.days.ago)
399
- valid_course = Course.create!(:start_at => Time.now)
400
-
401
- expect(Course.accessible_by(@ability)).to eq([valid_course])
402
- end
403
- end
404
- end
405
- end