rabarber 5.2.4 → 5.2.5
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 +7 -1
- data/README.md +92 -87
- data/lib/rabarber/controllers/concerns/authorization.rb +1 -1
- data/lib/rabarber/version.rb +1 -1
- data/rabarber.gemspec +5 -3
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23cc4a1c2e65a3b450653b65df49633601756ada11844173b0c6314c41067ca9
|
|
4
|
+
data.tar.gz: 95153a3172c58bf636cb4d17981ba70336a292368f6bac26c2b6a107c4fcbaaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 180407341e8e4e700fff624ab12de40d9aa66ed4e132e8ddc92c0c9cb4c010309194e7b37bc825a9a92a0e9baf64022a165788055ccba6b41e60aab8d040fc20
|
|
7
|
+
data.tar.gz: 882d08be5cab1db66d0056ef5e0ace724d2d8ce633cdf75af808d28980faeadbdf9e56c4362e3b7197a89b0de80925f77b2dafb12c5f6e44b1f21cb43be0730d
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## v5.2.5
|
|
2
|
+
|
|
3
|
+
### Misc:
|
|
4
|
+
|
|
5
|
+
- Added support for Ruby 4
|
|
6
|
+
|
|
1
7
|
## v5.2.4
|
|
2
8
|
|
|
3
9
|
### Misc:
|
|
@@ -91,7 +97,7 @@ To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/e
|
|
|
91
97
|
|
|
92
98
|
- Optimized various parts of the code and database queries for improved performance
|
|
93
99
|
- Streamlined the authorization process by requiring the user to be authenticated before access is verified
|
|
94
|
-
- Rabarber now skips roles with missing instance context
|
|
100
|
+
- Rabarber now skips roles with missing instance context; missing class context still raises errors
|
|
95
101
|
|
|
96
102
|
## v4.1.4
|
|
97
103
|
|
data/README.md
CHANGED
|
@@ -5,49 +5,44 @@
|
|
|
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
|
|
12
|
+
Consider a CRM system where users with different roles have distinct access levels. For instance, the role `accountant` can access invoice data but not marketing information, while the role `analyst` can view marketing data but not detailed financial records. You can define such authorization rules easily with Rabarber.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
___
|
|
15
|
+
|
|
16
|
+
And this is how your controller might look with Rabarber:
|
|
15
17
|
|
|
16
18
|
```rb
|
|
17
|
-
class
|
|
18
|
-
grant_access roles: :admin
|
|
19
|
+
class TicketsController < ApplicationController
|
|
20
|
+
grant_access roles: :admin
|
|
19
21
|
|
|
20
|
-
grant_access action: :index, roles:
|
|
22
|
+
grant_access action: :index, roles: :manager
|
|
21
23
|
def index
|
|
22
|
-
#
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
grant_access action: :show, roles: :accountant
|
|
26
|
-
def show
|
|
27
|
-
# Accessible to accountants
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
grant_access action: :analytics, roles: :analyst
|
|
31
|
-
def analytics
|
|
32
|
-
# Accessible to analysts
|
|
24
|
+
# ...
|
|
33
25
|
end
|
|
34
26
|
|
|
35
27
|
def destroy
|
|
36
|
-
#
|
|
28
|
+
# ...
|
|
37
29
|
end
|
|
38
30
|
end
|
|
39
31
|
```
|
|
40
32
|
|
|
33
|
+
This means that `admin` users can access everything in `TicketsController`, while the `manager` role can access only the `index` action.
|
|
34
|
+
|
|
41
35
|
## Table of Contents
|
|
42
36
|
|
|
43
37
|
**Gem Usage:**
|
|
44
38
|
- [Installation](#installation)
|
|
45
39
|
- [Configuration](#configuration)
|
|
46
40
|
- [User Role Methods](#user-role-methods)
|
|
47
|
-
- [Role Management](#role-management)
|
|
48
|
-
- [
|
|
49
|
-
- [Dynamic Rules](#dynamic-rules)
|
|
50
|
-
- [
|
|
41
|
+
- [Direct Role Management](#direct-role-management)
|
|
42
|
+
- [Authorization](#authorization)
|
|
43
|
+
- [Dynamic Authorization Rules](#dynamic-authorization-rules)
|
|
44
|
+
- [When Unauthorized](#when-unauthorized)
|
|
45
|
+
- [Context / Multi-tenancy](#context--multi-tenancy)
|
|
51
46
|
- [View Helpers](#view-helpers)
|
|
52
47
|
|
|
53
48
|
**Community Resources:**
|
|
@@ -70,7 +65,7 @@ Install the gem:
|
|
|
70
65
|
bundle install
|
|
71
66
|
```
|
|
72
67
|
|
|
73
|
-
Generate the migration
|
|
68
|
+
Generate the migration to store roles (replace `users` with your table name if different):
|
|
74
69
|
|
|
75
70
|
```shell
|
|
76
71
|
# For standard integer IDs
|
|
@@ -88,7 +83,7 @@ rails db:migrate
|
|
|
88
83
|
|
|
89
84
|
## Configuration
|
|
90
85
|
|
|
91
|
-
|
|
86
|
+
Create an initializer to customize Rabarber's behavior (optional):
|
|
92
87
|
|
|
93
88
|
```rb
|
|
94
89
|
Rabarber.configure do |config|
|
|
@@ -98,7 +93,7 @@ Rabarber.configure do |config|
|
|
|
98
93
|
end
|
|
99
94
|
```
|
|
100
95
|
|
|
101
|
-
Roles are cached by default for performance.
|
|
96
|
+
Roles are cached by default for better performance. Clear the cache manually when needed:
|
|
102
97
|
|
|
103
98
|
```rb
|
|
104
99
|
Rabarber::Cache.clear
|
|
@@ -112,13 +107,13 @@ Your user model is automatically augmented with role management methods:
|
|
|
112
107
|
|
|
113
108
|
```rb
|
|
114
109
|
# Assign roles (creates roles if they don't exist)
|
|
115
|
-
user.assign_roles(:
|
|
110
|
+
user.assign_roles(:admin, :manager)
|
|
116
111
|
|
|
117
|
-
# Assign only existing roles
|
|
112
|
+
# Assign only existing roles (don't create new ones)
|
|
118
113
|
user.assign_roles(:accountant, :manager, create_new: false)
|
|
119
114
|
|
|
120
115
|
# Revoke specific roles
|
|
121
|
-
user.revoke_roles(:
|
|
116
|
+
user.revoke_roles(:admin, :manager)
|
|
122
117
|
|
|
123
118
|
# Revoke all roles
|
|
124
119
|
user.revoke_all_roles
|
|
@@ -142,9 +137,9 @@ user.all_roles
|
|
|
142
137
|
User.with_role(:admin, :manager)
|
|
143
138
|
```
|
|
144
139
|
|
|
145
|
-
## Role Management
|
|
140
|
+
## Direct Role Management
|
|
146
141
|
|
|
147
|
-
|
|
142
|
+
You can also manage roles directly:
|
|
148
143
|
|
|
149
144
|
```rb
|
|
150
145
|
# Create a new role
|
|
@@ -165,41 +160,39 @@ Rabarber.roles
|
|
|
165
160
|
Rabarber.all_roles
|
|
166
161
|
```
|
|
167
162
|
|
|
168
|
-
> **Note:** Some methods have been deprecated in favor of the new API shown above.
|
|
163
|
+
> **Note:** Some methods have been deprecated in favor of the new API shown above. 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 replacements.
|
|
169
164
|
|
|
170
|
-
##
|
|
165
|
+
## Authorization
|
|
171
166
|
|
|
172
|
-
###
|
|
167
|
+
### Setup
|
|
173
168
|
|
|
174
|
-
Include
|
|
169
|
+
Include `Rabarber::Authorization` module in your controllers and configure protection:
|
|
175
170
|
|
|
176
171
|
```rb
|
|
177
172
|
class ApplicationController < ActionController::Base
|
|
178
173
|
include Rabarber::Authorization
|
|
179
174
|
|
|
180
|
-
with_authorization # Enable authorization for all actions
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
class InvoicesController < ApplicationController
|
|
184
|
-
with_authorization only: [:update, :destroy] # Selective authorization
|
|
175
|
+
with_authorization # Enable authorization check for all actions in all controllers by default
|
|
185
176
|
end
|
|
186
177
|
```
|
|
187
178
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
### Skip Authorization
|
|
191
|
-
|
|
192
|
-
You can also selectively skip authorization:
|
|
179
|
+
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
180
|
|
|
194
181
|
```rb
|
|
195
182
|
class TicketsController < ApplicationController
|
|
196
|
-
skip_authorization
|
|
183
|
+
skip_authorization only: [:index, :show] # Skip authorization for specific actions
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
class InvoicesController < ApplicationController
|
|
187
|
+
with_authorization except: [:index] # Enable authorization for all actions except index
|
|
197
188
|
end
|
|
198
189
|
```
|
|
199
190
|
|
|
191
|
+
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.
|
|
192
|
+
|
|
200
193
|
### Authorization Rules
|
|
201
194
|
|
|
202
|
-
Define
|
|
195
|
+
Define authorization rules using `grant_access`:
|
|
203
196
|
|
|
204
197
|
```rb
|
|
205
198
|
class TicketsController < ApplicationController
|
|
@@ -218,9 +211,7 @@ class TicketsController < ApplicationController
|
|
|
218
211
|
end
|
|
219
212
|
```
|
|
220
213
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
Rules are additive across inheritance chains and for the same actions:
|
|
214
|
+
Authorization rules are additive - they combine across inheritance chains and when defined multiple times for the same action:
|
|
224
215
|
|
|
225
216
|
```rb
|
|
226
217
|
class BaseController < ApplicationController
|
|
@@ -238,9 +229,7 @@ class InvoicesController < BaseController
|
|
|
238
229
|
end
|
|
239
230
|
```
|
|
240
231
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Omit roles to allow unrestricted access:
|
|
232
|
+
It's possible to omit roles to allow unrestricted access:
|
|
244
233
|
|
|
245
234
|
```rb
|
|
246
235
|
class UnrestrictedController < ApplicationController
|
|
@@ -260,64 +249,78 @@ class MixedController < ApplicationController
|
|
|
260
249
|
end
|
|
261
250
|
```
|
|
262
251
|
|
|
263
|
-
|
|
252
|
+
## Dynamic Authorization Rules
|
|
264
253
|
|
|
265
|
-
|
|
254
|
+
For more complex scenarios, Rabarber supports dynamic authorization rules:
|
|
266
255
|
|
|
267
256
|
```rb
|
|
268
|
-
class
|
|
269
|
-
|
|
257
|
+
class OrdersController < ApplicationController
|
|
258
|
+
grant_access roles: :manager, unless: -> { current_user.suspended? }
|
|
270
259
|
|
|
271
|
-
|
|
260
|
+
grant_access action: :show, roles: :client, if: :user_company_matches_order?
|
|
261
|
+
def show
|
|
262
|
+
# ...
|
|
263
|
+
end
|
|
272
264
|
|
|
273
265
|
private
|
|
274
266
|
|
|
275
|
-
def
|
|
276
|
-
|
|
267
|
+
def user_company_matches_order?
|
|
268
|
+
current_user.company == Order.find(params[:id]).company
|
|
277
269
|
end
|
|
278
270
|
end
|
|
279
271
|
```
|
|
280
272
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
## Dynamic Rules
|
|
273
|
+
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
274
|
|
|
285
|
-
|
|
275
|
+
Dynamic rules can also be used without roles at all, allowing you to define custom logic or even delegate to custom policy objects:
|
|
286
276
|
|
|
287
277
|
```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
|
|
278
|
+
class InvoicesController < ApplicationController
|
|
279
|
+
grant_access action: :update, unless: -> { Date.current.on_weekend? }
|
|
280
|
+
def update
|
|
281
|
+
# ...
|
|
296
282
|
end
|
|
297
283
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
# Accessible to company managers unless suspended, and to users based on custom policy logic
|
|
284
|
+
grant_access action: :destroy, if: :destroy_allowed?
|
|
285
|
+
def destroy
|
|
286
|
+
# ...
|
|
302
287
|
end
|
|
303
288
|
|
|
304
289
|
private
|
|
305
290
|
|
|
306
|
-
def
|
|
307
|
-
current_user.
|
|
291
|
+
def destroy_allowed?
|
|
292
|
+
InvoicePolicy.new(current_user).destroy?(Invoice.find(params[:id]))
|
|
308
293
|
end
|
|
294
|
+
end
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## When Unauthorized
|
|
309
298
|
|
|
310
|
-
|
|
311
|
-
|
|
299
|
+
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:
|
|
300
|
+
|
|
301
|
+
```rb
|
|
302
|
+
class ApplicationController < ActionController::Base
|
|
303
|
+
include Rabarber::Authorization
|
|
304
|
+
|
|
305
|
+
with_authorization
|
|
306
|
+
|
|
307
|
+
private
|
|
308
|
+
|
|
309
|
+
def when_unauthorized
|
|
310
|
+
head :not_found # Custom behavior to hide existence of protected resources
|
|
312
311
|
end
|
|
313
312
|
end
|
|
314
313
|
```
|
|
315
314
|
|
|
316
|
-
|
|
315
|
+
The `when_unauthorized` method can be overridden in any controller to provide controller-specific unauthorized access handling.
|
|
317
316
|
|
|
318
|
-
|
|
317
|
+
## Context / Multi-tenancy
|
|
319
318
|
|
|
320
|
-
|
|
319
|
+
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.
|
|
320
|
+
|
|
321
|
+
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.
|
|
322
|
+
|
|
323
|
+
### Contextual Role Assignment and Queries
|
|
321
324
|
|
|
322
325
|
```rb
|
|
323
326
|
# Assign roles within a specific model instance
|
|
@@ -335,7 +338,7 @@ user.has_role?(:admin, context: Project)
|
|
|
335
338
|
user.revoke_roles(:owner, context: project)
|
|
336
339
|
|
|
337
340
|
# Get user roles
|
|
338
|
-
user.roles(context:
|
|
341
|
+
user.roles(context: project)
|
|
339
342
|
|
|
340
343
|
# Get users with a role
|
|
341
344
|
User.with_role(:member, context: project)
|
|
@@ -359,6 +362,8 @@ Rabarber.roles(context: project)
|
|
|
359
362
|
|
|
360
363
|
### Contextual Authorization
|
|
361
364
|
|
|
365
|
+
In authorization rules, in addition to specifying context explicitly, you can also provide a proc or a symbol (similar to dynamic rules):
|
|
366
|
+
|
|
362
367
|
```rb
|
|
363
368
|
class ProjectsController < ApplicationController
|
|
364
369
|
grant_access roles: :admin, context: Project
|
|
@@ -383,11 +388,11 @@ class ProjectsController < ApplicationController
|
|
|
383
388
|
end
|
|
384
389
|
```
|
|
385
390
|
|
|
386
|
-
### Orphaned
|
|
391
|
+
### Orphaned Contextual Roles
|
|
387
392
|
|
|
388
393
|
When a context object is deleted from your database, its associated roles become orphaned and ignored by Rabarber.
|
|
389
394
|
|
|
390
|
-
To clean up orphaned
|
|
395
|
+
To clean up orphaned roles, use:
|
|
391
396
|
|
|
392
397
|
```rb
|
|
393
398
|
Rabarber.prune
|
|
@@ -395,13 +400,13 @@ Rabarber.prune
|
|
|
395
400
|
|
|
396
401
|
### Context Migrations
|
|
397
402
|
|
|
398
|
-
|
|
403
|
+
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
404
|
|
|
400
405
|
```rb
|
|
401
406
|
# Rename a context class (e.g., when you rename your Ticket model to Task)
|
|
402
407
|
migrate_authorization_context!("Ticket", "Task")
|
|
403
408
|
|
|
404
|
-
# Remove
|
|
409
|
+
# Remove context data (e.g., when you delete the Ticket model entirely)
|
|
405
410
|
delete_authorization_context!("Ticket")
|
|
406
411
|
```
|
|
407
412
|
|
data/lib/rabarber/version.rb
CHANGED
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.2", "<
|
|
22
|
+
spec.required_ruby_version = ">= 3.2", "< 4.1"
|
|
21
23
|
|
|
22
24
|
spec.files = [
|
|
23
25
|
"rabarber.gemspec", "README.md", "CHANGELOG.md", "LICENSE.txt"
|
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.2.
|
|
4
|
+
version: 5.2.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- enjaku4
|
|
@@ -58,11 +58,10 @@ dependencies:
|
|
|
58
58
|
- - "<"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
60
|
version: '8.2'
|
|
61
|
-
description:
|
|
62
|
-
|
|
63
|
-
control that separates authorization from business logic
|
|
61
|
+
description: Simple role-based authorization for Rails applications with multi-tenancy
|
|
62
|
+
support
|
|
64
63
|
email:
|
|
65
|
-
-
|
|
64
|
+
- contact@brownbox.dev
|
|
66
65
|
executables: []
|
|
67
66
|
extensions: []
|
|
68
67
|
extra_rdoc_files: []
|
|
@@ -106,6 +105,8 @@ metadata:
|
|
|
106
105
|
changelog_uri: https://github.com/enjaku4/rabarber/blob/main/CHANGELOG.md
|
|
107
106
|
bug_tracker_uri: https://github.com/enjaku4/rabarber/issues
|
|
108
107
|
documentation_uri: https://github.com/enjaku4/rabarber/blob/main/README.md
|
|
108
|
+
mailing_list_uri: https://github.com/enjaku4/rabarber/discussions
|
|
109
|
+
funding_uri: https://github.com/sponsors/enjaku4
|
|
109
110
|
rubygems_mfa_required: 'true'
|
|
110
111
|
rdoc_options: []
|
|
111
112
|
require_paths:
|
|
@@ -117,7 +118,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
117
118
|
version: '3.2'
|
|
118
119
|
- - "<"
|
|
119
120
|
- !ruby/object:Gem::Version
|
|
120
|
-
version: '
|
|
121
|
+
version: '4.1'
|
|
121
122
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
123
|
requirements:
|
|
123
124
|
- - ">="
|