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.
- 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
|