action_policy 0.2.4 → 0.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|