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.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 01a2dfdc255ba25c36b8054db9d56372ffb2a47137dd412568a6d3c9f2f2c81a
4
- data.tar.gz: 27f0e25159c6a5d5bca89a238e8342dcdc852d24a2f9129365de455ccb859690
3
+ metadata.gz: 3f655148e9920ff5afe7b4fdcd52fb15475dc72e84e46545820769b2d68b9f48
4
+ data.tar.gz: a4fd3da807acc43638357557f8df80745b14033d7f7183a5f7e5468f4df7fc51
5
5
  SHA512:
6
- metadata.gz: 682229b71f8de1c7828028d7731cc7fead59c86ad82285ae6989d1df476a402bc8f5f6984799e7b9dacb83e867bb5ad0688bc91d943045b116c005b35ccf0b47
7
- data.tar.gz: 2fe558adf815be0c32a9b683470ff95f75b3f3fe431aa959c3c60420f8faa2e28de3d390b20b19eb007cc86c999540d86b3491fdf94861a2a41cb98dc251bca5
6
+ metadata.gz: eadd511dc2a0d912ded1b35af655896210fbade0e18923d0e42261e7ee3db4bc68773b3c9acc90c9cb0a3e54e4dcec601800aedf5c52d2d7a3615edc18b26c44
7
+ data.tar.gz: c9e1ef737e1f3b764ec5d0a94585a5f80ed7c5f180c42e3ff7053ada2c1578d9b2bf6d8572199dca57dac330918b98e7aea4be032a9b2d6febf0ff96441c2eda
@@ -1,32 +1,44 @@
1
1
  require:
2
+ - standard/cop/semantic_blocks
2
3
  - rubocop-md
3
4
 
5
+ inherit_gem:
6
+ standard: config/base.yml
7
+
4
8
  AllCops:
5
- Include:
6
- - 'lib/**/*.rb'
7
- - 'lib/**/*.rake'
8
- - 'test/**/*.rb'
9
9
  Exclude:
10
- - 'bin/**/*'
11
- - 'gemfiles/**/*'
12
- - 'vendor/**/*'
10
+ - 'bin/*'
13
11
  - 'tmp/**/*'
12
+ - 'Gemfile'
13
+ - 'vendor/**/*'
14
+ - 'gemfiles/**/*'
14
15
  DisplayCopNames: true
15
- StyleGuideCopsOnly: false
16
- TargetRubyVersion: 2.3
16
+ TargetRubyVersion: 2.4s
17
17
 
18
- Rails:
18
+ Standard/SemanticBlocks:
19
19
  Enabled: false
20
20
 
21
- Bundler/OrderedGems:
22
- Enabled: false
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: true
23
+
24
+ Style/TrailingCommaInArrayLiteral:
25
+ EnforcedStyleForMultiline: no_comma
26
+
27
+ Style/TrailingCommaInHashLiteral:
28
+ EnforcedStyleForMultiline: no_comma
29
+
30
+ Layout/AlignParameters:
31
+ EnforcedStyle: with_first_parameter
23
32
 
24
33
  Lint/Void:
25
34
  Exclude:
26
35
  - '**/*.md'
27
36
 
28
- Lint/SplatKeywordArguments:
29
- Enabled: false
37
+ # See https://github.com/rubocop-hq/rubocop/issues/4222
38
+ Lint/AmbiguousBlockAssociation:
39
+ Exclude:
40
+ - 'spec/**/*'
41
+ - '**/*.md'
30
42
 
31
43
  Lint/DuplicateMethods:
32
44
  Exclude:
@@ -34,58 +46,8 @@ Lint/DuplicateMethods:
34
46
 
35
47
  Naming/FileName:
36
48
  Exclude:
37
- - 'Rakefile'
38
- - 'Gemfile'
39
49
  - '**/*.md'
40
50
 
41
51
  Layout/InitialIndentation:
42
52
  Exclude:
43
53
  - 'CHANGELOG.md'
44
-
45
- Naming/UncommunicativeMethodParamName:
46
- Enabled: false
47
-
48
- Naming/VariableNumber:
49
- Exclude:
50
- - 'test/**/*.rb'
51
-
52
- Style/SymbolArray:
53
- Enabled: false
54
-
55
- Style/Documentation:
56
- Exclude:
57
- - 'test/**/*.rb'
58
- - '**/*.md'
59
-
60
- Style/StringLiterals:
61
- EnforcedStyle: double_quotes
62
-
63
- Style/RegexpLiteral:
64
- Enabled: false
65
-
66
- Style/NumericPredicate:
67
- Enabled: false
68
-
69
- Style/Lambda:
70
- Enabled: false
71
-
72
- Layout/SpaceInsideStringInterpolation:
73
- EnforcedStyle: no_space
74
-
75
- Lint/AmbiguousRegexpLiteral:
76
- Enabled: false
77
-
78
- Metrics/LineLength:
79
- Max: 100
80
-
81
- Metrics/AbcSize:
82
- Exclude:
83
- - 'test/**/*.rb'
84
-
85
- Metrics/BlockLength:
86
- Exclude:
87
- - 'spec/**/*.rb'
88
-
89
- Metrics/MethodLength:
90
- Exclude:
91
- - 'test/**/*.rb'
@@ -1,28 +1,31 @@
1
1
  sudo: false
2
2
  language: ruby
3
- rvm:
4
- - 2.5.0
5
-
3
+ cache: bundler
6
4
  notifications:
7
5
  email: false
8
6
 
7
+ before_install:
8
+ - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
9
+ - gem install bundler -v '< 2'
10
+
11
+ script:
12
+ - bundle exec rake test:ci
13
+
9
14
  matrix:
10
15
  fast_finish: true
11
16
  include:
12
17
  - rvm: ruby-head
13
18
  gemfile: gemfiles/railsmaster.gemfile
14
- - rvm: jruby-9.2.0.0
19
+ - rvm: jruby-9.2.5.0
15
20
  gemfile: gemfiles/jruby.gemfile
16
- - rvm: 2.5.1
21
+ - rvm: 2.6.0
22
+ gemfile: gemfiles/rails6.gemfile
23
+ - rvm: 2.5.3
17
24
  gemfile: Gemfile
18
- - rvm: 2.5.1
19
- gemfile: gemfiles/rails42.gemfile
20
25
  - rvm: 2.4.3
21
- gemfile: Gemfile
22
- - rvm: 2.3.1
23
26
  gemfile: gemfiles/rails42.gemfile
24
27
  allow_failures:
25
28
  - rvm: ruby-head
26
29
  gemfile: gemfiles/railsmaster.gemfile
27
- - rvm: jruby-9.2.0.0
30
+ - rvm: jruby-9.2.5.0
28
31
  gemfile: gemfiles/jruby.gemfile
@@ -1,5 +1,218 @@
1
1
  ## master
2
2
 
3
+ ## 0.3.0.beta1 (2019-03-30)
4
+
5
+ - Added ActiveSupport-based instrumentation. ([@palkan][])
6
+
7
+ See [PR#4](https://github.com/palkan/action_policy/pull/4)
8
+
9
+ - Allow passing authorization context explicitly. ([@palkan][])
10
+
11
+ Closes [#3](https://github.com/palkan/action_policy/issues/3).
12
+
13
+ Now it's possible to override implicit authorization context
14
+ via `context` option:
15
+
16
+ ```ruby
17
+ authorize! target, to: :show?, context: {user: another_user}
18
+ authorized_scope User.all, context: {user: another_user}
19
+ ```
20
+
21
+ - Renamed `#authorized` to `#authorized_scope`. ([@palkan][])
22
+
23
+ **NOTE:** `#authorized` alias is also available.
24
+
25
+ - Added `Policy#pp(rule)` method to print annotated rule source code. ([@palkan][])
26
+
27
+ Example (debugging):
28
+
29
+ ```ruby
30
+ def edit?
31
+ binding.pry # rubocop:disable Lint/Debugger
32
+ (user.name == "John") && (admin? || access_feed?)
33
+ end
34
+ ```
35
+
36
+ ```sh
37
+ pry> pp :edit?
38
+ MyPolicy#edit?
39
+ ↳ (
40
+ user.name == "John" #=> false
41
+ )
42
+ AND
43
+ (
44
+ admin? #=> false
45
+ OR
46
+ access_feed? #=> true
47
+ )
48
+ )
49
+ ```
50
+
51
+ See [PR#63](https://github.com/palkan/action_policy/pull/63)
52
+
53
+ - Added ability to provide additional failure reasons details. ([@palkan][])
54
+
55
+ Example:
56
+
57
+ ```ruby
58
+ class ApplicantPolicy < ApplicationPolicy
59
+ def show?
60
+ allowed_to?(:show?, object.stage)
61
+ end
62
+ end
63
+
64
+ class StagePolicy < ApplicationPolicy
65
+ def show?
66
+ # Add stage title to the failure reason (if any)
67
+ # (could be used by client to show more descriptive message)
68
+ details[:title] = record.title
69
+ # then perform the checks
70
+ user.stages.where(id: record.id).exists?
71
+ end
72
+ end
73
+
74
+ # when accessing the reasons
75
+ p ex.result.reasons.details #=> { stage: [{show?: {title: "Onboarding"}] }
76
+ ```
77
+
78
+ See https://github.com/palkan/action_policy/pull/58
79
+
80
+ - Ruby 2.4+ is required. ([@palkan][])
81
+
82
+ - Added RSpec DSL for writing policy specs. ([@palkan])
83
+
84
+ The goal of this DSL is to reduce the boilerplate when writing
85
+ policies specs.
86
+
87
+ Example:
88
+
89
+ ```ruby
90
+ describe PostPolicy do
91
+ let(:user) { build_stubbed :user }
92
+ let(:record) { build_stubbed :post, draft: false }
93
+
94
+ let(:context) { {user: user} }
95
+
96
+ describe_rule :show? do
97
+ succeed "when post is published"
98
+
99
+ failed "when post is draft" do
100
+ before { post.draft = false }
101
+
102
+ succeed "when user is a manager" do
103
+ before { user.role = "manager" }
104
+ end
105
+ end
106
+ end
107
+ end
108
+ ```
109
+
110
+ - Added I18n support ([@DmitryTsepelev][])
111
+
112
+ Example:
113
+
114
+ ```ruby
115
+ class ApplicationController < ActionController::Base
116
+ rescue_from ActionPolicy::Unauthorized do |ex|
117
+ p ex.result.message #=> "You do not have access to the stage"
118
+ p ex.result.reasons.full_messages #=> ["You do not have access to the stage"]
119
+ end
120
+ end
121
+ ```
122
+
123
+ - Added scope options to scopes. ([@korolvs][])
124
+
125
+ See [#47](https://github.com/palkan/action_policy/pull/47).
126
+
127
+ Example:
128
+ ```ruby
129
+ # users_controller.rb
130
+ class UsersController < ApplicationController
131
+ def index
132
+ @user = authorized(User.all, scope_options: {with_deleted: true})
133
+ end
134
+ end
135
+
136
+ # user_policy.rb
137
+ describe UserPolicy < Application do
138
+ relation_scope do |relation, with_deleted: false|
139
+ rel = some_logic(relation)
140
+ with_deleted ? rel.with_deleted : rel
141
+ end
142
+ end
143
+ ```
144
+
145
+ - Added Symbol lookup to the lookup chain ([@DmitryTsepelev][])
146
+
147
+ For instance, lookup will implicitly use `AdminPolicy` in a following case:
148
+
149
+ ```ruby
150
+ # admin_controller.rb
151
+ class AdminController < ApplicationController
152
+ authorize! :admin, to: :update_settings
153
+ end
154
+ ```
155
+
156
+ - Added testing for scopes. ([@palkan][])
157
+
158
+ Example:
159
+
160
+ ```ruby
161
+ # users_controller.rb
162
+ class UsersController < ApplicationController
163
+ def index
164
+ @user = authorized(User.all)
165
+ end
166
+ end
167
+
168
+ # users_controller_spec.rb
169
+ describe UsersController do
170
+ subject { get :index }
171
+ it "has authorized scope" do
172
+ expect { subject }.to have_authorized_scope(:active_record_relation)
173
+ .with(PostPolicy)
174
+ end
175
+ end
176
+ ```
177
+
178
+ - Added scoping support. ([@palkan][])
179
+
180
+ See [#5](https://github.com/palkan/action_policy/issues/5).
181
+
182
+ By "scoping" we mean an ability to use policies to _scope data_.
183
+
184
+ For example, when you want to _scope_ Active Record collections depending
185
+ on the current user permissions:
186
+
187
+ ```ruby
188
+ class PostsController < ApplicationController
189
+ def index
190
+ @posts = authorized(Post.all)
191
+ end
192
+ end
193
+
194
+ class PostPolicy < ApplicationPolicy
195
+ relation_scope do |relation|
196
+ next relation if user.admin?
197
+ relation.where(user: user)
198
+ end
199
+ end
200
+ ```
201
+
202
+ Action Policy provides a flexible mechanism to apply scopes to anything you want.
203
+
204
+ Read more in [docs](https://actionpolicy.evilmartians.io/).
205
+
206
+ - Added `#implicit_authorization_target`. ([@palkan][]).
207
+
208
+ See [#35](https://github.com/palkan/action_policy/issues/35).
209
+
210
+ Implicit authorization target (defined by `implicit_authorization_target`) is used when no target specified for `authorize!` call.
211
+
212
+ For example, for Rails controllers integration it's just `controller_name.classify.safe_constantize`.
213
+
214
+ - Consider `record#policy_name` when looking up for a policy class. ([@palkan][])
215
+
3
216
  ## 0.2.4 (2018-09-06)
4
217
 
5
218
  - [Fix [#39](https://github.com/palkan/action_policy/issues/39)] Fix configuring cache store in Rails. ([@palkan][])
@@ -24,7 +237,7 @@
24
237
 
25
238
  It could be disabled by setting `ActionPolicy::LookupChain.namespace_cache_enabled = false`. It's enabled by default unless `RACK_ENV` env var is specified and is not equal to `"production"` (e.g. when `RACK_ENV=test` the cache is disabled).
26
239
 
27
- When using Rails it's enabled only in production mode but could be configured through setting the `config.config.action_policy.namespace_cache_enabled` parameter.
240
+ When using Rails it's enabled only in production mode but could be configured through setting the `config.action_policy.namespace_cache_enabled` parameter.
28
241
 
29
242
  - [Fix [#18](https://github.com/palkan/action_policy/issues/18)] Clarify documentation around, and fix the way `resolve_rule` resolves rules and rule aliases when subclasses are involved. ([@brendon][])
30
243
 
@@ -86,3 +299,5 @@
86
299
  [@palkan]: https://github.com/palkan
87
300
  [@ilyasgaraev]: https://github.com/ilyasgaraev
88
301
  [@brendon]: https://github.com/brendon
302
+ [@DmitryTsepelev]: https://github.com/DmitryTsepelev
303
+ [@korolvs]: https://github.com/korolvs
data/Gemfile CHANGED
@@ -6,6 +6,13 @@ gemspec
6
6
 
7
7
  gem "pry-byebug", platform: :mri
8
8
 
9
+ gem "method_source"
10
+ gem "unparser"
11
+
12
+ gem 'sqlite3', "~> 1.3.0", platform: :mri
13
+ gem 'activerecord-jdbcsqlite3-adapter', '~> 50.0', platform: :jruby
14
+ gem 'jdbc-sqlite3', platform: :jruby
15
+
9
16
  local_gemfile = File.join(__dir__, "Gemfile.local")
10
17
 
11
18
  if File.exist?(local_gemfile)
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Vladimir Dementyev
3
+ Copyright (c) 2018-2019 Vladimir Dementyev
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -15,4 +15,14 @@ Rake::TestTask.new(:test) do |t|
15
15
  t.test_files = FileList["test/**/*_test.rb"]
16
16
  end
17
17
 
18
+ namespace :test do
19
+ task :isolated do
20
+ Dir.glob("test/**/*_test.rb").all? do |file|
21
+ sh(Gem.ruby, "-w", "-Ilib:test", file)
22
+ end || raise("Failures")
23
+ end
24
+
25
+ task ci: %w[rubocop test:isolated spec]
26
+ end
27
+
18
28
  task default: [:rubocop, :test, :spec]
@@ -21,13 +21,15 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.required_ruby_version = ">= 2.3.0"
24
+ spec.required_ruby_version = ">= 2.4.0"
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.15"
26
+ spec.add_development_dependency "bundler", ">= 1.15"
27
27
  spec.add_development_dependency "minitest", "~> 5.0"
28
28
  spec.add_development_dependency "rake", "~> 10.0"
29
29
  spec.add_development_dependency "rspec", "~> 3.3"
30
- spec.add_development_dependency "rubocop", "~> 0.56.0"
30
+ spec.add_development_dependency "rubocop", "~> 0.65.0"
31
31
  spec.add_development_dependency "rubocop-md", "~> 0.2"
32
+ spec.add_development_dependency "standard", "~> 0.0.36"
32
33
  spec.add_development_dependency "benchmark-ips", "~> 2.7.0"
34
+ spec.add_development_dependency "i18n"
33
35
  end