action_policy 0.4.4 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +203 -174
  3. data/README.md +5 -4
  4. data/lib/.rbnext/2.7/action_policy/behaviours/policy_for.rb +62 -0
  5. data/lib/.rbnext/2.7/action_policy/i18n.rb +56 -0
  6. data/lib/.rbnext/2.7/action_policy/policy/cache.rb +101 -0
  7. data/lib/.rbnext/2.7/action_policy/policy/pre_check.rb +162 -0
  8. data/lib/.rbnext/2.7/action_policy/rspec/be_authorized_to.rb +89 -0
  9. data/lib/.rbnext/2.7/action_policy/rspec/have_authorized_scope.rb +124 -0
  10. data/lib/.rbnext/2.7/action_policy/utils/pretty_print.rb +159 -0
  11. data/lib/.rbnext/3.0/action_policy/behaviour.rb +115 -0
  12. data/lib/.rbnext/3.0/action_policy/behaviours/policy_for.rb +62 -0
  13. data/lib/.rbnext/3.0/action_policy/behaviours/scoping.rb +35 -0
  14. data/lib/.rbnext/3.0/action_policy/behaviours/thread_memoized.rb +59 -0
  15. data/lib/.rbnext/3.0/action_policy/ext/policy_cache_key.rb +72 -0
  16. data/lib/.rbnext/3.0/action_policy/policy/aliases.rb +69 -0
  17. data/lib/.rbnext/3.0/action_policy/policy/authorization.rb +87 -0
  18. data/lib/.rbnext/3.0/action_policy/policy/cache.rb +101 -0
  19. data/lib/.rbnext/3.0/action_policy/policy/core.rb +161 -0
  20. data/lib/.rbnext/3.0/action_policy/policy/defaults.rb +31 -0
  21. data/lib/.rbnext/3.0/action_policy/policy/execution_result.rb +37 -0
  22. data/lib/.rbnext/3.0/action_policy/policy/pre_check.rb +162 -0
  23. data/lib/.rbnext/3.0/action_policy/policy/reasons.rb +212 -0
  24. data/lib/.rbnext/3.0/action_policy/policy/scoping.rb +160 -0
  25. data/lib/.rbnext/3.0/action_policy/rspec/be_authorized_to.rb +89 -0
  26. data/lib/.rbnext/3.0/action_policy/rspec/have_authorized_scope.rb +124 -0
  27. data/lib/.rbnext/3.0/action_policy/utils/pretty_print.rb +159 -0
  28. data/lib/.rbnext/3.0/action_policy/utils/suggest_message.rb +19 -0
  29. data/lib/action_policy.rb +7 -1
  30. data/lib/action_policy/behaviour.rb +22 -16
  31. data/lib/action_policy/behaviours/policy_for.rb +10 -3
  32. data/lib/action_policy/behaviours/scoping.rb +2 -1
  33. data/lib/action_policy/behaviours/thread_memoized.rb +1 -3
  34. data/lib/action_policy/ext/module_namespace.rb +1 -6
  35. data/lib/action_policy/ext/policy_cache_key.rb +10 -30
  36. data/lib/action_policy/i18n.rb +1 -1
  37. data/lib/action_policy/lookup_chain.rb +29 -15
  38. data/lib/action_policy/policy/aliases.rb +7 -12
  39. data/lib/action_policy/policy/authorization.rb +8 -7
  40. data/lib/action_policy/policy/cache.rb +11 -17
  41. data/lib/action_policy/policy/core.rb +25 -12
  42. data/lib/action_policy/policy/defaults.rb +3 -9
  43. data/lib/action_policy/policy/execution_result.rb +3 -9
  44. data/lib/action_policy/policy/pre_check.rb +19 -58
  45. data/lib/action_policy/policy/reasons.rb +31 -19
  46. data/lib/action_policy/policy/scoping.rb +5 -6
  47. data/lib/action_policy/rails/controller.rb +6 -1
  48. data/lib/action_policy/rails/policy/instrumentation.rb +1 -1
  49. data/lib/action_policy/rspec/be_authorized_to.rb +5 -9
  50. data/lib/action_policy/rspec/dsl.rb +1 -1
  51. data/lib/action_policy/rspec/have_authorized_scope.rb +5 -7
  52. data/lib/action_policy/utils/pretty_print.rb +21 -24
  53. data/lib/action_policy/utils/suggest_message.rb +1 -3
  54. data/lib/action_policy/version.rb +1 -1
  55. data/lib/generators/action_policy/install/templates/{application_policy.rb → application_policy.rb.tt} +0 -0
  56. data/lib/generators/action_policy/policy/policy_generator.rb +4 -1
  57. data/lib/generators/action_policy/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  58. data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  59. data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
  60. metadata +54 -119
  61. data/.gitattributes +0 -2
  62. data/.github/ISSUE_TEMPLATE.md +0 -21
  63. data/.github/PULL_REQUEST_TEMPLATE.md +0 -29
  64. data/.github/bug_report_template.rb +0 -175
  65. data/.gitignore +0 -15
  66. data/.rubocop.yml +0 -54
  67. data/.tidelift.yml +0 -6
  68. data/.travis.yml +0 -31
  69. data/Gemfile +0 -22
  70. data/Rakefile +0 -27
  71. data/action_policy.gemspec +0 -44
  72. data/benchmarks/namespaced_lookup_cache.rb +0 -74
  73. data/benchmarks/pre_checks.rb +0 -73
  74. data/bin/console +0 -14
  75. data/bin/setup +0 -8
  76. data/docs/.nojekyll +0 -0
  77. data/docs/CNAME +0 -1
  78. data/docs/README.md +0 -79
  79. data/docs/_sidebar.md +0 -27
  80. data/docs/aliases.md +0 -122
  81. data/docs/assets/docsify-search.js +0 -364
  82. data/docs/assets/docsify.min.js +0 -3
  83. data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
  84. data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
  85. data/docs/assets/images/banner.png +0 -0
  86. data/docs/assets/images/cache.png +0 -0
  87. data/docs/assets/images/cache.svg +0 -70
  88. data/docs/assets/images/layer.png +0 -0
  89. data/docs/assets/images/layer.svg +0 -35
  90. data/docs/assets/prism-ruby.min.js +0 -1
  91. data/docs/assets/styles.css +0 -347
  92. data/docs/assets/vue.min.css +0 -1
  93. data/docs/authorization_context.md +0 -92
  94. data/docs/behaviour.md +0 -113
  95. data/docs/caching.md +0 -291
  96. data/docs/controller_action_aliases.md +0 -109
  97. data/docs/custom_lookup_chain.md +0 -48
  98. data/docs/custom_policy.md +0 -53
  99. data/docs/debugging.md +0 -55
  100. data/docs/decorators.md +0 -27
  101. data/docs/favicon.ico +0 -0
  102. data/docs/graphql.md +0 -302
  103. data/docs/i18n.md +0 -44
  104. data/docs/index.html +0 -43
  105. data/docs/instrumentation.md +0 -84
  106. data/docs/lookup_chain.md +0 -22
  107. data/docs/namespaces.md +0 -77
  108. data/docs/non_rails.md +0 -28
  109. data/docs/pre_checks.md +0 -57
  110. data/docs/pundit_migration.md +0 -80
  111. data/docs/quick_start.md +0 -118
  112. data/docs/rails.md +0 -120
  113. data/docs/reasons.md +0 -120
  114. data/docs/scoping.md +0 -255
  115. data/docs/testing.md +0 -390
  116. data/docs/writing_policies.md +0 -107
  117. data/gemfiles/jruby.gemfile +0 -8
  118. data/gemfiles/rails42.gemfile +0 -9
  119. data/gemfiles/rails6.gemfile +0 -8
  120. data/gemfiles/railsmaster.gemfile +0 -6
  121. data/lib/action_policy/ext/string_match.rb +0 -14
  122. data/lib/action_policy/ext/yield_self_then.rb +0 -25
@@ -1,109 +0,0 @@
1
- # Controller Action Aliases
2
-
3
- **This is a feature proposed here: https://github.com/palkan/action_policy/issues/25**
4
-
5
- If you'd like to see this feature implemented, please comment on the issue to show your support.
6
-
7
- ## Outline
8
-
9
- Say you have abstracted your `authorize!` call to a controller superclass because your policy can
10
- be executed without regard to the record in any of the subclass controllers:
11
-
12
- ```ruby
13
- class AbstractController < ApplicationController
14
- authorize :context
15
- before_action :authorize_context
16
-
17
- def context
18
- # Some code to get your policy context
19
- end
20
-
21
- private
22
-
23
- def authorize_context
24
- authorize! Context
25
- end
26
- end
27
- ```
28
-
29
- Your policy might look like this:
30
-
31
- ```ruby
32
- class ContextPolicy < ApplicationPolicy
33
- authorize :context
34
-
35
- alias_rule :index?, :show?, to: :view?
36
- alias_rule :new?, :create?, :update?, :destroy?, to: :edit?
37
-
38
- def view?
39
- context.has_permission_to(:view, user)
40
- end
41
-
42
- def edit?
43
- context.has_permission_to(:edit, user)
44
- end
45
- end
46
- ```
47
-
48
- We can safely add aliases for the common REST actions in the policy.
49
-
50
- You may then want to include a concern in your subclass controller(s) that add extra actions to the controller.
51
-
52
-
53
- ```ruby
54
- class ConcreteController < AbstractController
55
- include AdditionalFunctionalityConcern
56
-
57
- def index
58
- # Index Action
59
- end
60
-
61
- def new
62
- # New Action
63
- end
64
-
65
- # etc...
66
- end
67
- ```
68
-
69
- At this point you may be wondering how to tell your abstracted policy that these new methods map to either
70
- the `view?` or `edit?` rule. You can currently provide the rule to execute to the `authorize!` method with
71
- the `to:` parameter but since our call to `authorize!` is in a superclass it has no idea about our concern.
72
- I propose the following controller method:
73
-
74
- ```ruby
75
- alias_action(*actions, to_rule: rule)
76
- ```
77
-
78
- Here's an example:
79
-
80
- ```ruby
81
- module AdditionalFunctionalityConcern
82
- extend ActiveSupport::Concern
83
-
84
- included do
85
- alias_action [:first_action, :second_action], to_rule: :view?
86
- alias_action [:third_action], to_rule: :edit?
87
- end
88
-
89
- def first_action
90
- # First Action
91
- end
92
-
93
- def second_action
94
- # Second Action
95
- end
96
-
97
- def third_action
98
- # Third Action
99
- end
100
- end
101
- ```
102
-
103
- When `authorize!` is called in a controller, it will first check the action aliases for a corresponding
104
- rule. If one is found, it will execute that rule instead of a rule matching the name of the current action.
105
- The rule may point at a concrete rule in the policy, or a rule alias in the policy, it doens't matter, the
106
- alias in the policy will be resolved like normal.
107
-
108
- If you'd like to see this feature implemented, please show your support on the
109
- [Github Issue](https://github.com/palkan/action_policy/issues/25).
@@ -1,48 +0,0 @@
1
- # Custom Lookup Chain
2
-
3
- Action Policy's lookup chain is just an array of _probes_ (lambdas with a specific interface).
4
-
5
- The lookup process itself is pretty simple:
6
- - Call the first probe;
7
- - Return the result if it is not `nil`;
8
- - Go to the next probe.
9
-
10
- You can override the default chain with your own. For example:
11
-
12
- ```ruby
13
- ActionPolicy::LookupChain.chain = [
14
- # Probe accepts record as the first argument
15
- # and arbitrary options (passed to `authorize!` / `allowed_to?` call)
16
- lambda do |record, **options|
17
- # your custom lookup logic
18
- end
19
- ]
20
- ```
21
-
22
- ## NullPolicy example
23
-
24
- Let's consider a simple example of extending the existing lookup chain with one more probe.
25
-
26
- Suppose that we want to have a fallback policy (policy used when none found for the resource) instead of raising an `ActionPolicy::NotFound` error.
27
-
28
- Let's call this policy a `NullPolicy`:
29
-
30
- ```ruby
31
- class NullPolicy < ActionPolicy::Base
32
- default_rule :any?
33
-
34
- def any?
35
- false
36
- end
37
- end
38
- ```
39
-
40
- Here we use the [default rule](aliases.md#default-rule) to handle any rule applied.
41
-
42
- Now we need to add a simple probe to the end of our lookup chain:
43
-
44
- ```ruby
45
- ActionPolicy::LookupChain.chain << ->(_, _) { NullPolicy }
46
- ```
47
-
48
- That's it!
@@ -1,53 +0,0 @@
1
- # Custom Base Policy
2
-
3
- `ActionPolicy::Base` is a combination of all available policy extensions with the default configuration.
4
-
5
- It looks like this:
6
-
7
-
8
- ```ruby
9
- class ActionPolicy::Base
10
- include ActionPolicy::Policy::Core
11
- include ActionPolicy::Policy::Authorization
12
- include ActionPolicy::Policy::PreCheck
13
- include ActionPolicy::Policy::Reasons
14
- include ActionPolicy::Policy::Aliases
15
- include ActionPolicy::Policy::Scoping
16
- include ActionPolicy::Policy::Cache
17
- include ActionPolicy::Policy::CachedApply
18
- include ActionPolicy::Policy::Defaults
19
-
20
- # ActionPolicy::Policy::Defaults module adds the following
21
-
22
- authorize :user
23
-
24
- default_rule :manage?
25
- alias_rule :new?, to: :create?
26
-
27
- def index?
28
- false
29
- end
30
-
31
- def create?
32
- false
33
- end
34
-
35
- def manage?
36
- false
37
- end
38
- end
39
- ```
40
-
41
-
42
-
43
- You can write your `ApplicationPolicy` from scratch instead of inheriting from `ActionPolicy::Base`
44
- if the defaults above do not fit your needs. The only required component is `ActionPolicy::Policy::Core`:
45
-
46
- ```ruby
47
- # minimal ApplicationPolicy
48
- class ApplicationPolicy
49
- include ActionPolicy::Policy::Core
50
- end
51
- ```
52
-
53
- The `Core` module provides `apply` and `allowed_to?` methods.
@@ -1,55 +0,0 @@
1
- # Debug Helpers
2
-
3
- **NOTE:** this functionality requires two additional gems to be available in the app:
4
- - [unparser](https://github.com/mbj/unparser)
5
- - [method_source](https://github.com/banister/method_source).
6
-
7
- We usually describe policy rules using _boolean expressions_ (e.g. `A or (B and C)` where each of `A`, `B` and `C` is a simple boolean expression or predicate method).
8
-
9
- When dealing with complex policies, it could be hard to figure out which predicate/check made policy to fail.
10
-
11
- The `Policy#pp(rule)` method aims to help debug such situations.
12
-
13
- Consider a (synthetic) example:
14
-
15
- ```ruby
16
- def feed?
17
- (admin? || allowed_to?(:access_feed?)) &&
18
- (user.name == "Jack" || user.name == "Kate")
19
- end
20
-
21
- ```
22
-
23
- Suppose that you want to debug this rule ("Why does it return false?").
24
- You can drop a [`binding.pry`](https://github.com/deivid-rodriguez/pry-byebug) (or `binding.irb`) right at the beginning of the method:
25
-
26
- ```ruby
27
- def feed?
28
- binding.pry # rubocop:disable Lint/Debugger
29
- #...
30
- end
31
- ```
32
-
33
- Now, run your code and trigger the breakpoint (i.e., run the method):
34
-
35
- ```
36
- # now you can preview the execution of the rule using the `pp` method (defined on the policy instance)
37
- pry> pp :feed?
38
- MyPolicy#feed?
39
- ↳ (
40
- admin? #=> false
41
- OR
42
- allowed_to?(:access_feed?) #=> true
43
- )
44
- AND
45
- (
46
- user.name == "Jack" #=> false
47
- OR
48
- user.name == "Kate" #=> true
49
- )
50
-
51
- # you can also check other rules or methods as well
52
- pry> pp :admin?
53
- MyPolicy#admin?
54
- ↳ user.admin? #=> false
55
- ```
@@ -1,27 +0,0 @@
1
- # Dealing with Decorators
2
-
3
- Ref: [action_policy#7](https://github.com/palkan/action_policy/issues/7).
4
-
5
- Since Action Policy [lookup mechanism](./lookup_chain.md) relies on the target
6
- record's class properties (names, methods) it could break when using with _decorators_.
7
-
8
- To make `authorize!` and other [behaviour](./behaviour.md) methods work seamlessly with decorated
9
- objects, you might want to _enhance_ the `policy_for` method.
10
-
11
- For example, when using the [Draper](https://github.com/drapergem/draper) gem:
12
-
13
- ```ruby
14
- module ActionPolicy
15
- module Draper
16
- def policy_for(record:, **opts)
17
- # From https://github.com/GoodMeasuresLLC/draper-cancancan/blob/master/lib/draper/cancancan.rb
18
- record = record.model while record.is_a?(::Draper::Decorator)
19
- super(record: record, **opts)
20
- end
21
- end
22
- end
23
-
24
- class ApplicationController < ActionController::Base
25
- prepend ActionPolicy::Draper
26
- end
27
- ```
Binary file
@@ -1,302 +0,0 @@
1
- # GraphQL integration
2
-
3
- You can use Action Policy as an authorization library for your [GraphQL Ruby](https://graphql-ruby.org/) application via the [`action_policy-graphql` gem](https://github.com/palkan/action_policy-graphql).
4
-
5
- This integration provides the following features:
6
- - Fields & mutations authorization
7
- - List and connections scoping
8
- - [**Exposing permissions/authorization rules in the API**](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy).
9
-
10
- ## Getting Started
11
-
12
- First, add the `action_policy-graphql` gem to your Gemfile (see [installation instructions](https://github.com/palkan/action_policy-graphql#installation)).
13
-
14
- Then, include `ActionPolicy::GraphQL::Behaviour` to your base type (or any other type/mutation where you want to use authorization features):
15
-
16
- ```ruby
17
- # For fields authorization, lists scoping and rules exposing
18
- class Types::BaseObject < GraphQL::Schema::Object
19
- include ActionPolicy::GraphQL::Behaviour
20
- end
21
-
22
- # For using authorization helpers in mutations
23
- class Types::BaseMutation < GraphQL::Schema::Mutation
24
- include ActionPolicy::GraphQL::Behaviour
25
- end
26
-
27
- # For using authorization helpers in resolvers
28
- class Types::BaseResolver < GraphQL::Schema::Resolver
29
- include ActionPolicy::GraphQL::Behaviour
30
- end
31
- ```
32
-
33
- ## Authorization Context
34
-
35
- By default, Action Policy uses `context[:current_user]` as the `user` [authorization context](./authorization_context.md).
36
-
37
- **NOTE:** see below for more information on what's included into `ActionPolicy::GraphQL::Behaviour`.
38
-
39
- ## Authorizing Fields
40
-
41
- You can add `authorize: true` option to any field (=underlying object) to protect the access (it's equal to calling `authorize! object, to: :show?`):
42
-
43
- ```ruby
44
- # authorization could be useful for find-like methods,
45
- # where the object is resolved from the provided params (e.g., ID)
46
- field :home, Home, null: false, authorize: true do
47
- argument :id, ID, required: true
48
- end
49
-
50
- def home(id:)
51
- Home.find(id)
52
- end
53
-
54
- # Without `authorize: true` the code would look like this
55
- def home(id:)
56
- Home.find(id).tap { |home| authorize! home, to: :show? }
57
- end
58
- ```
59
-
60
- You can use authorization options to customize the behaviour, e.g. `authorize: {to: :preview?, with: CustomPolicy}`.
61
-
62
- By default, if a user is not authorized to access the field, an `ActionPolicy::Unauthorized` exception is raised.
63
-
64
- If you want to return a `nil` instead, you should add `raise: false` to the options:
65
-
66
- ```ruby
67
- # NOTE: don't forget to mark your field as nullable
68
- field :home, Home, null: true, authorize: {raise: false}
69
- ```
70
-
71
- You can make non-raising behaviour a default by setting a configuration option:
72
-
73
- ```ruby
74
- ActionPolicy::GraphQL.authorize_raise_exception = false
75
- ```
76
-
77
- You can also change the default `show?` rule globally:
78
-
79
- ```ruby
80
- ActionPolicy::GraphQL.default_authorize_rule = :show_graphql_field?
81
- ```
82
-
83
- If you want to perform authorization before resolving the field value, you can use `preauthorize: *` option:
84
-
85
- ```ruby
86
- field :homes, [Home], null: false, preauthorize: {with: HomePolicy}
87
-
88
- def homes
89
- Home.all
90
- end
91
- ```
92
-
93
- The code above is equal to:
94
-
95
- ```ruby
96
- field :homes, [Home], null: false
97
-
98
- def homes
99
- authorize! "homes", to: :index?, with: HomePolicy
100
- Home.all
101
- end
102
- ```
103
-
104
- **NOTE:** we pass the field's name as the `record` to the policy rule. We assume that preauthorization rules do not depend on
105
- the record itself and pass the field's name for debugging purposes only.
106
-
107
- You can customize the authorization options, e.g. `authorize: {to: :preview?, with: CustomPolicy}`.
108
-
109
- **NOTE:** unlike `authorize: *` you MUST specify the `with: SomePolicy` option.
110
- The default authorization rule depends on the type of the field:
111
-
112
- - for lists we use `index?` (configured by `ActionPolicy::GraphQL.default_preauthorize_list_rule` parameter)
113
- - for _singleton_ fields we use `show?` (configured by `ActionPolicy::GraphQL.default_preauthorize_node_rule` parameter)
114
-
115
- ### Class-level authorization
116
-
117
- You can use Action Policy in the class-level [authorization hooks](https://graphql-ruby.org/authorization/authorization.html) (`self.authorized?`) like this:
118
-
119
- ```ruby
120
- class Types::Friendship < Types::BaseObject
121
- def self.authorized?(object, context)
122
- super &&
123
- allowed_to?(
124
- :show?,
125
- object,
126
- # NOTE: you must provide context explicitly
127
- context: {user: context[:current_user]}
128
- )
129
- end
130
- end
131
- ```
132
-
133
- ## Authorizing Mutations
134
-
135
- A mutation is just a Ruby class with a single API method. There is nothing specific in authorizing mutations: from the Action Policy point of view, they are just [_behaviours_](./behaviour.md).
136
-
137
- If you want to authorize the mutation, you call `authorize!` method. For example:
138
-
139
- ```ruby
140
- class Mutations::DestroyUser < Types::BaseMutation
141
- argument :id, ID, required: true
142
-
143
- def resolve(id:)
144
- user = User.find(id)
145
-
146
- # Raise an exception if the user has not enough permissions
147
- authorize! user, to: :destroy?
148
- # Or check without raising and do what you want
149
- #
150
- # if allowed_to?(:destroy?, user)
151
-
152
- user.destroy!
153
-
154
- {deleted_id: user.id}
155
- end
156
- end
157
- ```
158
-
159
- ## Handling exceptions
160
-
161
- The query would fail with `ActionPolicy::Unauthorized` exception when using `authorize: true` (in raising mode) or calling `authorize!` explicitly.
162
-
163
- That could be useful to handle this exception and send a more detailed error message to the client, for example:
164
-
165
- ```ruby
166
- # in your schema file
167
- rescue_from(ActionPolicy::Unauthorized) do |exp|
168
- raise GraphQL::ExecutionError.new(
169
- # use result.message (backed by i18n) as an error message
170
- exp.result.message,
171
- # use GraphQL error extensions to provide more context
172
- extensions: {
173
- code: :unauthorized,
174
- fullMessages: exp.result.reasons.full_messages,
175
- details: exp.result.reasons.details
176
- }
177
- )
178
- end
179
- ```
180
-
181
- ## Scoping Data
182
-
183
- You can add `authorized_scope: true` option to a field (list or [_connection_](https://graphql-ruby.org/relay/connections.html)) to apply the corresponding policy rules to the data:
184
-
185
- ```ruby
186
- class CityType < ::Common::Graphql::Type
187
- # It would automatically apply the relation scope from the EventPolicy to
188
- # the relation (city.events)
189
- field :events, EventType.connection_type,
190
- null: false,
191
- authorized_scope: true
192
-
193
- # you can specify the policy explicitly
194
- field :events, EventType.connection_type,
195
- null: false,
196
- authorized_scope: {with: CustomEventPolicy}
197
-
198
- # without the option you would write the following code
199
- def events
200
- authorized_scope object.events
201
- # or if `with` option specified
202
- authorized_scope object.events, with: CustomEventPolicy
203
- end
204
- end
205
- ```
206
-
207
- **NOTE:** you cannot use `authorize: *` and `authorized_scope: *` at the same time but you can combine `preauthorize: *` with `authorized_scope: *`.
208
-
209
- See the documenation on [scoping](./scoping.md).
210
-
211
- ## Exposing Authorization Rules
212
-
213
- With `action_policy-graphql` gem, you can easily expose your authorization logic to the client in a standardized way.
214
-
215
- For example, if you want to "tell" the client which actions could be performed against the object you
216
- can use the `expose_authorization_rules` macro to add authorization-related fields to your type:
217
-
218
- ```ruby
219
- class ProfileType < Types::BaseType
220
- # Adds can_edit, can_destroy fields with
221
- # AuthorizationResult type.
222
-
223
- # NOTE: prefix "can_" is used by default, no need to specify it explicitly
224
- expose_authorization_rules :edit?, :destroy?, prefix: "can_"
225
- end
226
- ```
227
-
228
- **NOTE:** you can use [aliases](./aliases.md) here as well as defined rules.
229
-
230
- **NOTE:** This feature relies the [_failure reasons_](./reasons.md) and
231
- the [i18n integration](./i18n.md) extensions. If your policies don't include any of these,
232
- you won't be able to use it.
233
-
234
- Then the client could perform the following query:
235
-
236
- ```gql
237
- {
238
- post(id: $id) {
239
- canEdit {
240
- # (bool) true|false; not null
241
- value
242
- # top-level decline message ("Not authorized" by default); null if value is true
243
- message
244
- # detailed information about the decline reasons; null if value is true or you don't have "failure reasons" extension enabled
245
- reasons {
246
- details # JSON-encoded hash of the form { "event" => [:privacy_off?] }
247
- fullMessages # Array of human-readable reasons
248
- }
249
- }
250
-
251
- canDestroy {
252
- # ...
253
- }
254
- }
255
- }
256
- ```
257
-
258
- You can override a custom authorization field prefix (`can_`):
259
-
260
- ```ruby
261
- ActionPolicy::GraphQL.default_authorization_field_prefix = "allowed_to_"
262
- ```
263
-
264
- You can specify a custom field name as well (only for a single rule):
265
-
266
- ```ruby
267
- class ProfileType < ::Common::Graphql::Type
268
- # Adds can_create_post field.
269
-
270
- expose_authorization_rules :create?, with: PostPolicy, field_name: "can_create_post"
271
- end
272
- ```
273
-
274
- ## Custom Behaviour
275
-
276
- Including the default `ActionPolicy::GraphQL::Behaviour` is equal to adding the following to your base class:
277
-
278
- ```ruby
279
- class Types::BaseObject < GraphQL::Schema::Object
280
- # include Action Policy behaviour and its extensions
281
- include ActionPolicy::Behaviour
282
- include ActionPolicy::Behaviours::ThreadMemoized
283
- include ActionPolicy::Behaviours::Memoized
284
- include ActionPolicy::Behaviours::Namespaced
285
-
286
- # define authorization context
287
- authorize :user, through: :current_user
288
-
289
- # add a method helper to get the current_user from the context
290
- def current_user
291
- context[:current_user]
292
- end
293
-
294
- # extend the field class to add `authorize` and `authorized_scope` options
295
- field_class.prepend(ActionPolicy::GraphQL::AuthorizedField)
296
-
297
- # add `expose_authorization_rules` macro
298
- include ActionPolicy::GraphQL::Fields
299
- end
300
- ```
301
-
302
- Feel free to create your own behaviour by adding only the functionality you need.