rabarber 5.1.2 → 5.2.0

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: bcf3063fc3e0bb700ae4e372cec9807b3796863f8e94e5b2f8e79d9594d50b10
4
- data.tar.gz: 82abf396141f483ca0ec10982bb9ef35ea8a1d79f0a1e111f2c8b483a0e480e5
3
+ metadata.gz: 70a773c884c0e207de25a518ff864d25ff13cd1a05c712c88efd17d264782d52
4
+ data.tar.gz: 901200cff398f2355a307aff03c9e63f1bcdbecf07369bf768fe7f12ebed9151
5
5
  SHA512:
6
- metadata.gz: 023fddca28a49a89534c1d5c0b716e7f518a3a975e2b6a5883f156f75acd73a28564b475080fd5abdf7ffce294b21c1af07be276141ac747d92b9087b8cbac96
7
- data.tar.gz: 7ae10c74e73ee844d22011a6678a8b9af579d2657d3020fe28958912c3b4a1fb033ed0a11c7af126c7367e30fcd54a1058f529b39acf0cac31391f5bc0a76146
6
+ metadata.gz: dd83d11d1c831f3ecb5ea36fb6d650113453f5e30b5cfdfb1affb58a515a5f77da33eb067a03abde379997f47c7e8c44d5abdf05c3a1b363d778215241406ad0
7
+ data.tar.gz: 130c1c65c3d5bc2e33b788e35f4a790586784ced24a20a110b106303729a2c5a798330e5e05abfe6ebf58902a63761af99909443a92b2dcec496f6d26136926e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## v5.2.0
2
+
3
+ ### Features:
4
+
5
+ - Added `Rabarber.prune` method to allow manual pruning of roles with deleted contexts
6
+
7
+ ### Misc:
8
+
9
+ - `revoke_all_roles` now returns an empty array instead of `nil` for consistency with other role assignment methods
10
+ - Deprecated the following role management methods in favor of new counterparts:
11
+ - `Rabarber::Role.names` -> `Rabarber.roles`
12
+ - `Rabarber::Role.all_names` -> `Rabarber.all_roles`
13
+ - `Rabarber::Role.add` -> `Rabarber.create_role`
14
+ - `Rabarber::Role.rename` -> `Rabarber.rename_role`
15
+ - `Rabarber::Role.remove` -> `Rabarber.delete_role`
16
+ - `Rabarber::Role.assignees` -> `User.with_role`
17
+
1
18
  ## v5.1.2
2
19
 
3
20
  ### Misc:
@@ -35,7 +52,7 @@
35
52
  - Added `with_authorization` method for more granular authorization control
36
53
  - `Rabarber::Role.rename` and `Rabarber::Role.remove` now require the role to exist
37
54
 
38
- To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/77)
55
+ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/77)
39
56
 
40
57
  ### Features:
41
58
 
@@ -140,7 +157,7 @@ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/b
140
157
 
141
158
  - Changed Rabarber roles table structure
142
159
 
143
- To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/58)
160
+ To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/58)
144
161
 
145
162
  ### Features:
146
163
 
@@ -164,7 +181,7 @@ To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/b
164
181
  - Replaced `when_unauthorized` configuration option with an overridable controller method
165
182
  - Renamed `Rabarber::Role.assignees_for` method to `Rabarber::Role.assignees`
166
183
 
167
- To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/52)
184
+ To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/52)
168
185
 
169
186
  ### Features:
170
187
 
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Rabarber: Role-Based Authorization for Rails
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rabarber.svg)](http://badge.fury.io/rb/rabarber)
4
- [![Github Actions badge](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml)
4
+ [![Github Actions badge](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
5
5
 
6
6
  Rabarber is a role-based authorization library for Ruby on Rails that focuses on controller-level access control. Rather than answering domain questions like "can this user create a post?", Rabarber answers "can this user access the create post endpoint?", providing a clean separation between authorization and business logic.
7
7
 
@@ -25,7 +25,6 @@ Rabarber is a role-based authorization library for Ruby on Rails that focuses on
25
25
  - [Multi-tenancy / Context](#multi-tenancy--context)
26
26
  - [View Helpers](#view-helpers)
27
27
 
28
-
29
28
  **Community Resources:**
30
29
  - [Getting Help and Contributing](#getting-help-and-contributing)
31
30
  - [License](#license)
@@ -68,13 +67,13 @@ Configure Rabarber in an initializer if customization is needed:
68
67
 
69
68
  ```rb
70
69
  Rabarber.configure do |config|
71
- config.cache_enabled = true # Enable role caching (default: true)
70
+ config.cache_enabled = true # Enable/disable role caching (default: true)
72
71
  config.current_user_method = :current_user # Method to access current user (default: :current_user)
73
72
  config.user_model_name = "User" # User model name (default: "User")
74
73
  end
75
74
  ```
76
75
 
77
- To clear the role cache manually:
76
+ Roles are cached by default for performance. To clear the role cache manually:
78
77
 
79
78
  ```rb
80
79
  Rabarber::Cache.clear
@@ -100,17 +99,22 @@ user.revoke_roles(:accountant, :manager)
100
99
  user.revoke_all_roles
101
100
  ```
102
101
 
102
+ All role assignment methods return the list of roles currently assigned to the user.
103
+
103
104
  ### Role Queries
104
105
 
105
106
  ```rb
106
107
  # Check if user has any of the specified roles
107
108
  user.has_role?(:accountant, :manager)
108
109
 
109
- # Get user's roles
110
+ # Get user's roles in the global context
110
111
  user.roles
111
112
 
112
- # Get all roles grouped by context
113
+ # Get all user's roles grouped by context
113
114
  user.all_roles
115
+
116
+ # Get users with any of the specified roles
117
+ User.with_role(:admin, :manager)
114
118
  ```
115
119
 
116
120
  ## Role Management
@@ -119,24 +123,25 @@ user.all_roles
119
123
 
120
124
  ```rb
121
125
  # Create a new role
122
- Rabarber::Role.add(:admin)
126
+ Rabarber.create_role(:admin) # => true if created, false if already exists
123
127
 
124
128
  # Rename a role
125
- Rabarber::Role.rename(:admin, :administrator)
126
- Rabarber::Role.rename(:admin, :administrator, force: true) # Force if role is assigned to users
129
+ Rabarber.rename_role(:admin, :administrator) # => true if renamed, false if new name exists or role is assigned
130
+ Rabarber.rename_role(:admin, :administrator, force: true) # Force rename even if role is assigned
127
131
 
128
132
  # Remove a role
129
- Rabarber::Role.remove(:admin)
130
- Rabarber::Role.remove(:admin, force: true) # Force if role is assigned to users
133
+ Rabarber.delete_role(:admin) # => true if deleted, false if role is assigned
134
+ Rabarber.delete_role(:admin, force: true) # Force deletion even if role is assigned
131
135
 
132
- # List available roles
133
- Rabarber::Role.names
134
- Rabarber::Role.all_names # All roles grouped by context
136
+ # List available roles in the global context
137
+ Rabarber.roles
135
138
 
136
- # Get users assigned to a role
137
- Rabarber::Role.assignees(:admin)
139
+ # List all available roles grouped by context
140
+ Rabarber.all_roles
138
141
  ```
139
142
 
143
+ > **Note:** Some methods have been deprecated in favor of the new API shown above. The deprecated methods still work but will be removed in a future major version. See the [changelog](https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md#v520) for the complete list of deprecated methods and their new counterparts.
144
+
140
145
  ## Controller Authorization
141
146
 
142
147
  ### Basic Setup
@@ -155,7 +160,7 @@ class InvoicesController < ApplicationController
155
160
  end
156
161
  ```
157
162
 
158
- Authorization requires an authenticated user.
163
+ Authorization requires an authenticated user to be present.
159
164
 
160
165
  ### Skip Authorization
161
166
 
@@ -181,6 +186,10 @@ class TicketsController < ApplicationController
181
186
  def index
182
187
  # Accessible to admin, manager, and support roles
183
188
  end
189
+
190
+ def destroy
191
+ # Accessible to admin role only
192
+ end
184
193
  end
185
194
  ```
186
195
 
@@ -210,7 +219,7 @@ Omit roles to allow unrestricted access:
210
219
 
211
220
  ```rb
212
221
  class UnrestrictedController < ApplicationController
213
- grant_access # Allow all users
222
+ grant_access # Allow all users to access all actions
214
223
  end
215
224
 
216
225
  class MixedController < ApplicationController
@@ -244,7 +253,7 @@ class ApplicationController < ActionController::Base
244
253
  end
245
254
  ```
246
255
 
247
- By default, Rabarber will redirect back (HTML format) or return 403 (other formats).
256
+ By default, when unauthorized, Rabarber will redirect back (HTML format) or return 403 (other formats).
248
257
 
249
258
  ## Dynamic Rules
250
259
 
@@ -256,13 +265,13 @@ class OrdersController < ApplicationController
256
265
  grant_access roles: :manager, if: :company_manager?, unless: :suspended?
257
266
 
258
267
  # Proc-based conditions
259
- grant_access action: :show, roles: :client, if: -> { current_user.company_id == Order.find(params[:id]).company_id }
268
+ grant_access action: :show, roles: :client, if: -> { current_user.company == Order.find(params[:id]).company }
260
269
  def show
261
270
  # Accessible to company managers unless suspended, and to clients if the client's company matches the order's company
262
271
  end
263
272
 
264
273
  # Dynamic-only rules (no roles required, can be used with custom policies)
265
- grant_access action: :index, if: -> { OrdersPolicy.new(current_user).can_access?(:index) }
274
+ grant_access action: :index, if: -> { OrdersPolicy.new(current_user).index? }
266
275
  def index
267
276
  # Accessible to company managers unless suspended, and to users based on custom policy logic
268
277
  end
@@ -290,35 +299,52 @@ All Rabarber methods accept a `context` parameter, allowing you to work with rol
290
299
  user.assign_roles(:owner, context: project)
291
300
  user.assign_roles(:member, context: project)
292
301
 
293
- # Assign roles within a model class (e.g., project admin)
302
+ # Assign roles within a model class
294
303
  user.assign_roles(:admin, context: Project)
295
304
 
296
305
  # Check contextual roles
297
306
  user.has_role?(:owner, context: project)
298
307
  user.has_role?(:admin, context: Project)
299
308
 
300
- # Revoke roles within a specific context
309
+ # Revoke roles
301
310
  user.revoke_roles(:owner, context: project)
302
311
 
303
- # Get roles within context
304
- user.roles(context: project)
305
- Rabarber::Role.names(context: Project)
312
+ # Get user roles
313
+ user.roles(context: Project)
314
+
315
+ # Get users with a role
316
+ User.with_role(:member, context: project)
317
+ ```
318
+
319
+ ### Contextual Role Management
320
+
321
+ ```rb
322
+ # Create a new role within a context
323
+ Rabarber.create_role(:admin, context: Project)
324
+
325
+ # Rename a role within a context
326
+ Rabarber.rename_role(:admin, :owner, context: project)
327
+
328
+ # Remove a contextual role
329
+ Rabarber.delete_role(:admin, context: project)
330
+
331
+ # List available roles within a specific context
332
+ Rabarber.roles(context: project)
306
333
  ```
307
334
 
308
335
  ### Contextual Authorization
309
336
 
310
337
  ```rb
311
338
  class ProjectsController < ApplicationController
312
- # Class-based context
313
339
  grant_access roles: :admin, context: Project
314
340
 
315
- # Instance-based context (method)
341
+ # Method-based context resolution
316
342
  grant_access action: :show, roles: :member, context: :current_project
317
343
  def show
318
344
  # Accessible to Project admin and members of the current project
319
345
  end
320
346
 
321
- # Instance-based context (proc)
347
+ # Proc-based context resolution
322
348
  grant_access action: :update, roles: :owner, context: -> { Project.find(params[:id]) }
323
349
  def update
324
350
  # Accessible to Project admin and owner of the current project
@@ -332,6 +358,16 @@ class ProjectsController < ApplicationController
332
358
  end
333
359
  ```
334
360
 
361
+ ### Orphaned Context
362
+
363
+ When a context object is deleted from your database, its associated roles become orphaned and ignored by Rabarber.
364
+
365
+ To clean up orphaned context roles, use:
366
+
367
+ ```rb
368
+ Rabarber.prune
369
+ ```
370
+
335
371
  ### Context Migrations
336
372
 
337
373
  Handle context changes when models are renamed or removed. These are irreversible data migrations.
@@ -369,22 +405,23 @@ Use conditional rendering based on roles:
369
405
  </div>
370
406
  <% end %>
371
407
 
372
- <!-- With context -->
373
408
  <%= visible_to(:owner, context: @project) do %>
374
- <button>Delete Project</button>
409
+ <div class="project-owner-panel">
410
+ <!-- Content visible to project owners -->
411
+ </div>
375
412
  <% end %>
376
413
  ```
377
414
 
378
415
  ## Getting Help and Contributing
379
416
 
380
417
  ### Getting Help
381
- Have a question or need assistance? Open a discussion in our [discussions section](https://github.com/brownboxdev/rabarber/discussions) for:
418
+ Have a question or need assistance? Open a discussion in our [discussions section](https://github.com/enjaku4/rabarber/discussions) for:
382
419
  - Usage questions
383
420
  - Implementation guidance
384
421
  - Feature suggestions
385
422
 
386
423
  ### Reporting Issues
387
- Found a bug? Please [create an issue](https://github.com/brownboxdev/rabarber/issues) with:
424
+ Found a bug? Please [create an issue](https://github.com/enjaku4/rabarber/issues) with:
388
425
  - A clear description of the problem
389
426
  - Steps to reproduce the issue
390
427
  - Your environment details (Rails version, Ruby version, etc.)
@@ -393,21 +430,21 @@ Found a bug? Please [create an issue](https://github.com/brownboxdev/rabarber/is
393
430
  Ready to contribute? You can:
394
431
  - Fix bugs by submitting pull requests
395
432
  - Improve documentation
396
- - Add new features (please discuss first in our [discussions section](https://github.com/brownboxdev/rabarber/discussions))
433
+ - Add new features (please discuss first in our [discussions section](https://github.com/enjaku4/rabarber/discussions))
397
434
 
398
- Before contributing, please read the [contributing guidelines](https://github.com/brownboxdev/rabarber/blob/main/CONTRIBUTING.md)
435
+ Before contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md)
399
436
 
400
437
  ## License
401
438
 
402
- The gem is available as open source under the terms of the [MIT License](https://github.com/brownboxdev/rabarber/blob/main/LICENSE.txt).
439
+ The gem is available as open source under the terms of the [MIT License](https://github.com/enjaku4/rabarber/blob/main/LICENSE.txt).
403
440
 
404
441
  ## Code of Conduct
405
442
 
406
- Everyone interacting in the Rabarber project is expected to follow the [code of conduct](https://github.com/brownboxdev/rabarber/blob/main/CODE_OF_CONDUCT.md).
443
+ Everyone interacting in the Rabarber project is expected to follow the [code of conduct](https://github.com/enjaku4/rabarber/blob/main/CODE_OF_CONDUCT.md).
407
444
 
408
445
  ## Old Versions
409
446
 
410
447
  Only the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:
411
448
 
412
- [v4.x.x](https://github.com/brownboxdev/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/brownboxdev/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
413
- | [v2.x.x](https://github.com/brownboxdev/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/brownboxdev/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
449
+ [v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
450
+ | [v2.x.x](https://github.com/enjaku4/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/enjaku4/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Boolean < Base
5
+ class Boolean < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Strict::Bool
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Context < Base
5
+ class Context < Rabarber::Inputs::Base
6
6
  def resolve
7
7
  case context = process
8
8
  when nil
@@ -3,7 +3,7 @@
3
3
  module Rabarber
4
4
  module Inputs
5
5
  module Contexts
6
- class Authorizational < Context
6
+ class Authorizational < Rabarber::Inputs::Context
7
7
  private
8
8
 
9
9
  def type = self.class::Coercible::Symbol.constrained(min_size: 1) | self.class::Instance(Proc) | super
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class DynamicRule < Base
5
+ class DynamicRule < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Coercible::Symbol.constrained(min_size: 1) | self.class::Instance(Proc)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Model < Base
5
+ class Model < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Strict::Class.constructor { _1.try(:safe_constantize) }.constrained(lt: ActiveRecord::Base)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class NonEmptyString < Base
5
+ class NonEmptyString < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Strict::String.constrained(min_size: 1)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Role < Base
5
+ class Role < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Coercible::Symbol.constrained(min_size: 1, format: /\A[a-z0-9_]+\z/)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Roles < Base
5
+ class Roles < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Rabarber
4
4
  module Inputs
5
- class Symbol < Base
5
+ class Symbol < Rabarber::Inputs::Base
6
6
  private
7
7
 
8
8
  def type = self.class::Coercible::Symbol.constrained(min_size: 1)
@@ -1,17 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- module HasRoles
4
+ module Roleable
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- raise Rabarber::Error, "Rabarber::HasRoles can only be included once" if defined?(@@included) && @@included != name
8
+ raise Rabarber::Error, "Rabarber::Roleable can only be included once" if defined?(@@included) && @@included != name
9
9
 
10
10
  @@included = name
11
11
 
12
12
  has_and_belongs_to_many :rabarber_roles, class_name: "Rabarber::Role",
13
13
  foreign_key: "roleable_id",
14
14
  join_table: "rabarber_roles_roleables"
15
+
16
+ scope :with_role, -> (*role_names, context: nil) {
17
+ joins(:rabarber_roles).where(
18
+ rabarber_roles: { name: process_role_names(role_names), **process_context(context) }
19
+ ).distinct
20
+ }
21
+
22
+ class << self
23
+ def process_role_names(role_names)
24
+ Rabarber::Inputs::Roles.new(
25
+ role_names,
26
+ message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{role_names.inspect}"
27
+ ).process
28
+ end
29
+
30
+ def process_context(context)
31
+ Rabarber::Inputs::Context.new(
32
+ context,
33
+ error: Rabarber::InvalidContextError,
34
+ message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
35
+ ).resolve
36
+ end
37
+ end
38
+ delegate :process_role_names, :process_context, to: :class, private: true
15
39
  end
16
40
 
17
41
  def roles(context: nil)
@@ -64,13 +88,15 @@ module Rabarber
64
88
  end
65
89
 
66
90
  def revoke_all_roles
67
- return if rabarber_roles.none?
91
+ return [] if rabarber_roles.none?
68
92
 
69
93
  contexts = all_roles.keys.map { process_context(_1) }
70
94
 
71
95
  rabarber_roles.clear
72
96
 
73
97
  delete_roleable_cache(contexts:)
98
+
99
+ []
74
100
  end
75
101
 
76
102
  private
@@ -80,21 +106,6 @@ module Rabarber
80
106
  new_roles.each { |role_name| Rabarber::Role.create!(name: role_name, **context) }
81
107
  end
82
108
 
83
- def process_role_names(role_names)
84
- Rabarber::Inputs::Roles.new(
85
- role_names,
86
- message: "Expected an array of symbols or strings containing only lowercase letters, numbers, and underscores, got #{role_names.inspect}"
87
- ).process
88
- end
89
-
90
- def process_context(context)
91
- Rabarber::Inputs::Context.new(
92
- context,
93
- error: Rabarber::InvalidContextError,
94
- message: "Expected an instance of ActiveRecord model, a Class, or nil, got #{context.inspect}"
95
- ).resolve
96
- end
97
-
98
109
  def delete_roleable_cache(contexts:)
99
110
  Rabarber::Core::Cache.delete(*contexts.map { [roleable_id, _1] }, [roleable_id, :all])
100
111
  end
@@ -12,10 +12,14 @@ module Rabarber
12
12
 
13
13
  class << self
14
14
  def names(context: nil)
15
+ deprecation_warning("names", "Rabarber.roles")
16
+
15
17
  where(process_context(context)).pluck(:name).map(&:to_sym)
16
18
  end
17
19
 
18
20
  def all_names
21
+ deprecation_warning("all_names", "Rabarber.all_roles")
22
+
19
23
  includes(:context).each_with_object({}) do |role, hash|
20
24
  (hash[role.context] ||= []) << role.name.to_sym
21
25
  rescue ActiveRecord::RecordNotFound
@@ -26,6 +30,8 @@ module Rabarber
26
30
  end
27
31
 
28
32
  def add(name, context: nil)
33
+ deprecation_warning("add", "Rabarber.create_role")
34
+
29
35
  name = process_role_name(name)
30
36
  processed_context = process_context(context)
31
37
 
@@ -35,6 +41,8 @@ module Rabarber
35
41
  end
36
42
 
37
43
  def rename(old_name, new_name, context: nil, force: false)
44
+ deprecation_warning("rename", "Rabarber.rename_role")
45
+
38
46
  processed_context = process_context(context)
39
47
  role = find_by(name: process_role_name(old_name), **processed_context)
40
48
 
@@ -50,6 +58,8 @@ module Rabarber
50
58
  end
51
59
 
52
60
  def remove(name, context: nil, force: false)
61
+ deprecation_warning("remove", "Rabarber.delete_role")
62
+
53
63
  processed_context = process_context(context)
54
64
  role = find_by(name: process_role_name(name), **processed_context)
55
65
 
@@ -63,11 +73,36 @@ module Rabarber
63
73
  end
64
74
 
65
75
  def assignees(name, context: nil)
76
+ deprecation_warning("assignees", "#{Rabarber::Configuration.user_model_name}.with_role")
77
+
66
78
  find_by(name: process_role_name(name), **process_context(context))&.roleables || Rabarber::Configuration.user_model.none
67
79
  end
68
80
 
81
+ def prune
82
+ orphaned_roles = where.not(context_id: nil).includes(:context).filter_map do |role|
83
+ !role.context
84
+ rescue ActiveRecord::RecordNotFound
85
+ role.id
86
+ end
87
+
88
+ return if orphaned_roles.empty?
89
+
90
+ ActiveRecord::Base.transaction do
91
+ ActiveRecord::Base.connection.execute(
92
+ ActiveRecord::Base.sanitize_sql(
93
+ ["DELETE FROM rabarber_roles_roleables WHERE role_id IN (?)", orphaned_roles]
94
+ )
95
+ )
96
+ where(id: orphaned_roles).delete_all
97
+ end
98
+ end
99
+
69
100
  private
70
101
 
102
+ def deprecation_warning(method, alternative)
103
+ ActiveSupport::Deprecation.new("6.0.0", "rabarber").warn("`Rabarber::Role.#{method}` method is deprecated and will be removed in the next major version, use `#{alternative}` instead.") if caller_locations.map(&:label).exclude?(alternative)
104
+ end
105
+
71
106
  def delete_roleables_cache(role, context:)
72
107
  Rabarber::Core::Cache.delete(*role.roleables.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
73
108
  end
@@ -6,26 +6,18 @@ module Rabarber
6
6
  class Railtie < Rails::Railtie
7
7
  initializer "rabarber.to_prepare" do |app|
8
8
  app.config.to_prepare do
9
- unless app.config.eager_load
10
- Rabarber::Core::Permissions.reset!
11
- ApplicationController.class_eval do
12
- before_action :check_integrity
13
-
14
- private
15
-
16
- def check_integrity
17
- Rabarber::Core::IntegrityChecker.run!
18
- end
9
+ if ActiveRecord::Base.connection.data_source_exists?("rabarber_roles")
10
+ Rabarber::Role.where.not(context_type: nil).distinct.pluck(:context_type).each do |context_class|
11
+ context_class.constantize
12
+ rescue NameError => e
13
+ raise Rabarber::Error, "Context not found: class `#{e.name}` may have been renamed or deleted"
19
14
  end
20
15
  end
21
- user_model = Rabarber::Configuration.user_model
22
- user_model.include Rabarber::HasRoles unless user_model < Rabarber::HasRoles
23
- end
24
- end
25
16
 
26
- initializer "rabarber.after_initialize" do |app|
27
- app.config.after_initialize do
28
- Rabarber::Core::IntegrityChecker.run! if app.config.eager_load
17
+ Rabarber::Core::Permissions.reset! unless app.config.eager_load
18
+
19
+ user_model = Rabarber::Configuration.user_model
20
+ user_model.include Rabarber::Roleable unless user_model < Rabarber::Roleable
29
21
  end
30
22
  end
31
23
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "5.1.2"
4
+ VERSION = "5.2.0"
5
5
  end
data/lib/rabarber.rb CHANGED
@@ -27,6 +27,15 @@ module Rabarber
27
27
 
28
28
  delegate :configure, to: Rabarber::Configuration
29
29
  module_function :configure
30
+
31
+ class << self
32
+ def roles(context: nil) = Rabarber::Role.names(context:)
33
+ def all_roles = Rabarber::Role.all_names
34
+ def create_role(name, context: nil) = Rabarber::Role.add(name, context:)
35
+ def rename_role(old_name, new_name, context: nil, force: false) = Rabarber::Role.rename(old_name, new_name, context:, force:)
36
+ def delete_role(name, context: nil, force: false) = Rabarber::Role.remove(name, context:, force:)
37
+ def prune = Rabarber::Role.prune
38
+ end
30
39
  end
31
40
 
32
41
  require_relative "rabarber/core/cache"
@@ -36,10 +45,9 @@ require_relative "rabarber/core/roleable"
36
45
  require_relative "rabarber/controllers/concerns/authorization"
37
46
  require_relative "rabarber/helpers/helpers"
38
47
  require_relative "rabarber/helpers/migration_helpers"
39
- require_relative "rabarber/models/concerns/has_roles"
48
+ require_relative "rabarber/models/concerns/roleable"
40
49
  require_relative "rabarber/models/role"
41
50
 
42
51
  require_relative "rabarber/core/permissions"
43
- require_relative "rabarber/core/integrity_checker"
44
52
 
45
53
  require_relative "rabarber/railtie"
data/rabarber.gemspec CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |spec|
6
6
  spec.name = "rabarber"
7
7
  spec.version = Rabarber::VERSION
8
8
  spec.authors = ["enjaku4", "trafium"]
9
- spec.homepage = "https://github.com/brownboxdev/rabarber"
9
+ spec.homepage = "https://github.com/enjaku4/rabarber"
10
10
  spec.metadata["homepage_uri"] = spec.homepage
11
11
  spec.metadata["source_code_uri"] = spec.homepage
12
12
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
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: 5.1.2
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4
@@ -72,7 +72,6 @@ files:
72
72
  - lib/rabarber/controllers/concerns/authorization.rb
73
73
  - lib/rabarber/core/access.rb
74
74
  - lib/rabarber/core/cache.rb
75
- - lib/rabarber/core/integrity_checker.rb
76
75
  - lib/rabarber/core/permissions.rb
77
76
  - lib/rabarber/core/roleable.rb
78
77
  - lib/rabarber/core/rule.rb
@@ -88,18 +87,18 @@ files:
88
87
  - lib/rabarber/inputs/role.rb
89
88
  - lib/rabarber/inputs/roles.rb
90
89
  - lib/rabarber/inputs/symbol.rb
91
- - lib/rabarber/models/concerns/has_roles.rb
90
+ - lib/rabarber/models/concerns/roleable.rb
92
91
  - lib/rabarber/models/role.rb
93
92
  - lib/rabarber/railtie.rb
94
93
  - lib/rabarber/version.rb
95
94
  - rabarber.gemspec
96
- homepage: https://github.com/brownboxdev/rabarber
95
+ homepage: https://github.com/enjaku4/rabarber
97
96
  licenses:
98
97
  - MIT
99
98
  metadata:
100
- homepage_uri: https://github.com/brownboxdev/rabarber
101
- source_code_uri: https://github.com/brownboxdev/rabarber
102
- changelog_uri: https://github.com/brownboxdev/rabarber/blob/main/CHANGELOG.md
99
+ homepage_uri: https://github.com/enjaku4/rabarber
100
+ source_code_uri: https://github.com/enjaku4/rabarber
101
+ changelog_uri: https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md
103
102
  rubygems_mfa_required: 'true'
104
103
  rdoc_options: []
105
104
  require_paths:
@@ -118,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
118
117
  - !ruby/object:Gem::Version
119
118
  version: '0'
120
119
  requirements: []
121
- rubygems_version: 3.6.7
120
+ rubygems_version: 3.7.1
122
121
  specification_version: 4
123
122
  summary: Simple role-based authorization library for Ruby on Rails
124
123
  test_files: []
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Rabarber
4
- module Core
5
- module IntegrityChecker
6
- extend self
7
-
8
- def run!
9
- check_for_missing_class_context
10
- prune_missing_instance_context
11
- end
12
-
13
- private
14
-
15
- def check_for_missing_class_context
16
- Rabarber::Role.where.not(context_type: nil).distinct.pluck(:context_type).each do |context_class|
17
- context_class.constantize
18
- rescue NameError => e
19
- raise Rabarber::Error, "Context not found: class #{e.name} may have been renamed or deleted"
20
- end
21
- end
22
-
23
- def prune_missing_instance_context
24
- ids = Rabarber::Role.where.not(context_id: nil).includes(:context).filter_map do |role|
25
- role.context
26
- nil
27
- rescue ActiveRecord::RecordNotFound
28
- role.id
29
- end
30
-
31
- return if ids.empty?
32
-
33
- ActiveRecord::Base.transaction do
34
- ActiveRecord::Base.connection.execute(
35
- ActiveRecord::Base.sanitize_sql(
36
- ["DELETE FROM rabarber_roles_roleables WHERE role_id IN (?)", ids]
37
- )
38
- )
39
- Rabarber::Role.where(id: ids).delete_all
40
- end
41
- end
42
- end
43
- end
44
- end