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/spec/pundit_spec.rb CHANGED
@@ -10,15 +10,13 @@ RSpec.describe Pundit do
10
10
  let(:comment) { Comment.new }
11
11
  let(:comment_four_five_six) { CommentFourFiveSix.new }
12
12
  let(:article) { Article.new }
13
- let(:controller) { Controller.new(user, "update", {}) }
14
13
  let(:artificial_blog) { ArtificialBlog.new }
15
14
  let(:article_tag) { ArticleTag.new }
16
- let(:comments_relation) { CommentsRelation.new }
17
- let(:empty_comments_relation) { CommentsRelation.new(true) }
15
+ let(:comments_relation) { CommentsRelation.new(empty: false) }
16
+ let(:empty_comments_relation) { CommentsRelation.new(empty: true) }
18
17
  let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) }
19
18
  let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new }
20
19
  let(:wiki) { Wiki.new }
21
- let(:thread) { Thread.new }
22
20
 
23
21
  describe ".authorize" do
24
22
  it "infers the policy and authorizes based on it" do
@@ -49,19 +47,61 @@ RSpec.describe Pundit do
49
47
  expect(Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy)).to be_truthy
50
48
  end
51
49
 
50
+ it "can be given a different policy class using namespaces" do
51
+ expect(PublicationPolicy).to receive(:new).with(user, comment).and_call_original
52
+ expect(Pundit.authorize(user, [:project, comment], :create?, policy_class: PublicationPolicy)).to be_truthy
53
+ end
54
+
52
55
  it "works with anonymous class policies" do
53
56
  expect(Pundit.authorize(user, article_tag, :show?)).to be_truthy
54
57
  expect { Pundit.authorize(user, article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
55
58
  end
56
59
 
57
- it "raises an error with a query and action" do
60
+ it "raises an error with the policy, query and record" do
58
61
  # rubocop:disable Style/MultilineBlockChain
59
62
  expect do
60
63
  Pundit.authorize(user, post, :destroy?)
61
- end.to raise_error(Pundit::NotAuthorizedError, "not allowed to destroy? this Post") do |error|
64
+ end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? this Post") do |error|
62
65
  expect(error.query).to eq :destroy?
63
66
  expect(error.record).to eq post
64
- expect(error.policy).to eq Pundit.policy(user, post)
67
+ expect(error.policy).to have_attributes(
68
+ user: user,
69
+ record: post
70
+ )
71
+ expect(error.policy).to be_a(PostPolicy)
72
+ end
73
+ # rubocop:enable Style/MultilineBlockChain
74
+ end
75
+
76
+ it "raises an error with the policy, query and record when the record is namespaced" do
77
+ # rubocop:disable Style/MultilineBlockChain
78
+ expect do
79
+ Pundit.authorize(user, [:project, :admin, comment], :destroy?)
80
+ end.to raise_error(Pundit::NotAuthorizedError,
81
+ "not allowed to Project::Admin::CommentPolicy#destroy? this Comment") do |error|
82
+ expect(error.query).to eq :destroy?
83
+ expect(error.record).to eq comment
84
+ expect(error.policy).to have_attributes(
85
+ user: user,
86
+ record: comment
87
+ )
88
+ expect(error.policy).to be_a(Project::Admin::CommentPolicy)
89
+ end
90
+ # rubocop:enable Style/MultilineBlockChain
91
+ end
92
+
93
+ it "raises an error with the policy, query and the class name when a Class is given" do
94
+ # rubocop:disable Style/MultilineBlockChain
95
+ expect do
96
+ Pundit.authorize(user, Post, :destroy?)
97
+ end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? Post") do |error|
98
+ expect(error.query).to eq :destroy?
99
+ expect(error.record).to eq Post
100
+ expect(error.policy).to have_attributes(
101
+ user: user,
102
+ record: Post
103
+ )
104
+ expect(error.policy).to be_a(PostPolicy)
65
105
  end
66
106
  # rubocop:enable Style/MultilineBlockChain
67
107
  end
@@ -380,247 +420,22 @@ RSpec.describe Pundit do
380
420
  end
381
421
  end
382
422
 
383
- describe "#verify_authorized" do
384
- it "does nothing when authorized" do
385
- controller.authorize(post)
386
- controller.verify_authorized
387
- end
388
-
389
- it "raises an exception when not authorized" do
390
- expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
391
- end
392
- end
393
-
394
- describe "#verify_policy_scoped" do
395
- it "does nothing when policy_scope is used" do
396
- controller.policy_scope(Post)
397
- controller.verify_policy_scoped
398
- end
399
-
400
- it "raises an exception when policy_scope is not used" do
401
- expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
402
- end
403
- end
404
-
405
- describe "#pundit_policy_authorized?" do
406
- it "is true when authorized" do
407
- controller.authorize(post)
408
- expect(controller.pundit_policy_authorized?).to be true
409
- end
410
-
411
- it "is false when not authorized" do
412
- expect(controller.pundit_policy_authorized?).to be false
413
- end
414
- end
415
-
416
- describe "#pundit_policy_scoped?" do
417
- it "is true when policy_scope is used" do
418
- controller.policy_scope(Post)
419
- expect(controller.pundit_policy_scoped?).to be true
420
- end
421
-
422
- it "is false when policy scope is not used" do
423
- expect(controller.pundit_policy_scoped?).to be false
424
- end
425
- end
426
-
427
- describe "#authorize" do
428
- it "infers the policy name and authorizes based on it" do
429
- expect(controller.authorize(post)).to be_truthy
430
- end
431
-
432
- it "returns the record on successful authorization" do
433
- expect(controller.authorize(post)).to eq(post)
434
- end
435
-
436
- it "returns the record when passed record with namespace " do
437
- expect(controller.authorize([:project, comment], :update?)).to eq(comment)
438
- end
439
-
440
- it "returns the record when passed record with nested namespace " do
441
- expect(controller.authorize([:project, :admin, comment], :update?)).to eq(comment)
442
- end
443
-
444
- it "returns the policy name symbol when passed record with headless policy" do
445
- expect(controller.authorize(:publication, :create?)).to eq(:publication)
446
- end
447
-
448
- it "returns the class when passed record not a particular instance" do
449
- expect(controller.authorize(Post, :show?)).to eq(Post)
450
- end
423
+ describe ".included" do
424
+ it "includes Authorization module" do
425
+ klass = Class.new
451
426
 
452
- it "can be given a different permission to check" do
453
- expect(controller.authorize(post, :show?)).to be_truthy
454
- expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
455
- end
456
-
457
- it "can be given a different policy class" do
458
- expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
459
- end
460
-
461
- it "works with anonymous class policies" do
462
- expect(controller.authorize(article_tag, :show?)).to be_truthy
463
- expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
464
- end
465
-
466
- it "throws an exception when the permission check fails" do
467
- expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
468
- end
469
-
470
- it "throws an exception when a policy cannot be found" do
471
- expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
472
- end
473
-
474
- it "caches the policy" do
475
- expect(controller.policies[post]).to be_nil
476
- controller.authorize(post)
477
- expect(controller.policies[post]).not_to be_nil
478
- end
479
-
480
- it "raises an error when the given record is nil" do
481
- expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
482
- end
483
-
484
- it "raises an error with a invalid policy constructor" do
485
- expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
486
- end
487
- end
488
-
489
- describe "#skip_authorization" do
490
- it "disables authorization verification" do
491
- controller.skip_authorization
492
- expect { controller.verify_authorized }.not_to raise_error
493
- end
494
- end
495
-
496
- describe "#skip_policy_scope" do
497
- it "disables policy scope verification" do
498
- controller.skip_policy_scope
499
- expect { controller.verify_policy_scoped }.not_to raise_error
500
- end
501
- end
502
-
503
- describe "#pundit_user" do
504
- it "returns the same thing as current_user" do
505
- expect(controller.pundit_user).to eq controller.current_user
506
- end
507
- end
508
-
509
- describe "#policy" do
510
- it "returns an instantiated policy" do
511
- policy = controller.policy(post)
512
- expect(policy.user).to eq user
513
- expect(policy.post).to eq post
514
- end
515
-
516
- it "throws an exception if the given policy can't be found" do
517
- expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
518
- end
519
-
520
- it "raises an error with a invalid policy constructor" do
521
- expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
522
- end
523
-
524
- it "allows policy to be injected" do
525
- new_policy = OpenStruct.new
526
- controller.policies[post] = new_policy
527
-
528
- expect(controller.policy(post)).to eq new_policy
529
- end
530
- end
531
-
532
- describe "#policy_scope" do
533
- it "returns an instantiated policy scope" do
534
- expect(controller.policy_scope(Post)).to eq :published
535
- end
536
-
537
- it "allows policy scope class to be overriden" do
538
- expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
539
- end
540
-
541
- it "throws an exception if the given policy can't be found" do
542
- expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
543
- end
544
-
545
- it "raises an error with a invalid policy scope constructor" do
546
- expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
547
- end
548
-
549
- it "allows policy_scope to be injected" do
550
- new_scope = OpenStruct.new
551
- controller.policy_scopes[Post] = new_scope
552
-
553
- expect(controller.policy_scope(Post)).to eq new_scope
554
- end
555
- end
427
+ expect do
428
+ klass.include Pundit
429
+ end.to output.to_stderr
556
430
 
557
- describe "#permitted_attributes" do
558
- it "checks policy for permitted attributes" do
559
- params = ActionController::Parameters.new(
560
- post: {
561
- title: "Hello",
562
- votes: 5,
563
- admin: true
564
- }
565
- )
566
-
567
- action = "update"
568
-
569
- expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
570
- "title" => "Hello",
571
- "votes" => 5
572
- )
573
- expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
574
- end
575
-
576
- it "checks policy for permitted attributes for record of a ActiveModel type" do
577
- params = ActionController::Parameters.new(
578
- customer_post: {
579
- title: "Hello",
580
- votes: 5,
581
- admin: true
582
- }
583
- )
584
-
585
- action = "update"
586
-
587
- expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
588
- "title" => "Hello",
589
- "votes" => 5
590
- )
591
- expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
592
- "votes" => 5
593
- )
431
+ expect(klass).to include Pundit::Authorization
594
432
  end
595
- end
596
433
 
597
- describe "#permitted_attributes_for_action" do
598
- it "is checked if it is defined in the policy" do
599
- params = ActionController::Parameters.new(
600
- post: {
601
- title: "Hello",
602
- body: "blah",
603
- votes: 5,
604
- admin: true
605
- }
606
- )
607
-
608
- action = "revise"
609
- expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
610
- end
611
-
612
- it "can be explicitly set" do
613
- params = ActionController::Parameters.new(
614
- post: {
615
- title: "Hello",
616
- body: "blah",
617
- votes: 5,
618
- admin: true
619
- }
620
- )
621
-
622
- action = "update"
623
- expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
434
+ it "warns about deprecation" do
435
+ klass = Class.new
436
+ expect do
437
+ klass.include Pundit
438
+ end.to output(a_string_starting_with("'include Pundit' is deprecated")).to_stderr
624
439
  end
625
440
  end
626
441
 
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "simplecov"
4
- SimpleCov.start do
5
- add_filter "/spec/"
3
+ if ENV["COVERAGE"]
4
+ require "simplecov"
5
+ SimpleCov.start do
6
+ add_filter "/spec/"
7
+ end
6
8
  end
7
9
 
8
10
  require "pundit"
@@ -16,13 +18,56 @@ require "active_support/core_ext"
16
18
  require "active_model/naming"
17
19
  require "action_controller/metal/strong_parameters"
18
20
 
19
- class PostPolicy < Struct.new(:user, :post)
20
- class Scope < Struct.new(:user, :scope)
21
+ module InstanceTracking
22
+ module ClassMethods
23
+ def instances
24
+ @instances || 0
25
+ end
26
+
27
+ attr_writer :instances
28
+ end
29
+
30
+ def self.prepended(other)
31
+ other.extend(ClassMethods)
32
+ end
33
+
34
+ def initialize(*args, **kwargs, &block)
35
+ self.class.instances += 1
36
+ super(*args, **kwargs, &block)
37
+ end
38
+ end
39
+
40
+ class BasePolicy
41
+ prepend InstanceTracking
42
+
43
+ class BaseScope
44
+ prepend InstanceTracking
45
+
46
+ def initialize(user, scope)
47
+ @user = user
48
+ @scope = scope
49
+ end
50
+
51
+ attr_reader :user, :scope
52
+ end
53
+
54
+ def initialize(user, record)
55
+ @user = user
56
+ @record = record
57
+ end
58
+
59
+ attr_reader :user, :record
60
+ end
61
+
62
+ class PostPolicy < BasePolicy
63
+ class Scope < BaseScope
21
64
  def resolve
22
65
  scope.published
23
66
  end
24
67
  end
25
68
 
69
+ alias post record
70
+
26
71
  def update?
27
72
  post.user == user
28
73
  end
@@ -48,7 +93,13 @@ class PostPolicy < Struct.new(:user, :post)
48
93
  end
49
94
  end
50
95
 
51
- class Post < Struct.new(:user)
96
+ class Post
97
+ def initialize(user = nil)
98
+ @user = user
99
+ end
100
+
101
+ attr_reader :user
102
+
52
103
  def self.published
53
104
  :published
54
105
  end
@@ -67,7 +118,7 @@ class Post < Struct.new(:user)
67
118
  end
68
119
 
69
120
  module Customer
70
- class Post < Post
121
+ class Post < ::Post
71
122
  def model_name
72
123
  OpenStruct.new(param_key: "customer_post")
73
124
  end
@@ -80,6 +131,7 @@ end
80
131
 
81
132
  class CommentScope
82
133
  attr_reader :original_object
134
+
83
135
  def initialize(original_object)
84
136
  @original_object = original_object
85
137
  end
@@ -89,16 +141,18 @@ class CommentScope
89
141
  end
90
142
  end
91
143
 
92
- class CommentPolicy < Struct.new(:user, :comment)
93
- class Scope < Struct.new(:user, :scope)
144
+ class CommentPolicy < BasePolicy
145
+ class Scope < BaseScope
94
146
  def resolve
95
147
  CommentScope.new(scope)
96
148
  end
97
149
  end
150
+
151
+ alias comment record
98
152
  end
99
153
 
100
- class PublicationPolicy < Struct.new(:user, :publication)
101
- class Scope < Struct.new(:user, :scope)
154
+ class PublicationPolicy < BasePolicy
155
+ class Scope < BaseScope
102
156
  def resolve
103
157
  scope.published
104
158
  end
@@ -114,7 +168,7 @@ class Comment
114
168
  end
115
169
 
116
170
  class CommentsRelation
117
- def initialize(empty = false)
171
+ def initialize(empty: false)
118
172
  @empty = empty
119
173
  end
120
174
 
@@ -129,7 +183,9 @@ end
129
183
 
130
184
  class Article; end
131
185
 
132
- class BlogPolicy < Struct.new(:user, :blog); end
186
+ class BlogPolicy < BasePolicy
187
+ alias blog record
188
+ end
133
189
 
134
190
  class Blog; end
135
191
 
@@ -139,7 +195,7 @@ class ArtificialBlog < Blog
139
195
  end
140
196
  end
141
197
 
142
- class ArticleTagOtherNamePolicy < Struct.new(:user, :tag)
198
+ class ArticleTagOtherNamePolicy < BasePolicy
143
199
  def show?
144
200
  true
145
201
  end
@@ -147,6 +203,8 @@ class ArticleTagOtherNamePolicy < Struct.new(:user, :tag)
147
203
  def destroy?
148
204
  false
149
205
  end
206
+
207
+ alias tag record
150
208
  end
151
209
 
152
210
  class ArticleTag
@@ -155,51 +213,63 @@ class ArticleTag
155
213
  end
156
214
  end
157
215
 
158
- class CriteriaPolicy < Struct.new(:user, :criteria); end
216
+ class CriteriaPolicy < BasePolicy
217
+ alias criteria record
218
+ end
159
219
 
160
220
  module Project
161
- class CommentPolicy < Struct.new(:user, :comment)
162
- def update?
163
- true
164
- end
165
-
166
- class Scope < Struct.new(:user, :scope)
221
+ class CommentPolicy < BasePolicy
222
+ class Scope < BaseScope
167
223
  def resolve
168
224
  scope
169
225
  end
170
226
  end
227
+
228
+ def update?
229
+ true
230
+ end
231
+
232
+ alias comment record
171
233
  end
172
234
 
173
- class CriteriaPolicy < Struct.new(:user, :criteria); end
235
+ class CriteriaPolicy < BasePolicy
236
+ alias criteria record
237
+ end
174
238
 
175
- class PostPolicy < Struct.new(:user, :post)
176
- class Scope < Struct.new(:user, :scope)
239
+ class PostPolicy < BasePolicy
240
+ class Scope < BaseScope
177
241
  def resolve
178
242
  scope.read
179
243
  end
180
244
  end
245
+
246
+ alias post record
181
247
  end
182
248
 
183
249
  module Admin
184
- class CommentPolicy < Struct.new(:user, :comment)
250
+ class CommentPolicy < BasePolicy
185
251
  def update?
186
252
  true
187
253
  end
254
+
255
+ def destroy?
256
+ false
257
+ end
188
258
  end
189
259
  end
190
260
  end
191
261
 
192
- class DenierPolicy < Struct.new(:user, :record)
262
+ class DenierPolicy < BasePolicy
193
263
  def update?
194
264
  false
195
265
  end
196
266
  end
197
267
 
198
268
  class Controller
199
- include Pundit
269
+ include Pundit::Authorization
200
270
  # Mark protected methods public so they may be called in test
201
271
  # rubocop:disable Style/AccessModifierDeclarations
202
- public(*Pundit.protected_instance_methods)
272
+ public(*Pundit::Authorization.protected_instance_methods)
203
273
  # rubocop:enable Style/AccessModifierDeclarations
204
274
 
205
275
  attr_reader :current_user, :action_name, :params
@@ -211,7 +281,7 @@ class Controller
211
281
  end
212
282
  end
213
283
 
214
- class NilClassPolicy < Struct.new(:user, :record)
284
+ class NilClassPolicy < BasePolicy
215
285
  class Scope
216
286
  def initialize(*)
217
287
  raise Pundit::NotDefinedError, "Cannot scope NilClass"
@@ -228,6 +298,7 @@ class NilClassPolicy < Struct.new(:user, :record)
228
298
  end
229
299
 
230
300
  class Wiki; end
301
+
231
302
  class WikiPolicy
232
303
  class Scope
233
304
  # deliberate typo method
@@ -238,31 +309,44 @@ end
238
309
  class Thread
239
310
  def self.all; end
240
311
  end
241
- class ThreadPolicy < Struct.new(:user, :thread)
242
- class Scope < Struct.new(:user, :scope)
312
+
313
+ class ThreadPolicy < BasePolicy
314
+ class Scope < BaseScope
243
315
  def resolve
244
- # deliberate wrong useage of the method
316
+ # deliberate wrong usage of the method
245
317
  scope.all(:unvalid, :parameters)
246
318
  end
247
319
  end
248
320
  end
249
321
 
250
- class PostFourFiveSix < Struct.new(:user); end
322
+ class PostFourFiveSix
323
+ def initialize(user)
324
+ @user = user
325
+ end
326
+
327
+ attr_reader(:user)
328
+ end
251
329
 
252
330
  class CommentFourFiveSix; extend ActiveModel::Naming; end
253
331
 
254
332
  module ProjectOneTwoThree
255
- class CommentFourFiveSixPolicy < Struct.new(:user, :post); end
333
+ class CommentFourFiveSixPolicy < BasePolicy; end
256
334
 
257
- class CriteriaFourFiveSixPolicy < Struct.new(:user, :criteria); end
335
+ class CriteriaFourFiveSixPolicy < BasePolicy; end
258
336
 
259
- class PostFourFiveSixPolicy < Struct.new(:user, :post); end
337
+ class PostFourFiveSixPolicy < BasePolicy; end
260
338
 
261
- class TagFourFiveSix < Struct.new(:user); end
339
+ class TagFourFiveSix
340
+ def initialize(user)
341
+ @user = user
342
+ end
343
+
344
+ attr_reader(:user)
345
+ end
262
346
 
263
- class TagFourFiveSixPolicy < Struct.new(:user, :tag); end
347
+ class TagFourFiveSixPolicy < BasePolicy; end
264
348
 
265
349
  class AvatarFourFiveSix; extend ActiveModel::Naming; end
266
350
 
267
- class AvatarFourFiveSixPolicy < Struct.new(:user, :avatar); end
351
+ class AvatarFourFiveSixPolicy < BasePolicy; end
268
352
  end