rabarber 4.1.3 → 5.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 +39 -3
- data/README.md +105 -77
- data/lib/rabarber/configuration.rb +14 -10
- data/lib/rabarber/controllers/concerns/authorization.rb +10 -10
- data/lib/rabarber/core/cache.rb +6 -6
- data/lib/rabarber/core/integrity_checker.rb +44 -0
- data/lib/rabarber/core/permissions.rb +5 -0
- data/lib/rabarber/core/roleable.rb +10 -3
- data/lib/rabarber/core/rule.rb +6 -4
- data/lib/rabarber/helpers/helpers.rb +4 -4
- data/lib/rabarber/helpers/migration_helpers.rb +29 -0
- data/lib/rabarber/input/ar_model.rb +23 -0
- data/lib/rabarber/models/concerns/has_roles.rb +0 -23
- data/lib/rabarber/models/role.rb +21 -29
- data/lib/rabarber/railtie.rb +26 -1
- data/lib/rabarber/version.rb +1 -1
- data/lib/rabarber.rb +4 -6
- data/rabarber.gemspec +3 -4
- metadata +13 -19
- data/lib/rabarber/audit/events/base.rb +0 -49
- data/lib/rabarber/audit/events/roles_assigned.rb +0 -31
- data/lib/rabarber/audit/events/roles_revoked.rb +0 -31
- data/lib/rabarber/audit/events/unauthorized_attempt.rb +0 -27
- data/lib/rabarber/audit/logger.rb +0 -23
- data/lib/rabarber/core/null_roleable.rb +0 -23
- data/lib/rabarber/core/permissions_integrity_checker.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1de40ccff8f5d39718c2c908913c5a67149eeada18598a37b51ec53e900200cc
|
4
|
+
data.tar.gz: d9a02594d2c2215499cd2609d57f3fa14e796d9b3df6d0176b5fa1b510cf2d72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7862b8b5793a4c3076b2e9c34eec91df58b79b629273b7e2fc9a6c97b74d3a42b7ac53bd85bad8ee270549c13a6d8e5d747ea6fe3472b2d72f067aafc247028
|
7
|
+
data.tar.gz: a9f1cbf65045fde78aeac07e6217caca037dc1f85e97065e26a07f1c52bfce46deb4e60513617df6572a0883e792dacbf70a18c0f19d14f31e97171420903cc9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,39 @@
|
|
1
|
+
## v5.0.0
|
2
|
+
|
3
|
+
### Breaking:
|
4
|
+
|
5
|
+
- Dropped support for Ruby 3.1
|
6
|
+
- Dropped support for Rails 7.0
|
7
|
+
- Removed the `must_have_roles` configuration option and its associated behavior
|
8
|
+
- Completely removed Audit Trail feature
|
9
|
+
- Introduced a new `user_model_name` configuration option
|
10
|
+
- Added `with_authorization` method for more granular authorization control
|
11
|
+
- `Rabarber::Role.rename` and `Rabarber::Role.remove` now require the role to exist
|
12
|
+
|
13
|
+
To upgrade to v5.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/77)
|
14
|
+
|
15
|
+
### Features:
|
16
|
+
|
17
|
+
- Added migration helpers to assist with context renaming and removal
|
18
|
+
|
19
|
+
### Bugs:
|
20
|
+
|
21
|
+
- Fixed an error that occurred when Rabarber was used with eager loading disabled
|
22
|
+
- Fixed an issue where authorization rules weren’t cleared on code reload when eager loading was disabled
|
23
|
+
|
24
|
+
### Misc:
|
25
|
+
|
26
|
+
- Optimized various parts of the code and database queries for improved performance
|
27
|
+
- Streamlined the authorization process by requiring the user to be authenticated before access is verified
|
28
|
+
- Rabarber now skips roles with missing instance context and prunes them automatically; missing class context still raises errors
|
29
|
+
|
30
|
+
## v4.1.4
|
31
|
+
|
32
|
+
### Misc:
|
33
|
+
|
34
|
+
- Deprecated `must_have_roles` configuration option
|
35
|
+
- Optimized cache invalidation
|
36
|
+
|
1
37
|
## v4.1.3
|
2
38
|
|
3
39
|
### Bugs:
|
@@ -79,7 +115,7 @@
|
|
79
115
|
|
80
116
|
- Changed Rabarber roles table structure
|
81
117
|
|
82
|
-
To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/
|
118
|
+
To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/58)
|
83
119
|
|
84
120
|
### Features:
|
85
121
|
|
@@ -103,7 +139,7 @@ To upgrade to v3.0.0, please refer to the [migration guide](https://github.com/e
|
|
103
139
|
- Replaced `when_unauthorized` configuration option with an overridable controller method
|
104
140
|
- Renamed `Rabarber::Role.assignees_for` method to `Rabarber::Role.assignees`
|
105
141
|
|
106
|
-
To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/
|
142
|
+
To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/brownboxdev/rabarber/discussions/52)
|
107
143
|
|
108
144
|
### Features:
|
109
145
|
|
@@ -125,7 +161,7 @@ To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/e
|
|
125
161
|
|
126
162
|
- Add 'Audit trail' feature: Logging of role assignments, revocations, and unauthorized access attempts
|
127
163
|
- Add `audit_trail_enabled` configuration option, allowing to enable or disable the audit trail
|
128
|
-
- Deprecate `when_actions_missing` and `when_roles_missing` configuration options
|
164
|
+
- Deprecate `when_actions_missing` and `when_roles_missing` configuration options
|
129
165
|
|
130
166
|
## v1.3.1
|
131
167
|
|
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Rabarber: Simplified Authorization for Rails
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/rabarber)
|
4
|
-
[](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,
|
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, with support for multi-tenancy and fine-grained access control.
|
7
7
|
|
8
8
|
---
|
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 `marketer` has access to marketing-related data.
|
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. You can define such authorization rules easily with Rabarber.
|
13
13
|
|
14
14
|
---
|
15
15
|
|
@@ -29,6 +29,7 @@ class TicketsController < ApplicationController
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
```
|
32
|
+
|
32
33
|
This means that `admin` users can access everything in `TicketsController`, while `manager` role can access only `index` action.
|
33
34
|
|
34
35
|
## Table of Contents
|
@@ -36,26 +37,27 @@ This means that `admin` users can access everything in `TicketsController`, whil
|
|
36
37
|
**Gem usage:**
|
37
38
|
- [Installation](#installation)
|
38
39
|
- [Configuration](#configuration)
|
39
|
-
- [
|
40
|
+
- [Role Assignments](#role-assignments)
|
41
|
+
- [Role Management](#role-management)
|
40
42
|
- [Authorization Rules](#authorization-rules)
|
41
43
|
- [Dynamic Authorization Rules](#dynamic-authorization-rules)
|
42
44
|
- [Context / Multi-tenancy](#context--multi-tenancy)
|
43
45
|
- [When Unauthorized](#when-unauthorized)
|
44
46
|
- [Skip Authorization](#skip-authorization)
|
45
47
|
- [View Helpers](#view-helpers)
|
46
|
-
- [Audit Trail](#audit-trail)
|
47
48
|
|
48
49
|
**Community Resources:**
|
49
50
|
- [Problems?](#problems)
|
50
51
|
- [Contributing](#contributing)
|
51
52
|
- [Code of Conduct](#code-of-conduct)
|
53
|
+
- [Old Versions](#old-versions)
|
52
54
|
|
53
55
|
**Legal:**
|
54
56
|
- [License](#license)
|
55
57
|
|
56
58
|
## Installation
|
57
59
|
|
58
|
-
Add
|
60
|
+
Add Rabarber to your Gemfile:
|
59
61
|
|
60
62
|
```rb
|
61
63
|
gem "rabarber"
|
@@ -67,19 +69,19 @@ Install the gem:
|
|
67
69
|
bundle install
|
68
70
|
```
|
69
71
|
|
70
|
-
|
72
|
+
Generate a migration to create tables for storing roles. Run the generator with the table name used by the model that represents users in your application. For example, if the table is `users`, run:
|
71
73
|
|
72
74
|
```shell
|
73
75
|
rails g rabarber:roles users
|
74
76
|
```
|
75
77
|
|
76
|
-
Rabarber supports UUIDs as primary keys. If your application uses UUIDs, add `--uuid` option
|
78
|
+
Rabarber supports UUIDs as primary keys. If your application uses UUIDs, add `--uuid` option:
|
77
79
|
|
78
80
|
```shell
|
79
81
|
rails g rabarber:roles users --uuid
|
80
82
|
```
|
81
83
|
|
82
|
-
Finally, run the migration
|
84
|
+
Finally, run the migration:
|
83
85
|
|
84
86
|
```shell
|
85
87
|
rails db:migrate
|
@@ -87,34 +89,23 @@ rails db:migrate
|
|
87
89
|
|
88
90
|
## Configuration
|
89
91
|
|
90
|
-
If
|
92
|
+
If customization is required, Rabarber can be configured using `.configure` method in an initializer:
|
91
93
|
|
92
94
|
```rb
|
93
95
|
Rabarber.configure do |config|
|
94
|
-
config.audit_trail_enabled = true
|
95
96
|
config.cache_enabled = true
|
96
97
|
config.current_user_method = :current_user
|
97
|
-
config.
|
98
|
+
config.user_model_name = "User"
|
98
99
|
end
|
99
100
|
```
|
100
101
|
|
101
|
-
- `audit_trail_enabled` determines whether the audit trail functionality is enabled. The audit trail is enabled by default.
|
102
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`
|
104
|
-
- `
|
105
|
-
|
106
|
-
## Roles
|
103
|
+
- `current_user_method` defines the method used to access the currently authenticated user. Default is `:current_user`.
|
104
|
+
- `user_model_name` sets the name of the model representing the user in your application. Default is `"User"`.
|
107
105
|
|
108
|
-
|
109
|
-
|
110
|
-
```rb
|
111
|
-
class User < ApplicationRecord
|
112
|
-
include Rabarber::HasRoles
|
113
|
-
# ...
|
114
|
-
end
|
115
|
-
```
|
106
|
+
## Role Assignments
|
116
107
|
|
117
|
-
|
108
|
+
Rabarber automatically augments your user model (defined via `user_model_name` configuration) with role-related methods.
|
118
109
|
|
119
110
|
**`#assign_roles(*roles, context: nil, create_new: true)`**
|
120
111
|
|
@@ -123,10 +114,13 @@ To assign roles, use:
|
|
123
114
|
```rb
|
124
115
|
user.assign_roles(:accountant, :marketer)
|
125
116
|
```
|
126
|
-
|
117
|
+
|
118
|
+
By default, it will automatically create any roles that don't exist. To assign only existing roles and prevent automatic creation, pass `create_new: false`:
|
119
|
+
|
127
120
|
```rb
|
128
121
|
user.assign_roles(:accountant, :marketer, create_new: false)
|
129
122
|
```
|
123
|
+
|
130
124
|
The method returns an array of roles assigned to the user.
|
131
125
|
|
132
126
|
**`#revoke_roles(*roles, context: nil)`**
|
@@ -136,7 +130,8 @@ To revoke roles, use:
|
|
136
130
|
```rb
|
137
131
|
user.revoke_roles(:accountant, :marketer)
|
138
132
|
```
|
139
|
-
|
133
|
+
|
134
|
+
Roles the user doesn’t have are ignored.
|
140
135
|
|
141
136
|
The method returns an array of roles assigned to the user.
|
142
137
|
|
@@ -148,7 +143,7 @@ To check whether the user has a role, use:
|
|
148
143
|
user.has_role?(:accountant, :marketer)
|
149
144
|
```
|
150
145
|
|
151
|
-
It returns `true` if the user has at least one
|
146
|
+
It returns `true` if the user has at least one of the given roles.
|
152
147
|
|
153
148
|
**`#roles(context: nil)`**
|
154
149
|
|
@@ -166,9 +161,17 @@ To get all roles assigned to the user, grouped by context, use:
|
|
166
161
|
user.all_roles
|
167
162
|
```
|
168
163
|
|
169
|
-
|
164
|
+
**`.assignees(role_name, context: nil)`**
|
170
165
|
|
171
|
-
To
|
166
|
+
To get all the users to whom the role is assigned, use:
|
167
|
+
|
168
|
+
```rb
|
169
|
+
Rabarber::Role.assignees(:admin)
|
170
|
+
```
|
171
|
+
|
172
|
+
## Role Management
|
173
|
+
|
174
|
+
To manipulate roles directly, you can use the following methods:
|
172
175
|
|
173
176
|
**`.add(role_name, context: nil)`**
|
174
177
|
|
@@ -187,9 +190,11 @@ To rename a role, use:
|
|
187
190
|
```rb
|
188
191
|
Rabarber::Role.rename(:admin, :administrator)
|
189
192
|
```
|
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
193
|
|
192
|
-
The
|
194
|
+
The old role must exist. If it doesn’t, an error is raised. If a role with the new name already exists, the method returns `false` and the rename fails.
|
195
|
+
|
196
|
+
The rename also fails if the role is assigned to any user. To force it, use:
|
197
|
+
|
193
198
|
```rb
|
194
199
|
Rabarber::Role.rename(:admin, :administrator, force: true)
|
195
200
|
```
|
@@ -202,9 +207,10 @@ To remove a role, use:
|
|
202
207
|
Rabarber::Role.remove(:admin)
|
203
208
|
```
|
204
209
|
|
205
|
-
|
210
|
+
The old role must exist. If it doesn’t, an error is raised. The method returns `true` if successful.
|
211
|
+
|
212
|
+
If the role is assigned to any user, removal will fail. To force it, use:
|
206
213
|
|
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
214
|
```rb
|
209
215
|
Rabarber::Role.remove(:admin, force: true)
|
210
216
|
```
|
@@ -219,31 +225,35 @@ Rabarber::Role.names
|
|
219
225
|
|
220
226
|
**`.all_names`**
|
221
227
|
|
222
|
-
If you need list all roles available in your application, grouped by context, use:
|
228
|
+
If you need to list all roles available in your application, grouped by context, use:
|
223
229
|
|
224
230
|
```rb
|
225
231
|
Rabarber::Role.all_names
|
226
232
|
```
|
227
233
|
|
228
|
-
**`.assignees(role_name, context: nil)`**
|
229
|
-
|
230
|
-
To get all the users to whom the role is assigned, use:
|
231
|
-
|
232
|
-
```rb
|
233
|
-
Rabarber::Role.assignees(:admin)
|
234
|
-
```
|
235
|
-
|
236
234
|
## Authorization Rules
|
237
235
|
|
238
|
-
Include `Rabarber::Authorization` module
|
236
|
+
Include `Rabarber::Authorization` module in the controller where you want to define authorization rules. Typically, it is `ApplicationController`, but it can be any controller of your choice. Then use `.with_authorization(options = {})` method, which accepts the same options as Rails’ `before_action`, allowing you to perform authorization checks selectively.
|
239
237
|
|
240
238
|
```rb
|
241
239
|
class ApplicationController < ActionController::Base
|
242
240
|
include Rabarber::Authorization
|
241
|
+
|
242
|
+
with_authorization
|
243
|
+
|
244
|
+
# ...
|
245
|
+
end
|
246
|
+
|
247
|
+
class InvoicesController < ApplicationController
|
248
|
+
with_authorization only: [:update, :destroy]
|
249
|
+
|
243
250
|
# ...
|
244
251
|
end
|
245
252
|
```
|
246
|
-
|
253
|
+
|
254
|
+
You must ensure the user is authenticated before authorization checks are performed.
|
255
|
+
|
256
|
+
To define authorization rules, use `.grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)` method.
|
247
257
|
|
248
258
|
The most basic usage of the method is as follows:
|
249
259
|
|
@@ -264,6 +274,7 @@ module Crm
|
|
264
274
|
end
|
265
275
|
end
|
266
276
|
```
|
277
|
+
|
267
278
|
This grants access to `index` action for users with `accountant` or `admin` role, and access to `destroy` action for `admin` users only.
|
268
279
|
|
269
280
|
You can also define controller-wide rules (without `action` argument):
|
@@ -293,9 +304,10 @@ module Crm
|
|
293
304
|
end
|
294
305
|
end
|
295
306
|
```
|
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
307
|
|
298
|
-
|
308
|
+
In this example, `admin` and `manager` have access to all actions in `Crm::BaseController` and its descendants, while `accountant` role has access only to the actions in `Crm::InvoicesController`. Users with `marketer` role can only see the dashboard.
|
309
|
+
|
310
|
+
You can also omit roles to allow unrestricted access:
|
299
311
|
|
300
312
|
```rb
|
301
313
|
class OrdersController < ApplicationController
|
@@ -311,11 +323,10 @@ class InvoicesController < ApplicationController
|
|
311
323
|
end
|
312
324
|
```
|
313
325
|
|
314
|
-
This allows everyone to access `OrdersController` and its
|
326
|
+
This allows everyone to access `OrdersController` and its descendants as well as `index` action in `InvoicesController`.
|
315
327
|
|
316
|
-
|
328
|
+
Rules defined in descendant classes don't override ancestor rules but rather add to them:
|
317
329
|
|
318
|
-
Also keep in mind that rules defined in child classes don't override parent rules but rather add to them:
|
319
330
|
```rb
|
320
331
|
module Crm
|
321
332
|
class BaseController < ApplicationController
|
@@ -331,9 +342,11 @@ module Crm
|
|
331
342
|
end
|
332
343
|
end
|
333
344
|
```
|
345
|
+
|
334
346
|
This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
|
335
347
|
|
336
|
-
This applies
|
348
|
+
This also applies when defining multiple rules for the same controller or action:
|
349
|
+
|
337
350
|
```rb
|
338
351
|
module Crm
|
339
352
|
class OrdersController < ApplicationController
|
@@ -348,11 +361,12 @@ module Crm
|
|
348
361
|
end
|
349
362
|
end
|
350
363
|
```
|
364
|
+
|
351
365
|
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
366
|
|
353
367
|
## Dynamic Authorization Rules
|
354
368
|
|
355
|
-
For more complex
|
369
|
+
For more complex scenarios, Rabarber supports dynamic authorization rules:
|
356
370
|
|
357
371
|
```rb
|
358
372
|
module Crm
|
@@ -394,9 +408,11 @@ module Crm
|
|
394
408
|
end
|
395
409
|
end
|
396
410
|
```
|
397
|
-
|
411
|
+
|
412
|
+
You can pass a dynamic rule as `if` or `unless` argument. You can pass a symbol (method name) or a proc. Symbols refer to instance methods, and procs are evaluated in the controller at request time.
|
398
413
|
|
399
414
|
You can use only dynamic rules without specifying roles if that suits your needs:
|
415
|
+
|
400
416
|
```rb
|
401
417
|
class InvoicesController < ApplicationController
|
402
418
|
grant_access action: :index, if: -> { current_user.company == Company.find(params[:company_id]) }
|
@@ -405,7 +421,9 @@ class InvoicesController < ApplicationController
|
|
405
421
|
end
|
406
422
|
end
|
407
423
|
```
|
408
|
-
|
424
|
+
|
425
|
+
This allows you to use Rabarber as a policy-based authorization library by calling your own custom policy within a dynamic rule:
|
426
|
+
|
409
427
|
```rb
|
410
428
|
class InvoicesController < ApplicationController
|
411
429
|
grant_access action: :index, if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
|
@@ -417,9 +435,9 @@ end
|
|
417
435
|
|
418
436
|
## Context / Multi-tenancy
|
419
437
|
|
420
|
-
Rabarber supports multi-tenancy
|
438
|
+
Rabarber supports multi-tenancy through its context feature. This allows you to define and authorize roles and rules within a specific context.
|
421
439
|
|
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.
|
440
|
+
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. Besides being global, context can also be an instance of an `ActiveRecord` model or a class.
|
423
441
|
|
424
442
|
E.g., consider a model named `Project`, where each project has its owner and regular members. Roles can be defined like this:
|
425
443
|
|
@@ -428,7 +446,7 @@ user.assign_roles(:owner, context: project)
|
|
428
446
|
another_user.assign_roles(:member, context: project)
|
429
447
|
```
|
430
448
|
|
431
|
-
|
449
|
+
You can then check roles like this:
|
432
450
|
|
433
451
|
```rb
|
434
452
|
user.has_role?(:owner, context: project)
|
@@ -441,7 +459,7 @@ A role can also be added using a class as a context, e.g., for project admins wh
|
|
441
459
|
user.assign_roles(:admin, context: Project)
|
442
460
|
```
|
443
461
|
|
444
|
-
And
|
462
|
+
And to check it:
|
445
463
|
|
446
464
|
```rb
|
447
465
|
user.has_role?(:admin, context: Project)
|
@@ -471,7 +489,7 @@ class ProjectsController < ApplicationController
|
|
471
489
|
end
|
472
490
|
```
|
473
491
|
|
474
|
-
|
492
|
+
Role names are scoped by context, i.e. `admin` in a project is different from a global `admin`, or from an `admin` in another project.
|
475
493
|
|
476
494
|
If you want to see all the roles assigned to a user within a specific context, you can use:
|
477
495
|
|
@@ -485,16 +503,29 @@ Or if you want to get all the roles available in a specific context, you can use
|
|
485
503
|
Rabarber::Role.names(context: Project)
|
486
504
|
```
|
487
505
|
|
506
|
+
If a context object (e.g., a project) is deleted, any roles tied to it automatically become irrelevant and are no longer returned by role queries. Rabarber treats them as orphaned and ignores them.
|
507
|
+
|
508
|
+
However, if a context class is renamed (e.g., `Project` becomes `Campaign`), an exception will be raised the next time Rabarber attempts to load roles for that class. This is to ensure you explicitly handle the migration, either by migrating existing roles to the new context or by cleaning up old data.
|
509
|
+
|
510
|
+
To help with such scenarios, Rabarber provides two helper methods that can be called in migrations:
|
511
|
+
|
512
|
+
- `migrate_authorization_context!(old_context, new_context)` - renames a context
|
513
|
+
- `delete_authorization_context!(context)` – removes roles tied to a deleted context
|
514
|
+
|
515
|
+
These are irreversible data migrations.
|
516
|
+
|
488
517
|
## When Unauthorized
|
489
518
|
|
490
|
-
By default,
|
519
|
+
By default, Rabarber redirects back on unauthorized access if the request format is HTML (falling back to the root path), and returns a 401 Unauthorized for other formats.
|
491
520
|
|
492
|
-
|
521
|
+
You can customize this behavior by overriding the private `when_unauthorized` method:
|
493
522
|
|
494
523
|
```rb
|
495
524
|
class ApplicationController < ActionController::Base
|
496
525
|
include Rabarber::Authorization
|
497
526
|
|
527
|
+
with_authorization
|
528
|
+
|
498
529
|
# ...
|
499
530
|
|
500
531
|
private
|
@@ -518,11 +549,11 @@ class TicketsController < ApplicationController
|
|
518
549
|
end
|
519
550
|
```
|
520
551
|
|
521
|
-
This method accepts the same options as `skip_before_action
|
552
|
+
This method accepts the same options as Rails’ `skip_before_action`.
|
522
553
|
|
523
554
|
## View Helpers
|
524
555
|
|
525
|
-
Rabarber
|
556
|
+
Rabarber provides two helpers for use in views: `visible_to(*roles, context: nil, &block)` and `hidden_from(*roles, context: nil, &block)`. To enable them, include `Rabarber::Helpers` in your helper module, typically `ApplicationHelper`.
|
526
557
|
|
527
558
|
```rb
|
528
559
|
module ApplicationHelper
|
@@ -545,16 +576,6 @@ The usage is straightforward:
|
|
545
576
|
<% end %>
|
546
577
|
```
|
547
578
|
|
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
|
-
|
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`.
|
557
|
-
|
558
579
|
## Problems?
|
559
580
|
|
560
581
|
Facing a problem or want to suggest an enhancement?
|
@@ -568,12 +589,19 @@ Encountered a bug?
|
|
568
589
|
|
569
590
|
## Contributing
|
570
591
|
|
571
|
-
Before creating an issue or a pull request, please read the [contributing guidelines](https://github.com/
|
592
|
+
Before creating an issue or a pull request, please read the [contributing guidelines](https://github.com/brownboxdev/rabarber/blob/main/CONTRIBUTING.md).
|
572
593
|
|
573
594
|
## Code of Conduct
|
574
595
|
|
575
|
-
Everyone interacting in the Rabarber project is expected to follow the [code of conduct](https://github.com/
|
596
|
+
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).
|
597
|
+
|
598
|
+
## Old Versions
|
599
|
+
|
600
|
+
Only the latest major version is supported. Older versions are obsolete and not maintained, but their READMEs are available here for reference:
|
601
|
+
|
602
|
+
[v4.x.x](https://github.com/brownboxdev/rabarber/blob/9353e70281971154d5acd70693620197a132c543/README.md) | [v3.x.x](https://github.com/brownboxdev/rabarber/blob/3bb273de7e342004abc7ef07fa4d0a9a3ce3e249/README.md)
|
603
|
+
| [v2.x.x](https://github.com/brownboxdev/rabarber/blob/875b357ea949404ddc3645ad66eddea7ed4e2ee4/README.md) | [v1.x.x](https://github.com/brownboxdev/rabarber/blob/b81428429404e197d70317b763e7b2a21e02c296/README.md)
|
576
604
|
|
577
605
|
## License
|
578
606
|
|
579
|
-
The gem is available as open source under the terms of the [MIT License](https://github.com/
|
607
|
+
The gem is available as open source under the terms of the [MIT License](https://github.com/brownboxdev/rabarber/blob/main/LICENSE.txt).
|
@@ -6,26 +6,30 @@ module Rabarber
|
|
6
6
|
class Configuration
|
7
7
|
include Singleton
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :cache_enabled, :current_user_method
|
10
|
+
attr_accessor :user_model_name
|
10
11
|
|
11
12
|
def initialize
|
12
|
-
@audit_trail_enabled = true
|
13
13
|
@cache_enabled = true
|
14
14
|
@current_user_method = :current_user
|
15
|
-
@
|
15
|
+
@user_model_name = "User"
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
).process)
|
23
|
-
end
|
18
|
+
def cache_enabled=(value)
|
19
|
+
@cache_enabled = Rabarber::Input::Types::Boolean.new(
|
20
|
+
value, Rabarber::ConfigurationError, "Configuration `cache_enabled` must be a Boolean"
|
21
|
+
).process
|
24
22
|
end
|
25
23
|
|
26
24
|
def current_user_method=(method_name)
|
27
25
|
@current_user_method = Rabarber::Input::Types::Symbol.new(
|
28
|
-
method_name, Rabarber::ConfigurationError, "Configuration
|
26
|
+
method_name, Rabarber::ConfigurationError, "Configuration `current_user_method` must be a Symbol or a String"
|
27
|
+
).process
|
28
|
+
end
|
29
|
+
|
30
|
+
def user_model
|
31
|
+
Rabarber::Input::ArModel.new(
|
32
|
+
@user_model_name, Rabarber::ConfigurationError, "Configuration `user_model_name` must be an ActiveRecord model name"
|
29
33
|
).process
|
30
34
|
end
|
31
35
|
end
|
@@ -5,11 +5,17 @@ module Rabarber
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
include Rabarber::Core::Roleable
|
7
7
|
|
8
|
-
included { before_action :verify_access }
|
9
|
-
|
10
8
|
class_methods do
|
9
|
+
def with_authorization(options = {})
|
10
|
+
before_action :with_authorization, **options
|
11
|
+
rescue ArgumentError => e
|
12
|
+
raise Rabarber::Error, e.message
|
13
|
+
end
|
14
|
+
|
11
15
|
def skip_authorization(options = {})
|
12
|
-
skip_before_action :
|
16
|
+
skip_before_action :with_authorization, **options
|
17
|
+
rescue ArgumentError => e
|
18
|
+
raise Rabarber::Error, e.message
|
13
19
|
end
|
14
20
|
|
15
21
|
def grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)
|
@@ -26,15 +32,9 @@ module Rabarber
|
|
26
32
|
|
27
33
|
private
|
28
34
|
|
29
|
-
def
|
30
|
-
Rabarber::Core::PermissionsIntegrityChecker.new(self.class).run! unless Rails.configuration.eager_load
|
31
|
-
|
35
|
+
def with_authorization
|
32
36
|
return if Rabarber::Core::Permissions.access_granted?(roleable, action_name.to_sym, self)
|
33
37
|
|
34
|
-
Rabarber::Audit::Events::UnauthorizedAttempt.trigger(
|
35
|
-
roleable, path: request.path, request_method: request.request_method
|
36
|
-
)
|
37
|
-
|
38
38
|
when_unauthorized
|
39
39
|
end
|
40
40
|
|
data/lib/rabarber/core/cache.rb
CHANGED
@@ -7,16 +7,16 @@ module Rabarber
|
|
7
7
|
module Cache
|
8
8
|
module_function
|
9
9
|
|
10
|
-
def fetch(
|
10
|
+
def fetch(uid, &)
|
11
11
|
return yield unless enabled?
|
12
12
|
|
13
|
-
Rails.cache.fetch(prepare_key(
|
13
|
+
Rails.cache.fetch(prepare_key(uid), expires_in: 1.hour, race_condition_ttl: 5.seconds, &)
|
14
14
|
end
|
15
15
|
|
16
|
-
def delete(*
|
16
|
+
def delete(*uids)
|
17
17
|
return unless enabled?
|
18
18
|
|
19
|
-
Rails.cache.delete_multi(
|
19
|
+
Rails.cache.delete_multi(uids.map { prepare_key(_1) }) if uids.any?
|
20
20
|
end
|
21
21
|
|
22
22
|
def enabled?
|
@@ -27,8 +27,8 @@ module Rabarber
|
|
27
27
|
Rails.cache.delete_matched(/^#{CACHE_PREFIX}/o)
|
28
28
|
end
|
29
29
|
|
30
|
-
def prepare_key(
|
31
|
-
"#{CACHE_PREFIX}:#{Digest::SHA2.hexdigest(Marshal.dump(
|
30
|
+
def prepare_key(uid)
|
31
|
+
"#{CACHE_PREFIX}:#{Digest::SHA2.hexdigest(Marshal.dump(uid))}"
|
32
32
|
end
|
33
33
|
|
34
34
|
CACHE_PREFIX = "rabarber"
|