rabarber 4.1.1 → 4.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: feb905214f327d8aef99517ba1364252b6722430fe6ab1b4839055e1ae5b18fc
4
- data.tar.gz: ed0cacc2d6ad0ec5fd436ba24e96c3c26489264c3f00c73822b24f4593b2fa01
3
+ metadata.gz: ed0e1d5cc00bc6f410b2a677afb591fa4bb1acd21a7674729edc91e7c5b72cc1
4
+ data.tar.gz: 7dd9ded3179f7aef89f36a0983f28fe6f4695cd7e535cdeeba4d208053f8d53f
5
5
  SHA512:
6
- metadata.gz: 6f88384ea24337c233f9183fde8bda61ba2939c3947b9eff07c2b5a47ff44372ba0287258565c3a5e085c4dc80b5026206d7b126e23c58b541b6490778946123
7
- data.tar.gz: 9d7d4243f90fdc36cf8c27c391154449b38407a42995791240b72571e2dff14fa3836e62ce43ca92d5d0c65a5835a1e14119a077eda59ebedbe4c6b2a5aa8bac
6
+ metadata.gz: 99920a8e65cd953f2ddac63083628680b92f89195a65759b64e25d5619099517ad332b8821d2c681a2c0503286617aff0a26f41d5f5f99623fc678a33a028c93
7
+ data.tar.gz: ce9ddfa4d718ac7d2b2533c3c87ad9cc5d3658cb1b23aa4ae1f0e75fe544186a37fb30bbea97b14c0fc9623cdb94ef9bb41c75ae0ea6b27d5a6a72dd62250d17
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## v4.1.3
2
+
3
+ ### Bugs:
4
+
5
+ - Fixed an issue where role presence checking did not work correctly if `must_have_roles` configuration option was enabled
6
+
7
+ ### Misc:
8
+
9
+ - Added caching for `Rabarber::HasRoles#all_roles` method to improve performance
10
+
11
+ ## v4.1.2
12
+
13
+ ### Misc:
14
+
15
+ - Replaced `git ls-files` with `Dir.glob` in gemspec for improved portability and compatibility
16
+
1
17
  ## v4.1.1
2
18
 
3
19
  ### Bugs:
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
- class Crm::InvoicesController < ApplicationController
252
- grant_access action: :index, roles: [:accountant, :admin]
253
- def index
254
- @invoices = Invoice.all
255
- @invoices = @invoices.paid if current_user.has_role?(:accountant)
256
- # ...
257
- end
258
-
259
- grant_access action: :destroy, roles: :admin
260
- def destroy
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
- class Crm::BaseController < ApplicationController
271
- grant_access roles: [:admin, :manager]
272
+ module Crm
273
+ class BaseController < ApplicationController
274
+ grant_access roles: [:admin, :manager]
272
275
 
273
- grant_access action: :dashboard, roles: :marketer
274
- def dashboard
275
- # ...
276
+ grant_access action: :dashboard, roles: :marketer
277
+ def dashboard
278
+ # ...
279
+ end
276
280
  end
277
281
  end
278
282
 
279
- class Crm::InvoicesController < Crm::BaseController
280
- grant_access roles: :accountant
281
- def index
282
- # ...
283
- end
283
+ module Crm
284
+ class InvoicesController < Crm::BaseController
285
+ grant_access roles: :accountant
286
+ def index
287
+ # ...
288
+ end
284
289
 
285
- def delete
286
- # ...
290
+ def delete
291
+ # ...
292
+ end
287
293
  end
288
294
  end
289
295
  ```
@@ -311,28 +317,34 @@ 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
- class Crm::BaseController < ApplicationController
315
- grant_access roles: :admin
320
+ module Crm
321
+ class BaseController < ApplicationController
322
+ grant_access roles: :admin
316
323
  # ...
324
+ end
317
325
  end
318
326
 
319
- class Crm::InvoicesController < Crm::BaseController
320
- grant_access roles: :accountant
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
 
326
336
  This applies as well to multiple rules defined for the same controller or action:
327
337
  ```rb
328
- class Crm::OrdersController < ApplicationController
329
- grant_access roles: :manager, context: Order
330
- grant_access roles: :admin
338
+ module Crm
339
+ class OrdersController < ApplicationController
340
+ grant_access roles: :manager, context: Order
341
+ grant_access roles: :admin
331
342
 
332
- grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
333
- grant_access action: :show, roles: :accountant
334
- def show
335
- # ...
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
336
348
  end
337
349
  end
338
350
  ```
@@ -343,38 +355,42 @@ This will add rules for `manager` and `admin` roles for all actions in `Crm::Ord
343
355
  For more complex cases, Rabarber provides dynamic rules:
344
356
 
345
357
  ```rb
346
- class Crm::OrdersController < ApplicationController
347
- grant_access roles: :manager, if: :company_manager?, unless: :fired?
358
+ module Crm
359
+ class OrdersController < ApplicationController
360
+ grant_access roles: :manager, if: :company_manager?, unless: :fired?
348
361
 
349
- def index
350
- # ...
351
- end
362
+ def index
363
+ # ...
364
+ end
352
365
 
353
- private
366
+ private
354
367
 
355
- def company_manager?
356
- Company.find(params[:company_id]).manager == current_user
357
- end
368
+ def company_manager?
369
+ Company.find(params[:company_id]).manager == current_user
370
+ end
358
371
 
359
- def fired?
360
- current_user.fired?
372
+ def fired?
373
+ current_user.fired?
374
+ end
361
375
  end
362
376
  end
363
377
 
364
- class Crm::InvoicesController < ApplicationController
365
- grant_access roles: :senior_accountant
366
-
367
- grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
368
- def index
369
- @invoices = Invoice.all
370
- @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
371
- @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
372
- # ...
373
- end
374
-
375
- grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
376
- def show
377
- # ...
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
378
394
  end
379
395
  end
380
396
  ```
@@ -408,27 +424,27 @@ Every Rabarber method can accept a context as an additional keyword argument. By
408
424
  E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
409
425
 
410
426
  ```rb
411
- user.assign_roles(:owner, context: project)
412
- another_user.assign_roles(:member, context: project)
427
+ user.assign_roles(:owner, context: project)
428
+ another_user.assign_roles(:member, context: project)
413
429
  ```
414
430
 
415
431
  Then the roles can be verified:
416
432
 
417
433
  ```rb
418
- user.has_role?(:owner, context: project)
419
- another_user.has_role?(:member, context: project)
434
+ user.has_role?(:owner, context: project)
435
+ another_user.has_role?(:member, context: project)
420
436
  ```
421
437
 
422
438
  A role can also be added using a class as a context, e.g., for project admins who can manage all projects:
423
439
 
424
440
  ```rb
425
- user.assign_roles(:admin, context: Project)
441
+ user.assign_roles(:admin, context: Project)
426
442
  ```
427
443
 
428
444
  And then it can also be verified:
429
445
 
430
446
  ```rb
431
- user.has_role?(:admin, context: Project)
447
+ user.has_role?(:admin, context: Project)
432
448
  ```
433
449
 
434
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):
@@ -460,13 +476,13 @@ It's important to note that role names are not unique globally but are unique wi
460
476
  If you want to see all the roles assigned to a user within a specific context, you can use:
461
477
 
462
478
  ```rb
463
- user.roles(context: project)
479
+ user.roles(context: project)
464
480
  ```
465
481
 
466
482
  Or if you want to get all the roles available in a specific context, you can use:
467
483
 
468
484
  ```rb
469
- Rabarber::Role.names(context: Project)
485
+ Rabarber::Role.names(context: Project)
470
486
  ```
471
487
 
472
488
  ## When Unauthorized
@@ -5,22 +5,18 @@ require "digest/sha2"
5
5
  module Rabarber
6
6
  module Core
7
7
  module Cache
8
- CACHE_PREFIX = "rabarber"
9
- private_constant :CACHE_PREFIX
10
-
11
8
  module_function
12
9
 
13
- def fetch(roleable_id, context:, &block)
10
+ def fetch(key, &)
14
11
  return yield unless enabled?
15
12
 
16
- Rails.cache.fetch(key_for(roleable_id, context), expires_in: 1.hour, race_condition_ttl: 5.seconds, &block)
13
+ Rails.cache.fetch(prepare_key(key), expires_in: 1.hour, race_condition_ttl: 5.seconds, &)
17
14
  end
18
15
 
19
- def delete(*roleable_ids, context:)
16
+ def delete(*keys)
20
17
  return unless enabled?
21
18
 
22
- keys = roleable_ids.map { |roleable_id| key_for(roleable_id, context) }
23
- Rails.cache.delete_multi(keys) if keys.any?
19
+ Rails.cache.delete_multi(keys.map { prepare_key(_1) }) if keys.any?
24
20
  end
25
21
 
26
22
  def enabled?
@@ -31,17 +27,17 @@ module Rabarber
31
27
  Rails.cache.delete_matched(/^#{CACHE_PREFIX}/o)
32
28
  end
33
29
 
34
- def key_for(id, context)
35
- "#{CACHE_PREFIX}:#{Digest::SHA2.hexdigest("#{id}#{context.fetch(:context_type)}#{context.fetch(:context_id)}")}"
30
+ def prepare_key(key)
31
+ "#{CACHE_PREFIX}:#{Digest::SHA2.hexdigest(Marshal.dump(key))}"
36
32
  end
33
+
34
+ CACHE_PREFIX = "rabarber"
35
+ private_constant :CACHE_PREFIX
37
36
  end
38
37
  end
39
38
 
40
39
  module Cache
41
- module_function
42
-
43
- def clear
44
- Rabarber::Core::Cache.clear
45
- end
40
+ delegate :clear, to: :"Rabarber::Core::Cache"
41
+ module_function :clear
46
42
  end
47
43
  end
@@ -7,6 +7,10 @@ module Rabarber
7
7
  []
8
8
  end
9
9
 
10
+ def all_roles
11
+ {}
12
+ end
13
+
10
14
  def has_role?(*role_names, context: nil) # rubocop:disable Lint/UnusedMethodArgument
11
15
  false
12
16
  end
@@ -17,10 +17,9 @@ module Rabarber
17
17
  end
18
18
 
19
19
  def roles_permitted?(roleable, controller_instance)
20
- resolved_context = resolve_context(controller_instance)
21
- return false if Rabarber::Configuration.instance.must_have_roles && roleable.roles(context: resolved_context).empty?
20
+ return false if Rabarber::Configuration.instance.must_have_roles && roleable.all_roles.empty?
22
21
 
23
- roles.empty? || roleable.has_role?(*roles, context: resolved_context)
22
+ roles.empty? || roleable.has_role?(*roles, context: resolve_context(controller_instance))
24
23
  end
25
24
 
26
25
  def dynamic_rules_followed?(controller_instance)
@@ -16,11 +16,11 @@ module Rabarber
16
16
 
17
17
  def roles(context: nil)
18
18
  processed_context = process_context(context)
19
- Rabarber::Core::Cache.fetch(roleable_id, context: processed_context) { rabarber_roles.names(context: processed_context) }
19
+ Rabarber::Core::Cache.fetch([roleable_id, processed_context]) { rabarber_roles.names(context: processed_context) }
20
20
  end
21
21
 
22
22
  def all_roles
23
- rabarber_roles.all_names
23
+ Rabarber::Core::Cache.fetch([roleable_id, :all]) { rabarber_roles.all_names }
24
24
  end
25
25
 
26
26
  def has_role?(*role_names, context: nil)
@@ -102,7 +102,7 @@ module Rabarber
102
102
  end
103
103
 
104
104
  def delete_roleable_cache(context:)
105
- Rabarber::Core::Cache.delete(roleable_id, context:)
105
+ Rabarber::Core::Cache.delete([roleable_id, context], [roleable_id, :all])
106
106
  end
107
107
 
108
108
  def roleable_id
@@ -67,7 +67,7 @@ module Rabarber
67
67
  private
68
68
 
69
69
  def delete_roleables_cache(role, context:)
70
- Rabarber::Core::Cache.delete(*assigned_to_roleables(role), context:)
70
+ Rabarber::Core::Cache.delete(*assigned_to_roleables(role).flat_map { [[_1, context], [_1, :all]] })
71
71
  end
72
72
 
73
73
  def assigned_to_roleables(role)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "4.1.1"
4
+ VERSION = "4.1.3"
5
5
  end
data/rabarber.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  spec.files = [
20
20
  "rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
21
- ] + `git ls-files | grep -E '^(lib)'`.split("\n")
21
+ ] + Dir.glob("lib/**/*")
22
22
 
23
23
  spec.require_paths = ["lib"]
24
24
 
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.1
4
+ version: 4.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4
8
8
  - trafium
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-22 00:00:00.000000000 Z
11
+ date: 2025-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails