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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a2c188098c86ad5a0804044f80219014564501ef
4
- data.tar.gz: 2ab7f79275d9ae66e5d04cf980c11e2c2983a4ef
2
+ SHA256:
3
+ metadata.gz: 91c992d8bb2bc907e4c532fb868f0e6f4105d9f4a79f0871593ec374f6f173b0
4
+ data.tar.gz: a855c49b6ada65cd8d1309ed104d6d089236ec46fcade8e872b66be6b152a054
5
5
  SHA512:
6
- metadata.gz: 55fbbf71ad514c0cfe4f8933dea59915314f749efa53ab5579f2da9dfcf2b4786343cefa53d3a35e26f4a346776c1c513884595a39561d280b259e6b6fb9b31a
7
- data.tar.gz: bbcf9417801b22deac78afe2d5ea8a268193daf0e011f861006c25b1d0124d1f462aa37d4d38e623b9d438cb29ceb52b250575118e053862b74561795bbdd7a4
6
+ metadata.gz: 17b426b7b0a1410809be20410d20f2f291d26d3fde05bb333b68464f720c7824989f5063ba30c107459debaabb572f9a682738a74f35ebc08c4b3b6638350a3a
7
+ data.tar.gz: e28ba68e5f8c1a430a4cb1335b0ad95ff2d4dc35d5c2842e74321aad26e42cf8f1c383b217cbac8aab8449f20ef1d9faf10fe1432d27287fce961bea04e53b4e
@@ -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
- Style/AlignParameters:
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
- Style/ClosingParenthesisIndentation:
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
- Style/CaseIndentation:
53
- IndentWhenRelativeTo: case
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
- Style/AccessModifierIndentation:
72
+ Layout/AccessModifierIndentation:
65
73
  EnforcedStyle: outdent
66
74
 
67
75
  Style/SignalException:
68
76
  Enabled: false
69
77
 
70
- Style/IndentationWidth:
78
+ Layout/IndentationWidth:
71
79
  Enabled: false
72
80
 
73
81
  Style/TrivialAccessors:
74
82
  ExactNameMatch: true
75
83
 
76
- Lint/EndAlignment:
77
- AlignWith: variable
84
+ Layout/EndAlignment:
85
+ EnforcedStyleAlignWith: variable
78
86
 
79
- Lint/DefEndAlignment:
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
- Style/IndentHash:
99
+ Layout/IndentHash:
92
100
  Enabled: false
93
101
 
94
102
  Style/DoubleNegation:
@@ -1,11 +1,21 @@
1
1
  language: ruby
2
2
  sudo: false
3
- rvm:
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - jruby-19mode
8
- - rbx-2
9
- env:
10
- - RSPEC_VERSION="<2.99"
11
- - RSPEC_VERSION="~>3.0
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"
@@ -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
- gem "rspec", ENV["RSPEC_VERSION"] unless ENV["RSPEC_VERSION"].to_s.empty?
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/elabs/pundit.svg?branch=master)](https://travis-ci.org/elabs/pundit)
4
- [![Code Climate](https://codeclimate.com/github/elabs/pundit.svg)](https://codeclimate.com/github/elabs/pundit)
5
- [![Inline docs](http://inch-ci.org/github/elabs/pundit.svg?branch=master)](http://inch-ci.org/github/elabs/pundit)
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/elabs/pundit)
16
- - [Contributing](https://github.com/elabs/pundit/blob/master/CONTRIBUTING.md)
17
- - [Code of Conduct](https://github.com/elabs/pundit/blob/master/CODE_OF_CONDUCT.md)
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="http://d3cv91luii1z1d.cloudfront.net/logo-gh.png" alt="Elabs" height="50px"/>](http://elabs.se)
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
- raise "not authorized" unless PostPolicy.new(current_user, @post).update?
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 post.published?
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 post.published?
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/elabs/pundit/pull/136)
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