action_policy 0.7.2 → 0.7.4
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/policy/cache.rb +6 -6
- data/lib/.rbnext/3.0/action_policy/policy/core.rb +34 -16
- data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +0 -2
- data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +4 -3
- data/lib/.rbnext/3.2/action_policy/policy/core.rb +34 -16
- data/lib/action_policy/authorizer.rb +8 -5
- data/lib/action_policy/behaviour.rb +7 -6
- data/lib/action_policy/policy/cache.rb +6 -6
- data/lib/action_policy/policy/cached_apply.rb +5 -8
- data/lib/action_policy/policy/core.rb +34 -16
- data/lib/action_policy/policy/pre_check.rb +0 -2
- data/lib/action_policy/policy/reasons.rb +4 -3
- data/lib/action_policy/rails/authorizer.rb +4 -4
- data/lib/action_policy/rails/controller.rb +4 -1
- data/lib/action_policy/rails/policy/instrumentation.rb +3 -3
- data/lib/action_policy/rspec/dsl.rb +1 -0
- data/lib/action_policy/version.rb +1 -1
- metadata +7 -14
- data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +0 -70
- data/lib/.rbnext/2.7/action_policy/i18n.rb +0 -56
- data/lib/.rbnext/2.7/action_policy/policy/cache.rb +0 -101
- data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +0 -162
- data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +0 -96
- data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +0 -130
- data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +0 -155
- /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
@@ -1,70 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActionPolicy
|
4
|
-
module Behaviours
|
5
|
-
# Adds `policy_for` method
|
6
|
-
module PolicyFor
|
7
|
-
require "action_policy/ext/policy_cache_key"
|
8
|
-
using ActionPolicy::Ext::PolicyCacheKey
|
9
|
-
|
10
|
-
# Returns policy instance for the record.
|
11
|
-
def policy_for(record:, with: nil, namespace: authorization_namespace, context: nil, allow_nil: false, default: default_authorization_policy_class, strict_namespace: authorization_strict_namespace)
|
12
|
-
context = context ? build_authorization_context.merge(context) : authorization_context
|
13
|
-
|
14
|
-
policy_class = with || ::ActionPolicy.lookup(
|
15
|
-
record,
|
16
|
-
namespace: namespace, context: context, allow_nil: allow_nil, default: default, strict_namespace: strict_namespace
|
17
|
-
)
|
18
|
-
policy_class&.new(record, **context)
|
19
|
-
end
|
20
|
-
|
21
|
-
def authorization_context ; @authorization_context ||= build_authorization_context; end
|
22
|
-
|
23
|
-
def build_authorization_context
|
24
|
-
Kernel.raise NotImplementedError, "Please, define `build_authorization_context` method!"
|
25
|
-
end
|
26
|
-
|
27
|
-
def authorization_namespace
|
28
|
-
# override to provide specific authorization namespace
|
29
|
-
end
|
30
|
-
|
31
|
-
def default_authorization_policy_class
|
32
|
-
# override to provide a policy class use when no policy found
|
33
|
-
end
|
34
|
-
|
35
|
-
def authorization_strict_namespace
|
36
|
-
# override to provide strict namespace lookup option
|
37
|
-
end
|
38
|
-
|
39
|
-
# Override this method to provide implicit authorization target
|
40
|
-
# that would be used in case `record` is not specified in
|
41
|
-
# `authorize!` and `allowed_to?` call.
|
42
|
-
#
|
43
|
-
# It is also used to infer a policy for scoping (in `authorized_scope` method).
|
44
|
-
def implicit_authorization_target
|
45
|
-
# no-op
|
46
|
-
end
|
47
|
-
|
48
|
-
# Return implicit authorization target or raises an exception if it's nil
|
49
|
-
def implicit_authorization_target!
|
50
|
-
implicit_authorization_target || Kernel.raise(
|
51
|
-
NotFound,
|
52
|
-
[
|
53
|
-
self,
|
54
|
-
"Couldn't find implicit authorization target " \
|
55
|
-
"for #{self.class}. " \
|
56
|
-
"Please, provide policy class explicitly using `with` option or " \
|
57
|
-
"define the `implicit_authorization_target` method."
|
58
|
-
]
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
def policy_for_cache_key(record:, with: nil, namespace: nil, context: authorization_context, **__kwrest__)
|
63
|
-
record_key = record._policy_cache_key(use_object_id: true)
|
64
|
-
context_key = context.values.map { |_1| _1._policy_cache_key(use_object_id: true) }.join(".")
|
65
|
-
|
66
|
-
"#{namespace}/#{with}/#{context_key}/#{record_key}"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActionPolicy
|
4
|
-
module I18n # :nodoc:
|
5
|
-
DEFAULT_UNAUTHORIZED_MESSAGE = "You are not authorized to perform this action"
|
6
|
-
|
7
|
-
class << self
|
8
|
-
def full_message(policy_class, rule, details = nil)
|
9
|
-
candidates = candidates_for(policy_class, rule)
|
10
|
-
|
11
|
-
options = {scope: :action_policy}
|
12
|
-
options.merge!(details) unless details.nil?
|
13
|
-
|
14
|
-
::I18n.t(
|
15
|
-
candidates.shift,
|
16
|
-
default: candidates,
|
17
|
-
**options
|
18
|
-
)
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def candidates_for(policy_class, rule)
|
24
|
-
policy_hierarchy = policy_class.ancestors.select { |_1| _1.respond_to?(:identifier) }
|
25
|
-
[
|
26
|
-
*policy_hierarchy.map { |klass| :"policy.#{klass.identifier}.#{rule}" },
|
27
|
-
:"policy.#{rule}",
|
28
|
-
:unauthorized,
|
29
|
-
DEFAULT_UNAUTHORIZED_MESSAGE
|
30
|
-
]
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
ActionPolicy::Policy::FailureReasons.prepend(Module.new do
|
35
|
-
def full_messages
|
36
|
-
reasons.flat_map do |policy_klass, rules|
|
37
|
-
rules.flat_map do |rule|
|
38
|
-
if rule.is_a?(::Hash)
|
39
|
-
rule.map do |key, details|
|
40
|
-
ActionPolicy::I18n.full_message(policy_klass, key, details)
|
41
|
-
end
|
42
|
-
else
|
43
|
-
ActionPolicy::I18n.full_message(policy_klass, rule)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end)
|
49
|
-
|
50
|
-
ActionPolicy::Policy::ExecutionResult.prepend(Module.new do
|
51
|
-
def message
|
52
|
-
ActionPolicy::I18n.full_message(policy, rule, details)
|
53
|
-
end
|
54
|
-
end)
|
55
|
-
end
|
56
|
-
end
|
@@ -1,101 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action_policy/version"
|
4
|
-
|
5
|
-
module ActionPolicy # :nodoc:
|
6
|
-
using RubyNext
|
7
|
-
|
8
|
-
# By default cache namespace (or prefix) contains major and minor version of the gem
|
9
|
-
CACHE_NAMESPACE = "acp:#{ActionPolicy::VERSION.split(".").take(2).join(".")}"
|
10
|
-
|
11
|
-
require "action_policy/ext/policy_cache_key"
|
12
|
-
|
13
|
-
using ActionPolicy::Ext::PolicyCacheKey
|
14
|
-
|
15
|
-
module Policy
|
16
|
-
# Provides long-lived cache through ActionPolicy.cache_store.
|
17
|
-
#
|
18
|
-
# NOTE: if cache_store is nil then we silently skip all the caching.
|
19
|
-
module Cache
|
20
|
-
class << self
|
21
|
-
def included(base)
|
22
|
-
base.extend ClassMethods
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def cache_namespace() ; ActionPolicy::CACHE_NAMESPACE; end
|
27
|
-
|
28
|
-
def cache_key(*parts)
|
29
|
-
[
|
30
|
-
cache_namespace,
|
31
|
-
*parts
|
32
|
-
].map { |_1| _1._policy_cache_key }.join("/")
|
33
|
-
end
|
34
|
-
|
35
|
-
def rule_cache_key(rule)
|
36
|
-
cache_key(
|
37
|
-
context_cache_key,
|
38
|
-
record,
|
39
|
-
self.class,
|
40
|
-
rule
|
41
|
-
)
|
42
|
-
end
|
43
|
-
|
44
|
-
def context_cache_key
|
45
|
-
authorization_context.map { |_1, _2| _2._policy_cache_key.to_s }.join("/")
|
46
|
-
end
|
47
|
-
|
48
|
-
def apply_with_cache(rule)
|
49
|
-
options = self.class.cached_rules.fetch(rule)
|
50
|
-
key = rule_cache_key(rule)
|
51
|
-
|
52
|
-
ActionPolicy.cache_store.then do |store|
|
53
|
-
@result = store.read(key)
|
54
|
-
unless result.nil?
|
55
|
-
result.cached!
|
56
|
-
next result.value
|
57
|
-
end
|
58
|
-
yield
|
59
|
-
store.write(key, result, options)
|
60
|
-
result.value
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def apply(rule)
|
65
|
-
return super if ActionPolicy.cache_store.nil? ||
|
66
|
-
!self.class.cached_rules.key?(rule)
|
67
|
-
|
68
|
-
apply_with_cache(rule) { super }
|
69
|
-
end
|
70
|
-
|
71
|
-
def cache(*parts, **options)
|
72
|
-
key = cache_key(*parts)
|
73
|
-
ActionPolicy.cache_store.then do |store|
|
74
|
-
res = store.read(key)
|
75
|
-
next res unless res.nil?
|
76
|
-
res = yield
|
77
|
-
store.write(key, res, options)
|
78
|
-
res
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
module ClassMethods # :nodoc:
|
83
|
-
def cache(*rules, **options)
|
84
|
-
rules.each do |rule|
|
85
|
-
cached_rules[rule] = options
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def cached_rules
|
90
|
-
return @cached_rules if instance_variable_defined?(:@cached_rules)
|
91
|
-
|
92
|
-
@cached_rules = if superclass.respond_to?(:cached_rules)
|
93
|
-
superclass.cached_rules.dup
|
94
|
-
else
|
95
|
-
{}
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ActionPolicy
|
4
|
-
module Policy
|
5
|
-
# Adds callback-style checks to policies to
|
6
|
-
# extract common checks from rules.
|
7
|
-
#
|
8
|
-
# class ApplicationPolicy < ActionPolicy::Base
|
9
|
-
# authorize :user
|
10
|
-
# pre_check :allow_admins
|
11
|
-
#
|
12
|
-
# private
|
13
|
-
# # Allow every action for admins
|
14
|
-
# def allow_admins
|
15
|
-
# allow! if user.admin?
|
16
|
-
# end
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# You can specify conditional pre-checks (through `except` / `only`) options
|
20
|
-
# and skip already defined pre-checks if necessary.
|
21
|
-
#
|
22
|
-
# class UserPolicy < ApplicationPolicy
|
23
|
-
# skip_pre_check :allow_admins, only: :destroy?
|
24
|
-
#
|
25
|
-
# def destroy?
|
26
|
-
# user.admin? && !record.admin?
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
module PreCheck
|
30
|
-
# Single pre-check instance.
|
31
|
-
#
|
32
|
-
# Implements filtering logic.
|
33
|
-
class Check
|
34
|
-
attr_reader :name, :policy_class
|
35
|
-
|
36
|
-
def initialize(policy, name, except: nil, only: nil)
|
37
|
-
if !except.nil? && !only.nil?
|
38
|
-
raise ArgumentError,
|
39
|
-
"Only one of `except` and `only` may be specified for pre-check"
|
40
|
-
end
|
41
|
-
|
42
|
-
@policy_class = policy
|
43
|
-
@name = name
|
44
|
-
@blacklist = Array(except) unless except.nil?
|
45
|
-
@whitelist = Array(only) unless only.nil?
|
46
|
-
|
47
|
-
rebuild_filter
|
48
|
-
end
|
49
|
-
|
50
|
-
def applicable?(rule)
|
51
|
-
return true if filter.nil?
|
52
|
-
filter.call(rule)
|
53
|
-
end
|
54
|
-
|
55
|
-
def call(policy) ; policy.send(name); end
|
56
|
-
|
57
|
-
def skip!(except: nil, only: nil)
|
58
|
-
if !except.nil? && !only.nil?
|
59
|
-
raise ArgumentError,
|
60
|
-
"Only one of `except` and `only` may be specified when skipping pre-check"
|
61
|
-
end
|
62
|
-
|
63
|
-
if except.nil? && only.nil?
|
64
|
-
raise ArgumentError,
|
65
|
-
"At least one of `except` and `only` must be specified when skipping pre-check"
|
66
|
-
end
|
67
|
-
|
68
|
-
if except
|
69
|
-
@whitelist = Array(except)
|
70
|
-
@whitelist -= blacklist if blacklist
|
71
|
-
@blacklist = nil
|
72
|
-
else
|
73
|
-
# only
|
74
|
-
@blacklist += Array(only) if blacklist
|
75
|
-
@whitelist -= Array(only) if whitelist
|
76
|
-
@blacklist = Array(only) if filter.nil?
|
77
|
-
end
|
78
|
-
|
79
|
-
rebuild_filter
|
80
|
-
end
|
81
|
-
# rubocop: enable
|
82
|
-
# rubocop: enable
|
83
|
-
|
84
|
-
def dup
|
85
|
-
self.class.new(
|
86
|
-
policy_class,
|
87
|
-
name,
|
88
|
-
except: blacklist&.dup,
|
89
|
-
only: whitelist&.dup
|
90
|
-
)
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
attr_reader :whitelist, :blacklist, :filter
|
96
|
-
|
97
|
-
def rebuild_filter
|
98
|
-
@filter =
|
99
|
-
if whitelist
|
100
|
-
proc { |rule| whitelist.include?(rule) }
|
101
|
-
elsif blacklist
|
102
|
-
proc { |rule| !blacklist.include?(rule) }
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
class << self
|
108
|
-
def included(base)
|
109
|
-
base.extend ClassMethods
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def run_pre_checks(rule)
|
114
|
-
self.class.pre_checks.each do |check|
|
115
|
-
next unless check.applicable?(rule)
|
116
|
-
check.call(self)
|
117
|
-
end
|
118
|
-
|
119
|
-
yield if block_given?
|
120
|
-
end
|
121
|
-
|
122
|
-
def __apply__(rule)
|
123
|
-
run_pre_checks(rule) { super }
|
124
|
-
end
|
125
|
-
|
126
|
-
module ClassMethods # :nodoc:
|
127
|
-
def pre_check(*names, **options)
|
128
|
-
names.each do |name|
|
129
|
-
# do not allow pre-check override
|
130
|
-
check = pre_checks.find { |_1| _1.name == name }
|
131
|
-
raise "Pre-check already defined: #{name}" unless check.nil?
|
132
|
-
|
133
|
-
pre_checks << Check.new(self, name, **options)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def skip_pre_check(*names, **options)
|
138
|
-
names.each do |name|
|
139
|
-
check = pre_checks.find { |_1| _1.name == name }
|
140
|
-
raise "Pre-check not found: #{name}" if check.nil?
|
141
|
-
|
142
|
-
# when no options provided we remove this check completely
|
143
|
-
next pre_checks.delete(check) if options.empty?
|
144
|
-
|
145
|
-
# otherwise duplicate and apply skip options
|
146
|
-
pre_checks[pre_checks.index(check)] = check.dup.tap { |_1| _1.skip!(**options) }
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def pre_checks
|
151
|
-
return @pre_checks if instance_variable_defined?(:@pre_checks)
|
152
|
-
|
153
|
-
@pre_checks = if superclass.respond_to?(:pre_checks)
|
154
|
-
superclass.pre_checks.dup
|
155
|
-
else
|
156
|
-
[]
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action_policy/testing"
|
4
|
-
|
5
|
-
module ActionPolicy
|
6
|
-
module RSpec
|
7
|
-
# Authorization matcher `be_authorized_to`.
|
8
|
-
#
|
9
|
-
# Verifies that a block of code has been authorized using specific policy.
|
10
|
-
#
|
11
|
-
# Example:
|
12
|
-
#
|
13
|
-
# # in controller/request specs
|
14
|
-
# subject { patch :update, id: product.id }
|
15
|
-
#
|
16
|
-
# it "is authorized" do
|
17
|
-
# expect { subject }
|
18
|
-
# .to be_authorized_to(:manage?, product)
|
19
|
-
# .with(ProductPolicy)
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
class BeAuthorizedTo < ::RSpec::Matchers::BuiltIn::BaseMatcher
|
23
|
-
attr_reader :rule, :target, :policy, :actual_calls, :context
|
24
|
-
|
25
|
-
def initialize(rule, target)
|
26
|
-
@rule = rule
|
27
|
-
@target = target
|
28
|
-
end
|
29
|
-
|
30
|
-
def with(policy)
|
31
|
-
@policy = policy
|
32
|
-
self
|
33
|
-
end
|
34
|
-
|
35
|
-
def with_context(context)
|
36
|
-
@context = context
|
37
|
-
self
|
38
|
-
end
|
39
|
-
|
40
|
-
def match(_expected, actual)
|
41
|
-
raise "This matcher only supports block expectations" unless actual.is_a?(Proc)
|
42
|
-
|
43
|
-
@policy ||= ::ActionPolicy.lookup(target)
|
44
|
-
@context ||= nil
|
45
|
-
|
46
|
-
begin
|
47
|
-
ActionPolicy::Testing::AuthorizeTracker.tracking { actual.call }
|
48
|
-
rescue ActionPolicy::Unauthorized
|
49
|
-
# we don't want to care about authorization result
|
50
|
-
end
|
51
|
-
|
52
|
-
@actual_calls = ActionPolicy::Testing::AuthorizeTracker.calls
|
53
|
-
|
54
|
-
actual_calls.any? { |_1| _1.matches?(policy, rule, target, context) }
|
55
|
-
end
|
56
|
-
|
57
|
-
def does_not_match?(*__rest__)
|
58
|
-
raise "This matcher doesn't support negation"
|
59
|
-
end
|
60
|
-
|
61
|
-
def supports_block_expectations?() ; true; end
|
62
|
-
|
63
|
-
def failure_message
|
64
|
-
"expected #{formatted_record} " \
|
65
|
-
"to be authorized with #{policy}##{rule}, " \
|
66
|
-
"#{context ? "and context #{context.inspect}, " : ""}" \
|
67
|
-
"but #{actual_calls_message}"
|
68
|
-
end
|
69
|
-
|
70
|
-
def actual_calls_message
|
71
|
-
if actual_calls.empty?
|
72
|
-
"no authorization calls have been made"
|
73
|
-
else
|
74
|
-
"the following calls were encountered:\n" \
|
75
|
-
"#{formatted_calls}"
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def formatted_calls
|
80
|
-
actual_calls.map do |_1|
|
81
|
-
" - #{_1.inspect}"
|
82
|
-
end.join("\n")
|
83
|
-
end
|
84
|
-
|
85
|
-
def formatted_record(record = target) ; ::RSpec::Support::ObjectFormatter.format(record); end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
RSpec.configure do |config|
|
91
|
-
config.include(Module.new do
|
92
|
-
def be_authorized_to(rule, target)
|
93
|
-
ActionPolicy::RSpec::BeAuthorizedTo.new(rule, target)
|
94
|
-
end
|
95
|
-
end)
|
96
|
-
end
|
@@ -1,130 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "action_policy/testing"
|
4
|
-
|
5
|
-
module ActionPolicy
|
6
|
-
module RSpec
|
7
|
-
# Implements `have_authorized_scope` matcher.
|
8
|
-
#
|
9
|
-
# Verifies that a block of code applies authorization scoping using specific policy.
|
10
|
-
#
|
11
|
-
# Example:
|
12
|
-
#
|
13
|
-
# # in controller/request specs
|
14
|
-
# subject { get :index }
|
15
|
-
#
|
16
|
-
# it "has authorized scope" do
|
17
|
-
# expect { subject }
|
18
|
-
# .to have_authorized_scope(:active_record_relation)
|
19
|
-
# .with(ProductPolicy)
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
class HaveAuthorizedScope < ::RSpec::Matchers::BuiltIn::BaseMatcher
|
23
|
-
attr_reader :type, :name, :policy, :scope_options, :actual_scopes,
|
24
|
-
:target_expectations, :context
|
25
|
-
|
26
|
-
def initialize(type)
|
27
|
-
@type = type
|
28
|
-
@name = :default
|
29
|
-
@scope_options = nil
|
30
|
-
end
|
31
|
-
|
32
|
-
def with(policy)
|
33
|
-
@policy = policy
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
def as(name)
|
38
|
-
@name = name
|
39
|
-
self
|
40
|
-
end
|
41
|
-
|
42
|
-
def with_scope_options(scope_options)
|
43
|
-
@scope_options = scope_options
|
44
|
-
self
|
45
|
-
end
|
46
|
-
|
47
|
-
def with_target(&block)
|
48
|
-
@target_expectations = block
|
49
|
-
self
|
50
|
-
end
|
51
|
-
|
52
|
-
def with_context(context)
|
53
|
-
@context = context
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
|
-
def match(_expected, actual)
|
58
|
-
raise "This matcher only supports block expectations" unless actual.is_a?(Proc)
|
59
|
-
|
60
|
-
ActionPolicy::Testing::AuthorizeTracker.tracking { actual.call }
|
61
|
-
|
62
|
-
@actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings
|
63
|
-
|
64
|
-
matching_scopes = actual_scopes.select { |_1| _1.matches?(policy, type, name, scope_options, context) }
|
65
|
-
|
66
|
-
return false if matching_scopes.empty?
|
67
|
-
|
68
|
-
return true unless target_expectations
|
69
|
-
|
70
|
-
if matching_scopes.size > 1
|
71
|
-
raise "Too many matching scopings (#{matching_scopes.size}), " \
|
72
|
-
"you can run `.with_target` only when there is the only one match"
|
73
|
-
end
|
74
|
-
|
75
|
-
target_expectations.call(matching_scopes.first.target)
|
76
|
-
true
|
77
|
-
end
|
78
|
-
|
79
|
-
def does_not_match?(*__rest__)
|
80
|
-
raise "This matcher doesn't support negation"
|
81
|
-
end
|
82
|
-
|
83
|
-
def supports_block_expectations?() ; true; end
|
84
|
-
|
85
|
-
def failure_message
|
86
|
-
"expected a scoping named :#{name} for type :#{type} " \
|
87
|
-
"#{scope_options_message} " \
|
88
|
-
"#{context ? "and context #{context.inspect} " : ""}" \
|
89
|
-
"from #{policy} to have been applied, " \
|
90
|
-
"but #{actual_scopes_message}"
|
91
|
-
end
|
92
|
-
|
93
|
-
def scope_options_message
|
94
|
-
if scope_options
|
95
|
-
if defined?(::RSpec::Matchers::Composable) &&
|
96
|
-
scope_options.is_a?(::RSpec::Matchers::Composable)
|
97
|
-
"with scope options #{scope_options.description}"
|
98
|
-
else
|
99
|
-
"with scope options #{scope_options}"
|
100
|
-
end
|
101
|
-
else
|
102
|
-
"without scope options"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def actual_scopes_message
|
107
|
-
if actual_scopes.empty?
|
108
|
-
"no scopings have been made"
|
109
|
-
else
|
110
|
-
"the following scopings were encountered:\n" \
|
111
|
-
"#{formatted_scopings}"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def formatted_scopings
|
116
|
-
actual_scopes.map do |_1|
|
117
|
-
" - #{_1.inspect}"
|
118
|
-
end.join("\n")
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
RSpec.configure do |config|
|
125
|
-
config.include(Module.new do
|
126
|
-
def have_authorized_scope(type)
|
127
|
-
ActionPolicy::RSpec::HaveAuthorizedScope.new(type)
|
128
|
-
end
|
129
|
-
end)
|
130
|
-
end
|