rabarber 4.1.4 → 5.1.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.
data/README.md CHANGED
@@ -1,61 +1,40 @@
1
- # Rabarber: Simplified Authorization for Rails
1
+ # Rabarber: Role-Based Authorization for Rails
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rabarber.svg)](http://badge.fury.io/rb/rabarber)
4
- [![Github Actions badge](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
4
+ [![Github Actions badge](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/brownboxdev/rabarber/actions/workflows/ci.yml)
5
5
 
6
- Rabarber is a role-based authorization library for Ruby on Rails. It provides a set of tools for managing user roles and defining authorization rules, supports multi-tenancy and comes with audit logging for enhanced security.
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
 
8
- ---
8
+ **Key Features:**
9
9
 
10
- **Example of Usage**:
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 `marketer` has access to marketing-related data. Such authorization rules can be easily defined with Rabarber.
13
-
14
- ---
15
-
16
- And this is how your controller might look with Rabarber:
17
-
18
- ```rb
19
- class TicketsController < ApplicationController
20
- grant_access roles: :admin
21
-
22
- grant_access action: :index, roles: :manager
23
- def index
24
- # ...
25
- end
26
-
27
- def delete
28
- # ...
29
- end
30
- end
31
- ```
32
- This means that `admin` users can access everything in `TicketsController`, while `manager` role can access only `index` action.
10
+ - Role-based authorization
11
+ - Controller-level access control
12
+ - Multi-tenancy support through contextual roles
13
+ - Dynamic authorization with conditional logic
14
+ - View helpers for role-based content rendering
33
15
 
34
16
  ## Table of Contents
35
17
 
36
- **Gem usage:**
18
+ **Gem Usage:**
37
19
  - [Installation](#installation)
38
20
  - [Configuration](#configuration)
39
- - [Roles](#roles)
40
- - [Authorization Rules](#authorization-rules)
41
- - [Dynamic Authorization Rules](#dynamic-authorization-rules)
42
- - [Context / Multi-tenancy](#context--multi-tenancy)
43
- - [When Unauthorized](#when-unauthorized)
44
- - [Skip Authorization](#skip-authorization)
21
+ - [Role Management](#role-management)
22
+ - [User Role Methods](#user-role-methods)
23
+ - [Controller Authorization](#controller-authorization)
24
+ - [Dynamic Rules](#dynamic-rules)
25
+ - [Multi-tenancy / Context](#multi-tenancy--context)
45
26
  - [View Helpers](#view-helpers)
46
- - [Audit Trail](#audit-trail)
27
+
47
28
 
48
29
  **Community Resources:**
49
- - [Problems?](#problems)
50
30
  - [Contributing](#contributing)
51
- - [Code of Conduct](#code-of-conduct)
52
-
53
- **Legal:**
54
31
  - [License](#license)
32
+ - [Code of Conduct](#code-of-conduct)
33
+ - [Old Versions](#old-versions)
55
34
 
56
35
  ## Installation
57
36
 
58
- Add the Rabarber gem to your Gemfile:
37
+ Add Rabarber to your Gemfile:
59
38
 
60
39
  ```rb
61
40
  gem "rabarber"
@@ -67,19 +46,17 @@ Install the gem:
67
46
  bundle install
68
47
  ```
69
48
 
70
- Next, generate a migration to create tables for storing roles in the database. Make sure to specify the table name of the model representing users in your application as an argument. For instance, if the table name is `users`, run:
49
+ Generate the migration for role storage (replace `users` with your user table name if different):
71
50
 
72
51
  ```shell
73
- rails g rabarber:roles users
74
- ```
75
-
76
- Rabarber supports UUIDs as primary keys. If your application uses UUIDs, add `--uuid` option to the generator:
52
+ # For standard integer IDs
53
+ rails generate rabarber:roles users
77
54
 
78
- ```shell
79
- rails g rabarber:roles users --uuid
55
+ # For UUID primary keys
56
+ rails generate rabarber:roles users --uuid
80
57
  ```
81
58
 
82
- Finally, run the migration to apply the changes to the database:
59
+ Run the migration:
83
60
 
84
61
  ```shell
85
62
  rails db:migrate
@@ -87,493 +64,331 @@ rails db:migrate
87
64
 
88
65
  ## Configuration
89
66
 
90
- If specific customization is required, Rabarber can be configured by using `.configure` method in an initializer:
67
+ Configure Rabarber in an initializer if customization is needed:
91
68
 
92
69
  ```rb
93
70
  Rabarber.configure do |config|
94
- config.audit_trail_enabled = true
95
- config.cache_enabled = true
96
- config.current_user_method = :current_user
97
- config.must_have_roles = false
98
- end
99
- ```
100
-
101
- - `audit_trail_enabled` determines whether the audit trail functionality is enabled. The audit trail is enabled by default.
102
- - `cache_enabled` determines whether roles are cached to avoid unnecessary database queries. Roles are cached by default. If you need to clear the cache, use `Rabarber::Cache.clear` method.
103
- - `current_user_method` represents the method that returns the currently authenticated user. The default value is `:current_user`.
104
- - `must_have_roles` determines whether a user with no roles can access endpoints permitted to everyone. The default value is `false` (allowing users without roles to access such endpoints).
105
-
106
- ## Roles
107
-
108
- Include `Rabarber::HasRoles` module in your model representing users in your application:
109
-
110
- ```rb
111
- class User < ApplicationRecord
112
- include Rabarber::HasRoles
113
- # ...
71
+ config.cache_enabled = true # Enable role caching (default: true)
72
+ config.current_user_method = :current_user # Method to access current user (default: :current_user)
73
+ config.user_model_name = "User" # User model name (default: "User")
114
74
  end
115
75
  ```
116
76
 
117
- This adds the following methods:
77
+ To clear the role cache manually:
118
78
 
119
- **`#assign_roles(*roles, context: nil, create_new: true)`**
120
-
121
- To assign roles, use:
122
-
123
- ```rb
124
- user.assign_roles(:accountant, :marketer)
125
- ```
126
- By default, it will automatically create any roles that don't exist. If you want to assign only existing roles and prevent the creation of new ones, use the method with `create_new: false` argument:
127
79
  ```rb
128
- user.assign_roles(:accountant, :marketer, create_new: false)
80
+ Rabarber::Cache.clear
129
81
  ```
130
- The method returns an array of roles assigned to the user.
131
82
 
132
- **`#revoke_roles(*roles, context: nil)`**
83
+ ## Role Management
133
84
 
134
- To revoke roles, use:
85
+ ### Direct Role Operations
135
86
 
136
87
  ```rb
137
- user.revoke_roles(:accountant, :marketer)
138
- ```
139
- If the user doesn't have the role you want to revoke, it will be ignored.
88
+ # Create a new role
89
+ Rabarber::Role.add(:admin)
140
90
 
141
- The method returns an array of roles assigned to the user.
91
+ # Rename a role
92
+ Rabarber::Role.rename(:admin, :administrator)
93
+ Rabarber::Role.rename(:admin, :administrator, force: true) # Force if role is assigned to users
142
94
 
143
- **`#has_role?(*roles, context: nil)`**
95
+ # Remove a role
96
+ Rabarber::Role.remove(:admin)
97
+ Rabarber::Role.remove(:admin, force: true) # Force if role is assigned to users
144
98
 
145
- To check whether the user has a role, use:
99
+ # List available roles
100
+ Rabarber::Role.names
101
+ Rabarber::Role.all_names # All roles grouped by context
146
102
 
147
- ```rb
148
- user.has_role?(:accountant, :marketer)
103
+ # Get users assigned to a role
104
+ Rabarber::Role.assignees(:admin)
149
105
  ```
150
106
 
151
- It returns `true` if the user has at least one role and `false` otherwise.
107
+ ## User Role Methods
152
108
 
153
- **`#roles(context: nil)`**
109
+ Your user model is automatically augmented with role management methods:
154
110
 
155
- To get the list of roles assigned to the user, use:
111
+ ### Role Assignment
156
112
 
157
113
  ```rb
158
- user.roles
159
- ```
114
+ # Assign roles (creates roles if they don't exist)
115
+ user.assign_roles(:accountant, :manager)
160
116
 
161
- **`#all_roles`**
117
+ # Assign only existing roles
118
+ user.assign_roles(:accountant, :manager, create_new: false)
162
119
 
163
- To get all roles assigned to the user, grouped by context, use:
120
+ # Revoke specific roles
121
+ user.revoke_roles(:accountant, :manager)
164
122
 
165
- ```rb
166
- user.all_roles
123
+ # Revoke all roles
124
+ user.revoke_all_roles
167
125
  ```
168
126
 
169
- ---
170
-
171
- To manipulate roles directly, you can use `Rabarber::Role` methods:
172
-
173
- **`.add(role_name, context: nil)`**
174
-
175
- To add a new role, use:
127
+ ### Role Queries
176
128
 
177
129
  ```rb
178
- Rabarber::Role.add(:admin)
179
- ```
130
+ # Check if user has any of the specified roles
131
+ user.has_role?(:accountant, :manager)
180
132
 
181
- This will create a new role with the specified name and return `true`. If the role already exists, it will return `false`.
182
-
183
- **`.rename(old_role_name, new_role_name, context: nil, force: false)`**
184
-
185
- To rename a role, use:
133
+ # Get user's roles
134
+ user.roles
186
135
 
187
- ```rb
188
- Rabarber::Role.rename(:admin, :administrator)
136
+ # Get all roles grouped by context
137
+ user.all_roles
189
138
  ```
190
- The first argument is the old name, and the second argument is the new name. This will rename the role and return `true`. If the role with a new name already exists, it will return `false`.
191
139
 
192
- The method won't rename the role and will return `false` if it is assigned to any user. To force the rename, use the method with `force: true` argument:
193
- ```rb
194
- Rabarber::Role.rename(:admin, :administrator, force: true)
195
- ```
140
+ ## Controller Authorization
196
141
 
197
- **`.remove(role_name, context: nil, force: false)`**
142
+ ### Basic Setup
198
143
 
199
- To remove a role, use:
144
+ Include the authorization module and configure protection:
200
145
 
201
146
  ```rb
202
- Rabarber::Role.remove(:admin)
203
- ```
204
-
205
- This will remove the role and return `true`. If the role doesn't exist, it will return `false`.
206
-
207
- The method won't remove the role and will return `false` if it is assigned to any user. To force the removal, use the method with `force: true` argument:
208
- ```rb
209
- Rabarber::Role.remove(:admin, force: true)
210
- ```
211
-
212
- **`.names(context: nil)`**
147
+ class ApplicationController < ActionController::Base
148
+ include Rabarber::Authorization
213
149
 
214
- If you need to list the roles available in your application, use:
150
+ with_authorization # Enable authorization for all actions
151
+ end
215
152
 
216
- ```rb
217
- Rabarber::Role.names
153
+ class InvoicesController < ApplicationController
154
+ with_authorization only: [:update, :destroy] # Selective authorization
155
+ end
218
156
  ```
219
157
 
220
- **`.all_names`**
221
-
222
- If you need list all roles available in your application, grouped by context, use:
223
-
224
- ```rb
225
- Rabarber::Role.all_names
226
- ```
158
+ Authorization requires an authenticated user.
227
159
 
228
- **`.assignees(role_name, context: nil)`**
160
+ ### Skip Authorization
229
161
 
230
- To get all the users to whom the role is assigned, use:
162
+ You can also selectively skip authorization:
231
163
 
232
164
  ```rb
233
- Rabarber::Role.assignees(:admin)
165
+ class TicketsController < ApplicationController
166
+ skip_authorization except: [:create, :update, :destroy]
167
+ end
234
168
  ```
235
169
 
236
- ## Authorization Rules
170
+ ### Authorization Rules
237
171
 
238
- Include `Rabarber::Authorization` module into the controller that needs authorization rules to be applied. Typically, it is `ApplicationController`, but it can be any controller of your choice.
172
+ Define access rules using `grant_access`:
239
173
 
240
174
  ```rb
241
- class ApplicationController < ActionController::Base
242
- include Rabarber::Authorization
243
- # ...
244
- end
245
- ```
246
- This adds `.grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)` method which allows you to define the authorization rules.
175
+ class TicketsController < ApplicationController
176
+ # Controller-wide access
177
+ grant_access roles: :admin
247
178
 
248
- The most basic usage of the method is as follows:
179
+ # Action-specific access
180
+ grant_access action: :index, roles: [:manager, :support]
181
+ def index
182
+ # Accessible to admin, manager, and support roles
183
+ end
249
184
 
250
- ```rb
251
- module Crm
252
- class InvoicesController < ApplicationController
253
- grant_access action: :index, roles: [:accountant, :admin]
254
- def index
255
- @invoices = Invoice.all
256
- @invoices = @invoices.paid if current_user.has_role?(:accountant)
257
- # ...
258
- end
259
-
260
- grant_access action: :destroy, roles: :admin
261
- def destroy
262
- # ...
263
- end
185
+ grant_access action: :destroy, roles: :admin
186
+ def destroy
187
+ # Accessible to admin role only
264
188
  end
265
189
  end
266
190
  ```
267
- This grants access to `index` action for users with `accountant` or `admin` role, and access to `destroy` action for `admin` users only.
268
191
 
269
- You can also define controller-wide rules (without `action` argument):
192
+ ### Additive Rules
193
+
194
+ Rules are additive across inheritance chains and for the same actions:
270
195
 
271
196
  ```rb
272
- module Crm
273
- class BaseController < ApplicationController
274
- grant_access roles: [:admin, :manager]
275
-
276
- grant_access action: :dashboard, roles: :marketer
277
- def dashboard
278
- # ...
279
- end
280
- end
197
+ class BaseController < ApplicationController
198
+ grant_access roles: :admin # Admin can access everything
281
199
  end
282
200
 
283
- module Crm
284
- class InvoicesController < Crm::BaseController
285
- grant_access roles: :accountant
286
- def index
287
- # ...
288
- end
201
+ class InvoicesController < BaseController
202
+ grant_access roles: :accountant # Accountant can also access InvoicesController (along with admin)
289
203
 
290
- def delete
291
- # ...
292
- end
293
- end
204
+ grant_access action: :index, roles: :manager
205
+ grant_access action: :index, roles: :supervisor
206
+ # Index is accessible to admin, accountant, manager, and supervisor
294
207
  end
295
208
  ```
296
- This means that `admin` and `manager` have access to all the actions inside `Crm::BaseController` and its children, while `accountant` role has access only to the actions in `Crm::InvoicesController` and its possible children. Users with `marketer` role can only see the dashboard in this example.
297
209
 
298
- Roles can also be omitted:
210
+ ### Unrestricted Access
211
+
212
+ Omit roles to allow unrestricted access:
299
213
 
300
214
  ```rb
301
- class OrdersController < ApplicationController
302
- grant_access
303
- # ...
215
+ class UnrestrictedController < ApplicationController
216
+ grant_access # Allow all users
304
217
  end
305
218
 
306
- class InvoicesController < ApplicationController
307
- grant_access action: :index
308
- def index
309
- # ...
310
- end
219
+ class MixedController < ApplicationController
220
+ grant_access action: :index # Unrestricted index action
221
+ grant_access action: :show, roles: :member # Restricted show action
311
222
  end
312
223
  ```
313
224
 
314
- This allows everyone to access `OrdersController` and its children and also `index` action in `InvoicesController`.
225
+ ### Custom Unauthorized Handling
315
226
 
316
- If you've set `must_have_roles` setting to `true`, then only the users with at least one role can gain access. This setting can be useful if your requirements are such that users without roles are not allowed to access anything.
227
+ Override `when_unauthorized` method to customize unauthorized access behavior:
317
228
 
318
- Also keep in mind that rules defined in child classes don't override parent rules but rather add to them:
319
229
  ```rb
320
- module Crm
321
- class BaseController < ApplicationController
322
- grant_access roles: :admin
323
- # ...
324
- end
325
- end
230
+ class ApplicationController < ActionController::Base
231
+ include Rabarber::Authorization
326
232
 
327
- module Crm
328
- class InvoicesController < Crm::BaseController
329
- grant_access roles: :accountant
330
- # ...
331
- end
332
- end
333
- ```
334
- This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
233
+ with_authorization
335
234
 
336
- This applies as well to multiple rules defined for the same controller or action:
337
- ```rb
338
- module Crm
339
- class OrdersController < ApplicationController
340
- grant_access roles: :manager, context: Order
341
- grant_access roles: :admin
342
-
343
- grant_access action: :show, roles: :client, context: -> { Order.find(params[:id]) }
344
- grant_access action: :show, roles: :accountant
345
- def show
346
- # ...
347
- end
235
+ private
236
+
237
+ def when_unauthorized
238
+ # Default behavior: redirect back (HTML) or return 403 (other formats)
239
+ # Custom behavior example:
240
+ head :not_found # Hide existence of protected resources
348
241
  end
349
242
  end
350
243
  ```
351
- This will add rules for `manager` and `admin` roles for all actions in `Crm::OrdersController`, and for `client` and `accountant` roles for the `show` action.
352
244
 
353
- ## Dynamic Authorization Rules
245
+ ## Dynamic Rules
354
246
 
355
- For more complex cases, Rabarber provides dynamic rules:
247
+ Add conditional logic to authorization rules:
356
248
 
357
249
  ```rb
358
- module Crm
359
- class OrdersController < ApplicationController
360
- grant_access roles: :manager, if: :company_manager?, unless: :fired?
361
-
362
- def index
363
- # ...
364
- end
250
+ class OrdersController < ApplicationController
251
+ # Method-based conditions
252
+ grant_access roles: :manager, if: :company_manager?, unless: :suspended?
365
253
 
366
- private
254
+ # Proc-based conditions
255
+ grant_access action: :show, roles: :client, if: -> { current_user.company_id == Order.find(params[:id]).company_id }
367
256
 
368
- def company_manager?
369
- Company.find(params[:company_id]).manager == current_user
370
- end
257
+ # Dynamic-only rules (no roles required, can be used with custom policies)
258
+ grant_access action: :index, if: -> { OrdersPolicy.new(current_user).can_access?(:index) }
371
259
 
372
- def fired?
373
- current_user.fired?
374
- end
375
- end
376
- end
260
+ private
377
261
 
378
- module Crm
379
- class InvoicesController < ApplicationController
380
- grant_access roles: :senior_accountant
381
-
382
- grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
383
- def index
384
- @invoices = Invoice.all
385
- @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
386
- @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
387
- # ...
388
- end
389
-
390
- grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
391
- def show
392
- # ...
393
- end
262
+ def company_manager?
263
+ current_user.manager_of?(Company.find(params[:company_id]))
394
264
  end
395
- end
396
- ```
397
- You can pass a dynamic rule as `if` or `unless` argument. It can be a symbol, in which case the method with that name will be called, or alternatively it can be a proc that will be executed within the context of the controller instance at request time.
398
265
 
399
- You can use only dynamic rules without specifying roles if that suits your needs:
400
- ```rb
401
- class InvoicesController < ApplicationController
402
- grant_access action: :index, if: -> { current_user.company == Company.find(params[:company_id]) }
403
- def index
404
- # ...
405
- end
406
- end
407
- ```
408
- This basically allows you to use Rabarber as a policy-based authorization library by calling your own custom policy within a dynamic rule:
409
- ```rb
410
- class InvoicesController < ApplicationController
411
- grant_access action: :index, if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
412
- def index
413
- # ...
266
+ def suspended?
267
+ current_user.suspended?
414
268
  end
415
269
  end
416
270
  ```
417
271
 
418
- ## Context / Multi-tenancy
419
-
420
- Rabarber supports multi-tenancy by providing a context feature. This allows you to define and authorize roles and rules within a specific context.
272
+ ## Multi-tenancy / Context
421
273
 
422
- Every Rabarber method can accept a context as an additional keyword argument. By default, the context is set to `nil`, meaning the roles are global. Thus, all examples from other sections of this README are valid for global roles. Apart from being global, the context can be an instance of ActiveRecord model or a class.
274
+ All Rabarber methods accept a `context` parameter, allowing you to work with roles within specific scopes rather than globally.
423
275
 
424
- E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
276
+ ### Contextual Role Assignment
425
277
 
426
278
  ```rb
279
+ # Assign roles within a specific model instance
427
280
  user.assign_roles(:owner, context: project)
428
- another_user.assign_roles(:member, context: project)
429
- ```
430
-
431
- Then the roles can be verified:
281
+ user.assign_roles(:member, context: project)
432
282
 
433
- ```rb
434
- user.has_role?(:owner, context: project)
435
- another_user.has_role?(:member, context: project)
436
- ```
437
-
438
- A role can also be added using a class as a context, e.g., for project admins who can manage all projects:
439
-
440
- ```rb
283
+ # Assign roles within a model class (e.g., project admin)
441
284
  user.assign_roles(:admin, context: Project)
442
- ```
443
285
 
444
- And then it can also be verified:
445
-
446
- ```rb
286
+ # Check contextual roles
287
+ user.has_role?(:owner, context: project)
447
288
  user.has_role?(:admin, context: Project)
289
+
290
+ # Get roles within context
291
+ user.roles(context: project)
292
+ Rabarber::Role.names(context: Project)
448
293
  ```
449
294
 
450
- In authorization rules, the context can be used in the same way, but it also can be a proc or a symbol (similar to dynamic rules):
295
+ ### Contextual Authorization
451
296
 
452
297
  ```rb
453
298
  class ProjectsController < ApplicationController
299
+ # Class-based context
454
300
  grant_access roles: :admin, context: Project
455
301
 
456
- grant_access action: :show, roles: :member, context: :project
457
- def show
458
- # ...
459
- end
302
+ # Instance-based context (method)
303
+ grant_access action: :show, roles: :member, context: :current_project
460
304
 
305
+ # Instance-based context (proc)
461
306
  grant_access action: :update, roles: :owner, context: -> { Project.find(params[:id]) }
462
- def update
463
- # ...
464
- end
465
307
 
466
308
  private
467
309
 
468
- def project
469
- Project.find(params[:id])
310
+ def current_project
311
+ @current_project ||= Project.find(params[:id])
470
312
  end
471
313
  end
472
314
  ```
473
315
 
474
- It's important to note that role names are not unique globally but are unique within the scope of their context. E.g., `user.assign_roles(:admin, context: Project)` and `user.assign_roles(:admin)` assign different roles to the user. The same as `Rabarber::Role.add(:admin, context: Project)` and `Rabarber::Role.add(:admin)` create different roles.
475
-
476
- If you want to see all the roles assigned to a user within a specific context, you can use:
477
-
478
- ```rb
479
- user.roles(context: project)
480
- ```
316
+ ### Context Migrations
481
317
 
482
- Or if you want to get all the roles available in a specific context, you can use:
318
+ Handle context changes when models are renamed or removed. These are irreversible data migrations.
483
319
 
484
320
  ```rb
485
- Rabarber::Role.names(context: Project)
486
- ```
487
-
488
- ## When Unauthorized
489
-
490
- By default, in the event of an unauthorized attempt, Rabarber redirects the user back if the request format is HTML (with fallback to the root path), and returns a 401 (Unauthorized) status code otherwise.
321
+ # Rename a context class (e.g., when you rename your Project model to Campaign)
322
+ migrate_authorization_context!("Project", "Campaign")
491
323
 
492
- This behavior can be customized by overriding private `when_unauthorized` method:
493
-
494
- ```rb
495
- class ApplicationController < ActionController::Base
496
- include Rabarber::Authorization
497
-
498
- # ...
499
-
500
- private
501
-
502
- def when_unauthorized
503
- head :not_found # pretend the page doesn't exist
504
- end
505
- end
324
+ # Remove orphaned context data (e.g., when you delete a model entirely)
325
+ delete_authorization_context!("DeletedModel")
506
326
  ```
507
327
 
508
- The method can be overridden in different controllers, providing flexibility in handling unauthorized access attempts.
509
-
510
- ## Skip Authorization
511
-
512
- To skip authorization, use `.skip_authorization(options = {})` method:
513
-
514
- ```rb
515
- class TicketsController < ApplicationController
516
- skip_authorization only: :index
517
- # ...
518
- end
519
- ```
520
-
521
- This method accepts the same options as `skip_before_action` method in Rails.
522
-
523
328
  ## View Helpers
524
329
 
525
- Rabarber also provides a couple of helpers that can be used in views: `visible_to(*roles, context: nil, &block)` and `hidden_from(*roles, context: nil, &block)`. To use them, simply include `Rabarber::Helpers` in the desired helper. Usually it is `ApplicationHelper`, but it can be any helper of your choice.
330
+ Include view helpers in your application:
526
331
 
527
332
  ```rb
528
333
  module ApplicationHelper
529
334
  include Rabarber::Helpers
530
- # ...
531
335
  end
532
336
  ```
533
337
 
534
- The usage is straightforward:
338
+ Use conditional rendering based on roles:
535
339
 
536
340
  ```erb
537
341
  <%= visible_to(:admin, :manager) do %>
538
- <p>Visible only to admins and managers</p>
342
+ <div class="admin-panel">
343
+ <!-- Admin/Manager content -->
344
+ </div>
539
345
  <% end %>
540
- ```
541
346
 
542
- ```erb
543
- <%= hidden_from(:accountant) do %>
544
- <p>Accountant cannot see this</p>
347
+ <%= hidden_from(:guest) do %>
348
+ <div class="member-content">
349
+ <!-- Content hidden from guests -->
350
+ </div>
545
351
  <% end %>
546
- ```
547
-
548
- ## Audit Trail
549
-
550
- Rabarber supports audit trail, which provides a record of user access control activity. This feature logs the following events:
551
352
 
552
- - Role assignments to users
553
- - Role revocations from users
554
- - Unauthorized access attempts
555
-
556
- The logs are written to the file `log/rabarber_audit.log` unless the `audit_trail_enabled` configuration option is set to `false`.
353
+ <!-- With context -->
354
+ <%= visible_to(:owner, context: @project) do %>
355
+ <button>Delete Project</button>
356
+ <% end %>
357
+ ```
557
358
 
558
- ## Problems?
359
+ ## Contributing
559
360
 
560
- Facing a problem or want to suggest an enhancement?
361
+ ### Getting Help
362
+ Have a question or need assistance? Open a discussion in our [discussions section](https://github.com/brownboxdev/rabarber/discussions) for:
363
+ - Usage questions
364
+ - Implementation guidance
365
+ - Feature suggestions
561
366
 
562
- - **Open a Discussion**: If you have a question, experience difficulties using the gem, or have a suggestion for improvements, feel free to use the Discussions section.
367
+ ### Reporting Issues
368
+ Found a bug? Please [create an issue](https://github.com/brownboxdev/rabarber/issues) with:
369
+ - A clear description of the problem
370
+ - Steps to reproduce the issue
371
+ - Your environment details (Rails version, Ruby version, etc.)
563
372
 
564
- Encountered a bug?
373
+ ### Contributing Code
374
+ Ready to contribute? You can:
375
+ - Fix bugs by submitting pull requests
376
+ - Improve documentation
377
+ - Add new features (please discuss first in our [discussions section](https://github.com/brownboxdev/rabarber/discussions))
565
378
 
566
- - **Create an Issue**: If you've identified a bug, please create an issue. Be sure to provide detailed information about the problem, including the steps to reproduce it.
567
- - **Contribute a Solution**: Found a fix for the issue? Feel free to create a pull request with your changes.
379
+ Before contributing, please read the [contributing guidelines](https://github.com/brownboxdev/rabarber/blob/main/CONTRIBUTING.md)
568
380
 
569
- ## Contributing
381
+ ## License
570
382
 
571
- Before creating an issue or a pull request, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md).
383
+ The gem is available as open source under the terms of the [MIT License](https://github.com/brownboxdev/rabarber/blob/main/LICENSE.txt).
572
384
 
573
385
  ## Code of Conduct
574
386
 
575
- 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).
387
+ Everyone interacting in the Rabarber project is expected to follow the [code of conduct](https://github.com/brownboxdev/rabarber/blob/main/CODE_OF_CONDUCT.md).
576
388
 
577
- ## License
389
+ ## Old Versions
390
+
391
+ Only the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:
578
392
 
579
- The gem is available as open source under the terms of the [MIT License](https://github.com/enjaku4/rabarber/blob/main/LICENSE.txt).
393
+ [v4.x.x](https://github.com/brownboxdev/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/brownboxdev/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
394
+ | [v2.x.x](https://github.com/brownboxdev/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/brownboxdev/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)