rabarber 5.1.2 → 5.2.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 +4 -4
- data/CHANGELOG.md +26 -3
- data/README.md +76 -39
- data/lib/rabarber/inputs/boolean.rb +1 -1
- data/lib/rabarber/inputs/context.rb +1 -1
- data/lib/rabarber/inputs/contexts/authorizational.rb +1 -1
- data/lib/rabarber/inputs/dynamic_rule.rb +1 -1
- data/lib/rabarber/inputs/model.rb +1 -1
- data/lib/rabarber/inputs/non_empty_string.rb +1 -1
- data/lib/rabarber/inputs/role.rb +1 -1
- data/lib/rabarber/inputs/roles.rb +1 -1
- data/lib/rabarber/inputs/symbol.rb +1 -1
- data/lib/rabarber/models/concerns/{has_roles.rb → roleable.rb} +29 -18
- data/lib/rabarber/models/role.rb +35 -0
- data/lib/rabarber/railtie.rb +15 -17
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +10 -2
- data/rabarber.gemspec +1 -1
- metadata +7 -8
- data/lib/rabarber/core/integrity_checker.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86f27dfa62e1520493b6803b3e7fcc55025ff07af38d3b7f5fd60b0d8bcbe548
|
4
|
+
data.tar.gz: 7ef5aacdb3f9fb3a95b799307a74c7e9764167613cfa1506f4bf75205af84ecc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15ea45c42082ba4c4832c6dc58b2f70a2b35b5d206bf428faae89ce4646a5a666edd025f434d51b8c531077e4425f2b7e8a2b9208b412495971777bee79d1916
|
7
|
+
data.tar.gz: 9f7883fe80aa5a24ab7ccbe6fe751c28f08138b1e7f291ef831572c3530d6b0bdfe9e99ba281f9ee0dd4fcdfd56119713b87779fa455651d3f3d2a1b55733008
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
## v5.2.1
|
2
|
+
|
3
|
+
### Bugs:
|
4
|
+
|
5
|
+
- Fixed error raised on Rails console commands when the database was not yet set up
|
6
|
+
|
7
|
+
## v5.2.0
|
8
|
+
|
9
|
+
### Features:
|
10
|
+
|
11
|
+
- Added `Rabarber.prune` method to allow manual pruning of roles with deleted contexts
|
12
|
+
|
13
|
+
### Misc:
|
14
|
+
|
15
|
+
- `revoke_all_roles` now returns an empty array instead of `nil` for consistency with other role assignment methods
|
16
|
+
- Deprecated the following role management methods in favor of new counterparts:
|
17
|
+
- `Rabarber::Role.names` -> `Rabarber.roles`
|
18
|
+
- `Rabarber::Role.all_names` -> `Rabarber.all_roles`
|
19
|
+
- `Rabarber::Role.add` -> `Rabarber.create_role`
|
20
|
+
- `Rabarber::Role.rename` -> `Rabarber.rename_role`
|
21
|
+
- `Rabarber::Role.remove` -> `Rabarber.delete_role`
|
22
|
+
- `Rabarber::Role.assignees` -> `User.with_role`
|
23
|
+
|
1
24
|
## v5.1.2
|
2
25
|
|
3
26
|
### Misc:
|
@@ -35,7 +58,7 @@
|
|
35
58
|
- Added `with_authorization` method for more granular authorization control
|
36
59
|
- `Rabarber::Role.rename` and `Rabarber::Role.remove` now require the role to exist
|
37
60
|
|
38
|
-
To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/
|
61
|
+
To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/77)
|
39
62
|
|
40
63
|
### Features:
|
41
64
|
|
@@ -140,7 +163,7 @@ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/b
|
|
140
163
|
|
141
164
|
- Changed Rabarber roles table structure
|
142
165
|
|
143
|
-
To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/
|
166
|
+
To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/58)
|
144
167
|
|
145
168
|
### Features:
|
146
169
|
|
@@ -164,7 +187,7 @@ To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/b
|
|
164
187
|
- Replaced `when_unauthorized` configuration option with an overridable controller method
|
165
188
|
- Renamed `Rabarber::Role.assignees_for` method to `Rabarber::Role.assignees`
|
166
189
|
|
167
|
-
To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/
|
190
|
+
To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/52)
|
168
191
|
|
169
192
|
### Features:
|
170
193
|
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Rabarber: Role-Based Authorization for Rails
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/rabarber)
|
4
|
-
[](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
|
126
|
+
Rabarber.create_role(:admin) # => true if created, false if already exists
|
123
127
|
|
124
128
|
# Rename a role
|
125
|
-
Rabarber
|
126
|
-
Rabarber
|
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
|
130
|
-
Rabarber
|
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
|
134
|
-
Rabarber::Role.all_names # All roles grouped by context
|
136
|
+
# List available roles in the global context
|
137
|
+
Rabarber.roles
|
135
138
|
|
136
|
-
#
|
137
|
-
Rabarber
|
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.
|
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).
|
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
|
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
|
309
|
+
# Revoke roles
|
301
310
|
user.revoke_roles(:owner, context: project)
|
302
311
|
|
303
|
-
# Get roles
|
304
|
-
user.roles(context:
|
305
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
<
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
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/
|
413
|
-
| [v2.x.x](https://github.com/
|
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)
|
data/lib/rabarber/inputs/role.rb
CHANGED
@@ -1,17 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Rabarber
|
4
|
-
module
|
4
|
+
module Roleable
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
raise Rabarber::Error, "Rabarber::
|
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
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -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
|
data/lib/rabarber/railtie.rb
CHANGED
@@ -4,28 +4,26 @@ require "rails/railtie"
|
|
4
4
|
|
5
5
|
module Rabarber
|
6
6
|
class Railtie < Rails::Railtie
|
7
|
+
def self.table_exists?
|
8
|
+
ActiveRecord::Base.connection.data_source_exists?("rabarber_roles")
|
9
|
+
rescue ActiveRecord::NoDatabaseError, ActiveRecord::ConnectionNotEstablished
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
7
13
|
initializer "rabarber.to_prepare" do |app|
|
8
14
|
app.config.to_prepare do
|
9
|
-
|
10
|
-
Rabarber::
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def check_integrity
|
17
|
-
Rabarber::Core::IntegrityChecker.run!
|
18
|
-
end
|
15
|
+
if Rabarber::Railtie.table_exists?
|
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"
|
19
20
|
end
|
20
21
|
end
|
21
|
-
user_model = Rabarber::Configuration.user_model
|
22
|
-
user_model.include Rabarber::HasRoles unless user_model < Rabarber::HasRoles
|
23
|
-
end
|
24
|
-
end
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
Rabarber::
|
23
|
+
Rabarber::Core::Permissions.reset! unless app.config.eager_load
|
24
|
+
|
25
|
+
user_model = Rabarber::Configuration.user_model
|
26
|
+
user_model.include Rabarber::Roleable unless user_model < Rabarber::Roleable
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
data/lib/rabarber/version.rb
CHANGED
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/
|
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/
|
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
|
4
|
+
version: 5.2.1
|
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/
|
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/
|
95
|
+
homepage: https://github.com/enjaku4/rabarber
|
97
96
|
licenses:
|
98
97
|
- MIT
|
99
98
|
metadata:
|
100
|
-
homepage_uri: https://github.com/
|
101
|
-
source_code_uri: https://github.com/
|
102
|
-
changelog_uri: https://github.com/
|
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.
|
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
|