action_policy 0.4.4 → 0.5.0

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +203 -174
  3. data/README.md +5 -4
  4. data/lib/action_policy.rb +7 -1
  5. data/lib/action_policy/behaviour.rb +22 -16
  6. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  7. data/lib/action_policy/behaviours/scoping.rb +2 -1
  8. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  9. data/lib/action_policy/ext/module_namespace.rb +1 -6
  10. data/lib/action_policy/ext/policy_cache_key.rb +10 -30
  11. data/lib/action_policy/i18n.rb +1 -1
  12. data/lib/action_policy/lookup_chain.rb +29 -15
  13. data/lib/action_policy/policy/aliases.rb +7 -12
  14. data/lib/action_policy/policy/authorization.rb +8 -7
  15. data/lib/action_policy/policy/cache.rb +11 -17
  16. data/lib/action_policy/policy/core.rb +25 -12
  17. data/lib/action_policy/policy/defaults.rb +3 -9
  18. data/lib/action_policy/policy/execution_result.rb +3 -9
  19. data/lib/action_policy/policy/pre_check.rb +19 -58
  20. data/lib/action_policy/policy/reasons.rb +29 -19
  21. data/lib/action_policy/policy/scoping.rb +5 -6
  22. data/lib/action_policy/rails/controller.rb +6 -1
  23. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  24. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  25. data/lib/action_policy/rspec/dsl.rb +1 -1
  26. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  27. data/lib/action_policy/utils/pretty_print.rb +21 -24
  28. data/lib/action_policy/utils/suggest_message.rb +1 -3
  29. data/lib/action_policy/version.rb +1 -1
  30. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +0 -0
  31. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  32. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  33. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  34. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  35. metadata +29 -119
  36. data/.gitattributes +0 -2
  37. data/.github/ISSUE_TEMPLATE.md +0 -21
  38. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  39. data/.github/bug_report_template.rb +0 -175
  40. data/.gitignore +0 -15
  41. data/.rubocop.yml +0 -54
  42. data/.tidelift.yml +0 -6
  43. data/.travis.yml +0 -31
  44. data/Gemfile +0 -22
  45. data/Rakefile +0 -27
  46. data/action_policy.gemspec +0 -44
  47. data/benchmarks/namespaced_lookup_cache.rb +0 -74
  48. data/benchmarks/pre_checks.rb +0 -73
  49. data/bin/console +0 -14
  50. data/bin/setup +0 -8
  51. data/docs/.nojekyll +0 -0
  52. data/docs/CNAME +0 -1
  53. data/docs/README.md +0 -79
  54. data/docs/_sidebar.md +0 -27
  55. data/docs/aliases.md +0 -122
  56. data/docs/assets/docsify-search.js +0 -364
  57. data/docs/assets/docsify.min.js +0 -3
  58. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  59. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  60. data/docs/assets/images/banner.png +0 -0
  61. data/docs/assets/images/cache.png +0 -0
  62. data/docs/assets/images/cache.svg +0 -70
  63. data/docs/assets/images/layer.png +0 -0
  64. data/docs/assets/images/layer.svg +0 -35
  65. data/docs/assets/prism-ruby.min.js +0 -1
  66. data/docs/assets/styles.css +0 -347
  67. data/docs/assets/vue.min.css +0 -1
  68. data/docs/authorization_context.md +0 -92
  69. data/docs/behaviour.md +0 -113
  70. data/docs/caching.md +0 -291
  71. data/docs/controller_action_aliases.md +0 -109
  72. data/docs/custom_lookup_chain.md +0 -48
  73. data/docs/custom_policy.md +0 -53
  74. data/docs/debugging.md +0 -55
  75. data/docs/decorators.md +0 -27
  76. data/docs/favicon.ico +0 -0
  77. data/docs/graphql.md +0 -302
  78. data/docs/i18n.md +0 -44
  79. data/docs/index.html +0 -43
  80. data/docs/instrumentation.md +0 -84
  81. data/docs/lookup_chain.md +0 -22
  82. data/docs/namespaces.md +0 -77
  83. data/docs/non_rails.md +0 -28
  84. data/docs/pre_checks.md +0 -57
  85. data/docs/pundit_migration.md +0 -80
  86. data/docs/quick_start.md +0 -118
  87. data/docs/rails.md +0 -120
  88. data/docs/reasons.md +0 -120
  89. data/docs/scoping.md +0 -255
  90. data/docs/testing.md +0 -390
  91. data/docs/writing_policies.md +0 -107
  92. data/gemfiles/jruby.gemfile +0 -8
  93. data/gemfiles/rails42.gemfile +0 -9
  94. data/gemfiles/rails6.gemfile +0 -8
  95. data/gemfiles/railsmaster.gemfile +0 -6
  96. data/lib/action_policy/ext/string_match.rb +0 -14
  97. data/lib/action_policy/ext/yield_self_then.rb +0 -25
data/.gitignore DELETED
@@ -1,15 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- Gemfile.local
11
-
12
- spec/dummy/log/*
13
- spec/dummy/db/*.sqlite3
14
- spec/dummy/db/*.sqlite3-journal
15
- spec/dummy/tmp/
@@ -1,54 +0,0 @@
1
- require:
2
- - standard/cop/semantic_blocks
3
- - rubocop-md
4
-
5
- inherit_gem:
6
- standard: config/base.yml
7
-
8
- AllCops:
9
- Exclude:
10
- - 'bin/*'
11
- - 'tmp/**/*'
12
- - 'Gemfile'
13
- - 'vendor/**/*'
14
- - 'gemfiles/**/*'
15
- - 'lib/generators/**/templates/**/*'
16
- DisplayCopNames: true
17
- TargetRubyVersion: 2.4
18
-
19
- Standard/SemanticBlocks:
20
- Enabled: false
21
-
22
- Style/FrozenStringLiteralComment:
23
- Enabled: true
24
-
25
- Style/TrailingCommaInArrayLiteral:
26
- EnforcedStyleForMultiline: no_comma
27
-
28
- Style/TrailingCommaInHashLiteral:
29
- EnforcedStyleForMultiline: no_comma
30
-
31
- Layout/AlignParameters:
32
- EnforcedStyle: with_first_parameter
33
-
34
- Lint/Void:
35
- Exclude:
36
- - '**/*.md'
37
-
38
- # See https://github.com/rubocop-hq/rubocop/issues/4222
39
- Lint/AmbiguousBlockAssociation:
40
- Exclude:
41
- - 'spec/**/*'
42
- - '**/*.md'
43
-
44
- Lint/DuplicateMethods:
45
- Exclude:
46
- - '**/*.md'
47
-
48
- Naming/FileName:
49
- Exclude:
50
- - '**/*.md'
51
-
52
- Layout/InitialIndentation:
53
- Exclude:
54
- - 'CHANGELOG.md'
@@ -1,6 +0,0 @@
1
- ci:
2
- type:
3
- development:
4
- tests:
5
- unlicensed: skip
6
- outdated: skip
@@ -1,31 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- notifications:
5
- email: false
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
-
14
- matrix:
15
- fast_finish: true
16
- include:
17
- - rvm: ruby-head
18
- gemfile: gemfiles/railsmaster.gemfile
19
- - rvm: jruby-9.2.8.0
20
- gemfile: gemfiles/jruby.gemfile
21
- - rvm: 2.6.5
22
- gemfile: gemfiles/rails6.gemfile
23
- - rvm: 2.5.3
24
- gemfile: Gemfile
25
- - rvm: 2.5.3
26
- gemfile: gemfiles/rails42.gemfile
27
- allow_failures:
28
- - rvm: ruby-head
29
- gemfile: gemfiles/railsmaster.gemfile
30
- - rvm: jruby-9.2.8.0
31
- gemfile: gemfiles/jruby.gemfile
data/Gemfile DELETED
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gemspec
6
-
7
- gem "pry-byebug", platform: :mri
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
-
16
- local_gemfile = File.join(__dir__, "Gemfile.local")
17
-
18
- if File.exist?(local_gemfile)
19
- eval(File.read(local_gemfile)) # rubocop:disable Security/Eval
20
- else
21
- gem "rails", "~> 5.0"
22
- end
data/Rakefile DELETED
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rake/testtask"
4
- require "rubocop/rake_task"
5
- require "rspec/core/rake_task"
6
-
7
- RuboCop::RakeTask.new
8
-
9
- RSpec::Core::RakeTask.new(:spec)
10
-
11
- Rake::TestTask.new(:test) do |t|
12
- t.libs << "test"
13
- t.libs << "lib"
14
- t.test_files = FileList["test/**/*_test.rb"]
15
- end
16
-
17
- namespace :test do
18
- task :isolated do
19
- Dir.glob("test/**/*_test.rb").all? do |file|
20
- sh(Gem.ruby, "-w", "-Ilib:test", file)
21
- end || raise("Failures")
22
- end
23
-
24
- task ci: %w[rubocop test:isolated spec]
25
- end
26
-
27
- task default: [:rubocop, :test, :spec]
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path("lib", __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "action_policy/version"
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = "action_policy"
9
- spec.version = ActionPolicy::VERSION
10
- spec.authors = ["Vladimir Dementyev"]
11
- spec.email = ["dementiev.vm@gmail.com"]
12
-
13
- spec.summary = "Authorization framework for Ruby/Rails application"
14
- spec.description = "Authorization framework for Ruby/Rails application"
15
- spec.homepage = "https://github.com/palkan/action_policy"
16
- spec.license = "MIT"
17
-
18
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
- f.match(%r{^(test|spec|features)/})
20
- end
21
-
22
- spec.metadata = {
23
- "bug_tracker_uri" => "http://github.com/palkan/action_policy/issues",
24
- "changelog_uri" => "https://github.com/palkan/action_policy/blob/master/CHANGELOG.md",
25
- "documentation_uri" => "https://actionpolicy.evilmartians.io/",
26
- "homepage_uri" => "https://actionpolicy.evilmartians.io/",
27
- "source_code_uri" => "http://github.com/palkan/action_policy"
28
- }
29
-
30
- spec.require_paths = ["lib"]
31
-
32
- spec.required_ruby_version = ">= 2.4.0"
33
-
34
- spec.add_development_dependency "ammeter", "~> 1.1.3"
35
- spec.add_development_dependency "bundler", ">= 1.15"
36
- spec.add_development_dependency "minitest", "~> 5.0"
37
- spec.add_development_dependency "rake", "~> 10.0"
38
- spec.add_development_dependency "rspec", "~> 3.3"
39
- spec.add_development_dependency "rubocop", "~> 0.67.0"
40
- spec.add_development_dependency "rubocop-md", "~> 0.2"
41
- spec.add_development_dependency "standard", "~> 0.0.39"
42
- spec.add_development_dependency "benchmark-ips", "~> 2.7.0"
43
- spec.add_development_dependency "i18n"
44
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- #
4
- # This benchmark measures the efficiency of NamespaceCache.
5
- #
6
- # Run it multiple times with cache on/off to see the results:
7
- #
8
- # $ bundle exec ruby namespaced_lookup_cache.rb
9
- # $ bundle exec ruby namespaced_lookup_cache.rb
10
- # $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
11
- # $ NO_CACHE=1 bundle exec ruby namespaced_lookup_cache.rb
12
- #
13
-
14
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
15
-
16
- require "action_policy"
17
- require "benchmark/ips"
18
-
19
- GC.disable
20
-
21
- class A; end
22
-
23
- class B; end
24
-
25
- module X
26
- class BPolicy < ActionPolicy::Base; end
27
-
28
- module Y
29
- module Z
30
- class APolicy < ActionPolicy::Base; end
31
- end
32
- end
33
- end
34
-
35
- a = A.new
36
- b = B.new
37
-
38
- if ENV["NO_CACHE"]
39
- ActionPolicy::LookupChain.namespace_cache_enabled = false
40
- end
41
-
42
- results_path = File.join(__dir__, "../tmp/#{__FILE__.sub(/\.rb$/, ".txt")}")
43
- FileUtils.mkdir_p File.dirname(results_path)
44
-
45
- Benchmark.ips do |x|
46
- x.warmup = 0
47
-
48
- x.report("cache A") do
49
- ActionPolicy.lookup(a, namespace: X::Y::Z)
50
- end
51
-
52
- x.report("cache B") do
53
- ActionPolicy.lookup(b, namespace: X::Y::Z)
54
- end
55
-
56
- x.report("no cache A") do
57
- ActionPolicy.lookup(a, namespace: X::Y::Z)
58
- end
59
-
60
- x.report("no cache B") do
61
- ActionPolicy.lookup(b, namespace: X::Y::Z)
62
- end
63
-
64
- x.hold! results_path
65
-
66
- x.compare!
67
- end
68
-
69
- #
70
- # Comparison:
71
- # cache A: 204788.3 i/s
72
- # cache B: 202536.9 i/s - same-ish: difference falls within error
73
- # no cache A: 127772.8 i/s - same-ish: difference falls within error
74
- # no cache B: 63487.2 i/s - 3.23x slower
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # This benchmark measures the difference between catch/throw and jump-less implementation.
4
-
5
- $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
6
-
7
- require "action_policy"
8
- require "benchmark/ips"
9
-
10
- GC.disable
11
-
12
- class A; end
13
-
14
- class B; end
15
-
16
- class APolicy < ActionPolicy::Base
17
- authorize :user, optional: true
18
-
19
- pre_check :do_nothing, :deny_all
20
-
21
- def show?
22
- true
23
- end
24
-
25
- def do_nothing
26
- end
27
-
28
- def deny_all
29
- deny!
30
- end
31
- end
32
-
33
- class BPolicy < APolicy
34
- def run_pre_checks(rule)
35
- catch :policy_fulfilled do
36
- self.class.pre_checks.each do |check|
37
- next unless check.applicable?(rule)
38
- check.call(self)
39
- end
40
-
41
- return yield if block_given?
42
- end
43
-
44
- result.value
45
- end
46
-
47
- def deny!
48
- result.load false
49
- throw :policy_fulfilled
50
- end
51
- end
52
-
53
- a = A.new
54
-
55
- (APolicy.new(record: a).apply(:show?) == BPolicy.new(record: a).apply(:show?)) || raise("Implementations are not equal")
56
-
57
- Benchmark.ips do |x|
58
- x.warmup = 0
59
-
60
- x.report("loop pre-check") do
61
- APolicy.new(record: a).apply(:show?)
62
- end
63
-
64
- x.report("catch/throw pre-check") do
65
- BPolicy.new(record: a).apply(:show?)
66
- end
67
-
68
- x.compare!
69
- end
70
-
71
- # Comparison:
72
- # catch/throw pre-check: 286094.2 i/s
73
- # loop pre-check: 184786.1 i/s - 1.55x slower
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "action_policy"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
File without changes
data/docs/CNAME DELETED
@@ -1 +0,0 @@
1
- actionpolicy.evilmartians.io
@@ -1,79 +0,0 @@
1
- [![Gem Version](https://badge.fury.io/rb/action_policy.svg)](https://badge.fury.io/rb/action_policy)
2
- [![Build Status](https://travis-ci.org/palkan/action_policy.svg?branch=master)](https://travis-ci.org/palkan/action_policy)
3
-
4
- ## Action Policy
5
-
6
- > Authorization framework for Ruby and Rails.
7
- <br>Composable. Extensible. Performant.
8
-
9
- **NOTE:** this documentation is for the version "0.3.0+".
10
-
11
- ## What is it?
12
-
13
- _Authorization_ is an act of giving **someone** official
14
- permission to **do something** (to not be confused with [_authentication_](https://en.wikipedia.org/wiki/Authentication)).
15
-
16
- Action Policy provides flexible tools to build an _authorization layer_ for your application.
17
-
18
- <div class="chart-container">
19
- <img src="assets/images/layer.svg" alt="Authorization layer" width="80%">
20
- </div>
21
-
22
- **NOTE:** Action Policy does not force you to use a specific authorization model (i.e., roles, permissions, etc.) and does not provide one. It only answers a single question: **How to verify access?**
23
-
24
- ## Where to go from here?
25
- - [Quick start](./quick_start.md)
26
- - [Using with Rails](./rails.md)
27
- - [Using with other Ruby frameworks](./non_rails.md)
28
- - [Using with GraphQL Ruby](./graphql.md)
29
-
30
- ## Project State
31
-
32
- The project is being used in production since mid 2018. Major features have been implemented, API has been stabilized. Check out our [development board](https://github.com/palkan/action_policy/projects/1) to see what's coming next.
33
-
34
- ## History
35
-
36
- Action Policy gem is an _extraction_-kind of a library. Most of the code has been used in production for several years in different [Evil Martians][] projects.
37
-
38
- We have decided to collect all our authorization techniques and pack them into a standalone gem–and that is how Action Policy was born!
39
-
40
- ## What about the existing solutions?
41
-
42
- Why did we decide to build our own authorization gem instead of using the existing solutions, such as [Pundit][] and [CanCanCan][]?
43
-
44
- **TL;DR they didn't solve all of our problems.**
45
-
46
- [Pundit][] has been our framework of choice for a long time. Being too _dead-simple_, it required a lot of hacking to fulfill business logic requirements.
47
-
48
- These _hacks_ later became Action Policy (initially, we even called it "Pundit, re-visited").
49
-
50
- We also took a few ideas from [CanCanCan][]—such as [default rules and rule aliases](./aliases.md).
51
-
52
- It is also worth noting that Action Policy (despite having a _Railsy_ name) is designed to be **Rails-free**. On the other hand, it contains some Rails-specific extensions and seamlessly integrates into the framework.
53
-
54
- So, what are the main reasons to consider Action Policy as your authorization tool?
55
-
56
- - **Performance**: multiple [caching strategies](./caching.md) out-of-the-box make authorization overhead as small as possible–especially useful when your rules involve DB queries; you can also monitor the performance and detect the bottlenecks using the built-in [instrumentation](./instrumentation) features.
57
-
58
- - **Composition & Customization**: use [only the features you need](./custom_policy.md) or easily extend the functionality–it's just Ruby classes and modules, (almost) zero magic! And you can add authorization [anywhere in your code](./non_rails.md), not only in controllers.
59
-
60
- - **Code Organization**: use [namespaces](./namespaces.md) to organize your policies (for example, when you have multiple authorization strategies); add [pre-checks](./pre_checks.md) to make rules more readable and better express your business-logic.
61
-
62
- - **...and more**: [testability](./testing.md), [i18n](./i18n.md) integrations, [actionable errors](./reasons.md).
63
-
64
- Learn more about the motivation behind the Action Policy and its features by watching this [RailsConf talk](https://www.youtube.com/watch?v=NVwx0DARDis).
65
-
66
- ## Resources
67
-
68
- - RubyRussia, 2019 "Welcome, or access denied?" talk ([video](https://www.youtube.com/watch?v=y15a2g7v8i0) [RU], [slides](https://speakerdeck.com/palkan/rubyrussia-2019-welcome-or-access-denied))
69
-
70
- - Seattle.rb, 2019 "A Denial!" talk [[slides](https://speakerdeck.com/palkan/seattle-dot-rb-2019-a-denial)]
71
-
72
- - RailsConf, 2018 "Access Denied" talk [[video](https://www.youtube.com/watch?v=NVwx0DARDis), [slides](https://speakerdeck.com/palkan/railsconf-2018-access-denied-the-missing-guide-to-authorization-in-rails)]
73
-
74
- <a href="https://evilmartians.com/?utm_source=action_policy">
75
- <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
76
-
77
- [CanCanCan]: https://github.com/CanCanCommunity/cancancan
78
- [Pundit]: https://github.com/varvet/pundit
79
- [Evil Martians]: https://evilmartians.com