pundit 1.1.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +30 -49
- data/.travis.yml +20 -10
- data/CHANGELOG.md +42 -0
- data/Gemfile +4 -1
- data/LICENSE.txt +1 -1
- data/README.md +241 -60
- data/Rakefile +2 -1
- data/lib/generators/pundit/install/install_generator.rb +1 -1
- data/lib/generators/pundit/install/templates/application_policy.rb +2 -6
- data/lib/generators/pundit/policy/policy_generator.rb +1 -1
- data/lib/generators/pundit/policy/templates/policy.rb +1 -1
- data/lib/generators/rspec/policy_generator.rb +1 -1
- data/lib/generators/rspec/templates/policy_spec.rb +1 -2
- data/lib/generators/test_unit/policy_generator.rb +1 -1
- data/lib/generators/test_unit/templates/policy_test.rb +0 -1
- data/lib/pundit.rb +103 -64
- data/lib/pundit/policy_finder.rb +27 -31
- data/lib/pundit/rspec.rb +13 -7
- data/lib/pundit/version.rb +3 -1
- data/pundit.gemspec +10 -9
- data/spec/policies/post_policy_spec.rb +2 -0
- data/spec/policy_finder_spec.rb +124 -0
- data/spec/pundit_spec.rb +174 -53
- data/spec/spec_helper.rb +87 -11
- metadata +26 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 371516754ff155f90b2093a0ce80aacf097ab555027b19ea22b7c823de72a66a
|
4
|
+
data.tar.gz: 41e69a7d6a317b46ad35d1d1485d2119b443b8a430e5c78e62935ec502c7d08f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c77a792bec5d87f487fd3ee419d00745dcab754bd1bd38504d9987b71d80be3bd32fb1aab8419a8e63ef3c3718e1bd8a255ff0117be8f8a5c743c221d87fccdd
|
7
|
+
data.tar.gz: 3086b4036cdbafb499f462f22405f185c83d12c8d8175136531dd053733320574b3d5d05c8379895940d854d54d7abb59d6a0958a9d0e6fdfc03f7691883c3ab
|
data/.rubocop.yml
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
TargetRubyVersion: 2.2
|
2
4
|
Exclude:
|
3
5
|
- "gemfiles/**/*"
|
4
6
|
- "vendor/**/*"
|
5
7
|
- "lib/generators/**/*"
|
6
8
|
|
9
|
+
Metrics/BlockLength:
|
10
|
+
Exclude:
|
11
|
+
- "**/*_spec.rb"
|
12
|
+
|
7
13
|
Metrics/MethodLength:
|
8
14
|
Max: 40
|
9
15
|
|
10
16
|
Metrics/ModuleLength:
|
11
17
|
Max: 200
|
18
|
+
Exclude:
|
19
|
+
- "**/*_spec.rb"
|
12
20
|
|
13
21
|
Metrics/LineLength:
|
14
22
|
Max: 120
|
@@ -22,74 +30,47 @@ Metrics/CyclomaticComplexity:
|
|
22
30
|
Metrics/PerceivedComplexity:
|
23
31
|
Enabled: false
|
24
32
|
|
25
|
-
|
26
|
-
Enabled: false
|
27
|
-
|
28
|
-
Style/AlignParameters:
|
33
|
+
Layout/AlignParameters:
|
29
34
|
EnforcedStyle: with_fixed_indentation
|
30
35
|
|
31
|
-
|
32
|
-
EnforcedStyle:
|
33
|
-
|
34
|
-
Style/StringLiteralsInInterpolation:
|
35
|
-
EnforcedStyle: double_quotes
|
36
|
-
|
37
|
-
Style/ClosingParenthesisIndentation:
|
38
|
-
Enabled: false
|
39
|
-
|
40
|
-
Style/OneLineConditional:
|
41
|
-
Enabled: false
|
42
|
-
|
43
|
-
Style/AndOr:
|
44
|
-
Enabled: false
|
45
|
-
|
46
|
-
Style/Not:
|
47
|
-
Enabled: false
|
48
|
-
|
49
|
-
Documentation:
|
50
|
-
Enabled: false # TODO: Enable again once we have more docs
|
51
|
-
|
52
|
-
Style/CaseIndentation:
|
53
|
-
IndentWhenRelativeTo: case
|
36
|
+
Layout/CaseIndentation:
|
37
|
+
EnforcedStyle: case
|
54
38
|
SupportedStyles:
|
55
39
|
- case
|
56
40
|
- end
|
57
41
|
IndentOneStep: true
|
58
42
|
|
59
|
-
|
60
|
-
PreferredDelimiters:
|
61
|
-
'%w': "[]"
|
62
|
-
'%W': "[]"
|
63
|
-
|
64
|
-
Style/AccessModifierIndentation:
|
43
|
+
Layout/AccessModifierIndentation:
|
65
44
|
EnforcedStyle: outdent
|
66
45
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
Style/IndentationWidth:
|
71
|
-
Enabled: false
|
46
|
+
Layout/EndAlignment:
|
47
|
+
EnforcedStyleAlignWith: variable
|
72
48
|
|
73
|
-
Style/
|
74
|
-
|
49
|
+
Style/FrozenStringLiteralComment:
|
50
|
+
Enabled: true
|
75
51
|
|
76
|
-
|
77
|
-
|
52
|
+
Style/PercentLiteralDelimiters:
|
53
|
+
PreferredDelimiters:
|
54
|
+
'%w': "[]"
|
55
|
+
'%W': "[]"
|
78
56
|
|
79
|
-
|
80
|
-
|
57
|
+
Style/StringLiterals:
|
58
|
+
EnforcedStyle: double_quotes
|
81
59
|
|
82
|
-
|
83
|
-
|
60
|
+
Style/StringLiteralsInInterpolation:
|
61
|
+
EnforcedStyle: double_quotes
|
84
62
|
|
85
|
-
Style/
|
63
|
+
Style/StructInheritance:
|
86
64
|
Enabled: false
|
87
65
|
|
88
|
-
Style/
|
66
|
+
Style/AndOr:
|
89
67
|
Enabled: false
|
90
68
|
|
91
|
-
Style/
|
69
|
+
Style/Not:
|
92
70
|
Enabled: false
|
93
71
|
|
94
72
|
Style/DoubleNegation:
|
95
73
|
Enabled: false
|
74
|
+
|
75
|
+
Documentation:
|
76
|
+
Enabled: false # TODO: Enable again once we have more docs
|
data/.travis.yml
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
language: ruby
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
before_install:
|
3
|
+
- gem install bundler -v 1.17.3
|
4
|
+
|
5
|
+
matrix:
|
6
|
+
include:
|
7
|
+
- rvm: 2.5.1 # Pre-installed Ruby version
|
8
|
+
script: bundle exec rake rubocop # ONLY lint once, first
|
9
|
+
- rvm: 2.1
|
10
|
+
- rvm: 2.2
|
11
|
+
- rvm: 2.3.5
|
12
|
+
- rvm: 2.4.6
|
13
|
+
- rvm: 2.5.5
|
14
|
+
- rvm: 2.6.3
|
15
|
+
- rvm: jruby-9.1.8.0
|
16
|
+
env:
|
17
|
+
- JRUBY_OPTS="--debug"
|
18
|
+
jdk: openjdk8
|
19
|
+
- rvm: jruby-9.2.8.0
|
20
|
+
env:
|
21
|
+
- JRUBY_OPTS="--debug"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
+
### Fixed
|
4
|
+
|
5
|
+
- Avoid name clashes with the Error class. (#590)
|
6
|
+
|
7
|
+
### Changed
|
8
|
+
|
9
|
+
- Return a safer default NotAuthorizedError message. (#583)
|
10
|
+
|
11
|
+
## 2.0.1 (2019-01-18)
|
12
|
+
|
13
|
+
### Breaking changes
|
14
|
+
|
15
|
+
None
|
16
|
+
|
17
|
+
### Other changes
|
18
|
+
|
19
|
+
- Improve exception handling for `#policy_scope` and `#policy_scope!`. (#550)
|
20
|
+
- Add `:policy` metadata to RSpec template. (#566)
|
21
|
+
|
22
|
+
## 2.0.0 (2018-07-21)
|
23
|
+
|
24
|
+
No changes since beta1
|
25
|
+
|
26
|
+
## 2.0.0.beta1 (2018-07-04)
|
27
|
+
|
28
|
+
### Breaking changes
|
29
|
+
|
30
|
+
- Only pass last element of "namespace array" to policy and scope. (#529)
|
31
|
+
- Raise `InvalidConstructorError` if a policy or policy scope with an invalid constructor is called. (#462)
|
32
|
+
- Return passed object from `#authorize` method to make chaining possible. (#385)
|
33
|
+
|
34
|
+
### Other changes
|
35
|
+
|
36
|
+
- Add `policy_class` option to `authorize` to be able to override the policy. (#441)
|
37
|
+
- Add `policy_scope_class` option to `authorize` to be able to override the policy scope. (#441)
|
38
|
+
- Fix `param_key` issue when passed an array. (#529)
|
39
|
+
- Allow specification of a `NilClassPolicy`. (#525)
|
40
|
+
- Make sure `policy_class` override is called when passed an array. (#475)
|
41
|
+
|
42
|
+
- Use `action_name` instead of `params[:action]`. (#419)
|
43
|
+
- Add `pundit_params_for` method to make it easy to customize params fetching. (#502)
|
44
|
+
|
3
45
|
## 1.1.0 (2016-01-14)
|
4
46
|
|
5
47
|
- Can retrieve policies via an array of symbols/objects.
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
-
[![Build Status](https://secure.travis-ci.org/
|
4
|
-
[![Code Climate](https://codeclimate.com/github/
|
5
|
-
[![Inline docs](http://inch-ci.org/github/
|
3
|
+
[![Build Status](https://secure.travis-ci.org/varvet/pundit.svg?branch=master)](https://travis-ci.org/varvet/pundit)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/varvet/pundit.svg)](https://codeclimate.com/github/varvet/pundit)
|
5
|
+
[![Inline docs](http://inch-ci.org/github/varvet/pundit.svg?branch=master)](http://inch-ci.org/github/varvet/pundit)
|
6
6
|
[![Gem Version](https://badge.fury.io/rb/pundit.svg)](http://badge.fury.io/rb/pundit)
|
7
7
|
|
8
8
|
Pundit provides a set of helpers which guide you in leveraging regular Ruby
|
@@ -12,13 +12,13 @@ scaleable authorization system.
|
|
12
12
|
Links:
|
13
13
|
|
14
14
|
- [API documentation](http://www.rubydoc.info/gems/pundit)
|
15
|
-
- [Source Code](https://github.com/
|
16
|
-
- [Contributing](https://github.com/
|
17
|
-
- [Code of Conduct](https://github.com/
|
15
|
+
- [Source Code](https://github.com/varvet/pundit)
|
16
|
+
- [Contributing](https://github.com/varvet/pundit/blob/master/CONTRIBUTING.md)
|
17
|
+
- [Code of Conduct](https://github.com/varvet/pundit/blob/master/CODE_OF_CONDUCT.md)
|
18
18
|
|
19
19
|
Sponsored by:
|
20
20
|
|
21
|
-
[<img src="
|
21
|
+
[<img src="https://www.varvet.com/images/wordmark-red.svg" alt="Varvet" height="50px"/>](https://www.varvet.com)
|
22
22
|
|
23
23
|
## Installation
|
24
24
|
|
@@ -116,7 +116,9 @@ and the given record. It then infers from the action name, that it should call
|
|
116
116
|
`authorize` would have done something like this:
|
117
117
|
|
118
118
|
``` ruby
|
119
|
-
|
119
|
+
unless PostPolicy.new(current_user, @post).update?
|
120
|
+
raise Pundit::NotAuthorizedError, "not allowed to update? this #{@post.inspect}"
|
121
|
+
end
|
120
122
|
```
|
121
123
|
|
122
124
|
You can pass a second argument to `authorize` if the name of the permission you
|
@@ -131,6 +133,18 @@ def publish
|
|
131
133
|
end
|
132
134
|
```
|
133
135
|
|
136
|
+
You can pass an argument to override the policy class if necessary. For example:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
def create
|
140
|
+
@publication = find_publication # assume this method returns any model that behaves like a publication
|
141
|
+
# @publication.class => Post
|
142
|
+
authorize @publication, policy_class: PublicationPolicy
|
143
|
+
@publication.publish!
|
144
|
+
redirect_to @publication
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
134
148
|
If you don't have an instance for the first argument to `authorize`, then you can pass
|
135
149
|
the class. For example:
|
136
150
|
|
@@ -151,6 +165,15 @@ def admin_list
|
|
151
165
|
end
|
152
166
|
```
|
153
167
|
|
168
|
+
`authorize` returns the object passed to it, so you can chain it like this:
|
169
|
+
|
170
|
+
Controller:
|
171
|
+
```ruby
|
172
|
+
def show
|
173
|
+
@user = authorize User.find(params[:id])
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
154
177
|
You can easily get a hold of an instance of the policy through the `policy`
|
155
178
|
method in both the view and controller. This is especially useful for
|
156
179
|
conditionally showing links or buttons in the view:
|
@@ -172,6 +195,10 @@ class DashboardPolicy < Struct.new(:user, :dashboard)
|
|
172
195
|
end
|
173
196
|
```
|
174
197
|
|
198
|
+
Note that the headless policy still needs to accept two arguments. The
|
199
|
+
second argument will just be the symbol `:dashboard` in this case which
|
200
|
+
is what is passed as the record to `authorize` below.
|
201
|
+
|
175
202
|
```ruby
|
176
203
|
# In controllers
|
177
204
|
authorize :dashboard, :show?
|
@@ -184,54 +211,6 @@ authorize :dashboard, :show?
|
|
184
211
|
<% end %>
|
185
212
|
```
|
186
213
|
|
187
|
-
## Ensuring policies are used
|
188
|
-
|
189
|
-
Pundit adds a method called `verify_authorized` to your controllers. This
|
190
|
-
method will raise an exception if `authorize` has not yet been called. You
|
191
|
-
should run this method in an `after_action` to ensure that you haven't
|
192
|
-
forgotten to authorize the action. For example:
|
193
|
-
|
194
|
-
``` ruby
|
195
|
-
class ApplicationController < ActionController::Base
|
196
|
-
after_action :verify_authorized
|
197
|
-
end
|
198
|
-
```
|
199
|
-
|
200
|
-
Likewise, Pundit also adds `verify_policy_scoped` to your controller. This
|
201
|
-
will raise an exception in the vein of `verify_authorized`. However, it tracks
|
202
|
-
if `policy_scope` is used instead of `authorize`. This is mostly useful for
|
203
|
-
controller actions like `index` which find collections with a scope and don't
|
204
|
-
authorize individual instances.
|
205
|
-
|
206
|
-
``` ruby
|
207
|
-
class ApplicationController < ActionController::Base
|
208
|
-
after_action :verify_authorized, except: :index
|
209
|
-
after_action :verify_policy_scoped, only: :index
|
210
|
-
end
|
211
|
-
```
|
212
|
-
|
213
|
-
If you're using `verify_authorized` in your controllers but need to
|
214
|
-
conditionally bypass verification, you can use `skip_authorization`. For
|
215
|
-
bypassing `verify_policy_scoped`, use `skip_policy_scope`. These are useful
|
216
|
-
in circumstances where you don't want to disable verification for the
|
217
|
-
entire action, but have some cases where you intend to not authorize.
|
218
|
-
|
219
|
-
```ruby
|
220
|
-
def show
|
221
|
-
record = Record.find_by(attribute: "value")
|
222
|
-
if record.present?
|
223
|
-
authorize record
|
224
|
-
else
|
225
|
-
skip_authorization
|
226
|
-
end
|
227
|
-
end
|
228
|
-
```
|
229
|
-
|
230
|
-
If you need to perform some more sophisticated logic or you want to raise a custom
|
231
|
-
exception you can use the two lower level methods `pundit_policy_authorized?`
|
232
|
-
and `pundit_policy_scoped?` which return `true` or `false` depending on whether
|
233
|
-
`authorize` or `policy_scope` have been called, respectively.
|
234
|
-
|
235
214
|
## Scopes
|
236
215
|
|
237
216
|
Often, you will want to have some kind of view listing records which a
|
@@ -258,7 +237,7 @@ class PostPolicy < ApplicationPolicy
|
|
258
237
|
end
|
259
238
|
|
260
239
|
def update?
|
261
|
-
user.admin? or not
|
240
|
+
user.admin? or not record.published?
|
262
241
|
end
|
263
242
|
end
|
264
243
|
```
|
@@ -291,7 +270,7 @@ class PostPolicy < ApplicationPolicy
|
|
291
270
|
end
|
292
271
|
|
293
272
|
def update?
|
294
|
-
user.admin? or not
|
273
|
+
user.admin? or not record.published?
|
295
274
|
end
|
296
275
|
end
|
297
276
|
```
|
@@ -302,6 +281,19 @@ You can now use this class from your controller via the `policy_scope` method:
|
|
302
281
|
def index
|
303
282
|
@posts = policy_scope(Post)
|
304
283
|
end
|
284
|
+
|
285
|
+
def show
|
286
|
+
@post = policy_scope(Post).find(params[:id])
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
290
|
+
Like with the authorize method, you can also override the policy scope class:
|
291
|
+
|
292
|
+
``` ruby
|
293
|
+
def index
|
294
|
+
# publication_class => Post
|
295
|
+
@publications = policy_scope(publication_class, policy_scope_class: PublicationPolicy::Scope)
|
296
|
+
end
|
305
297
|
```
|
306
298
|
|
307
299
|
Just as with your policy, this will automatically infer that you want to use
|
@@ -322,6 +314,70 @@ You can, and are encouraged to, use this method in views:
|
|
322
314
|
<% end %>
|
323
315
|
```
|
324
316
|
|
317
|
+
## Ensuring policies and scopes are used
|
318
|
+
|
319
|
+
When you are developing an application with Pundit it can be easy to forget to
|
320
|
+
authorize some action. People are forgetful after all. Since Pundit encourages
|
321
|
+
you to add the `authorize` call manually to each controller action, it's really
|
322
|
+
easy to miss one.
|
323
|
+
|
324
|
+
Thankfully, Pundit has a handy feature which reminds you in case you forget.
|
325
|
+
Pundit tracks whether you have called `authorize` anywhere in your controller
|
326
|
+
action. Pundit also adds a method to your controllers called
|
327
|
+
`verify_authorized`. This method will raise an exception if `authorize` has not
|
328
|
+
yet been called. You should run this method in an `after_action` hook to ensure
|
329
|
+
that you haven't forgotten to authorize the action. For example:
|
330
|
+
|
331
|
+
``` ruby
|
332
|
+
class ApplicationController < ActionController::Base
|
333
|
+
include Pundit
|
334
|
+
after_action :verify_authorized
|
335
|
+
end
|
336
|
+
```
|
337
|
+
|
338
|
+
Likewise, Pundit also adds `verify_policy_scoped` to your controller. This
|
339
|
+
will raise an exception similar to `verify_authorized`. However, it tracks
|
340
|
+
if `policy_scope` is used instead of `authorize`. This is mostly useful for
|
341
|
+
controller actions like `index` which find collections with a scope and don't
|
342
|
+
authorize individual instances.
|
343
|
+
|
344
|
+
``` ruby
|
345
|
+
class ApplicationController < ActionController::Base
|
346
|
+
include Pundit
|
347
|
+
after_action :verify_authorized, except: :index
|
348
|
+
after_action :verify_policy_scoped, only: :index
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
**This verification mechanism only exists to aid you while developing your
|
353
|
+
application, so you don't forget to call `authorize`. It is not some kind of
|
354
|
+
failsafe mechanism or authorization mechanism. You should be able to remove
|
355
|
+
these filters without affecting how your app works in any way.**
|
356
|
+
|
357
|
+
Some people have found this feature confusing, while many others
|
358
|
+
find it extremely helpful. If you fall into the category of people who find it
|
359
|
+
confusing then you do not need to use it. Pundit will work just fine without
|
360
|
+
using `verify_authorized` and `verify_policy_scoped`.
|
361
|
+
|
362
|
+
### Conditional verification
|
363
|
+
|
364
|
+
If you're using `verify_authorized` in your controllers but need to
|
365
|
+
conditionally bypass verification, you can use `skip_authorization`. For
|
366
|
+
bypassing `verify_policy_scoped`, use `skip_policy_scope`. These are useful
|
367
|
+
in circumstances where you don't want to disable verification for the
|
368
|
+
entire action, but have some cases where you intend to not authorize.
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
def show
|
372
|
+
record = Record.find_by(attribute: "value")
|
373
|
+
if record.present?
|
374
|
+
authorize record
|
375
|
+
else
|
376
|
+
skip_authorization
|
377
|
+
end
|
378
|
+
end
|
379
|
+
```
|
380
|
+
|
325
381
|
## Manually specifying policy classes
|
326
382
|
|
327
383
|
Sometimes you might want to explicitly declare which policy to use for a given
|
@@ -362,7 +418,8 @@ rails g pundit:policy post
|
|
362
418
|
|
363
419
|
In many applications, only logged in users are really able to do anything. If
|
364
420
|
you're building such a system, it can be kind of cumbersome to check that the
|
365
|
-
user in a policy isn't `nil` for every single permission.
|
421
|
+
user in a policy isn't `nil` for every single permission. Aside from policies,
|
422
|
+
you can add this check to the base class for scopes.
|
366
423
|
|
367
424
|
We suggest that you define a filter that redirects unauthenticated users to the
|
368
425
|
login page. As a secondary defence, if you've defined an ApplicationPolicy, it
|
@@ -376,6 +433,37 @@ class ApplicationPolicy
|
|
376
433
|
@user = user
|
377
434
|
@record = record
|
378
435
|
end
|
436
|
+
|
437
|
+
class Scope
|
438
|
+
attr_reader :user, :scope
|
439
|
+
|
440
|
+
def initialize(user, scope)
|
441
|
+
raise Pundit::NotAuthorizedError, "must be logged in" unless user
|
442
|
+
@user = user
|
443
|
+
@scope = scope
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
```
|
448
|
+
|
449
|
+
## NilClassPolicy
|
450
|
+
|
451
|
+
To support a [null object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern)
|
452
|
+
you may find that you want to implement a `NilClassPolicy`. This might be useful
|
453
|
+
where you want to extend your ApplicationPolicy to allow some tolerance of, for
|
454
|
+
example, associations which might be `nil`.
|
455
|
+
|
456
|
+
```ruby
|
457
|
+
class NilClassPolicy < ApplicationPolicy
|
458
|
+
class Scope < Scope
|
459
|
+
def resolve
|
460
|
+
raise Pundit::NotDefinedError, "Cannot scope NilClass"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def show?
|
465
|
+
false # Nobody can see nothing
|
466
|
+
end
|
379
467
|
end
|
380
468
|
```
|
381
469
|
|
@@ -402,6 +490,10 @@ class ApplicationController < ActionController::Base
|
|
402
490
|
end
|
403
491
|
```
|
404
492
|
|
493
|
+
Alternatively, you can globally handle Pundit::NotAuthorizedError's by having rails handle them as a 403 error and serving a 403 error page. Add the following to application.rb:
|
494
|
+
|
495
|
+
```config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden```
|
496
|
+
|
405
497
|
## Creating custom error messages
|
406
498
|
|
407
499
|
`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
|
@@ -469,6 +561,48 @@ def pundit_user
|
|
469
561
|
end
|
470
562
|
```
|
471
563
|
|
564
|
+
## Policy Namespacing
|
565
|
+
In some cases it might be helpful to have multiple policies that serve different contexts for a
|
566
|
+
resource. A prime example of this is the case where User policies differ from Admin policies. To
|
567
|
+
authorize with a namespaced policy, pass the namespace into the `authorize` helper in an array:
|
568
|
+
|
569
|
+
```ruby
|
570
|
+
authorize(post) # => will look for a PostPolicy
|
571
|
+
authorize([:admin, post]) # => will look for an Admin::PostPolicy
|
572
|
+
authorize([:foo, :bar, post]) # => will look for a Foo::Bar::PostPolicy
|
573
|
+
|
574
|
+
policy_scope(Post) # => will look for a PostPolicy::Scope
|
575
|
+
policy_scope([:admin, Post]) # => will look for an Admin::PostPolicy::Scope
|
576
|
+
policy_scope([:foo, :bar, Post]) # => will look for a Foo::Bar::PostPolicy::Scope
|
577
|
+
```
|
578
|
+
|
579
|
+
If you are using namespaced policies for something like Admin views, it can be useful to
|
580
|
+
override the `policy_scope` and `authorize` helpers in your `AdminController` to automatically
|
581
|
+
apply the namespacing:
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
class AdminController < ApplicationController
|
585
|
+
def policy_scope(scope)
|
586
|
+
super([:admin, scope])
|
587
|
+
end
|
588
|
+
|
589
|
+
def authorize(record, query = nil)
|
590
|
+
super([:admin, record], query)
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
class Admin::PostController < AdminController
|
595
|
+
def index
|
596
|
+
policy_scope(Post)
|
597
|
+
end
|
598
|
+
|
599
|
+
def show
|
600
|
+
post = Post.find(params[:id])
|
601
|
+
authorize(post)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
```
|
605
|
+
|
472
606
|
## Additional context
|
473
607
|
|
474
608
|
Pundit strongly encourages you to model your application in such a way that the
|
@@ -564,6 +698,45 @@ class PostsController < ApplicationController
|
|
564
698
|
end
|
565
699
|
```
|
566
700
|
|
701
|
+
If you want to permit different attributes based on the current action, you can define a `permitted_attributes_for_#{action}` method on your policy:
|
702
|
+
|
703
|
+
```ruby
|
704
|
+
# app/policies/post_policy.rb
|
705
|
+
class PostPolicy < ApplicationPolicy
|
706
|
+
def permitted_attributes_for_create
|
707
|
+
[:title, :body]
|
708
|
+
end
|
709
|
+
|
710
|
+
def permitted_attributes_for_edit
|
711
|
+
[:body]
|
712
|
+
end
|
713
|
+
end
|
714
|
+
```
|
715
|
+
|
716
|
+
If you have defined an action-specific method on your policy for the current action, the `permitted_attributes` helper will call it instead of calling `permitted_attributes` on your controller.
|
717
|
+
|
718
|
+
If you need to fetch parameters based on namespaces different from the suggested one, override the below method, in your controller, and return an instance of `ActionController::Parameters`.
|
719
|
+
|
720
|
+
```ruby
|
721
|
+
def pundit_params_for(record)
|
722
|
+
params.require(PolicyFinder.new(record).param_key)
|
723
|
+
end
|
724
|
+
```
|
725
|
+
|
726
|
+
For example:
|
727
|
+
|
728
|
+
```ruby
|
729
|
+
# If you don't want to use require
|
730
|
+
def pundit_params_for(record)
|
731
|
+
params.fetch(PolicyFinder.new(record).param_key, {})
|
732
|
+
end
|
733
|
+
|
734
|
+
# If you are using something like the JSON API spec
|
735
|
+
def pundit_params_for(_record)
|
736
|
+
params.fetch(:data, {}).fetch(:attributes, {})
|
737
|
+
end
|
738
|
+
```
|
739
|
+
|
567
740
|
## RSpec
|
568
741
|
|
569
742
|
### Policy Specs
|
@@ -600,14 +773,22 @@ end
|
|
600
773
|
An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
|
601
774
|
[excellent post](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) and implemented in the third party [pundit-matchers](https://github.com/chrisalley/pundit-matchers) gem.
|
602
775
|
|
776
|
+
### Scope Specs
|
777
|
+
|
778
|
+
Pundit does not provide a DSL for testing scopes. Just test it like a regular Ruby class!
|
779
|
+
|
603
780
|
# External Resources
|
604
781
|
|
605
782
|
- [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
|
606
783
|
- [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
|
607
784
|
- [Testing Pundit Policies with RSpec](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
|
608
|
-
- [Using Pundit outside of a Rails controller](https://github.com/
|
785
|
+
- [Using Pundit outside of a Rails controller](https://github.com/varvet/pundit/pull/136)
|
609
786
|
- [Straightforward Rails Authorization with Pundit](http://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
|
610
787
|
|
788
|
+
## Other implementations
|
789
|
+
|
790
|
+
- [Flask-Pundit](https://github.com/anurag90x/flask-pundit) (Python) is a [Flask](http://flask.pocoo.org/) extension "heavily inspired by" Pundit
|
791
|
+
|
611
792
|
# License
|
612
793
|
|
613
794
|
Licensed under the MIT license, see the separate LICENSE.txt file.
|