pundit 2.4.0 → 2.5.1

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -42
  3. data/README.md +31 -1
  4. data/lib/generators/pundit/install/install_generator.rb +3 -1
  5. data/lib/generators/pundit/policy/policy_generator.rb +3 -1
  6. data/lib/generators/rspec/policy_generator.rb +4 -1
  7. data/lib/generators/test_unit/policy_generator.rb +4 -1
  8. data/lib/pundit/authorization.rb +170 -77
  9. data/lib/pundit/cache_store/legacy_store.rb +10 -0
  10. data/lib/pundit/cache_store/null_store.rb +12 -0
  11. data/lib/pundit/cache_store.rb +24 -0
  12. data/lib/pundit/context.rb +89 -26
  13. data/lib/pundit/error.rb +71 -0
  14. data/lib/pundit/helper.rb +16 -0
  15. data/lib/pundit/policy_finder.rb +33 -1
  16. data/lib/pundit/railtie.rb +20 -0
  17. data/lib/pundit/rspec.rb +69 -6
  18. data/lib/pundit/version.rb +2 -1
  19. data/lib/pundit.rb +27 -61
  20. metadata +19 -179
  21. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -20
  22. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -26
  23. data/.github/PULL_REQUEST_TEMPLATE/gem_release_template.md +0 -8
  24. data/.github/pull_request_template.md +0 -9
  25. data/.github/workflows/main.yml +0 -112
  26. data/.github/workflows/push_gem.yml +0 -33
  27. data/.gitignore +0 -19
  28. data/.rubocop.yml +0 -63
  29. data/.yardopts +0 -1
  30. data/CODE_OF_CONDUCT.md +0 -28
  31. data/CONTRIBUTING.md +0 -31
  32. data/Gemfile +0 -8
  33. data/Rakefile +0 -20
  34. data/config/rubocop-rspec.yml +0 -5
  35. data/pundit.gemspec +0 -35
  36. data/spec/authorization_spec.rb +0 -274
  37. data/spec/dsl_spec.rb +0 -30
  38. data/spec/generators_spec.rb +0 -43
  39. data/spec/policies/post_policy_spec.rb +0 -49
  40. data/spec/policy_finder_spec.rb +0 -187
  41. data/spec/pundit_spec.rb +0 -448
  42. data/spec/spec_helper.rb +0 -352
  43. /data/lib/generators/pundit/install/templates/{application_policy.rb → application_policy.rb.tt} +0 -0
  44. /data/lib/generators/pundit/policy/templates/{policy.rb → policy.rb.tt} +0 -0
  45. /data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +0 -0
  46. /data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
data/CODE_OF_CONDUCT.md DELETED
@@ -1,28 +0,0 @@
1
- # Contributor Code of Conduct
2
-
3
- As contributors and maintainers of this project, we pledge to respect all
4
- people who contribute through reporting issues, posting feature requests,
5
- updating documentation, submitting pull requests or patches, and other
6
- activities.
7
-
8
- We are committed to making participation in this project a harassment-free
9
- experience for everyone, regardless of level of experience, gender, gender
10
- identity and expression, sexual orientation, disability, personal appearance,
11
- body size, race, age, or religion.
12
-
13
- Examples of unacceptable behavior by participants include the use of sexual
14
- language or imagery, derogatory comments or personal attacks, trolling, public
15
- or private harassment, insults, or other unprofessional conduct.
16
-
17
- Project maintainers have the right and responsibility to remove, edit, or
18
- reject comments, commits, code, wiki edits, issues, and other contributions
19
- that are not aligned to this Code of Conduct. Project maintainers who do not
20
- follow the Code of Conduct may be removed from the project team.
21
-
22
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
23
- reported by opening an issue or contacting one or more of the project
24
- maintainers.
25
-
26
- This Code of Conduct is adapted from the [Contributor
27
- Covenant](http:contributor-covenant.org), version 1.0.0, available at
28
- [https://contributor-covenant.org/version/1/0/0/](https://contributor-covenant.org/version/1/0/0/)
data/CONTRIBUTING.md DELETED
@@ -1,31 +0,0 @@
1
- ## Security issues
2
-
3
- If you have found a security related issue, please do not file an issue on GitHub or send a PR addressing the issue. Refer to [SECURITY.md](./SECURITY.md) for instructions.
4
-
5
- ## Reporting issues
6
-
7
- Please try to answer the following questions in your bug report:
8
-
9
- - What did you do?
10
- - What did you expect to happen?
11
- - What happened instead?
12
-
13
- Make sure to include as much relevant information as possible. Ruby version,
14
- Pundit version, OS version and any stack traces you have are very valuable.
15
-
16
- ## Pull Requests
17
-
18
- - **Add tests!** Your patch won't be accepted if it doesn't have tests.
19
-
20
- - **Document any change in behaviour**. Make sure the README and any other
21
- relevant documentation are kept up-to-date.
22
-
23
- - **Create topic branches**. Please don't ask us to pull from your main branch.
24
-
25
- - **One pull request per feature**. If you want to do more than one thing, send
26
- multiple pull requests.
27
-
28
- - **Send coherent history**. Make sure each individual commit in your pull
29
- request is meaningful. If you had to make multiple intermediate commits while
30
- developing, please squash them before sending them to us.
31
- - **Update the CHANGELOG.** Don't forget to add your new changes to the CHANGELOG.
data/Gemfile DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gemspec
6
-
7
- # https://github.com/ruby/psych/issues/655
8
- gem "psych", "!= 5.1.1", platforms: %i[jruby]
data/Rakefile DELETED
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "rubygems"
4
- require "bundler/gem_tasks"
5
- require "rspec/core/rake_task"
6
- require "yard"
7
- require "rubocop/rake_task"
8
-
9
- RuboCop::RakeTask.new
10
-
11
- desc "Run all examples"
12
- RSpec::Core::RakeTask.new(:spec) do |t|
13
- t.rspec_opts = %w[--color]
14
- end
15
-
16
- YARD::Rake::YardocTask.new do |t|
17
- t.files = ["lib/**/*.rb"]
18
- end
19
-
20
- task default: :spec
@@ -1,5 +0,0 @@
1
- RSpec:
2
- Language:
3
- ExampleGroups:
4
- Regular:
5
- - permissions
data/pundit.gemspec DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path("lib", __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "pundit/version"
6
-
7
- Gem::Specification.new do |gem|
8
- gem.name = "pundit"
9
- gem.version = Pundit::VERSION
10
- gem.authors = ["Jonas Nicklas", "Varvet AB"]
11
- gem.email = ["jonas.nicklas@gmail.com", "info@varvet.com"]
12
- gem.description = "Object oriented authorization for Rails applications"
13
- gem.summary = "OO authorization for Rails"
14
- gem.homepage = "https://github.com/varvet/pundit"
15
- gem.license = "MIT"
16
-
17
- gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
18
- gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
- gem.require_paths = ["lib"]
21
-
22
- gem.metadata = { "rubygems_mfa_required" => "true" }
23
-
24
- gem.add_dependency "activesupport", ">= 3.0.0"
25
- gem.add_development_dependency "actionpack", ">= 3.0.0"
26
- gem.add_development_dependency "activemodel", ">= 3.0.0"
27
- gem.add_development_dependency "bundler"
28
- gem.add_development_dependency "pry"
29
- gem.add_development_dependency "railties", ">= 3.0.0"
30
- gem.add_development_dependency "rake"
31
- gem.add_development_dependency "rspec", ">= 3.0.0"
32
- gem.add_development_dependency "rubocop"
33
- gem.add_development_dependency "simplecov", ">= 0.17.0"
34
- gem.add_development_dependency "yard"
35
- end
@@ -1,274 +0,0 @@
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 DELETED
@@ -1,30 +0,0 @@
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
@@ -1,43 +0,0 @@
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
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "spec_helper"
4
-
5
- RSpec.describe PostPolicy do
6
- let(:user) { double }
7
- let(:own_post) { double(user: user) }
8
- let(:other_post) { double(user: double) }
9
- subject { described_class }
10
-
11
- permissions :update?, :show? do
12
- it "is successful when all permissions match" do
13
- should permit(user, own_post)
14
- end
15
-
16
- it "fails when any permissions do not match" do
17
- expect do
18
- should permit(user, other_post)
19
- end.to raise_error(RSpec::Expectations::ExpectationNotMetError)
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
48
- end
49
- end