mongoid_ability 0.0.6 → 0.0.7
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/lib/mongoid_ability.rb +2 -1
- data/lib/mongoid_ability/accessible_query_builder.rb +113 -0
- data/lib/mongoid_ability/owner.rb +3 -1
- data/lib/mongoid_ability/subject.rb +4 -53
- data/lib/mongoid_ability/version.rb +1 -1
- data/test/mongoid_ability/accessible_query_builder_test.rb +20 -0
- data/test/mongoid_ability/subject_test.rb +0 -4
- data/test/support/test_classes.rb +86 -0
- data/test/test_helper.rb +2 -91
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fbc4ee9c2161070d8e1a2f9d79f53318239fe61
|
4
|
+
data.tar.gz: 5356a70dcb98c19d3744dea5eea22c28b6d4b2a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1bab6862b3fb65d502b87982a4181eb6a2054ac78d7dced498732a7aaf194ed843f35ff0df8c13f10580ae1523dfeb760dc4c643c024d8b99b0ccd351f6592f
|
7
|
+
data.tar.gz: f3fc6a6559d09bc54233b60b5efabfd037ec68a32af7b2416e65bb295d981c6c53715e0a85dd15f0efe78f12bdef9fa659dc937be0423a94f253c30d20737f19
|
data/lib/mongoid_ability.rb
CHANGED
@@ -2,6 +2,7 @@ require "mongoid_ability/version"
|
|
2
2
|
|
3
3
|
require "mongoid_ability/ability"
|
4
4
|
require "mongoid_ability/ability_resolver"
|
5
|
+
require "mongoid_ability/accessible_query_builder"
|
5
6
|
require "mongoid_ability/lock"
|
6
7
|
require "mongoid_ability/owner"
|
7
8
|
require "mongoid_ability/subject"
|
@@ -14,4 +15,4 @@ if defined?(Rails)
|
|
14
15
|
@current_ability ||= MongoidAbility::Ability.new(current_user)
|
15
16
|
end
|
16
17
|
end
|
17
|
-
end
|
18
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module MongoidAbility
|
2
|
+
class AccessibleQueryBuilder < Struct.new(:base_class, :ability, :action)
|
3
|
+
|
4
|
+
def self.call *args
|
5
|
+
new(*args).call
|
6
|
+
end
|
7
|
+
|
8
|
+
# =====================================================================
|
9
|
+
|
10
|
+
def call
|
11
|
+
criteria = base_criteria
|
12
|
+
|
13
|
+
base_class_and_descendants.each do |cls|
|
14
|
+
criteria = criteria.merge(criteria_for_class(cls))
|
15
|
+
end
|
16
|
+
|
17
|
+
criteria
|
18
|
+
end
|
19
|
+
|
20
|
+
private # =============================================================
|
21
|
+
|
22
|
+
def base_criteria
|
23
|
+
@base_criteria ||= base_class.criteria
|
24
|
+
end
|
25
|
+
|
26
|
+
# ---------------------------------------------------------------------
|
27
|
+
|
28
|
+
def base_class_superclass
|
29
|
+
@base_class_superclass ||= (base_class.ancestors_with_default_locks.last || base_class)
|
30
|
+
end
|
31
|
+
|
32
|
+
def base_class_descendants
|
33
|
+
@base_class_descendants ||= ObjectSpace.each_object(Class).select{ |cls| cls < base_class_superclass }
|
34
|
+
end
|
35
|
+
|
36
|
+
def base_class_and_descendants
|
37
|
+
[base_class].concat(base_class_descendants)
|
38
|
+
end
|
39
|
+
|
40
|
+
def hereditary?
|
41
|
+
base_class_and_descendants.count > 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# ---------------------------------------------------------------------
|
45
|
+
|
46
|
+
def user
|
47
|
+
ability.user
|
48
|
+
end
|
49
|
+
|
50
|
+
def roles
|
51
|
+
user.roles_relation
|
52
|
+
end
|
53
|
+
|
54
|
+
# ---------------------------------------------------------------------
|
55
|
+
|
56
|
+
def user_id_locks_for_subject_type cls
|
57
|
+
user.locks_relation.id_locks.for_action(action).for_subject_type(cls.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def roles_ids_locks_for_subject_type cls
|
61
|
+
roles.collect { |role| role.locks_relation.id_locks.for_action(action).for_subject_type(cls.to_s) }.flatten
|
62
|
+
end
|
63
|
+
|
64
|
+
# ---------------------------------------------------------------------
|
65
|
+
|
66
|
+
def role_has_open_id_lock? cls, subject_id
|
67
|
+
roles_ids_locks_for_subject_type(cls).
|
68
|
+
select(&:open?).
|
69
|
+
map(&:subject_id).
|
70
|
+
include?(subject_id)
|
71
|
+
end
|
72
|
+
|
73
|
+
def user_has_open_id_lock? cls, subject_id
|
74
|
+
user_id_locks_for_subject_type(cls).
|
75
|
+
select(&:open?).
|
76
|
+
map(&:subject_id).
|
77
|
+
include?(subject_id)
|
78
|
+
end
|
79
|
+
|
80
|
+
# ---------------------------------------------------------------------
|
81
|
+
|
82
|
+
def criteria_for_class cls
|
83
|
+
ability.can?(action, cls) ? exclude_criteria(cls) : include_criteria(cls)
|
84
|
+
end
|
85
|
+
|
86
|
+
def exclude_criteria cls
|
87
|
+
id_locks = roles_ids_locks_for_subject_type(cls).select(&:closed?)
|
88
|
+
id_locks = id_locks.reject{ |lock| role_has_open_id_lock?(cls, lock.subject_id) }
|
89
|
+
id_locks = id_locks.reject{ |lock| user_has_open_id_lock?(cls, lock.subject_id) }
|
90
|
+
id_locks += user_id_locks_for_subject_type(cls).select(&:closed?)
|
91
|
+
|
92
|
+
excluded_ids = id_locks.map(&:subject_id).flatten
|
93
|
+
|
94
|
+
conditions = { :_id.nin => excluded_ids }
|
95
|
+
conditions = conditions.merge(_type: cls.to_s) if hereditary?
|
96
|
+
|
97
|
+
base_criteria.or(conditions)
|
98
|
+
end
|
99
|
+
|
100
|
+
def include_criteria cls
|
101
|
+
id_locks = roles_ids_locks_for_subject_type(cls).select(&:open?)
|
102
|
+
id_locks += user_id_locks_for_subject_type(cls).select(&:open?)
|
103
|
+
|
104
|
+
included_ids = id_locks.map(&:subject_id).flatten
|
105
|
+
|
106
|
+
conditions = { :_id.in => included_ids }
|
107
|
+
conditions = conditions.merge(_type: cls.to_s) if hereditary?
|
108
|
+
|
109
|
+
base_criteria.or(conditions)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -27,7 +27,9 @@ module MongoidAbility
|
|
27
27
|
# override if needed
|
28
28
|
# return for example 'MyLock'
|
29
29
|
def lock_class_name
|
30
|
-
|
30
|
+
lock_classes = ObjectSpace.each_object(Class).select{ |cls| cls < MongoidAbility::Lock }
|
31
|
+
lock_superclasses = lock_classes.reject{ |cls| lock_classes.any?{ |c| cls < c } }
|
32
|
+
@lock_class_name ||= lock_superclasses.first.name
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -27,7 +27,9 @@ module MongoidAbility
|
|
27
27
|
# override if needed
|
28
28
|
# return for example 'MyLock'
|
29
29
|
def lock_class_name
|
30
|
-
|
30
|
+
lock_classes = ObjectSpace.each_object(Class).select{ |cls| cls < MongoidAbility::Lock }
|
31
|
+
lock_superclasses = lock_classes.reject{ |cls| lock_classes.any?{ |c| cls < c } }
|
32
|
+
@lock_class_name ||= lock_superclasses.first.name
|
31
33
|
end
|
32
34
|
|
33
35
|
# ---------------------------------------------------------------------
|
@@ -42,59 +44,8 @@ module MongoidAbility
|
|
42
44
|
|
43
45
|
# ---------------------------------------------------------------------
|
44
46
|
|
45
|
-
# TODO: obviously this could be cleaner
|
46
47
|
def accessible_by ability, action=:read
|
47
|
-
|
48
|
-
|
49
|
-
return cr unless ability.user.present?
|
50
|
-
|
51
|
-
supercls = self.ancestors_with_default_locks.last || self
|
52
|
-
subject_classes = [supercls].concat(supercls.descendants)
|
53
|
-
|
54
|
-
subject_classes.each do |cls|
|
55
|
-
|
56
|
-
roles_id_locks = ability.user.roles_relation.collect{ |role| role.locks_relation.for_subject_type(cls.to_s).id_locks.for_action(action) }.flatten
|
57
|
-
user_id_locks = ability.user.locks_relation.for_subject_type(cls.to_s).id_locks.for_action(action)
|
58
|
-
|
59
|
-
closed_roles_id_locks = roles_id_locks.to_a.select(&:closed?)
|
60
|
-
open_roles_id_locks = roles_id_locks.to_a.select(&:open?)
|
61
|
-
|
62
|
-
closed_user_id_locks = user_id_locks.to_a.select(&:closed?)
|
63
|
-
open_user_id_locks = user_id_locks.to_a.select(&:open?)
|
64
|
-
|
65
|
-
if ability.can?(action, cls)
|
66
|
-
excluded_ids = []
|
67
|
-
|
68
|
-
id_locks = closed_roles_id_locks.
|
69
|
-
reject{ |cl| open_roles_id_locks.any?{ |ol| ol.subject_id == cl.subject_id } }.
|
70
|
-
reject{ |cl| open_user_id_locks.any?{ |ol| ol.subject_id == cl.subject_id } }
|
71
|
-
|
72
|
-
id_locks += closed_user_id_locks
|
73
|
-
|
74
|
-
excluded_ids << id_locks.map(&:subject_id)
|
75
|
-
|
76
|
-
if subject_classes.count == 1
|
77
|
-
cr = cr.or(:_id.nin => excluded_ids.flatten)
|
78
|
-
else
|
79
|
-
cr = cr.or(_type: cls.to_s, :_id.nin => excluded_ids.flatten)
|
80
|
-
end
|
81
|
-
else
|
82
|
-
included_ids = []
|
83
|
-
|
84
|
-
id_locks = open_roles_id_locks
|
85
|
-
id_locks += open_user_id_locks
|
86
|
-
|
87
|
-
included_ids << id_locks.map(&:subject_id)
|
88
|
-
|
89
|
-
if subject_classes.count == 1
|
90
|
-
cr = cr.or(:_id.in => included_ids.flatten)
|
91
|
-
else
|
92
|
-
cr = cr.or(_type: cls.to_s, :_id.in => included_ids.flatten)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
cr
|
48
|
+
AccessibleQueryBuilder.call(self, ability, action)
|
98
49
|
end
|
99
50
|
end
|
100
51
|
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
module MongoidAbility
|
4
|
+
describe AccessibleQueryBuilder do
|
5
|
+
|
6
|
+
let(:base_class) { SubjectTest }
|
7
|
+
|
8
|
+
let(:user) { TestUser.new }
|
9
|
+
let(:ability) { Ability.new(user) }
|
10
|
+
|
11
|
+
let(:action) { :read }
|
12
|
+
|
13
|
+
subject { AccessibleQueryBuilder.call(base_class, ability, action) }
|
14
|
+
|
15
|
+
it 'returns Mongoid::Criteria' do
|
16
|
+
subject.must_be_kind_of Mongoid::Criteria
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -71,10 +71,6 @@ module MongoidAbility
|
|
71
71
|
subject_single_test
|
72
72
|
end
|
73
73
|
|
74
|
-
it 'returns Mongoid::Criteria' do
|
75
|
-
subject.class.accessible_by(ability).must_be_kind_of Mongoid::Criteria
|
76
|
-
end
|
77
|
-
|
78
74
|
# ---------------------------------------------------------------------
|
79
75
|
|
80
76
|
describe 'default locks' do
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class TestLock
|
2
|
+
include Mongoid::Document
|
3
|
+
include MongoidAbility::Lock
|
4
|
+
|
5
|
+
embedded_in :owner, polymorphic: true
|
6
|
+
end
|
7
|
+
|
8
|
+
class TestLockSub < TestLock
|
9
|
+
end
|
10
|
+
|
11
|
+
# ---------------------------------------------------------------------
|
12
|
+
|
13
|
+
class TestOwnerSuper
|
14
|
+
include Mongoid::Document
|
15
|
+
include MongoidAbility::Owner
|
16
|
+
|
17
|
+
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
18
|
+
end
|
19
|
+
|
20
|
+
class TestOwner < TestOwnerSuper
|
21
|
+
end
|
22
|
+
|
23
|
+
# ---------------------------------------------------------------------
|
24
|
+
|
25
|
+
class SubjectTest
|
26
|
+
include Mongoid::Document
|
27
|
+
include MongoidAbility::Subject
|
28
|
+
|
29
|
+
default_lock :read, true
|
30
|
+
end
|
31
|
+
|
32
|
+
class SubjectTestOne < SubjectTest
|
33
|
+
end
|
34
|
+
|
35
|
+
class SubjectTestTwo < SubjectTest
|
36
|
+
end
|
37
|
+
|
38
|
+
class SubjectSingleTest
|
39
|
+
include Mongoid::Document
|
40
|
+
include MongoidAbility::Subject
|
41
|
+
|
42
|
+
default_lock :read, true
|
43
|
+
end
|
44
|
+
|
45
|
+
# ---------------------------------------------------------------------
|
46
|
+
|
47
|
+
class TestAbilityResolverSubject
|
48
|
+
include Mongoid::Document
|
49
|
+
include MongoidAbility::Subject
|
50
|
+
|
51
|
+
default_lock :read, true
|
52
|
+
end
|
53
|
+
|
54
|
+
class TestAbilitySubjectSuper2
|
55
|
+
include Mongoid::Document
|
56
|
+
include MongoidAbility::Subject
|
57
|
+
|
58
|
+
default_lock :read, false
|
59
|
+
default_lock :update, true
|
60
|
+
end
|
61
|
+
|
62
|
+
class TestAbilitySubjectSuper1 < TestAbilitySubjectSuper2
|
63
|
+
end
|
64
|
+
|
65
|
+
class TestAbilitySubject < TestAbilitySubjectSuper1
|
66
|
+
end
|
67
|
+
|
68
|
+
# ---------------------------------------------------------------------
|
69
|
+
|
70
|
+
class TestRole
|
71
|
+
include Mongoid::Document
|
72
|
+
include MongoidAbility::Owner
|
73
|
+
|
74
|
+
field :name, type: String
|
75
|
+
|
76
|
+
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
77
|
+
has_and_belongs_to_many :users, class_name: 'TestUser'
|
78
|
+
end
|
79
|
+
|
80
|
+
class TestUser
|
81
|
+
include Mongoid::Document
|
82
|
+
include MongoidAbility::Owner
|
83
|
+
|
84
|
+
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
85
|
+
has_and_belongs_to_many :roles, class_name: 'TestRole'
|
86
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -7,8 +7,8 @@ require 'mongoid'
|
|
7
7
|
|
8
8
|
require 'mongoid_ability'
|
9
9
|
|
10
|
-
#
|
11
|
-
|
10
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
11
|
+
|
12
12
|
if ENV["CI"]
|
13
13
|
require "coveralls"
|
14
14
|
Coveralls.wear!
|
@@ -43,93 +43,4 @@ DatabaseCleaner.strategy = :truncation
|
|
43
43
|
class MiniTest::Spec
|
44
44
|
before(:each) { DatabaseCleaner.start }
|
45
45
|
after(:each) { DatabaseCleaner.clean }
|
46
|
-
end
|
47
|
-
|
48
|
-
# =====================================================================
|
49
|
-
|
50
|
-
class TestLock
|
51
|
-
include Mongoid::Document
|
52
|
-
include MongoidAbility::Lock
|
53
|
-
|
54
|
-
embedded_in :owner, polymorphic: true
|
55
|
-
end
|
56
|
-
|
57
|
-
class TestLockSub < TestLock
|
58
|
-
end
|
59
|
-
|
60
|
-
# ---------------------------------------------------------------------
|
61
|
-
|
62
|
-
class TestOwnerSuper
|
63
|
-
include Mongoid::Document
|
64
|
-
include MongoidAbility::Owner
|
65
|
-
|
66
|
-
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
67
|
-
end
|
68
|
-
|
69
|
-
class TestOwner < TestOwnerSuper
|
70
|
-
end
|
71
|
-
|
72
|
-
# ---------------------------------------------------------------------
|
73
|
-
|
74
|
-
class SubjectTest
|
75
|
-
include Mongoid::Document
|
76
|
-
include MongoidAbility::Subject
|
77
|
-
|
78
|
-
default_lock :read, true
|
79
|
-
end
|
80
|
-
|
81
|
-
class SubjectTestOne < SubjectTest
|
82
|
-
end
|
83
|
-
|
84
|
-
class SubjectTestTwo < SubjectTest
|
85
|
-
end
|
86
|
-
|
87
|
-
class SubjectSingleTest
|
88
|
-
include Mongoid::Document
|
89
|
-
include MongoidAbility::Subject
|
90
|
-
|
91
|
-
default_lock :read, true
|
92
|
-
end
|
93
|
-
|
94
|
-
# ---------------------------------------------------------------------
|
95
|
-
|
96
|
-
class TestAbilityResolverSubject
|
97
|
-
include Mongoid::Document
|
98
|
-
include MongoidAbility::Subject
|
99
|
-
|
100
|
-
default_lock :read, true
|
101
|
-
end
|
102
|
-
|
103
|
-
class TestAbilitySubjectSuper2
|
104
|
-
include Mongoid::Document
|
105
|
-
include MongoidAbility::Subject
|
106
|
-
|
107
|
-
default_lock :read, false
|
108
|
-
default_lock :update, true
|
109
|
-
end
|
110
|
-
|
111
|
-
class TestAbilitySubjectSuper1 < TestAbilitySubjectSuper2
|
112
|
-
end
|
113
|
-
|
114
|
-
class TestAbilitySubject < TestAbilitySubjectSuper1
|
115
|
-
end
|
116
|
-
|
117
|
-
# ---------------------------------------------------------------------
|
118
|
-
|
119
|
-
class TestRole
|
120
|
-
include Mongoid::Document
|
121
|
-
include MongoidAbility::Owner
|
122
|
-
|
123
|
-
field :name, type: String
|
124
|
-
|
125
|
-
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
126
|
-
has_and_belongs_to_many :users, class_name: 'TestUser'
|
127
|
-
end
|
128
|
-
|
129
|
-
class TestUser
|
130
|
-
include Mongoid::Document
|
131
|
-
include MongoidAbility::Owner
|
132
|
-
|
133
|
-
embeds_many :test_locks, class_name: 'TestLock', as: :owner
|
134
|
-
has_and_belongs_to_many :roles, class_name: 'TestRole'
|
135
46
|
end
|
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: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tomas Celizna
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cancancan
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- lib/mongoid_ability.rb
|
156
156
|
- lib/mongoid_ability/ability.rb
|
157
157
|
- lib/mongoid_ability/ability_resolver.rb
|
158
|
+
- lib/mongoid_ability/accessible_query_builder.rb
|
158
159
|
- lib/mongoid_ability/lock.rb
|
159
160
|
- lib/mongoid_ability/owner.rb
|
160
161
|
- lib/mongoid_ability/subject.rb
|
@@ -162,9 +163,11 @@ files:
|
|
162
163
|
- mongoid_ability.gemspec
|
163
164
|
- test/mongoid_ability/ability_resolver_test.rb
|
164
165
|
- test/mongoid_ability/ability_test.rb
|
166
|
+
- test/mongoid_ability/accessible_query_builder_test.rb
|
165
167
|
- test/mongoid_ability/lock_test.rb
|
166
168
|
- test/mongoid_ability/owner_test.rb
|
167
169
|
- test/mongoid_ability/subject_test.rb
|
170
|
+
- test/support/test_classes.rb
|
168
171
|
- test/test_helper.rb
|
169
172
|
homepage: https://github.com/tomasc/mongoid_ability
|
170
173
|
licenses:
|
@@ -194,7 +197,9 @@ summary: Custom Ability class that allows CanCanCan authorization library store
|
|
194
197
|
test_files:
|
195
198
|
- test/mongoid_ability/ability_resolver_test.rb
|
196
199
|
- test/mongoid_ability/ability_test.rb
|
200
|
+
- test/mongoid_ability/accessible_query_builder_test.rb
|
197
201
|
- test/mongoid_ability/lock_test.rb
|
198
202
|
- test/mongoid_ability/owner_test.rb
|
199
203
|
- test/mongoid_ability/subject_test.rb
|
204
|
+
- test/support/test_classes.rb
|
200
205
|
- test/test_helper.rb
|