action_policy 0.7.3 → 0.7.5
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 +14 -0
- data/lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb +1 -1
- data/lib/.rbnext/3.0/action_policy/policy/cache.rb +1 -1
- data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +1 -1
- data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +3 -5
- data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +2 -0
- data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +2 -2
- data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +2 -2
- data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +1 -1
- data/lib/.rbnext/3.1/action_policy/behaviours/policy_for.rb +1 -1
- data/lib/.rbnext/3.2/action_policy/behaviours/policy_for.rb +1 -1
- data/lib/.rbnext/3.2/action_policy/rspec/be_authorized_to.rb +2 -2
- data/lib/.rbnext/3.2/action_policy/rspec/have_authorized_scope.rb +2 -2
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/behaviours/policy_for.rb +4 -4
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/i18n.rb +1 -1
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/policy/cache.rb +3 -3
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/policy/pre_check.rb +4 -6
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/rspec/be_authorized_to.rb +6 -6
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/rspec/have_authorized_scope.rb +5 -5
- data/lib/.rbnext/{2.7 → 3.4}/action_policy/utils/pretty_print.rb +4 -4
- data/lib/action_policy/behaviour.rb +6 -4
- data/lib/action_policy/behaviours/policy_for.rb +1 -1
- data/lib/action_policy/i18n.rb +1 -1
- data/lib/action_policy/policy/cache.rb +1 -1
- data/lib/action_policy/policy/execution_result.rb +1 -1
- data/lib/action_policy/policy/pre_check.rb +3 -5
- data/lib/action_policy/policy/reasons.rb +2 -0
- data/lib/action_policy/rails/controller.rb +4 -1
- data/lib/action_policy/rspec/be_authorized_to.rb +2 -2
- data/lib/action_policy/rspec/have_authorized_scope.rb +2 -2
- data/lib/action_policy/utils/pretty_print.rb +1 -1
- data/lib/action_policy/version.rb +1 -1
- data/lib/ruby_lsp/action_policy/addon.rb +170 -0
- metadata +15 -14
- /data/lib/.rbnext/{2.7 → 3.0}/action_policy/rails/scope_matchers/action_controller_params.rb +0 -0
- /data/lib/.rbnext/{2.7 → 3.0}/action_policy/rails/scope_matchers/active_record.rb +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3680162e91000d7fb369fcfa17710f557c9a12435c5bf00c6fb367a5045b970
|
4
|
+
data.tar.gz: 000760f640032e5a0b44e9ea7918908a32c2568cefeffc747fa4ea45c46949bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f03892d33e150921d3ec4bdfe1038b017301825474597037585c45e0f780affdfd72ed5171c577cee007af69a8542d4172cb8266526a97895b9c3461b4b2abb3
|
7
|
+
data.tar.gz: e5d9a4259d5dcd8cac8a0cab28b6d85f3aedc46050c51057aa9d458b4c0b8597428bed1a6c57473f3836500b1696cfb1f4a4c1aeac868c2771bb443dc59f10fa
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 0.7.5 (2025-05-09) 🎇
|
6
|
+
|
7
|
+
- Ensure `result.value` is true or false. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.7.4 (2025-03-12)
|
10
|
+
|
11
|
+
- Let authorize! return the policy record. ([@sedubois][])
|
12
|
+
|
13
|
+
- Enable `allowance_to` as a helper method by default. ([@stephannv][])
|
14
|
+
|
15
|
+
- Allow the `:through` option of `authorize` to be passed a Proc. ([@brendon][])
|
16
|
+
|
5
17
|
## 0.7.3 (2024-12-18)
|
6
18
|
|
7
19
|
- Fix keeping the result object in concurrent (Fiber-ed) execution environments. ([@palkan][])
|
@@ -539,3 +551,5 @@ This value is now stored in a cache (if any) instead of just the call result (`t
|
|
539
551
|
[@matsales28]: https://github.com/matsales28
|
540
552
|
[@killondark]: https://github.com/killondark
|
541
553
|
[@Spone]: https://github.com/Spone
|
554
|
+
[@stephannv]: https://github.com/stephannv
|
555
|
+
[@sedubois]: https://github.com/sedubois
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
|
63
63
|
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
|
64
|
+
context_key = context.values.map { it = _1;it._policy_cache_key(use_object_id: true) }.join(".")
|
65
65
|
|
66
66
|
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
67
|
end
|
@@ -78,8 +78,6 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
rebuild_filter
|
80
80
|
end
|
81
|
-
# rubocop: enable
|
82
|
-
# rubocop: enable
|
83
81
|
|
84
82
|
def dup
|
85
83
|
self.class.new(
|
@@ -127,7 +125,7 @@ module ActionPolicy
|
|
127
125
|
def pre_check(*names, **options)
|
128
126
|
names.each do |name|
|
129
127
|
# do not allow pre-check override
|
130
|
-
check = pre_checks.find { _1.name == name }
|
128
|
+
check = pre_checks.find { it = _1;it.name == name }
|
131
129
|
raise "Pre-check already defined: #{name}" unless check.nil?
|
132
130
|
|
133
131
|
pre_checks << Check.new(self, name, **options)
|
@@ -136,14 +134,14 @@ module ActionPolicy
|
|
136
134
|
|
137
135
|
def skip_pre_check(*names, **options)
|
138
136
|
names.each do |name|
|
139
|
-
check = pre_checks.find { _1.name == name }
|
137
|
+
check = pre_checks.find { it = _1;it.name == name }
|
140
138
|
raise "Pre-check not found: #{name}" if check.nil?
|
141
139
|
|
142
140
|
# when no options provided we remove this check completely
|
143
141
|
next pre_checks.delete(check) if options.empty?
|
144
142
|
|
145
143
|
# otherwise duplicate and apply skip options
|
146
|
-
pre_checks[pre_checks.index(check)] = check.dup.tap { _1.skip!(**options) }
|
144
|
+
pre_checks[pre_checks.index(check)] = check.dup.tap { it = _1;it.skip!(**options) }
|
147
145
|
end
|
148
146
|
end
|
149
147
|
|
@@ -51,7 +51,7 @@ module ActionPolicy
|
|
51
51
|
|
52
52
|
@actual_calls = ActionPolicy::Testing::AuthorizeTracker.calls
|
53
53
|
|
54
|
-
actual_calls.any? { _1.matches?(policy, rule, target, context) }
|
54
|
+
actual_calls.any? { it = _1;it.matches?(policy, rule, target, context) }
|
55
55
|
end
|
56
56
|
|
57
57
|
def does_not_match?(*__rest__)
|
@@ -78,7 +78,7 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
def formatted_calls
|
80
80
|
actual_calls.map do
|
81
|
-
" - #{
|
81
|
+
it = _1;" - #{it.inspect}"
|
82
82
|
end.join("\n")
|
83
83
|
end
|
84
84
|
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
@actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings
|
63
63
|
|
64
|
-
matching_scopes = actual_scopes.select { _1.matches?(policy, type, name, scope_options, context) }
|
64
|
+
matching_scopes = actual_scopes.select { it = _1;it.matches?(policy, type, name, scope_options, context) }
|
65
65
|
|
66
66
|
return false if matching_scopes.empty?
|
67
67
|
|
@@ -114,7 +114,7 @@ module ActionPolicy
|
|
114
114
|
|
115
115
|
def formatted_scopings
|
116
116
|
actual_scopes.map do
|
117
|
-
" - #{
|
117
|
+
it = _1;" - #{it.inspect}"
|
118
118
|
end.join("\n")
|
119
119
|
end
|
120
120
|
end
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
|
63
63
|
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
|
64
|
+
context_key = context.values.map { it = _1;it._policy_cache_key(use_object_id: true) }.join(".")
|
65
65
|
|
66
66
|
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
67
|
end
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
|
63
63
|
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map { _1._policy_cache_key(use_object_id: true) }.join(".")
|
64
|
+
context_key = context.values.map { it = _1;it._policy_cache_key(use_object_id: true) }.join(".")
|
65
65
|
|
66
66
|
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
67
|
end
|
@@ -51,7 +51,7 @@ module ActionPolicy
|
|
51
51
|
|
52
52
|
@actual_calls = ActionPolicy::Testing::AuthorizeTracker.calls
|
53
53
|
|
54
|
-
actual_calls.any? { _1.matches?(policy, rule, target, context) }
|
54
|
+
actual_calls.any? { it = _1;it.matches?(policy, rule, target, context) }
|
55
55
|
end
|
56
56
|
|
57
57
|
def does_not_match?(*__rest__)
|
@@ -78,7 +78,7 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
def formatted_calls
|
80
80
|
actual_calls.map do
|
81
|
-
" - #{
|
81
|
+
it = _1;" - #{it.inspect}"
|
82
82
|
end.join("\n")
|
83
83
|
end
|
84
84
|
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
@actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings
|
63
63
|
|
64
|
-
matching_scopes = actual_scopes.select { _1.matches?(policy, type, name, scope_options, context) }
|
64
|
+
matching_scopes = actual_scopes.select { it = _1;it.matches?(policy, type, name, scope_options, context) }
|
65
65
|
|
66
66
|
return false if matching_scopes.empty?
|
67
67
|
|
@@ -114,7 +114,7 @@ module ActionPolicy
|
|
114
114
|
|
115
115
|
def formatted_scopings
|
116
116
|
actual_scopes.map do
|
117
|
-
" - #{
|
117
|
+
it = _1;" - #{it.inspect}"
|
118
118
|
end.join("\n")
|
119
119
|
end
|
120
120
|
end
|
@@ -13,12 +13,12 @@ module ActionPolicy
|
|
13
13
|
|
14
14
|
policy_class = with || ::ActionPolicy.lookup(
|
15
15
|
record,
|
16
|
-
namespace
|
16
|
+
namespace:, context:, allow_nil:, default:, strict_namespace:
|
17
17
|
)
|
18
18
|
policy_class&.new(record, **context)
|
19
19
|
end
|
20
20
|
|
21
|
-
def authorization_context
|
21
|
+
def authorization_context = @authorization_context ||= build_authorization_context
|
22
22
|
|
23
23
|
def build_authorization_context
|
24
24
|
Kernel.raise NotImplementedError, "Please, define `build_authorization_context` method!"
|
@@ -59,9 +59,9 @@ module ActionPolicy
|
|
59
59
|
)
|
60
60
|
end
|
61
61
|
|
62
|
-
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **
|
62
|
+
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
|
63
63
|
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map {
|
64
|
+
context_key = context.values.map { it = _1;it._policy_cache_key(use_object_id: true) }.join(".")
|
65
65
|
|
66
66
|
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
67
|
end
|
@@ -21,7 +21,7 @@ module ActionPolicy
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def candidates_for(policy_class, rule)
|
24
|
-
policy_hierarchy = policy_class.ancestors.select {
|
24
|
+
policy_hierarchy = policy_class.ancestors.select { it = _1;it.respond_to?(:identifier) }
|
25
25
|
[
|
26
26
|
*policy_hierarchy.map { |klass| :"policy.#{klass.identifier}.#{rule}" },
|
27
27
|
:"policy.#{rule}",
|
@@ -23,13 +23,13 @@ module ActionPolicy # :nodoc:
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def cache_namespace()
|
26
|
+
def cache_namespace() = ActionPolicy::CACHE_NAMESPACE
|
27
27
|
|
28
28
|
def cache_key(*parts)
|
29
29
|
[
|
30
30
|
cache_namespace,
|
31
31
|
*parts
|
32
|
-
].map {
|
32
|
+
].map { it = _1;it._policy_cache_key }.join("/")
|
33
33
|
end
|
34
34
|
|
35
35
|
def rule_cache_key(rule)
|
@@ -42,7 +42,7 @@ module ActionPolicy # :nodoc:
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def context_cache_key
|
45
|
-
authorization_context.map {
|
45
|
+
authorization_context.map { _2._policy_cache_key.to_s }.join("/")
|
46
46
|
end
|
47
47
|
|
48
48
|
def apply_with_cache(rule)
|
@@ -52,7 +52,7 @@ module ActionPolicy
|
|
52
52
|
filter.call(rule)
|
53
53
|
end
|
54
54
|
|
55
|
-
def call(policy)
|
55
|
+
def call(policy) = policy.send(name)
|
56
56
|
|
57
57
|
def skip!(except: nil, only: nil)
|
58
58
|
if !except.nil? && !only.nil?
|
@@ -78,8 +78,6 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
rebuild_filter
|
80
80
|
end
|
81
|
-
# rubocop: enable
|
82
|
-
# rubocop: enable
|
83
81
|
|
84
82
|
def dup
|
85
83
|
self.class.new(
|
@@ -127,7 +125,7 @@ module ActionPolicy
|
|
127
125
|
def pre_check(*names, **options)
|
128
126
|
names.each do |name|
|
129
127
|
# do not allow pre-check override
|
130
|
-
check = pre_checks.find {
|
128
|
+
check = pre_checks.find { it = _1;it.name == name }
|
131
129
|
raise "Pre-check already defined: #{name}" unless check.nil?
|
132
130
|
|
133
131
|
pre_checks << Check.new(self, name, **options)
|
@@ -136,14 +134,14 @@ module ActionPolicy
|
|
136
134
|
|
137
135
|
def skip_pre_check(*names, **options)
|
138
136
|
names.each do |name|
|
139
|
-
check = pre_checks.find {
|
137
|
+
check = pre_checks.find { it = _1;it.name == name }
|
140
138
|
raise "Pre-check not found: #{name}" if check.nil?
|
141
139
|
|
142
140
|
# when no options provided we remove this check completely
|
143
141
|
next pre_checks.delete(check) if options.empty?
|
144
142
|
|
145
143
|
# otherwise duplicate and apply skip options
|
146
|
-
pre_checks[pre_checks.index(check)] = check.dup.tap {
|
144
|
+
pre_checks[pre_checks.index(check)] = check.dup.tap { it = _1;it.skip!(**options) }
|
147
145
|
end
|
148
146
|
end
|
149
147
|
|
@@ -51,14 +51,14 @@ module ActionPolicy
|
|
51
51
|
|
52
52
|
@actual_calls = ActionPolicy::Testing::AuthorizeTracker.calls
|
53
53
|
|
54
|
-
actual_calls.any? {
|
54
|
+
actual_calls.any? { it = _1;it.matches?(policy, rule, target, context) }
|
55
55
|
end
|
56
56
|
|
57
|
-
def does_not_match?(*
|
57
|
+
def does_not_match?(*)
|
58
58
|
raise "This matcher doesn't support negation"
|
59
59
|
end
|
60
60
|
|
61
|
-
def supports_block_expectations?()
|
61
|
+
def supports_block_expectations?() = true
|
62
62
|
|
63
63
|
def failure_message
|
64
64
|
"expected #{formatted_record} " \
|
@@ -77,12 +77,12 @@ module ActionPolicy
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def formatted_calls
|
80
|
-
actual_calls.map do
|
81
|
-
" - #{
|
80
|
+
actual_calls.map do
|
81
|
+
it = _1;" - #{it.inspect}"
|
82
82
|
end.join("\n")
|
83
83
|
end
|
84
84
|
|
85
|
-
def formatted_record(record = target)
|
85
|
+
def formatted_record(record = target) = ::RSpec::Support::ObjectFormatter.format(record)
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
@actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings
|
63
63
|
|
64
|
-
matching_scopes = actual_scopes.select {
|
64
|
+
matching_scopes = actual_scopes.select { it = _1;it.matches?(policy, type, name, scope_options, context) }
|
65
65
|
|
66
66
|
return false if matching_scopes.empty?
|
67
67
|
|
@@ -76,11 +76,11 @@ module ActionPolicy
|
|
76
76
|
true
|
77
77
|
end
|
78
78
|
|
79
|
-
def does_not_match?(*
|
79
|
+
def does_not_match?(*)
|
80
80
|
raise "This matcher doesn't support negation"
|
81
81
|
end
|
82
82
|
|
83
|
-
def supports_block_expectations?()
|
83
|
+
def supports_block_expectations?() = true
|
84
84
|
|
85
85
|
def failure_message
|
86
86
|
"expected a scoping named :#{name} for type :#{type} " \
|
@@ -113,8 +113,8 @@ module ActionPolicy
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def formatted_scopings
|
116
|
-
actual_scopes.map do
|
117
|
-
" - #{
|
116
|
+
actual_scopes.map do
|
117
|
+
it = _1;" - #{it.inspect}"
|
118
118
|
end.join("\n")
|
119
119
|
end
|
120
120
|
end
|
@@ -119,7 +119,7 @@ module ActionPolicy
|
|
119
119
|
|
120
120
|
# Some lines should not be evaled
|
121
121
|
def ignore_exp?(exp)
|
122
|
-
PrettyPrint.ignore_expressions.any? {
|
122
|
+
PrettyPrint.ignore_expressions.any? { it = _1;exp.match?(it) }
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
@@ -127,7 +127,7 @@ module ActionPolicy
|
|
127
127
|
attr_accessor :ignore_expressions
|
128
128
|
|
129
129
|
if defined?(::Prism) && defined?(::MethodSource)
|
130
|
-
def available?()
|
130
|
+
def available?() = true
|
131
131
|
|
132
132
|
def print_method(object, method_name)
|
133
133
|
ast = Prism.parse(object.method(method_name).source)
|
@@ -135,9 +135,9 @@ module ActionPolicy
|
|
135
135
|
Visitor.new(object).collect(ast)
|
136
136
|
end
|
137
137
|
else
|
138
|
-
def available?()
|
138
|
+
def available?() = false
|
139
139
|
|
140
|
-
def print_method(_, _)
|
140
|
+
def print_method(_, _) = ""
|
141
141
|
end
|
142
142
|
|
143
143
|
def colorize(val)
|
@@ -33,11 +33,13 @@ module ActionPolicy
|
|
33
33
|
# Policy is inferred from record
|
34
34
|
# (unless explicitly specified through `with` option).
|
35
35
|
#
|
36
|
+
# @return the policy record
|
36
37
|
# Raises `ActionPolicy::Unauthorized` if check failed.
|
37
38
|
def authorize!(record = :__undef__, to:, **options)
|
38
39
|
policy = lookup_authorization_policy(record, **options)
|
39
40
|
|
40
41
|
Authorizer.call(policy, authorization_rule_for(policy, to))
|
42
|
+
policy.record
|
41
43
|
end
|
42
44
|
|
43
45
|
# Checks that an activity is allowed for the current context (e.g. user).
|
@@ -62,8 +64,8 @@ module ActionPolicy
|
|
62
64
|
|
63
65
|
private def build_authorization_context
|
64
66
|
self.class.authorization_targets
|
65
|
-
.each_with_object({}) do |(key,
|
66
|
-
obj[key] = send(
|
67
|
+
.each_with_object({}) do |(key, method_or_proc), obj|
|
68
|
+
obj[key] = method_or_proc.is_a?(Proc) ? method_or_proc.call : send(method_or_proc)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
@@ -103,8 +105,8 @@ module ActionPolicy
|
|
103
105
|
# authorize :user
|
104
106
|
# end
|
105
107
|
def authorize(key, through: nil)
|
106
|
-
|
107
|
-
authorization_targets[key] =
|
108
|
+
method_or_proc = through || key
|
109
|
+
authorization_targets[key] = method_or_proc
|
108
110
|
end
|
109
111
|
|
110
112
|
def authorization_targets
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **)
|
63
63
|
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map {
|
64
|
+
context_key = context.values.map { it._policy_cache_key(use_object_id: true) }.join(".")
|
65
65
|
|
66
66
|
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
67
|
end
|
data/lib/action_policy/i18n.rb
CHANGED
@@ -21,7 +21,7 @@ module ActionPolicy
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def candidates_for(policy_class, rule)
|
24
|
-
policy_hierarchy = policy_class.ancestors.select {
|
24
|
+
policy_hierarchy = policy_class.ancestors.select { it.respond_to?(:identifier) }
|
25
25
|
[
|
26
26
|
*policy_hierarchy.map { |klass| :"policy.#{klass.identifier}.#{rule}" },
|
27
27
|
:"policy.#{rule}",
|
@@ -78,8 +78,6 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
rebuild_filter
|
80
80
|
end
|
81
|
-
# rubocop: enable
|
82
|
-
# rubocop: enable
|
83
81
|
|
84
82
|
def dup
|
85
83
|
self.class.new(
|
@@ -127,7 +125,7 @@ module ActionPolicy
|
|
127
125
|
def pre_check(*names, **options)
|
128
126
|
names.each do |name|
|
129
127
|
# do not allow pre-check override
|
130
|
-
check = pre_checks.find {
|
128
|
+
check = pre_checks.find { it.name == name }
|
131
129
|
raise "Pre-check already defined: #{name}" unless check.nil?
|
132
130
|
|
133
131
|
pre_checks << Check.new(self, name, **options)
|
@@ -136,14 +134,14 @@ module ActionPolicy
|
|
136
134
|
|
137
135
|
def skip_pre_check(*names, **options)
|
138
136
|
names.each do |name|
|
139
|
-
check = pre_checks.find {
|
137
|
+
check = pre_checks.find { it.name == name }
|
140
138
|
raise "Pre-check not found: #{name}" if check.nil?
|
141
139
|
|
142
140
|
# when no options provided we remove this check completely
|
143
141
|
next pre_checks.delete(check) if options.empty?
|
144
142
|
|
145
143
|
# otherwise duplicate and apply skip options
|
146
|
-
pre_checks[pre_checks.index(check)] = check.dup.tap {
|
144
|
+
pre_checks[pre_checks.index(check)] = check.dup.tap { it.skip!(**options) }
|
147
145
|
end
|
148
146
|
end
|
149
147
|
|
@@ -26,6 +26,7 @@ module ActionPolicy
|
|
26
26
|
if respond_to?(:helper_method)
|
27
27
|
helper_method :allowed_to?
|
28
28
|
helper_method :authorized_scope
|
29
|
+
helper_method :allowance_to
|
29
30
|
end
|
30
31
|
|
31
32
|
attr_writer :authorize_count
|
@@ -44,13 +45,15 @@ module ActionPolicy
|
|
44
45
|
# If record is not provided, tries to infer the resource class
|
45
46
|
# from controller name (i.e. `controller_name.classify.safe_constantize`).
|
46
47
|
#
|
48
|
+
# @return the policy record
|
47
49
|
# Raises `ActionPolicy::Unauthorized` if check failed.
|
48
50
|
def authorize!(record = :__undef__, to: nil, **options)
|
49
51
|
to ||= :"#{action_name}?"
|
50
52
|
|
51
|
-
super
|
53
|
+
policy_record = super
|
52
54
|
|
53
55
|
self.authorize_count += 1
|
56
|
+
policy_record
|
54
57
|
end
|
55
58
|
|
56
59
|
# Tries to infer the resource class from controller name
|
@@ -51,7 +51,7 @@ module ActionPolicy
|
|
51
51
|
|
52
52
|
@actual_calls = ActionPolicy::Testing::AuthorizeTracker.calls
|
53
53
|
|
54
|
-
actual_calls.any? {
|
54
|
+
actual_calls.any? { it.matches?(policy, rule, target, context) }
|
55
55
|
end
|
56
56
|
|
57
57
|
def does_not_match?(*)
|
@@ -78,7 +78,7 @@ module ActionPolicy
|
|
78
78
|
|
79
79
|
def formatted_calls
|
80
80
|
actual_calls.map do
|
81
|
-
" - #{
|
81
|
+
" - #{it.inspect}"
|
82
82
|
end.join("\n")
|
83
83
|
end
|
84
84
|
|
@@ -61,7 +61,7 @@ module ActionPolicy
|
|
61
61
|
|
62
62
|
@actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings
|
63
63
|
|
64
|
-
matching_scopes = actual_scopes.select {
|
64
|
+
matching_scopes = actual_scopes.select { it.matches?(policy, type, name, scope_options, context) }
|
65
65
|
|
66
66
|
return false if matching_scopes.empty?
|
67
67
|
|
@@ -114,7 +114,7 @@ module ActionPolicy
|
|
114
114
|
|
115
115
|
def formatted_scopings
|
116
116
|
actual_scopes.map do
|
117
|
-
" - #{
|
117
|
+
" - #{it.inspect}"
|
118
118
|
end.join("\n")
|
119
119
|
end
|
120
120
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# From https://gist.github.com/skryukov/35539d57b51f38235faaace2c1a2c1a1
|
4
|
+
|
5
|
+
require "active_support/inflector"
|
6
|
+
|
7
|
+
module RubyLsp
|
8
|
+
module ActionPolicy
|
9
|
+
class Addon < ::RubyLsp::Addon
|
10
|
+
def name
|
11
|
+
"ActionPolicy"
|
12
|
+
end
|
13
|
+
|
14
|
+
def activate(global_state, outgoing_queue)
|
15
|
+
require "action_policy"
|
16
|
+
warn "[ActionPolicy] Activating Ruby LSP addon v#{::ActionPolicy::VERSION}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def deactivate
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_definition_listener(response_builder, node_context, uri, dispatcher)
|
23
|
+
Definition.new(response_builder, node_context, uri, dispatcher)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Definition
|
28
|
+
include Requests::Support::Common
|
29
|
+
include ActiveSupport::Inflector
|
30
|
+
|
31
|
+
POLICY_SUPERCLASSES = ["ApplicationPolicy", "ActionPolicy::Base"].freeze
|
32
|
+
|
33
|
+
def initialize(response_builder, uri, node_context, dispatcher)
|
34
|
+
@response_builder = response_builder
|
35
|
+
@node_context = node_context
|
36
|
+
@uri = uri
|
37
|
+
@path = uri.to_standardized_path
|
38
|
+
@policy_rules_cache = {}
|
39
|
+
|
40
|
+
dispatcher.register(self, :on_symbol_node_enter)
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_symbol_node_enter(node)
|
44
|
+
return unless in_authorize_call?
|
45
|
+
|
46
|
+
target = @node_context.call_node
|
47
|
+
# authorization target is the first argument (if explicit)
|
48
|
+
policy_class = find_policy_class(target.arguments&.child_node&.first)
|
49
|
+
return unless policy_class
|
50
|
+
|
51
|
+
policy_path = find_policy_file(policy_class)
|
52
|
+
return unless policy_path
|
53
|
+
|
54
|
+
ensure_policy_rules_cached(policy_path)
|
55
|
+
add_definition(policy_path, node.value)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def in_authorize_call?
|
61
|
+
call = @node_context.call_node
|
62
|
+
call.is_a?(Prism::CallNode) && call.message == "authorize!"
|
63
|
+
end
|
64
|
+
|
65
|
+
def find_policy_class(target)
|
66
|
+
content = File.read(@path)
|
67
|
+
document = Prism.parse(content)
|
68
|
+
class_node = find_containing_class(document.value)
|
69
|
+
return derive_policy_from_target(target) unless class_node
|
70
|
+
|
71
|
+
class_name = class_node.constant_path.slice
|
72
|
+
return unless class_name.end_with?("Controller", "Channel")
|
73
|
+
|
74
|
+
resource_name = class_name
|
75
|
+
.delete_suffix("Controller")
|
76
|
+
.delete_suffix("Channel")
|
77
|
+
.singularize
|
78
|
+
"#{resource_name}Policy"
|
79
|
+
end
|
80
|
+
|
81
|
+
def find_containing_class(root)
|
82
|
+
return unless root.respond_to?(:statements)
|
83
|
+
|
84
|
+
root.statements.body.find do |node|
|
85
|
+
node.is_a?(Prism::ClassNode) &&
|
86
|
+
node.constant_path.slice.end_with?("Controller", "Channel")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def derive_policy_from_target(target)
|
91
|
+
target_name = case target
|
92
|
+
when Prism::InstanceVariableReadNode
|
93
|
+
target.name.to_s[1..].classify
|
94
|
+
when Prism::ConstantReadNode
|
95
|
+
target.name.to_s
|
96
|
+
when NilClass
|
97
|
+
return
|
98
|
+
else
|
99
|
+
target.slice
|
100
|
+
end
|
101
|
+
|
102
|
+
target_name.end_with?("Policy") ? target_name : "#{target_name}Policy"
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_policy_file(policy_class)
|
106
|
+
file_path = policy_class.gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase
|
107
|
+
root_path = Dir.pwd
|
108
|
+
|
109
|
+
[
|
110
|
+
File.join(root_path, "app/policies/#{file_path}.rb"),
|
111
|
+
File.join(root_path, "app/policies/#{file_path}_policy.rb"),
|
112
|
+
*Dir.glob(File.join(root_path, "app/policies/**/#{file_path}.rb")),
|
113
|
+
*Dir.glob(File.join(root_path, "app/policies/**/#{file_path}_policy.rb"))
|
114
|
+
].find { |path| File.exist?(path) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def ensure_policy_rules_cached(policy_path)
|
118
|
+
return if @policy_rules_cache[policy_path]
|
119
|
+
|
120
|
+
content = File.read(policy_path)
|
121
|
+
document = Prism.parse(content)
|
122
|
+
|
123
|
+
document.value.statements.body.each do |stmt|
|
124
|
+
if stmt.is_a?(Prism::ClassNode)
|
125
|
+
@policy_rules_cache[policy_path] = extract_rules(stmt)
|
126
|
+
break
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def extract_rules(node)
|
132
|
+
return {} unless node.body
|
133
|
+
|
134
|
+
rules = {}
|
135
|
+
private_section = false
|
136
|
+
|
137
|
+
node.body.child_nodes.each do |stmt|
|
138
|
+
case stmt
|
139
|
+
when Prism::CallNode
|
140
|
+
if stmt.message == "private"
|
141
|
+
private_section = true
|
142
|
+
elsif stmt.message == "alias_rule" && stmt.arguments&.arguments
|
143
|
+
stmt.arguments.arguments
|
144
|
+
.select { |arg| arg.is_a?(Prism::SymbolNode) }
|
145
|
+
.each { |arg| rules[arg.value] ||= stmt.location.start_line }
|
146
|
+
end
|
147
|
+
when Prism::DefNode
|
148
|
+
next if private_section
|
149
|
+
rules[stmt.name.to_s] = stmt.location.start_line
|
150
|
+
end
|
151
|
+
end
|
152
|
+
rules
|
153
|
+
end
|
154
|
+
|
155
|
+
def add_definition(policy_path, action)
|
156
|
+
rules = @policy_rules_cache[policy_path]
|
157
|
+
line_number = rules&.[](action.to_s)
|
158
|
+
return unless line_number
|
159
|
+
|
160
|
+
@response_builder << Interface::Location.new(
|
161
|
+
uri: URI::Generic.from_path(path: policy_path).to_s,
|
162
|
+
range: Interface::Range.new(
|
163
|
+
start: Interface::Position.new(line: line_number - 1, character: 0),
|
164
|
+
end: Interface::Position.new(line: line_number - 1, character: 0)
|
165
|
+
)
|
166
|
+
)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_policy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-next-core
|
@@ -133,15 +133,6 @@ files:
|
|
133
133
|
- LICENSE.txt
|
134
134
|
- README.md
|
135
135
|
- config/rubocop-rspec.yml
|
136
|
-
- lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb
|
137
|
-
- lib/.rbnext/2.7/action_policy/i18n.rb
|
138
|
-
- lib/.rbnext/2.7/action_policy/policy/cache.rb
|
139
|
-
- lib/.rbnext/2.7/action_policy/policy/pre_check.rb
|
140
|
-
- lib/.rbnext/2.7/action_policy/rails/scope_matchers/action_controller_params.rb
|
141
|
-
- lib/.rbnext/2.7/action_policy/rails/scope_matchers/active_record.rb
|
142
|
-
- lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb
|
143
|
-
- lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb
|
144
|
-
- lib/.rbnext/2.7/action_policy/utils/pretty_print.rb
|
145
136
|
- lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb
|
146
137
|
- lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb
|
147
138
|
- lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb
|
@@ -152,6 +143,8 @@ files:
|
|
152
143
|
- lib/.rbnext/3.0/action_policy/policy/execution_result.rb
|
153
144
|
- lib/.rbnext/3.0/action_policy/policy/pre_check.rb
|
154
145
|
- lib/.rbnext/3.0/action_policy/policy/reasons.rb
|
146
|
+
- lib/.rbnext/3.0/action_policy/rails/scope_matchers/action_controller_params.rb
|
147
|
+
- lib/.rbnext/3.0/action_policy/rails/scope_matchers/active_record.rb
|
155
148
|
- lib/.rbnext/3.0/action_policy/rspec/be_an_alias_of.rb
|
156
149
|
- lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb
|
157
150
|
- lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb
|
@@ -169,6 +162,13 @@ files:
|
|
169
162
|
- lib/.rbnext/3.2/action_policy/rspec/be_authorized_to.rb
|
170
163
|
- lib/.rbnext/3.2/action_policy/rspec/have_authorized_scope.rb
|
171
164
|
- lib/.rbnext/3.2/action_policy/utils/suggest_message.rb
|
165
|
+
- lib/.rbnext/3.4/action_policy/behaviours/policy_for.rb
|
166
|
+
- lib/.rbnext/3.4/action_policy/i18n.rb
|
167
|
+
- lib/.rbnext/3.4/action_policy/policy/cache.rb
|
168
|
+
- lib/.rbnext/3.4/action_policy/policy/pre_check.rb
|
169
|
+
- lib/.rbnext/3.4/action_policy/rspec/be_authorized_to.rb
|
170
|
+
- lib/.rbnext/3.4/action_policy/rspec/have_authorized_scope.rb
|
171
|
+
- lib/.rbnext/3.4/action_policy/utils/pretty_print.rb
|
172
172
|
- lib/action_policy.rb
|
173
173
|
- lib/action_policy/authorizer.rb
|
174
174
|
- lib/action_policy/base.rb
|
@@ -225,6 +225,7 @@ files:
|
|
225
225
|
- lib/generators/rspec/templates/policy_spec.rb.tt
|
226
226
|
- lib/generators/test_unit/policy_generator.rb
|
227
227
|
- lib/generators/test_unit/templates/policy_test.rb.tt
|
228
|
+
- lib/ruby_lsp/action_policy/addon.rb
|
228
229
|
homepage: https://github.com/palkan/action_policy
|
229
230
|
licenses:
|
230
231
|
- MIT
|
@@ -234,7 +235,7 @@ metadata:
|
|
234
235
|
documentation_uri: https://actionpolicy.evilmartians.io/
|
235
236
|
homepage_uri: https://actionpolicy.evilmartians.io/
|
236
237
|
source_code_uri: http://github.com/palkan/action_policy
|
237
|
-
post_install_message:
|
238
|
+
post_install_message:
|
238
239
|
rdoc_options: []
|
239
240
|
require_paths:
|
240
241
|
- lib
|
@@ -250,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
251
|
version: '0'
|
251
252
|
requirements: []
|
252
253
|
rubygems_version: 3.4.19
|
253
|
-
signing_key:
|
254
|
+
signing_key:
|
254
255
|
specification_version: 4
|
255
256
|
summary: Authorization framework for Ruby/Rails application
|
256
257
|
test_files: []
|
/data/lib/.rbnext/{2.7 → 3.0}/action_policy/rails/scope_matchers/action_controller_params.rb
RENAMED
File without changes
|
File without changes
|