rabarber 1.4.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23a3e2a6c83a827ad3cd468761de12d2124cdd2f1b022e18661fdf97f21302e7
4
- data.tar.gz: 1b56b41289ce816796856832f07e76453ecfa0c290f1cd8f37f4a83690227abc
3
+ metadata.gz: f719eb670170521e94a58a1c75d30110699ab941da6d5dae151fbf53e0eb9880
4
+ data.tar.gz: bd06646e15ab2eb5ff7b49d2a18316c4b539b95aa98bd2ec1b4de5c6b5fb921a
5
5
  SHA512:
6
- metadata.gz: ac4af855dc20a41b78633fa4c4e946d1c54fad1df93e5f8505954bc4ff2cd43307fc94789718e1f9a563b44a57d3266660b84057f9de1d61afdab1111eb5b506
7
- data.tar.gz: 17408ae706718e23ac594cd7c3144413c78aa69ce1ae0d0a230e5f62400917e1d5a82a22bd727df19391c7cbf235510cbaea8bbf6edc7aba47b8bcab06e6de81
6
+ metadata.gz: 0db86c7d46337decbe9972ea3b0c1ad30e1c66f2b76b3ef27a494ef8c2ecc9bcf35ce3f135360e74045b7f20beb38149f5ca308af0cb3d3a0db829ff9dc7147b
7
+ data.tar.gz: d1a20c732318d12ccfe49338df7338dcf09bd1222fd574e8581d035cb38b55295e501bfa45ef63ea0a556a8c21a28abdad7d06ca765c3f14eed6e6cd98db93be
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## v2.0.0
2
+
3
+ ### Breaking:
4
+
5
+ - Removed `when_actions_missing` and `when_roles_missing` configuration options
6
+ - Replaced `when_unauthorized` configuration option with an overridable controller method
7
+ - Renamed `Rabarber::Role.assignees_for` method to `Rabarber::Role.assignees`
8
+
9
+ To upgrade to v2.0.0, please refer to the [migration guide](https://github.com/enjaku4/rabarber/discussions/52).
10
+
11
+ ### Features:
12
+
13
+ - Added support for UUID primary keys
14
+
15
+ ### Bugs:
16
+
17
+ - Fixed the issue where an error would occur if the user was not authenticated
18
+
19
+ ### Misc:
20
+
21
+ - Significant refactoring and code improvements
22
+
1
23
  ## v1.4.1
2
24
 
3
25
  - Fix an issue where an error could be raised when using controller-wide dynamic rules
data/README.md CHANGED
@@ -9,7 +9,7 @@ Rabarber is a role-based authorization library for Ruby on Rails, primarily desi
9
9
 
10
10
  **Example of Usage**:
11
11
 
12
- Consider a CRM 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.
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
13
 
14
14
  ---
15
15
 
@@ -21,39 +21,65 @@ class TicketsController < ApplicationController
21
21
 
22
22
  grant_access action: :index, roles: :manager
23
23
  def index
24
- ...
24
+ # ...
25
25
  end
26
26
 
27
27
  def delete
28
- ...
28
+ # ...
29
29
  end
30
30
  end
31
31
  ```
32
32
  This means that `admin` users can access everything in `TicketsController`, while `manager` role can access only `index` action.
33
33
 
34
+ ## Table of Contents
35
+
36
+ **Gem usage:**
37
+ - [Installation](#installation)
38
+ - [Configuration](#configuration)
39
+ - [Roles](#roles)
40
+ - [Authorization Rules](#authorization-rules)
41
+ - [Dynamic Authorization Rules](#dynamic-authorization-rules)
42
+ - [When Unauthorized](#when-unauthorized)
43
+ - [View Helpers](#view-helpers)
44
+ - [Audit Trail](#audit-trail)
45
+
46
+ **Community Resources:**
47
+ - [Problems?](#problems)
48
+ - [Contributing](#contributing)
49
+ - [Code of Conduct](#code-of-conduct)
50
+
51
+ **Legal:**
52
+ - [License](#license)
53
+
34
54
  ## Installation
35
55
 
36
56
  Add the Rabarber gem to your Gemfile:
37
57
 
38
- ```
58
+ ```rb
39
59
  gem "rabarber"
40
60
  ```
41
61
 
42
62
  Install the gem:
43
63
 
44
- ```
64
+ ```shell
45
65
  bundle install
46
66
  ```
47
67
 
48
68
  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
69
 
50
- ```
70
+ ```shell
51
71
  rails g rabarber:roles users
52
72
  ```
53
73
 
54
- Finally, run the migration to apply the changes to the database:
74
+ Rabarber supports UUIDs as primary keys. If your application uses UUIDs, add `--uuid` option to the generator:
55
75
 
76
+ ```shell
77
+ rails g rabarber:roles users --uuid
56
78
  ```
79
+
80
+ Finally, run the migration to apply the changes to the database:
81
+
82
+ ```shell
57
83
  rails db:migrate
58
84
  ```
59
85
 
@@ -67,28 +93,13 @@ Rabarber.configure do |config|
67
93
  config.cache_enabled = true
68
94
  config.current_user_method = :current_user
69
95
  config.must_have_roles = false
70
- config.when_unauthorized = -> (controller) {
71
- if controller.request.format.html?
72
- controller.redirect_back fallback_location: controller.root_path
73
- else
74
- controller.head :unauthorized
75
- end
76
- }
77
96
  end
78
97
  ```
79
98
 
80
- - `audit_trail_enabled` must be a boolean determining whether the audit trail functionality is enabled. _The audit trail is enabled by default._
81
- - `cache_enabled` must be a boolean determining whether roles are cached. _Roles are cached by default to avoid unnecessary database queries._ If you want to disable caching, set this option to `false`. If caching is enabled and you need to clear the cache, use `Rabarber::Cache.clear` method.
82
- - `current_user_method` must be a symbol representing the method that returns the currently authenticated user. _The default value is `:current_user`._
83
- - `must_have_roles` must be a boolean determining whether a user with no roles can access endpoints permitted to everyone. _The default value is `false` (allowing users without roles to access endpoints permitted to everyone)._
84
- - `when_unauthorized` must be a proc where you can define the behaviour when access is not authorized. Lambda argument `controller` is an instance of the controller where the code is executed. _By default, the user is redirected back if the request format is HTML; otherwise, a 401 Unauthorized response is sent._
85
-
86
- ### Deprecated Configuration Options
87
-
88
- The following configuration options are deprecated and will be removed in the next major version (see [the discussion](https://github.com/enjaku4/rabarber/discussions/48)):
89
-
90
- - `when_actions_missing` must be a proc where you can define the behaviour when the action specified in `grant_access` method cannot be found in the controller. Lambda argument `missing_actions` is an array of symbols, e.g., `[:index]`, while `context` argument is a hash that looks like this: `{ controller: "InvoicesController" }`. This check is performed when the application is initialized if `eager_load` configuration is enabled in Rails and also on every request. _By default, an error is raised when action is missing._
91
- - `when_roles_missing` must be a proc where you can define the behaviour when the roles specified in `grant_access` method cannot be found in the database. Lambda argument `missing_roles` is an array of symbols, e.g., `[:admin]`, while `context` argument is a hash that looks like this: `{ controller: "InvoicesController", action: "index" }`. This check is performed when the application is initialized if `eager_load` configuration is enabled in Rails and also on every request. _By default, a warning is logged when roles are missing._
99
+ - `audit_trail_enabled` determines whether the audit trail functionality is enabled. The audit trail is enabled by default.
100
+ - `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.
101
+ - `current_user_method` represents the method that returns the currently authenticated user. The default value is `:current_user`.
102
+ - `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).
92
103
 
93
104
  ## Roles
94
105
 
@@ -97,7 +108,7 @@ Include `Rabarber::HasRoles` module in your model representing users in your app
97
108
  ```rb
98
109
  class User < ApplicationRecord
99
110
  include Rabarber::HasRoles
100
- ...
111
+ # ...
101
112
  end
102
113
  ```
103
114
 
@@ -123,7 +134,7 @@ To revoke roles, use:
123
134
  ```rb
124
135
  user.revoke_roles(:accountant, :marketer)
125
136
  ```
126
- If any of the specified roles doesn't exist or the user doesn't have the role you want to revoke, it will be ignored.
137
+ If the user doesn't have the role you want to revoke, it will be ignored.
127
138
 
128
139
  The method returns an array of roles assigned to the user.
129
140
 
@@ -139,7 +150,7 @@ It returns `true` if the user has at least one role and `false` otherwise.
139
150
 
140
151
  **`#roles`**
141
152
 
142
- To view all the roles assigned to the user, use:
153
+ To get all the roles assigned to the user, use:
143
154
 
144
155
  ```rb
145
156
  user.roles
@@ -149,7 +160,7 @@ user.roles
149
160
 
150
161
  To manipulate roles directly, you can use `Rabarber::Role` methods:
151
162
 
152
- **`.add(role)`**
163
+ **`.add(role_name)`**
153
164
 
154
165
  To add a new role, use:
155
166
 
@@ -166,14 +177,14 @@ To rename a role, use:
166
177
  ```rb
167
178
  Rabarber::Role.rename(:admin, :administrator)
168
179
  ```
169
- The first argument is the old name, and the second argument is the new name. This will rename the role and return `true`. If a role with the new name already exists, it will return `false`.
180
+ 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`.
170
181
 
171
182
  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:
172
183
  ```rb
173
184
  Rabarber::Role.rename(:admin, :administrator, force: true)
174
185
  ```
175
186
 
176
- **`.remove(role, force: false)`**
187
+ **`.remove(role_name, force: false)`**
177
188
 
178
189
  To remove a role, use:
179
190
 
@@ -196,12 +207,12 @@ If you need to list all the role names available in your application, use:
196
207
  Rabarber::Role.names
197
208
  ```
198
209
 
199
- **`.assignees_for(role)`**
210
+ **`.assignees(role_name)`**
200
211
 
201
212
  To get all the users to whom the role is assigned, use:
202
213
 
203
214
  ```rb
204
- Rabarber::Role.assignees_for(:admin)
215
+ Rabarber::Role.assignees(:admin)
205
216
  ```
206
217
 
207
218
  ## Authorization Rules
@@ -211,7 +222,7 @@ Include `Rabarber::Authorization` module into the controller that needs authoriz
211
222
  ```rb
212
223
  class ApplicationController < ActionController::Base
213
224
  include Rabarber::Authorization
214
- ...
225
+ # ...
215
226
  end
216
227
  ```
217
228
  This adds `.grant_access(action: nil, roles: nil, if: nil, unless: nil)` method which allows you to define the authorization rules.
@@ -219,22 +230,24 @@ This adds `.grant_access(action: nil, roles: nil, if: nil, unless: nil)` method
219
230
  The most basic usage of the method is as follows:
220
231
 
221
232
  ```rb
222
- class InvoicesController < ApplicationController
233
+ class Crm::InvoicesController < ApplicationController
223
234
  grant_access action: :index, roles: [:accountant, :admin]
224
235
  def index
225
236
  @invoices = Invoice.all
226
237
  @invoices = @invoices.paid if current_user.has_role?(:accountant)
227
- ...
238
+ # ...
228
239
  end
229
240
 
230
241
  grant_access action: :destroy, roles: :admin
231
242
  def destroy
232
- ...
243
+ # ...
233
244
  end
234
245
  end
235
246
  ```
236
247
  This grants access to `index` action for users with `accountant` or `admin` role, and access to `destroy` action for `admin` users only.
237
248
 
249
+ Please note that Rabarber does not provide any built-in data scoping mechanism as it is not a part of the authorization layer and is not necessarily role specific or has anything to do with the current user. The business logic can vary drastically depending on the application, so you're encouraged to limit the data visibility yourself, for example, in the same way as in the example above, where `accountant` role can only see paid invoices.
250
+
238
251
  You can also define controller-wide rules (without `action` argument):
239
252
 
240
253
  ```rb
@@ -243,18 +256,18 @@ class Crm::BaseController < ApplicationController
243
256
 
244
257
  grant_access action: :dashboard, roles: :marketer
245
258
  def dashboard
246
- ...
259
+ # ...
247
260
  end
248
261
  end
249
262
 
250
263
  class Crm::InvoicesController < Crm::BaseController
251
264
  grant_access roles: :accountant
252
265
  def index
253
- ...
266
+ # ...
254
267
  end
255
268
 
256
269
  def delete
257
- ...
270
+ # ...
258
271
  end
259
272
  end
260
273
  ```
@@ -265,31 +278,47 @@ Roles can also be omitted:
265
278
  ```rb
266
279
  class OrdersController < ApplicationController
267
280
  grant_access
268
- ...
281
+ # ...
269
282
  end
270
283
 
271
284
  class InvoicesController < ApplicationController
272
285
  grant_access action: :index
273
286
  def index
274
- ...
287
+ # ...
275
288
  end
276
289
  end
277
290
  ```
278
291
 
279
292
  This allows everyone to access `OrdersController` and its children and also `index` action in `InvoicesController`. This extends to scenarios where there is no user present, i.e. when the method responsible for returning the currently authenticated user in your application returns `nil`.
280
293
 
281
- _Be aware that if the user is not authenticated (the method responsible for returning the currently authenticated user in your application returns `nil`), Rabarber will treat this situation as if the user with no roles assigned was authenticated._
294
+ If the user is not authenticated (the method responsible for returning the currently authenticated user in your application returns `nil`), Rabarber will handle this situation as if the user has no roles.
295
+
296
+ 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 (or unauthenticated users) are not allowed to access anything.
297
+
298
+ Also keep in mind that rules defined in child classes don't override parent rules but rather add to them:
299
+ ```rb
300
+ class Crm::BaseController < ApplicationController
301
+ grant_access roles: :admin
302
+ # ...
303
+ end
304
+
305
+ class Crm::InvoicesController < Crm::BaseController
306
+ grant_access roles: :accountant
307
+ # ...
308
+ end
309
+ ```
310
+ This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
282
311
 
283
- If you've set `must_have_roles` setting to `true`, then, only the users with at least one role can have access. This setting can be useful if your requirements are such that users without roles are not allowed to access anything.
312
+ ## Dynamic Authorization Rules
284
313
 
285
314
  For more complex cases, Rabarber provides dynamic rules:
286
315
 
287
316
  ```rb
288
- class OrdersController < ApplicationController
317
+ class Crm::OrdersController < ApplicationController
289
318
  grant_access roles: :manager, if: :company_manager?, unless: :fired?
290
319
 
291
320
  def index
292
- ...
321
+ # ...
293
322
  end
294
323
 
295
324
  private
@@ -303,7 +332,7 @@ class OrdersController < ApplicationController
303
332
  end
304
333
  end
305
334
 
306
- class InvoicesController < ApplicationController
335
+ class Crm::InvoicesController < ApplicationController
307
336
  grant_access roles: :senior_accountant
308
337
 
309
338
  grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
@@ -311,30 +340,57 @@ class InvoicesController < ApplicationController
311
340
  @invoices = Invoice.all
312
341
  @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
313
342
  @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
314
- ...
343
+ # ...
315
344
  end
316
345
 
317
346
  grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
318
347
  def show
319
- ...
348
+ # ...
320
349
  end
321
350
  end
322
351
  ```
323
- You can pass a dynamic rule as `if` or `unless` argument. It can be a symbol, in which case the method with the same name will be called. Alternatively, it can be a proc, which will be executed within the context of the controller's instance.
352
+ 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. Alternatively, it can be a proc, which will be executed within the context of the controller's instance.
324
353
 
325
- Rules defined in child classes don't override parent rules but rather add to them:
354
+ You can use only dynamic rules without specifying roles if that suits your needs:
326
355
  ```rb
327
- class Crm::BaseController < ApplicationController
328
- grant_access roles: :admin
329
- ...
356
+ class InvoicesController < ApplicationController
357
+ grant_access action: :index, if: -> { current_user.company == Company.find(params[:company_id]) }
358
+ def index
359
+ # ...
360
+ end
361
+ end
362
+ ```
363
+ This basically allows you to use Rabarber as a policy-based authorization library by calling your own custom policy within a dynamic rule:
364
+ ```rb
365
+ class InvoicesController < ApplicationController
366
+ grant_access action: :index, if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
367
+ def index
368
+ # ...
369
+ end
330
370
  end
371
+ ```
331
372
 
332
- class Crm::InvoicesController < Crm::BaseController
333
- grant_access roles: :accountant
334
- ...
373
+ ## When Unauthorized
374
+
375
+ 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.
376
+
377
+ This behavior can be customized by overriding private `when_unauthorized` method:
378
+
379
+ ```rb
380
+ class ApplicationController < ActionController::Base
381
+ include Rabarber::Authorization
382
+
383
+ # ...
384
+
385
+ private
386
+
387
+ def when_unauthorized
388
+ head :not_found # pretend the page doesn't exist
389
+ end
335
390
  end
336
391
  ```
337
- This means that `Crm::InvoicesController` is still accessible to `admin` but is also accessible to `accountant`.
392
+
393
+ The method can be overridden in different controllers, providing flexibility in handling unauthorized access attempts.
338
394
 
339
395
  ## View Helpers
340
396
 
@@ -343,7 +399,7 @@ Rabarber also provides a couple of helpers that can be used in views: `visible_t
343
399
  ```rb
344
400
  module ApplicationHelper
345
401
  include Rabarber::Helpers
346
- ...
402
+ # ...
347
403
  end
348
404
  ```
349
405
 
@@ -375,7 +431,7 @@ The logs are written to the file `log/rabarber_audit.log` unless the `audit_trai
375
431
 
376
432
  Facing a problem or want to suggest an enhancement?
377
433
 
378
- - **Open a Discussion**: If you have a question, experience difficulties using the gem, or have an improvement suggestion, feel free to use the Discussions section.
434
+ - **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.
379
435
 
380
436
  Encountered a bug?
381
437
 
@@ -384,12 +440,12 @@ Encountered a bug?
384
440
 
385
441
  ## Contributing
386
442
 
387
- Before opening an issue or creating a pull request, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md).
388
-
389
- ## License
390
-
391
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
443
+ Before creating an issue or a pull request, please read the [contributing guidelines](https://github.com/enjaku4/rabarber/blob/main/CONTRIBUTING.md).
392
444
 
393
445
  ## Code of Conduct
394
446
 
395
447
  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).
448
+
449
+ ## License
450
+
451
+ The gem is available as open source under the terms of the [MIT License](https://github.com/enjaku4/rabarber/blob/main/LICENSE.txt).
@@ -10,6 +10,8 @@ module Rabarber
10
10
 
11
11
  argument :table_name, type: :string, required: true
12
12
 
13
+ class_option :uuid, type: :boolean, default: false, desc: "Use UUIDs as primary keys"
14
+
13
15
  def create_migrations
14
16
  migration_template "create_rabarber_roles.rb.erb", "db/migrate/create_rabarber_roles.rb"
15
17
  end
@@ -2,14 +2,14 @@
2
2
 
3
3
  class CreateRabarberRoles < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version.to_s %>]
4
4
  def change
5
- create_table :rabarber_roles do |t|
5
+ create_table :rabarber_roles<%= ", id: :uuid" if options[:uuid] %> do |t|
6
6
  t.string :name, null: false, index: { unique: true }
7
7
  t.timestamps
8
8
  end
9
9
 
10
10
  create_table :rabarber_roles_roleables, id: false do |t|
11
- t.belongs_to :role, null: false, index: true, foreign_key: { to_table: :rabarber_roles }
12
- t.belongs_to :roleable, null: false, index: true, foreign_key: { to_table: <%= table_name.to_sym.inspect %> }
11
+ t.belongs_to :role, null: false, index: true, foreign_key: { to_table: :rabarber_roles }<%= ", type: :uuid" if options[:uuid] %>
12
+ t.belongs_to :roleable, null: false, index: true, foreign_key: { to_table: <%= table_name.to_sym.inspect %> }<%= ", type: :uuid" if options[:uuid] %>
13
13
  end
14
14
 
15
15
  add_index :rabarber_roles_roleables, [:role_id, :roleable_id], unique: true
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../logger"
4
+
5
+ module Rabarber
6
+ module Audit
7
+ module Events
8
+ class Base
9
+ attr_reader :roleable, :specifics
10
+
11
+ def self.trigger(roleable, specifics)
12
+ new(roleable, specifics).send(:log)
13
+ end
14
+
15
+ private
16
+
17
+ def initialize(roleable, specifics)
18
+ raise ArgumentError, "Roleable is required for #{self.class} event" if roleable.nil? && !nil_roleable_allowed?
19
+
20
+ @roleable = roleable
21
+ @specifics = specifics
22
+ end
23
+
24
+ def nil_roleable_allowed?
25
+ raise NotImplementedError
26
+ end
27
+
28
+ def log
29
+ Rabarber::Audit::Logger.log(log_level, message)
30
+ end
31
+
32
+ def log_level
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def message
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def identity
41
+ roleable_identity(with_roles: identity_with_roles?)
42
+ end
43
+
44
+ def identity_with_roles?
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def roleable_identity(with_roles:)
49
+ if roleable
50
+ model_name = roleable.model_name.human
51
+ primary_key = roleable.class.primary_key
52
+ roleable_id = roleable.public_send(primary_key)
53
+
54
+ roles = with_roles ? ", roles: #{roleable.roles}" : ""
55
+
56
+ "#{model_name} with #{primary_key}: '#{roleable_id}'#{roles}"
57
+ else
58
+ "Unauthenticated #{Rabarber::HasRoles.roleable_class.model_name.human.downcase}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Audit
5
+ module Events
6
+ class RolesAssigned < Base
7
+ private
8
+
9
+ def nil_roleable_allowed?
10
+ false
11
+ end
12
+
13
+ def log_level
14
+ :info
15
+ end
16
+
17
+ def message
18
+ "[Role Assignment] #{identity} has been assigned the following roles: #{roles_to_assign}, current roles: #{current_roles}"
19
+ end
20
+
21
+ def identity_with_roles?
22
+ false
23
+ end
24
+
25
+ def roles_to_assign
26
+ specifics.fetch(:roles_to_assign)
27
+ end
28
+
29
+ def current_roles
30
+ specifics.fetch(:current_roles)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Audit
5
+ module Events
6
+ class RolesRevoked < Base
7
+ private
8
+
9
+ def nil_roleable_allowed?
10
+ false
11
+ end
12
+
13
+ def log_level
14
+ :info
15
+ end
16
+
17
+ def message
18
+ "[Role Revocation] #{identity} has been revoked from the following roles: #{roles_to_revoke}, current roles: #{current_roles}"
19
+ end
20
+
21
+ def identity_with_roles?
22
+ false
23
+ end
24
+
25
+ def roles_to_revoke
26
+ specifics.fetch(:roles_to_revoke)
27
+ end
28
+
29
+ def current_roles
30
+ specifics.fetch(:current_roles)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rabarber
4
+ module Audit
5
+ module Events
6
+ class UnauthorizedAttempt < Base
7
+ private
8
+
9
+ def nil_roleable_allowed?
10
+ true
11
+ end
12
+
13
+ def log_level
14
+ :warn
15
+ end
16
+
17
+ def message
18
+ "[Unauthorized Attempt] #{identity} attempted to access '#{path}'"
19
+ end
20
+
21
+ def identity_with_roles?
22
+ true
23
+ end
24
+
25
+ def path
26
+ specifics.fetch(:path)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module Rabarber
6
+ module Audit
7
+ class Logger
8
+ include Singleton
9
+
10
+ attr_reader :logger
11
+
12
+ def initialize
13
+ @logger = ::Logger.new(Rails.root.join("log/rabarber_audit.log"))
14
+ end
15
+
16
+ def self.log(log_level, message)
17
+ return unless Rabarber::Configuration.instance.audit_trail_enabled
18
+
19
+ instance.logger.public_send(log_level, message)
20
+ end
21
+ end
22
+ end
23
+ end