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.
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Pundit::Authorization do
6
+ def to_params(*args, **kwargs, &block)
7
+ ActionController::Parameters.new(*args, **kwargs, &block)
8
+ end
9
+
10
+ let(:controller) { Controller.new(user, "update", to_params({})) }
11
+ let(:user) { double }
12
+ let(:post) { Post.new(user) }
13
+ let(:comment) { Comment.new }
14
+ let(:article) { Article.new }
15
+ let(:article_tag) { ArticleTag.new }
16
+ let(:wiki) { Wiki.new }
17
+
18
+ describe "#verify_authorized" do
19
+ it "does nothing when authorized" do
20
+ controller.authorize(post)
21
+ controller.verify_authorized
22
+ end
23
+
24
+ it "raises an exception when not authorized" do
25
+ expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
26
+ end
27
+ end
28
+
29
+ describe "#verify_policy_scoped" do
30
+ it "does nothing when policy_scope is used" do
31
+ controller.policy_scope(Post)
32
+ controller.verify_policy_scoped
33
+ end
34
+
35
+ it "raises an exception when policy_scope is not used" do
36
+ expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
37
+ end
38
+ end
39
+
40
+ describe "#pundit_policy_authorized?" do
41
+ it "is true when authorized" do
42
+ controller.authorize(post)
43
+ expect(controller.pundit_policy_authorized?).to be true
44
+ end
45
+
46
+ it "is false when not authorized" do
47
+ expect(controller.pundit_policy_authorized?).to be false
48
+ end
49
+ end
50
+
51
+ describe "#pundit_policy_scoped?" do
52
+ it "is true when policy_scope is used" do
53
+ controller.policy_scope(Post)
54
+ expect(controller.pundit_policy_scoped?).to be true
55
+ end
56
+
57
+ it "is false when policy scope is not used" do
58
+ expect(controller.pundit_policy_scoped?).to be false
59
+ end
60
+ end
61
+
62
+ describe "#authorize" do
63
+ it "infers the policy name and authorizes based on it" do
64
+ expect(controller.authorize(post)).to be_truthy
65
+ end
66
+
67
+ it "returns the record on successful authorization" do
68
+ expect(controller.authorize(post)).to eq(post)
69
+ end
70
+
71
+ it "returns the record when passed record with namespace " do
72
+ expect(controller.authorize([:project, comment], :update?)).to eq(comment)
73
+ end
74
+
75
+ it "returns the record when passed record with nested namespace " do
76
+ expect(controller.authorize([:project, :admin, comment], :update?)).to eq(comment)
77
+ end
78
+
79
+ it "returns the policy name symbol when passed record with headless policy" do
80
+ expect(controller.authorize(:publication, :create?)).to eq(:publication)
81
+ end
82
+
83
+ it "returns the class when passed record not a particular instance" do
84
+ expect(controller.authorize(Post, :show?)).to eq(Post)
85
+ end
86
+
87
+ it "can be given a different permission to check" do
88
+ expect(controller.authorize(post, :show?)).to be_truthy
89
+ expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
90
+ end
91
+
92
+ it "can be given a different policy class" do
93
+ expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
94
+ end
95
+
96
+ it "works with anonymous class policies" do
97
+ expect(controller.authorize(article_tag, :show?)).to be_truthy
98
+ expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
99
+ end
100
+
101
+ it "throws an exception when the permission check fails" do
102
+ expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
103
+ end
104
+
105
+ it "throws an exception when a policy cannot be found" do
106
+ expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
107
+ end
108
+
109
+ it "caches the policy" do
110
+ expect(controller.policies[post]).to be_nil
111
+ controller.authorize(post)
112
+ expect(controller.policies[post]).not_to be_nil
113
+ end
114
+
115
+ it "raises an error when the given record is nil" do
116
+ expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
117
+ end
118
+
119
+ it "raises an error with a invalid policy constructor" do
120
+ expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
121
+ end
122
+ end
123
+
124
+ describe "#skip_authorization" do
125
+ it "disables authorization verification" do
126
+ controller.skip_authorization
127
+ expect { controller.verify_authorized }.not_to raise_error
128
+ end
129
+ end
130
+
131
+ describe "#skip_policy_scope" do
132
+ it "disables policy scope verification" do
133
+ controller.skip_policy_scope
134
+ expect { controller.verify_policy_scoped }.not_to raise_error
135
+ end
136
+ end
137
+
138
+ describe "#pundit_user" do
139
+ it "returns the same thing as current_user" do
140
+ expect(controller.pundit_user).to eq controller.current_user
141
+ end
142
+ end
143
+
144
+ describe "#policy" do
145
+ it "returns an instantiated policy" do
146
+ policy = controller.policy(post)
147
+ expect(policy.user).to eq user
148
+ expect(policy.post).to eq post
149
+ end
150
+
151
+ it "throws an exception if the given policy can't be found" do
152
+ expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
153
+ end
154
+
155
+ it "raises an error with a invalid policy constructor" do
156
+ expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
157
+ end
158
+
159
+ it "allows policy to be injected" do
160
+ new_policy = OpenStruct.new
161
+ controller.policies[post] = new_policy
162
+
163
+ expect(controller.policy(post)).to eq new_policy
164
+ end
165
+ end
166
+
167
+ describe "#policy_scope" do
168
+ it "returns an instantiated policy scope" do
169
+ expect(controller.policy_scope(Post)).to eq :published
170
+ end
171
+
172
+ it "allows policy scope class to be overridden" do
173
+ expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
174
+ end
175
+
176
+ it "throws an exception if the given policy can't be found" do
177
+ expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
178
+ end
179
+
180
+ it "raises an error with a invalid policy scope constructor" do
181
+ expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
182
+ end
183
+
184
+ it "allows policy_scope to be injected" do
185
+ new_scope = OpenStruct.new
186
+ controller.policy_scopes[Post] = new_scope
187
+
188
+ expect(controller.policy_scope(Post)).to eq new_scope
189
+ end
190
+ end
191
+
192
+ describe "#permitted_attributes" do
193
+ it "checks policy for permitted attributes" do
194
+ params = to_params(
195
+ post: {
196
+ title: "Hello",
197
+ votes: 5,
198
+ admin: true
199
+ }
200
+ )
201
+
202
+ action = "update"
203
+
204
+ expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
205
+ "title" => "Hello",
206
+ "votes" => 5
207
+ )
208
+ expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
209
+ end
210
+
211
+ it "checks policy for permitted attributes for record of a ActiveModel type" do
212
+ customer_post = Customer::Post.new(user)
213
+ params = to_params(
214
+ customer_post: {
215
+ title: "Hello",
216
+ votes: 5,
217
+ admin: true
218
+ }
219
+ )
220
+
221
+ action = "update"
222
+
223
+ expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
224
+ "title" => "Hello",
225
+ "votes" => 5
226
+ )
227
+ expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
228
+ "votes" => 5
229
+ )
230
+ end
231
+
232
+ it "goes through the policy cache" do
233
+ params = to_params(post: { title: "Hello" })
234
+ user = double
235
+ post = Post.new(user)
236
+ controller = Controller.new(user, "update", params)
237
+
238
+ expect do
239
+ expect(controller.permitted_attributes(post)).to be_truthy
240
+ expect(controller.permitted_attributes(post)).to be_truthy
241
+ end.to change { PostPolicy.instances }.by(1)
242
+ end
243
+ end
244
+
245
+ describe "#permitted_attributes_for_action" do
246
+ it "is checked if it is defined in the policy" do
247
+ params = to_params(
248
+ post: {
249
+ title: "Hello",
250
+ body: "blah",
251
+ votes: 5,
252
+ admin: true
253
+ }
254
+ )
255
+
256
+ action = "revise"
257
+ expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
258
+ end
259
+
260
+ it "can be explicitly set" do
261
+ params = to_params(
262
+ post: {
263
+ title: "Hello",
264
+ body: "blah",
265
+ votes: 5,
266
+ admin: true
267
+ }
268
+ )
269
+
270
+ action = "update"
271
+ expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
272
+ end
273
+ end
274
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "Pundit RSpec DSL" do
6
+ let(:fake_rspec) do
7
+ double = class_double(RSpec::ExampleGroups)
8
+ double.extend(::Pundit::RSpec::DSL)
9
+ double
10
+ end
11
+ let(:block) { proc { "block content" } }
12
+
13
+ it "calls describe with the correct metadata and without :focus" do
14
+ expected_metadata = { permissions: %i[item1 item2], caller: instance_of(Array) }
15
+ expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
16
+ expect(block.call).to eq("block content")
17
+ end
18
+
19
+ fake_rspec.permissions(:item1, :item2, &block)
20
+ end
21
+
22
+ it "calls describe with the correct metadata and with :focus" do
23
+ expected_metadata = { permissions: %i[item1 item2], caller: instance_of(Array), focus: true }
24
+ expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
25
+ expect(block.call).to eq("block content")
26
+ end
27
+
28
+ fake_rspec.permissions(:item1, :item2, :focus, &block)
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "tmpdir"
5
+
6
+ require "rails/generators"
7
+ require "generators/pundit/install/install_generator"
8
+ require "generators/pundit/policy/policy_generator"
9
+
10
+ RSpec.describe "generators" do
11
+ before(:all) do
12
+ @tmpdir = Dir.mktmpdir
13
+
14
+ Dir.chdir(@tmpdir) do
15
+ Pundit::Generators::InstallGenerator.new([], { quiet: true }).invoke_all
16
+ Pundit::Generators::PolicyGenerator.new(%w[Widget], { quiet: true }).invoke_all
17
+
18
+ require "./app/policies/application_policy"
19
+ require "./app/policies/widget_policy"
20
+ end
21
+ end
22
+
23
+ after(:all) do
24
+ FileUtils.remove_entry(@tmpdir)
25
+ end
26
+
27
+ describe "WidgetPolicy", type: :policy do
28
+ permissions :index?, :show?, :create?, :new?, :update?, :edit?, :destroy? do
29
+ it "has safe defaults" do
30
+ expect(WidgetPolicy).not_to permit(double("User"), double("Widget"))
31
+ end
32
+ end
33
+
34
+ describe "WidgetPolicy::Scope" do
35
+ describe "#resolve" do
36
+ it "raises a descriptive error" do
37
+ scope = WidgetPolicy::Scope.new(double("User"), double("User.all"))
38
+ expect { scope.resolve }.to raise_error(NoMethodError, /WidgetPolicy::Scope/)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -18,5 +18,32 @@ RSpec.describe PostPolicy do
18
18
  should permit(user, other_post)
19
19
  end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
20
20
  end
21
+
22
+ it "uses the default description if not overridden" do
23
+ expect(permit(user, own_post).description).to eq("permit #{user.inspect} and #{own_post.inspect}")
24
+ end
25
+
26
+ context "when the matcher description is overridden" do
27
+ after do
28
+ Pundit::RSpec::Matchers.description = nil
29
+ end
30
+
31
+ it "sets a custom matcher description with a Proc" do
32
+ allow(user).to receive(:role).and_return("default_role")
33
+ allow(own_post).to receive(:id).and_return(1)
34
+
35
+ Pundit::RSpec::Matchers.description = lambda { |user, record|
36
+ "permit user with role #{user.role} to access record with ID #{record.id}"
37
+ }
38
+
39
+ description = permit(user, own_post).description
40
+ expect(description).to eq("permit user with role default_role to access record with ID 1")
41
+ end
42
+
43
+ it "sets a custom matcher description with a string" do
44
+ Pundit::RSpec::Matchers.description = "permit user"
45
+ expect(permit(user, own_post).description).to eq("permit user")
46
+ end
47
+ end
21
48
  end
22
49
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "spec_helper"
4
4
 
5
+ class Foo; end
5
6
  RSpec.describe Pundit::PolicyFinder do
6
7
  let(:user) { double }
7
8
  let(:post) { Post.new(user) }
@@ -114,7 +115,6 @@ RSpec.describe Pundit::PolicyFinder do
114
115
 
115
116
  context "with a class that doesn't have an associated policy" do
116
117
  it "returns nil" do
117
- class Foo; end
118
118
  object = described_class.new(Foo)
119
119
 
120
120
  expect(object.policy).to eq nil