mongoid_ability 1.0.0 → 2.0.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/Gemfile +7 -0
- data/README.md +55 -23
- data/lib/cancancan/model_adapters/mongoid_adapter.rb +156 -0
- data/lib/cancancan/model_additions.rb +30 -0
- data/lib/mongoid_ability.rb +12 -12
- data/lib/mongoid_ability/ability.rb +67 -26
- data/lib/mongoid_ability/find_lock.rb +71 -0
- data/lib/mongoid_ability/lock.rb +25 -16
- data/lib/mongoid_ability/locks_decorator.rb +45 -0
- data/lib/mongoid_ability/owner.rb +23 -3
- data/lib/mongoid_ability/subject.rb +10 -22
- data/lib/mongoid_ability/version.rb +1 -1
- data/test/cancancan/model_adapters/mongoid_adapter_options_test.rb +102 -0
- data/test/cancancan/model_adapters/mongoid_adapter_test.rb +207 -0
- data/test/mongoid_ability/ability_basic_benchmark.rb +30 -0
- data/test/mongoid_ability/ability_basic_test.rb +44 -13
- data/test/mongoid_ability/ability_marshal_test.rb +17 -0
- data/test/mongoid_ability/ability_options_test.rb +93 -0
- data/test/mongoid_ability/ability_test.rb +87 -106
- data/test/mongoid_ability/find_lock_test.rb +67 -0
- data/test/mongoid_ability/lock_test.rb +32 -40
- data/test/mongoid_ability/owner_locks_test.rb +14 -21
- data/test/mongoid_ability/owner_test.rb +4 -14
- data/test/mongoid_ability/subject_test.rb +32 -58
- data/test/support/test_classes/my_lock.rb +8 -13
- data/test/support/test_classes/my_owner.rb +13 -15
- data/test/support/test_classes/my_role.rb +9 -11
- data/test/support/test_classes/my_subject.rb +16 -9
- data/test/test_helper.rb +12 -2
- metadata +18 -25
- data/lib/mongoid_ability/accessible_query_builder.rb +0 -64
- data/lib/mongoid_ability/resolve_default_locks.rb +0 -17
- data/lib/mongoid_ability/resolve_inherited_locks.rb +0 -35
- data/lib/mongoid_ability/resolve_locks.rb +0 -12
- data/lib/mongoid_ability/resolve_owner_locks.rb +0 -35
- data/lib/mongoid_ability/resolver.rb +0 -24
- data/lib/mongoid_ability/values_for_accessible_query.rb +0 -74
- data/test/mongoid_ability/ability_syntactic_sugar_test.rb +0 -32
- data/test/mongoid_ability/accessible_query_builder_test.rb +0 -119
- data/test/mongoid_ability/can_options_test.rb +0 -17
- data/test/mongoid_ability/resolve_default_locks_test.rb +0 -41
- data/test/mongoid_ability/resolve_inherited_locks_test.rb +0 -50
- data/test/mongoid_ability/resolve_owner_locks_test.rb +0 -56
- data/test/mongoid_ability/resolver_test.rb +0 -23
- data/test/mongoid_ability/subject_accessible_by_test.rb +0 -147
@@ -0,0 +1,71 @@
|
|
1
|
+
module MongoidAbility
|
2
|
+
# finds first lock that controls specified params
|
3
|
+
|
4
|
+
class FindLock < Struct.new(:owner, :action, :subject_type, :subject_id, :options)
|
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.to_s, subject_id, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
lock = nil
|
15
|
+
subject_class.self_and_ancestors_with_default_locks.each do |cls|
|
16
|
+
break if lock = FindOwnedLock.call(owner, action, cls, subject_id, options)
|
17
|
+
break if lock = FindInheritedLock.call(owner, action, cls, subject_id, options)
|
18
|
+
break if lock = FindDefaultLock.call(owner, action, cls, subject_id, options)
|
19
|
+
end
|
20
|
+
lock
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def subject_class
|
26
|
+
subject_type.constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
# ---------------------------------------------------------------------
|
30
|
+
|
31
|
+
class FindDefaultLock < FindLock
|
32
|
+
def call
|
33
|
+
locks = subject_class.default_locks.for_action(action)
|
34
|
+
locks.compact.detect(&:closed?) || locks.compact.detect(&:open?)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class FindInheritedLock < FindLock
|
39
|
+
def call
|
40
|
+
return unless owner.respond_to?(owner.class.inherit_from_relation_name)
|
41
|
+
locks = LocksDecorator.new(
|
42
|
+
owner.inherit_from_relation
|
43
|
+
.flat_map { |inherited_owner| FindOwnedLock.call(inherited_owner, action, subject_type, subject_id, options) }
|
44
|
+
)
|
45
|
+
|
46
|
+
if subject_id.present?
|
47
|
+
lock = locks.for_subject_id(subject_id).detect(&:closed?) ||
|
48
|
+
locks.for_subject_id(subject_id).detect(&:open?)
|
49
|
+
return lock unless lock.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
locks.class_locks.detect(&:open?) || locks.class_locks.detect(&:closed?)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class FindOwnedLock < FindLock
|
57
|
+
def call
|
58
|
+
return unless owner.respond_to?(:locks_relation)
|
59
|
+
locks = owner.locks_relation.for_action(action).for_subject_type(subject_type)
|
60
|
+
|
61
|
+
if subject_id.present?
|
62
|
+
lock = locks.for_subject_id(subject_id).detect(&:closed?) ||
|
63
|
+
locks.for_subject_id(subject_id).detect(&:open?)
|
64
|
+
return lock unless lock.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
locks.class_locks.detect(&:closed?) || locks.class_locks.detect(&:open?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/mongoid_ability/lock.rb
CHANGED
@@ -46,32 +46,24 @@ module MongoidAbility
|
|
46
46
|
end
|
47
47
|
|
48
48
|
concerning :Outcome do
|
49
|
-
|
50
|
-
def calculated_outcome(_options = {})
|
49
|
+
def open?
|
51
50
|
outcome
|
52
51
|
end
|
53
52
|
|
54
|
-
def
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
def closed?(options = {})
|
59
|
-
!open?(options)
|
53
|
+
def closed?
|
54
|
+
!open?
|
60
55
|
end
|
61
56
|
end
|
62
57
|
|
58
|
+
# calculates outcome as if this lock is not present
|
63
59
|
concerning :InheritedOutcome do
|
64
|
-
|
65
|
-
|
66
|
-
return calculated_outcome(options) unless owner.present?
|
60
|
+
def inherited_outcome(options = {})
|
61
|
+
return outcome unless owner.present?
|
67
62
|
cloned_owner = owner.clone
|
68
63
|
cloned_owner.locks_relation = cloned_owner.locks_relation - [self]
|
69
|
-
MongoidAbility::Ability.new(cloned_owner)
|
70
|
-
end
|
64
|
+
cloned_ability = MongoidAbility::Ability.new(cloned_owner)
|
71
65
|
|
72
|
-
|
73
|
-
def default_options
|
74
|
-
{}
|
66
|
+
cloned_ability.can?(action, (subject.present? ? subject : subject_class), options)
|
75
67
|
end
|
76
68
|
end
|
77
69
|
|
@@ -80,5 +72,22 @@ module MongoidAbility
|
|
80
72
|
subject_type.constantize
|
81
73
|
end
|
82
74
|
end
|
75
|
+
|
76
|
+
concerning :Group do
|
77
|
+
def group_key_for_calc
|
78
|
+
[subject_type, subject_id, action, options]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
concerning :Sort do
|
83
|
+
class_methods do
|
84
|
+
def sort
|
85
|
+
-> (a, b) {
|
86
|
+
[a.subject_type, a.subject_id.to_s, a.action, (a.outcome ? -1 : 1)] <=>
|
87
|
+
[b.subject_type, b.subject_id.to_s, b.action, (b.outcome ? -1 : 1)]
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
83
92
|
end
|
84
93
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MongoidAbility
|
2
|
+
# adds scope-like methods on top of an Array containing locks
|
3
|
+
|
4
|
+
class LocksDecorator < SimpleDelegator
|
5
|
+
def for_action(action)
|
6
|
+
compact.select do |lock|
|
7
|
+
lock.action == action.to_sym
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def for_subject_type(subject_type)
|
12
|
+
compact.select do |lock|
|
13
|
+
lock.subject_type == subject_type.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def for_subject_types(subject_types)
|
18
|
+
subject_types = Array(subject_types).map(&:to_s)
|
19
|
+
compact.select do |lock|
|
20
|
+
subject_types.include?(lock.subject_type)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def for_subject_id(subject_id)
|
25
|
+
compact.select do |lock|
|
26
|
+
lock.subject_id == BSON::ObjectId.from_string(subject_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def for_subject(subject)
|
31
|
+
compact.select do |lock|
|
32
|
+
lock.subject_type == subject.class.model_name &&
|
33
|
+
lock.subject_id == subject.id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def class_locks
|
38
|
+
compact.select(&:class_lock?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def id_locks
|
42
|
+
compact.select(&:id_lock?)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -3,7 +3,9 @@ module MongoidAbility
|
|
3
3
|
def self.included(base)
|
4
4
|
base.extend ClassMethods
|
5
5
|
base.class_eval do
|
6
|
-
delegate
|
6
|
+
delegate :can?, :cannot?,
|
7
|
+
to: :ability
|
8
|
+
|
7
9
|
before_save :cleanup_locks
|
8
10
|
end
|
9
11
|
end
|
@@ -19,14 +21,17 @@ module MongoidAbility
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def locks_relation
|
24
|
+
return unless respond_to?(self.class.locks_relation_name)
|
22
25
|
send(self.class.locks_relation_name)
|
23
26
|
end
|
24
27
|
|
25
28
|
def locks_relation=(val)
|
29
|
+
return unless respond_to?("#{self.class.locks_relation_name}=")
|
26
30
|
send "#{self.class.locks_relation_name}=", val
|
27
31
|
end
|
28
32
|
|
29
33
|
def inherit_from_relation
|
34
|
+
return unless respond_to?(self.class.inherit_from_relation_name)
|
30
35
|
send(self.class.inherit_from_relation_name)
|
31
36
|
end
|
32
37
|
|
@@ -36,14 +41,29 @@ module MongoidAbility
|
|
36
41
|
|
37
42
|
def has_lock?(lock)
|
38
43
|
@has_lock ||= {}
|
39
|
-
|
44
|
+
|
45
|
+
return @has_lock[lock.id.to_s] if @has_lock.key?(lock.id.to_s)
|
46
|
+
|
47
|
+
@has_lock[lock.id.to_s] ||= begin
|
48
|
+
locks_relation.where(
|
49
|
+
subject_type: lock.subject_type,
|
50
|
+
subject_id: lock.subject_id.presence,
|
51
|
+
action: lock.action,
|
52
|
+
options: lock.options
|
53
|
+
).exists?
|
54
|
+
end
|
40
55
|
end
|
41
56
|
|
42
57
|
private
|
43
58
|
|
44
59
|
def cleanup_locks
|
45
60
|
locks_relation.select(&:open?).each do |lock|
|
46
|
-
lock.destroy if locks_relation.where(
|
61
|
+
lock.destroy if locks_relation.where(
|
62
|
+
subject_type: lock.subject_type,
|
63
|
+
subject_id: lock.subject_id.presence,
|
64
|
+
action: lock.action,
|
65
|
+
options: lock.options
|
66
|
+
).any?(&:closed?)
|
47
67
|
end
|
48
68
|
end
|
49
69
|
end
|
@@ -3,12 +3,18 @@ module MongoidAbility
|
|
3
3
|
def self.included(base)
|
4
4
|
base.extend ClassMethods
|
5
5
|
base.class_eval do
|
6
|
+
# always set the _type field as it is used by :accessible_by queries
|
7
|
+
field :_type, type: String, default: model_name
|
6
8
|
end
|
7
9
|
end
|
8
10
|
|
9
11
|
module ClassMethods
|
10
12
|
def default_locks
|
11
|
-
@default_locks ||= []
|
13
|
+
@default_locks ||= LocksDecorator.new([])
|
14
|
+
end
|
15
|
+
|
16
|
+
def reset_default_locks!
|
17
|
+
@default_locks = LocksDecorator.new([])
|
12
18
|
end
|
13
19
|
|
14
20
|
def default_locks=(locks)
|
@@ -19,16 +25,14 @@ module MongoidAbility
|
|
19
25
|
lock = lock_cls.new(subject_type: to_s, action: action, outcome: outcome, options: options)
|
20
26
|
|
21
27
|
# remove any existing locks
|
22
|
-
if existing_lock = default_locks.detect { |l| l.action == lock.action }
|
28
|
+
if existing_lock = default_locks.detect { |l| l.action == lock.action && l.options == lock.options }
|
23
29
|
default_locks.delete(existing_lock)
|
24
30
|
end
|
25
31
|
|
26
32
|
# add new lock
|
27
|
-
default_locks.push
|
33
|
+
default_locks.push(lock)
|
28
34
|
end
|
29
35
|
|
30
|
-
# ---------------------------------------------------------------------
|
31
|
-
|
32
36
|
def self_and_ancestors_with_default_locks
|
33
37
|
ancestors.select { |a| a.is_a?(Class) && a.respond_to?(:default_locks) }
|
34
38
|
end
|
@@ -45,24 +49,8 @@ module MongoidAbility
|
|
45
49
|
self_and_ancestors_with_default_locks.last
|
46
50
|
end
|
47
51
|
|
48
|
-
# ---------------------------------------------------------------------
|
49
|
-
|
50
|
-
def default_lock_for_action(action)
|
51
|
-
default_locks.detect { |lock| lock.action == action.to_sym }
|
52
|
-
end
|
53
|
-
|
54
52
|
def has_default_lock_for_action?(action)
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
# ---------------------------------------------------------------------
|
59
|
-
|
60
|
-
def accessible_by(ability, action = :read, options = {})
|
61
|
-
AccessibleQueryBuilder.call(self, ability, action, options)
|
62
|
-
end
|
63
|
-
|
64
|
-
def values_for_accessible_query(ability, action = :read, options = {})
|
65
|
-
ValuesForAccessibleQuery.call(self, ability, action, options)
|
53
|
+
default_locks.for_action(action).present?
|
66
54
|
end
|
67
55
|
end
|
68
56
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module CanCan
|
4
|
+
module ModelAdapters
|
5
|
+
describe MongoidAdapter do
|
6
|
+
describe '.accessible_by' do
|
7
|
+
let(:owner) { MyOwner.new }
|
8
|
+
let(:ability) { MongoidAbility::Ability.new(owner) }
|
9
|
+
let(:subject1) { MySubject.new }
|
10
|
+
|
11
|
+
before do
|
12
|
+
subject1.save!
|
13
|
+
subject2.save!
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'Boolean' do
|
17
|
+
let(:subject2) { MySubject.new(override: true) }
|
18
|
+
|
19
|
+
describe 'positive' do
|
20
|
+
before(:all) { MySubject.default_lock MyLock, :read, true, override: true }
|
21
|
+
|
22
|
+
it { MySubject.accessible_by(ability).wont_include subject1 }
|
23
|
+
it { MySubject.accessible_by(ability).must_include subject2 }
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'negative' do
|
27
|
+
before(:all) do
|
28
|
+
MySubject.default_lock MyLock, :read, true
|
29
|
+
MySubject.default_lock MyLock, :read, false, override: true
|
30
|
+
end
|
31
|
+
|
32
|
+
it { MySubject.accessible_by(ability).must_include subject1 }
|
33
|
+
it { MySubject.accessible_by(ability).wont_include subject2 }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'String' do
|
38
|
+
let(:subject2) { MySubject.new(str_val: "Jan Tschichold") }
|
39
|
+
|
40
|
+
describe 'positive' do
|
41
|
+
before(:all) { MySubject.default_lock MyLock, :read, true, str_val: 'Jan Tschichold' }
|
42
|
+
|
43
|
+
it { MySubject.accessible_by(ability).wont_include subject1 }
|
44
|
+
it { MySubject.accessible_by(ability).must_include subject2 }
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'negative' do
|
48
|
+
before(:all) do
|
49
|
+
MySubject.default_lock MyLock, :read, true
|
50
|
+
MySubject.default_lock MyLock, :read, false, str_val: 'Jan Tschichold'
|
51
|
+
end
|
52
|
+
|
53
|
+
it { MySubject.accessible_by(ability).must_include subject1 }
|
54
|
+
it { MySubject.accessible_by(ability).wont_include subject2 }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'Regexp' do
|
59
|
+
let(:subject2) { MySubject.new(str_val: "Jan Tschichold") }
|
60
|
+
|
61
|
+
describe 'positive' do
|
62
|
+
before(:all) { MySubject.default_lock MyLock, :read, true, str_val: /tschichold/i }
|
63
|
+
|
64
|
+
it { MySubject.accessible_by(ability).wont_include subject1 }
|
65
|
+
it { MySubject.accessible_by(ability).must_include subject2 }
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'negative' do
|
69
|
+
before(:all) do
|
70
|
+
MySubject.default_lock MyLock, :read, true
|
71
|
+
MySubject.default_lock MyLock, :read, false, str_val: /tschichold/i
|
72
|
+
end
|
73
|
+
|
74
|
+
it { MySubject.accessible_by(ability).must_include subject1 }
|
75
|
+
it { MySubject.accessible_by(ability).wont_include subject2 }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'Array' do
|
80
|
+
let(:subject2) { MySubject.new(str_val: "John") }
|
81
|
+
|
82
|
+
describe 'positive' do
|
83
|
+
before(:all) { MySubject.default_lock MyLock, :read, true, str_val: %w(John Paul George Ringo) }
|
84
|
+
|
85
|
+
it { MySubject.accessible_by(ability).wont_include subject1 }
|
86
|
+
it { MySubject.accessible_by(ability).must_include subject2 }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'negative' do
|
90
|
+
before(:all) do
|
91
|
+
MySubject.default_lock MyLock, :read, true
|
92
|
+
MySubject.default_lock MyLock, :read, false, str_val: %w(John Paul George Ringo)
|
93
|
+
end
|
94
|
+
|
95
|
+
it { MySubject.accessible_by(ability).must_include subject1 }
|
96
|
+
it { MySubject.accessible_by(ability).wont_include subject2 }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module CanCan
|
4
|
+
module ModelAdapters
|
5
|
+
describe MongoidAdapter do
|
6
|
+
describe '.accessible_by' do
|
7
|
+
let(:my_subject) { MySubject.new }
|
8
|
+
let(:my_subject1) { MySubject1.new }
|
9
|
+
let(:my_subject11) { MySubject11.new }
|
10
|
+
let(:my_subject2) { MySubject2.new }
|
11
|
+
let(:my_subject21) { MySubject21.new }
|
12
|
+
|
13
|
+
let(:role_1) { MyRole.new }
|
14
|
+
let(:role_2) { MyRole.new }
|
15
|
+
let(:owner) { MyOwner.new(my_roles: [role_1, role_2]) }
|
16
|
+
let(:ability) { MongoidAbility::Ability.new(owner) }
|
17
|
+
|
18
|
+
before do
|
19
|
+
my_subject.save!
|
20
|
+
my_subject1.save!
|
21
|
+
my_subject11.save!
|
22
|
+
my_subject2.save!
|
23
|
+
my_subject21.save!
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'subject type locks' do
|
27
|
+
describe 'default open locks' do
|
28
|
+
before { MySubject.default_lock MyLock, :read, true }
|
29
|
+
|
30
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject }
|
31
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
32
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject2 }
|
33
|
+
|
34
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject }
|
35
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
36
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include my_subject2 }
|
37
|
+
|
38
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject }
|
39
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
40
|
+
it { MySubject2.accessible_by(ability, :read).to_a.must_include my_subject2 }
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'default closed locks' do
|
44
|
+
before { MySubject.default_lock MyLock, :read, false }
|
45
|
+
|
46
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject }
|
47
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
48
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
49
|
+
|
50
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject }
|
51
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
52
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
53
|
+
|
54
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject }
|
55
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
56
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
57
|
+
|
58
|
+
it { MySubject.accessible_by(ability, :read).selector.must_equal({}) }
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'default combined locks' do
|
62
|
+
before(:all) do
|
63
|
+
MySubject.default_lock MyLock, :read, false
|
64
|
+
MySubject1.default_lock MyLock, :read, true
|
65
|
+
MySubject2.default_lock MyLock, :read, false
|
66
|
+
end
|
67
|
+
|
68
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject }
|
69
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
70
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
71
|
+
|
72
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
73
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
74
|
+
|
75
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'combined locks' do
|
79
|
+
before(:all) do
|
80
|
+
MySubject.default_lock MyLock, :read, true
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:lock) { MyLock.new(subject_type: MySubject1, action: :read, outcome: false) }
|
84
|
+
let(:role) { MyRole.new(my_locks: [lock]) }
|
85
|
+
let(:owner) { MyOwner.new(my_roles: [role]) }
|
86
|
+
|
87
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject }
|
88
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
89
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
90
|
+
|
91
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject1 }
|
92
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
93
|
+
|
94
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'conditions locks' do
|
99
|
+
describe 'subject locks' do
|
100
|
+
describe 'closed id locks' do
|
101
|
+
let(:lock) { MyLock.new(subject: my_subject, action: :read, outcome: false) }
|
102
|
+
let(:role_1) { MyRole.new(my_locks: [lock]) }
|
103
|
+
|
104
|
+
before(:all) do
|
105
|
+
MySubject.default_lock MyLock, :read, true
|
106
|
+
end
|
107
|
+
|
108
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject }
|
109
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
110
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include my_subject2 }
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'open id locks' do
|
114
|
+
let(:lock) { MyLock.new(subject: my_subject1, action: :read, outcome: true) }
|
115
|
+
let(:role_1) { MyRole.new(my_locks: [lock]) }
|
116
|
+
|
117
|
+
before(:all) do
|
118
|
+
MySubject.default_lock MyLock, :read, false
|
119
|
+
end
|
120
|
+
|
121
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include my_subject }
|
122
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include my_subject1 }
|
123
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include my_subject2 }
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'closed types & open ids' do
|
127
|
+
let(:lock_1) { MyLock.new(subject_type: MySubject, action: :read, outcome: false) }
|
128
|
+
let(:lock_2) { MyLock.new(subject: my_subject, action: :read, outcome: true) }
|
129
|
+
let(:lock_3) { MyLock.new(subject: my_subject1, action: :read, outcome: true) }
|
130
|
+
|
131
|
+
let(:owner) { MyOwner.new(my_locks: [lock_1, lock_2, lock_3]) }
|
132
|
+
|
133
|
+
it { MySubject.accessible_by(ability, :read).must_include my_subject }
|
134
|
+
it { MySubject.accessible_by(ability, :read).must_include my_subject1 }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'arbitrary conditions' do
|
139
|
+
describe 'positive' do
|
140
|
+
let(:my_subject1) { MySubject1.new(override: true) }
|
141
|
+
|
142
|
+
before(:all) { MySubject.default_lock MyLock, :read, true, override: true }
|
143
|
+
|
144
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include(my_subject) }
|
145
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include(my_subject1) }
|
146
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include(my_subject2) }
|
147
|
+
|
148
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include(my_subject) }
|
149
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include(my_subject1) }
|
150
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include(my_subject2) }
|
151
|
+
|
152
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include(my_subject) }
|
153
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include(my_subject1) }
|
154
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include(my_subject2) }
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'negative' do
|
158
|
+
let(:my_subject1) { MySubject1.new(override: true) }
|
159
|
+
|
160
|
+
before(:all) do
|
161
|
+
MySubject.default_lock MyLock, :read, true
|
162
|
+
MySubject.default_lock MyLock, :read, false, override: true
|
163
|
+
end
|
164
|
+
|
165
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include(my_subject) }
|
166
|
+
it { MySubject.accessible_by(ability, :read).to_a.wont_include(my_subject1) }
|
167
|
+
it { MySubject.accessible_by(ability, :read).to_a.must_include(my_subject2) }
|
168
|
+
|
169
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include(my_subject) }
|
170
|
+
it { MySubject1.accessible_by(ability, :read).to_a.wont_include(my_subject1) }
|
171
|
+
it { MySubject1.accessible_by(ability, :read).to_a.must_include(my_subject2) }
|
172
|
+
|
173
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include(my_subject) }
|
174
|
+
it { MySubject2.accessible_by(ability, :read).to_a.wont_include(my_subject1) }
|
175
|
+
it { MySubject2.accessible_by(ability, :read).to_a.must_include(my_subject2) }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe 'prefix' do
|
181
|
+
let(:lock_1) { MyLock.new(subject: my_subject1, action: :read, outcome: true) }
|
182
|
+
let(:lock_2) { MyLock.new(subject: my_subject2, action: :read, outcome: false) }
|
183
|
+
let(:role_1) { MyRole.new(my_locks: [lock_1, lock_2]) }
|
184
|
+
|
185
|
+
let(:prefix) { :subject }
|
186
|
+
let(:selector) { MySubject.accessible_by(ability, :read, prefix: prefix).selector }
|
187
|
+
|
188
|
+
before(:all) do
|
189
|
+
MySubject.default_lock MyLock, :read, true
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'allows to pass prefix' do
|
193
|
+
selector.must_equal(
|
194
|
+
'$and' => [
|
195
|
+
{ '$or' => [
|
196
|
+
{ 'subject_type' => { '$nin' => [] } },
|
197
|
+
{ 'subject_id' => my_subject1.id }
|
198
|
+
] },
|
199
|
+
{ 'subject_id' => { '$ne' => my_subject2.id } }
|
200
|
+
]
|
201
|
+
)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|