rabarber 5.2.4 → 6.0.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 +4 -4
- data/CHANGELOG.md +26 -1
- data/README.md +128 -106
- data/lib/rabarber/configuration.rb +50 -36
- data/lib/rabarber/controllers/concerns/authorization.rb +2 -2
- data/lib/rabarber/core/cache.rb +25 -12
- data/lib/rabarber/inputs/base.rb +4 -9
- data/lib/rabarber/inputs/boolean.rb +1 -1
- data/lib/rabarber/inputs/context.rb +7 -8
- data/lib/rabarber/inputs/contexts/authorizational.rb +13 -1
- data/lib/rabarber/inputs/dynamic_rule.rb +5 -1
- data/lib/rabarber/inputs/model.rb +6 -1
- data/lib/rabarber/inputs/non_empty_string.rb +3 -1
- data/lib/rabarber/inputs/non_empty_symbol.rb +15 -0
- data/lib/rabarber/inputs/role.rb +5 -1
- data/lib/rabarber/inputs/roles.rb +4 -2
- data/lib/rabarber/models/concerns/roleable.rb +4 -4
- data/lib/rabarber/models/role.rb +5 -29
- data/lib/rabarber/models/role_management.rb +12 -0
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +4 -10
- data/rabarber.gemspec +6 -6
- metadata +14 -40
- data/lib/rabarber/inputs/symbol.rb +0 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b63470cbdf375117e5c901ae46992d0fb85868acedacde3ab17472437d8f4a10
|
|
4
|
+
data.tar.gz: bba19d48ebad8a6440c70e7555d5befcc9c64f67c0a43f0408cd966f9d24aa15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5b43430eb2fc105add42baa08aa12349137f1aab6021d371400cc48c48c134db4c20bd81e3cbff043e63bf6f22161d6645ab57619090bf1c08b905f6be60fbe1
|
|
7
|
+
data.tar.gz: cb648ea4388e067d1f06d57f2ba9b45fb7139df3a5a1cbb102ae42441730a2a83749b116d468fb0c6d3b9b0be419c010a62c73c3c0be2d90752086aa5f4e8837
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## v6.0.0
|
|
2
|
+
|
|
3
|
+
### Breaking:
|
|
4
|
+
|
|
5
|
+
- Removed methods deprecated in v5.2.0 from the public API
|
|
6
|
+
- Dropped support for Ruby 3.2
|
|
7
|
+
- Dropped support for Rails 7.1
|
|
8
|
+
|
|
9
|
+
### Bugs:
|
|
10
|
+
|
|
11
|
+
- Fixed cache clearing not working with Memcached
|
|
12
|
+
|
|
13
|
+
### Misc:
|
|
14
|
+
|
|
15
|
+
- Optimized cache key generation
|
|
16
|
+
- Dropped some unnecessary dependencies
|
|
17
|
+
|
|
18
|
+
To upgrade to v6.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/101)
|
|
19
|
+
|
|
20
|
+
## v5.2.5
|
|
21
|
+
|
|
22
|
+
### Misc:
|
|
23
|
+
|
|
24
|
+
- Added support for Ruby 4
|
|
25
|
+
|
|
1
26
|
## v5.2.4
|
|
2
27
|
|
|
3
28
|
### Misc:
|
|
@@ -91,7 +116,7 @@ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/e
|
|
|
91
116
|
|
|
92
117
|
- Optimized various parts of the code and database queries for improved performance
|
|
93
118
|
- Streamlined the authorization process by requiring the user to be authenticated before access is verified
|
|
94
|
-
- Rabarber now skips roles with missing instance context
|
|
119
|
+
- Rabarber now skips roles with missing instance context; missing class context still raises errors
|
|
95
120
|
|
|
96
121
|
## v4.1.4
|
|
97
122
|
|
data/README.md
CHANGED
|
@@ -5,35 +5,23 @@
|
|
|
5
5
|
[](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
Rabarber is a role-based authorization library for Ruby on Rails
|
|
8
|
+
Rabarber is a role-based authorization library for Ruby on Rails. It provides a set of tools for managing user roles and defining access rules, with support for multi-tenancy through context.
|
|
9
9
|
|
|
10
10
|
**Example of Usage:**
|
|
11
11
|
|
|
12
|
-
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can
|
|
13
|
-
|
|
14
|
-
And here's how your controller might look:
|
|
12
|
+
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access and manage invoice data, while the role `analyst` can only view it. Here's how you'd define that with Rabarber:
|
|
15
13
|
|
|
16
14
|
```rb
|
|
17
15
|
class InvoicesController < ApplicationController
|
|
18
|
-
grant_access roles: :
|
|
16
|
+
grant_access roles: :accountant
|
|
19
17
|
|
|
20
|
-
grant_access action: :index, roles:
|
|
18
|
+
grant_access action: :index, roles: :analyst
|
|
21
19
|
def index
|
|
22
|
-
# Accessible to
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
grant_access action: :show, roles: :accountant
|
|
26
|
-
def show
|
|
27
|
-
# Accessible to accountants
|
|
20
|
+
# Accessible to accountant and analyst
|
|
28
21
|
end
|
|
29
22
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# Accessible to analysts
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def destroy
|
|
36
|
-
# Accessible to admins only
|
|
23
|
+
def update
|
|
24
|
+
# Accessible to accountant only
|
|
37
25
|
end
|
|
38
26
|
end
|
|
39
27
|
```
|
|
@@ -44,10 +32,11 @@ end
|
|
|
44
32
|
- [Installation](#installation)
|
|
45
33
|
- [Configuration](#configuration)
|
|
46
34
|
- [User Role Methods](#user-role-methods)
|
|
47
|
-
- [Role Management](#role-management)
|
|
48
|
-
- [
|
|
49
|
-
- [Dynamic Rules](#dynamic-rules)
|
|
50
|
-
- [
|
|
35
|
+
- [Direct Role Management](#direct-role-management)
|
|
36
|
+
- [Authorization](#authorization)
|
|
37
|
+
- [Dynamic Authorization Rules](#dynamic-authorization-rules)
|
|
38
|
+
- [When Unauthorized](#when-unauthorized)
|
|
39
|
+
- [Context / Multi-tenancy](#context--multi-tenancy)
|
|
51
40
|
- [View Helpers](#view-helpers)
|
|
52
41
|
|
|
53
42
|
**Community Resources:**
|
|
@@ -70,7 +59,7 @@ Install the gem:
|
|
|
70
59
|
bundle install
|
|
71
60
|
```
|
|
72
61
|
|
|
73
|
-
Generate the migration
|
|
62
|
+
Generate the migration to store roles (replace `users` with your table name if different):
|
|
74
63
|
|
|
75
64
|
```shell
|
|
76
65
|
# For standard integer IDs
|
|
@@ -88,17 +77,20 @@ rails db:migrate
|
|
|
88
77
|
|
|
89
78
|
## Configuration
|
|
90
79
|
|
|
91
|
-
|
|
80
|
+
Create an initializer to customize Rabarber's behavior (optional):
|
|
92
81
|
|
|
93
82
|
```rb
|
|
94
83
|
Rabarber.configure do |config|
|
|
95
|
-
|
|
96
|
-
config.
|
|
97
|
-
|
|
84
|
+
# Enable/disable role caching (default: true)
|
|
85
|
+
config.cache_enabled = true
|
|
86
|
+
# Method to access current user (default: :current_user)
|
|
87
|
+
config.current_user_method = :current_user
|
|
88
|
+
# User model name (default: "User")
|
|
89
|
+
config.user_model_name = "User"
|
|
98
90
|
end
|
|
99
91
|
```
|
|
100
92
|
|
|
101
|
-
Roles are cached by default for performance.
|
|
93
|
+
Roles are cached by default for better performance. Clear the cache manually when needed:
|
|
102
94
|
|
|
103
95
|
```rb
|
|
104
96
|
Rabarber::Cache.clear
|
|
@@ -112,19 +104,19 @@ Your user model is automatically augmented with role management methods:
|
|
|
112
104
|
|
|
113
105
|
```rb
|
|
114
106
|
# Assign roles (creates roles if they don't exist)
|
|
115
|
-
user.assign_roles(:
|
|
107
|
+
user.assign_roles(:admin, :manager)
|
|
116
108
|
|
|
117
|
-
# Assign only existing roles
|
|
109
|
+
# Assign only existing roles (don't create new ones)
|
|
118
110
|
user.assign_roles(:accountant, :manager, create_new: false)
|
|
119
111
|
|
|
120
112
|
# Revoke specific roles
|
|
121
|
-
user.revoke_roles(:
|
|
113
|
+
user.revoke_roles(:admin, :manager)
|
|
122
114
|
|
|
123
115
|
# Revoke all roles
|
|
124
116
|
user.revoke_all_roles
|
|
125
117
|
```
|
|
126
118
|
|
|
127
|
-
All role assignment methods return the list of roles currently assigned to the user.
|
|
119
|
+
All role assignment and revocation methods return the list of roles currently assigned to the user.
|
|
128
120
|
|
|
129
121
|
### Role Queries
|
|
130
122
|
|
|
@@ -142,21 +134,28 @@ user.all_roles
|
|
|
142
134
|
User.with_role(:admin, :manager)
|
|
143
135
|
```
|
|
144
136
|
|
|
145
|
-
## Role Management
|
|
137
|
+
## Direct Role Management
|
|
146
138
|
|
|
147
|
-
|
|
139
|
+
You can also directly manage roles available in the application:
|
|
148
140
|
|
|
149
141
|
```rb
|
|
150
142
|
# Create a new role
|
|
151
|
-
Rabarber.create_role(:admin)
|
|
143
|
+
Rabarber.create_role(:admin)
|
|
144
|
+
# => true if created, false if already exists
|
|
152
145
|
|
|
153
146
|
# Rename a role
|
|
154
|
-
Rabarber.rename_role(:admin, :administrator)
|
|
155
|
-
|
|
147
|
+
Rabarber.rename_role(:admin, :administrator)
|
|
148
|
+
# => true if renamed, false if new name exists or role is assigned
|
|
149
|
+
|
|
150
|
+
# Force rename even if role is assigned
|
|
151
|
+
Rabarber.rename_role(:admin, :administrator, force: true)
|
|
156
152
|
|
|
157
153
|
# Remove a role
|
|
158
|
-
Rabarber.delete_role(:admin)
|
|
159
|
-
|
|
154
|
+
Rabarber.delete_role(:admin)
|
|
155
|
+
# => true if deleted, false if role is assigned
|
|
156
|
+
|
|
157
|
+
# Force deletion even if role is assigned
|
|
158
|
+
Rabarber.delete_role(:admin, force: true)
|
|
160
159
|
|
|
161
160
|
# List available roles in the global context
|
|
162
161
|
Rabarber.roles
|
|
@@ -165,41 +164,42 @@ Rabarber.roles
|
|
|
165
164
|
Rabarber.all_roles
|
|
166
165
|
```
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
## Authorization
|
|
169
168
|
|
|
170
|
-
|
|
169
|
+
### Setup
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Include the authorization module and configure protection:
|
|
171
|
+
Include `Rabarber::Authorization` module in your controllers and configure authorization:
|
|
175
172
|
|
|
176
173
|
```rb
|
|
177
174
|
class ApplicationController < ActionController::Base
|
|
178
175
|
include Rabarber::Authorization
|
|
179
176
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
class InvoicesController < ApplicationController
|
|
184
|
-
with_authorization only: [:update, :destroy] # Selective authorization
|
|
177
|
+
# Enable authorization check for all actions in all controllers by default
|
|
178
|
+
with_authorization
|
|
185
179
|
end
|
|
186
180
|
```
|
|
187
181
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
### Skip Authorization
|
|
191
|
-
|
|
192
|
-
You can also selectively skip authorization:
|
|
182
|
+
You can also enable authorization checks selectively. Both `with_authorization` and `skip_authorization` work exactly the same as Rails' `before_action` and `skip_before_action` methods:
|
|
193
183
|
|
|
194
184
|
```rb
|
|
195
185
|
class TicketsController < ApplicationController
|
|
196
|
-
|
|
186
|
+
# Skip authorization for specific actions
|
|
187
|
+
skip_authorization only: [:index, :show]
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
class InvoicesController < ApplicationController
|
|
191
|
+
# Enable authorization for all actions except index
|
|
192
|
+
with_authorization except: [:index]
|
|
197
193
|
end
|
|
198
194
|
```
|
|
199
195
|
|
|
196
|
+
Authorization requires an authenticated user. Rabarber will raise an error if no user is found via the configured `current_user_method`. Ensure authentication happens before authorization.
|
|
197
|
+
|
|
200
198
|
### Authorization Rules
|
|
201
199
|
|
|
202
|
-
|
|
200
|
+
Rabarber follows a deny-by-default principle: if no `grant_access` rule is defined for an action or controller, access is denied to everyone.
|
|
201
|
+
|
|
202
|
+
Define authorization rules using `grant_access`:
|
|
203
203
|
|
|
204
204
|
```rb
|
|
205
205
|
class TicketsController < ApplicationController
|
|
@@ -218,17 +218,17 @@ class TicketsController < ApplicationController
|
|
|
218
218
|
end
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
Rules are additive across inheritance chains and for the same actions:
|
|
221
|
+
Authorization rules are additive - they combine across inheritance chains and when defined multiple times for the same action or controller:
|
|
224
222
|
|
|
225
223
|
```rb
|
|
226
224
|
class BaseController < ApplicationController
|
|
227
|
-
|
|
225
|
+
# Admin can access everything
|
|
226
|
+
grant_access roles: :admin
|
|
228
227
|
end
|
|
229
228
|
|
|
230
229
|
class InvoicesController < BaseController
|
|
231
|
-
|
|
230
|
+
# Accountant can also access InvoicesController (along with admin)
|
|
231
|
+
grant_access roles: :accountant
|
|
232
232
|
|
|
233
233
|
grant_access action: :index, roles: :manager
|
|
234
234
|
grant_access action: :index, roles: :supervisor
|
|
@@ -238,86 +238,106 @@ class InvoicesController < BaseController
|
|
|
238
238
|
end
|
|
239
239
|
```
|
|
240
240
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Omit roles to allow unrestricted access:
|
|
241
|
+
It's possible to omit roles to allow unrestricted access:
|
|
244
242
|
|
|
245
243
|
```rb
|
|
246
244
|
class UnrestrictedController < ApplicationController
|
|
247
|
-
|
|
245
|
+
# Allow all users to access all actions
|
|
246
|
+
grant_access
|
|
248
247
|
end
|
|
249
248
|
|
|
250
249
|
class MixedController < ApplicationController
|
|
251
|
-
|
|
250
|
+
# Unrestricted index action
|
|
251
|
+
grant_access action: :index
|
|
252
252
|
def index
|
|
253
253
|
# Accessible to all users
|
|
254
254
|
end
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
# Restricted show action
|
|
257
|
+
grant_access action: :show, roles: :member
|
|
257
258
|
def show
|
|
258
259
|
# Accessible to members only
|
|
259
260
|
end
|
|
260
261
|
end
|
|
261
262
|
```
|
|
262
263
|
|
|
263
|
-
|
|
264
|
+
## Dynamic Authorization Rules
|
|
264
265
|
|
|
265
|
-
|
|
266
|
+
For more complex scenarios, Rabarber supports dynamic authorization rules:
|
|
266
267
|
|
|
267
268
|
```rb
|
|
268
|
-
class
|
|
269
|
-
|
|
269
|
+
class OrdersController < ApplicationController
|
|
270
|
+
grant_access roles: :manager, unless: -> { current_user.suspended? }
|
|
270
271
|
|
|
271
|
-
|
|
272
|
+
grant_access action: :show, roles: :client, if: :user_company_matches_order?
|
|
273
|
+
def show
|
|
274
|
+
# ...
|
|
275
|
+
end
|
|
272
276
|
|
|
273
277
|
private
|
|
274
278
|
|
|
275
|
-
def
|
|
276
|
-
|
|
279
|
+
def user_company_matches_order?
|
|
280
|
+
current_user.company == Order.find(params[:id]).company
|
|
277
281
|
end
|
|
278
282
|
end
|
|
279
283
|
```
|
|
280
284
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
## Dynamic Rules
|
|
285
|
+
You can pass a dynamic rule as an `if` or `unless` argument, which can be a symbol or a proc. Symbols refer to instance methods, and procs are evaluated in the controller at request time.
|
|
284
286
|
|
|
285
|
-
|
|
287
|
+
Dynamic rules can also be used without roles at all, allowing you to define custom logic or even delegate to custom policy objects:
|
|
286
288
|
|
|
287
289
|
```rb
|
|
288
|
-
class
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
# Proc-based conditions
|
|
293
|
-
grant_access action: :show, roles: :client, if: -> { current_user.company == Order.find(params[:id]).company }
|
|
294
|
-
def show
|
|
295
|
-
# Accessible to company managers unless suspended, and to clients if the client's company matches the order's company
|
|
290
|
+
class InvoicesController < ApplicationController
|
|
291
|
+
grant_access action: :update, unless: -> { invoice.period_closed? }
|
|
292
|
+
def update
|
|
293
|
+
# ...
|
|
296
294
|
end
|
|
297
295
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
# Accessible to company managers unless suspended, and to users based on custom policy logic
|
|
296
|
+
grant_access action: :destroy, if: :destroy_allowed?
|
|
297
|
+
def destroy
|
|
298
|
+
# ...
|
|
302
299
|
end
|
|
303
300
|
|
|
304
301
|
private
|
|
305
302
|
|
|
306
|
-
def
|
|
307
|
-
current_user.
|
|
303
|
+
def destroy_allowed?
|
|
304
|
+
InvoicePolicy.new(current_user).destroy?(invoice)
|
|
308
305
|
end
|
|
309
306
|
|
|
310
|
-
def
|
|
311
|
-
|
|
307
|
+
def invoice
|
|
308
|
+
@invoice ||= Invoice.find(params[:id])
|
|
312
309
|
end
|
|
313
310
|
end
|
|
314
311
|
```
|
|
315
312
|
|
|
316
|
-
##
|
|
313
|
+
## When Unauthorized
|
|
317
314
|
|
|
318
|
-
|
|
315
|
+
By default, when unauthorized, Rabarber will redirect back (HTML format) or return 403 (other formats). You can override `when_unauthorized` method to customize unauthorized access behavior:
|
|
319
316
|
|
|
320
|
-
|
|
317
|
+
```rb
|
|
318
|
+
class ApplicationController < ActionController::Base
|
|
319
|
+
include Rabarber::Authorization
|
|
320
|
+
|
|
321
|
+
with_authorization
|
|
322
|
+
|
|
323
|
+
private
|
|
324
|
+
|
|
325
|
+
def when_unauthorized
|
|
326
|
+
# Custom behavior to hide existence of protected resources
|
|
327
|
+
head :not_found
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The `when_unauthorized` method can be overridden in any controller to provide controller-specific unauthorized access handling.
|
|
333
|
+
|
|
334
|
+
## Context / Multi-tenancy
|
|
335
|
+
|
|
336
|
+
Rabarber supports multi-tenancy through its context feature. All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes. By default, context is `nil`, meaning roles are global. Context can also be an instance of an `ActiveRecord` model or a class.
|
|
337
|
+
|
|
338
|
+
For example, in a project management app, you might want users to have different roles in different projects - someone could be an `owner` in one project but just a `member` in another.
|
|
339
|
+
|
|
340
|
+
### Contextual Role Assignment and Queries
|
|
321
341
|
|
|
322
342
|
```rb
|
|
323
343
|
# Assign roles within a specific model instance
|
|
@@ -335,7 +355,7 @@ user.has_role?(:admin, context: Project)
|
|
|
335
355
|
user.revoke_roles(:owner, context: project)
|
|
336
356
|
|
|
337
357
|
# Get user roles
|
|
338
|
-
user.roles(context:
|
|
358
|
+
user.roles(context: project)
|
|
339
359
|
|
|
340
360
|
# Get users with a role
|
|
341
361
|
User.with_role(:member, context: project)
|
|
@@ -359,6 +379,8 @@ Rabarber.roles(context: project)
|
|
|
359
379
|
|
|
360
380
|
### Contextual Authorization
|
|
361
381
|
|
|
382
|
+
In authorization rules, in addition to specifying context explicitly, you can also provide a proc or a symbol (similar to dynamic rules):
|
|
383
|
+
|
|
362
384
|
```rb
|
|
363
385
|
class ProjectsController < ApplicationController
|
|
364
386
|
grant_access roles: :admin, context: Project
|
|
@@ -383,11 +405,11 @@ class ProjectsController < ApplicationController
|
|
|
383
405
|
end
|
|
384
406
|
```
|
|
385
407
|
|
|
386
|
-
### Orphaned
|
|
408
|
+
### Orphaned Contextual Roles
|
|
387
409
|
|
|
388
410
|
When a context object is deleted from your database, its associated roles become orphaned and ignored by Rabarber.
|
|
389
411
|
|
|
390
|
-
To clean up orphaned
|
|
412
|
+
To clean up orphaned roles, use:
|
|
391
413
|
|
|
392
414
|
```rb
|
|
393
415
|
Rabarber.prune
|
|
@@ -395,13 +417,13 @@ Rabarber.prune
|
|
|
395
417
|
|
|
396
418
|
### Context Migrations
|
|
397
419
|
|
|
398
|
-
|
|
420
|
+
When you rename or remove models used as contexts, you need to update Rabarber's stored context data accordingly. Use these irreversible data migrations:
|
|
399
421
|
|
|
400
422
|
```rb
|
|
401
423
|
# Rename a context class (e.g., when you rename your Ticket model to Task)
|
|
402
424
|
migrate_authorization_context!("Ticket", "Task")
|
|
403
425
|
|
|
404
|
-
# Remove
|
|
426
|
+
# Remove context data (e.g., when you delete the Ticket model entirely)
|
|
405
427
|
delete_authorization_context!("Ticket")
|
|
406
428
|
```
|
|
407
429
|
|
|
@@ -440,7 +462,7 @@ Use conditional rendering based on roles:
|
|
|
440
462
|
## Getting Help and Contributing
|
|
441
463
|
|
|
442
464
|
### Getting Help
|
|
443
|
-
Have a question or need assistance? Open a discussion in
|
|
465
|
+
Have a question or need assistance? Open a discussion in the [discussions section](https://github.com/enjaku4/rabarber/discussions) for:
|
|
444
466
|
- Usage questions
|
|
445
467
|
- Implementation guidance
|
|
446
468
|
- Feature suggestions
|
|
@@ -455,7 +477,7 @@ Found a bug? Please [create an issue](https://github.com/enjaku4/rabarber/issues
|
|
|
455
477
|
Ready to contribute? You can:
|
|
456
478
|
- Fix bugs by submitting pull requests
|
|
457
479
|
- Improve documentation
|
|
458
|
-
- Add new features (please discuss first in
|
|
480
|
+
- Add new features (please discuss first in the [discussions section](https://github.com/enjaku4/rabarber/discussions))
|
|
459
481
|
|
|
460
482
|
Before contributing, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md)
|
|
461
483
|
|
|
@@ -471,5 +493,5 @@ Everyone interacting in the Rabarber project is expected to follow the [code of
|
|
|
471
493
|
|
|
472
494
|
Only the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:
|
|
473
495
|
|
|
474
|
-
[v4.x.x](https://github.com/enjaku4/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/enjaku4/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
|
|
496
|
+
[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)
|
|
475
497
|
| [v2.x.x](https://github.com/enjaku4/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/enjaku4/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
|
|
@@ -1,43 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
3
|
+
require "singleton"
|
|
4
4
|
|
|
5
5
|
module Rabarber
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
6
|
+
class Configuration
|
|
7
|
+
include Singleton
|
|
8
|
+
|
|
9
|
+
attr_reader :cache_enabled, :current_user_method, :user_model_name
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
reset_to_defaults!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def cache_enabled=(value)
|
|
16
|
+
@cache_enabled = Rabarber::Inputs::Boolean.new(
|
|
17
|
+
value,
|
|
18
|
+
error: Rabarber::ConfigurationError,
|
|
19
|
+
message: "Invalid configuration `cache_enabled`, expected a boolean, got #{value.inspect}"
|
|
20
|
+
).process
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def current_user_method=(value)
|
|
24
|
+
@current_user_method = Rabarber::Inputs::NonEmptySymbol.new(
|
|
25
|
+
value,
|
|
26
|
+
error: Rabarber::ConfigurationError,
|
|
27
|
+
message: "Invalid configuration `current_user_method`, expected a symbol or a string, got #{value.inspect}"
|
|
28
|
+
).process
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def user_model_name=(value)
|
|
32
|
+
@user_model_name = Rabarber::Inputs::NonEmptyString.new(
|
|
33
|
+
value,
|
|
34
|
+
error: Rabarber::ConfigurationError,
|
|
35
|
+
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{value.inspect}"
|
|
36
|
+
).process
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset_to_defaults!
|
|
40
|
+
self.cache_enabled = true
|
|
41
|
+
self.current_user_method = :current_user
|
|
42
|
+
self.user_model_name = "User"
|
|
43
|
+
end
|
|
41
44
|
|
|
42
45
|
def user_model
|
|
43
46
|
Rabarber::Inputs::Model.new(
|
|
@@ -46,5 +49,16 @@ module Rabarber
|
|
|
46
49
|
message: "Invalid configuration `user_model_name`, expected an ActiveRecord model name, got #{user_model_name.inspect}"
|
|
47
50
|
).process
|
|
48
51
|
end
|
|
52
|
+
|
|
53
|
+
def configure
|
|
54
|
+
yield self
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class << self
|
|
58
|
+
delegate :cache_enabled, :current_user_method, :user_model_name,
|
|
59
|
+
:cache_enabled=, :current_user_method=, :user_model_name=,
|
|
60
|
+
:user_model, :reset_to_defaults!, :configure,
|
|
61
|
+
to: :instance
|
|
62
|
+
end
|
|
49
63
|
end
|
|
50
64
|
end
|
|
@@ -24,7 +24,7 @@ module Rabarber
|
|
|
24
24
|
|
|
25
25
|
Rabarber::Core::Permissions.add(
|
|
26
26
|
self,
|
|
27
|
-
Rabarber::Inputs::
|
|
27
|
+
Rabarber::Inputs::NonEmptySymbol.new(
|
|
28
28
|
action,
|
|
29
29
|
optional: true,
|
|
30
30
|
message: "Expected a symbol or a string, got #{action.inspect}"
|
|
@@ -61,7 +61,7 @@ module Rabarber
|
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def when_unauthorized
|
|
64
|
-
request.format.html? ?
|
|
64
|
+
request.format.html? ? redirect_back_or_to(root_path) : head(:forbidden)
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
end
|
data/lib/rabarber/core/cache.rb
CHANGED
|
@@ -1,38 +1,51 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "digest/
|
|
3
|
+
require "digest/md5"
|
|
4
|
+
require "securerandom"
|
|
4
5
|
|
|
5
6
|
module Rabarber
|
|
6
7
|
module Core
|
|
7
8
|
module Cache
|
|
8
|
-
|
|
9
|
+
extend self
|
|
9
10
|
|
|
10
|
-
def fetch(
|
|
11
|
+
def fetch(roleable_id, scope, &)
|
|
11
12
|
return yield unless enabled?
|
|
12
13
|
|
|
13
|
-
Rails.cache.fetch(prepare_key(
|
|
14
|
+
Rails.cache.fetch(prepare_key(roleable_id, scope), expires_in: 1.hour, race_condition_ttl: 5.seconds, &)
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def delete(*
|
|
17
|
+
def delete(*pairs)
|
|
17
18
|
return unless enabled?
|
|
18
19
|
|
|
19
|
-
Rails.cache.delete_multi(
|
|
20
|
+
Rails.cache.delete_multi(pairs.map { |roleable_id, scope| prepare_key(roleable_id, scope) }) if pairs.any?
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
def clear
|
|
24
|
+
Rails.cache.write(VERSION_KEY, SecureRandom.alphanumeric(8))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
22
29
|
def enabled?
|
|
23
30
|
Rabarber::Configuration.cache_enabled
|
|
24
31
|
end
|
|
25
32
|
|
|
26
|
-
def
|
|
27
|
-
|
|
33
|
+
def prepare_key(roleable_id, scope)
|
|
34
|
+
Digest::MD5.base64digest(Marshal.dump([current_version, roleable_id, scope]))
|
|
28
35
|
end
|
|
29
36
|
|
|
30
|
-
def
|
|
31
|
-
|
|
37
|
+
def current_version
|
|
38
|
+
version = Rails.cache.read(VERSION_KEY).presence
|
|
39
|
+
|
|
40
|
+
return version if version
|
|
41
|
+
|
|
42
|
+
clear
|
|
43
|
+
|
|
44
|
+
Rails.cache.read(VERSION_KEY)
|
|
32
45
|
end
|
|
33
46
|
|
|
34
|
-
|
|
35
|
-
private_constant :
|
|
47
|
+
VERSION_KEY = "rabarber"
|
|
48
|
+
private_constant :VERSION_KEY
|
|
36
49
|
end
|
|
37
50
|
end
|
|
38
51
|
|
data/lib/rabarber/inputs/base.rb
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "dry-types"
|
|
4
|
-
|
|
5
3
|
module Rabarber
|
|
6
4
|
module Inputs
|
|
7
5
|
class Base
|
|
8
|
-
include Dry.Types()
|
|
9
|
-
|
|
10
6
|
def initialize(value, optional: false, error: Rabarber::InvalidArgumentError, message: nil)
|
|
11
7
|
@value = value
|
|
12
8
|
@optional = optional
|
|
@@ -15,15 +11,14 @@ module Rabarber
|
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def process
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
raise_error
|
|
14
|
+
return @value if @value.nil? && @optional
|
|
15
|
+
|
|
16
|
+
validate_and_normalize
|
|
22
17
|
end
|
|
23
18
|
|
|
24
19
|
private
|
|
25
20
|
|
|
26
|
-
def
|
|
21
|
+
def validate_and_normalize
|
|
27
22
|
raise NotImplementedError
|
|
28
23
|
end
|
|
29
24
|
|
|
@@ -19,14 +19,13 @@ module Rabarber
|
|
|
19
19
|
|
|
20
20
|
private
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
self.class::Nil
|
|
22
|
+
def validate_and_normalize
|
|
23
|
+
return @value if @value.nil?
|
|
24
|
+
return @value if @value.is_a?(Class)
|
|
25
|
+
return @value if @value.is_a?(ActiveRecord::Base)
|
|
26
|
+
return @value if @value in { context_type: String | nil, context_id: String | Integer | nil }
|
|
27
|
+
|
|
28
|
+
raise_error
|
|
30
29
|
end
|
|
31
30
|
end
|
|
32
31
|
end
|
|
@@ -4,9 +4,21 @@ module Rabarber
|
|
|
4
4
|
module Inputs
|
|
5
5
|
module Contexts
|
|
6
6
|
class Authorizational < Rabarber::Inputs::Context
|
|
7
|
+
def resolve
|
|
8
|
+
result = process
|
|
9
|
+
return result if result.is_a?(Symbol) || result.is_a?(Proc)
|
|
10
|
+
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
private
|
|
8
15
|
|
|
9
|
-
def
|
|
16
|
+
def validate_and_normalize
|
|
17
|
+
return @value if @value.is_a?(Proc)
|
|
18
|
+
return @value.to_sym if (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
19
|
+
|
|
20
|
+
super
|
|
21
|
+
end
|
|
10
22
|
end
|
|
11
23
|
end
|
|
12
24
|
end
|
|
@@ -5,7 +5,11 @@ module Rabarber
|
|
|
5
5
|
class DynamicRule < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
return @value if @value.is_a?(Proc)
|
|
10
|
+
|
|
11
|
+
Rabarber::Inputs::NonEmptySymbol.new(@value, error: @error, message: @message).process
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -5,7 +5,12 @@ module Rabarber
|
|
|
5
5
|
class Model < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
model = @value.try(:safe_constantize)
|
|
10
|
+
raise_error unless model.is_a?(Class) && model < ActiveRecord::Base
|
|
11
|
+
|
|
12
|
+
model
|
|
13
|
+
end
|
|
9
14
|
end
|
|
10
15
|
end
|
|
11
16
|
end
|
|
@@ -5,7 +5,9 @@ module Rabarber
|
|
|
5
5
|
class NonEmptyString < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
@value.is_a?(String) && @value.present? ? @value : raise_error
|
|
10
|
+
end
|
|
9
11
|
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rabarber
|
|
4
|
+
module Inputs
|
|
5
|
+
class NonEmptySymbol < Rabarber::Inputs::Base
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.present?
|
|
10
|
+
|
|
11
|
+
@value.to_sym
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/rabarber/inputs/role.rb
CHANGED
|
@@ -5,7 +5,11 @@ module Rabarber
|
|
|
5
5
|
class Role < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
raise_error unless (@value.is_a?(String) || @value.is_a?(Symbol)) && @value.to_s.match?(/\A[a-z0-9_]+\z/)
|
|
10
|
+
|
|
11
|
+
@value.to_sym
|
|
12
|
+
end
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
end
|
|
@@ -5,8 +5,10 @@ module Rabarber
|
|
|
5
5
|
class Roles < Rabarber::Inputs::Base
|
|
6
6
|
private
|
|
7
7
|
|
|
8
|
-
def
|
|
9
|
-
|
|
8
|
+
def validate_and_normalize
|
|
9
|
+
Array(@value).map do |role|
|
|
10
|
+
Rabarber::Inputs::Role.new(role, error: @error, message: @message).process
|
|
11
|
+
end
|
|
10
12
|
end
|
|
11
13
|
end
|
|
12
14
|
end
|
|
@@ -40,11 +40,11 @@ module Rabarber
|
|
|
40
40
|
|
|
41
41
|
def roles(context: nil)
|
|
42
42
|
processed_context = process_context(context)
|
|
43
|
-
Rabarber::Core::Cache.fetch(
|
|
43
|
+
Rabarber::Core::Cache.fetch(roleable_id, processed_context) { rabarber_roles.list(context: processed_context) }
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def all_roles
|
|
47
|
-
Rabarber::Core::Cache.fetch(
|
|
47
|
+
Rabarber::Core::Cache.fetch(roleable_id, :all) { rabarber_roles.list_all }
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def has_role?(*role_names, context: nil)
|
|
@@ -60,7 +60,7 @@ module Rabarber
|
|
|
60
60
|
create_new_roles(processed_role_names, context: processed_context) if create_new
|
|
61
61
|
|
|
62
62
|
roles_to_assign = Rabarber::Role.where(
|
|
63
|
-
name: (processed_role_names - rabarber_roles.
|
|
63
|
+
name: (processed_role_names - rabarber_roles.list(context: processed_context)), **processed_context
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
if roles_to_assign.any?
|
|
@@ -76,7 +76,7 @@ module Rabarber
|
|
|
76
76
|
processed_context = process_context(context)
|
|
77
77
|
|
|
78
78
|
roles_to_revoke = Rabarber::Role.where(
|
|
79
|
-
name: processed_role_names.intersection(rabarber_roles.
|
|
79
|
+
name: processed_role_names.intersection(rabarber_roles.list(context: processed_context)), **processed_context
|
|
80
80
|
)
|
|
81
81
|
|
|
82
82
|
if roles_to_revoke.any?
|
data/lib/rabarber/models/role.rb
CHANGED
|
@@ -11,15 +11,11 @@ module Rabarber
|
|
|
11
11
|
join_table: "rabarber_roles_roleables"
|
|
12
12
|
|
|
13
13
|
class << self
|
|
14
|
-
def
|
|
15
|
-
deprecation_warning("names", "Rabarber.roles")
|
|
16
|
-
|
|
14
|
+
def list(context: nil)
|
|
17
15
|
where(process_context(context)).pluck(:name).map(&:to_sym)
|
|
18
16
|
end
|
|
19
17
|
|
|
20
|
-
def
|
|
21
|
-
deprecation_warning("all_names", "Rabarber.all_roles")
|
|
22
|
-
|
|
18
|
+
def list_all
|
|
23
19
|
includes(:context).each_with_object({}) do |role, hash|
|
|
24
20
|
(hash[role.context] ||= []) << role.name.to_sym
|
|
25
21
|
rescue ActiveRecord::RecordNotFound
|
|
@@ -29,9 +25,7 @@ module Rabarber
|
|
|
29
25
|
raise Rabarber::NotFoundError, "Context not found: class #{e.name} may have been renamed or deleted"
|
|
30
26
|
end
|
|
31
27
|
|
|
32
|
-
def
|
|
33
|
-
deprecation_warning("add", "Rabarber.create_role")
|
|
34
|
-
|
|
28
|
+
def register(name, context: nil)
|
|
35
29
|
name = process_role_name(name)
|
|
36
30
|
processed_context = process_context(context)
|
|
37
31
|
|
|
@@ -40,9 +34,7 @@ module Rabarber
|
|
|
40
34
|
!!create!(name:, **processed_context)
|
|
41
35
|
end
|
|
42
36
|
|
|
43
|
-
def
|
|
44
|
-
deprecation_warning("rename", "Rabarber.rename_role")
|
|
45
|
-
|
|
37
|
+
def amend(old_name, new_name, context: nil, force: false)
|
|
46
38
|
processed_context = process_context(context)
|
|
47
39
|
role = find_by(name: process_role_name(old_name), **processed_context)
|
|
48
40
|
|
|
@@ -57,9 +49,7 @@ module Rabarber
|
|
|
57
49
|
role.update!(name:)
|
|
58
50
|
end
|
|
59
51
|
|
|
60
|
-
def
|
|
61
|
-
deprecation_warning("remove", "Rabarber.delete_role")
|
|
62
|
-
|
|
52
|
+
def drop(name, context: nil, force: false)
|
|
63
53
|
processed_context = process_context(context)
|
|
64
54
|
role = find_by(name: process_role_name(name), **processed_context)
|
|
65
55
|
|
|
@@ -72,12 +62,6 @@ module Rabarber
|
|
|
72
62
|
!!role.destroy!
|
|
73
63
|
end
|
|
74
64
|
|
|
75
|
-
def assignees(name, context: nil)
|
|
76
|
-
deprecation_warning("assignees", "#{Rabarber::Configuration.user_model_name}.with_role")
|
|
77
|
-
|
|
78
|
-
find_by(name: process_role_name(name), **process_context(context))&.roleables || Rabarber::Configuration.user_model.none
|
|
79
|
-
end
|
|
80
|
-
|
|
81
65
|
def prune
|
|
82
66
|
orphaned_roles = where.not(context_id: nil).includes(:context).filter_map do |role|
|
|
83
67
|
!role.context
|
|
@@ -99,14 +83,6 @@ module Rabarber
|
|
|
99
83
|
|
|
100
84
|
private
|
|
101
85
|
|
|
102
|
-
def deprecation_warning(method, alternative)
|
|
103
|
-
callers = caller_locations.map(&:label)
|
|
104
|
-
|
|
105
|
-
return if callers.include?(alternative) || callers.include?("ActiveRecord::Relation#scoping")
|
|
106
|
-
|
|
107
|
-
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.")
|
|
108
|
-
end
|
|
109
|
-
|
|
110
86
|
def delete_roleables_cache(role, context:)
|
|
111
87
|
Rabarber::Core::Cache.delete(*role.roleables.pluck(:id).flat_map { [[_1, context], [_1, :all]] })
|
|
112
88
|
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rabarber
|
|
4
|
+
module RoleManagement
|
|
5
|
+
def roles(context: nil) = Rabarber::Role.list(context:)
|
|
6
|
+
def all_roles = Rabarber::Role.list_all
|
|
7
|
+
def create_role(name, context: nil) = Rabarber::Role.register(name, context:)
|
|
8
|
+
def rename_role(old_name, new_name, context: nil, force: false) = Rabarber::Role.amend(old_name, new_name, context:, force:)
|
|
9
|
+
def delete_role(name, context: nil, force: false) = Rabarber::Role.drop(name, context:, force:)
|
|
10
|
+
def prune = Rabarber::Role.prune # rubocop:disable Rails/Delegate
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/rabarber/version.rb
CHANGED
data/lib/rabarber.rb
CHANGED
|
@@ -14,7 +14,7 @@ require_relative "rabarber/inputs/model"
|
|
|
14
14
|
require_relative "rabarber/inputs/non_empty_string"
|
|
15
15
|
require_relative "rabarber/inputs/role"
|
|
16
16
|
require_relative "rabarber/inputs/roles"
|
|
17
|
-
require_relative "rabarber/inputs/
|
|
17
|
+
require_relative "rabarber/inputs/non_empty_symbol"
|
|
18
18
|
|
|
19
19
|
require_relative "rabarber/configuration"
|
|
20
20
|
|
|
@@ -27,15 +27,6 @@ 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
|
|
39
30
|
end
|
|
40
31
|
|
|
41
32
|
require_relative "rabarber/core/cache"
|
|
@@ -47,6 +38,9 @@ require_relative "rabarber/helpers/helpers"
|
|
|
47
38
|
require_relative "rabarber/helpers/migration_helpers"
|
|
48
39
|
require_relative "rabarber/models/concerns/roleable"
|
|
49
40
|
require_relative "rabarber/models/role"
|
|
41
|
+
require_relative "rabarber/models/role_management"
|
|
42
|
+
|
|
43
|
+
Rabarber.extend Rabarber::RoleManagement
|
|
50
44
|
|
|
51
45
|
require_relative "rabarber/core/permissions"
|
|
52
46
|
|
data/rabarber.gemspec
CHANGED
|
@@ -6,18 +6,20 @@ Gem::Specification.new do |spec|
|
|
|
6
6
|
spec.name = "rabarber"
|
|
7
7
|
spec.version = Rabarber::VERSION
|
|
8
8
|
spec.authors = ["enjaku4", "trafium"]
|
|
9
|
-
spec.email = ["
|
|
9
|
+
spec.email = ["contact@brownbox.dev"]
|
|
10
10
|
spec.homepage = "https://github.com/enjaku4/rabarber"
|
|
11
11
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
12
12
|
spec.metadata["source_code_uri"] = spec.homepage
|
|
13
13
|
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
14
14
|
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
15
15
|
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/README.md"
|
|
16
|
+
spec.metadata["mailing_list_uri"] = "#{spec.homepage}/discussions"
|
|
17
|
+
spec.metadata["funding_uri"] = "https://github.com/sponsors/enjaku4"
|
|
16
18
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
17
19
|
spec.summary = "Simple role-based authorization library for Ruby on Rails"
|
|
18
|
-
spec.description = "
|
|
20
|
+
spec.description = "Simple role-based authorization for Rails applications with multi-tenancy support"
|
|
19
21
|
spec.license = "MIT"
|
|
20
|
-
spec.required_ruby_version = ">= 3.
|
|
22
|
+
spec.required_ruby_version = ">= 3.3", "< 4.1"
|
|
21
23
|
|
|
22
24
|
spec.files = [
|
|
23
25
|
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
|
@@ -25,7 +27,5 @@ Gem::Specification.new do |spec|
|
|
|
25
27
|
|
|
26
28
|
spec.require_paths = ["lib"]
|
|
27
29
|
|
|
28
|
-
spec.add_dependency "
|
|
29
|
-
spec.add_dependency "dry-types", "~> 1.7"
|
|
30
|
-
spec.add_dependency "rails", ">= 7.1", "< 8.2"
|
|
30
|
+
spec.add_dependency "rails", ">= 7.2", "< 8.2"
|
|
31
31
|
end
|
metadata
CHANGED
|
@@ -1,50 +1,22 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rabarber
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 6.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- enjaku4
|
|
8
8
|
- trafium
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-04-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: dry-configurable
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.1'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.1'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: dry-types
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - "~>"
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '1.7'
|
|
34
|
-
type: :runtime
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - "~>"
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '1.7'
|
|
41
13
|
- !ruby/object:Gem::Dependency
|
|
42
14
|
name: rails
|
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|
|
44
16
|
requirements:
|
|
45
17
|
- - ">="
|
|
46
18
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '7.
|
|
19
|
+
version: '7.2'
|
|
48
20
|
- - "<"
|
|
49
21
|
- !ruby/object:Gem::Version
|
|
50
22
|
version: '8.2'
|
|
@@ -54,15 +26,14 @@ dependencies:
|
|
|
54
26
|
requirements:
|
|
55
27
|
- - ">="
|
|
56
28
|
- !ruby/object:Gem::Version
|
|
57
|
-
version: '7.
|
|
29
|
+
version: '7.2'
|
|
58
30
|
- - "<"
|
|
59
31
|
- !ruby/object:Gem::Version
|
|
60
32
|
version: '8.2'
|
|
61
|
-
description:
|
|
62
|
-
|
|
63
|
-
control that separates authorization from business logic
|
|
33
|
+
description: Simple role-based authorization for Rails applications with multi-tenancy
|
|
34
|
+
support
|
|
64
35
|
email:
|
|
65
|
-
-
|
|
36
|
+
- contact@brownbox.dev
|
|
66
37
|
executables: []
|
|
67
38
|
extensions: []
|
|
68
39
|
extra_rdoc_files: []
|
|
@@ -89,11 +60,12 @@ files:
|
|
|
89
60
|
- lib/rabarber/inputs/dynamic_rule.rb
|
|
90
61
|
- lib/rabarber/inputs/model.rb
|
|
91
62
|
- lib/rabarber/inputs/non_empty_string.rb
|
|
63
|
+
- lib/rabarber/inputs/non_empty_symbol.rb
|
|
92
64
|
- lib/rabarber/inputs/role.rb
|
|
93
65
|
- lib/rabarber/inputs/roles.rb
|
|
94
|
-
- lib/rabarber/inputs/symbol.rb
|
|
95
66
|
- lib/rabarber/models/concerns/roleable.rb
|
|
96
67
|
- lib/rabarber/models/role.rb
|
|
68
|
+
- lib/rabarber/models/role_management.rb
|
|
97
69
|
- lib/rabarber/railtie.rb
|
|
98
70
|
- lib/rabarber/version.rb
|
|
99
71
|
- rabarber.gemspec
|
|
@@ -106,6 +78,8 @@ metadata:
|
|
|
106
78
|
changelog_uri: https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md
|
|
107
79
|
bug_tracker_uri: https://github.com/enjaku4/rabarber/issues
|
|
108
80
|
documentation_uri: https://github.com/enjaku4/rabarber/blob/main/README.md
|
|
81
|
+
mailing_list_uri: https://github.com/enjaku4/rabarber/discussions
|
|
82
|
+
funding_uri: https://github.com/sponsors/enjaku4
|
|
109
83
|
rubygems_mfa_required: 'true'
|
|
110
84
|
rdoc_options: []
|
|
111
85
|
require_paths:
|
|
@@ -114,17 +88,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
88
|
requirements:
|
|
115
89
|
- - ">="
|
|
116
90
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '3.
|
|
91
|
+
version: '3.3'
|
|
118
92
|
- - "<"
|
|
119
93
|
- !ruby/object:Gem::Version
|
|
120
|
-
version: '
|
|
94
|
+
version: '4.1'
|
|
121
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
96
|
requirements:
|
|
123
97
|
- - ">="
|
|
124
98
|
- !ruby/object:Gem::Version
|
|
125
99
|
version: '0'
|
|
126
100
|
requirements: []
|
|
127
|
-
rubygems_version:
|
|
101
|
+
rubygems_version: 4.0.8
|
|
128
102
|
specification_version: 4
|
|
129
103
|
summary: Simple role-based authorization library for Ruby on Rails
|
|
130
104
|
test_files: []
|