rabarber 4.1.0 → 4.1.2
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 +16 -0
- data/README.md +91 -60
- data/lib/rabarber/core/access.rb +3 -5
- data/lib/rabarber/core/cache.rb +2 -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/version.rb +1 -1
- data/rabarber.gemspec +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: 73050af26c88e78f295c489ee5fc49c782b3950157db2ecd1dd48cdda5b10045
|
4
|
+
data.tar.gz: '08f7da98672774c2198c155be4fb6f795c23e7da72e565631ef0f08207cd2cb9'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1482714aa484c800970ea77a081388a0df4b8945e8fc0931b76e8d6415ed9f88c01ad8834e873d9161a20c7a5f2208b10782fe95857024b1a6069919f83dd47
|
7
|
+
data.tar.gz: '06383d81e91f872419fb567b2ef18991abf6a32263297645725e52b70a9c383a769c72d3657fb9f61a6cc11fb30b2129d813ca63e1a352761f99795f287c86b7'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## v4.1.2
|
2
|
+
|
3
|
+
### Misc:
|
4
|
+
|
5
|
+
- Replaced `git ls-files` with `Dir.glob` in gemspec for improved portability and compatibility
|
6
|
+
|
7
|
+
## v4.1.1
|
8
|
+
|
9
|
+
### Bugs:
|
10
|
+
|
11
|
+
- 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
|
12
|
+
|
13
|
+
### Misc:
|
14
|
+
|
15
|
+
- Minor performance improvement for authorization checks
|
16
|
+
|
1
17
|
## v4.1.0
|
2
18
|
|
3
19
|
### Features:
|
data/README.md
CHANGED
@@ -248,17 +248,19 @@ This adds `.grant_access(action: nil, roles: nil, context: nil, if: nil, unless:
|
|
248
248
|
The most basic usage of the method is as follows:
|
249
249
|
|
250
250
|
```rb
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
251
|
+
module Crm
|
252
|
+
class InvoicesController < ApplicationController
|
253
|
+
grant_access action: :index, roles: [:accountant, :admin]
|
254
|
+
def index
|
255
|
+
@invoices = Invoice.all
|
256
|
+
@invoices = @invoices.paid if current_user.has_role?(:accountant)
|
257
|
+
# ...
|
258
|
+
end
|
259
|
+
|
260
|
+
grant_access action: :destroy, roles: :admin
|
261
|
+
def destroy
|
262
|
+
# ...
|
263
|
+
end
|
262
264
|
end
|
263
265
|
end
|
264
266
|
```
|
@@ -267,23 +269,27 @@ This grants access to `index` action for users with `accountant` or `admin` role
|
|
267
269
|
You can also define controller-wide rules (without `action` argument):
|
268
270
|
|
269
271
|
```rb
|
270
|
-
|
271
|
-
|
272
|
+
module Crm
|
273
|
+
class BaseController < ApplicationController
|
274
|
+
grant_access roles: [:admin, :manager]
|
272
275
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
+
grant_access action: :dashboard, roles: :marketer
|
277
|
+
def dashboard
|
278
|
+
# ...
|
279
|
+
end
|
276
280
|
end
|
277
281
|
end
|
278
282
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
283
|
+
module Crm
|
284
|
+
class InvoicesController < Crm::BaseController
|
285
|
+
grant_access roles: :accountant
|
286
|
+
def index
|
287
|
+
# ...
|
288
|
+
end
|
284
289
|
|
285
|
-
|
286
|
-
|
290
|
+
def delete
|
291
|
+
# ...
|
292
|
+
end
|
287
293
|
end
|
288
294
|
end
|
289
295
|
```
|
@@ -311,55 +317,80 @@ If you've set `must_have_roles` setting to `true`, then only the users with at l
|
|
311
317
|
|
312
318
|
Also keep in mind that rules defined in child classes don't override parent rules but rather add to them:
|
313
319
|
```rb
|
314
|
-
|
315
|
-
|
320
|
+
module Crm
|
321
|
+
class BaseController < ApplicationController
|
322
|
+
grant_access roles: :admin
|
316
323
|
# ...
|
324
|
+
end
|
317
325
|
end
|
318
326
|
|
319
|
-
|
320
|
-
|
327
|
+
module Crm
|
328
|
+
class InvoicesController < Crm::BaseController
|
329
|
+
grant_access roles: :accountant
|
321
330
|
# ...
|
331
|
+
end
|
322
332
|
end
|
323
333
|
```
|
324
334
|
This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
|
325
335
|
|
336
|
+
This applies as well to multiple rules defined for the same controller or action:
|
337
|
+
```rb
|
338
|
+
module Crm
|
339
|
+
class OrdersController < ApplicationController
|
340
|
+
grant_access roles: :manager, context: Order
|
341
|
+
grant_access roles: :admin
|
342
|
+
|
343
|
+
grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
|
344
|
+
grant_access action: :show, roles: :accountant
|
345
|
+
def show
|
346
|
+
# ...
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
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.
|
352
|
+
|
326
353
|
## Dynamic Authorization Rules
|
327
354
|
|
328
355
|
For more complex cases, Rabarber provides dynamic rules:
|
329
356
|
|
330
357
|
```rb
|
331
|
-
|
332
|
-
|
358
|
+
module Crm
|
359
|
+
class OrdersController < ApplicationController
|
360
|
+
grant_access roles: :manager, if: :company_manager?, unless: :fired?
|
333
361
|
|
334
|
-
|
335
|
-
|
336
|
-
|
362
|
+
def index
|
363
|
+
# ...
|
364
|
+
end
|
337
365
|
|
338
|
-
|
366
|
+
private
|
339
367
|
|
340
|
-
|
341
|
-
|
342
|
-
|
368
|
+
def company_manager?
|
369
|
+
Company.find(params[:company_id]).manager == current_user
|
370
|
+
end
|
343
371
|
|
344
|
-
|
345
|
-
|
372
|
+
def fired?
|
373
|
+
current_user.fired?
|
374
|
+
end
|
346
375
|
end
|
347
376
|
end
|
348
377
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
378
|
+
module Crm
|
379
|
+
class InvoicesController < ApplicationController
|
380
|
+
grant_access roles: :senior_accountant
|
381
|
+
|
382
|
+
grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
|
383
|
+
def index
|
384
|
+
@invoices = Invoice.all
|
385
|
+
@invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
|
386
|
+
@invoices = @invoices.unpaid if current_user.has_role?(:secretary)
|
387
|
+
# ...
|
388
|
+
end
|
389
|
+
|
390
|
+
grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
|
391
|
+
def show
|
392
|
+
# ...
|
393
|
+
end
|
363
394
|
end
|
364
395
|
end
|
365
396
|
```
|
@@ -393,27 +424,27 @@ Every Rabarber method can accept a context as an additional keyword argument. By
|
|
393
424
|
E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
|
394
425
|
|
395
426
|
```rb
|
396
|
-
|
397
|
-
|
427
|
+
user.assign_roles(:owner, context: project)
|
428
|
+
another_user.assign_roles(:member, context: project)
|
398
429
|
```
|
399
430
|
|
400
431
|
Then the roles can be verified:
|
401
432
|
|
402
433
|
```rb
|
403
|
-
|
404
|
-
|
434
|
+
user.has_role?(:owner, context: project)
|
435
|
+
another_user.has_role?(:member, context: project)
|
405
436
|
```
|
406
437
|
|
407
438
|
A role can also be added using a class as a context, e.g., for project admins who can manage all projects:
|
408
439
|
|
409
440
|
```rb
|
410
|
-
|
441
|
+
user.assign_roles(:admin, context: Project)
|
411
442
|
```
|
412
443
|
|
413
444
|
And then it can also be verified:
|
414
445
|
|
415
446
|
```rb
|
416
|
-
|
447
|
+
user.has_role?(:admin, context: Project)
|
417
448
|
```
|
418
449
|
|
419
450
|
In authorization rules, the context can be used in the same way, but it also can be a proc or a symbol (similar to dynamic rules):
|
@@ -445,13 +476,13 @@ It's important to note that role names are not unique globally but are unique wi
|
|
445
476
|
If you want to see all the roles assigned to a user within a specific context, you can use:
|
446
477
|
|
447
478
|
```rb
|
448
|
-
|
479
|
+
user.roles(context: project)
|
449
480
|
```
|
450
481
|
|
451
482
|
Or if you want to get all the roles available in a specific context, you can use:
|
452
483
|
|
453
484
|
```rb
|
454
|
-
|
485
|
+
Rabarber::Role.names(context: Project)
|
455
486
|
```
|
456
487
|
|
457
488
|
## When Unauthorized
|
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
|
data/lib/rabarber/core/cache.rb
CHANGED
@@ -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 }
|
data/lib/rabarber/version.rb
CHANGED
data/rabarber.gemspec
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.1.
|
4
|
+
version: 4.1.2
|
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-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|