action_policy 0.2.4 → 0.3.0.beta1
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/.rubocop.yml +26 -64
- data/.travis.yml +13 -10
- data/CHANGELOG.md +216 -1
- data/Gemfile +7 -0
- data/LICENSE.txt +1 -1
- data/Rakefile +10 -0
- data/action_policy.gemspec +5 -3
- data/benchmarks/namespaced_lookup_cache.rb +18 -22
- data/docs/README.md +3 -3
- data/docs/_sidebar.md +4 -0
- data/docs/aliases.md +9 -5
- data/docs/authorization_context.md +59 -1
- data/docs/behaviour.md +113 -0
- data/docs/caching.md +6 -4
- data/docs/custom_policy.md +1 -2
- data/docs/debugging.md +55 -0
- data/docs/decorators.md +27 -0
- data/docs/i18n.md +41 -2
- data/docs/instrumentation.md +70 -2
- data/docs/lookup_chain.md +5 -4
- data/docs/namespaces.md +1 -1
- data/docs/non_rails.md +2 -3
- data/docs/pundit_migration.md +77 -2
- data/docs/quick_start.md +5 -5
- data/docs/rails.md +5 -2
- data/docs/reasons.md +50 -3
- data/docs/scoping.md +262 -0
- data/docs/testing.md +232 -21
- data/docs/writing_policies.md +1 -1
- data/gemfiles/jruby.gemfile +3 -0
- data/gemfiles/rails42.gemfile +3 -0
- data/gemfiles/rails6.gemfile +8 -0
- data/gemfiles/railsmaster.gemfile +1 -1
- data/lib/action_policy.rb +3 -3
- data/lib/action_policy/authorizer.rb +12 -4
- data/lib/action_policy/base.rb +2 -0
- data/lib/action_policy/behaviour.rb +14 -3
- data/lib/action_policy/behaviours/memoized.rb +1 -1
- data/lib/action_policy/behaviours/policy_for.rb +12 -3
- data/lib/action_policy/behaviours/scoping.rb +32 -0
- data/lib/action_policy/behaviours/thread_memoized.rb +1 -1
- data/lib/action_policy/ext/hash_transform_keys.rb +19 -0
- data/lib/action_policy/ext/module_namespace.rb +1 -1
- data/lib/action_policy/ext/policy_cache_key.rb +2 -1
- data/lib/action_policy/ext/proc_case_eq.rb +14 -0
- data/lib/action_policy/ext/string_constantize.rb +1 -0
- data/lib/action_policy/ext/symbol_classify.rb +22 -0
- data/lib/action_policy/i18n.rb +56 -0
- data/lib/action_policy/lookup_chain.rb +21 -3
- data/lib/action_policy/policy/cache.rb +10 -6
- data/lib/action_policy/policy/core.rb +31 -19
- data/lib/action_policy/policy/execution_result.rb +12 -0
- data/lib/action_policy/policy/pre_check.rb +2 -6
- data/lib/action_policy/policy/reasons.rb +99 -12
- data/lib/action_policy/policy/scoping.rb +165 -0
- data/lib/action_policy/rails/authorizer.rb +20 -0
- data/lib/action_policy/rails/controller.rb +4 -14
- data/lib/action_policy/rails/ext/active_record.rb +10 -0
- data/lib/action_policy/rails/policy/instrumentation.rb +24 -0
- data/lib/action_policy/rails/scope_matchers/action_controller_params.rb +19 -0
- data/lib/action_policy/rails/scope_matchers/active_record.rb +29 -0
- data/lib/action_policy/railtie.rb +29 -7
- data/lib/action_policy/rspec.rb +1 -0
- data/lib/action_policy/rspec/be_authorized_to.rb +1 -1
- data/lib/action_policy/rspec/dsl.rb +103 -0
- data/lib/action_policy/rspec/have_authorized_scope.rb +126 -0
- data/lib/action_policy/rspec/pundit_syntax.rb +1 -1
- data/lib/action_policy/test_helper.rb +69 -4
- data/lib/action_policy/testing.rb +54 -0
- data/lib/action_policy/utils/pretty_print.rb +137 -0
- data/lib/action_policy/utils/suggest_message.rb +21 -0
- data/lib/action_policy/version.rb +1 -1
- metadata +58 -11
@@ -24,11 +24,50 @@ module ActionPolicy
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
class Scoping # :nodoc:
|
28
|
+
attr_reader :policy, :target, :type, :name, :scope_options
|
29
|
+
|
30
|
+
def initialize(policy, target, type, name, scope_options)
|
31
|
+
@policy = policy
|
32
|
+
@target = target
|
33
|
+
@type = type
|
34
|
+
@name = name
|
35
|
+
@scope_options = scope_options
|
36
|
+
end
|
37
|
+
|
38
|
+
def matches?(policy_class, actual_type, actual_name, actual_scope_options)
|
39
|
+
policy_class == policy.class &&
|
40
|
+
type == actual_type &&
|
41
|
+
name == actual_name &&
|
42
|
+
actual_scope_options === scope_options
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
"#{policy.class} :#{name} for :#{type} #{scope_options_message}"
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def scope_options_message
|
52
|
+
if scope_options
|
53
|
+
if defined?(::RSpec::Matchers::Composable) &&
|
54
|
+
scope_options.is_a?(::RSpec::Matchers::Composable)
|
55
|
+
"with scope options #{scope_options.description}"
|
56
|
+
else
|
57
|
+
"with scope options #{scope_options}"
|
58
|
+
end
|
59
|
+
else
|
60
|
+
"without scope options"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
27
65
|
class << self
|
28
66
|
# Wrap code under inspection into this method
|
29
67
|
# to track authorize! calls
|
30
68
|
def tracking
|
31
69
|
calls.clear
|
70
|
+
scopings.clear
|
32
71
|
Thread.current[:__action_policy_tracking] = true
|
33
72
|
yield
|
34
73
|
ensure
|
@@ -41,10 +80,20 @@ module ActionPolicy
|
|
41
80
|
calls << Call.new(policy, rule)
|
42
81
|
end
|
43
82
|
|
83
|
+
# Called from Authorizer
|
84
|
+
def track_scope(target, policy, type:, name:, scope_options:)
|
85
|
+
return unless tracking?
|
86
|
+
scopings << Scoping.new(policy, target, type, name, scope_options)
|
87
|
+
end
|
88
|
+
|
44
89
|
def calls
|
45
90
|
Thread.current[:__action_policy_calls] ||= []
|
46
91
|
end
|
47
92
|
|
93
|
+
def scopings
|
94
|
+
Thread.current[:__action_policy_scopings] ||= []
|
95
|
+
end
|
96
|
+
|
48
97
|
def tracking?
|
49
98
|
Thread.current[:__action_policy_tracking] == true
|
50
99
|
end
|
@@ -57,6 +106,11 @@ module ActionPolicy
|
|
57
106
|
AuthorizeTracker.track(*args)
|
58
107
|
super
|
59
108
|
end
|
109
|
+
|
110
|
+
def scopify(*args)
|
111
|
+
AuthorizeTracker.track_scope(*args)
|
112
|
+
super
|
113
|
+
end
|
60
114
|
end
|
61
115
|
|
62
116
|
ActionPolicy::Authorizer.singleton_class.prepend(AuthorizerExt)
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
old_verbose = $VERBOSE
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "method_source"
|
7
|
+
# Ignore parse warnings when patch
|
8
|
+
# Ruby version mismatches
|
9
|
+
$VERBOSE = nil
|
10
|
+
require "parser/current"
|
11
|
+
require "unparser"
|
12
|
+
rescue LoadError
|
13
|
+
# do nothing
|
14
|
+
ensure
|
15
|
+
$VERBOSE = old_verbose
|
16
|
+
end
|
17
|
+
|
18
|
+
module ActionPolicy
|
19
|
+
unless "".respond_to?(:then)
|
20
|
+
require "action_policy/ext/yield_self_then"
|
21
|
+
using ActionPolicy::Ext::YieldSelfThen
|
22
|
+
end
|
23
|
+
|
24
|
+
# Takes the object and a method name,
|
25
|
+
# and returns the "annotated" source code for the method:
|
26
|
+
# code is split into parts by logical operators and each
|
27
|
+
# part is evaluated separately.
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
#
|
31
|
+
# class MyClass
|
32
|
+
# def access?
|
33
|
+
# admin? && access_feed?
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# puts PrettyPrint.format_method(MyClass.new, :access?)
|
38
|
+
#
|
39
|
+
# #=> MyClass#access?
|
40
|
+
# #=> ↳ admin? #=> false
|
41
|
+
# #=> AND
|
42
|
+
# #=> access_feed? #=> true
|
43
|
+
module PrettyPrint
|
44
|
+
class Visitor
|
45
|
+
attr_reader :lines, :object
|
46
|
+
attr_accessor :indent
|
47
|
+
|
48
|
+
def initialize(object)
|
49
|
+
@object = object
|
50
|
+
end
|
51
|
+
|
52
|
+
def collect(ast)
|
53
|
+
@lines = []
|
54
|
+
@indent = 0
|
55
|
+
|
56
|
+
visit_node(ast)
|
57
|
+
|
58
|
+
lines.join("\n")
|
59
|
+
end
|
60
|
+
|
61
|
+
def visit_node(ast)
|
62
|
+
if respond_to?("visit_#{ast.type}")
|
63
|
+
send("visit_#{ast.type}", ast)
|
64
|
+
else
|
65
|
+
visit_missing ast
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def expression_with_result(sexp)
|
70
|
+
expression = Unparser.unparse(sexp)
|
71
|
+
"#{expression} #=> #{eval_exp(expression)}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def eval_exp(exp)
|
75
|
+
object.instance_eval(exp)
|
76
|
+
rescue => e
|
77
|
+
"Failed: #{e.message}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_and(ast)
|
81
|
+
visit_node(ast.children[0])
|
82
|
+
lines << indented("AND")
|
83
|
+
visit_node(ast.children[1])
|
84
|
+
end
|
85
|
+
|
86
|
+
def visit_or(ast)
|
87
|
+
visit_node(ast.children[0])
|
88
|
+
lines << indented("OR")
|
89
|
+
visit_node(ast.children[1])
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parens
|
93
|
+
def visit_begin(ast)
|
94
|
+
lines << indented("(")
|
95
|
+
self.indent += 2
|
96
|
+
visit_node(ast.children[0])
|
97
|
+
self.indent -= 2
|
98
|
+
lines << indented(")")
|
99
|
+
end
|
100
|
+
|
101
|
+
def visit_missing(ast)
|
102
|
+
lines << indented(expression_with_result(ast))
|
103
|
+
end
|
104
|
+
|
105
|
+
def indented(str)
|
106
|
+
"#{indent.zero? ? "↳ " : ""}#{" " * indent}#{str}".tap do
|
107
|
+
# increase indent after the first expression
|
108
|
+
self.indent += 2 if indent.zero?
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class << self
|
114
|
+
if defined?(::Unparser) && defined?(::MethodSource)
|
115
|
+
def available?
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def print_method(object, method_name)
|
120
|
+
ast = object.method(method_name).source.then(&Unparser.method(:parse))
|
121
|
+
# outer node is a method definition itself
|
122
|
+
body = ast.children[2]
|
123
|
+
|
124
|
+
Visitor.new(object).collect(body)
|
125
|
+
end
|
126
|
+
else
|
127
|
+
def available?
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
131
|
+
def print_method(_, _)
|
132
|
+
""
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionPolicy
|
4
|
+
# Adds `suggest` method which uses did_you_mean
|
5
|
+
# to generate a suggestion message
|
6
|
+
module SuggestMessage
|
7
|
+
if defined?(::DidYouMean::SpellChecker)
|
8
|
+
def suggest(needle, heystack)
|
9
|
+
suggestion = ::DidYouMean::SpellChecker.new(
|
10
|
+
dictionary: heystack
|
11
|
+
).correct(needle).first
|
12
|
+
|
13
|
+
suggestion ? "\nDid you mean? #{suggestion}" : ""
|
14
|
+
end
|
15
|
+
else
|
16
|
+
def suggest(*)
|
17
|
+
""
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_policy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dementyev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.15'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.15'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
75
|
+
version: 0.65.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
82
|
+
version: 0.65.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rubocop-md
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: standard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.0.36
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.0.36
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: benchmark-ips
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +122,20 @@ dependencies:
|
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: 2.7.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: i18n
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
111
139
|
description: Authorization framework for Ruby/Rails application
|
112
140
|
email:
|
113
141
|
- dementiev.vm@gmail.com
|
@@ -147,10 +175,13 @@ files:
|
|
147
175
|
- docs/assets/styles.css
|
148
176
|
- docs/assets/vue.min.css
|
149
177
|
- docs/authorization_context.md
|
178
|
+
- docs/behaviour.md
|
150
179
|
- docs/caching.md
|
151
180
|
- docs/controller_action_aliases.md
|
152
181
|
- docs/custom_lookup_chain.md
|
153
182
|
- docs/custom_policy.md
|
183
|
+
- docs/debugging.md
|
184
|
+
- docs/decorators.md
|
154
185
|
- docs/favicon.ico
|
155
186
|
- docs/i18n.md
|
156
187
|
- docs/index.html
|
@@ -163,10 +194,12 @@ files:
|
|
163
194
|
- docs/quick_start.md
|
164
195
|
- docs/rails.md
|
165
196
|
- docs/reasons.md
|
197
|
+
- docs/scoping.md
|
166
198
|
- docs/testing.md
|
167
199
|
- docs/writing_policies.md
|
168
200
|
- gemfiles/jruby.gemfile
|
169
201
|
- gemfiles/rails42.gemfile
|
202
|
+
- gemfiles/rails6.gemfile
|
170
203
|
- gemfiles/railsmaster.gemfile
|
171
204
|
- lib/action_policy.rb
|
172
205
|
- lib/action_policy/authorizer.rb
|
@@ -175,14 +208,19 @@ files:
|
|
175
208
|
- lib/action_policy/behaviours/memoized.rb
|
176
209
|
- lib/action_policy/behaviours/namespaced.rb
|
177
210
|
- lib/action_policy/behaviours/policy_for.rb
|
211
|
+
- lib/action_policy/behaviours/scoping.rb
|
178
212
|
- lib/action_policy/behaviours/thread_memoized.rb
|
179
213
|
- lib/action_policy/cache_middleware.rb
|
214
|
+
- lib/action_policy/ext/hash_transform_keys.rb
|
180
215
|
- lib/action_policy/ext/module_namespace.rb
|
181
216
|
- lib/action_policy/ext/policy_cache_key.rb
|
217
|
+
- lib/action_policy/ext/proc_case_eq.rb
|
182
218
|
- lib/action_policy/ext/string_constantize.rb
|
183
219
|
- lib/action_policy/ext/string_match.rb
|
184
220
|
- lib/action_policy/ext/string_underscore.rb
|
221
|
+
- lib/action_policy/ext/symbol_classify.rb
|
185
222
|
- lib/action_policy/ext/yield_self_then.rb
|
223
|
+
- lib/action_policy/i18n.rb
|
186
224
|
- lib/action_policy/lookup_chain.rb
|
187
225
|
- lib/action_policy/policy/aliases.rb
|
188
226
|
- lib/action_policy/policy/authorization.rb
|
@@ -193,14 +231,24 @@ files:
|
|
193
231
|
- lib/action_policy/policy/execution_result.rb
|
194
232
|
- lib/action_policy/policy/pre_check.rb
|
195
233
|
- lib/action_policy/policy/reasons.rb
|
234
|
+
- lib/action_policy/policy/scoping.rb
|
235
|
+
- lib/action_policy/rails/authorizer.rb
|
196
236
|
- lib/action_policy/rails/channel.rb
|
197
237
|
- lib/action_policy/rails/controller.rb
|
238
|
+
- lib/action_policy/rails/ext/active_record.rb
|
239
|
+
- lib/action_policy/rails/policy/instrumentation.rb
|
240
|
+
- lib/action_policy/rails/scope_matchers/action_controller_params.rb
|
241
|
+
- lib/action_policy/rails/scope_matchers/active_record.rb
|
198
242
|
- lib/action_policy/railtie.rb
|
199
243
|
- lib/action_policy/rspec.rb
|
200
244
|
- lib/action_policy/rspec/be_authorized_to.rb
|
245
|
+
- lib/action_policy/rspec/dsl.rb
|
246
|
+
- lib/action_policy/rspec/have_authorized_scope.rb
|
201
247
|
- lib/action_policy/rspec/pundit_syntax.rb
|
202
248
|
- lib/action_policy/test_helper.rb
|
203
249
|
- lib/action_policy/testing.rb
|
250
|
+
- lib/action_policy/utils/pretty_print.rb
|
251
|
+
- lib/action_policy/utils/suggest_message.rb
|
204
252
|
- lib/action_policy/version.rb
|
205
253
|
homepage: https://github.com/palkan/action_policy
|
206
254
|
licenses:
|
@@ -214,15 +262,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
214
262
|
requirements:
|
215
263
|
- - ">="
|
216
264
|
- !ruby/object:Gem::Version
|
217
|
-
version: 2.
|
265
|
+
version: 2.4.0
|
218
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
267
|
requirements:
|
220
|
-
- - "
|
268
|
+
- - ">"
|
221
269
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
270
|
+
version: 1.3.1
|
223
271
|
requirements: []
|
224
|
-
|
225
|
-
rubygems_version: 2.7.6
|
272
|
+
rubygems_version: 3.0.2
|
226
273
|
signing_key:
|
227
274
|
specification_version: 4
|
228
275
|
summary: Authorization framework for Ruby/Rails application
|