pundit 1.1.0 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +18 -10
- data/.travis.yml +19 -9
- data/CHANGELOG.md +13 -0
- data/Gemfile +13 -1
- data/README.md +233 -60
- data/Rakefile +0 -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 +0 -1
- 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 +84 -59
- data/lib/pundit/policy_finder.rb +25 -31
- data/lib/pundit/rspec.rb +11 -7
- data/lib/pundit/version.rb +3 -1
- data/pundit.gemspec +2 -11
- data/spec/policy_finder_spec.rb +122 -0
- data/spec/pundit_spec.rb +136 -32
- data/spec/spec_helper.rb +73 -11
- metadata +8 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 91c992d8bb2bc907e4c532fb868f0e6f4105d9f4a79f0871593ec374f6f173b0
|
4
|
+
data.tar.gz: a855c49b6ada65cd8d1309ed104d6d089236ec46fcade8e872b66be6b152a054
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17b426b7b0a1410809be20410d20f2f291d26d3fde05bb333b68464f720c7824989f5063ba30c107459debaabb572f9a682738a74f35ebc08c4b3b6638350a3a
|
7
|
+
data.tar.gz: e28ba68e5f8c1a430a4cb1335b0ad95ff2d4dc35d5c2842e74321aad26e42cf8f1c383b217cbac8aab8449f20ef1d9faf10fe1432d27287fce961bea04e53b4e
|
data/.rubocop.yml
CHANGED
@@ -1,14 +1,22 @@
|
|
1
1
|
AllCops:
|
2
|
+
DisplayCopNames: true
|
3
|
+
TargetRubyVersion: 2.1
|
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
|
@@ -25,7 +33,7 @@ Metrics/PerceivedComplexity:
|
|
25
33
|
Style/StructInheritance:
|
26
34
|
Enabled: false
|
27
35
|
|
28
|
-
|
36
|
+
Layout/AlignParameters:
|
29
37
|
EnforcedStyle: with_fixed_indentation
|
30
38
|
|
31
39
|
Style/StringLiterals:
|
@@ -34,7 +42,7 @@ Style/StringLiterals:
|
|
34
42
|
Style/StringLiteralsInInterpolation:
|
35
43
|
EnforcedStyle: double_quotes
|
36
44
|
|
37
|
-
|
45
|
+
Layout/ClosingParenthesisIndentation:
|
38
46
|
Enabled: false
|
39
47
|
|
40
48
|
Style/OneLineConditional:
|
@@ -49,8 +57,8 @@ Style/Not:
|
|
49
57
|
Documentation:
|
50
58
|
Enabled: false # TODO: Enable again once we have more docs
|
51
59
|
|
52
|
-
|
53
|
-
|
60
|
+
Layout/CaseIndentation:
|
61
|
+
EnforcedStyle: case
|
54
62
|
SupportedStyles:
|
55
63
|
- case
|
56
64
|
- end
|
@@ -61,22 +69,22 @@ Style/PercentLiteralDelimiters:
|
|
61
69
|
'%w': "[]"
|
62
70
|
'%W': "[]"
|
63
71
|
|
64
|
-
|
72
|
+
Layout/AccessModifierIndentation:
|
65
73
|
EnforcedStyle: outdent
|
66
74
|
|
67
75
|
Style/SignalException:
|
68
76
|
Enabled: false
|
69
77
|
|
70
|
-
|
78
|
+
Layout/IndentationWidth:
|
71
79
|
Enabled: false
|
72
80
|
|
73
81
|
Style/TrivialAccessors:
|
74
82
|
ExactNameMatch: true
|
75
83
|
|
76
|
-
|
77
|
-
|
84
|
+
Layout/EndAlignment:
|
85
|
+
EnforcedStyleAlignWith: variable
|
78
86
|
|
79
|
-
|
87
|
+
Layout/DefEndAlignment:
|
80
88
|
Enabled: false
|
81
89
|
|
82
90
|
Lint/HandleExceptions:
|
@@ -88,7 +96,7 @@ Style/SpecialGlobalVars:
|
|
88
96
|
Style/TrivialAccessors:
|
89
97
|
Enabled: false
|
90
98
|
|
91
|
-
|
99
|
+
Layout/IndentHash:
|
92
100
|
Enabled: false
|
93
101
|
|
94
102
|
Style/DoubleNegation:
|
data/.travis.yml
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
-
|
4
|
-
-
|
5
|
-
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
before_install:
|
4
|
+
- gem update --system
|
5
|
+
- gem install bundler
|
6
|
+
|
7
|
+
matrix:
|
8
|
+
include:
|
9
|
+
- rvm: 2.5.1
|
10
|
+
script: bundle exec rake rubocop # ONLY lint once, first
|
11
|
+
- rvm: 2.1
|
12
|
+
- rvm: 2.2.8
|
13
|
+
- rvm: 2.3.5
|
14
|
+
- rvm: 2.4.2
|
15
|
+
- rvm: 2.5.1
|
16
|
+
- rvm: jruby-9.1.8.0
|
17
|
+
env:
|
18
|
+
- JRUBY_OPTS="--debug"
|
19
|
+
- rvm: jruby-9.2.0.0
|
20
|
+
env:
|
21
|
+
- JRUBY_OPTS="--debug"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
+
## 2.0.0.beta1 (2018-07-04)
|
4
|
+
|
5
|
+
- Add `policy_class` option to `authorize` to be able to override the policy. (#441)
|
6
|
+
- Add `policy_scope_class` option to `authorize` to be able to override the policy scope. (#441)
|
7
|
+
- Fix `param_key` issue when passed an array. (#529)
|
8
|
+
- Only pass last element of "namespace array" to policy and scope. (#529)
|
9
|
+
- Allow specification of a `NilClassPolicy`. (#525)
|
10
|
+
- Make sure `policy_class` override is called when passed an array. (#475)
|
11
|
+
- Raise `InvalidConstructorError` if a policy or policy scope with an invalid constructor is called. (#462)
|
12
|
+
- Use `action_name` instead of `params[:action]`. (#419)
|
13
|
+
- Add `pundit_params_for` method to make it easy to customize params fetching. (#502)
|
14
|
+
- Return passed object from `#authorize` method to make chaining possible. (#385)
|
15
|
+
|
3
16
|
## 1.1.0 (2016-01-14)
|
4
17
|
|
5
18
|
- Can retrieve policies via an array of symbols/objects.
|
data/Gemfile
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
|
3
|
+
ruby RUBY_VERSION
|
4
|
+
|
4
5
|
gemspec
|
6
|
+
|
7
|
+
group :development, :test do
|
8
|
+
gem "actionpack"
|
9
|
+
gem "activemodel"
|
10
|
+
gem "bundler"
|
11
|
+
gem "pry"
|
12
|
+
gem "rake"
|
13
|
+
gem "rspec"
|
14
|
+
gem "rubocop"
|
15
|
+
gem "yard"
|
16
|
+
end
|
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:
|
@@ -184,54 +207,6 @@ authorize :dashboard, :show?
|
|
184
207
|
<% end %>
|
185
208
|
```
|
186
209
|
|
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
210
|
## Scopes
|
236
211
|
|
237
212
|
Often, you will want to have some kind of view listing records which a
|
@@ -258,7 +233,7 @@ class PostPolicy < ApplicationPolicy
|
|
258
233
|
end
|
259
234
|
|
260
235
|
def update?
|
261
|
-
user.admin? or not
|
236
|
+
user.admin? or not record.published?
|
262
237
|
end
|
263
238
|
end
|
264
239
|
```
|
@@ -291,7 +266,7 @@ class PostPolicy < ApplicationPolicy
|
|
291
266
|
end
|
292
267
|
|
293
268
|
def update?
|
294
|
-
user.admin? or not
|
269
|
+
user.admin? or not record.published?
|
295
270
|
end
|
296
271
|
end
|
297
272
|
```
|
@@ -302,6 +277,19 @@ You can now use this class from your controller via the `policy_scope` method:
|
|
302
277
|
def index
|
303
278
|
@posts = policy_scope(Post)
|
304
279
|
end
|
280
|
+
|
281
|
+
def show
|
282
|
+
@post = policy_scope(Post).find(params[:id])
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
Like with the authorize method, you can also override the policy scope class:
|
287
|
+
|
288
|
+
``` ruby
|
289
|
+
def index
|
290
|
+
# publication_class => Post
|
291
|
+
@publications = policy_scope(publication_class, policy_scope_class: PublicationPolicy::Scope)
|
292
|
+
end
|
305
293
|
```
|
306
294
|
|
307
295
|
Just as with your policy, this will automatically infer that you want to use
|
@@ -322,6 +310,70 @@ You can, and are encouraged to, use this method in views:
|
|
322
310
|
<% end %>
|
323
311
|
```
|
324
312
|
|
313
|
+
## Ensuring policies and scopes are used
|
314
|
+
|
315
|
+
When you are developing an application with Pundit it can be easy to forget to
|
316
|
+
authorize some action. People are forgetful after all. Since Pundit encourages
|
317
|
+
you to add the `authorize` call manually to each controller action, it's really
|
318
|
+
easy to miss one.
|
319
|
+
|
320
|
+
Thankfully, Pundit has a handy feature which reminds you in case you forget.
|
321
|
+
Pundit tracks whether you have called `authorize` anywhere in your controller
|
322
|
+
action. Pundit also adds a method to your controllers called
|
323
|
+
`verify_authorized`. This method will raise an exception if `authorize` has not
|
324
|
+
yet been called. You should run this method in an `after_action` hook to ensure
|
325
|
+
that you haven't forgotten to authorize the action. For example:
|
326
|
+
|
327
|
+
``` ruby
|
328
|
+
class ApplicationController < ActionController::Base
|
329
|
+
include Pundit
|
330
|
+
after_action :verify_authorized
|
331
|
+
end
|
332
|
+
```
|
333
|
+
|
334
|
+
Likewise, Pundit also adds `verify_policy_scoped` to your controller. This
|
335
|
+
will raise an exception similar to `verify_authorized`. However, it tracks
|
336
|
+
if `policy_scope` is used instead of `authorize`. This is mostly useful for
|
337
|
+
controller actions like `index` which find collections with a scope and don't
|
338
|
+
authorize individual instances.
|
339
|
+
|
340
|
+
``` ruby
|
341
|
+
class ApplicationController < ActionController::Base
|
342
|
+
include Pundit
|
343
|
+
after_action :verify_authorized, except: :index
|
344
|
+
after_action :verify_policy_scoped, only: :index
|
345
|
+
end
|
346
|
+
```
|
347
|
+
|
348
|
+
**This verification mechanism only exists to aid you while developing your
|
349
|
+
application, so you don't forget to call `authorize`. It is not some kind of
|
350
|
+
failsafe mechanism or authorization mechanism. You should be able to remove
|
351
|
+
these filters without affecting how your app works in any way.**
|
352
|
+
|
353
|
+
Some people have found this feature confusing, while many others
|
354
|
+
find it extremely helpful. If you fall into the category of people who find it
|
355
|
+
confusing then you do not need to use it. Pundit will work just fine without
|
356
|
+
using `verify_authorized` and `verify_policy_scoped`.
|
357
|
+
|
358
|
+
### Conditional verification
|
359
|
+
|
360
|
+
If you're using `verify_authorized` in your controllers but need to
|
361
|
+
conditionally bypass verification, you can use `skip_authorization`. For
|
362
|
+
bypassing `verify_policy_scoped`, use `skip_policy_scope`. These are useful
|
363
|
+
in circumstances where you don't want to disable verification for the
|
364
|
+
entire action, but have some cases where you intend to not authorize.
|
365
|
+
|
366
|
+
```ruby
|
367
|
+
def show
|
368
|
+
record = Record.find_by(attribute: "value")
|
369
|
+
if record.present?
|
370
|
+
authorize record
|
371
|
+
else
|
372
|
+
skip_authorization
|
373
|
+
end
|
374
|
+
end
|
375
|
+
```
|
376
|
+
|
325
377
|
## Manually specifying policy classes
|
326
378
|
|
327
379
|
Sometimes you might want to explicitly declare which policy to use for a given
|
@@ -362,7 +414,8 @@ rails g pundit:policy post
|
|
362
414
|
|
363
415
|
In many applications, only logged in users are really able to do anything. If
|
364
416
|
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.
|
417
|
+
user in a policy isn't `nil` for every single permission. Aside from policies,
|
418
|
+
you can add this check to the base class for scopes.
|
366
419
|
|
367
420
|
We suggest that you define a filter that redirects unauthenticated users to the
|
368
421
|
login page. As a secondary defence, if you've defined an ApplicationPolicy, it
|
@@ -376,6 +429,37 @@ class ApplicationPolicy
|
|
376
429
|
@user = user
|
377
430
|
@record = record
|
378
431
|
end
|
432
|
+
|
433
|
+
class Scope
|
434
|
+
attr_reader :user, :scope
|
435
|
+
|
436
|
+
def initialize(user, scope)
|
437
|
+
raise Pundit::NotAuthorizedError, "must be logged in" unless user
|
438
|
+
@user = user
|
439
|
+
@scope = scope
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
```
|
444
|
+
|
445
|
+
## NilClassPolicy
|
446
|
+
|
447
|
+
To support a [null object pattern](https://en.wikipedia.org/wiki/Null_Object_pattern)
|
448
|
+
you may find that you want to implement a `NilClassPolicy`. This might be useful
|
449
|
+
where you want to extend your ApplicationPolicy to allow some tolerance of, for
|
450
|
+
example, associations which might be `nil`.
|
451
|
+
|
452
|
+
```ruby
|
453
|
+
class NilClassPolicy < ApplicationPolicy
|
454
|
+
class Scope < Scope
|
455
|
+
def resolve
|
456
|
+
raise Pundit::NotDefinedError, "Cannot scope NilClass"
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def show?
|
461
|
+
false # Nobody can see nothing
|
462
|
+
end
|
379
463
|
end
|
380
464
|
```
|
381
465
|
|
@@ -402,6 +486,10 @@ class ApplicationController < ActionController::Base
|
|
402
486
|
end
|
403
487
|
```
|
404
488
|
|
489
|
+
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:
|
490
|
+
|
491
|
+
```config.action_dispatch.rescue_responses["Pundit::NotAuthorizedError"] = :forbidden```
|
492
|
+
|
405
493
|
## Creating custom error messages
|
406
494
|
|
407
495
|
`NotAuthorizedError`s provide information on what query (e.g. `:create?`), what
|
@@ -469,6 +557,48 @@ def pundit_user
|
|
469
557
|
end
|
470
558
|
```
|
471
559
|
|
560
|
+
## Policy Namespacing
|
561
|
+
In some cases it might be helpful to have multiple policies that serve different contexts for a
|
562
|
+
resource. A prime example of this is the case where User policies differ from Admin policies. To
|
563
|
+
authorize with a namespaced policy, pass the namespace into the `authorize` helper in an array:
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
authorize(post) # => will look for a PostPolicy
|
567
|
+
authorize([:admin, post]) # => will look for an Admin::PostPolicy
|
568
|
+
authorize([:foo, :bar, post]) # => will look for a Foo::Bar::PostPolicy
|
569
|
+
|
570
|
+
policy_scope(Post) # => will look for a PostPolicy::Scope
|
571
|
+
policy_scope([:admin, Post]) # => will look for an Admin::PostPolicy::Scope
|
572
|
+
policy_scope([:foo, :bar, Post]) # => will look for a Foo::Bar::PostPolicy::Scope
|
573
|
+
```
|
574
|
+
|
575
|
+
If you are using namespaced policies for something like Admin views, it can be useful to
|
576
|
+
override the `policy_scope` and `authorize` helpers in your `AdminController` to automatically
|
577
|
+
apply the namespacing:
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
class AdminController < ApplicationController
|
581
|
+
def policy_scope(scope)
|
582
|
+
super([:admin, scope])
|
583
|
+
end
|
584
|
+
|
585
|
+
def authorize(record, query = nil)
|
586
|
+
super([:admin, record], query)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
class Admin::PostController < AdminController
|
591
|
+
def index
|
592
|
+
policy_scope(Post)
|
593
|
+
end
|
594
|
+
|
595
|
+
def show
|
596
|
+
post = Post.find(params[:id])
|
597
|
+
authorize(post)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
```
|
601
|
+
|
472
602
|
## Additional context
|
473
603
|
|
474
604
|
Pundit strongly encourages you to model your application in such a way that the
|
@@ -564,6 +694,45 @@ class PostsController < ApplicationController
|
|
564
694
|
end
|
565
695
|
```
|
566
696
|
|
697
|
+
If you want to permit different attributes based on the current action, you can define a `permitted_attributes_for_#{action}` method on your policy:
|
698
|
+
|
699
|
+
```ruby
|
700
|
+
# app/policies/post_policy.rb
|
701
|
+
class PostPolicy < ApplicationPolicy
|
702
|
+
def permitted_attributes_for_create
|
703
|
+
[:title, :body]
|
704
|
+
end
|
705
|
+
|
706
|
+
def permitted_attributes_for_edit
|
707
|
+
[:body]
|
708
|
+
end
|
709
|
+
end
|
710
|
+
```
|
711
|
+
|
712
|
+
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.
|
713
|
+
|
714
|
+
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`.
|
715
|
+
|
716
|
+
```ruby
|
717
|
+
def pundit_params_for(record)
|
718
|
+
params.require(PolicyFinder.new(record).param_key)
|
719
|
+
end
|
720
|
+
```
|
721
|
+
|
722
|
+
For example:
|
723
|
+
|
724
|
+
```ruby
|
725
|
+
# If you don't want to use require
|
726
|
+
def pundit_params_for(record)
|
727
|
+
params.fetch(PolicyFinder.new(record).param_key, {})
|
728
|
+
end
|
729
|
+
|
730
|
+
# If you are using something like the JSON API spec
|
731
|
+
def pundit_params_for(_record)
|
732
|
+
params.fetch(:data, {}).fetch(:attributes, {})
|
733
|
+
end
|
734
|
+
```
|
735
|
+
|
567
736
|
## RSpec
|
568
737
|
|
569
738
|
### Policy Specs
|
@@ -600,12 +769,16 @@ end
|
|
600
769
|
An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
|
601
770
|
[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
771
|
|
772
|
+
### Scope Specs
|
773
|
+
|
774
|
+
Pundit does not provide a DSL for testing scopes. Just test it like a regular Ruby class!
|
775
|
+
|
603
776
|
# External Resources
|
604
777
|
|
605
778
|
- [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
|
606
779
|
- [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
|
607
780
|
- [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/
|
781
|
+
- [Using Pundit outside of a Rails controller](https://github.com/varvet/pundit/pull/136)
|
609
782
|
- [Straightforward Rails Authorization with Pundit](http://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
|
610
783
|
|
611
784
|
# License
|