mongoid_ability 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +7 -0
  4. data/README.md +55 -23
  5. data/lib/cancancan/model_adapters/mongoid_adapter.rb +156 -0
  6. data/lib/cancancan/model_additions.rb +30 -0
  7. data/lib/mongoid_ability.rb +12 -12
  8. data/lib/mongoid_ability/ability.rb +67 -26
  9. data/lib/mongoid_ability/find_lock.rb +71 -0
  10. data/lib/mongoid_ability/lock.rb +25 -16
  11. data/lib/mongoid_ability/locks_decorator.rb +45 -0
  12. data/lib/mongoid_ability/owner.rb +23 -3
  13. data/lib/mongoid_ability/subject.rb +10 -22
  14. data/lib/mongoid_ability/version.rb +1 -1
  15. data/test/cancancan/model_adapters/mongoid_adapter_options_test.rb +102 -0
  16. data/test/cancancan/model_adapters/mongoid_adapter_test.rb +207 -0
  17. data/test/mongoid_ability/ability_basic_benchmark.rb +30 -0
  18. data/test/mongoid_ability/ability_basic_test.rb +44 -13
  19. data/test/mongoid_ability/ability_marshal_test.rb +17 -0
  20. data/test/mongoid_ability/ability_options_test.rb +93 -0
  21. data/test/mongoid_ability/ability_test.rb +87 -106
  22. data/test/mongoid_ability/find_lock_test.rb +67 -0
  23. data/test/mongoid_ability/lock_test.rb +32 -40
  24. data/test/mongoid_ability/owner_locks_test.rb +14 -21
  25. data/test/mongoid_ability/owner_test.rb +4 -14
  26. data/test/mongoid_ability/subject_test.rb +32 -58
  27. data/test/support/test_classes/my_lock.rb +8 -13
  28. data/test/support/test_classes/my_owner.rb +13 -15
  29. data/test/support/test_classes/my_role.rb +9 -11
  30. data/test/support/test_classes/my_subject.rb +16 -9
  31. data/test/test_helper.rb +12 -2
  32. metadata +18 -25
  33. data/lib/mongoid_ability/accessible_query_builder.rb +0 -64
  34. data/lib/mongoid_ability/resolve_default_locks.rb +0 -17
  35. data/lib/mongoid_ability/resolve_inherited_locks.rb +0 -35
  36. data/lib/mongoid_ability/resolve_locks.rb +0 -12
  37. data/lib/mongoid_ability/resolve_owner_locks.rb +0 -35
  38. data/lib/mongoid_ability/resolver.rb +0 -24
  39. data/lib/mongoid_ability/values_for_accessible_query.rb +0 -74
  40. data/test/mongoid_ability/ability_syntactic_sugar_test.rb +0 -32
  41. data/test/mongoid_ability/accessible_query_builder_test.rb +0 -119
  42. data/test/mongoid_ability/can_options_test.rb +0 -17
  43. data/test/mongoid_ability/resolve_default_locks_test.rb +0 -41
  44. data/test/mongoid_ability/resolve_inherited_locks_test.rb +0 -50
  45. data/test/mongoid_ability/resolve_owner_locks_test.rb +0 -56
  46. data/test/mongoid_ability/resolver_test.rb +0 -23
  47. data/test/mongoid_ability/subject_accessible_by_test.rb +0 -147
@@ -25,8 +25,18 @@ DatabaseCleaner.orm = :mongoid
25
25
  DatabaseCleaner.strategy = :truncation
26
26
 
27
27
  class MiniTest::Spec
28
- before(:each) { DatabaseCleaner.start }
29
- after(:each) { DatabaseCleaner.clean }
28
+ before(:each) do
29
+ DatabaseCleaner.start
30
+ end
31
+
32
+ after(:each) do
33
+ [ MySubject,
34
+ MySubject1, MySubject2,
35
+ MySubject11, MySubject21
36
+ ].each(&:reset_default_locks!)
37
+
38
+ DatabaseCleaner.clean
39
+ end
30
40
  end
31
41
 
32
42
  class Object
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_ability
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomas Celizna
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-08 00:00:00.000000000 Z
11
+ date: 2018-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cancancan
@@ -153,33 +153,28 @@ files:
153
153
  - LICENSE.txt
154
154
  - README.md
155
155
  - Rakefile
156
+ - lib/cancancan/model_adapters/mongoid_adapter.rb
157
+ - lib/cancancan/model_additions.rb
156
158
  - lib/mongoid_ability.rb
157
159
  - lib/mongoid_ability/ability.rb
158
- - lib/mongoid_ability/accessible_query_builder.rb
160
+ - lib/mongoid_ability/find_lock.rb
159
161
  - lib/mongoid_ability/lock.rb
162
+ - lib/mongoid_ability/locks_decorator.rb
160
163
  - lib/mongoid_ability/owner.rb
161
- - lib/mongoid_ability/resolve_default_locks.rb
162
- - lib/mongoid_ability/resolve_inherited_locks.rb
163
- - lib/mongoid_ability/resolve_locks.rb
164
- - lib/mongoid_ability/resolve_owner_locks.rb
165
- - lib/mongoid_ability/resolver.rb
166
164
  - lib/mongoid_ability/subject.rb
167
- - lib/mongoid_ability/values_for_accessible_query.rb
168
165
  - lib/mongoid_ability/version.rb
169
166
  - mongoid_ability.gemspec
167
+ - test/cancancan/model_adapters/mongoid_adapter_options_test.rb
168
+ - test/cancancan/model_adapters/mongoid_adapter_test.rb
169
+ - test/mongoid_ability/ability_basic_benchmark.rb
170
170
  - test/mongoid_ability/ability_basic_test.rb
171
- - test/mongoid_ability/ability_syntactic_sugar_test.rb
171
+ - test/mongoid_ability/ability_marshal_test.rb
172
+ - test/mongoid_ability/ability_options_test.rb
172
173
  - test/mongoid_ability/ability_test.rb
173
- - test/mongoid_ability/accessible_query_builder_test.rb
174
- - test/mongoid_ability/can_options_test.rb
174
+ - test/mongoid_ability/find_lock_test.rb
175
175
  - test/mongoid_ability/lock_test.rb
176
176
  - test/mongoid_ability/owner_locks_test.rb
177
177
  - test/mongoid_ability/owner_test.rb
178
- - test/mongoid_ability/resolve_default_locks_test.rb
179
- - test/mongoid_ability/resolve_inherited_locks_test.rb
180
- - test/mongoid_ability/resolve_owner_locks_test.rb
181
- - test/mongoid_ability/resolver_test.rb
182
- - test/mongoid_ability/subject_accessible_by_test.rb
183
178
  - test/mongoid_ability/subject_test.rb
184
179
  - test/support/expectations.rb
185
180
  - test/support/test_classes/my_lock.rb
@@ -213,19 +208,17 @@ specification_version: 4
213
208
  summary: Custom Ability class that allows CanCanCan authorization library store permissions
214
209
  in MongoDB via the Mongoid gem.
215
210
  test_files:
211
+ - test/cancancan/model_adapters/mongoid_adapter_options_test.rb
212
+ - test/cancancan/model_adapters/mongoid_adapter_test.rb
213
+ - test/mongoid_ability/ability_basic_benchmark.rb
216
214
  - test/mongoid_ability/ability_basic_test.rb
217
- - test/mongoid_ability/ability_syntactic_sugar_test.rb
215
+ - test/mongoid_ability/ability_marshal_test.rb
216
+ - test/mongoid_ability/ability_options_test.rb
218
217
  - test/mongoid_ability/ability_test.rb
219
- - test/mongoid_ability/accessible_query_builder_test.rb
220
- - test/mongoid_ability/can_options_test.rb
218
+ - test/mongoid_ability/find_lock_test.rb
221
219
  - test/mongoid_ability/lock_test.rb
222
220
  - test/mongoid_ability/owner_locks_test.rb
223
221
  - test/mongoid_ability/owner_test.rb
224
- - test/mongoid_ability/resolve_default_locks_test.rb
225
- - test/mongoid_ability/resolve_inherited_locks_test.rb
226
- - test/mongoid_ability/resolve_owner_locks_test.rb
227
- - test/mongoid_ability/resolver_test.rb
228
- - test/mongoid_ability/subject_accessible_by_test.rb
229
222
  - test/mongoid_ability/subject_test.rb
230
223
  - test/support/expectations.rb
231
224
  - test/support/test_classes/my_lock.rb
@@ -1,64 +0,0 @@
1
- module MongoidAbility
2
- class AccessibleQueryBuilder < Struct.new(:base_class, :ability, :action, :options)
3
- def self.call(*args)
4
- new(*args).call
5
- end
6
-
7
- def initialize(base_class, ability, action, options = {})
8
- super(base_class, ability, action, options)
9
- end
10
-
11
- # =====================================================================
12
-
13
- def call
14
- base_class.criteria.where(conditions)
15
- end
16
-
17
- def conditions
18
- { '$and' => [{ '$or' => [closed_types_condition, open_types_and_ids_condition] }, closed_ids_condition] }
19
- end
20
-
21
- private # =============================================================
22
-
23
- def closed_types_condition
24
- { type_key => { '$nin' => values.closed_types.uniq } }
25
- end
26
-
27
- def open_types_and_ids_condition
28
- {
29
- type_key => { '$in' => values.open_types_and_ids.map(&:type).uniq },
30
- id_key => { '$in' => values.open_types_and_ids.map(&:id).uniq }
31
- }
32
- end
33
-
34
- def closed_ids_condition
35
- { id_key => { '$nin' => values.closed_ids.uniq } }
36
- end
37
-
38
- # ---------------------------------------------------------------------
39
-
40
- def values
41
- @values ||= base_class.values_for_accessible_query(ability, action, options)
42
- end
43
-
44
- # ---------------------------------------------------------------------
45
-
46
- def base_class_superclass
47
- @base_class_superclass ||= (base_class.ancestors_with_default_locks.last || base_class)
48
- end
49
-
50
- # ---------------------------------------------------------------------
51
-
52
- def prefix
53
- options.fetch(:prefix, nil)
54
- end
55
-
56
- def id_key
57
- [prefix, '_id'].reject(&:blank?).join.to_sym
58
- end
59
-
60
- def type_key
61
- [prefix, '_type'].reject(&:blank?).join.to_sym
62
- end
63
- end
64
- end
@@ -1,17 +0,0 @@
1
- module MongoidAbility
2
- class ResolveDefaultLocks < Resolver
3
- def call
4
- closed_lock = default_locks.detect { |l| l.closed?(options) }
5
- return closed_lock if closed_lock
6
-
7
- open_lock = default_locks.detect { |l| l.open?(options) }
8
- return open_lock if open_lock
9
- end
10
-
11
- private
12
-
13
- def default_locks
14
- subject_class.default_locks.select { |l| l.action.to_s == action.to_s }
15
- end
16
- end
17
- end
@@ -1,35 +0,0 @@
1
- module MongoidAbility
2
- class ResolveInheritedLocks < Resolver
3
- def call
4
- owner_lock = resolved_owner_lock
5
- return owner_lock if owner_lock
6
-
7
- if owner.respond_to?(owner.class.inherit_from_relation_name) && !owner.inherit_from_relation.nil?
8
- resolved_inherited_owner_locks = owner.inherit_from_relation.map { |inherited_owner| resolved_inherited_owner_lock(inherited_owner) }.compact
9
-
10
- open_lock = resolved_inherited_owner_locks.detect { |l| l.open?(options) }
11
- return open_lock if open_lock
12
-
13
- closed_lock = resolved_inherited_owner_locks.detect { |l| l.closed?(options) }
14
- return closed_lock if closed_lock
15
- end
16
-
17
- resolved_default_lock
18
- end
19
-
20
- private
21
-
22
- def resolved_owner_lock
23
- @resolved_owner_lock ||= ResolveOwnerLocks.call(owner, action, subject_class, subject_id, options)
24
- end
25
-
26
- def resolved_inherited_owner_lock(inherited_owner)
27
- @resolved_inherited_owner_lock ||= {}
28
- @resolved_inherited_owner_lock[inherited_owner] ||= ResolveOwnerLocks.call(inherited_owner, action, subject_class, subject_id, options)
29
- end
30
-
31
- def resolved_default_lock
32
- @resolved_default_lock ||= ResolveDefaultLocks.call(nil, action, subject_class, nil, options)
33
- end
34
- end
35
- end
@@ -1,12 +0,0 @@
1
- module MongoidAbility
2
- class ResolveLocks < Resolver
3
- def call
4
- lock = nil
5
- subject_class.self_and_ancestors_with_default_locks.each do |cls|
6
- lock = ResolveInheritedLocks.call(owner, action, cls.to_s, subject_id, options)
7
- break unless lock.nil?
8
- end
9
- lock
10
- end
11
- end
12
- end
@@ -1,35 +0,0 @@
1
- # OPTIMIZE: this seems quite expensive
2
-
3
- module MongoidAbility
4
- class ResolveOwnerLocks < Resolver
5
- def call
6
- return unless owner.respond_to?(:locks_relation)
7
-
8
- locks_for_subject_type = owner.locks_relation.for_action(action).for_subject_type(subject_type).cache
9
-
10
- return unless locks_for_subject_type.exists?
11
-
12
- # return lock if owner defines lock for id
13
- if subject_id.present?
14
- id_locks = locks_for_subject_type.id_locks.for_subject_id(subject_id).cache
15
-
16
- closed_lock = id_locks.detect { |l| l.closed?(options) }
17
- return closed_lock if closed_lock
18
-
19
- open_lock = id_locks.detect { |l| l.open?(options) }
20
- return open_lock if open_lock
21
- end
22
-
23
- # return lock if owner defines lock for subject_type
24
- class_locks = locks_for_subject_type.class_locks.cache
25
-
26
- closed_lock = class_locks.class_locks.detect { |l| l.closed?(options) }
27
- return closed_lock if closed_lock
28
-
29
- open_lock = class_locks.class_locks.detect { |l| l.open?(options) }
30
- return open_lock if open_lock
31
-
32
- nil
33
- end
34
- end
35
- end
@@ -1,24 +0,0 @@
1
- module MongoidAbility
2
- class Resolver < Struct.new(:owner, :action, :subject_type, :subject_id, :options)
3
- attr_reader :subject_class
4
-
5
- def self.call(*args)
6
- new(*args).call
7
- end
8
-
9
- def initialize(owner, action, subject_type, subject_id = nil, options = {})
10
- super(owner, action, subject_type, subject_id, options)
11
-
12
- @subject_class = subject_type.to_s.constantize
13
-
14
- raise StandardError, "#{subject_type} class does not have default locks" unless @subject_class.respond_to?(:default_locks)
15
- raise StandardError, "#{subject_type} class does not have default lock for :#{action} action" unless @subject_class.self_and_ancestors_with_default_locks.any? do |cls|
16
- cls.default_locks.any? { |l| l.action == action }
17
- end
18
- end
19
-
20
- def call
21
- raise NotImplementedError
22
- end
23
- end
24
- end
@@ -1,74 +0,0 @@
1
- module MongoidAbility
2
- class ValuesForAccessibleQuery < Struct.new(:base_class, :ability, :action, :options)
3
- def self.call(*args)
4
- new(*args).call
5
- end
6
-
7
- def initialize(base_class, ability, action, options = {})
8
- super(base_class, ability, action, options)
9
- end
10
-
11
- # =====================================================================
12
-
13
- def call
14
- closed_types = [] # [cls]
15
- open_types_and_ids = [] # OpenStruct.new(type: …, id: …)
16
- closed_ids = [] # [id]
17
-
18
- base_class_and_descendants.each do |cls|
19
- closed_types << cls.to_s if ability.cannot?(action, cls, options)
20
- id_locks(cls).each do |lock|
21
- if ability.can?(action, cls.new(_id: lock.subject_id), options)
22
- open_types_and_ids << OpenStruct.new(type: cls.to_s, id: lock.subject_id)
23
- else
24
- closed_ids << lock.subject_id
25
- end
26
- end
27
- end
28
-
29
- OpenStruct.new(
30
- closed_types: closed_types,
31
- open_types_and_ids: open_types_and_ids,
32
- closed_ids: closed_ids
33
- )
34
- end
35
-
36
- private # =============================================================
37
-
38
- def base_class_and_descendants
39
- @base_class_and_descendants ||= [base_class].concat(base_class.descendants)
40
- end
41
-
42
- # ---------------------------------------------------------------------
43
-
44
- def id_locks(cls)
45
- (Array(owner_id_locks_for_subject_type(cls)) + Array(inherited_from_relation_ids_locks_for_subject_type(cls))).flatten
46
- end
47
-
48
- def owner_id_locks_for_subject_type(cls)
49
- @owner_id_locks_for_subject_type ||= {}
50
- @owner_id_locks_for_subject_type[cls] ||= owner.locks_relation.id_locks.for_action(action).for_subject_type(cls.to_s).cache
51
- end
52
-
53
- def inherited_from_relation_ids_locks_for_subject_type(cls)
54
- return [] unless inherited_from_relation
55
- @inherited_from_relation_ids_locks_for_subject_type ||= {}
56
- @inherited_from_relation_ids_locks_for_subject_type[cls] ||= inherited_from_relation.collect do |o|
57
- o.locks_relation.id_locks.for_action(action).for_subject_type(cls.to_s).cache
58
- end.flatten
59
- end
60
-
61
- # ---------------------------------------------------------------------
62
-
63
- def owner
64
- @owner ||= ability.owner
65
- end
66
-
67
- # ---------------------------------------------------------------------
68
-
69
- def inherited_from_relation
70
- return unless owner.respond_to?(owner.class.inherit_from_relation_name)
71
- owner.inherit_from_relation
72
- end
73
- end
74
- end
@@ -1,32 +0,0 @@
1
- require 'test_helper'
2
-
3
- module MongoidAbility
4
- describe 'syntactic sugar' do
5
- let(:read_lock) { MyLock.new(subject_type: MySubject, action: :read, outcome: false) }
6
- let(:owner) { MyRole.new(my_locks: [read_lock]) }
7
- let(:ability) { Ability.new(owner) }
8
- let(:options) { { x: 1 } }
9
-
10
- let(:default_locks) { [MyLock.new(action: :read, outcome: true)] }
11
-
12
- it 'owner can?' do
13
- MySubject.stub :default_locks, default_locks do
14
- [MySubject].select(&ability.can_read(options)).must_equal []
15
- [MySubject].select(&ability.can_read?(options)).must_equal []
16
-
17
- ability.can_read(MySubject, options).must_equal false
18
- ability.can_read?(MySubject, options).must_equal false
19
- end
20
- end
21
-
22
- it 'owner cannot?' do
23
- MySubject.stub :default_locks, default_locks do
24
- [MySubject].select(&ability.cannot_read(options)).must_equal [MySubject]
25
- [MySubject].select(&ability.cannot_read?(options)).must_equal [MySubject]
26
-
27
- ability.cannot_read(MySubject, options).must_equal true
28
- ability.cannot_read?(MySubject, options).must_equal true
29
- end
30
- end
31
- end
32
- end
@@ -1,119 +0,0 @@
1
- require 'test_helper'
2
-
3
- module MongoidAbility
4
- describe AccessibleQueryBuilder do
5
- let(:base_class) { MySubject }
6
- let(:owner) { MyOwner.new }
7
- let(:ability) { Ability.new(owner) }
8
- let(:action) { :read }
9
- let(:options) { Hash.new }
10
-
11
- let(:my_subject_default_locks) { [MyLock.new(subject_type: MySubject, action: :read, outcome: true)] }
12
- let(:my_subject_1_default_locks) { [MyLock.new(subject_type: MySubject1, action: :false, outcome: true)] }
13
- let(:my_subject_2_default_locks) { [] }
14
-
15
- subject { AccessibleQueryBuilder.call(base_class, ability, action, options) }
16
-
17
- it 'returns Mongoid::Criteria' do
18
- MySubject.stub :default_locks, my_subject_default_locks do
19
- MySubject1.stub :default_locks, my_subject_1_default_locks do
20
- MySubject2.stub :default_locks, my_subject_2_default_locks do
21
- subject.must_be_kind_of Mongoid::Criteria
22
- end
23
- end
24
- end
25
- end
26
-
27
- describe 'prefix' do
28
- let(:my_subject) { MySubject.create! }
29
- let(:my_subject_1) { MySubject1.create! }
30
- let(:my_subject_2) { MySubject2.create! }
31
- let(:prefix) { :foo }
32
-
33
- before do
34
- my_subject; my_subject_1
35
- owner.my_locks = [MyLock.new(subject_type: MySubject, action: :read, outcome: false)]
36
- end
37
-
38
- it 'allows to pass prefix' do
39
- MySubject.stub :default_locks, my_subject_default_locks do
40
- MySubject1.stub :default_locks, my_subject_1_default_locks do
41
- MySubject2.stub :default_locks, my_subject_2_default_locks do
42
- selector = AccessibleQueryBuilder.call(base_class, ability, action, prefix: prefix).selector
43
- selector.must_equal('$and' => [{ '$or' => [{ "#{prefix}_type" => { '$nin' => ['MongoidAbility::MySubject', 'MongoidAbility::MySubject1', 'MongoidAbility::MySubject2'] } }, { "#{prefix}_type" => { '$in' => [] }, "#{prefix}_id" => { '$in' => [] } }] }, { "#{prefix}_id" => { '$nin' => [] } }])
44
- end
45
- end
46
- end
47
- end
48
- end
49
-
50
- # ---------------------------------------------------------------------
51
-
52
- describe 'closed_types' do
53
- let(:my_subject) { MySubject.create! }
54
- let(:my_subject_1) { MySubject1.create! }
55
-
56
- before do
57
- my_subject; my_subject_1
58
- owner.my_locks = [MyLock.new(subject_type: MySubject, action: :read, outcome: false)]
59
- end
60
-
61
- it 'does not return subject with that id' do
62
- MySubject.stub :default_locks, my_subject_default_locks do
63
- MySubject1.stub :default_locks, my_subject_1_default_locks do
64
- MySubject2.stub :default_locks, my_subject_2_default_locks do
65
- MySubject.accessible_by(ability, :read).wont_include my_subject
66
- MySubject.accessible_by(ability, :read).wont_include my_subject_1
67
- MySubject1.accessible_by(ability, :read).wont_include my_subject_1
68
- end
69
- end
70
- end
71
- end
72
- end
73
-
74
- describe 'closed_ids' do
75
- let(:my_subject_a) { MySubject.create! }
76
- let(:my_subject_b) { MySubject.create! }
77
-
78
- before do
79
- my_subject_a; my_subject_b
80
- owner.my_locks = [MyLock.new(subject: my_subject_a, action: :read, outcome: false)]
81
- end
82
-
83
- it 'does not return subject with that id' do
84
- MySubject.stub :default_locks, my_subject_default_locks do
85
- MySubject1.stub :default_locks, my_subject_1_default_locks do
86
- MySubject2.stub :default_locks, my_subject_2_default_locks do
87
- MySubject.accessible_by(ability, :read).wont_include my_subject_a
88
- MySubject.accessible_by(ability, :read).must_include my_subject_b
89
- end
90
- end
91
- end
92
- end
93
- end
94
-
95
- describe 'closed_types & open_ids' do
96
- let(:my_subject) { MySubject.create! }
97
- let(:my_subject_1) { MySubject1.create! }
98
-
99
- before do
100
- owner.my_locks = [
101
- MyLock.new(subject_type: MySubject, action: :read, outcome: false),
102
- MyLock.new(subject: my_subject, action: :read, outcome: true),
103
- MyLock.new(subject: my_subject_1, action: :read, outcome: true)
104
- ]
105
- end
106
-
107
- it 'does not return subject with that id' do
108
- MySubject.stub :default_locks, my_subject_default_locks do
109
- MySubject1.stub :default_locks, my_subject_1_default_locks do
110
- MySubject2.stub :default_locks, my_subject_2_default_locks do
111
- MySubject.accessible_by(ability, :read).must_include my_subject
112
- MySubject.accessible_by(ability, :read).must_include my_subject_1
113
- end
114
- end
115
- end
116
- end
117
- end
118
- end
119
- end