action_policy 0.4.0 → 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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -171
  3. data/LICENSE.txt +1 -1
  4. data/README.md +7 -11
  5. data/lib/action_policy.rb +7 -1
  6. data/lib/action_policy/behaviour.rb +22 -16
  7. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  8. data/lib/action_policy/behaviours/scoping.rb +2 -1
  9. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  10. data/lib/action_policy/ext/module_namespace.rb +1 -6
  11. data/lib/action_policy/ext/policy_cache_key.rb +15 -33
  12. data/lib/action_policy/ext/{symbol_classify.rb → symbol_camelize.rb} +6 -6
  13. data/lib/action_policy/i18n.rb +1 -1
  14. data/lib/action_policy/lookup_chain.rb +41 -21
  15. data/lib/action_policy/policy/aliases.rb +7 -12
  16. data/lib/action_policy/policy/authorization.rb +14 -17
  17. data/lib/action_policy/policy/cache.rb +34 -18
  18. data/lib/action_policy/policy/core.rb +25 -12
  19. data/lib/action_policy/policy/defaults.rb +3 -9
  20. data/lib/action_policy/policy/execution_result.rb +3 -9
  21. data/lib/action_policy/policy/pre_check.rb +19 -58
  22. data/lib/action_policy/policy/reasons.rb +30 -20
  23. data/lib/action_policy/policy/scoping.rb +5 -6
  24. data/lib/action_policy/rails/controller.rb +6 -1
  25. data/lib/action_policy/rails/ext/active_record.rb +7 -0
  26. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  27. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  28. data/lib/action_policy/rspec/dsl.rb +3 -3
  29. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  30. data/lib/action_policy/testing.rb +1 -1
  31. data/lib/action_policy/utils/pretty_print.rb +21 -24
  32. data/lib/action_policy/utils/suggest_message.rb +1 -3
  33. data/lib/action_policy/version.rb +1 -1
  34. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +1 -1
  35. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  36. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  37. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  38. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  39. metadata +30 -119
  40. data/.gitattributes +0 -2
  41. data/.github/FUNDING.yml +0 -1
  42. data/.github/ISSUE_TEMPLATE.md +0 -18
  43. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  44. data/.gitignore +0 -15
  45. data/.rubocop.yml +0 -54
  46. data/.tidelift.yml +0 -6
  47. data/.travis.yml +0 -31
  48. data/Gemfile +0 -22
  49. data/Rakefile +0 -27
  50. data/action_policy.gemspec +0 -44
  51. data/benchmarks/namespaced_lookup_cache.rb +0 -71
  52. data/bin/console +0 -14
  53. data/bin/setup +0 -8
  54. data/docs/.nojekyll +0 -0
  55. data/docs/CNAME +0 -1
  56. data/docs/README.md +0 -77
  57. data/docs/_sidebar.md +0 -27
  58. data/docs/aliases.md +0 -122
  59. data/docs/assets/docsify-search.js +0 -364
  60. data/docs/assets/docsify.min.js +0 -3
  61. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  62. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  63. data/docs/assets/images/banner.png +0 -0
  64. data/docs/assets/images/cache.png +0 -0
  65. data/docs/assets/images/cache.svg +0 -70
  66. data/docs/assets/images/layer.png +0 -0
  67. data/docs/assets/images/layer.svg +0 -35
  68. data/docs/assets/prism-ruby.min.js +0 -1
  69. data/docs/assets/styles.css +0 -347
  70. data/docs/assets/vue.min.css +0 -1
  71. data/docs/authorization_context.md +0 -92
  72. data/docs/behaviour.md +0 -113
  73. data/docs/caching.md +0 -273
  74. data/docs/controller_action_aliases.md +0 -109
  75. data/docs/custom_lookup_chain.md +0 -48
  76. data/docs/custom_policy.md +0 -53
  77. data/docs/debugging.md +0 -55
  78. data/docs/decorators.md +0 -27
  79. data/docs/favicon.ico +0 -0
  80. data/docs/graphql.md +0 -302
  81. data/docs/i18n.md +0 -44
  82. data/docs/index.html +0 -43
  83. data/docs/instrumentation.md +0 -84
  84. data/docs/lookup_chain.md +0 -17
  85. data/docs/namespaces.md +0 -77
  86. data/docs/non_rails.md +0 -28
  87. data/docs/pre_checks.md +0 -57
  88. data/docs/pundit_migration.md +0 -80
  89. data/docs/quick_start.md +0 -118
  90. data/docs/rails.md +0 -120
  91. data/docs/reasons.md +0 -120
  92. data/docs/scoping.md +0 -255
  93. data/docs/testing.md +0 -333
  94. data/docs/writing_policies.md +0 -107
  95. data/gemfiles/jruby.gemfile +0 -8
  96. data/gemfiles/rails42.gemfile +0 -8
  97. data/gemfiles/rails6.gemfile +0 -8
  98. data/gemfiles/railsmaster.gemfile +0 -6
  99. data/lib/action_policy/ext/string_match.rb +0 -14
  100. data/lib/action_policy/ext/yield_self_then.rb +0 -25
@@ -1,44 +0,0 @@
1
- # I18n Support
2
-
3
- `ActionPolicy` integrates with [`i18n`][] to support localizable `full_messages` for [reasons](./reasons.md) and the execution result's `message`:
4
-
5
- ```ruby
6
- class ApplicationController < ActionController::Base
7
- rescue_from ActionPolicy::Unauthorized do |ex|
8
- p ex.result.message #=> "You do not have access to the stage"
9
- p ex.result.reasons.full_messages #=> ["You do not have access to the stage"]
10
- end
11
- end
12
- ```
13
-
14
- The message contains a string for the _rule_ that was called, while `full_messages` contains the list of reasons, why `ActionPolicy::Unauthorized` has been raised. You can find more information about tracking failure reasons [here](./reasons.md).
15
-
16
- ## Configuration
17
-
18
- `ActionPolicy` doesn't provide any localization out-of-the-box and uses "You are not authorized to perform this action" as the default message.
19
-
20
- You can add your app-level default fallback by providing the `action_policy.unauthorized` key value.
21
-
22
- When using **Rails**, all you need is to add translations to any file under the `config/locales` folder (or create a new file, e.g. `config/locales/policies.yml`).
23
-
24
- Non-Rails projets should configure [`i18n`][] gem manually:
25
-
26
- ```ruby
27
- I18n.load_path << Dir[File.expand_path("config/locales") + "/*.yml"]
28
- ```
29
-
30
- ## Translations lookup
31
-
32
- `ActionPolicy` uses the `action_policy` scope. Specific policies translations must be stored inside the `policy` sub-scope.
33
-
34
- The following algorithm is used to find out the translation for a policy with a class `klass` and rule `rule`:
35
- 1. Translation for `"#{klass.identifier}.#{rule}"` key, when `self.identifier =` is not specified then underscored class name without the _Policy_ suffix would be used (e.g. `GuestUserPolicy` turns into `guest_user:` scope)
36
- 2. Repeat step 1 for each ancestor which looks like a policy (`.respond_to?(:identifier)?`) up to `ActionPolicy::Base`
37
- 3. Use `#{rule}` key
38
- 4. Use `en.action_policy.unauthorized` key
39
- 5. Use a default message provided by the gem
40
-
41
- For example, given a `GuestUserPolicy` class which is inherited from `DefaultUserPolicy` and a rule `feed?`, the following list of possible translation keys would be used: `[:"action_policy.policy.guest_user.feed?", :"action_policy.policy.default_user.feed?", :"action_policy.policy.feed?", :"action_policy.unauthorized"]`
42
-
43
-
44
- [`i18n`]: https://github.com/svenfuchs/i18n
@@ -1,43 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Action Policy: authorization framework for Ruby/Rails applications</title>
6
- <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
7
- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
8
- <meta itemprop="name" content="Action Policy" />
9
- <meta property="og:title" content="Action Policy" />
10
- <meta name="description" content="Athorization framework for Ruby/Rails application" />
11
- <meta itemprop="description" content="Authorization framework for Ruby/Rails application" />
12
- <meta property="og:description" content="Authorization framework for Ruby/Rails application" />
13
- <meta itemprop="image" content="http://actionpolicy.evilmartians.io/assets/images/banner.png" />
14
- <meta property="og:image" content="http://actionpolicy.evilmartians.io/assets/images/banner.png" />
15
- <meta name="twitter:card" content="summary_large_image" />
16
- <meta name="twitter:site" content="@palkan_tula" />
17
- <meta name="twitter:creator" content="@palkan_tula" />
18
- <meta property="og:site_name" content="Action Policy" />
19
- <meta name="keywords" content="ruby, rails, authorization, open-source" />
20
- <meta name="theme-color" content="#CB2028" />
21
- <link rel="stylesheet" href="assets/vue.min.css">
22
- <link rel="stylesheet" href="assets/styles.css">
23
- </head>
24
- <body>
25
- <div id="app"></div>
26
- <script>
27
- window.$docsify = {
28
- name: 'action_policy',
29
- repo: 'https://github.com/palkan/action_policy',
30
- loadSidebar: true,
31
- subMaxLevel: 3,
32
- ga: 'UA-104346673-3',
33
- auto2top: true,
34
- search: {
35
- namespace: 'action-policy'
36
- }
37
- }
38
- </script>
39
- <script src="assets/docsify.min.js"></script>
40
- <script src="assets/docsify-search.js"></script>
41
- <script src="assets/prism-ruby.min.js"></script>
42
- </body>
43
- </html>
@@ -1,84 +0,0 @@
1
- # Instrumentation
2
-
3
- Action Policy integrates with [Rails instrumentation system](https://guides.rubyonrails.org/active_support_instrumentation.html), `ActiveSupport::Notifications`.
4
-
5
- ## Events
6
-
7
- ### `action_policy.apply_rule`
8
-
9
- This event is triggered every time a policy rule is applied:
10
- - when `authorize!` is called
11
- - when `allowed_to?` is called within the policy or the [behaviour](behaviour)
12
- - when `apply_rule` is called explicitly (i.e. `SomePolicy.new(record, context).apply_rule(record)`).
13
-
14
- The event contains the following information:
15
- - `:policy` – policy class name
16
- - `:rule` – applied rule (String)
17
- - `:value` – the result of the rule application (true of false)
18
- - `:cached` – whether we hit the [cache](caching)\*.
19
-
20
- \* This parameter tracks only the cache store usage, not memoization.
21
-
22
- You can use this event to track your policy cache usage and also detect _slow_ checks.
23
-
24
- Here is an example code for sending policy stats to [Librato](https://librato.com/)
25
- using [`librato-rack`](https://github.com/librato/librato-rack):
26
-
27
- ```ruby
28
- ActiveSupport::Notifications.subscribe("action_policy.apply_rule") do |event, started, finished, _, data|
29
- # Track hit and miss events separately (to display two measurements)
30
- measurement = "#{event}.#{(data[:cached] ? "hit" : "miss")}"
31
- # show ms times
32
- timing = ((finished - started) * 1000).to_i
33
- Librato.tracker.check_worker
34
- Librato.timing measurement, timing, percentile: [95, 99]
35
- end
36
- ```
37
-
38
- ### `action_policy.authorize`
39
-
40
- This event is identical to `action_policy.apply_rule` with the one difference:
41
- **it's only triggered when `authorize!` method is called**.
42
-
43
- The motivation behind having a separate event for this method is to monitor the number of failed
44
- authorizations: the high number of failed authorizations usually means that we do not take
45
- into account authorization rules in the application UI (e.g., we show a "Delete" button to the user not
46
- permitted to do that).
47
-
48
- The `action_policy.apply_rule` might have a large number of failures, 'cause it also tracks the usage of non-raising applications (i.e. `allowed_to?`).
49
-
50
- ### `action_policy.init`
51
-
52
- This event is triggered every time a new policy object is initialized.
53
-
54
- The event contains the following information:
55
-
56
- - `:policy` – policy class name.
57
-
58
- This event is useful if you want to track the number of initialized policies per _action_ (for example, when you want to ensure that
59
- the [memoization](caching.md) works as expected).
60
-
61
- ## Turn off instrumentation
62
-
63
- Instrumentation is enabled by default. To turn it off add to your configuration:
64
-
65
- ```ruby
66
- config.action_policy.instrumentation_enabled = false
67
- ```
68
-
69
- **NOTE:** changing this setting after the application has been initialized doesn't take any effect.
70
-
71
- ## Non-Rails usage
72
-
73
- If you don't use Rails itself but have `ActiveSupport::Notifications` available in your application,
74
- you can use the instrumentation feature with some additional configuration:
75
-
76
- ```ruby
77
- # Enable `apply_rule` event by extending the base policy class
78
- require "action_policy/rails/policy/instrumentation"
79
- ActionPolicy::Base.include ActionPolicy::Policy::Rails::Instrumentation
80
-
81
- # Enabled `authorize` event by extending the authorizer class
82
- require "action_policy/rails/authorizer"
83
- ActionPolicy::Authorizer.singleton_class.prepend ActionPolicy::Rails::Authorizer
84
- ```
@@ -1,17 +0,0 @@
1
- # Policy Lookup
2
-
3
- Action Policy tries to automatically infer policy class from the target using the following _probes_:
4
-
5
- 1. If the target is a `Symbol`, then use `"#{target.to_s.classify}Policy"` as a `policy_name` (see below);
6
- 2. If the target responds to `policy_class`, then use it;
7
- 3. If the target's class responds to `policy_class`, then use it;
8
- 4. If the target or the target's class responds to `policy_name`, then use it (the `policy_name` should end with `Policy` as it's not appended automatically);
9
- 5. Otherwise, use `#{target.class.name}Policy`.
10
-
11
- > \* [Namespaces](namespaces.md) could be also be considered when `namespace` option is set.
12
-
13
- You can call `ActionPolicy.lookup(record, options)` to infer policy class for the record.
14
-
15
- When no policy class is found, an `ActionPolicy::NotFound` error is raised.
16
-
17
- You can [customize lookup](custom_lookup_chain.md) logic if necessary.
@@ -1,77 +0,0 @@
1
- # Namespaces
2
-
3
- Action Policy can lookup policies with respect to the current execution _namespace_ (i.e., authorization class module).
4
-
5
- Consider an example:
6
-
7
- ```ruby
8
- module Admin
9
- class UsersController < ApplictionController
10
- def index
11
- # uses Admin::UserPolicy if any, otherwise fallbacks to UserPolicy
12
- authorize!
13
- end
14
- end
15
- end
16
- ```
17
-
18
- Module nesting is also supported:
19
-
20
- ```ruby
21
- module Admin
22
- module Client
23
- class UsersController < ApplictionController
24
- def index
25
- # lookup for Admin::Client::UserPolicy -> Admin::UserPolicy -> UserPolicy
26
- authorize!
27
- end
28
- end
29
- end
30
- end
31
- ```
32
-
33
- **NOTE**: to support namespaced lookup for non-inferrable resources,
34
- you should specify `policy_name` at a class level (instead of `policy_class`, which doesn't take namespaces into account):
35
-
36
- ```ruby
37
- class Guest < User
38
- def self.policy_name
39
- "UserPolicy"
40
- end
41
- end
42
- ```
43
-
44
- **NOTE**: by default, we use class's name as a policy name; so, for namespaced resources, the namespace part is also included:
45
-
46
- ```ruby
47
- class Admin
48
- class User
49
- end
50
- end
51
-
52
- # search for Admin::UserPolicy, but not for UserPolicy
53
- authorize! Admin::User.new
54
- ```
55
-
56
- You can access the current authorization namespace through `authorization_namespace` method.
57
-
58
- You can also define your own namespacing logic by overriding `authorization_namespace`:
59
-
60
- ```ruby
61
- def authorization_namespace
62
- return ::Admin if current_user.admin?
63
- return ::Staff if current_user.staff?
64
- # fallback to current namespace
65
- super
66
- end
67
- ```
68
-
69
- **NOTE**: namespace support is an extension for `ActionPolicy::Behaviour` and could be included with `ActionPolicy::Behaviours::Namespaced` (included into Rails controllers and channel integrations by default).
70
-
71
- ## Namespace resolution cache
72
-
73
- We cache namespaced policy resolution for better performance (it could affect performance when we look up a policy from a deeply nested module context, see the [benchmark](https://github.com/palkan/action_policy/blob/master/benchmarks/namespaced_lookup_cache.rb)).
74
-
75
- 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).
76
-
77
- When using Rails it's enabled only in production mode but could be configured through setting the `config.action_policy.namespace_cache_enabled` parameter.
@@ -1,28 +0,0 @@
1
- # Using with Ruby applications
2
-
3
- Action Policy is designed to be independent of any framework and does not have specific dependencies on Ruby on Rails.
4
-
5
- You can [write your policies](writing_policies.md) for non-Rails applications the same way as you would do for Rails applications.
6
-
7
- In order to have `authorize!` / `allowed_to?` / `authorized` methods, you will have to include [`ActionPolicy::Behaviour`](./behaviour.md) into your class (where you want to perform authorization):
8
-
9
- ```ruby
10
- class PostUpdateAction
11
- include ActionPolicy::Behaviour
12
-
13
- # provide authorization subject (performer)
14
- authorize :user
15
-
16
- attr_reader :user
17
-
18
- def initialize(user)
19
- @user = user
20
- end
21
-
22
- def call(post, params)
23
- authorize! post, to: :update?
24
-
25
- post.update!(params)
26
- end
27
- end
28
- ```
@@ -1,57 +0,0 @@
1
- # Pre-Checks
2
-
3
- Consider a typical situation when you start most—or even all—of your rules with the same predicates.
4
-
5
- For example, when you have a super-user role in the application:
6
-
7
- ```ruby
8
- class PostPolicy < ApplicationPolicy
9
- def show?
10
- user.super_admin? || record.published
11
- end
12
-
13
- def update?
14
- user.super_admin? || (user.id == record.user_id)
15
- end
16
-
17
- # more rules
18
- end
19
- ```
20
-
21
- Action Policy allows you to extract the common parts from rules into _pre-checks_:
22
-
23
- ```ruby
24
- class PostPolicy < ApplicationPolicy
25
- pre_check :allow_admins
26
-
27
- def show?
28
- record.published
29
- end
30
-
31
- def update?
32
- user.id == record.user_id
33
- end
34
-
35
- private
36
-
37
- def allow_admins
38
- allow! if user.super_admin?
39
- end
40
- end
41
- ```
42
-
43
- Pre-checks act like _callbacks_: you can add multiple pre-checks, specify `except` and `only` options, and skip already defined pre-checks if necessary:
44
-
45
- ```ruby
46
- class UserPolicy < ApplicationPolicy
47
- skip_pre_check :allow_admins, only: :destroy?
48
-
49
- def destroy?
50
- user.admin? && !record.admin?
51
- end
52
- end
53
- ```
54
-
55
- To halt the authorization process within a pre-check, you must return either `allow!` or `deny!` call value. When any other value is returned, the pre-check is ignored, and the rule is called (or next pre-check).
56
-
57
- **NOTE**: pre-checks are available only if you inherit from `ActionPolicy::Base` or include `ActionPolicy::Policy::PreCheck` into your `ApplicationPolicy`.
@@ -1,80 +0,0 @@
1
- # Migrate from Pundit to Action Policy
2
-
3
- Migration from Pundit to Action Policy could be done in a progressive way: first, we make Pundit polices and authorization helpers use Action Policy under the hood, then you can rewrite policies in the Action Policy way.
4
-
5
- ### Phase 1. Quacking like a Pundit.
6
-
7
- #### Step 1. Prepare controllers.
8
-
9
- - Remove `include Pundit` from ApplicationController
10
-
11
- - Add `authorize` method:
12
-
13
- ```ruby
14
- def authorize(record, rule = nil)
15
- options = {}
16
- options[:to] = rule unless rule.nil?
17
-
18
- authorize! record, **options
19
- end
20
- ```
21
-
22
- - Configure [authorization context](authorization_context) if necessary, e.g. add `authorize :current_user, as: :user` to `ApplicationController` (**NOTE:** added automatically in Rails apps)
23
-
24
- - Add `policy` and `policy_scope` helpers:
25
-
26
- ```ruby
27
- helper_method :policy
28
- helper_method :policy_scope
29
-
30
- def policy(record)
31
- policy_for(record)
32
- end
33
-
34
- def policy_scope(scope)
35
- authorized scope
36
- end
37
-
38
- ```
39
-
40
- **NOTE**: `policy` defined above is not equal to `allowed_to?` since it doesn't take into account pre-checks.
41
-
42
- #### Step 2. Prepare policies.
43
-
44
- We assume that you have a base class for all your policies, e.g. `ApplicationPolicy`.
45
-
46
- Then do the following:
47
- - Add `include ActionPolicy::Policy::Core` to `ApplicationPolicy`
48
-
49
- - Update `ApplicationPolicy#initialize`:
50
-
51
- ```ruby
52
- def initialize(target, user:)
53
- # ...
54
- end
55
- ```
56
-
57
- - [Rewrite scopes](scoping).
58
-
59
- Unfortunately, there is no easy way to migrate Pundit class-based scope to Action Policies scopes.
60
-
61
- #### Step 3. Replace RSpec helper:
62
-
63
- We provide a Pundit-compatibile syntax for RSpec tests:
64
-
65
- ```
66
- # Remove DSL
67
- # require "pundit/rspec"
68
- #
69
- # Add Action Policy Pundit DSL
70
- require "action_policy/rspec/pundit_syntax"
71
- ```
72
-
73
- ### Phase 2. No more Pundit.
74
-
75
- When everything is green, it's time to fully migrate to ActionPolicy:
76
- - make ApplicationPolicy inherit from `ActionPolicy::Base`
77
- - migrate view helpers (from `policy(..)` to `allowed_to?`, from `policy_scope` to `authorized`)
78
- - re-write specs using simple non-DSL syntax (or [Action Policy RSpec syntax](testing#rspec-dsl))
79
- - add [authorization tests](testing#testing-authorization) (add `require 'action_policy/rspec'`)
80
- - use [Reasons](reasons), [I18n integration](i18n), [cache](caching) and other Action Policy features!
@@ -1,118 +0,0 @@
1
- # Quick Start
2
-
3
- ## Installation
4
-
5
- Install Action Policy with RubyGems:
6
-
7
- ```ruby
8
- gem install action_policy
9
- ```
10
-
11
- Or add `action_policy` to your application's `Gemfile`:
12
-
13
- ```ruby
14
- gem "action_policy"
15
- ```
16
-
17
- And then execute:
18
-
19
- $ bundle
20
-
21
- ## Basic usage
22
-
23
- The core component of Action Policy is a _policy class_. Policy class describes how you control access to resources.
24
-
25
- We suggest having a separate policy class for each resource and encourage you to follow these conventions:
26
- - put policies into the `app/policies` folder (when using with Rails);
27
- - name policies using the corresponding singular resource name (model name) with a `Policy` suffix, e.g. `Post -> PostPolicy`;
28
- - name rules using a predicate form of the corresponding activity (typically, a controller's action), e.g. `PostsController#update -> PostPolicy#update?`.
29
-
30
- We also recommend to use an application-specific `ApplicationPolicy` with a global configuration to inherit from:
31
-
32
- ```ruby
33
- class ApplicationPolicy < ActionPolicy::Base
34
- end
35
- ```
36
-
37
- You could use the following command to generate it when using Rails:
38
-
39
- ```sh
40
- rails generate action_policy:install
41
- ```
42
-
43
- **NOTE:** it is not necessary to inherit from `ActionPolicy::Base`; instead, you can [construct basic policy](custom_policy.md) choosing only the components you need.
44
-
45
- Rules must be public methods on the class. Using private methods as rules will raise an error.
46
-
47
- Consider a simple example:
48
-
49
- ```ruby
50
- class PostPolicy < ApplicationPolicy
51
- # everyone can see any post
52
- def show?
53
- true
54
- end
55
-
56
- def update?
57
- # `user` is a performing subject,
58
- # `record` is a target object (post we want to update)
59
- user.admin? || (user.id == record.user_id)
60
- end
61
- end
62
- ```
63
-
64
- Now you can easily add authorization to your Rails\* controller:
65
-
66
- ```ruby
67
- class PostsController < ApplicationController
68
- def update
69
- @post = Post.find(params[:id])
70
- authorize! @post
71
-
72
- if @post.update(post_params)
73
- redirect_to @post
74
- else
75
- render :edit
76
- end
77
- end
78
- end
79
- ```
80
-
81
- \* See [Non-Rails Usage](non_rails.md) on how to add `authorize!` to any Ruby project.
82
-
83
- In the above case, Action Policy automatically infers a policy class and a rule to verify access: `@post -> Post -> PostPolicy`, rule is inferred from the action name (`update -> update?`), and `current_user` is used as `user` within the policy by default (read more about [authorization context](authorization_context.md)).
84
-
85
- When authorization is successful (i.e., the corresponding rule returns `true`), nothing happens, but in case of an authorization failure `ActionPolicy::Unauthorized` error is raised:
86
-
87
- ```ruby
88
- rescue_from ActionPolicy::Unauthorized do |ex|
89
- # Exception object contains the following information
90
- ex.policy #=> policy class, e.g. UserPolicy
91
- ex.rule #=> applied rule, e.g. :show?
92
- end
93
- ```
94
-
95
- There is also an `allowed_to?` method which returns `true` or `false` and could be used, for example, in views:
96
-
97
- ```erb
98
- <% @posts.each do |post| %>
99
- <li><%= post.title %>
100
- <% if allowed_to?(:edit?, post) %>
101
- = link_to "Edit", post
102
- <% end %>
103
- </li>
104
- <% end %>
105
- ```
106
-
107
- Although Action Policy tries to [infer the corresponding policy class](policy_lookup.md) and rule itself, there could be a situation when you want to specify those values explicitly:
108
-
109
- ```ruby
110
- # specify the rule to verify access
111
- authorize! @post, to: :update?
112
-
113
- # specify policy class
114
- authorize! @post, with: CustomPostPolicy
115
-
116
- # or
117
- allowed_to? :edit?, @post, with: CustomPostPolicy
118
- ```