rabarber 6.0.0 → 6.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b63470cbdf375117e5c901ae46992d0fb85868acedacde3ab17472437d8f4a10
4
- data.tar.gz: bba19d48ebad8a6440c70e7555d5befcc9c64f67c0a43f0408cd966f9d24aa15
3
+ metadata.gz: 6cb92ea0f4a25ee7dd730aae35b07d16230434393882cd0833d853117e7887e9
4
+ data.tar.gz: 3056af62a93df97b88f708dc725e0b54c92cfc198aad225ba80262fcfd36a31a
5
5
  SHA512:
6
- metadata.gz: 5b43430eb2fc105add42baa08aa12349137f1aab6021d371400cc48c48c134db4c20bd81e3cbff043e63bf6f22161d6645ab57619090bf1c08b905f6be60fbe1
7
- data.tar.gz: cb648ea4388e067d1f06d57f2ba9b45fb7139df3a5a1cbb102ae42441730a2a83749b116d468fb0c6d3b9b0be419c010a62c73c3c0be2d90752086aa5f4e8837
6
+ metadata.gz: 1e599ea0709127eeed62dd0aae019f9dee5a73843e08671089e1a84b16e13a2db4ad8f908080b51f3a5a8d2fa82d9a7f21efed638dd6e1054f5c80fdb8b5f423
7
+ data.tar.gz: 0b2548c277de1beb990d9ce0321b0989d7358c521e3cb0d20994cf389ea931feb288dc670d7e886c6a1ccff29dc4d7bdd7c1a07885f0ad327f7b94e3cf8d6f82
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## v6.0.1
2
+
3
+ ### Bugs:
4
+
5
+ - Fixed role management methods raising `NameError` when the configured user model is not named `User`
6
+ - Fixed a race condition where outdated roles could remain cached after being changed
7
+ - Fixed role creation and renaming raising `ActiveRecord::RecordNotUnique` instead of returning `false` when called concurrently
8
+ - Fixed uniqueness of global and class context roles not being enforced at the database level
9
+
10
+ This release is fully backward compatible: existing applications continue to work as before without any changes. The new indexes only apply to new applications installing Rabarber. To get the same database-level protection in an existing application, run the following optional migration if your database supports partial indexes:
11
+
12
+ ```rb
13
+ add_index :rabarber_roles, :name, unique: true, where: "context_type IS NULL AND context_id IS NULL"
14
+ add_index :rabarber_roles, [:name, :context_type], unique: true, where: "context_type IS NOT NULL AND context_id IS NULL"
15
+ ```
16
+
17
+ On databases without partial index support, such as MySQL, uniqueness of global and class context roles remains, for now, enforced at the application level only.
18
+
19
+ ### Misc:
20
+
21
+ - `Rabarber.prune` now clears the role cache
22
+
1
23
  ## v6.0.0
2
24
 
3
25
  ### Breaking:
data/README.md CHANGED
@@ -218,7 +218,7 @@ class TicketsController < ApplicationController
218
218
  end
219
219
  ```
220
220
 
221
- Authorization rules are additive - they combine across inheritance chains and when defined multiple times for the same action or controller:
221
+ Authorization rules are additive - rules defined multiple times combine, and controller-wide rules are inherited by subclasses:
222
222
 
223
223
  ```rb
224
224
  class BaseController < ApplicationController
@@ -459,27 +459,31 @@ Use conditional rendering based on roles:
459
459
  <% end %>
460
460
  ```
461
461
 
462
+ Like authorization, these helpers require an authenticated user. In views that can be rendered for both authenticated and unauthenticated visitors, wrap them in your authentication check.
463
+
462
464
  ## Getting Help and Contributing
463
465
 
464
466
  ### Getting Help
467
+
465
468
  Have a question or need assistance? Open a discussion in the [discussions section](https://github.com/enjaku4/rabarber/discussions) for:
469
+
466
470
  - Usage questions
467
471
  - Implementation guidance
468
- - Feature suggestions
472
+ - Open-ended ideas or suggestions
473
+
474
+ ### Issues
475
+
476
+ [Issues](https://github.com/enjaku4/rabarber/issues) track bugs, planned features, and other work. When reporting a bug, please include:
469
477
 
470
- ### Reporting Issues
471
- Found a bug? Please [create an issue](https://github.com/enjaku4/rabarber/issues) with:
472
478
  - A clear description of the problem
473
479
  - Steps to reproduce the issue
474
- - Your environment details (Rails version, Ruby version, etc.)
480
+ - Your environment details (Rails and Ruby versions, OS, etc.)
475
481
 
476
482
  ### Contributing Code
477
- Ready to contribute? You can:
478
- - Fix bugs by submitting pull requests
479
- - Improve documentation
480
- - Add new features (please discuss first in the [discussions section](https://github.com/enjaku4/rabarber/discussions))
481
483
 
482
- Before contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md)
484
+ Any open issue is free to pick up or discuss. Check the [project board](https://github.com/users/enjaku4/projects/10) or [issues tab](https://github.com/enjaku4/rabarber/issues).
485
+
486
+ Before contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md).
483
487
 
484
488
  ## License
485
489
 
@@ -491,7 +495,7 @@ Everyone interacting in the Rabarber project is expected to follow the [code of
491
495
 
492
496
  ## Old Versions
493
497
 
494
- Only the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:
498
+ Only the latest major version is actively maintained. READMEs for older versions are available here for reference:
495
499
 
496
500
  [v5.x.x](https://github.com/enjaku4/rabarber/blob/12a23858e974f5cc0a4ebddfc18bdf84171dd554/README.md) | [v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
497
501
  | [v2.x.x](https://github.com/enjaku4/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/enjaku4/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
@@ -10,6 +10,11 @@ class CreateRabarberRoles < ActiveRecord::Migration[<%= ActiveRecord::Migration.
10
10
 
11
11
  add_index :rabarber_roles, [:name, :context_type, :context_id], unique: true
12
12
 
13
+ if connection.supports_partial_index?
14
+ add_index :rabarber_roles, :name, unique: true, where: "context_type IS NULL AND context_id IS NULL"
15
+ add_index :rabarber_roles, [:name, :context_type], unique: true, where: "context_type IS NOT NULL AND context_id IS NULL"
16
+ end
17
+
13
18
  create_table :rabarber_roles_roleables, id: false do |t|
14
19
  t.belongs_to :role, null: false, index: true, foreign_key: { to_table: :rabarber_roles }<%= ", type: :uuid" if options[:uuid] %>
15
20
  t.belongs_to :roleable, null: false, index: true, foreign_key: { to_table: <%= table_name.to_sym.inspect %> }<%= ", type: :uuid" if options[:uuid] %>
@@ -15,9 +15,12 @@ module Rabarber
15
15
  end
16
16
 
17
17
  def delete(*pairs)
18
- return unless enabled?
18
+ return unless enabled? && pairs.any?
19
19
 
20
- Rails.cache.delete_multi(pairs.map { |roleable_id, scope| prepare_key(roleable_id, scope) }) if pairs.any?
20
+ keys = pairs.map { |roleable_id, scope| prepare_key(roleable_id, scope) }
21
+
22
+ Rails.cache.delete_multi(keys)
23
+ ActiveRecord.after_all_transactions_commit { Rails.cache.delete_multi(keys) } if ActiveRecord::Base.connection.transaction_open?
21
24
  end
22
25
 
23
26
  def clear
@@ -50,7 +50,7 @@ module Rabarber
50
50
  def has_role?(*role_names, context: nil)
51
51
  processed_context = process_context(context)
52
52
  processed_roles = process_role_names(role_names)
53
- roles(context: processed_context).any? { |role_name| processed_roles.include?(role_name) }
53
+ roles(context: processed_context).intersect?(processed_roles)
54
54
  end
55
55
 
56
56
  def assign_roles(*role_names, context: nil, create_new: true)
@@ -64,8 +64,12 @@ module Rabarber
64
64
  )
65
65
 
66
66
  if roles_to_assign.any?
67
+ begin
68
+ rabarber_roles << roles_to_assign
69
+ rescue ActiveRecord::RecordNotUnique
70
+ rabarber_roles.reset
71
+ end
67
72
  delete_roleable_cache(contexts: [processed_context])
68
- rabarber_roles << roles_to_assign
69
73
  end
70
74
 
71
75
  roles(context: processed_context)
@@ -80,8 +84,8 @@ module Rabarber
80
84
  )
81
85
 
82
86
  if roles_to_revoke.any?
83
- delete_roleable_cache(contexts: [processed_context])
84
87
  self.rabarber_roles -= roles_to_revoke
88
+ delete_roleable_cache(contexts: [processed_context])
85
89
  end
86
90
 
87
91
  roles(context: processed_context)
@@ -103,7 +107,7 @@ module Rabarber
103
107
 
104
108
  def create_new_roles(role_names, context:)
105
109
  new_roles = role_names - Rabarber.roles(context:)
106
- new_roles.each { |role_name| Rabarber::Role.create!(name: role_name, **context) }
110
+ new_roles.each { |role_name| Rabarber::Role.register(role_name, context:) }
107
111
  end
108
112
 
109
113
  def delete_roleable_cache(contexts:)
@@ -6,10 +6,6 @@ module Rabarber
6
6
 
7
7
  belongs_to :context, polymorphic: true, optional: true
8
8
 
9
- has_and_belongs_to_many :roleables, class_name: Rabarber::Configuration.user_model_name,
10
- association_foreign_key: "roleable_id",
11
- join_table: "rabarber_roles_roleables"
12
-
13
9
  class << self
14
10
  def list(context: nil)
15
11
  where(process_context(context)).pluck(:name).map(&:to_sym)
@@ -32,6 +28,8 @@ module Rabarber
32
28
  return false if exists?(name:, **processed_context)
33
29
 
34
30
  !!create!(name:, **processed_context)
31
+ rescue ActiveRecord::RecordNotUnique
32
+ false
35
33
  end
36
34
 
37
35
  def amend(old_name, new_name, context: nil, force: false)
@@ -44,9 +42,13 @@ module Rabarber
44
42
 
45
43
  return false if exists?(name:, **processed_context) || role.roleables.exists? && !force
46
44
 
47
- delete_roleables_cache(role, context: processed_context)
48
-
49
45
  role.update!(name:)
46
+
47
+ delete_roleables_cache(role.roleables.pluck(:id), context: processed_context)
48
+
49
+ true
50
+ rescue ActiveRecord::RecordNotUnique
51
+ false
50
52
  end
51
53
 
52
54
  def drop(name, context: nil, force: false)
@@ -57,9 +59,13 @@ module Rabarber
57
59
 
58
60
  return false if role.roleables.exists? && !force
59
61
 
60
- delete_roleables_cache(role, context: processed_context)
62
+ roleable_ids = role.roleables.pluck(:id)
61
63
 
62
- !!role.destroy!
64
+ role.destroy!
65
+
66
+ delete_roleables_cache(roleable_ids, context: processed_context)
67
+
68
+ true
63
69
  end
64
70
 
65
71
  def prune
@@ -79,12 +85,14 @@ module Rabarber
79
85
  )
80
86
  where(id: orphaned_roles).delete_all
81
87
  end
88
+
89
+ Rabarber::Core::Cache.clear
82
90
  end
83
91
 
84
92
  private
85
93
 
86
- def delete_roleables_cache(role, context:)
87
- Rabarber::Core::Cache.delete(*role.roleables.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
94
+ def delete_roleables_cache(roleable_ids, context:)
95
+ Rabarber::Core::Cache.delete(*roleable_ids.flat_map { [[_1, context], [_1, :all]] })
88
96
  end
89
97
 
90
98
  def process_role_name(name)
@@ -28,6 +28,11 @@ module Rabarber
28
28
 
29
29
  user_model = Rabarber::Configuration.user_model
30
30
  user_model.include Rabarber::Roleable unless user_model < Rabarber::Roleable
31
+
32
+ Rabarber::Role.send(:remove_const, :HABTM_Roleables) if Rabarber::Role.const_defined?(:HABTM_Roleables, false)
33
+ Rabarber::Role.has_and_belongs_to_many :roleables, class_name: Rabarber::Configuration.user_model_name,
34
+ association_foreign_key: "roleable_id",
35
+ join_table: "rabarber_roles_roleables"
31
36
  end
32
37
  end
33
38
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "6.0.0"
4
+ VERSION = "6.0.1"
5
5
  end
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: 6.0.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4
8
8
  - trafium
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-01 00:00:00.000000000 Z
11
+ date: 2026-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 4.0.8
101
+ rubygems_version: 4.0.15
102
102
  specification_version: 4
103
103
  summary: Simple role-based authorization library for Ruby on Rails
104
104
  test_files: []