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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +26 -64
  3. data/.travis.yml +13 -10
  4. data/CHANGELOG.md +216 -1
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +1 -1
  7. data/Rakefile +10 -0
  8. data/action_policy.gemspec +5 -3
  9. data/benchmarks/namespaced_lookup_cache.rb +18 -22
  10. data/docs/README.md +3 -3
  11. data/docs/_sidebar.md +4 -0
  12. data/docs/aliases.md +9 -5
  13. data/docs/authorization_context.md +59 -1
  14. data/docs/behaviour.md +113 -0
  15. data/docs/caching.md +6 -4
  16. data/docs/custom_policy.md +1 -2
  17. data/docs/debugging.md +55 -0
  18. data/docs/decorators.md +27 -0
  19. data/docs/i18n.md +41 -2
  20. data/docs/instrumentation.md +70 -2
  21. data/docs/lookup_chain.md +5 -4
  22. data/docs/namespaces.md +1 -1
  23. data/docs/non_rails.md +2 -3
  24. data/docs/pundit_migration.md +77 -2
  25. data/docs/quick_start.md +5 -5
  26. data/docs/rails.md +5 -2
  27. data/docs/reasons.md +50 -3
  28. data/docs/scoping.md +262 -0
  29. data/docs/testing.md +232 -21
  30. data/docs/writing_policies.md +1 -1
  31. data/gemfiles/jruby.gemfile +3 -0
  32. data/gemfiles/rails42.gemfile +3 -0
  33. data/gemfiles/rails6.gemfile +8 -0
  34. data/gemfiles/railsmaster.gemfile +1 -1
  35. data/lib/action_policy.rb +3 -3
  36. data/lib/action_policy/authorizer.rb +12 -4
  37. data/lib/action_policy/base.rb +2 -0
  38. data/lib/action_policy/behaviour.rb +14 -3
  39. data/lib/action_policy/behaviours/memoized.rb +1 -1
  40. data/lib/action_policy/behaviours/policy_for.rb +12 -3
  41. data/lib/action_policy/behaviours/scoping.rb +32 -0
  42. data/lib/action_policy/behaviours/thread_memoized.rb +1 -1
  43. data/lib/action_policy/ext/hash_transform_keys.rb +19 -0
  44. data/lib/action_policy/ext/module_namespace.rb +1 -1
  45. data/lib/action_policy/ext/policy_cache_key.rb +2 -1
  46. data/lib/action_policy/ext/proc_case_eq.rb +14 -0
  47. data/lib/action_policy/ext/string_constantize.rb +1 -0
  48. data/lib/action_policy/ext/symbol_classify.rb +22 -0
  49. data/lib/action_policy/i18n.rb +56 -0
  50. data/lib/action_policy/lookup_chain.rb +21 -3
  51. data/lib/action_policy/policy/cache.rb +10 -6
  52. data/lib/action_policy/policy/core.rb +31 -19
  53. data/lib/action_policy/policy/execution_result.rb +12 -0
  54. data/lib/action_policy/policy/pre_check.rb +2 -6
  55. data/lib/action_policy/policy/reasons.rb +99 -12
  56. data/lib/action_policy/policy/scoping.rb +165 -0
  57. data/lib/action_policy/rails/authorizer.rb +20 -0
  58. data/lib/action_policy/rails/controller.rb +4 -14
  59. data/lib/action_policy/rails/ext/active_record.rb +10 -0
  60. data/lib/action_policy/rails/policy/instrumentation.rb +24 -0
  61. data/lib/action_policy/rails/scope_matchers/action_controller_params.rb +19 -0
  62. data/lib/action_policy/rails/scope_matchers/active_record.rb +29 -0
  63. data/lib/action_policy/railtie.rb +29 -7
  64. data/lib/action_policy/rspec.rb +1 -0
  65. data/lib/action_policy/rspec/be_authorized_to.rb +1 -1
  66. data/lib/action_policy/rspec/dsl.rb +103 -0
  67. data/lib/action_policy/rspec/have_authorized_scope.rb +126 -0
  68. data/lib/action_policy/rspec/pundit_syntax.rb +1 -1
  69. data/lib/action_policy/test_helper.rb +69 -4
  70. data/lib/action_policy/testing.rb +54 -0
  71. data/lib/action_policy/utils/pretty_print.rb +137 -0
  72. data/lib/action_policy/utils/suggest_message.rb +21 -0
  73. data/lib/action_policy/version.rb +1 -1
  74. 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionPolicy
4
- VERSION = "0.2.4"
4
+ VERSION = "0.3.0.beta1"
5
5
  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.2.4
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: 2018-09-06 00:00:00.000000000 Z
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.56.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.56.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.3.0
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: '0'
270
+ version: 1.3.1
223
271
  requirements: []
224
- rubyforge_project:
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