rabarber 4.0.2 → 4.1.1
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 +21 -0
- data/README.md +32 -1
- data/lib/rabarber/core/access.rb +3 -5
- data/lib/rabarber/core/permissions.rb +6 -3
- data/lib/rabarber/core/permissions_integrity_checker.rb +2 -2
- data/lib/rabarber/core/rule.rb +2 -3
- data/lib/rabarber/models/concerns/has_roles.rb +5 -1
- data/lib/rabarber/models/role.rb +20 -0
- data/lib/rabarber/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: feb905214f327d8aef99517ba1364252b6722430fe6ab1b4839055e1ae5b18fc
|
4
|
+
data.tar.gz: ed0cacc2d6ad0ec5fd436ba24e96c3c26489264c3f00c73822b24f4593b2fa01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f88384ea24337c233f9183fde8bda61ba2939c3947b9eff07c2b5a47ff44372ba0287258565c3a5e085c4dc80b5026206d7b126e23c58b541b6490778946123
|
7
|
+
data.tar.gz: 9d7d4243f90fdc36cf8c27c391154449b38407a42995791240b72571e2dff14fa3836e62ce43ca92d5d0c65a5835a1e14119a077eda59ebedbe4c6b2a5aa8bac
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
## v4.1.1
|
2
|
+
|
3
|
+
### Bugs:
|
4
|
+
|
5
|
+
- Fixed an issue where controller-wide `grant_access` calls would overwrite each other instead of being additive, causing inconsistent access control based on statement order
|
6
|
+
|
7
|
+
### Misc:
|
8
|
+
|
9
|
+
- Minor performance improvement for authorization checks
|
10
|
+
|
11
|
+
## v4.1.0
|
12
|
+
|
13
|
+
### Features:
|
14
|
+
|
15
|
+
- Added `Rabarber::Role.all_names` method to retrieve all roles available in the application, grouped by context
|
16
|
+
- Added `Rabarber::HasRoles#all_roles` method to retrieve all roles assigned to a user, grouped by context
|
17
|
+
|
18
|
+
### Bugs:
|
19
|
+
|
20
|
+
- Fixed potential bug in role revocation caused by checking for the presence of a role in the cache instead of the database
|
21
|
+
|
1
22
|
## v4.0.2
|
2
23
|
|
3
24
|
### Misc:
|
data/README.md
CHANGED
@@ -158,6 +158,14 @@ To get the list of roles assigned to the user, use:
|
|
158
158
|
user.roles
|
159
159
|
```
|
160
160
|
|
161
|
+
**`#all_roles`**
|
162
|
+
|
163
|
+
To get all roles assigned to the user, grouped by context, use:
|
164
|
+
|
165
|
+
```rb
|
166
|
+
user.all_roles
|
167
|
+
```
|
168
|
+
|
161
169
|
---
|
162
170
|
|
163
171
|
To manipulate roles directly, you can use `Rabarber::Role` methods:
|
@@ -203,12 +211,20 @@ Rabarber::Role.remove(:admin, force: true)
|
|
203
211
|
|
204
212
|
**`.names(context: nil)`**
|
205
213
|
|
206
|
-
If you need to list the
|
214
|
+
If you need to list the roles available in your application, use:
|
207
215
|
|
208
216
|
```rb
|
209
217
|
Rabarber::Role.names
|
210
218
|
```
|
211
219
|
|
220
|
+
**`.all_names`**
|
221
|
+
|
222
|
+
If you need list all roles available in your application, grouped by context, use:
|
223
|
+
|
224
|
+
```rb
|
225
|
+
Rabarber::Role.all_names
|
226
|
+
```
|
227
|
+
|
212
228
|
**`.assignees(role_name, context: nil)`**
|
213
229
|
|
214
230
|
To get all the users to whom the role is assigned, use:
|
@@ -307,6 +323,21 @@ end
|
|
307
323
|
```
|
308
324
|
This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
|
309
325
|
|
326
|
+
This applies as well to multiple rules defined for the same controller or action:
|
327
|
+
```rb
|
328
|
+
class Crm::OrdersController < ApplicationController
|
329
|
+
grant_access roles: :manager, context: Order
|
330
|
+
grant_access roles: :admin
|
331
|
+
|
332
|
+
grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
|
333
|
+
grant_access action: :show, roles: :accountant
|
334
|
+
def show
|
335
|
+
# ...
|
336
|
+
end
|
337
|
+
end
|
338
|
+
```
|
339
|
+
This will add rules for `manager` and `admin` roles for all actions in `Crm::OrdersController`, and for `client` and `accountant` roles for the `show` action.
|
340
|
+
|
310
341
|
## Dynamic Authorization Rules
|
311
342
|
|
312
343
|
For more complex cases, Rabarber provides dynamic rules:
|
data/lib/rabarber/core/access.rb
CHANGED
@@ -8,15 +8,13 @@ module Rabarber
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def controller_accessible?(roleable, controller_instance)
|
11
|
-
controller_rules.any? do |
|
12
|
-
controller_instance.is_a?(
|
11
|
+
controller_rules.any? do |controller, rules|
|
12
|
+
controller_instance.is_a?(controller) && rules.any? { _1.verify_access(roleable, controller_instance) }
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def action_accessible?(roleable, action, controller_instance)
|
17
|
-
action_rules[controller_instance.class].any?
|
18
|
-
rule.action == action && rule.verify_access(roleable, controller_instance)
|
19
|
-
end
|
17
|
+
action_rules[controller_instance.class][action].any? { _1.verify_access(roleable, controller_instance) }
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -13,13 +13,16 @@ module Rabarber
|
|
13
13
|
attr_reader :storage
|
14
14
|
|
15
15
|
def initialize
|
16
|
-
@storage = {
|
16
|
+
@storage = {
|
17
|
+
controller_rules: Hash.new { |h, k| h[k] = [] },
|
18
|
+
action_rules: Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = [] } }
|
19
|
+
}
|
17
20
|
end
|
18
21
|
|
19
22
|
class << self
|
20
23
|
def add(controller, action, roles, context, dynamic_rule, negated_dynamic_rule)
|
21
|
-
rule = Rabarber::Core::Rule.new(
|
22
|
-
action ? action_rules[controller] += [rule] : controller_rules[controller]
|
24
|
+
rule = Rabarber::Core::Rule.new(roles, context, dynamic_rule, negated_dynamic_rule)
|
25
|
+
action ? action_rules[controller][action] += [rule] : controller_rules[controller] += [rule]
|
23
26
|
end
|
24
27
|
|
25
28
|
def controller_rules
|
@@ -21,8 +21,8 @@ module Rabarber
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def missing_list
|
24
|
-
@missing_list ||= action_rules.each_with_object([]) do |(controller,
|
25
|
-
missing_actions =
|
24
|
+
@missing_list ||= action_rules.each_with_object([]) do |(controller, hash), arr|
|
25
|
+
missing_actions = hash.keys - controller.action_methods.map(&:to_sym)
|
26
26
|
arr << { controller => missing_actions } if missing_actions.any?
|
27
27
|
end
|
28
28
|
end
|
data/lib/rabarber/core/rule.rb
CHANGED
@@ -3,10 +3,9 @@
|
|
3
3
|
module Rabarber
|
4
4
|
module Core
|
5
5
|
class Rule
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :roles, :context, :dynamic_rule, :negated_dynamic_rule
|
7
7
|
|
8
|
-
def initialize(
|
9
|
-
@action = action
|
8
|
+
def initialize(roles, context, dynamic_rule, negated_dynamic_rule)
|
10
9
|
@roles = Array(roles)
|
11
10
|
@context = context
|
12
11
|
@dynamic_rule = dynamic_rule || -> { true }
|
@@ -19,6 +19,10 @@ module Rabarber
|
|
19
19
|
Rabarber::Core::Cache.fetch(roleable_id, context: processed_context) { rabarber_roles.names(context: processed_context) }
|
20
20
|
end
|
21
21
|
|
22
|
+
def all_roles
|
23
|
+
rabarber_roles.all_names
|
24
|
+
end
|
25
|
+
|
22
26
|
def has_role?(*role_names, context: nil)
|
23
27
|
processed_context = process_context(context)
|
24
28
|
processed_roles = process_role_names(role_names)
|
@@ -55,7 +59,7 @@ module Rabarber
|
|
55
59
|
processed_context = process_context(context)
|
56
60
|
|
57
61
|
roles_to_revoke = Rabarber::Role.where(
|
58
|
-
name: processed_role_names.intersection(
|
62
|
+
name: processed_role_names.intersection(rabarber_roles.names(context: processed_context)), **processed_context
|
59
63
|
)
|
60
64
|
|
61
65
|
if roles_to_revoke.any?
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -9,6 +9,8 @@ module Rabarber
|
|
9
9
|
format: { with: Rabarber::Input::Role::REGEX },
|
10
10
|
strict: true
|
11
11
|
|
12
|
+
belongs_to :context, polymorphic: true, optional: true
|
13
|
+
|
12
14
|
before_destroy :delete_assignments
|
13
15
|
|
14
16
|
class << self
|
@@ -16,6 +18,14 @@ module Rabarber
|
|
16
18
|
where(process_context(context)).pluck(:name).map(&:to_sym)
|
17
19
|
end
|
18
20
|
|
21
|
+
def all_names
|
22
|
+
includes(:context).group_by(&:context).transform_values { |roles| roles.map { _1.name.to_sym } }
|
23
|
+
rescue ActiveRecord::RecordNotFound => e
|
24
|
+
raise Rabarber::Error, "Context not found: #{e.model}##{e.id}"
|
25
|
+
rescue NameError => e
|
26
|
+
raise Rabarber::Error, "Context not found: #{e.name}"
|
27
|
+
end
|
28
|
+
|
19
29
|
def add(name, context: nil)
|
20
30
|
name = process_role_name(name)
|
21
31
|
processed_context = process_context(context)
|
@@ -77,6 +87,16 @@ module Rabarber
|
|
77
87
|
end
|
78
88
|
end
|
79
89
|
|
90
|
+
def context
|
91
|
+
return context_type.constantize if context_type.present? && context_id.blank?
|
92
|
+
|
93
|
+
record = super
|
94
|
+
|
95
|
+
raise ActiveRecord::RecordNotFound.new(nil, context_type, nil, context_id) if context_id.present? && !record
|
96
|
+
|
97
|
+
record
|
98
|
+
end
|
99
|
+
|
80
100
|
private
|
81
101
|
|
82
102
|
def delete_assignments
|
data/lib/rabarber/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabarber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enjaku4
|
8
8
|
- trafium
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|