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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5450b60dd272da8e0b78af00e44a3e82b29c7b9a9e8b510c68efccbba30ba150
4
- data.tar.gz: 7bbdd6de4ed8f2e33be07363c9c9d4e099170099ca7cb32a50df217373503284
3
+ metadata.gz: 73050af26c88e78f295c489ee5fc49c782b3950157db2ecd1dd48cdda5b10045
4
+ data.tar.gz: '08f7da98672774c2198c155be4fb6f795c23e7da72e565631ef0f08207cd2cb9'
5
5
  SHA512:
6
- metadata.gz: 8c9aa4a196f99dbb5250dd1b625fed829418843f5de8f35a4b2cde5f933f8780be0865f8c23e90c57490a3358f004fd1168dc28bcc9be9f66f0bb56b9ebcfddf
7
- data.tar.gz: 3ddc04e91777a31631d4e9db9a54d6d1e23f588935e09c9d9691f8c607fffbabe18a49553cc9bad22ed8944dbec8b6cb58ecaf6a432c7ac7da59c1aca27a334f
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
- 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,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
- 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
 
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
- class Crm::OrdersController < ApplicationController
332
- 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?
333
361
 
334
- def index
335
- # ...
336
- end
362
+ def index
363
+ # ...
364
+ end
337
365
 
338
- private
366
+ private
339
367
 
340
- def company_manager?
341
- Company.find(params[:company_id]).manager == current_user
342
- end
368
+ def company_manager?
369
+ Company.find(params[:company_id]).manager == current_user
370
+ end
343
371
 
344
- def fired?
345
- current_user.fired?
372
+ def fired?
373
+ current_user.fired?
374
+ end
346
375
  end
347
376
  end
348
377
 
349
- class Crm::InvoicesController < ApplicationController
350
- grant_access roles: :senior_accountant
351
-
352
- grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
353
- def index
354
- @invoices = Invoice.all
355
- @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
356
- @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
357
- # ...
358
- end
359
-
360
- grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
361
- def show
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
- user.assign_roles(:owner, context: project)
397
- another_user.assign_roles(:member, context: project)
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
- user.has_role?(:owner, context: project)
404
- another_user.has_role?(:member, context: project)
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
- user.assign_roles(:admin, context: Project)
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
- user.has_role?(:admin, context: Project)
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
- user.roles(context: project)
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
- Rabarber::Role.names(context: Project)
485
+ Rabarber::Role.names(context: Project)
455
486
  ```
456
487
 
457
488
  ## When Unauthorized
@@ -8,15 +8,13 @@ module Rabarber
8
8
  end
9
9
 
10
10
  def controller_accessible?(roleable, controller_instance)
11
- controller_rules.any? do |rule_controller, rule|
12
- controller_instance.is_a?(rule_controller) && rule.verify_access(roleable, controller_instance)
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? do |rule|
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
@@ -38,10 +38,7 @@ module Rabarber
38
38
  end
39
39
 
40
40
  module Cache
41
- module_function
42
-
43
- def clear
44
- Rabarber::Core::Cache.clear
45
- end
41
+ delegate :clear, to: :"Rabarber::Core::Cache"
42
+ module_function :clear
46
43
  end
47
44
  end
@@ -13,13 +13,16 @@ module Rabarber
13
13
  attr_reader :storage
14
14
 
15
15
  def initialize
16
- @storage = { controller_rules: Hash.new({}), action_rules: Hash.new([]) }
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(action, roles, context, dynamic_rule, negated_dynamic_rule)
22
- action ? action_rules[controller] += [rule] : controller_rules[controller] = rule
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, rules), arr|
25
- missing_actions = rules.map(&:action) - controller.action_methods.map(&:to_sym)
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
@@ -3,10 +3,9 @@
3
3
  module Rabarber
4
4
  module Core
5
5
  class Rule
6
- attr_reader :action, :roles, :context, :dynamic_rule, :negated_dynamic_rule
6
+ attr_reader :roles, :context, :dynamic_rule, :negated_dynamic_rule
7
7
 
8
- def initialize(action, roles, context, dynamic_rule, negated_dynamic_rule)
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 }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "4.1.0"
4
+ VERSION = "4.1.2"
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.0
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: 2024-12-29 00:00:00.000000000 Z
11
+ date: 2025-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails