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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b6af1a1a593e84b481d92883ca7adf385e2ee1fc9a3fb5615818169fc595444
4
- data.tar.gz: 97738fdc3bb6e3463b8e331088982a4dcd40e62e4251977ee63cc8b742fcb96a
3
+ metadata.gz: 23cc4a1c2e65a3b450653b65df49633601756ada11844173b0c6314c41067ca9
4
+ data.tar.gz: 95153a3172c58bf636cb4d17981ba70336a292368f6bac26c2b6a107c4fcbaaa
5
5
  SHA512:
6
- metadata.gz: 535dc707a59522461ad3b5bd26d0e394008454c738ff52e84fa6cd7ebfdd26bb441405326563b2ba4a2229008d0ddcf3b6b426dc9d21186896e53aadf99b5fa6
7
- data.tar.gz: b32d8c3983deb895e7cefe2e3d273cbad0542477c1bad134a7681ac5ae1e390725f7e34b964de4c0f264fcb1b0d21546cb70b7d89ca29cb3c513e831d281c89c
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 and prunes them automatically; missing class context still raises errors
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
  [![Github Actions badge](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
6
6
  [![License](https://img.shields.io/github/license/enjaku4/rabarber.svg)](LICENSE)
7
7
 
8
- 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. It supports multi-tenancy through contextual roles, dynamic authorization with conditional logic, and includes view helpers for role-based content rendering.
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 interact with invoices but cannot access marketing information, while the role `analyst` has access to marketing-related data. You can define such authorization rules easily with Rabarber.
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
- And here's how your controller might look:
14
+ ___
15
+
16
+ And this is how your controller might look with Rabarber:
15
17
 
16
18
  ```rb
17
- class InvoicesController < ApplicationController
18
- grant_access roles: :admin # Admin can access everything
19
+ class TicketsController < ApplicationController
20
+ grant_access roles: :admin
19
21
 
20
- grant_access action: :index, roles: [:accountant, :analyst]
22
+ grant_access action: :index, roles: :manager
21
23
  def index
22
- # Accessible to both analysts and accountants
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
- # Accessible to admins only
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
- - [Controller Authorization](#controller-authorization)
49
- - [Dynamic Rules](#dynamic-rules)
50
- - [Multi-tenancy / Context](#multi-tenancy--context)
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 for role storage (replace `users` with your user table name if different):
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
- Configure Rabarber in an initializer if customization is needed:
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. To clear the role cache manually:
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(:accountant, :manager)
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(:accountant, :manager)
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
- ### Direct Role Operations
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. 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.
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
- ## Controller Authorization
165
+ ## Authorization
171
166
 
172
- ### Basic Setup
167
+ ### Setup
173
168
 
174
- Include the authorization module and configure protection:
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
- Authorization requires an authenticated user to be present.
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 except: [:create, :update, :destroy]
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 access rules using `grant_access`:
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
- ### Additive Rules
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
- ### Unrestricted Access
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
- ### Custom Unauthorized Handling
252
+ ## Dynamic Authorization Rules
264
253
 
265
- Override `when_unauthorized` method to customize unauthorized access behavior:
254
+ For more complex scenarios, Rabarber supports dynamic authorization rules:
266
255
 
267
256
  ```rb
268
- class ApplicationController < ActionController::Base
269
- include Rabarber::Authorization
257
+ class OrdersController < ApplicationController
258
+ grant_access roles: :manager, unless: -> { current_user.suspended? }
270
259
 
271
- with_authorization
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 when_unauthorized
276
- head :not_found # Custom behavior to hide existence of protected resources
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
- By default, when unauthorized, Rabarber will redirect back (HTML format) or return 403 (other formats).
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
- Add conditional logic to authorization rules:
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 OrdersController < ApplicationController
289
- # Method-based conditions
290
- grant_access roles: :manager, if: :company_manager?, unless: :suspended?
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
- # Dynamic-only rules (no roles required, can be used with custom policies)
299
- grant_access action: :index, if: -> { OrdersPolicy.new(current_user).index? }
300
- def index
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 company_manager?
307
- current_user.manager_of?(Company.find(params[:company_id]))
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
- def suspended?
311
- current_user.suspended?
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
- ## Multi-tenancy / Context
315
+ The `when_unauthorized` method can be overridden in any controller to provide controller-specific unauthorized access handling.
317
316
 
318
- 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.
317
+ ## Context / Multi-tenancy
319
318
 
320
- ### Contextual Role Assignment
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: Project)
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 Context
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 context roles, use:
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
- Handle context changes when models are renamed or removed. These are irreversible data migrations.
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 orphaned context data (e.g., when you delete the Ticket model entirely)
409
+ # Remove context data (e.g., when you delete the Ticket model entirely)
405
410
  delete_authorization_context!("Ticket")
406
411
  ```
407
412
 
@@ -61,7 +61,7 @@ module Rabarber
61
61
  end
62
62
 
63
63
  def when_unauthorized
64
- request.format.html? ? redirect_back(fallback_location: root_path) : head(:forbidden)
64
+ request.format.html? ? redirect_back_or_to(root_path) : head(:forbidden)
65
65
  end
66
66
  end
67
67
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "5.2.4"
4
+ VERSION = "5.2.5"
5
5
  end
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 = ["enjaku4@icloud.com"]
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 = "Rabarber provides role-based authorization for Ruby on Rails applications with support for multi-tenancy, dynamic rules, and clean controller-level access control that separates authorization from business logic"
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", "< 3.5"
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
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: Rabarber provides role-based authorization for Ruby on Rails applications
62
- with support for multi-tenancy, dynamic rules, and clean controller-level access
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
- - enjaku4@icloud.com
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: '3.5'
121
+ version: '4.1'
121
122
  required_rubygems_version: !ruby/object:Gem::Requirement
122
123
  requirements:
123
124
  - - ">="