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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +26 -0
- data/.github/PULL_REQUEST_TEMPLATE/gem_release_template.md +8 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/main.yml +112 -0
- data/.github/workflows/push_gem.yml +33 -0
- data/.rubocop.yml +14 -18
- data/CHANGELOG.md +52 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/CONTRIBUTING.md +3 -5
- data/Gemfile +3 -2
- data/README.md +117 -62
- data/SECURITY.md +19 -0
- data/config/rubocop-rspec.yml +5 -0
- data/lib/generators/pundit/install/templates/application_policy.rb +1 -1
- data/lib/generators/pundit/policy/templates/policy.rb +11 -4
- data/lib/generators/rspec/templates/policy_spec.rb +1 -1
- data/lib/pundit/authorization.rb +176 -0
- data/lib/pundit/cache_store/legacy_store.rb +17 -0
- data/lib/pundit/cache_store/null_store.rb +18 -0
- data/lib/pundit/context.rb +127 -0
- data/lib/pundit/policy_finder.rb +1 -1
- data/lib/pundit/rspec.rb +23 -1
- data/lib/pundit/version.rb +1 -1
- data/lib/pundit.rb +35 -249
- data/pundit.gemspec +5 -2
- data/spec/authorization_spec.rb +274 -0
- data/spec/dsl_spec.rb +30 -0
- data/spec/generators_spec.rb +43 -0
- data/spec/policies/post_policy_spec.rb +27 -0
- data/spec/policy_finder_spec.rb +1 -1
- data/spec/pundit_spec.rb +59 -244
- data/spec/spec_helper.rb +123 -39
- metadata +42 -10
- data/.travis.yml +0 -25
data/README.md
CHANGED
@@ -1,36 +1,36 @@
|
|
1
1
|
# Pundit
|
2
2
|
|
3
|
-
[](
|
3
|
+
[](https://github.com/varvet/pundit/actions/workflows/main.yml)
|
4
|
+
[](https://codeclimate.com/github/varvet/pundit/maintainability)
|
5
|
+
[](https://inch-ci.org/github/varvet/pundit)
|
6
|
+
[](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
|
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](
|
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/
|
17
|
-
- [Code of Conduct](https://github.com/varvet/pundit/blob/
|
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
|
-
|
26
|
-
|
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
|
51
|
-
|
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
|
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
|
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
|
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
|
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
|
-
|
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 :
|
352
|
-
|
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
|
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
|
-
##
|
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
|
-
|
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
|
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](
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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
|
-
|
785
|
-
|
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.
|
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](
|
795
|
-
- [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](
|
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](
|
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!
|
@@ -1,9 +1,16 @@
|
|
1
1
|
<% module_namespacing do -%>
|
2
2
|
class <%= class_name %>Policy < ApplicationPolicy
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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 -%>
|
@@ -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
|