action_policy 0.0.1 → 0.1.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 (73) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +85 -0
  3. data/.travis.yml +25 -2
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +12 -3
  6. data/README.md +71 -12
  7. data/Rakefile +9 -1
  8. data/action_policy.gemspec +11 -5
  9. data/docs/.nojekyll +0 -0
  10. data/docs/CNAME +1 -0
  11. data/docs/README.md +46 -0
  12. data/docs/_sidebar.md +19 -0
  13. data/docs/aliases.md +54 -0
  14. data/docs/assets/docsify.min.js +1 -0
  15. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  16. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  17. data/docs/assets/images/cache.png +0 -0
  18. data/docs/assets/images/cache.svg +70 -0
  19. data/docs/assets/images/layer.png +0 -0
  20. data/docs/assets/images/layer.svg +92 -0
  21. data/docs/assets/prism-ruby.min.js +1 -0
  22. data/docs/assets/styles.css +317 -0
  23. data/docs/assets/vue.min.css +1 -0
  24. data/docs/authorization_context.md +33 -0
  25. data/docs/caching.md +262 -0
  26. data/docs/custom_lookup_chain.md +48 -0
  27. data/docs/custom_policy.md +51 -0
  28. data/docs/favicon.ico +0 -0
  29. data/docs/i18n.md +3 -0
  30. data/docs/index.html +25 -0
  31. data/docs/instrumentation.md +3 -0
  32. data/docs/lookup_chain.md +16 -0
  33. data/docs/namespaces.md +69 -0
  34. data/docs/non_rails.md +29 -0
  35. data/docs/pre_checks.md +57 -0
  36. data/docs/quick_start.md +102 -0
  37. data/docs/rails.md +110 -0
  38. data/docs/reasons.md +67 -0
  39. data/docs/testing.md +116 -0
  40. data/docs/writing_policies.md +55 -0
  41. data/gemfiles/jruby.gemfile +5 -0
  42. data/gemfiles/rails42.gemfile +5 -0
  43. data/gemfiles/railsmaster.gemfile +6 -0
  44. data/lib/action_policy.rb +34 -2
  45. data/lib/action_policy/authorizer.rb +28 -0
  46. data/lib/action_policy/base.rb +24 -0
  47. data/lib/action_policy/behaviour.rb +94 -0
  48. data/lib/action_policy/behaviours/memoized.rb +56 -0
  49. data/lib/action_policy/behaviours/namespaced.rb +80 -0
  50. data/lib/action_policy/behaviours/policy_for.rb +23 -0
  51. data/lib/action_policy/behaviours/thread_memoized.rb +54 -0
  52. data/lib/action_policy/ext/module_namespace.rb +21 -0
  53. data/lib/action_policy/ext/policy_cache_key.rb +67 -0
  54. data/lib/action_policy/ext/string_constantize.rb +23 -0
  55. data/lib/action_policy/lookup_chain.rb +84 -0
  56. data/lib/action_policy/policy/aliases.rb +69 -0
  57. data/lib/action_policy/policy/authorization.rb +91 -0
  58. data/lib/action_policy/policy/cache.rb +74 -0
  59. data/lib/action_policy/policy/cached_apply.rb +28 -0
  60. data/lib/action_policy/policy/core.rb +64 -0
  61. data/lib/action_policy/policy/defaults.rb +37 -0
  62. data/lib/action_policy/policy/pre_check.rb +210 -0
  63. data/lib/action_policy/policy/reasons.rb +109 -0
  64. data/lib/action_policy/rails/channel.rb +15 -0
  65. data/lib/action_policy/rails/controller.rb +90 -0
  66. data/lib/action_policy/railtie.rb +74 -0
  67. data/lib/action_policy/rspec.rb +3 -0
  68. data/lib/action_policy/rspec/be_authorized_to.rb +93 -0
  69. data/lib/action_policy/rspec/pundit_syntax.rb +48 -0
  70. data/lib/action_policy/test_helper.rb +46 -0
  71. data/lib/action_policy/testing.rb +64 -0
  72. data/lib/action_policy/version.rb +3 -1
  73. metadata +115 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 17194a757a9567d4499a4477fa4cc35257283ebb
4
- data.tar.gz: ce45f2edd96504a8e438cec24fd30df415f1f8e0
2
+ SHA256:
3
+ metadata.gz: a039b5a6e9cdfa9821dfc8ac42d0e78f52239379d532e75e64e9a136123971f1
4
+ data.tar.gz: b756862f053a8ab07cc957813231988a8447dce8d2655a8d1f0171dda47ab957
5
5
  SHA512:
6
- metadata.gz: 58086abbb1a2ec4d91350bd478a7e12049ed89196439cf7c6019a0740ea4c63eb7621f6d41f770704eb8e641f28e3741c0a9b298cef474367d1445d0b34bc4bf
7
- data.tar.gz: 3a46de4ef1f3b50e2ea77306e6a289e5542aa8d6f7cd10f7a7a52660f9878f6131326addb9d40d0a622407741972526392c93ceaca43e1a7372a90d8840ead04
6
+ metadata.gz: f6855bd1057d2153300d0cc2d92fc7091bab8a49c6ac4d67fd805372e0fdb0caabb6dd38fc30f28f4bcc74d83b8e37d3dfc96e0410e7616f8e7ada2949c69d6e
7
+ data.tar.gz: b5ee9d56785412b634f405954b7bd456536d1564d31d8c0927926d045068ef67125e42b0674cdda574f6fefc366f30c94fb798e4f641235e95a31aa2c4f9a65c
@@ -0,0 +1,85 @@
1
+ require:
2
+ - rubocop-md
3
+
4
+ AllCops:
5
+ Include:
6
+ - 'lib/**/*.rb'
7
+ - 'lib/**/*.rake'
8
+ - 'test/**/*.rb'
9
+ Exclude:
10
+ - 'bin/**/*'
11
+ - 'gemfiles/**/*'
12
+ - 'test/dummy/**/*'
13
+ - 'vendor/**/*'
14
+ - 'tmp/**/*'
15
+ DisplayCopNames: true
16
+ StyleGuideCopsOnly: false
17
+ TargetRubyVersion: 2.3
18
+
19
+ Rails:
20
+ Enabled: false
21
+
22
+ Bundler/OrderedGems:
23
+ Enabled: false
24
+
25
+ Lint/Void:
26
+ Exclude:
27
+ - '**/*.md'
28
+
29
+ Lint/DuplicateMethods:
30
+ Exclude:
31
+ - '**/*.md'
32
+
33
+ Naming/FileName:
34
+ Exclude:
35
+ - 'Rakefile'
36
+ - 'Gemfile'
37
+ - '**/*.md'
38
+
39
+ Naming/UncommunicativeMethodParamName:
40
+ Enabled: false
41
+
42
+ Naming/VariableNumber:
43
+ Exclude:
44
+ - 'test/**/*.rb'
45
+
46
+ Style/SymbolArray:
47
+ Enabled: false
48
+
49
+ Style/Documentation:
50
+ Exclude:
51
+ - 'test/**/*.rb'
52
+ - '**/*.md'
53
+
54
+ Style/StringLiterals:
55
+ EnforcedStyle: double_quotes
56
+
57
+ Style/RegexpLiteral:
58
+ Enabled: false
59
+
60
+ Style/NumericPredicate:
61
+ Enabled: false
62
+
63
+ Style/Lambda:
64
+ Enabled: false
65
+
66
+ Layout/SpaceInsideStringInterpolation:
67
+ EnforcedStyle: no_space
68
+
69
+ Lint/AmbiguousRegexpLiteral:
70
+ Enabled: false
71
+
72
+ Metrics/LineLength:
73
+ Max: 100
74
+
75
+ Metrics/AbcSize:
76
+ Exclude:
77
+ - 'test/**/*.rb'
78
+
79
+ Metrics/BlockLength:
80
+ Exclude:
81
+ - 'spec/**/*.rb'
82
+
83
+ Metrics/MethodLength:
84
+ Exclude:
85
+ - 'test/**/*.rb'
@@ -1,5 +1,28 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4.2
5
- before_install: gem install bundler -v 1.15.4
4
+ - 2.5.0
5
+
6
+ notifications:
7
+ email: false
8
+
9
+ matrix:
10
+ fast_finish: true
11
+ include:
12
+ - rvm: ruby-head
13
+ gemfile: gemfiles/railsmaster.gemfile
14
+ - rvm: jruby-9.1.0.0
15
+ gemfile: gemfiles/jruby.gemfile
16
+ - rvm: 2.5.0
17
+ gemfile: Gemfile
18
+ - rvm: 2.4.3
19
+ gemfile: Gemfile
20
+ - rvm: 2.3.1
21
+ gemfile: gemfiles/rails42.gemfile
22
+ allow_failures:
23
+ - rvm: 2.3.1
24
+ gemfile: gemfiles/rails42.gemfile
25
+ - rvm: ruby-head
26
+ gemfile: gemfiles/railsmaster.gemfile
27
+ - rvm: jruby-9.1.0.0
28
+ gemfile: gemfiles/jruby.gemfile
@@ -0,0 +1,7 @@
1
+ ## master
2
+
3
+ ## 0.1.0 (2018-04-17)
4
+
5
+ - Initial pre-release version. ([@palkan][])
6
+
7
+ [@palkan]: https://github.com/palkan
data/Gemfile CHANGED
@@ -1,6 +1,15 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source "https://rubygems.org"
4
4
 
5
- # Specify your gem's dependencies in action_policy.gemspec
6
5
  gemspec
6
+
7
+ gem "pry-byebug", platform: :mri
8
+
9
+ local_gemfile = File.join(__dir__, "Gemfile.local")
10
+
11
+ if File.exist?(local_gemfile)
12
+ eval(File.read(local_gemfile)) # rubocop:disable Security/Eval
13
+ else
14
+ gem "rails", "~> 5.0"
15
+ end
data/README.md CHANGED
@@ -1,39 +1,98 @@
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
+ [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](http://actionpolicy.evilmartians.io)
4
+
1
5
  # ActionPolicy
2
6
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/action_policy`. To experiment with that code, run `bin/console` for an interactive prompt.
7
+ Action Policy is an authorization framework for Ruby and Rails applications.
8
+
9
+ 📑 [Documentation][]
4
10
 
5
- TODO: Delete this and the text above, and describe your gem
11
+ <a href="https://evilmartians.com/?utm_source=action_policy">
12
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
6
13
 
7
14
  ## Installation
8
15
 
9
- Add this line to your application's Gemfile:
16
+ Add this line to your application's `Gemfile`:
10
17
 
11
18
  ```ruby
12
- gem 'action_policy'
19
+ gem "action_policy"
13
20
  ```
14
21
 
15
22
  And then execute:
16
23
 
17
24
  $ bundle
18
25
 
19
- Or install it yourself as:
26
+ ## Usage
20
27
 
21
- $ gem install action_policy
28
+ Action Policy relies on resource-specific policy classes (just like [Pundit](https://github.com/varvet/pundit)).
22
29
 
23
- ## Usage
30
+ First, add an application-specific `ApplicationPolicy` with some global configuration to inherit from:
24
31
 
25
- TODO: Write usage instructions here
32
+ ```ruby
33
+ class ApplicationPolicy < ActionPolicy::Base
34
+ end
35
+ ```
26
36
 
27
- ## Development
37
+ Then write a policy for a resource. For example:
28
38
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
39
+ ```ruby
40
+ class PostPolicy < ApplicationPolicy
41
+ # everyone can see any post
42
+ def show?
43
+ true
44
+ end
45
+
46
+ def update?
47
+ # `user` is a performing subject,
48
+ # `record` is a target object (post we want to update)
49
+ user.admin? || (user.id == record.user_id)
50
+ end
51
+ end
52
+ ```
30
53
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
54
+ Now you can easily add authorization to your Rails\* controller:
55
+
56
+ ```ruby
57
+ class PostsController < ApplicationController
58
+ def update
59
+ @post = Post.find(params[:id])
60
+ authorize! @post
61
+
62
+ if @post.update(post_params)
63
+ redirect_to @post
64
+ else
65
+ render :edit
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ \* See [Non-Rails Usage](docs/non_rails.md) on how to add `authorize!` to any Ruby project.
72
+
73
+
74
+ When authorization is successful (i.e., the corresponding rule returns `true`), nothing happens, but in case of authorization failure `ActionPolicy::Unauthorized` error is raised.
75
+
76
+ There is also an `allowed_to?` method which returns `true` or `false`, and could be used, in views, for example:
77
+
78
+ ```erb
79
+ <% @posts.each do |post| %>
80
+ <li><%= post.title %>
81
+ <% if allowed_to?(:edit?, post) %>
82
+ = link_to post, "Edit"
83
+ <% end %>
84
+ </li>
85
+ <% end %>
86
+ ```
87
+
88
+ Read more in our [Documentation][].
32
89
 
33
90
  ## Contributing
34
91
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/action_policy.
92
+ Bug reports and pull requests are welcome on GitHub at https://github.com/palkan/action_policy.
36
93
 
37
94
  ## License
38
95
 
39
96
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
97
+
98
+ [Documentation]: http://actionpolicy.evilmartians.io
data/Rakefile CHANGED
@@ -1,5 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rake/testtask"
5
+ require "rubocop/rake_task"
6
+ require "rspec/core/rake_task"
7
+
8
+ RuboCop::RakeTask.new
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
3
11
 
4
12
  Rake::TestTask.new(:test) do |t|
5
13
  t.libs << "test"
@@ -7,4 +15,4 @@ Rake::TestTask.new(:test) do |t|
7
15
  t.test_files = FileList["test/**/*_test.rb"]
8
16
  end
9
17
 
10
- task :default => :test
18
+ task default: [:rubocop, :test, :spec]
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "action_policy/version"
5
6
 
@@ -9,8 +10,8 @@ Gem::Specification.new do |spec|
9
10
  spec.authors = ["Vladimir Dementyev"]
10
11
  spec.email = ["dementiev.vm@gmail.com"]
11
12
 
12
- spec.summary = "WIP: Authorization framework"
13
- spec.description = "WIP: Authorization framework"
13
+ spec.summary = "Authorization framework for Ruby/Rails application"
14
+ spec.description = "Authorization framework for Ruby/Rails application"
14
15
  spec.homepage = "https://github.com/palkan/action-policy"
15
16
  spec.license = "MIT"
16
17
 
@@ -20,7 +21,12 @@ Gem::Specification.new do |spec|
20
21
 
21
22
  spec.require_paths = ["lib"]
22
23
 
24
+ spec.required_ruby_version = ">= 2.3.0"
25
+
23
26
  spec.add_development_dependency "bundler", "~> 1.15"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
27
  spec.add_development_dependency "minitest", "~> 5.0"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "rspec", "~> 3.3"
30
+ spec.add_development_dependency "rubocop", "~> 0.51"
31
+ spec.add_development_dependency "rubocop-md", "~> 0.2"
26
32
  end
File without changes
@@ -0,0 +1 @@
1
+ actionpolicy.evilmartians.io
@@ -0,0 +1,46 @@
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
+ > Action Policy is an authorization framework for Ruby and Rails applications.
7
+
8
+ ## What is it?
9
+
10
+ _Authorization_ is an act of giving **someone** official
11
+ permission to **do something** (to not be confused with [_authentication_](https://en.wikipedia.org/wiki/Authentication)).
12
+
13
+ Action Policy provides flexible tools to build an _authorization layer_ for your application.
14
+
15
+ <div class="chart-container">
16
+ <img src="assets/images/layer.svg" alt="Authorization layer" width="80%">
17
+ </div>
18
+
19
+ **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?**
20
+
21
+ ## History
22
+
23
+ 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.
24
+
25
+ We have decided to collect all our authorization techniques and pack them into a standalone gem–and that is how Action Policy was born!
26
+
27
+ ## What about the existing solutions?
28
+
29
+ Why did we decide to build our own authorization gem instead of using the existing solutions, such as [Pundit][] and [CanCanCan][]?
30
+
31
+ **TL;DR they didn't solve all of our problems.**
32
+
33
+ [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.
34
+
35
+ These _hacks_ later become into Action Policy (initially, we even called it "Pundit, re-visited").
36
+
37
+ We also took a few ideas from [CanCanCan][]—such as default rules and rule aliases.
38
+
39
+ It is also worth noting that Action Policy (despite from 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.
40
+
41
+ <a href="https://evilmartians.com/?utm_source=action_policy">
42
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
43
+
44
+ [CanCanCan]: https://github.com/CanCanCommunity/cancancan
45
+ [Pundit]: https://github.com/varvet/pundit
46
+ [Evil Martians]: https://evilmartians.com
@@ -0,0 +1,19 @@
1
+ * Getting Started
2
+ * [Quick Start](quick_start.md)
3
+ * [Writing Policies](writing_policies.md)
4
+ * [Rails Integration](rails.md)
5
+ * [Non-Rails Usage](non_rails.md)
6
+ * [Testing](testing.md)
7
+ * Features
8
+ * [Policy Lookup](lookup_chain.md)
9
+ * [Authorization Context](authorization_context.md)
10
+ * [Aliases](aliases.md)
11
+ * [Pre-Checks](pre_checks.md)
12
+ * [Caching](caching.md)
13
+ * [Namespaces](namespaces.md)
14
+ * [Failure Reasons](reasons.md)
15
+ * [Instrumentation](instrumentation.md)
16
+ * [I18n Support](i18n.md)
17
+ * Customize
18
+ * [Base Policy](custom_policy.md)
19
+ * [Lookup Chain](custom_lookup_chain.md)
@@ -0,0 +1,54 @@
1
+ # Rule Aliases
2
+
3
+ Action Policy allows you to add rule aliases. It is useful when you rely on _implicit_ rules in controllers. For example:
4
+
5
+ ```ruby
6
+ class PostsController < ApplicationController
7
+ before_action :load_post, only: [:edit, :update, :destroy]
8
+
9
+ private
10
+
11
+ def load_post
12
+ @post = Post.find(params[:id])
13
+ # depending on action, an `edit?`, `update?` or `destroy?`
14
+ # rule would be applied
15
+ authorize! @post
16
+ end
17
+ end
18
+ ```
19
+
20
+ In your policy, you can create aliases to avoid duplication:
21
+
22
+ ```ruby
23
+ class PostPolicy < ApplicationPolicy
24
+ alias_rule :edit?, :destroy?, to: :update?
25
+ end
26
+ ```
27
+
28
+ **NOTE**: `alias_rule` is available only if you inherit from `ActionPolicy::Base` or include `ActionPolicy::Policy::Aliases` into your `ApplicationPolicy`.
29
+
30
+ **Why not just use Ruby's `alias`?**
31
+
32
+ An alias created with `alias_rule` is resolved at _authorization time_ (during an `authorize!` or `allowed_to?` call), and it does not add an alias method to the class.
33
+
34
+ That allows us to write tests easier, as we should only test the rule, not the alias–and to leverage [caching](caching.md) better.
35
+
36
+ By default, `ActionPolicy::Base` adds one alias: `alias_rule :new?, to: :create?`.
37
+
38
+ ## Default rule
39
+
40
+ You can add a _default_ rule–the rule that would be applied if the rule specified during authorization is missing (like a "wildcard" alias):
41
+
42
+ ```ruby
43
+ class PostPolicy < ApplicationPolicy
44
+ default_rule :manage?
45
+
46
+ def manage?
47
+ # ...
48
+ end
49
+ end
50
+ ```
51
+
52
+ Now when you call `authorize! post` with any rule not explicitly defined in policy class, the `manage?` rule is applied.
53
+
54
+ By default, `ActionPolicy::Base` sets `manage?` as a default rule.