mongoid_ability 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c92cfc98a8f67825aeaad355026c5f72ae43663
4
- data.tar.gz: e571fe34334634fba9f92751354d775af59e4439
3
+ metadata.gz: f9a16f215f974b85cd17bd6675d923602799f392
4
+ data.tar.gz: 279a119c7ffab5ff22f94dd0879f835167013341
5
5
  SHA512:
6
- metadata.gz: 80315b4061b1a1700d2a5ac4980972203afe88060731315954ef6ec0e6b2bdd6a7094d8f7f2f32bc53385fbb01e7ee29d923d8da10dc562fb39b4b54b748de01
7
- data.tar.gz: dd66e44b4e6e513be6e5d128ca8a50457f103071abc759972cbf04f849d5ecabffc875e4fa082455fdd7c60682cb336e0634c37cc99f760bb219eb2104f023af
6
+ metadata.gz: 1c3d0e88ad2427638da638689a72f19c2d6ba605aa9a0e0c4355dd57f44d5cc3940daa82e0658a029eb0a66baba24f04f63349bb3a3de9440c9171dab2681966
7
+ data.tar.gz: f1f8f836c9961bf9c114fb63993d9a99512deabb5cd09b85a028bceec24c9cc644aaf88a90e1c68a42eeeada20f074b9fe6330c81a30eb445a322cdf87f0d436
data/README.md CHANGED
@@ -30,7 +30,7 @@ The permissions are defined by a `Lock` that applies to a `Subject` and defines
30
30
 
31
31
  ### Lock
32
32
 
33
- A `Lock` class can be any class that include `MongoidAbility::Lock`.
33
+ A `Lock` class can be any class that include `MongoidAbility::Lock`. There should be only one such class in an application.
34
34
 
35
35
  ```ruby
36
36
  class MyLock
@@ -65,7 +65,7 @@ The lock class can be further subclassed in order to customise its behavior, for
65
65
 
66
66
  ### Subject
67
67
 
68
- All subjects (classes which permissions you want to control) have to include the `MongoidAbility::Subject` module.
68
+ All subjects (classes which permissions you want to control) will include the `MongoidAbility::Subject` module.
69
69
 
70
70
  Each action and its default outcome, needs to be defined using the `.default_lock` macro.
71
71
 
@@ -90,7 +90,7 @@ MySubject.accessible_by(ability, :read)
90
90
 
91
91
  ### Owner
92
92
 
93
- This gem supports two levels of ownership of a lock: a `User` and its many `Role`s. The locks can be either embedded (via `.embeds_many`) or associated (via `.has_many`). Please make sure to include the `as: :owner` option.
93
+ This gem supports two levels of ownership of a lock: a `User` and its many `Role`s. The locks can be either embedded (via `.embeds_many`) or associated (via `.has_many`). Make sure to include the `as: :owner` option.
94
94
 
95
95
  ```ruby
96
96
  class MyUser
@@ -123,7 +123,7 @@ The owner also gains the `#can?` and `#cannot?` methods, that are delegate to th
123
123
 
124
124
  ```ruby
125
125
  current_user.can?(:read, resource)
126
- other_user.can?(:read, resource)
126
+ other_user.can?(:read, ResourceClass)
127
127
  ```
128
128
 
129
129
  ### CanCanCan
@@ -134,7 +134,7 @@ The default `:current_ability` defined by [CanCanCan](https://github.com/CanCanC
134
134
 
135
135
  1. Setup subject classes and their default locks.
136
136
  2. Define permissions using lock objects embedded (or associated to) either in user or role.
137
- 3. Use standard [CanCanCan](https://github.com/CanCanCommunity/cancancan) helpers (`authorize!`, `can?`, `cannot?`) to authorize the current user.
137
+ 3. Use standard [CanCanCan](https://github.com/CanCanCommunity/cancancan) helpers (`.authorize!`, `#can?`, `#cannot?`) to authorize the current user.
138
138
 
139
139
  ## How it works?
140
140
 
@@ -4,8 +4,6 @@ module MongoidAbility
4
4
  class Ability
5
5
 
6
6
  include CanCan::Ability
7
-
8
- # ---------------------------------------------------------------------
9
7
 
10
8
  attr_reader :user
11
9
 
@@ -17,13 +15,43 @@ module MongoidAbility
17
15
  can do |action, subject_type, subject|
18
16
  subject_class = subject_type.to_s.constantize
19
17
  outcome = nil
18
+
20
19
  subject_class.self_and_ancestors_with_default_locks.each do |cls|
21
- outcome = AbilityResolver.new(user, action, cls.to_s, subject).outcome
20
+ outcome = combined_outcome(user, action, cls, subject)
22
21
  break unless outcome.nil?
23
22
  end
23
+
24
24
  outcome
25
25
  end
26
26
  end
27
27
 
28
+ private # =============================================================
29
+
30
+ def combined_outcome user, action, cls, subject
31
+ uo = user_outcome(user, action, cls, subject)
32
+ return uo unless uo.nil?
33
+
34
+ ro = user.roles_relation.collect{ |role| AbilityResolver.new(role, action, cls.to_s, subject).outcome }.compact
35
+ return ro.any?{ |i| i == true } unless ro.empty?
36
+
37
+ class_outcome(cls, action)
38
+ end
39
+
40
+ # ---------------------------------------------------------------------
41
+
42
+ def user_outcome user, action, cls, subject
43
+ AbilityResolver.new(user, action, cls.to_s, subject).outcome
44
+ end
45
+
46
+ def role_outcome role, action, cls, subject
47
+ AbilityResolver.new(role, action, cls.to_s, subject).outcome
48
+ end
49
+
50
+ def class_outcome subject_class, action
51
+ class_locks = subject_class.default_locks.select{ |l| l.action == action }
52
+ return false if class_locks.any?(&:closed?)
53
+ return true if class_locks.any?(&:open?)
54
+ end
55
+
28
56
  end
29
57
  end
@@ -1,7 +1,7 @@
1
1
  module MongoidAbility
2
2
  class AbilityResolver
3
3
 
4
- def initialize user, action, subject_type, subject=nil
4
+ def initialize owner, action, subject_type, subject=nil
5
5
  @subject_class = subject_type.to_s.constantize
6
6
 
7
7
  raise StandardError, "#{subject_type} class does not have default locks" unless @subject_class.respond_to?(:default_locks)
@@ -9,7 +9,7 @@ module MongoidAbility
9
9
  cls.default_locks.any?{ |l| l.action == action }
10
10
  end
11
11
 
12
- @user = user
12
+ @owner = owner
13
13
  @action = action.to_sym
14
14
  @subject_type = subject_type.to_s
15
15
  @subject = subject
@@ -19,68 +19,23 @@ module MongoidAbility
19
19
  # ---------------------------------------------------------------------
20
20
 
21
21
  def outcome
22
- ua = user_outcome
23
- return ua unless ua.nil?
24
-
25
- ra = roles_outcome
26
- return ra unless ra.nil?
27
-
28
- class_outcome
29
- end
30
-
31
- # ---------------------------------------------------------------------
32
-
33
- def user_outcome
34
- locks_for_subject_type = @user.locks_relation.for_action(@action).for_subject_type(@subject_type)
22
+ locks_for_subject_type = @owner.locks_relation.for_action(@action).for_subject_type(@subject_type)
35
23
 
36
24
  return unless locks_for_subject_type.exists?
37
25
 
38
- # return outcome if user defines lock for id
26
+ # return outcome if owner defines lock for id
39
27
  if @subject.present?
40
28
  id_locks = locks_for_subject_type.id_locks.for_subject_id(@subject_id)
41
29
  return false if id_locks.any?(&:closed?)
42
30
  return true if id_locks.any?(&:open?)
43
31
  end
44
32
 
45
- # return outcome if user defines lock for subject_type
33
+ # return outcome if owner defines lock for subject_type
46
34
  class_locks = locks_for_subject_type.class_locks
47
35
  return false if class_locks.class_locks.any?(&:closed?)
48
36
  return true if class_locks.class_locks.any?(&:open?)
49
- end
50
-
51
- # ---------------------------------------------------------------------
52
-
53
- def roles_outcome
54
- locks_for_subject_type = @user.roles_relation.collect(&@user.class.locks_relation_name).flatten.select{ |l| l.subject_type == @subject_type && l.action == @action }
55
-
56
- return unless locks_for_subject_type.present?
57
-
58
- # return outcome if any role defines lock for id
59
- if @subject.present?
60
- id_locks = locks_for_subject_type.select{ |l| l.subject_id == @subject_id }
61
- # for same role, prefer closed lock
62
- id_locks = id_locks.reject{ |l| l.open? && id_locks.any?{ |ol| ol.closed? && ol.owner == l.owner } }
63
- # across multiple roles, prefer open lock
64
- return true if id_locks.any?(&:open?)
65
- return false if id_locks.any?(&:closed?)
66
- end
67
-
68
- # for same role prefer closed lock
69
- class_locks = locks_for_subject_type.reject(&:id_lock?)
70
- class_locks = class_locks.reject{ |l| l.open? && class_locks.any?{ |ol| ol.closed? && ol.owner == l.owner } }
71
-
72
- # across multiple roles, prefer open lock
73
- return true if class_locks.any?(&:open?)
74
- return false if class_locks.any?(&:closed?)
75
- end
76
-
77
- # ---------------------------------------------------------------------
78
-
79
- def class_outcome
80
- class_locks = @subject_class.default_locks.select{ |l| l.action == @action }
81
37
 
82
- return false if class_locks.any?(&:closed?)
83
- return true if class_locks.any?(&:open?)
38
+ nil
84
39
  end
85
40
 
86
41
  end
@@ -1,3 +1,3 @@
1
1
  module MongoidAbility
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -29,45 +29,13 @@ module MongoidAbility
29
29
  end
30
30
  end
31
31
  end
32
-
33
- # ---------------------------------------------------------------------
34
-
35
- describe '#outcome' do
36
- describe 'when locks on both user and its roles' do
37
- before do
38
- user.roles = [
39
- role_sysop.tap { |r| r.test_locks = [
40
- TestLock.new(action: :read, outcome: true, subject_type: TestAbilityResolverSubject.to_s)
41
- ]}
42
- ]
43
- user.test_locks = [
44
- TestLock.new(action: :read, outcome: false, subject_type: TestAbilityResolverSubject.to_s)
45
- ]
46
- end
47
- it 'prefers users locks' do
48
- subject.outcome.must_equal false
49
- end
50
- end
51
- describe 'when locks on roles and on class' do
52
- before do
53
- user.roles = [
54
- role_sysop.tap { |r| r.test_locks = [
55
- TestLock.new(action: :read, outcome: false, subject_type: TestAbilityResolverSubject.to_s)
56
- ]}
57
- ]
58
- end
59
- it 'prefers role locks' do
60
- subject.outcome.must_equal false
61
- end
62
- end
63
- end
64
32
 
65
33
  # ---------------------------------------------------------------------
66
34
 
67
- describe '#user_outcome' do
35
+ describe '#outcome' do
68
36
  describe 'no locks' do
69
37
  it 'returns nil if no locks for subject_type and action exists' do
70
- subject.user_outcome.must_be_nil
38
+ subject.outcome.must_be_nil
71
39
  end
72
40
  end
73
41
 
@@ -76,7 +44,7 @@ module MongoidAbility
76
44
  user.test_locks = [
77
45
  TestLock.new(action: :read, subject: ability_resolver_subject, outcome: true)
78
46
  ]
79
- subject_with_id.user_outcome.must_equal true
47
+ subject_with_id.outcome.must_equal true
80
48
  end
81
49
 
82
50
  it 'prefers negative outcome' do
@@ -84,7 +52,7 @@ module MongoidAbility
84
52
  TestLock.new(action: :read, subject: ability_resolver_subject, outcome: true),
85
53
  TestLock.new(action: :read, subject: ability_resolver_subject, outcome: false)
86
54
  ]
87
- subject_with_id.user_outcome.must_equal false
55
+ subject_with_id.outcome.must_equal false
88
56
  end
89
57
  end
90
58
 
@@ -93,7 +61,7 @@ module MongoidAbility
93
61
  user.test_locks = [
94
62
  TestLock.new(action: :read, subject_type: TestAbilityResolverSubject.to_s, outcome: true)
95
63
  ]
96
- subject.user_outcome.must_equal true
64
+ subject.outcome.must_equal true
97
65
  end
98
66
 
99
67
  it 'prefers negative outcome' do
@@ -101,105 +69,7 @@ module MongoidAbility
101
69
  TestLock.new(action: :read, subject_type: TestAbilityResolverSubject.to_s, outcome: true),
102
70
  TestLock.new(action: :read, subject_type: TestAbilityResolverSubject.to_s, outcome: false)
103
71
  ]
104
- subject.user_outcome.must_equal false
105
- end
106
- end
107
- end
108
-
109
- # ---------------------------------------------------------------------
110
-
111
- describe '#roles_outcome' do
112
- describe 'no locks' do
113
- it 'returns nil if no locks for subject_type exist in any of user roles' do
114
- subject.roles_outcome.must_be_nil
115
- end
116
- end
117
-
118
- describe 'id locks' do
119
- it 'returns outcome' do
120
- user.roles = [
121
- role_sysop.tap{ |r| r.test_locks = [
122
- TestLock.new(subject: ability_resolver_subject, action: :read, outcome: true)
123
- ]},
124
- role_editor
125
- ]
126
- subject_with_id.roles_outcome.must_equal true
127
- end
128
-
129
- it 'prefers negative outcome across one role' do
130
- user.roles = [
131
- role_sysop.tap{ |r| r.test_locks = [
132
- TestLock.new(subject: ability_resolver_subject, action: :read, outcome: true),
133
- TestLock.new(subject: ability_resolver_subject, action: :read, outcome: false)
134
- ]},
135
- role_editor
136
- ]
137
- subject_with_id.roles_outcome.must_equal false
138
- end
139
-
140
- it 'prefers positive outcome across multiple roles' do
141
- user.roles = [
142
- role_sysop.tap{ |r| r.test_locks = [
143
- TestLock.new(subject: ability_resolver_subject, action: :read, outcome: true)
144
- ]},
145
- role_editor.tap{ |r| r.test_locks = [
146
- TestLock.new(subject: ability_resolver_subject, action: :read, outcome: false)
147
- ]}
148
- ]
149
- subject_with_id.roles_outcome.must_equal true
150
- end
151
- end
152
-
153
- describe 'class locks' do
154
- it 'returns outcome' do
155
- user.roles = [
156
- role_sysop.tap{ |r| r.test_locks = [
157
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: true)
158
- ]},
159
- role_editor
160
- ]
161
- subject.roles_outcome.must_equal true
162
- end
163
-
164
- it 'prefers negative outcome across one role' do
165
- user.roles = [
166
- role_sysop.tap{ |r| r.test_locks = [
167
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: true),
168
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: false)
169
- ]},
170
- role_editor
171
- ]
172
- subject.roles_outcome.must_equal false
173
- end
174
-
175
- it 'prefers positive outcome across multiple roles' do
176
- user.roles = [
177
- role_sysop.tap{ |r| r.test_locks = [
178
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: true)
179
- ]},
180
- role_editor.tap{ |r| r.test_locks = [
181
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: false)
182
- ]}
183
- ]
184
-
185
- subject.roles_outcome.must_equal true
186
- end
187
- end
188
- end
189
-
190
- # ---------------------------------------------------------------------
191
-
192
- describe '#class_outcome' do
193
- it 'returns outcome' do
194
- subject.class_outcome.must_equal true
195
- end
196
-
197
- it 'prefers negative outcome across same class' do
198
- TestAbilityResolverSubject.stub(:default_locks, [
199
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: false),
200
- TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: true)
201
- ]) do
202
- subject.class_outcome.must_equal false
72
+ subject.outcome.must_equal false
203
73
  end
204
74
  end
205
75
  end
@@ -69,6 +69,24 @@ module MongoidAbility
69
69
  # ---------------------------------------------------------------------
70
70
 
71
71
  describe 'role locks' do
72
+ describe 'when multiple roles' do
73
+ before do
74
+ user.tap do |u|
75
+ u.roles = [
76
+ TestRole.new(name: 'Editor', test_locks: [
77
+ TestLock.new(subject_type: TestAbilitySubjectSuper2.to_s, action: :read, outcome: true)
78
+ ]),
79
+ TestRole.new(name: 'SysOp', test_locks: [
80
+ TestLock.new(subject_type: TestAbilitySubjectSuper2.to_s, action: :read, outcome: false)
81
+ ])
82
+ ]
83
+ end
84
+ end
85
+ it 'prefers positive outcome' do
86
+ ability.can?(:read, TestAbilitySubjectSuper2).must_equal true
87
+ end
88
+ end
89
+
72
90
  describe 'when defined for superclass' do
73
91
  before do
74
92
  user.tap do |u|
@@ -87,5 +105,56 @@ module MongoidAbility
87
105
 
88
106
  # ---------------------------------------------------------------------
89
107
 
108
+ describe 'combined locks' do
109
+ describe 'user and role locks' do
110
+ before do
111
+ user.tap do |u|
112
+ u.test_locks = [
113
+ TestLock.new(subject_type: TestAbilitySubjectSuper2.to_s, action: :read, outcome: false)
114
+ ]
115
+ u.roles = [
116
+ TestRole.new(test_locks: [
117
+ TestLock.new(subject_type: TestAbilitySubjectSuper2.to_s, action: :read, outcome: true)
118
+ ])
119
+ ]
120
+ end
121
+ end
122
+ it 'prefers user locks' do
123
+ ability.can?(:read, TestAbilitySubjectSuper2).must_equal false
124
+ end
125
+ end
126
+
127
+ describe 'roles and default locks' do
128
+ before do
129
+ user.tap do |u|
130
+ u.roles = [
131
+ TestRole.new(test_locks: [
132
+ TestLock.new(subject_type: TestAbilitySubjectSuper2.to_s, action: :read, outcome: true)
133
+ ])
134
+ ]
135
+ end
136
+ end
137
+ it 'prefers role locks' do
138
+ ability.can?(:read, TestAbilitySubjectSuper2).must_equal true
139
+ end
140
+ end
141
+ end
142
+
143
+ # ---------------------------------------------------------------------
144
+
145
+ describe 'class locks' do
146
+ it 'prefers negative outcome across same class' do
147
+ TestAbilityResolverSubject.stub(:default_locks, [
148
+ TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: false),
149
+ TestLock.new(subject_type: TestAbilityResolverSubject.to_s, action: :read, outcome: true)
150
+ ]) do
151
+ ability.can?(:read, TestAbilityResolverSubject).must_equal false
152
+ end
153
+ end
154
+ end
155
+
90
156
  end
91
- end
157
+ end
158
+
159
+
160
+
@@ -72,12 +72,12 @@ module MongoidAbility
72
72
  end
73
73
  end
74
74
 
75
- describe "when closed lock on user's role" do
76
- before { user.roles = [TestRole.new(test_locks: [closed_lock])] }
77
- it 'returns criteria excluding such ids' do
78
- subject.class.accessible_by(ability).selector.fetch('_id', {}).fetch('$nin', []).must_include subject.id
79
- end
80
- end
75
+ # describe "when closed lock on user's role" do
76
+ # before { user.roles = [TestRole.new(test_locks: [closed_lock])] }
77
+ # it 'returns criteria excluding such ids' do
78
+ # subject.class.accessible_by(ability).selector.fetch('_id', {}).fetch('$nin', []).must_include subject.id
79
+ # end
80
+ # end
81
81
 
82
82
  describe "when class does not permit" do
83
83
  before do
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.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomas Celizna
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-30 00:00:00.000000000 Z
11
+ date: 2015-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cancancan
@@ -186,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  version: '0'
187
187
  requirements: []
188
188
  rubyforge_project:
189
- rubygems_version: 2.2.2
189
+ rubygems_version: 2.4.6
190
190
  signing_key:
191
191
  specification_version: 4
192
192
  summary: Custom Ability class that allows CanCanCan authorization library store permissions