rabarber 3.0.1 → 3.0.2
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 +7 -0
- data/lib/rabarber/audit/events/base.rb +1 -14
- data/lib/rabarber/audit/events/roles_assigned.rb +0 -4
- data/lib/rabarber/audit/events/roles_revoked.rb +0 -4
- data/lib/rabarber/audit/events/unauthorized_attempt.rb +0 -4
- data/lib/rabarber/configuration.rb +10 -38
- data/lib/rabarber/controllers/concerns/authorization.rb +3 -8
- data/lib/rabarber/core/access.rb +1 -1
- data/lib/rabarber/core/cache.rb +10 -9
- data/lib/rabarber/core/null_roleable.rb +19 -0
- data/lib/rabarber/core/permissions.rb +1 -8
- data/lib/rabarber/core/permissions_integrity_checker.rb +2 -5
- data/lib/rabarber/core/roleable.rb +2 -6
- data/lib/rabarber/core/rule.rb +11 -23
- data/lib/rabarber/helpers/helpers.rb +2 -6
- data/lib/rabarber/input/action.rb +1 -4
- data/lib/rabarber/input/authorization_context.rb +2 -2
- data/lib/rabarber/input/base.rb +2 -2
- data/lib/rabarber/input/dynamic_rule.rb +1 -4
- data/lib/rabarber/input/role.rb +1 -1
- data/lib/rabarber/input/roles.rb +1 -1
- data/lib/rabarber/models/concerns/has_roles.rb +6 -1
- data/lib/rabarber/models/role.rb +2 -2
- data/lib/rabarber/railtie.rb +1 -1
- data/lib/rabarber/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 577541d4928a55318bf4358aab22e46b094b873172c78ab3a5cdcd5c48e2fd1f
|
4
|
+
data.tar.gz: 8705e8907a36e9cc811784c951011b448fe37ec5cd5a63b70d263589af035164
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b35a496964096d6ce0538f2d84d43786c86fb367b03afa1a8604cd9e9225f015266fb2d94c4df01513c07cbb1e639851f858fb12bcded98646718a586218fd8a
|
7
|
+
data.tar.gz: 8bab47c3abf70362eecd13237eca64db16c3b7a0bae06334efe8c7ba3e0f1cb48b39a1c443ce5456957ef643c7256693a8ac0e0d2ecbabb3b701463a00120dc9
|
data/CHANGELOG.md
CHANGED
@@ -15,16 +15,10 @@ module Rabarber
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def initialize(roleable, specifics)
|
18
|
-
raise ArgumentError, "Roleable is required for #{self.class} event" if roleable.nil? && !nil_roleable_allowed?
|
19
|
-
|
20
18
|
@roleable = roleable
|
21
19
|
@specifics = specifics
|
22
20
|
end
|
23
21
|
|
24
|
-
def nil_roleable_allowed?
|
25
|
-
raise NotImplementedError
|
26
|
-
end
|
27
|
-
|
28
22
|
def log
|
29
23
|
Rabarber::Audit::Logger.log(log_level, message)
|
30
24
|
end
|
@@ -38,14 +32,7 @@ module Rabarber
|
|
38
32
|
end
|
39
33
|
|
40
34
|
def identity
|
41
|
-
|
42
|
-
"Unauthenticated #{Rabarber::HasRoles.roleable_class.model_name.human.downcase}"
|
43
|
-
else
|
44
|
-
model_name = roleable.model_name.human
|
45
|
-
roleable_id = roleable.public_send(roleable.class.primary_key)
|
46
|
-
|
47
|
-
"#{model_name}##{roleable_id}"
|
48
|
-
end
|
35
|
+
roleable.log_identity
|
49
36
|
end
|
50
37
|
|
51
38
|
def human_context
|
@@ -9,22 +9,18 @@ module Rabarber
|
|
9
9
|
attr_reader :audit_trail_enabled, :cache_enabled, :current_user_method, :must_have_roles
|
10
10
|
|
11
11
|
def initialize
|
12
|
-
@audit_trail_enabled =
|
13
|
-
@cache_enabled =
|
14
|
-
@current_user_method =
|
15
|
-
@must_have_roles =
|
12
|
+
@audit_trail_enabled = true
|
13
|
+
@cache_enabled = true
|
14
|
+
@current_user_method = :current_user
|
15
|
+
@must_have_roles = false
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
def cache_enabled=(value)
|
25
|
-
@cache_enabled = Rabarber::Input::Types::Boolean.new(
|
26
|
-
value, Rabarber::ConfigurationError, "Configuration 'cache_enabled' must be a Boolean"
|
27
|
-
).process
|
18
|
+
[:audit_trail_enabled, :cache_enabled, :must_have_roles].each do |method_name|
|
19
|
+
define_method(:"#{method_name}=") do |value|
|
20
|
+
instance_variable_set(:"@#{method_name}", Rabarber::Input::Types::Boolean.new(
|
21
|
+
value, Rabarber::ConfigurationError, "Configuration '#{method_name}' must be a Boolean"
|
22
|
+
).process)
|
23
|
+
end
|
28
24
|
end
|
29
25
|
|
30
26
|
def current_user_method=(method_name)
|
@@ -32,29 +28,5 @@ module Rabarber
|
|
32
28
|
method_name, Rabarber::ConfigurationError, "Configuration 'current_user_method' must be a Symbol or a String"
|
33
29
|
).process
|
34
30
|
end
|
35
|
-
|
36
|
-
def must_have_roles=(value)
|
37
|
-
@must_have_roles = Rabarber::Input::Types::Boolean.new(
|
38
|
-
value, Rabarber::ConfigurationError, "Configuration 'must_have_roles' must be a Boolean"
|
39
|
-
).process
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def default_audit_trail_enabled
|
45
|
-
true
|
46
|
-
end
|
47
|
-
|
48
|
-
def default_cache_enabled
|
49
|
-
true
|
50
|
-
end
|
51
|
-
|
52
|
-
def default_current_user_method
|
53
|
-
:current_user
|
54
|
-
end
|
55
|
-
|
56
|
-
def default_must_have_roles
|
57
|
-
false
|
58
|
-
end
|
59
31
|
end
|
60
32
|
end
|
@@ -3,12 +3,9 @@
|
|
3
3
|
module Rabarber
|
4
4
|
module Authorization
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
|
7
6
|
include Rabarber::Core::Roleable
|
8
7
|
|
9
|
-
included
|
10
|
-
before_action :verify_access
|
11
|
-
end
|
8
|
+
included { before_action :verify_access }
|
12
9
|
|
13
10
|
class_methods do
|
14
11
|
def skip_authorization(options = {})
|
@@ -16,15 +13,13 @@ module Rabarber
|
|
16
13
|
end
|
17
14
|
|
18
15
|
def grant_access(action: nil, roles: nil, context: nil, if: nil, unless: nil)
|
19
|
-
dynamic_rule, negated_dynamic_rule = binding.local_variable_get(:if), binding.local_variable_get(:unless)
|
20
|
-
|
21
16
|
Rabarber::Core::Permissions.add(
|
22
17
|
self,
|
23
18
|
Rabarber::Input::Action.new(action).process,
|
24
19
|
Rabarber::Input::Roles.new(roles).process,
|
25
20
|
Rabarber::Input::AuthorizationContext.new(context).process,
|
26
|
-
Rabarber::Input::DynamicRule.new(
|
27
|
-
Rabarber::Input::DynamicRule.new(
|
21
|
+
Rabarber::Input::DynamicRule.new(binding.local_variable_get(:if)).process,
|
22
|
+
Rabarber::Input::DynamicRule.new(binding.local_variable_get(:unless)).process
|
28
23
|
)
|
29
24
|
end
|
30
25
|
end
|
data/lib/rabarber/core/access.rb
CHANGED
@@ -9,7 +9,7 @@ module Rabarber
|
|
9
9
|
|
10
10
|
def controller_accessible?(roleable, controller_instance)
|
11
11
|
controller_rules.any? do |rule_controller, rule|
|
12
|
-
controller_instance.
|
12
|
+
controller_instance.is_a?(rule_controller) && rule.verify_access(roleable, controller_instance)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/lib/rabarber/core/cache.rb
CHANGED
@@ -5,22 +5,22 @@ require "digest/sha2"
|
|
5
5
|
module Rabarber
|
6
6
|
module Core
|
7
7
|
module Cache
|
8
|
-
module_function
|
9
|
-
|
10
8
|
CACHE_PREFIX = "rabarber"
|
11
9
|
private_constant :CACHE_PREFIX
|
12
10
|
|
11
|
+
module_function
|
12
|
+
|
13
13
|
def fetch(roleable_id, context:, &block)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
yield
|
18
|
-
end
|
14
|
+
return yield unless enabled?
|
15
|
+
|
16
|
+
Rails.cache.fetch(key_for(roleable_id, context), expires_in: 1.hour, race_condition_ttl: 5.seconds, &block)
|
19
17
|
end
|
20
18
|
|
21
19
|
def delete(*roleable_ids, context:)
|
20
|
+
return unless enabled?
|
21
|
+
|
22
22
|
keys = roleable_ids.map { |roleable_id| key_for(roleable_id, context) }
|
23
|
-
Rails.cache.delete_multi(keys) if
|
23
|
+
Rails.cache.delete_multi(keys) if keys.any?
|
24
24
|
end
|
25
25
|
|
26
26
|
def enabled?
|
@@ -38,9 +38,10 @@ module Rabarber
|
|
38
38
|
end
|
39
39
|
|
40
40
|
module Cache
|
41
|
+
module_function
|
42
|
+
|
41
43
|
def clear
|
42
44
|
Rabarber::Core::Cache.clear
|
43
45
|
end
|
44
|
-
module_function :clear
|
45
46
|
end
|
46
47
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rabarber
|
4
|
+
module Core
|
5
|
+
class NullRoleable
|
6
|
+
def roles(context:) # rubocop:disable Lint/UnusedMethodArgument
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_role?(*role_names, context: nil) # rubocop:disable Lint/UnusedMethodArgument
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_identity
|
15
|
+
"Unauthenticated #{Rabarber::HasRoles.roleable_class.model_name.human.downcase}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -2,14 +2,12 @@
|
|
2
2
|
|
3
3
|
require_relative "access"
|
4
4
|
require_relative "rule"
|
5
|
-
|
6
5
|
require "singleton"
|
7
6
|
|
8
7
|
module Rabarber
|
9
8
|
module Core
|
10
9
|
class Permissions
|
11
10
|
include Singleton
|
12
|
-
|
13
11
|
extend Access
|
14
12
|
|
15
13
|
attr_reader :storage
|
@@ -21,12 +19,7 @@ module Rabarber
|
|
21
19
|
class << self
|
22
20
|
def add(controller, action, roles, context, dynamic_rule, negated_dynamic_rule)
|
23
21
|
rule = Rabarber::Core::Rule.new(action, roles, context, dynamic_rule, negated_dynamic_rule)
|
24
|
-
|
25
|
-
if action
|
26
|
-
instance.storage[:action_rules][controller] += [rule]
|
27
|
-
else
|
28
|
-
instance.storage[:controller_rules][controller] = rule
|
29
|
-
end
|
22
|
+
action ? action_rules[controller] += [rule] : controller_rules[controller] = rule
|
30
23
|
end
|
31
24
|
|
32
25
|
def controller_rules
|
@@ -28,11 +28,8 @@ module Rabarber
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def action_rules
|
31
|
-
|
32
|
-
|
33
|
-
else
|
34
|
-
Rabarber::Core::Permissions.action_rules
|
35
|
-
end
|
31
|
+
rules = Rabarber::Core::Permissions.action_rules
|
32
|
+
controller ? rules.slice(controller) : rules
|
36
33
|
end
|
37
34
|
end
|
38
35
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative "null_roleable"
|
4
|
+
|
3
5
|
module Rabarber
|
4
6
|
module Core
|
5
7
|
module Roleable
|
@@ -11,11 +13,5 @@ module Rabarber
|
|
11
13
|
roleable.roles(context: context)
|
12
14
|
end
|
13
15
|
end
|
14
|
-
|
15
|
-
class NullRoleable
|
16
|
-
def roles(context:) # rubocop:disable Lint/UnusedMethodArgument
|
17
|
-
[]
|
18
|
-
end
|
19
|
-
end
|
20
16
|
end
|
21
17
|
end
|
data/lib/rabarber/core/rule.rb
CHANGED
@@ -9,44 +9,32 @@ module Rabarber
|
|
9
9
|
@action = action
|
10
10
|
@roles = Array(roles)
|
11
11
|
@context = context
|
12
|
-
@dynamic_rule = dynamic_rule
|
13
|
-
@negated_dynamic_rule = negated_dynamic_rule
|
12
|
+
@dynamic_rule = dynamic_rule || -> { true }
|
13
|
+
@negated_dynamic_rule = negated_dynamic_rule || -> { false }
|
14
14
|
end
|
15
15
|
|
16
16
|
def verify_access(roleable, controller_instance)
|
17
|
-
roles_permitted?(roleable, controller_instance) &&
|
17
|
+
roles_permitted?(roleable, controller_instance) && dynamic_rules_followed?(controller_instance)
|
18
18
|
end
|
19
19
|
|
20
20
|
def roles_permitted?(roleable, controller_instance)
|
21
|
-
|
22
|
-
|
21
|
+
resolved_context = resolve_context(controller_instance)
|
22
|
+
return false if Rabarber::Configuration.instance.must_have_roles && roleable.roles(context: resolved_context).empty?
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
roles.empty? || roles.intersection(roleable_roles).any?
|
24
|
+
roles.empty? || roleable.has_role?(*roles, context: resolved_context)
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
27
|
+
def dynamic_rules_followed?(controller_instance)
|
28
|
+
execute_rule(controller_instance, dynamic_rule) && !execute_rule(controller_instance, negated_dynamic_rule)
|
31
29
|
end
|
32
30
|
|
33
31
|
private
|
34
32
|
|
35
|
-
def
|
36
|
-
rule
|
37
|
-
|
38
|
-
return true if rule.nil?
|
39
|
-
|
40
|
-
result = if rule.is_a?(Proc)
|
41
|
-
controller_instance.instance_exec(&rule)
|
42
|
-
else
|
43
|
-
controller_instance.send(rule)
|
44
|
-
end
|
45
|
-
|
46
|
-
is_negated ? !result : result
|
33
|
+
def execute_rule(controller_instance, rule)
|
34
|
+
!!(rule.is_a?(Proc) ? controller_instance.instance_exec(&rule) : controller_instance.send(rule))
|
47
35
|
end
|
48
36
|
|
49
|
-
def
|
37
|
+
def resolve_context(controller_instance)
|
50
38
|
case context
|
51
39
|
when Proc then Rabarber::Input::Context.new(controller_instance.instance_exec(&context)).process
|
52
40
|
when Symbol then Rabarber::Input::Context.new(controller_instance.send(context)).process
|
@@ -5,15 +5,11 @@ module Rabarber
|
|
5
5
|
include Rabarber::Core::Roleable
|
6
6
|
|
7
7
|
def visible_to(*roles, context: nil, &block)
|
8
|
-
|
9
|
-
|
10
|
-
capture(&block)
|
8
|
+
capture(&block) if roleable.has_role?(*roles, context: context)
|
11
9
|
end
|
12
10
|
|
13
11
|
def hidden_from(*roles, context: nil, &block)
|
14
|
-
|
15
|
-
|
16
|
-
capture(&block)
|
12
|
+
capture(&block) unless roleable.has_role?(*roles, context: context)
|
17
13
|
end
|
18
14
|
end
|
19
15
|
end
|
data/lib/rabarber/input/base.rb
CHANGED
@@ -5,10 +5,10 @@ module Rabarber
|
|
5
5
|
class Base
|
6
6
|
attr_reader :value, :error_type, :error_message
|
7
7
|
|
8
|
-
def initialize(value, error_type = Rabarber::InvalidArgumentError, error_message =
|
8
|
+
def initialize(value, error_type = Rabarber::InvalidArgumentError, error_message = nil)
|
9
9
|
@value = value
|
10
10
|
@error_type = error_type
|
11
|
-
@error_message = error_message
|
11
|
+
@error_message = error_message || default_error_message
|
12
12
|
end
|
13
13
|
|
14
14
|
def process
|
data/lib/rabarber/input/role.rb
CHANGED
@@ -16,7 +16,7 @@ module Rabarber
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def default_error_message
|
19
|
-
"Role name must be a Symbol or a String and may only contain lowercase letters, numbers and underscores"
|
19
|
+
"Role name must be a Symbol or a String and may only contain lowercase letters, numbers, and underscores"
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
data/lib/rabarber/input/roles.rb
CHANGED
@@ -18,7 +18,7 @@ module Rabarber
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def default_error_message
|
21
|
-
"Role names must be Symbols or Strings and may only contain lowercase letters, numbers and underscores"
|
21
|
+
"Role names must be Symbols or Strings and may only contain lowercase letters, numbers, and underscores"
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -21,7 +21,8 @@ module Rabarber
|
|
21
21
|
|
22
22
|
def has_role?(*role_names, context: nil)
|
23
23
|
processed_context = process_context(context)
|
24
|
-
|
24
|
+
processed_roles = process_role_names(role_names)
|
25
|
+
roles(context: processed_context).any? { |role_name| processed_roles.include?(role_name) }
|
25
26
|
end
|
26
27
|
|
27
28
|
def assign_roles(*role_names, context: nil, create_new: true)
|
@@ -72,6 +73,10 @@ module Rabarber
|
|
72
73
|
roles(context: processed_context)
|
73
74
|
end
|
74
75
|
|
76
|
+
def log_identity
|
77
|
+
"#{model_name.human}##{roleable_id}"
|
78
|
+
end
|
79
|
+
|
75
80
|
def roleable_class
|
76
81
|
@@included.constantize
|
77
82
|
end
|
data/lib/rabarber/models/role.rb
CHANGED
@@ -13,7 +13,7 @@ module Rabarber
|
|
13
13
|
|
14
14
|
class << self
|
15
15
|
def names(context: nil)
|
16
|
-
where(
|
16
|
+
where(process_context(context)).pluck(:name).map(&:to_sym)
|
17
17
|
end
|
18
18
|
|
19
19
|
def add(name, context: nil)
|
@@ -50,7 +50,7 @@ module Rabarber
|
|
50
50
|
|
51
51
|
def assignees(name, context: nil)
|
52
52
|
Rabarber::HasRoles.roleable_class.joins(:rabarber_roles).where(
|
53
|
-
rabarber_roles: { name:
|
53
|
+
rabarber_roles: { name: process_role_name(name), **process_context(context) }
|
54
54
|
)
|
55
55
|
end
|
56
56
|
|
data/lib/rabarber/railtie.rb
CHANGED
@@ -6,7 +6,7 @@ module Rabarber
|
|
6
6
|
class Railtie < Rails::Railtie
|
7
7
|
initializer "rabarber.after_initialize" do |app|
|
8
8
|
app.config.after_initialize do
|
9
|
-
Rabarber::Core::PermissionsIntegrityChecker.new.run! if
|
9
|
+
Rabarber::Core::PermissionsIntegrityChecker.new.run! if app.config.eager_load
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/rabarber/version.rb
CHANGED
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: 3.0.
|
4
|
+
version: 3.0.2
|
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-
|
12
|
+
date: 2024-10-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- lib/rabarber/controllers/concerns/authorization.rb
|
54
54
|
- lib/rabarber/core/access.rb
|
55
55
|
- lib/rabarber/core/cache.rb
|
56
|
+
- lib/rabarber/core/null_roleable.rb
|
56
57
|
- lib/rabarber/core/permissions.rb
|
57
58
|
- lib/rabarber/core/permissions_integrity_checker.rb
|
58
59
|
- lib/rabarber/core/roleable.rb
|