pundit 2.1.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,36 +1,36 @@
1
1
  # Pundit
2
2
 
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
- [![Gem Version](https://badge.fury.io/rb/pundit.svg)](http://badge.fury.io/rb/pundit)
3
+ [![Main](https://github.com/varvet/pundit/actions/workflows/main.yml/badge.svg)](https://github.com/varvet/pundit/actions/workflows/main.yml)
4
+ [![Code Climate](https://api.codeclimate.com/v1/badges/a940030f96c9fb43046a/maintainability)](https://codeclimate.com/github/varvet/pundit/maintainability)
5
+ [![Inline docs](https://inch-ci.org/github/varvet/pundit.svg?branch=main)](https://inch-ci.org/github/varvet/pundit)
6
+ [![Gem Version](https://badge.fury.io/rb/pundit.svg)](https://badge.fury.io/rb/pundit)
7
7
 
8
8
  Pundit provides a set of helpers which guide you in leveraging regular Ruby
9
- classes and object oriented design patterns to build a simple, robust and
9
+ classes and object oriented design patterns to build a straightforward, robust, and
10
10
  scalable authorization system.
11
11
 
12
- Links:
12
+ ## Links:
13
13
 
14
- - [API documentation](http://www.rubydoc.info/gems/pundit)
14
+ - [API documentation for the most recent version](https://www.rubydoc.info/gems/pundit)
15
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)
16
+ - [Contributing](https://github.com/varvet/pundit/blob/main/CONTRIBUTING.md)
17
+ - [Code of Conduct](https://github.com/varvet/pundit/blob/main/CODE_OF_CONDUCT.md)
18
18
 
19
- Sponsored by:
20
-
21
- [<img src="https://www.varvet.com/images/wordmark-red.svg" alt="Varvet" height="50px"/>](https://www.varvet.com)
19
+ <strong>Sponsored by:</strong> <a href="https://www.varvet.com">Varvet<br><br><img src="https://github.com/varvet/pundit/assets/99166/aa9efa0a-6903-4037-abee-1824edc57f1a" alt="Varvet logo" height="120"></div>
22
20
 
23
21
  ## Installation
24
22
 
25
- ``` ruby
26
- gem "pundit"
23
+ > **Please note** that the README on GitHub is accurate with the _latest code on GitHub_. You are most likely using a released version of Pundit, so please refer to the [documentation for the latest released version of Pundit](https://www.rubydoc.info/gems/pundit).
24
+
25
+ ``` sh
26
+ bundle add pundit
27
27
  ```
28
28
 
29
- Include Pundit in your application controller:
29
+ Include `Pundit::Authorization` in your application controller:
30
30
 
31
31
  ``` ruby
32
32
  class ApplicationController < ActionController::Base
33
- include Pundit
33
+ include Pundit::Authorization
34
34
  end
35
35
  ```
36
36
 
@@ -47,8 +47,8 @@ can pick up any classes in the new `app/policies/` directory.
47
47
  ## Policies
48
48
 
49
49
  Pundit is focused around the notion of policy classes. We suggest that you put
50
- these classes in `app/policies`. This is a simple example that allows updating
51
- a post if the user is an admin, or if the post is unpublished:
50
+ these classes in `app/policies`. This is an example that allows updating a post
51
+ if the user is an admin, or if the post is unpublished:
52
52
 
53
53
  ``` ruby
54
54
  class PostPolicy
@@ -65,7 +65,7 @@ class PostPolicy
65
65
  end
66
66
  ```
67
67
 
68
- As you can see, this is just a plain Ruby class. Pundit makes the following
68
+ As you can see, this is a plain Ruby class. Pundit makes the following
69
69
  assumptions about this class:
70
70
 
71
71
  - The class has the same name as some kind of model class, only suffixed
@@ -116,7 +116,7 @@ and the given record. It then infers from the action name, that it should call
116
116
 
117
117
  ``` ruby
118
118
  unless PostPolicy.new(current_user, @post).update?
119
- raise Pundit::NotAuthorizedError, "not allowed to update? this #{@post.inspect}"
119
+ raise Pundit::NotAuthorizedError, "not allowed to PostPolicy#update? this Post"
120
120
  end
121
121
  ```
122
122
 
@@ -194,18 +194,30 @@ you can retrieve it by passing a symbol.
194
194
 
195
195
  ```ruby
196
196
  # app/policies/dashboard_policy.rb
197
- class DashboardPolicy < Struct.new(:user, :dashboard)
198
- # ...
197
+ class DashboardPolicy
198
+ attr_reader :user
199
+
200
+ # `_record` in this example will be :dashboard
201
+ def initialize(user, _record)
202
+ @user = user
203
+ end
204
+
205
+ def show?
206
+ user.admin?
207
+ end
199
208
  end
200
209
  ```
201
210
 
202
211
  Note that the headless policy still needs to accept two arguments. The
203
- second argument will just be the symbol `:dashboard` in this case which
212
+ second argument will be the symbol `:dashboard` in this case, which
204
213
  is what is passed as the record to `authorize` below.
205
214
 
206
215
  ```ruby
207
216
  # In controllers
208
- authorize :dashboard, :show?
217
+ def show
218
+ authorize :dashboard, :show?
219
+ ...
220
+ end
209
221
  ```
210
222
 
211
223
  ```erb
@@ -265,7 +277,7 @@ generator, or create your own base class to inherit from:
265
277
 
266
278
  ``` ruby
267
279
  class PostPolicy < ApplicationPolicy
268
- class Scope < Scope
280
+ class Scope < ApplicationPolicy::Scope
269
281
  def resolve
270
282
  if user.admin?
271
283
  scope.all
@@ -334,7 +346,7 @@ that you haven't forgotten to authorize the action. For example:
334
346
 
335
347
  ``` ruby
336
348
  class ApplicationController < ActionController::Base
337
- include Pundit
349
+ include Pundit::Authorization
338
350
  after_action :verify_authorized
339
351
  end
340
352
  ```
@@ -347,9 +359,16 @@ authorize individual instances.
347
359
 
348
360
  ``` ruby
349
361
  class ApplicationController < ActionController::Base
350
- include Pundit
351
- after_action :verify_authorized, except: :index
352
- after_action :verify_policy_scoped, only: :index
362
+ include Pundit::Authorization
363
+ after_action :verify_pundit_authorization
364
+
365
+ def verify_pundit_authorization
366
+ if action_name == "index"
367
+ verify_policy_scoped
368
+ else
369
+ verify_authorized
370
+ end
371
+ end
353
372
  end
354
373
  ```
355
374
 
@@ -360,7 +379,7 @@ these filters without affecting how your app works in any way.**
360
379
 
361
380
  Some people have found this feature confusing, while many others
362
381
  find it extremely helpful. If you fall into the category of people who find it
363
- confusing then you do not need to use it. Pundit will work just fine without
382
+ confusing then you do not need to use it. Pundit will work fine without
364
383
  using `verify_authorized` and `verify_policy_scoped`.
365
384
 
366
385
  ### Conditional verification
@@ -405,20 +424,13 @@ class Post
405
424
  end
406
425
  ```
407
426
 
408
- ## Just plain old Ruby
427
+ ## Plain old Ruby
428
+
429
+ Pundit is a very small library on purpose, and it doesn't do anything you can't do yourself. There's no secret sauce here. It does as little as possible, and then gets out of your way.
409
430
 
410
- As you can see, Pundit doesn't do anything you couldn't have easily done
411
- yourself. It's a very small library, it just provides a few neat helpers.
412
- Together these give you the power of building a well structured, fully working
413
- authorization system without using any special DSLs or funky syntax or
414
- anything.
431
+ With the few but powerful helpers available in Pundit, you have the power to build a well structured, fully working authorization system without using any special DSLs or funky syntax.
415
432
 
416
- Remember that all of the policy and scope classes are just plain Ruby classes,
417
- which means you can use the same mechanisms you always use to DRY things up.
418
- Encapsulate a set of permissions into a module and include them in multiple
419
- policies. Use `alias_method` to make some permissions behave the same as
420
- others. Inherit from a base set of permissions. Use metaprogramming if you
421
- really have to.
433
+ Remember that all of the policy and scope classes are plain Ruby classes, which means you can use the same mechanisms you always use to DRY things up. Encapsulate a set of permissions into a module and include them in multiple policies. Use `alias_method` to make some permissions behave the same as others. Inherit from a base set of permissions. Use metaprogramming if you really have to.
422
434
 
423
435
  ## Generator
424
436
 
@@ -469,7 +481,7 @@ example, associations which might be `nil`.
469
481
 
470
482
  ```ruby
471
483
  class NilClassPolicy < ApplicationPolicy
472
- class Scope < Scope
484
+ class Scope < ApplicationPolicy::Scope
473
485
  def resolve
474
486
  raise Pundit::NotDefinedError, "Cannot scope NilClass"
475
487
  end
@@ -484,13 +496,13 @@ end
484
496
  ## Rescuing a denied Authorization in Rails
485
497
 
486
498
  Pundit raises a `Pundit::NotAuthorizedError` you can
487
- [rescue_from](http://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
499
+ [rescue_from](https://guides.rubyonrails.org/action_controller_overview.html#rescue-from)
488
500
  in your `ApplicationController`. You can customize the `user_not_authorized`
489
501
  method in every controller.
490
502
 
491
503
  ```ruby
492
504
  class ApplicationController < ActionController::Base
493
- include Pundit
505
+ include Pundit::Authorization
494
506
 
495
507
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
496
508
 
@@ -498,7 +510,7 @@ class ApplicationController < ActionController::Base
498
510
 
499
511
  def user_not_authorized
500
512
  flash[:alert] = "You are not authorized to perform this action."
501
- redirect_to(request.referrer || root_path)
513
+ redirect_back_or_to(root_path)
502
514
  end
503
515
  end
504
516
  ```
@@ -527,7 +539,7 @@ class ApplicationController < ActionController::Base
527
539
  policy_name = exception.policy.class.to_s.underscore
528
540
 
529
541
  flash[:error] = t "#{policy_name}.#{exception.query}", scope: "pundit", default: :default
530
- redirect_to(request.referrer || root_path)
542
+ redirect_back_or_to(root_path)
531
543
  end
532
544
  end
533
545
  ```
@@ -541,8 +553,7 @@ en:
541
553
  create?: 'You cannot create posts!'
542
554
  ```
543
555
 
544
- Of course, this is just an example. Pundit is agnostic as to how you implement
545
- your error messaging.
556
+ This is an example. Pundit is agnostic as to how you implement your error messaging.
546
557
 
547
558
  ## Manually retrieving policies and scopes
548
559
 
@@ -564,9 +575,7 @@ those without the bang will return nil.
564
575
 
565
576
  ## Customize Pundit user
566
577
 
567
- In some cases your controller might not have access to `current_user`, or your
568
- `current_user` is not the method that should be invoked by Pundit. Simply
569
- define a method in your controller called `pundit_user`.
578
+ On occasion, your controller may be unable to access `current_user`, or the method that should be invoked by Pundit may not be `current_user`. To address this, you can define a method in your controller named `pundit_user`.
570
579
 
571
580
  ```ruby
572
581
  def pundit_user
@@ -643,7 +652,7 @@ class UserContext
643
652
  end
644
653
 
645
654
  class ApplicationController
646
- include Pundit
655
+ include Pundit::Authorization
647
656
 
648
657
  def pundit_user
649
658
  UserContext.new(current_user, request.ip)
@@ -678,7 +687,7 @@ You can now retrieve these attributes from the policy:
678
687
  class PostsController < ApplicationController
679
688
  def update
680
689
  @post = Post.find(params[:id])
681
- if @post.update_attributes(post_params)
690
+ if @post.update(post_params)
682
691
  redirect_to @post
683
692
  else
684
693
  render :edit
@@ -700,7 +709,7 @@ However, this is a bit cumbersome, so Pundit provides a convenient helper method
700
709
  class PostsController < ApplicationController
701
710
  def update
702
711
  @post = Post.find(params[:id])
703
- if @post.update_attributes(permitted_attributes(@post))
712
+ if @post.update(permitted_attributes(@post))
704
713
  redirect_to @post
705
714
  else
706
715
  render :edit
@@ -752,6 +761,10 @@ end
752
761
 
753
762
  ### Policy Specs
754
763
 
764
+ > [!TIP]
765
+ > An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
766
+ [excellent post](https://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/) and implemented in the third party [pundit-matchers](https://github.com/punditcommunity/pundit-matchers) gem.
767
+
755
768
  Pundit includes a mini-DSL for writing expressive tests for your policies in RSpec.
756
769
  Require `pundit/rspec` in your `spec_helper.rb`:
757
770
 
@@ -781,25 +794,67 @@ describe PostPolicy do
781
794
  end
782
795
  ```
783
796
 
784
- An alternative approach to Pundit policy specs is scoping them to a user context as outlined in this
785
- [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.
797
+ ### Custom matcher description
798
+
799
+ By default rspec includes an inspected `user` and `record` in the matcher description, which might become overly verbose:
800
+
801
+ ```
802
+ PostPolicy
803
+ update? and show?
804
+ is expected to permit #<User:0x0000000104aefd80> and #<Post:0x0000000104aef8d0 @user=#<User:0x0000000104aefd80>>
805
+ ```
806
+
807
+ You can override the default description with a static string, or a block:
808
+
809
+ ```ruby
810
+ # static alternative: Pundit::RSpec::Matchers.description = "permit the user"
811
+ Pundit::RSpec::Matchers.description = ->(user, record) do
812
+ "permit user with role #{user.role} to access record with ID #{record.id}"
813
+ end
814
+ ```
815
+
816
+ Which would make for a less chatty output:
817
+
818
+ ```
819
+ PostPolicy
820
+ update? and show?
821
+ is expected to permit user with role admin to access record with ID 130
822
+ ```
823
+
824
+ ### Focus Support
825
+
826
+ If your RSpec config has `filter_run_when_matching :focus`, you may tag the `permissions` helper like so:
827
+
828
+ ```
829
+ permissions :show?, :focus do
830
+ ```
786
831
 
787
832
  ### Scope Specs
788
833
 
789
- Pundit does not provide a DSL for testing scopes. Just test it like a regular Ruby class!
834
+ Pundit does not provide a DSL for testing scopes. Test them like you would a regular Ruby class!
835
+
836
+ ### Linting with RuboCop RSpec
837
+
838
+ When you lint your RSpec spec files with `rubocop-rspec`, it will fail to properly detect RSpec constructs that Pundit defines, `permissions`.
839
+ Make sure to use `rubocop-rspec` 2.0 or newer and add the following to your `.rubocop.yml`:
840
+
841
+ ```yaml
842
+ inherit_gem:
843
+ pundit: config/rubocop-rspec.yml
844
+ ```
790
845
 
791
846
  # External Resources
792
847
 
793
848
  - [RailsApps Example Application: Pundit and Devise](https://github.com/RailsApps/rails-devise-pundit)
794
- - [Migrating to Pundit from CanCan](http://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
795
- - [Testing Pundit Policies with RSpec](http://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
849
+ - [Migrating to Pundit from CanCan](https://blog.carbonfive.com/2013/10/21/migrating-to-pundit-from-cancan/)
850
+ - [Testing Pundit Policies with RSpec](https://thunderboltlabs.com/blog/2013/03/27/testing-pundit-policies-with-rspec/)
796
851
  - [Testing Pundit with Minitest](https://github.com/varvet/pundit/issues/204#issuecomment-60166450)
797
852
  - [Using Pundit outside of a Rails controller](https://github.com/varvet/pundit/pull/136)
798
- - [Straightforward Rails Authorization with Pundit](http://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
853
+ - [Straightforward Rails Authorization with Pundit](https://www.sitepoint.com/straightforward-rails-authorization-with-pundit/)
799
854
 
800
855
  ## Other implementations
801
856
 
802
- - [Flask-Pundit](https://github.com/anurag90x/flask-pundit) (Python) is a [Flask](http://flask.pocoo.org/) extension "heavily inspired by" Pundit
857
+ - [Flask-Pundit](https://github.com/anurag90x/flask-pundit) (Python) is a [Flask](https://flask.pocoo.org/) extension "heavily inspired by" Pundit
803
858
 
804
859
  # License
805
860
 
data/SECURITY.md ADDED
@@ -0,0 +1,19 @@
1
+ # Security Policy
2
+
3
+ Please do not file an issue on GitHub, or send a PR addressing the issue.
4
+
5
+ ## Supported versions
6
+
7
+ Most recent major version only.
8
+
9
+ ## Reporting a vulnerability
10
+
11
+ Contact one of the maintainers directly:
12
+
13
+ * [@Burgestrand](https://github.com/Burgestrand)
14
+ * [@dgmstuart](https://github.com/dgmstuart)
15
+ * [@varvet](https://github.com/varvet)
16
+
17
+ You can report vulnerabilities on GitHub too: https://github.com/varvet/pundit/security
18
+
19
+ Thank you!
@@ -0,0 +1,5 @@
1
+ RSpec:
2
+ Language:
3
+ ExampleGroups:
4
+ Regular:
5
+ - permissions
@@ -43,7 +43,7 @@ class ApplicationPolicy
43
43
  end
44
44
 
45
45
  def resolve
46
- scope.all
46
+ raise NoMethodError, "You must define #resolve in #{self.class}"
47
47
  end
48
48
 
49
49
  private
@@ -1,9 +1,16 @@
1
1
  <% module_namespacing do -%>
2
2
  class <%= class_name %>Policy < ApplicationPolicy
3
- class Scope < Scope
4
- def resolve
5
- scope.all
6
- end
3
+ # NOTE: Up to Pundit v2.3.1, the inheritance was declared as
4
+ # `Scope < Scope` rather than `Scope < ApplicationPolicy::Scope`.
5
+ # In most cases the behavior will be identical, but if updating existing
6
+ # code, beware of possible changes to the ancestors:
7
+ # https://gist.github.com/Burgestrand/4b4bc22f31c8a95c425fc0e30d7ef1f5
8
+
9
+ class Scope < ApplicationPolicy::Scope
10
+ # NOTE: Be explicit about which records you allow access to!
11
+ # def resolve
12
+ # scope.all
13
+ # end
7
14
  end
8
15
  end
9
16
  <% end -%>
@@ -1,4 +1,4 @@
1
- require '<%= File.exists?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
1
+ require '<%= File.exist?('spec/rails_helper.rb') ? 'rails_helper' : 'spec_helper' %>'
2
2
 
3
3
  RSpec.describe <%= class_name %>Policy, type: :policy do
4
4
  let(:user) { User.new }
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pundit
4
+ module Authorization
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper Helper if respond_to?(:helper)
9
+ if respond_to?(:helper_method)
10
+ helper_method :policy
11
+ helper_method :pundit_policy_scope
12
+ helper_method :pundit_user
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ # @return [Pundit::Context] a new instance of {Pundit::Context} with the current user
19
+ def pundit
20
+ @pundit ||= Pundit::Context.new(
21
+ user: pundit_user,
22
+ policy_cache: Pundit::CacheStore::LegacyStore.new(policies)
23
+ )
24
+ end
25
+
26
+ # @return [Boolean] whether authorization has been performed, i.e. whether
27
+ # one {#authorize} or {#skip_authorization} has been called
28
+ def pundit_policy_authorized?
29
+ !!@_pundit_policy_authorized
30
+ end
31
+
32
+ # @return [Boolean] whether policy scoping has been performed, i.e. whether
33
+ # one {#policy_scope} or {#skip_policy_scope} has been called
34
+ def pundit_policy_scoped?
35
+ !!@_pundit_policy_scoped
36
+ end
37
+
38
+ # Raises an error if authorization has not been performed, usually used as an
39
+ # `after_action` filter to prevent programmer error in forgetting to call
40
+ # {#authorize} or {#skip_authorization}.
41
+ #
42
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
43
+ # @raise [AuthorizationNotPerformedError] if authorization has not been performed
44
+ # @return [void]
45
+ def verify_authorized
46
+ raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
47
+ end
48
+
49
+ # Raises an error if policy scoping has not been performed, usually used as an
50
+ # `after_action` filter to prevent programmer error in forgetting to call
51
+ # {#policy_scope} or {#skip_policy_scope} in index actions.
52
+ #
53
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
54
+ # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
55
+ # @return [void]
56
+ def verify_policy_scoped
57
+ raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
58
+ end
59
+
60
+ # Retrieves the policy for the given record, initializing it with the record
61
+ # and current user and finally throwing an error if the user is not
62
+ # authorized to perform the given action.
63
+ #
64
+ # @param record [Object, Array] the object we're checking permissions of
65
+ # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
66
+ # If omitted then this defaults to the Rails controller action name.
67
+ # @param policy_class [Class] the policy class we want to force use of
68
+ # @raise [NotAuthorizedError] if the given query method returned false
69
+ # @return [Object] Always returns the passed object record
70
+ def authorize(record, query = nil, policy_class: nil)
71
+ query ||= "#{action_name}?"
72
+
73
+ @_pundit_policy_authorized = true
74
+
75
+ pundit.authorize(record, query: query, policy_class: policy_class)
76
+ end
77
+
78
+ # Allow this action not to perform authorization.
79
+ #
80
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
81
+ # @return [void]
82
+ def skip_authorization
83
+ @_pundit_policy_authorized = :skipped
84
+ end
85
+
86
+ # Allow this action not to perform policy scoping.
87
+ #
88
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
89
+ # @return [void]
90
+ def skip_policy_scope
91
+ @_pundit_policy_scoped = :skipped
92
+ end
93
+
94
+ # Retrieves the policy scope for the given record.
95
+ #
96
+ # @see https://github.com/varvet/pundit#scopes
97
+ # @param scope [Object] the object we're retrieving the policy scope for
98
+ # @param policy_scope_class [Class] the policy scope class we want to force use of
99
+ # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
100
+ def policy_scope(scope, policy_scope_class: nil)
101
+ @_pundit_policy_scoped = true
102
+ policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
103
+ end
104
+
105
+ # Retrieves the policy for the given record.
106
+ #
107
+ # @see https://github.com/varvet/pundit#policies
108
+ # @param record [Object] the object we're retrieving the policy for
109
+ # @return [Object] instance of policy class with query methods
110
+ def policy(record)
111
+ pundit.policy!(record)
112
+ end
113
+
114
+ # Retrieves a set of permitted attributes from the policy by instantiating
115
+ # the policy class for the given record and calling `permitted_attributes` on
116
+ # it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
117
+ # what key the record should have in the params hash and retrieves the
118
+ # permitted attributes from the params hash under that key.
119
+ #
120
+ # @see https://github.com/varvet/pundit#strong-parameters
121
+ # @param record [Object] the object we're retrieving permitted attributes for
122
+ # @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
123
+ # If omitted then this defaults to the Rails controller action name.
124
+ # @return [Hash{String => Object}] the permitted attributes
125
+ def permitted_attributes(record, action = action_name)
126
+ policy = policy(record)
127
+ method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
128
+ "permitted_attributes_for_#{action}"
129
+ else
130
+ "permitted_attributes"
131
+ end
132
+ pundit_params_for(record).permit(*policy.public_send(method_name))
133
+ end
134
+
135
+ # Retrieves the params for the given record.
136
+ #
137
+ # @param record [Object] the object we're retrieving params for
138
+ # @return [ActionController::Parameters] the params
139
+ def pundit_params_for(record)
140
+ params.require(PolicyFinder.new(record).param_key)
141
+ end
142
+
143
+ # Cache of policies. You should not rely on this method.
144
+ #
145
+ # @api private
146
+ # rubocop:disable Naming/MemoizedInstanceVariableName
147
+ def policies
148
+ @_pundit_policies ||= {}
149
+ end
150
+ # rubocop:enable Naming/MemoizedInstanceVariableName
151
+
152
+ # Cache of policy scope. You should not rely on this method.
153
+ #
154
+ # @api private
155
+ # rubocop:disable Naming/MemoizedInstanceVariableName
156
+ def policy_scopes
157
+ @_pundit_policy_scopes ||= {}
158
+ end
159
+ # rubocop:enable Naming/MemoizedInstanceVariableName
160
+
161
+ # Hook method which allows customizing which user is passed to policies and
162
+ # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
163
+ #
164
+ # @see https://github.com/varvet/pundit#customize-pundit-user
165
+ # @return [Object] the user object to be used with pundit
166
+ def pundit_user
167
+ current_user
168
+ end
169
+
170
+ private
171
+
172
+ def pundit_policy_scope(scope)
173
+ policy_scopes[scope] ||= pundit.policy_scope!(scope)
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pundit
4
+ module CacheStore
5
+ # @api private
6
+ class LegacyStore
7
+ def initialize(hash = {})
8
+ @store = hash
9
+ end
10
+
11
+ def fetch(user:, record:)
12
+ _ = user
13
+ @store[record] ||= yield
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pundit
4
+ module CacheStore
5
+ # @api private
6
+ class NullStore
7
+ @instance = new
8
+
9
+ class << self
10
+ attr_reader :instance
11
+ end
12
+
13
+ def fetch(*, **)
14
+ yield
15
+ end
16
+ end
17
+ end
18
+ end