rabarber 1.0.5 → 1.1.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: e54ea7ddbb816cbc279edf4cb5d8863086fb452528f1403da9c944e4d23b862a
4
- data.tar.gz: a6cc1c88ced2c76561c368669e6413a476b7590fa105a9876a6dfcbd139c2b41
3
+ metadata.gz: bf7a31c94f695cb0edb8209562d07971f3fc712bd868bf589fd4b1ee4ffb2865
4
+ data.tar.gz: 2c5d939673b685beba64910dc4ee361f811d760cc8bb6982b502b3e0f4354c5b
5
5
  SHA512:
6
- metadata.gz: ae10eca6248824b4dab6a7038b813933ee9ecaab1fd1cce67ef07ed95fdb0a77f5f70b3138574e9b42ea5d570da2a56186845978950b1fda03240e836cb07f8f
7
- data.tar.gz: f8f4e00a8b76a8eb7a23e7ad7088ab38325e23ebd2ccab43b744def1d977b531f02866025c56f982578d3af8894582756bd980057159095a1bfe9eb3f6d261d5
6
+ metadata.gz: c513a7ea4140a12b37384b0bc91969eec5305f1d422a24d3a6117607d9b1b0dbdaa77398f4ce49fa2025b9fb1101439c09c274cded09acbb58a46b0bd54b9b3b
7
+ data.tar.gz: 93f18fe608254896b97e772e05d9c94e31f17f769fd4d4f5314460450d19408c7f3f41a1197a7834b43ab81e6bbf694446ed94b23df9af6b9e904040534afc1a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 1.1.0
2
+
3
+ - Add support for `unless` argument in `grant_access` method, allowing to define negated dynamic rules
4
+ - Fix a bug where specifying a dynamic rule as a symbol without specifying an action would result in an error
5
+
1
6
  ## 1.0.5
2
7
 
3
8
  - Add co-author: [trafium](https://github.com/trafium)
data/README.md CHANGED
@@ -3,13 +3,13 @@
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 an 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 useful tools for managing user roles and defining authorization rules.
6
+ Rabarber is an authorization library for Ruby on Rails, primarily designed for use in the application layer but not limited to that. It offers a set of useful tools for managing user roles and defining authorization rules.
7
7
 
8
8
  ---
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 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
 
@@ -145,7 +145,11 @@ Rabarber::Role.names
145
145
 
146
146
  `Rabarber::Role` is a model that represents roles within your application. It has a single attribute, `name`, which is validated for both uniqueness and presence. You can treat `Rabarber::Role` as a regular Rails model and use Active Record methods on it if necessary.
147
147
 
148
- Utilize the aforementioned methods to manipulate user roles. For example, create a custom UI for managing roles or assign necessary roles during migration or runtime (e.g., when the user is created). You can also write custom authorization policies based on `#has_role?` method (e.g., to scope the data that the user can access). Adapt these methods to fit the requirements of your application.
148
+ *Utilize the aforementioned methods to manipulate user roles. For example, create a UI for managing roles or assign roles during migration or runtime (e.g. when the user is created).*
149
+
150
+ *You are also encouraged to write your own authorization policies based on `#has_role?` method (e.g. to scope the data that the role can access).*
151
+
152
+ *Adapt the tools Rabarber provides to fit the requirements of your application.*
149
153
 
150
154
  ## Authorization Rules
151
155
 
@@ -221,11 +225,12 @@ This allows everyone to access `OrdersController` and its children and `index` a
221
225
 
222
226
  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 see anything.
223
227
 
224
- For more complex rules, Rabarber provides the following:
228
+ For more complex cases, Rabarber provides dynamic rules:
225
229
 
226
230
  ```rb
227
231
  class OrdersController < ApplicationController
228
232
  grant_access if: :user_has_access?
233
+ grant_access unless: :user_has_no_access?
229
234
  ...
230
235
 
231
236
  private
@@ -233,6 +238,10 @@ class OrdersController < ApplicationController
233
238
  def user_has_access?
234
239
  ...
235
240
  end
241
+
242
+ def user_has_no_access?
243
+ ...
244
+ end
236
245
  end
237
246
 
238
247
  class InvoicesController < ApplicationController
@@ -240,11 +249,16 @@ class InvoicesController < ApplicationController
240
249
  def index
241
250
  ...
242
251
  end
252
+
253
+ grant_access action: :show, roles: :client, unless: -> { current_user.banned? }
254
+ def show
255
+ ...
256
+ end
243
257
  end
244
258
  ```
245
- You can pass a custom rule as `if` argument. It can be a symbol (the method with the same name will be called) or a lambda.
259
+ You can pass a dynamic rule as `if` or `unless` argument. It can be a symbol (the method with the same name will be called) or a lambda.
246
260
 
247
- Rules defined in children don't override parent rules but rather add to them:
261
+ Rules defined in child classes don't override parent rules but rather add to them:
248
262
  ```rb
249
263
  class Crm::BaseController < ApplicationController
250
264
  grant_access roles: :admin
@@ -2,25 +2,25 @@
2
2
 
3
3
  module Rabarber
4
4
  module Access
5
- def access_granted?(roles, controller, action, custom_rule_receiver)
6
- controller_accessible?(roles, controller, custom_rule_receiver) ||
7
- action_accessible?(roles, controller, action, custom_rule_receiver)
5
+ def access_granted?(roles, controller, action, dynamic_rule_receiver)
6
+ controller_accessible?(roles, controller, dynamic_rule_receiver) ||
7
+ action_accessible?(roles, controller, action, dynamic_rule_receiver)
8
8
  end
9
9
 
10
- def controller_accessible?(roles, controller, custom_rule_receiver)
11
- accessible_controllers(roles, custom_rule_receiver).any? do |accessible_controller|
10
+ def controller_accessible?(roles, controller, dynamic_rule_receiver)
11
+ accessible_controllers(roles, dynamic_rule_receiver).any? do |accessible_controller|
12
12
  controller <= accessible_controller
13
13
  end
14
14
  end
15
15
 
16
- def action_accessible?(roles, controller, action, custom_rule_receiver)
17
- action_rules[controller].any? { |rule| rule.verify_access(roles, custom_rule_receiver, action) }
16
+ def action_accessible?(roles, controller, action, dynamic_rule_receiver)
17
+ action_rules[controller].any? { |rule| rule.verify_access(roles, dynamic_rule_receiver, action) }
18
18
  end
19
19
 
20
20
  private
21
21
 
22
- def accessible_controllers(roles, custom_rule_receiver)
23
- controller_rules.select { |_, rule| rule.verify_access(roles, custom_rule_receiver) }.keys
22
+ def accessible_controllers(roles, dynamic_rule_receiver)
23
+ controller_rules.select { |_, rule| rule.verify_access(roles, dynamic_rule_receiver) }.keys
24
24
  end
25
25
  end
26
26
  end
@@ -9,8 +9,10 @@ module Rabarber
9
9
  end
10
10
 
11
11
  class_methods do
12
- def grant_access(action: nil, roles: nil, if: nil)
13
- Permissions.write(self, action, roles, binding.local_variable_get(:if))
12
+ def grant_access(action: nil, roles: nil, if: nil, unless: nil)
13
+ dynamic_rule, negated_dynamic_rule = binding.local_variable_get(:if), binding.local_variable_get(:unless)
14
+
15
+ Permissions.write(self, action, roles, dynamic_rule, negated_dynamic_rule)
14
16
  end
15
17
  end
16
18
 
@@ -15,8 +15,8 @@ module Rabarber
15
15
  @storage = { controller_rules: Hash.new({}), action_rules: Hash.new([]) }
16
16
  end
17
17
 
18
- def self.write(controller, action, roles, custom_rule)
19
- rule = Rule.new(action, roles, custom_rule)
18
+ def self.write(controller, action, roles, dynamic_rule, negated_dynamic_rule)
19
+ rule = Rule.new(action, roles, dynamic_rule, negated_dynamic_rule)
20
20
 
21
21
  if action
22
22
  instance.storage[:action_rules][controller] += [rule]
data/lib/rabarber/rule.rb CHANGED
@@ -2,16 +2,17 @@
2
2
 
3
3
  module Rabarber
4
4
  class Rule
5
- attr_reader :action, :roles, :custom
5
+ attr_reader :action, :roles, :dynamic_rule, :negated_dynamic_rule
6
6
 
7
- def initialize(action, roles, custom)
7
+ def initialize(action, roles, dynamic_rule, negated_dynamic_rule)
8
8
  @action = pre_process_action(action)
9
9
  @roles = RoleNames.pre_process(Array(roles))
10
- @custom = pre_process_custom_rule(custom)
10
+ @dynamic_rule = pre_process_dynamic_rule(dynamic_rule)
11
+ @negated_dynamic_rule = pre_process_dynamic_rule(negated_dynamic_rule)
11
12
  end
12
13
 
13
- def verify_access(user_roles, custom_rule_receiver, action_name = nil)
14
- action_accessible?(action_name) && roles_permitted?(user_roles) && custom_rule_followed?(custom_rule_receiver)
14
+ def verify_access(user_roles, dynamic_rule_receiver, action_name = nil)
15
+ action_accessible?(action_name) && roles_permitted?(user_roles) && dynamic_rule_followed?(dynamic_rule_receiver)
15
16
  end
16
17
 
17
18
  def action_accessible?(action_name)
@@ -24,18 +25,24 @@ module Rabarber
24
25
  roles.empty? || (roles & user_roles).any?
25
26
  end
26
27
 
27
- def custom_rule_followed?(custom_rule_receiver)
28
- custom.nil? || execute_custom_rule(custom_rule_receiver)
28
+ def dynamic_rule_followed?(dynamic_rule_receiver)
29
+ !!(execute_dynamic_rule(dynamic_rule_receiver, false) && execute_dynamic_rule(dynamic_rule_receiver, true))
29
30
  end
30
31
 
31
32
  private
32
33
 
33
- def execute_custom_rule(custom_rule_receiver)
34
- if custom.is_a?(Proc)
35
- custom_rule_receiver.instance_exec(&custom)
36
- else
37
- custom_rule_receiver.send(custom)
38
- end
34
+ def execute_dynamic_rule(dynamic_rule_receiver, is_negated)
35
+ rule = is_negated ? negated_dynamic_rule : dynamic_rule
36
+
37
+ return true if rule.nil?
38
+
39
+ result = if rule.is_a?(Proc)
40
+ dynamic_rule_receiver.instance_exec(&rule)
41
+ else
42
+ dynamic_rule_receiver.send(rule)
43
+ end
44
+
45
+ is_negated ? !result : result
39
46
  end
40
47
 
41
48
  def pre_process_action(action)
@@ -45,11 +52,11 @@ module Rabarber
45
52
  raise InvalidArgumentError, "Action name must be a Symbol or a String"
46
53
  end
47
54
 
48
- def pre_process_custom_rule(custom)
49
- return custom.to_sym if (custom.is_a?(String) || custom.is_a?(Symbol)) && action.present?
50
- return custom if custom.nil? || custom.is_a?(Proc)
55
+ def pre_process_dynamic_rule(rule)
56
+ return rule.to_sym if (rule.is_a?(String) || rule.is_a?(Symbol)) && rule.present?
57
+ return rule if rule.nil? || rule.is_a?(Proc)
51
58
 
52
- raise InvalidArgumentError, "Custom rule must be a Symbol, a String, or a Proc"
59
+ raise InvalidArgumentError, "Dynamic rule must be a Symbol, a String, or a Proc"
53
60
  end
54
61
  end
55
62
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rabarber
4
- VERSION = "1.0.5"
4
+ VERSION = "1.1.0"
5
5
  end
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.0.5
4
+ version: 1.1.0
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-01-10 00:00:00.000000000 Z
12
+ date: 2024-01-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails