action_policy 0.0.1 → 0.1.0

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