rabarber 1.2.1 → 1.3.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 +11 -0
- data/README.md +60 -10
- data/lib/rabarber/cache.rb +4 -4
- data/lib/rabarber/configuration.rb +2 -2
- data/lib/rabarber/logger.rb +11 -0
- data/lib/rabarber/missing/base.rb +2 -2
- data/lib/rabarber/missing/roles.rb +2 -2
- data/lib/rabarber/models/concerns/has_roles.rb +21 -7
- data/lib/rabarber/models/role.rb +57 -7
- data/lib/rabarber/permissions.rb +15 -13
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77a15b7eb4164c74d70e81d34630d6051915acc2abe35896df8938d9cc53e473
|
4
|
+
data.tar.gz: 847f675e00e9a85a158032af7ddc158f3189df78d4e6dae4ce15e2fe5024515e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d16835ed84fd9ee2c2e7871080c5256bad34944ee0c1a3448c609f14732f1c7901539c0287bc5206feddb79ef77ee4aa001dd26a642d7c23b05498f5a668659
|
7
|
+
data.tar.gz: 0bd35329a6608d4f6680b4aba7882d015df36f38e0b0d963c9df988edd40e490e67180e504b1901f05c957e27f8bffe585372f6494ccad2989310daaa7feec68
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
## 1.3.0
|
2
|
+
|
3
|
+
- Add methods to directly add, rename, and remove roles
|
4
|
+
- `HasRoles#assign_roles` and `HasRoles#revoke_roles` methods now return the list of roles assigned to the user
|
5
|
+
- Minor performance improvements
|
6
|
+
|
7
|
+
## 1.2.2
|
8
|
+
|
9
|
+
- Refactor to improve readability and maintainability
|
10
|
+
- Fix minor code errors
|
11
|
+
|
1
12
|
## 1.2.1
|
2
13
|
|
3
14
|
- Cache roles to avoid unnecessary database queries
|
data/README.md
CHANGED
@@ -67,16 +67,16 @@ Rabarber can be configured by using `.configure` method in an initializer:
|
|
67
67
|
|
68
68
|
```rb
|
69
69
|
Rabarber.configure do |config|
|
70
|
-
config.cache_enabled =
|
71
|
-
config.current_user_method = :
|
72
|
-
config.must_have_roles =
|
70
|
+
config.cache_enabled = true
|
71
|
+
config.current_user_method = :current_user
|
72
|
+
config.must_have_roles = false
|
73
73
|
config.when_actions_missing = -> (missing_actions, context) { ... }
|
74
74
|
config.when_roles_missing = -> (missing_roles, context) { ... }
|
75
75
|
config.when_unauthorized = -> (controller) { ... }
|
76
76
|
end
|
77
77
|
```
|
78
78
|
|
79
|
-
- `cache_enabled` must be a boolean determining whether roles are cached. Roles are cached by default to avoid unnecessary database queries. If you want to disable caching, set this option to `false`. If caching is enabled and you need to clear the cache, use
|
79
|
+
- `cache_enabled` must be a boolean determining whether roles are cached. Roles are cached by default to avoid unnecessary database queries. If you want to disable caching, set this option to `false`. If caching is enabled and you need to clear the cache, use `Rabarber::Cache.clear` method.
|
80
80
|
|
81
81
|
- `current_user_method` must be a symbol representing the method that returns the currently authenticated user. The default value is `:current_user`.
|
82
82
|
|
@@ -112,6 +112,7 @@ By default, `#assign_roles` method will automatically create any roles that don'
|
|
112
112
|
```rb
|
113
113
|
user.assign_roles(:accountant, :marketer, create_new: false)
|
114
114
|
```
|
115
|
+
The method returns an array of roles assigned to the user.
|
115
116
|
|
116
117
|
**`#revoke_roles`**
|
117
118
|
|
@@ -122,6 +123,8 @@ user.revoke_roles(:accountant, :marketer)
|
|
122
123
|
```
|
123
124
|
If any of the specified roles doesn't exist or the user doesn't have the role you want to revoke, it will be ignored.
|
124
125
|
|
126
|
+
The method returns an array of roles assigned to the user.
|
127
|
+
|
125
128
|
**`#has_role?`**
|
126
129
|
|
127
130
|
To check whether the user has a role, use:
|
@@ -140,6 +143,51 @@ To view all the roles assigned to the user, use:
|
|
140
143
|
user.roles
|
141
144
|
```
|
142
145
|
|
146
|
+
---
|
147
|
+
|
148
|
+
To manipulate roles directly, you can use `Rabarber::Role` methods:
|
149
|
+
|
150
|
+
**`.add`**
|
151
|
+
|
152
|
+
To add a new role, use:
|
153
|
+
|
154
|
+
```rb
|
155
|
+
Rabarber::Role.add(:admin)
|
156
|
+
```
|
157
|
+
|
158
|
+
This will create a new role with the specified name and return `true`. If the role already exists, it will return `false`.
|
159
|
+
|
160
|
+
**`.rename`**
|
161
|
+
|
162
|
+
To rename a role, use:
|
163
|
+
|
164
|
+
```rb
|
165
|
+
Rabarber::Role.rename(:admin, :administrator)
|
166
|
+
```
|
167
|
+
The first argument is the old name, and the second argument is the new name. This will rename the role and return `true`. If a role with the new name already exists, it will return `false`.
|
168
|
+
|
169
|
+
The method won't rename the role if it is assigned to any user. To force the rename, use the method with `force: true` argument:
|
170
|
+
```rb
|
171
|
+
Rabarber::Role.rename(:admin, :administrator, force: true)
|
172
|
+
```
|
173
|
+
|
174
|
+
**`.remove`**
|
175
|
+
|
176
|
+
To remove a role, use:
|
177
|
+
|
178
|
+
```rb
|
179
|
+
Rabarber::Role.remove(:admin)
|
180
|
+
```
|
181
|
+
|
182
|
+
This will remove the role and return `true`. If the role doesn't exist, it will return `false`.
|
183
|
+
|
184
|
+
The method won't remove the role if it is assigned to any user. To force the removal, use the method with `force: true` argument:
|
185
|
+
```rb
|
186
|
+
Rabarber::Role.remove(:admin, force: true)
|
187
|
+
```
|
188
|
+
|
189
|
+
**`.names`**
|
190
|
+
|
143
191
|
If you need to list all the role names available in your application, use:
|
144
192
|
|
145
193
|
```rb
|
@@ -218,24 +266,26 @@ end
|
|
218
266
|
|
219
267
|
This allows everyone to access `OrdersController` and its children and `index` action in `InvoicesController`. This also extends to scenarios where there is no user present, i.e. when the method responsible for returning the currently authenticated user in your application returns `nil`.
|
220
268
|
|
269
|
+
Be aware that if the user is not authenticated (the method responsible for returning the currently authenticated user in your application returns `nil`), Rabarber will treat this situation as if the user with no roles assigned was authenticated.
|
270
|
+
|
221
271
|
If you've set `must_have_roles` setting to `true`, then, only the users with at least one role can have access. This setting can be useful if your requirements are such that users without roles are not allowed to access anything.
|
222
272
|
|
223
273
|
For more complex cases, Rabarber provides dynamic rules:
|
224
274
|
|
225
275
|
```rb
|
226
276
|
class OrdersController < ApplicationController
|
227
|
-
grant_access if: :
|
228
|
-
grant_access unless: :
|
277
|
+
grant_access if: :current_company_accountant?
|
278
|
+
grant_access unless: :fired?
|
229
279
|
...
|
230
280
|
|
231
281
|
private
|
232
282
|
|
233
|
-
def
|
234
|
-
|
283
|
+
def current_company_accountant?
|
284
|
+
current_company.accountant == current_user
|
235
285
|
end
|
236
286
|
|
237
|
-
def
|
238
|
-
|
287
|
+
def fired?
|
288
|
+
current_user.fired?
|
239
289
|
end
|
240
290
|
end
|
241
291
|
|
data/lib/rabarber/cache.rb
CHANGED
@@ -10,16 +10,16 @@ module Rabarber
|
|
10
10
|
enabled? ? Rails.cache.fetch(key, options, &block) : yield
|
11
11
|
end
|
12
12
|
|
13
|
-
def delete(
|
14
|
-
Rails.cache.
|
13
|
+
def delete(*keys)
|
14
|
+
Rails.cache.delete_multi(keys) if enabled?
|
15
15
|
end
|
16
16
|
|
17
17
|
def enabled?
|
18
18
|
Rabarber::Configuration.instance.cache_enabled
|
19
19
|
end
|
20
20
|
|
21
|
-
def key_for(
|
22
|
-
"rabarber:roles_#{
|
21
|
+
def key_for(id)
|
22
|
+
"rabarber:roles_#{id}"
|
23
23
|
end
|
24
24
|
|
25
25
|
def clear
|
@@ -78,13 +78,13 @@ module Rabarber
|
|
78
78
|
-> (missing_roles, context) {
|
79
79
|
delimiter = context[:action] ? "#" : ""
|
80
80
|
message = "Missing roles: #{missing_roles}, context: #{context[:controller]}#{delimiter}#{context[:action]}"
|
81
|
-
|
81
|
+
Rabarber::Logger.log(:warn, message)
|
82
82
|
}
|
83
83
|
end
|
84
84
|
|
85
85
|
def default_when_unauthorized
|
86
86
|
-> (controller) do
|
87
|
-
|
87
|
+
Rabarber::Logger.log(:warn, "Unauthorized attempt")
|
88
88
|
if controller.request.format.html?
|
89
89
|
controller.redirect_back fallback_location: controller.main_app.root_path
|
90
90
|
else
|
@@ -41,7 +41,7 @@ module Rabarber
|
|
41
41
|
|
42
42
|
def controller_rules
|
43
43
|
if controller
|
44
|
-
|
44
|
+
Rabarber::Permissions.controller_rules.slice(controller)
|
45
45
|
else
|
46
46
|
Rabarber::Permissions.controller_rules
|
47
47
|
end
|
@@ -49,7 +49,7 @@ module Rabarber
|
|
49
49
|
|
50
50
|
def action_rules
|
51
51
|
if controller
|
52
|
-
|
52
|
+
Rabarber::Permissions.action_rules.slice(controller)
|
53
53
|
else
|
54
54
|
Rabarber::Permissions.action_rules
|
55
55
|
end
|
@@ -7,8 +7,8 @@ module Rabarber
|
|
7
7
|
|
8
8
|
def check_controller_rules
|
9
9
|
controller_rules.each do |controller, controller_rule|
|
10
|
-
missing_roles = controller_rule.roles - all_roles
|
11
|
-
missing_list << Rabarber::Missing::Item.new(missing_roles, controller, nil)
|
10
|
+
missing_roles = controller_rule.roles - all_roles
|
11
|
+
missing_list << Rabarber::Missing::Item.new(missing_roles, controller, nil) unless missing_roles.empty?
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
@@ -17,7 +17,7 @@ module Rabarber
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def roles
|
20
|
-
Rabarber::Cache.fetch(Rabarber::Cache.key_for(
|
20
|
+
Rabarber::Cache.fetch(Rabarber::Cache.key_for(roleable_id), expires_in: 1.hour, race_condition_ttl: 5.seconds) do
|
21
21
|
rabarber_roles.names
|
22
22
|
end
|
23
23
|
end
|
@@ -31,15 +31,25 @@ module Rabarber
|
|
31
31
|
|
32
32
|
create_new_roles(roles_to_assign) if create_new
|
33
33
|
|
34
|
-
|
34
|
+
new_roles = Rabarber::Role.where(name: roles_to_assign) - rabarber_roles
|
35
35
|
|
36
|
-
|
36
|
+
if new_roles.any?
|
37
|
+
delete_roleable_cache
|
38
|
+
rabarber_roles << new_roles
|
39
|
+
end
|
40
|
+
|
41
|
+
roles
|
37
42
|
end
|
38
43
|
|
39
44
|
def revoke_roles(*role_names)
|
40
|
-
|
45
|
+
new_roles = rabarber_roles - Rabarber::Role.where(name: process_role_names(role_names))
|
46
|
+
|
47
|
+
if rabarber_roles != new_roles
|
48
|
+
delete_roleable_cache
|
49
|
+
self.rabarber_roles = new_roles
|
50
|
+
end
|
41
51
|
|
42
|
-
|
52
|
+
roles
|
43
53
|
end
|
44
54
|
|
45
55
|
private
|
@@ -53,8 +63,12 @@ module Rabarber
|
|
53
63
|
Rabarber::Input::Roles.new(role_names).process
|
54
64
|
end
|
55
65
|
|
56
|
-
def
|
57
|
-
Rabarber::Cache.delete(Rabarber::Cache.key_for(
|
66
|
+
def delete_roleable_cache
|
67
|
+
Rabarber::Cache.delete(Rabarber::Cache.key_for(roleable_id))
|
68
|
+
end
|
69
|
+
|
70
|
+
def roleable_id
|
71
|
+
public_send(self.class.primary_key)
|
58
72
|
end
|
59
73
|
end
|
60
74
|
end
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -6,16 +6,66 @@ module Rabarber
|
|
6
6
|
|
7
7
|
validates :name, presence: true, uniqueness: true, format: { with: Rabarber::Input::Roles::REGEX }
|
8
8
|
|
9
|
-
|
9
|
+
has_and_belongs_to_many :roleables, join_table: "rabarber_roles_roleables"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
class << self
|
12
|
+
def names
|
13
|
+
pluck(:name).map(&:to_sym)
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(name)
|
17
|
+
name = process_role_name(name)
|
18
|
+
|
19
|
+
return false if exists?(name: name)
|
20
|
+
|
21
|
+
delete_roles_cache
|
22
|
+
|
23
|
+
!!create!(name: name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def rename(old_name, new_name, force: false)
|
27
|
+
role = find_by(name: process_role_name(old_name))
|
28
|
+
name = process_role_name(new_name)
|
29
|
+
|
30
|
+
return false if !role || exists?(name: name) || assigned_to_roleables(role).any? && !force
|
31
|
+
|
32
|
+
delete_roles_cache
|
33
|
+
delete_roleables_cache(role)
|
34
|
+
|
35
|
+
role.update!(name: name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove(name, force: false)
|
39
|
+
role = find_by(name: process_role_name(name))
|
40
|
+
|
41
|
+
return false if !role || assigned_to_roleables(role).any? && !force
|
42
|
+
|
43
|
+
delete_roles_cache
|
44
|
+
delete_roleables_cache(role)
|
45
|
+
|
46
|
+
!!role.destroy!
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def delete_roles_cache
|
52
|
+
Rabarber::Cache.delete(Rabarber::Cache::ALL_ROLES_KEY)
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete_roleables_cache(role)
|
56
|
+
keys = assigned_to_roleables(role).map { |roleable_id| Rabarber::Cache.key_for(roleable_id) }
|
57
|
+
Rabarber::Cache.delete(*keys) if keys.any?
|
58
|
+
end
|
14
59
|
|
15
|
-
|
60
|
+
def assigned_to_roleables(role)
|
61
|
+
ActiveRecord::Base.connection.select_values(
|
62
|
+
"SELECT roleable_id FROM rabarber_roles_roleables WHERE role_id = #{role.id}"
|
63
|
+
)
|
64
|
+
end
|
16
65
|
|
17
|
-
|
18
|
-
|
66
|
+
def process_role_name(name)
|
67
|
+
Rabarber::Input::Roles.new(name).process[0]
|
68
|
+
end
|
19
69
|
end
|
20
70
|
end
|
21
71
|
end
|
data/lib/rabarber/permissions.rb
CHANGED
@@ -15,22 +15,24 @@ module Rabarber
|
|
15
15
|
@storage = { controller_rules: Hash.new({}), action_rules: Hash.new([]) }
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
class << self
|
19
|
+
def add(controller, action, roles, dynamic_rule, negated_dynamic_rule)
|
20
|
+
rule = Rabarber::Rule.new(action, roles, dynamic_rule, negated_dynamic_rule)
|
21
|
+
|
22
|
+
if action
|
23
|
+
instance.storage[:action_rules][controller] += [rule]
|
24
|
+
else
|
25
|
+
instance.storage[:controller_rules][controller] = rule
|
26
|
+
end
|
25
27
|
end
|
26
|
-
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def controller_rules
|
30
|
+
instance.storage[:controller_rules]
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
33
|
+
def action_rules
|
34
|
+
instance.storage[:action_rules]
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabarber
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- enjaku4
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-02-
|
12
|
+
date: 2024-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -50,6 +50,7 @@ files:
|
|
50
50
|
- lib/rabarber/input/types/booleans.rb
|
51
51
|
- lib/rabarber/input/types/procs.rb
|
52
52
|
- lib/rabarber/input/types/symbols.rb
|
53
|
+
- lib/rabarber/logger.rb
|
53
54
|
- lib/rabarber/missing/actions.rb
|
54
55
|
- lib/rabarber/missing/base.rb
|
55
56
|
- lib/rabarber/missing/roles.rb
|
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
80
|
- !ruby/object:Gem::Version
|
80
81
|
version: '0'
|
81
82
|
requirements: []
|
82
|
-
rubygems_version: 3.
|
83
|
+
rubygems_version: 3.3.26
|
83
84
|
signing_key:
|
84
85
|
specification_version: 4
|
85
86
|
summary: Simple authorization library for Ruby on Rails.
|