rabarber 1.3.1 → 1.4.1

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: fc0447acdc988dc558859b695d24d227697258a92bb1f444944fb76bfbace526
4
- data.tar.gz: 975f4377d5cc4fb2f28c42060012b67b3a85fea589aadb5106c33ecb943cde4a
3
+ metadata.gz: 23a3e2a6c83a827ad3cd468761de12d2124cdd2f1b022e18661fdf97f21302e7
4
+ data.tar.gz: 1b56b41289ce816796856832f07e76453ecfa0c290f1cd8f37f4a83690227abc
5
5
  SHA512:
6
- metadata.gz: cbfed2814ae750ec0b2870126818cb67c6b55c28a7529d8ab2db3364adc463768853b41ac89ba4653d014aa1024622ae261f5b2c8afcc10b99194fd916a40266
7
- data.tar.gz: 7b77adeb8cc95acc4b104e6c39c6366b466262a8b0df0584d9e7d99d6369d96d01779a5962c7963696bccf7a941578a82774f2af23ac76d4a515bf37f7bed2ad
6
+ metadata.gz: ac4af855dc20a41b78633fa4c4e946d1c54fad1df93e5f8505954bc4ff2cd43307fc94789718e1f9a563b44a57d3266660b84057f9de1d61afdab1111eb5b506
7
+ data.tar.gz: 17408ae706718e23ac594cd7c3144413c78aa69ce1ae0d0a230e5f62400917e1d5a82a22bd727df19391c7cbf235510cbaea8bbf6edc7aba47b8bcab06e6de81
data/CHANGELOG.md CHANGED
@@ -1,21 +1,31 @@
1
- ## 1.3.1
1
+ ## v1.4.1
2
+
3
+ - Fix an issue where an error could be raised when using controller-wide dynamic rules
4
+
5
+ ## v1.4.0
6
+
7
+ - Add 'Audit trail' feature: Logging of role assignments, revocations, and unauthorized access attempts
8
+ - Add `audit_trail_enabled` configuration option, allowing to enable or disable the audit trail
9
+ - Deprecate `when_actions_missing` and `when_roles_missing` configuration options (see [the discussion](https://github.com/enjaku4/rabarber/discussions/48))
10
+
11
+ ## v1.3.1
2
12
 
3
13
  - Add `Rabarber::Role.assignees_for` method
4
14
  - Fix inconsistent behavior where passing `nil` as a role name to role management methods would raise an `ActiveRecord` error instead of `Rabarber` error
5
15
  - Various minor code improvements
6
16
 
7
- ## 1.3.0
17
+ ## v1.3.0
8
18
 
9
19
  - Add methods to directly add, rename, and remove roles
10
20
  - Modify `Rabarber::HasRoles#assign_roles` and `Rabarber::HasRoles#revoke_roles` methods to return the list of roles assigned to the user
11
21
  - Minor performance improvements
12
22
 
13
- ## 1.2.2
23
+ ## v1.2.2
14
24
 
15
25
  - Refactor to improve readability and maintainability
16
26
  - Fix minor code errors
17
27
 
18
- ## 1.2.1
28
+ ## v1.2.1
19
29
 
20
30
  - Cache roles to avoid unnecessary database queries
21
31
  - Introduce `cache_enabled` configuration option allowing to enable or disable role caching
@@ -23,61 +33,61 @@
23
33
  - Fix an issue where an error would be raised if the user is not authenticated
24
34
  - Various minor improvements
25
35
 
26
- ## 1.2.0
36
+ ## v1.2.0
27
37
 
28
38
  - Enhance handling of missing actions and roles specified in `grant_access` method by raising an error for missing actions and logging a warning for missing roles
29
39
  - Introduce `when_actions_missing` and `when_roles_missing` configuration options, allowing to customize the behavior when actions or roles are not found
30
40
 
31
- ## 1.1.0
41
+ ## v1.1.0
32
42
 
33
43
  - Add support for `unless` argument in `grant_access` method, allowing to define negated dynamic rules
34
44
  - Fix a bug where specifying a dynamic rule as a symbol without specifying an action would result in an error
35
45
 
36
- ## 1.0.5
46
+ ## v1.0.5
37
47
 
38
48
  - Add co-author: [trafium](https://github.com/trafium)
39
49
 
40
- ## 1.0.4
50
+ ## v1.0.4
41
51
 
42
52
  - Allow to use strings as role names
43
53
 
44
- ## 1.0.3
54
+ ## v1.0.3
45
55
 
46
56
  - Enhance clarity by improving error types and messages
47
57
  - Resolve inconsistency in types of role names
48
58
 
49
- ## 1.0.2
59
+ ## v1.0.2
50
60
 
51
61
  - Various enhancements for gem development and release
52
62
  - Modify `Rabarber::HasRoles#roles` method to return an array of role names instead of `Rabarber::Role` objects
53
63
 
54
- ## 1.0.1
64
+ ## v1.0.1
55
65
 
56
66
  - Various enhancements for gem development
57
67
 
58
- ## 1.0.0
68
+ ## v1.0.0
59
69
 
60
70
  - Drop support for Ruby 2.7
61
71
  - Add support for Ruby 3.3
62
72
  - Various minor improvements
63
73
 
64
- ## 0.1.5
74
+ ## v0.1.5
65
75
 
66
76
  - Add missing `foreign_key` option to `CreateRabarberRoles` migration
67
77
  - Allow only lowercase alphanumeric characters and underscores in role names
68
78
 
69
- ## 0.1.4
79
+ ## v0.1.4
70
80
 
71
81
  - Remove `Rabarber::HasRoles#role?` method as unnecessary
72
82
 
73
- ## 0.1.3
83
+ ## v0.1.3
74
84
 
75
85
  - Fully revise and update README for clarity
76
86
 
77
- ## 0.1.2
87
+ ## v0.1.2
78
88
 
79
89
  - Fix check that `Rabarber::HasRoles` can only be included once
80
90
 
81
- ## 0.1.1
91
+ ## v0.1.1
82
92
 
83
93
  - Initial release
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/rabarber.svg)](http://badge.fury.io/rb/rabarber)
4
4
  [![Github Actions badge](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml/badge.svg)](https://github.com/enjaku4/rabarber/actions/workflows/ci.yml)
5
5
 
6
- Rabarber is a role-based authorization library for Ruby on Rails, designed primarily for use in the application's web layer (specifically controllers and views) but not limited to that. It provides tools for managing user roles and defining authorization rules and mainly focuses on answering the question: 'Who has access to which endpoints?'.
6
+ Rabarber is a role-based authorization library for Ruby on Rails, primarily designed for use in the web layer of your application but not limited to that. It provides a set of tools for managing user roles and defining authorization rules, along with audit logging for enhanced security.
7
7
 
8
8
  ---
9
9
 
@@ -63,26 +63,32 @@ If specific customization is required, Rabarber can be configured by using `.con
63
63
 
64
64
  ```rb
65
65
  Rabarber.configure do |config|
66
+ config.audit_trail_enabled = true
66
67
  config.cache_enabled = true
67
68
  config.current_user_method = :current_user
68
69
  config.must_have_roles = false
69
- config.when_actions_missing = -> (missing_actions, context) { ... }
70
- config.when_roles_missing = -> (missing_roles, context) { ... }
71
- config.when_unauthorized = -> (controller) { ... }
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
+ }
72
77
  end
73
78
  ```
74
79
 
80
+ - `audit_trail_enabled` must be a boolean determining whether the audit trail functionality is enabled. _The audit trail is enabled by default._
75
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.
76
-
77
82
  - `current_user_method` must be a symbol representing the method that returns the currently authenticated user. _The default value is `:current_user`._
78
-
79
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._
80
85
 
81
- - `when_actions_missing` must be a proc where you can define the behaviour when the actions specified in `grant_access` method cannot be found in the controller (`missing_actions` is an array of symbols e.g., `[:index]`, `context` is a hash that looks like this: `{ controller: "InvoicesController" }`). This check is performed on every request and when the application is initialized if `eager_load` configuration is enabled in Rails. _By default, an error is raised when actions are missing._
86
+ ### Deprecated Configuration Options
82
87
 
83
- - `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 (`missing_roles` is an array of symbols e.g., `[:admin]`, `context` is a hash that looks like this: `{ controller: "InvoicesController", action: "index" }`). This check is performed on every request and when the application is initialized if `eager_load` configuration is enabled in Rails. _By default, only a warning is logged when roles are missing._
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)):
84
89
 
85
- - `when_unauthorized` must be a proc where you can define the behaviour when access is not authorized (`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._
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._
86
92
 
87
93
  ## Roles
88
94
 
@@ -97,20 +103,20 @@ end
97
103
 
98
104
  This adds the following methods:
99
105
 
100
- **`#assign_roles`**
106
+ **`#assign_roles(*roles, create_new: true)`**
101
107
 
102
108
  To assign roles, use:
103
109
 
104
110
  ```rb
105
111
  user.assign_roles(:accountant, :marketer)
106
112
  ```
107
- By default, `#assign_roles` method 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:
113
+ 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:
108
114
  ```rb
109
115
  user.assign_roles(:accountant, :marketer, create_new: false)
110
116
  ```
111
117
  The method returns an array of roles assigned to the user.
112
118
 
113
- **`#revoke_roles`**
119
+ **`#revoke_roles(*roles)`**
114
120
 
115
121
  To revoke roles, use:
116
122
 
@@ -121,7 +127,7 @@ If any of the specified roles doesn't exist or the user doesn't have the role yo
121
127
 
122
128
  The method returns an array of roles assigned to the user.
123
129
 
124
- **`#has_role?`**
130
+ **`#has_role?(*roles)`**
125
131
 
126
132
  To check whether the user has a role, use:
127
133
 
@@ -143,7 +149,7 @@ user.roles
143
149
 
144
150
  To manipulate roles directly, you can use `Rabarber::Role` methods:
145
151
 
146
- **`.add`**
152
+ **`.add(role)`**
147
153
 
148
154
  To add a new role, use:
149
155
 
@@ -153,7 +159,7 @@ Rabarber::Role.add(:admin)
153
159
 
154
160
  This will create a new role with the specified name and return `true`. If the role already exists, it will return `false`.
155
161
 
156
- **`.rename`**
162
+ **`.rename(old_role_name, new_role_name, force: false)`**
157
163
 
158
164
  To rename a role, use:
159
165
 
@@ -167,7 +173,7 @@ The method won't rename the role and will return `false` if it is assigned to an
167
173
  Rabarber::Role.rename(:admin, :administrator, force: true)
168
174
  ```
169
175
 
170
- **`.remove`**
176
+ **`.remove(role, force: false)`**
171
177
 
172
178
  To remove a role, use:
173
179
 
@@ -190,7 +196,7 @@ If you need to list all the role names available in your application, use:
190
196
  Rabarber::Role.names
191
197
  ```
192
198
 
193
- **`.assignees_for`**
199
+ **`.assignees_for(role)`**
194
200
 
195
201
  To get all the users to whom the role is assigned, use:
196
202
 
@@ -208,7 +214,7 @@ class ApplicationController < ActionController::Base
208
214
  ...
209
215
  end
210
216
  ```
211
- This adds `.grant_access` method which allows you to define the authorization rules.
217
+ This adds `.grant_access(action: nil, roles: nil, if: nil, unless: nil)` method which allows you to define the authorization rules.
212
218
 
213
219
  The most basic usage of the method is as follows:
214
220
 
@@ -216,16 +222,18 @@ The most basic usage of the method is as follows:
216
222
  class InvoicesController < ApplicationController
217
223
  grant_access action: :index, roles: [:accountant, :admin]
218
224
  def index
225
+ @invoices = Invoice.all
226
+ @invoices = @invoices.paid if current_user.has_role?(:accountant)
219
227
  ...
220
228
  end
221
229
 
222
- grant_access action: :delete, roles: :admin
223
- def delete
230
+ grant_access action: :destroy, roles: :admin
231
+ def destroy
224
232
  ...
225
233
  end
226
234
  end
227
235
  ```
228
- This grants access to `index` action for users with `accountant` or `admin` role, and access to `delete` action for `admin` users only.
236
+ This grants access to `index` action for users with `accountant` or `admin` role, and access to `destroy` action for `admin` users only.
229
237
 
230
238
  You can also define controller-wide rules (without `action` argument):
231
239
 
@@ -278,14 +286,16 @@ For more complex cases, Rabarber provides dynamic rules:
278
286
 
279
287
  ```rb
280
288
  class OrdersController < ApplicationController
281
- grant_access if: :current_company_accountant?
282
- grant_access unless: :fired?
283
- ...
289
+ grant_access roles: :manager, if: :company_manager?, unless: :fired?
290
+
291
+ def index
292
+ ...
293
+ end
284
294
 
285
295
  private
286
296
 
287
- def current_company_accountant?
288
- current_company.accountant == current_user
297
+ def company_manager?
298
+ Company.find(params[:company_id]).manager == current_user
289
299
  end
290
300
 
291
301
  def fired?
@@ -294,12 +304,17 @@ class OrdersController < ApplicationController
294
304
  end
295
305
 
296
306
  class InvoicesController < ApplicationController
297
- grant_access action: :index, roles: :accountant, if: -> { current_user.passed_probation_period? }
307
+ grant_access roles: :senior_accountant
308
+
309
+ grant_access action: :index, roles: [:secretary, :accountant], if: -> { InvoicesPolicy.new(current_user).can_access?(:index) }
298
310
  def index
311
+ @invoices = Invoice.all
312
+ @invoices = @invoices.where("total < 10000") if current_user.has_role?(:accountant)
313
+ @invoices = @invoices.unpaid if current_user.has_role?(:secretary)
299
314
  ...
300
315
  end
301
316
 
302
- grant_access action: :show, roles: :client, unless: -> { current_user.banned? }
317
+ grant_access action: :show, roles: :accountant, unless: -> { Invoice.find(params[:id]).total > 10_000 }
303
318
  def show
304
319
  ...
305
320
  end
@@ -323,7 +338,7 @@ This means that `Crm::InvoicesController` is still accessible to `admin` but is
323
338
 
324
339
  ## View Helpers
325
340
 
326
- Rabarber also provides a couple of helpers that can be used in views: `visible_to` and `hidden_from`. To use them, simply include `Rabarber::Helpers` in the desired helper. Usually it is `ApplicationHelper`, but it can be any helper of your choice.
341
+ Rabarber also provides a couple of helpers that can be used in views: `visible_to(*roles, &block)` and `hidden_from(*roles, &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.
327
342
 
328
343
  ```rb
329
344
  module ApplicationHelper
@@ -346,6 +361,16 @@ The usage is straightforward:
346
361
  <% end %>
347
362
  ```
348
363
 
364
+ ## Audit Trail
365
+
366
+ Rabarber supports audit trail, which provides a record of user access control activity. This feature logs the following events:
367
+
368
+ - Role assignments to users
369
+ - Role revocations from users
370
+ - Unauthorized access attempts
371
+
372
+ The logs are written to the file `log/rabarber_audit.log` unless the `audit_trail_enabled` configuration option is set to `false`.
373
+
349
374
  ## Problems?
350
375
 
351
376
  Facing a problem or want to suggest an enhancement?
@@ -1,15 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "singleton"
4
-
5
3
  module Rabarber
6
4
  class Configuration
7
5
  include Singleton
8
6
 
9
- attr_reader :cache_enabled, :current_user_method, :must_have_roles,
7
+ attr_reader :audit_trail_enabled, :cache_enabled, :current_user_method, :must_have_roles,
10
8
  :when_actions_missing, :when_roles_missing, :when_unauthorized
11
9
 
12
10
  def initialize
11
+ @audit_trail_enabled = default_audit_trail_enabled
13
12
  @cache_enabled = default_cache_enabled
14
13
  @current_user_method = default_current_user_method
15
14
  @must_have_roles = default_must_have_roles
@@ -18,6 +17,12 @@ module Rabarber
18
17
  @when_unauthorized = default_when_unauthorized
19
18
  end
20
19
 
20
+ def audit_trail_enabled=(value)
21
+ @audit_trail_enabled = Rabarber::Input::Types::Boolean.new(
22
+ value, Rabarber::ConfigurationError, "Configuration 'audit_trail_enabled' must be a Boolean"
23
+ ).process
24
+ end
25
+
21
26
  def cache_enabled=(value)
22
27
  @cache_enabled = Rabarber::Input::Types::Boolean.new(
23
28
  value, Rabarber::ConfigurationError, "Configuration 'cache_enabled' must be a Boolean"
@@ -56,6 +61,10 @@ module Rabarber
56
61
 
57
62
  private
58
63
 
64
+ def default_audit_trail_enabled
65
+ true
66
+ end
67
+
59
68
  def default_cache_enabled
60
69
  true
61
70
  end
@@ -70,21 +79,20 @@ module Rabarber
70
79
 
71
80
  def default_when_actions_missing
72
81
  -> (missing_actions, context) {
73
- raise Rabarber::Error, "Missing actions: #{missing_actions}, context: #{context[:controller]}"
82
+ raise(Rabarber::Error, "'grant_access' method called with non-existent actions: #{missing_actions}, context: '#{context[:controller]}'")
74
83
  }
75
84
  end
76
85
 
77
86
  def default_when_roles_missing
78
87
  -> (missing_roles, context) {
79
88
  delimiter = context[:action] ? "#" : ""
80
- message = "Missing roles: #{missing_roles}, context: #{context[:controller]}#{delimiter}#{context[:action]}"
89
+ message = "'grant_access' method called with non-existent roles: #{missing_roles}, context: '#{context[:controller]}#{delimiter}#{context[:action]}'"
81
90
  Rabarber::Logger.log(:warn, message)
82
91
  }
83
92
  end
84
93
 
85
94
  def default_when_unauthorized
86
95
  -> (controller) do
87
- Rabarber::Logger.log(:warn, "Unauthorized attempt")
88
96
  if controller.request.format.html?
89
97
  controller.redirect_back fallback_location: controller.main_app.root_path
90
98
  else
@@ -28,14 +28,18 @@ module Rabarber
28
28
  Rabarber::Missing::Actions.new(self.class).handle
29
29
  Rabarber::Missing::Roles.new(self.class).handle
30
30
 
31
- return if Rabarber::Core::Permissions.access_granted?(rabarber_roles, self.class, action_name.to_sym, self)
31
+ roleable = send(Rabarber::Configuration.instance.current_user_method)
32
32
 
33
- Rabarber::Configuration.instance.when_unauthorized.call(self)
34
- end
33
+ return if Rabarber::Core::Permissions.access_granted?(
34
+ roleable ? roleable.roles : [], self.class, action_name.to_sym, self
35
+ )
35
36
 
36
- def rabarber_roles
37
- user = send(Rabarber::Configuration.instance.current_user_method)
38
- user ? user.roles : []
37
+ Rabarber::Logger.audit(
38
+ :warn,
39
+ "[Unauthorized Attempt] #{Rabarber::Logger.roleable_identity(roleable, with_roles: true)} attempted to access '#{request.path}'"
40
+ )
41
+
42
+ Rabarber::Configuration.instance.when_unauthorized.call(self)
39
43
  end
40
44
  end
41
45
  end
@@ -9,20 +9,14 @@ module Rabarber
9
9
  end
10
10
 
11
11
  def controller_accessible?(roles, controller, dynamic_rule_receiver)
12
- accessible_controllers(roles, dynamic_rule_receiver).any? do |accessible_controller|
13
- controller <= accessible_controller
12
+ controller_rules.any? do |rule_controller, rule|
13
+ controller <= rule_controller && rule.verify_access(roles, dynamic_rule_receiver)
14
14
  end
15
15
  end
16
16
 
17
17
  def action_accessible?(roles, controller, action, dynamic_rule_receiver)
18
18
  action_rules[controller].any? { |rule| rule.verify_access(roles, dynamic_rule_receiver, action) }
19
19
  end
20
-
21
- private
22
-
23
- def accessible_controllers(roles, dynamic_rule_receiver)
24
- controller_rules.select { |_, rule| rule.verify_access(roles, dynamic_rule_receiver) }.keys
25
- end
26
20
  end
27
21
  end
28
22
  end
@@ -1,11 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- module Logger
5
- module_function
4
+ class Logger
5
+ include Singleton
6
6
 
7
- def log(log_level, message)
8
- Rails.logger.tagged("Rabarber") { Rails.logger.public_send(log_level, message) }
7
+ attr_reader :rails_logger, :audit_logger
8
+
9
+ def initialize
10
+ @rails_logger = Rails.logger
11
+ @audit_logger = ::Logger.new(Rails.root.join("log/rabarber_audit.log"))
12
+ end
13
+
14
+ class << self
15
+ def log(log_level, message)
16
+ instance.rails_logger.tagged("Rabarber") { instance.rails_logger.public_send(log_level, message) }
17
+ end
18
+
19
+ def audit(log_level, message)
20
+ return unless Rabarber::Configuration.instance.audit_trail_enabled
21
+
22
+ instance.audit_logger.public_send(log_level, message)
23
+ end
24
+
25
+ def roleable_identity(roleable, with_roles:)
26
+ if roleable
27
+ model_name = roleable.model_name.human
28
+ primary_key = roleable.class.primary_key
29
+ roleable_id = roleable.public_send(primary_key)
30
+
31
+ roles = with_roles ? ", roles: #{roleable.roles}" : ""
32
+
33
+ "#{model_name} with #{primary_key}: '#{roleable_id}'#{roles}"
34
+ else
35
+ "Unauthenticated user"
36
+ end
37
+ end
9
38
  end
10
39
  end
11
40
  end
@@ -16,9 +16,7 @@ module Rabarber
16
16
  action_rules.each do |controller, controller_action_rules|
17
17
  controller_action_rules.each do |action_rule|
18
18
  missing_roles = action_rule.roles - all_roles
19
- if missing_roles.any?
20
- missing_list << Rabarber::Missing::Item.new(missing_roles, controller, action_rule.action)
21
- end
19
+ missing_list << Rabarber::Missing::Item.new(missing_roles, controller, action_rule.action) if missing_roles.any?
22
20
  end
23
21
  end
24
22
  end
@@ -5,9 +5,7 @@ module Rabarber
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- if defined?(@@included) && @@included != name
9
- raise Rabarber::Error, "Rabarber::HasRoles can only be included once"
10
- end
8
+ raise Rabarber::Error, "Rabarber::HasRoles can only be included once" if defined?(@@included) && @@included != name
11
9
 
12
10
  @@included = name
13
11
 
@@ -27,26 +25,37 @@ module Rabarber
27
25
  end
28
26
 
29
27
  def assign_roles(*role_names, create_new: true)
30
- roles_to_assign = process_role_names(role_names)
28
+ processed_role_names = process_role_names(role_names)
31
29
 
32
- create_new_roles(roles_to_assign) if create_new
30
+ create_new_roles(processed_role_names) if create_new
33
31
 
34
- new_roles = Rabarber::Role.where(name: roles_to_assign) - rabarber_roles
32
+ roles_to_assign = Rabarber::Role.where(name: processed_role_names) - rabarber_roles
35
33
 
36
- if new_roles.any?
34
+ if roles_to_assign.any?
37
35
  delete_roleable_cache
38
- rabarber_roles << new_roles
36
+ rabarber_roles << roles_to_assign
37
+
38
+ Rabarber::Logger.audit(
39
+ :info,
40
+ "[Role Assignment] #{Rabarber::Logger.roleable_identity(self, with_roles: false)} has been assigned the following roles: #{roles_to_assign.pluck(:name).map(&:to_sym)}, current roles: #{roles}"
41
+ )
39
42
  end
40
43
 
41
44
  roles
42
45
  end
43
46
 
44
47
  def revoke_roles(*role_names)
45
- new_roles = rabarber_roles - Rabarber::Role.where(name: process_role_names(role_names))
48
+ processed_role_names = process_role_names(role_names)
49
+ roles_to_revoke = Rabarber::Role.where(name: processed_role_names.intersection(roles))
46
50
 
47
- if rabarber_roles != new_roles
51
+ if roles_to_revoke.any?
48
52
  delete_roleable_cache
49
- self.rabarber_roles = new_roles
53
+ self.rabarber_roles -= roles_to_revoke
54
+
55
+ Rabarber::Logger.audit(
56
+ :info,
57
+ "[Role Revocation] #{Rabarber::Logger.roleable_identity(self, with_roles: false)} has been revoked from the following roles: #{roles_to_revoke.pluck(:name).map(&:to_sym)}, current roles: #{roles}"
58
+ )
50
59
  end
51
60
 
52
61
  roles
@@ -8,6 +8,11 @@ module Rabarber
8
8
  app.config.after_initialize do
9
9
  Rabarber::Missing::Actions.new.handle
10
10
  Rabarber::Missing::Roles.new.handle if Rabarber::Role.table_exists?
11
+
12
+ Rabarber::Logger.log(
13
+ :warn,
14
+ "DEPRECATION WARNING: Configurations 'when_actions_missing' and 'when_roles_missing' are deprecated and will be removed in v2.0.0"
15
+ )
11
16
  end
12
17
  end
13
18
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "1.3.1"
4
+ VERSION = "1.4.1"
5
5
  end
data/lib/rabarber.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "singleton"
4
+
3
5
  require_relative "rabarber/version"
4
6
  require_relative "rabarber/logger"
5
7
  require_relative "rabarber/configuration"
data/rabarber.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Rabarber::VERSION
8
8
  spec.authors = ["enjaku4", "trafium"]
9
9
  spec.email = ["rabarber_gem@icloud.com"]
10
-
10
+ spec.metadata["rubygems_mfa_required"] = "true"
11
11
  spec.summary = "Simple role-based authorization library for Ruby on Rails."
12
12
  spec.homepage = "https://github.com/enjaku4/rabarber"
13
13
  spec.license = "MIT"
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: 1.3.1
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - enjaku4
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-03-07 00:00:00.000000000 Z
12
+ date: 2024-04-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -65,7 +65,8 @@ files:
65
65
  homepage: https://github.com/enjaku4/rabarber
66
66
  licenses:
67
67
  - MIT
68
- metadata: {}
68
+ metadata:
69
+ rubygems_mfa_required: 'true'
69
70
  post_install_message:
70
71
  rdoc_options: []
71
72
  require_paths:
@@ -81,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
82
  - !ruby/object:Gem::Version
82
83
  version: '0'
83
84
  requirements: []
84
- rubygems_version: 3.3.26
85
+ rubygems_version: 3.2.33
85
86
  signing_key:
86
87
  specification_version: 4
87
88
  summary: Simple role-based authorization library for Ruby on Rails.