cancancan 1.13.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) 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 +93 -194
  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 +38 -41
  11. data/lib/cancan/controller_resource.rb +52 -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 -13
  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 -105
  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 -56
  37. data/.gitignore +0 -15
  38. data/.rspec +0 -1
  39. data/.travis.yml +0 -28
  40. data/Appraisals +0 -81
  41. data/CHANGELOG.rdoc +0 -518
  42. data/CONTRIBUTING.md +0 -23
  43. data/Gemfile +0 -3
  44. data/LICENSE +0 -22
  45. data/README.md +0 -214
  46. data/Rakefile +0 -9
  47. data/gemfiles/activerecord_3.2.gemfile +0 -16
  48. data/gemfiles/activerecord_4.0.gemfile +0 -17
  49. data/gemfiles/activerecord_4.1.gemfile +0 -17
  50. data/gemfiles/activerecord_4.2.gemfile +0 -18
  51. data/gemfiles/mongoid_2.x.gemfile +0 -16
  52. data/gemfiles/sequel_3.x.gemfile +0 -16
  53. data/lib/cancan/inherited_resource.rb +0 -20
  54. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  55. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -54
  56. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  57. data/spec/README.rdoc +0 -27
  58. data/spec/cancan/ability_spec.rb +0 -521
  59. data/spec/cancan/controller_additions_spec.rb +0 -141
  60. data/spec/cancan/controller_resource_spec.rb +0 -632
  61. data/spec/cancan/exceptions_spec.rb +0 -58
  62. data/spec/cancan/inherited_resource_spec.rb +0 -71
  63. data/spec/cancan/matchers_spec.rb +0 -29
  64. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -85
  65. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -384
  66. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  67. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -227
  68. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -132
  69. data/spec/cancan/rule_spec.rb +0 -52
  70. data/spec/matchers.rb +0 -13
  71. data/spec/spec.opts +0 -2
  72. data/spec/spec_helper.rb +0 -27
  73. data/spec/support/ability.rb +0 -7
@@ -1,7 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe CanCan::ModelAdapters::DefaultAdapter do
4
- it "is default for generic classes" do
5
- expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Object)).to eq(CanCan::ModelAdapters::DefaultAdapter)
6
- end
7
- end
@@ -1,227 +0,0 @@
1
- require "spec_helper"
2
-
3
- if defined? CanCan::ModelAdapters::MongoidAdapter
4
-
5
- class MongoidCategory
6
- include Mongoid::Document
7
-
8
- references_many :mongoid_projects
9
- end
10
-
11
- class MongoidProject
12
- include Mongoid::Document
13
-
14
- referenced_in :mongoid_category
15
- end
16
-
17
- Mongoid.configure do |config|
18
- config.master = Mongo::Connection.new('127.0.0.1', 27017).db("cancan_mongoid_spec")
19
- end
20
-
21
- describe CanCan::ModelAdapters::MongoidAdapter do
22
- context "Mongoid defined" do
23
- before(:each) do
24
- (@ability = double).extend(CanCan::Ability)
25
- end
26
-
27
- after(:each) do
28
- Mongoid.master.collections.select do |collection|
29
- collection.name !~ /system/
30
- end.each(&:drop)
31
- end
32
-
33
- it "is for only Mongoid classes" do
34
- expect(CanCan::ModelAdapters::MongoidAdapter).not_to be_for_class(Object)
35
- expect(CanCan::ModelAdapters::MongoidAdapter).to be_for_class(MongoidProject)
36
- expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(MongoidProject)).to eq(CanCan::ModelAdapters::MongoidAdapter)
37
- end
38
-
39
- it "finds record" do
40
- project = MongoidProject.create
41
- expect(CanCan::ModelAdapters::MongoidAdapter.find(MongoidProject, project.id)).to eq(project)
42
- end
43
-
44
- it "compares properties on mongoid documents with the conditions hash" do
45
- model = MongoidProject.new
46
- @ability.can :read, MongoidProject, :id => model.id
47
- expect(@ability).to be_able_to(:read, model)
48
- end
49
-
50
- it "is able to read hashes when field is array" do
51
- one_to_three = MongoidProject.create(:numbers => ['one', 'two', 'three'])
52
- two_to_five = MongoidProject.create(:numbers => ['two', 'three', 'four', 'five'])
53
-
54
- @ability.can :foo, MongoidProject, :numbers => 'one'
55
- expect(@ability).to be_able_to(:foo, one_to_three)
56
- expect(@ability).not_to be_able_to(:foo, two_to_five)
57
- end
58
-
59
- it "returns [] when no ability is defined so no records are found" do
60
- MongoidProject.create(:title => 'Sir')
61
- MongoidProject.create(:title => 'Lord')
62
- MongoidProject.create(:title => 'Dude')
63
-
64
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([])
65
- end
66
-
67
- it "returns the correct records based on the defined ability" do
68
- @ability.can :read, MongoidProject, :title => "Sir"
69
- sir = MongoidProject.create(:title => 'Sir')
70
- lord = MongoidProject.create(:title => 'Lord')
71
- dude = MongoidProject.create(:title => 'Dude')
72
-
73
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([sir])
74
- end
75
-
76
- it "returns the correct records when a mix of can and cannot rules in defined ability" do
77
- @ability.can :manage, MongoidProject, :title => 'Sir'
78
- @ability.cannot :destroy, MongoidProject
79
-
80
- sir = MongoidProject.create(:title => 'Sir')
81
- lord = MongoidProject.create(:title => 'Lord')
82
- dude = MongoidProject.create(:title => 'Dude')
83
-
84
- expect(MongoidProject.accessible_by(@ability, :destroy).entries).to eq([sir])
85
- end
86
-
87
- it "is able to mix empty conditions and hashes" do
88
- @ability.can :read, MongoidProject
89
- @ability.can :read, MongoidProject, :title => 'Sir'
90
- sir = MongoidProject.create(:title => 'Sir')
91
- lord = MongoidProject.create(:title => 'Lord')
92
-
93
- expect(MongoidProject.accessible_by(@ability, :read).count).to eq(2)
94
- end
95
-
96
- it "returns everything when the defined ability is access all" do
97
- @ability.can :manage, :all
98
- sir = MongoidProject.create(:title => 'Sir')
99
- lord = MongoidProject.create(:title => 'Lord')
100
- dude = MongoidProject.create(:title => 'Dude')
101
-
102
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([sir, lord, dude])
103
- end
104
-
105
- it "allows a scope for conditions" do
106
- @ability.can :read, MongoidProject, MongoidProject.where(:title => 'Sir')
107
- sir = MongoidProject.create(:title => 'Sir')
108
- lord = MongoidProject.create(:title => 'Lord')
109
- dude = MongoidProject.create(:title => 'Dude')
110
-
111
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([sir])
112
- end
113
-
114
- describe "Mongoid::Criteria where clause Symbol extensions using MongoDB expressions" do
115
- it "handles :field.in" do
116
- obj = MongoidProject.create(:title => 'Sir')
117
- @ability.can :read, MongoidProject, :title.in => ["Sir", "Madam"]
118
- expect(@ability.can?(:read, obj)).to eq(true)
119
- expect(MongoidProject.accessible_by(@ability, :read)).to eq([obj])
120
-
121
- obj2 = MongoidProject.create(:title => 'Lord')
122
- expect(@ability.can?(:read, obj2)).to be(false)
123
- end
124
-
125
- describe "activates only when there are Criteria in the hash" do
126
- it "Calls where on the model class when there are criteria" do
127
- obj = MongoidProject.create(:title => 'Bird')
128
- @conditions = {:title.nin => ["Fork", "Spoon"]}
129
-
130
- @ability.can :read, MongoidProject, @conditions
131
- expect(@ability).to be_able_to(:read, obj)
132
- end
133
- it "Calls the base version if there are no mongoid criteria" do
134
- obj = MongoidProject.new(:title => 'Bird')
135
- @conditions = {:id => obj.id}
136
- @ability.can :read, MongoidProject, @conditions
137
- expect(@ability).to be_able_to(:read, obj)
138
- end
139
- end
140
-
141
- it "handles :field.nin" do
142
- obj = MongoidProject.create(:title => 'Sir')
143
- @ability.can :read, MongoidProject, :title.nin => ["Lord", "Madam"]
144
- expect(@ability.can?(:read, obj)).to eq(true)
145
- expect(MongoidProject.accessible_by(@ability, :read)).to eq([obj])
146
-
147
- obj2 = MongoidProject.create(:title => 'Lord')
148
- expect(@ability.can?(:read, obj2)).to be(false)
149
- end
150
-
151
- it "handles :field.size" do
152
- obj = MongoidProject.create(:titles => ['Palatin', 'Margrave'])
153
- @ability.can :read, MongoidProject, :titles.size => 2
154
- expect(@ability.can?(:read, obj)).to eq(true)
155
- expect(MongoidProject.accessible_by(@ability, :read)).to eq([obj])
156
-
157
- obj2 = MongoidProject.create(:titles => ['Palatin', 'Margrave', 'Marquis'])
158
- expect(@ability.can?(:read, obj2)).to be(false)
159
- end
160
-
161
- it "handles :field.exists" do
162
- obj = MongoidProject.create(:titles => ['Palatin', 'Margrave'])
163
- @ability.can :read, MongoidProject, :titles.exists => true
164
- expect(@ability.can?(:read, obj)).to eq(true)
165
- expect(MongoidProject.accessible_by(@ability, :read)).to eq([obj])
166
-
167
- obj2 = MongoidProject.create
168
- expect(@ability.can?(:read, obj2)).to be(false)
169
- end
170
-
171
- it "handles :field.gt" do
172
- obj = MongoidProject.create(:age => 50)
173
- @ability.can :read, MongoidProject, :age.gt => 45
174
- expect(@ability.can?(:read, obj)).to eq(true)
175
- expect(MongoidProject.accessible_by(@ability, :read)).to eq([obj])
176
-
177
- obj2 = MongoidProject.create(:age => 40)
178
- expect(@ability.can?(:read, obj2)).to be(false)
179
- end
180
-
181
- it "handles instance not saved to database" do
182
- obj = MongoidProject.new(:title => 'Sir')
183
- @ability.can :read, MongoidProject, :title.in => ["Sir", "Madam"]
184
- expect(@ability.can?(:read, obj)).to eq(true)
185
-
186
- # accessible_by only returns saved records
187
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([])
188
-
189
- obj2 = MongoidProject.new(:title => 'Lord')
190
- expect(@ability.can?(:read, obj2)).to be(false)
191
- end
192
- end
193
-
194
- it "calls where with matching ability conditions" do
195
- obj = MongoidProject.create(:foo => {:bar => 1})
196
- @ability.can :read, MongoidProject, :foo => {:bar => 1}
197
- expect(MongoidProject.accessible_by(@ability, :read).entries.first).to eq(obj)
198
- end
199
-
200
- it "excludes from the result if set to cannot" do
201
- obj = MongoidProject.create(:bar => 1)
202
- obj2 = MongoidProject.create(:bar => 2)
203
- @ability.can :read, MongoidProject
204
- @ability.cannot :read, MongoidProject, :bar => 2
205
- expect(MongoidProject.accessible_by(@ability, :read).entries).to eq([obj])
206
- end
207
-
208
- it "combines the rules" do
209
- obj = MongoidProject.create(:bar => 1)
210
- obj2 = MongoidProject.create(:bar => 2)
211
- obj3 = MongoidProject.create(:bar => 3)
212
- @ability.can :read, MongoidProject, :bar => 1
213
- @ability.can :read, MongoidProject, :bar => 2
214
- expect(MongoidProject.accessible_by(@ability, :read).entries).to match_array([obj, obj2])
215
- end
216
-
217
- it "does not allow to fetch records when ability with just block present" do
218
- @ability.can :read, MongoidProject do
219
- false
220
- end
221
- expect {
222
- MongoidProject.accessible_by(@ability)
223
- }.to raise_error(CanCan::Error)
224
- end
225
- end
226
- end
227
- end
@@ -1,132 +0,0 @@
1
- require "spec_helper"
2
-
3
- if defined? CanCan::ModelAdapters::SequelAdapter
4
-
5
- describe CanCan::ModelAdapters::SequelAdapter do
6
- DB = if RUBY_PLATFORM == 'java'
7
- Sequel.connect('jdbc:sqlite:db.sqlite3')
8
- else
9
- Sequel.sqlite
10
- end
11
-
12
- DB.create_table :users do
13
- primary_key :id
14
- String :name
15
- end
16
-
17
- class User < Sequel::Model
18
- one_to_many :articles
19
- end
20
-
21
- DB.create_table :articles do
22
- primary_key :id
23
- String :name
24
- TrueClass :published
25
- TrueClass :secret
26
- Integer :priority
27
- foreign_key :user_id, :users
28
- end
29
-
30
- class Article < Sequel::Model
31
- many_to_one :user
32
- one_to_many :comments
33
- end
34
-
35
- DB.create_table :comments do
36
- primary_key :id
37
- TrueClass :spam
38
- foreign_key :article_id, :articles
39
- end
40
-
41
- class Comment < Sequel::Model
42
- many_to_one :article
43
- end
44
-
45
- before(:each) do
46
- Comment.dataset.delete
47
- Article.dataset.delete
48
- User.dataset.delete
49
- (@ability = double).extend(CanCan::Ability)
50
- end
51
-
52
- it "should be for only sequel model classes" do
53
- expect(CanCan::ModelAdapters::SequelAdapter).to_not be_for_class(Object)
54
- expect(CanCan::ModelAdapters::SequelAdapter).to be_for_class(Article)
55
- expect(CanCan::ModelAdapters::AbstractAdapter.adapter_class(Article)).to eq CanCan::ModelAdapters::SequelAdapter
56
- end
57
-
58
- it "should find record" do
59
- article = Article.create
60
- expect(CanCan::ModelAdapters::SequelAdapter.find(Article, article.id)).to eq article
61
- end
62
-
63
- it "should not fetch any records when no abilities are defined" do
64
- Article.create
65
- expect(Article.accessible_by(@ability).all).to be_empty
66
- end
67
-
68
- it "should fetch all articles when one can read all" do
69
- @ability.can :read, Article
70
- article = Article.create
71
- expect(Article.accessible_by(@ability).all).to eq [article]
72
- end
73
-
74
- it "should fetch only the articles that are published" do
75
- @ability.can :read, Article, :published => true
76
- article1 = Article.create(:published => true)
77
- article2 = Article.create(:published => false)
78
- expect(Article.accessible_by(@ability).all).to eq [article1]
79
- end
80
-
81
- it "should fetch any articles which are published or secret" do
82
- @ability.can :read, Article, :published => true
83
- @ability.can :read, Article, :secret => true
84
- article1 = Article.create(:published => true, :secret => false)
85
- article2 = Article.create(:published => true, :secret => true)
86
- article3 = Article.create(:published => false, :secret => true)
87
- article4 = Article.create(:published => false, :secret => false)
88
- expect(Article.accessible_by(@ability).all).to eq([article1, article2, article3])
89
- end
90
-
91
- it "should fetch only the articles that are published and not secret" do
92
- @ability.can :read, Article, :published => true
93
- @ability.cannot :read, Article, :secret => true
94
- article1 = Article.create(:published => true, :secret => false)
95
- article2 = Article.create(:published => true, :secret => true)
96
- article3 = Article.create(:published => false, :secret => true)
97
- article4 = Article.create(:published => false, :secret => false)
98
- expect(Article.accessible_by(@ability).all).to eq [article1]
99
- end
100
-
101
- it "should only read comments for articles which are published" do
102
- @ability.can :read, Comment, :article => { :published => true }
103
- comment1 = Comment.create(:article => Article.create(:published => true))
104
- comment2 = Comment.create(:article => Article.create(:published => false))
105
- expect(Comment.accessible_by(@ability).all).to eq [comment1]
106
- end
107
-
108
- it "should only read comments for articles which are published and user is 'me'" do
109
- @ability.can :read, Comment, :article => { :user => { :name => 'me' }, :published => true }
110
- user1 = User.create(:name => 'me')
111
- comment1 = Comment.create(:article => Article.create(:published => true, :user => user1))
112
- comment2 = Comment.create(:article => Article.create(:published => true))
113
- comment3 = Comment.create(:article => Article.create(:published => false, :user => user1))
114
- expect(Comment.accessible_by(@ability).all).to eq [comment1]
115
- end
116
-
117
- it "should allow conditions in SQL and merge with hash conditions" do
118
- @ability.can :read, Article, :published => true
119
- @ability.can :read, Article, ["secret=?", true] do |article|
120
- article.secret
121
- end
122
- @ability.cannot :read, Article, "priority > 1" do |article|
123
- article.priority > 1
124
- end
125
- article1 = Article.create(:published => true, :secret => false, :priority => 1)
126
- article2 = Article.create(:published => true, :secret => true, :priority => 1)
127
- article3 = Article.create(:published => true, :secret => true, :priority => 2)
128
- article4 = Article.create(:published => false, :secret => false, :priority => 2)
129
- expect(Article.accessible_by(@ability).all).to eq [article1, article2]
130
- end
131
- end
132
- end
@@ -1,52 +0,0 @@
1
- require "spec_helper"
2
- require "ostruct" # for OpenStruct below
3
-
4
- # Most of Rule functionality is tested in Ability specs
5
- describe CanCan::Rule do
6
- before(:each) do
7
- @conditions = {}
8
- @rule = CanCan::Rule.new(true, :read, Integer, @conditions, nil)
9
- end
10
-
11
- it "returns no association joins if none exist" do
12
- expect(@rule.associations_hash).to eq({})
13
- end
14
-
15
- it "returns no association for joins if just attributes" do
16
- @conditions[:foo] = :bar
17
- expect(@rule.associations_hash).to eq({})
18
- end
19
-
20
- it "returns single association for joins" do
21
- @conditions[:foo] = {:bar => 1}
22
- expect(@rule.associations_hash).to eq({:foo => {}})
23
- end
24
-
25
- it "returns multiple associations for joins" do
26
- @conditions[:foo] = {:bar => 1}
27
- @conditions[:test] = {1 => 2}
28
- expect(@rule.associations_hash).to eq({:foo => {}, :test => {}})
29
- end
30
-
31
- it "returns nested associations for joins" do
32
- @conditions[:foo] = {:bar => {1 => 2}}
33
- expect(@rule.associations_hash).to eq({:foo => {:bar => {}}})
34
- end
35
-
36
- it "returns no association joins if conditions is nil" do
37
- rule = CanCan::Rule.new(true, :read, Integer, nil, nil)
38
- expect(rule.associations_hash).to eq({})
39
- end
40
-
41
- it "is not mergeable if conditions are not simple hashes" do
42
- meta_where = OpenStruct.new(:name => 'metawhere', :column => 'test')
43
- @conditions[meta_where] = :bar
44
-
45
- expect(@rule).to be_unmergeable
46
- end
47
-
48
- it "is not mergeable if conditions is an empty hash" do
49
- @conditions = {}
50
- expect(@rule).to_not be_unmergeable
51
- end
52
- end
@@ -1,13 +0,0 @@
1
- RSpec::Matchers.define :orderlessly_match do |original_string|
2
- match do |given_string|
3
- original_string.split('').sort == given_string.split('').sort
4
- end
5
-
6
- failure_message do |given_string|
7
- "expected \"#{given_string}\" to have the same characters as \"#{original_string}\""
8
- end
9
-
10
- failure_message_when_negated do |given_string|
11
- "expected \"#{given_string}\" not to have the same characters as \"#{original_string}\""
12
- end
13
- end
@@ -1,2 +0,0 @@
1
- --color
2
- --backtrace
@@ -1,27 +0,0 @@
1
- require 'rubygems'
2
- require 'bundler/setup'
3
-
4
- Bundler.require
5
-
6
- require 'matchers'
7
- require 'cancan/matchers'
8
-
9
- # I8n setting to fix deprecation.
10
- if defined?(I18n) && I18n.respond_to?('enforce_available_locales=')
11
- I18n.enforce_available_locales = false
12
- end
13
-
14
- # Add support to load paths
15
- $:.unshift File.expand_path('../support', __FILE__)
16
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
17
-
18
- RSpec.configure do |config|
19
- config.filter_run :focus => true
20
- config.run_all_when_everything_filtered = true
21
- config.mock_with :rspec
22
- config.order = 'random'
23
-
24
- config.expect_with :rspec do |c|
25
- c.syntax = :expect
26
- end
27
- end