pundit 1.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pundit.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "pundit/version"
2
4
  require "pundit/policy_finder"
3
5
  require "active_support/concern"
@@ -5,6 +7,12 @@ require "active_support/core_ext/string/inflections"
5
7
  require "active_support/core_ext/object/blank"
6
8
  require "active_support/core_ext/module/introspection"
7
9
  require "active_support/dependencies/autoload"
10
+ require "pundit/authorization"
11
+
12
+ # @api private
13
+ # To avoid name clashes with common Error naming when mixing in Pundit,
14
+ # keep it here with compact class style definition.
15
+ class Pundit::Error < StandardError; end # rubocop:disable Style/ClassAndModuleChildren
8
16
 
9
17
  # @api public
10
18
  module Pundit
@@ -13,10 +21,7 @@ module Pundit
13
21
  # @api private
14
22
  module Generators; end
15
23
 
16
- # @api private
17
- class Error < StandardError; end
18
-
19
- # Error that will be raiser when authorization has failed
24
+ # Error that will be raised when authorization has failed
20
25
  class NotAuthorizedError < Error
21
26
  attr_reader :query, :record, :policy
22
27
 
@@ -28,13 +33,16 @@ module Pundit
28
33
  @record = options[:record]
29
34
  @policy = options[:policy]
30
35
 
31
- message = options.fetch(:message) { "not allowed to #{query} this #{record.inspect}" }
36
+ message = options.fetch(:message) { "not allowed to #{query} this #{record.class}" }
32
37
  end
33
38
 
34
39
  super(message)
35
40
  end
36
41
  end
37
42
 
43
+ # Error that will be raised if a policy or policy scope constructor is not called correctly.
44
+ class InvalidConstructorError < Error; end
45
+
38
46
  # Error that will be raised if a controller action has not called the
39
47
  # `authorize` or `skip_authorization` methods.
40
48
  class AuthorizationNotPerformedError < Error; end
@@ -46,7 +54,12 @@ module Pundit
46
54
  # Error that will be raised if a policy or policy scope is not defined.
47
55
  class NotDefinedError < Error; end
48
56
 
49
- extend ActiveSupport::Concern
57
+ def self.included(base)
58
+ ActiveSupport::Deprecation.warn <<~WARNING
59
+ 'include Pundit' is deprecated. Please use 'include Pundit::Authorization' instead.
60
+ WARNING
61
+ base.include Authorization
62
+ end
50
63
 
51
64
  class << self
52
65
  # Retrieves the policy for the given record, initializing it with the
@@ -54,62 +67,99 @@ module Pundit
54
67
  # authorized to perform the given action.
55
68
  #
56
69
  # @param user [Object] the user that initiated the action
57
- # @param record [Object] the object we're checking permissions of
58
- # @param record [Symbol] the query method to check on the policy (e.g. `:show?`)
70
+ # @param possibly_namespaced_record [Object, Array] the object we're checking permissions of
71
+ # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
72
+ # @param policy_class [Class] the policy class we want to force use of
73
+ # @param cache [#[], #[]=] a Hash-like object to cache the found policy instance in
59
74
  # @raise [NotAuthorizedError] if the given query method returned false
60
- # @return [true] Always returns true
61
- def authorize(user, record, query)
62
- policy = policy!(user, record)
63
-
64
- unless policy.public_send(query)
65
- raise NotAuthorizedError, query: query, record: record, policy: policy
75
+ # @return [Object] Always returns the passed object record
76
+ def authorize(user, possibly_namespaced_record, query, policy_class: nil, cache: {})
77
+ record = pundit_model(possibly_namespaced_record)
78
+ policy = if policy_class
79
+ policy_class.new(user, record)
80
+ else
81
+ cache[possibly_namespaced_record] ||= policy!(user, possibly_namespaced_record)
66
82
  end
67
83
 
68
- true
84
+ raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
85
+
86
+ record
69
87
  end
70
88
 
71
89
  # Retrieves the policy scope for the given record.
72
90
  #
73
- # @see https://github.com/elabs/pundit#scopes
91
+ # @see https://github.com/varvet/pundit#scopes
74
92
  # @param user [Object] the user that initiated the action
75
- # @param record [Object] the object we're retrieving the policy scope for
93
+ # @param scope [Object] the object we're retrieving the policy scope for
94
+ # @raise [InvalidConstructorError] if the policy constructor called incorrectly
76
95
  # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
77
96
  def policy_scope(user, scope)
78
- policy_scope = PolicyFinder.new(scope).scope
79
- policy_scope.new(user, scope).resolve if policy_scope
97
+ policy_scope_class = PolicyFinder.new(scope).scope
98
+ return unless policy_scope_class
99
+
100
+ begin
101
+ policy_scope = policy_scope_class.new(user, pundit_model(scope))
102
+ rescue ArgumentError
103
+ raise InvalidConstructorError, "Invalid #<#{policy_scope_class}> constructor is called"
104
+ end
105
+
106
+ policy_scope.resolve
80
107
  end
81
108
 
82
109
  # Retrieves the policy scope for the given record.
83
110
  #
84
- # @see https://github.com/elabs/pundit#scopes
111
+ # @see https://github.com/varvet/pundit#scopes
85
112
  # @param user [Object] the user that initiated the action
86
- # @param record [Object] the object we're retrieving the policy scope for
113
+ # @param scope [Object] the object we're retrieving the policy scope for
87
114
  # @raise [NotDefinedError] if the policy scope cannot be found
115
+ # @raise [InvalidConstructorError] if the policy constructor called incorrectly
88
116
  # @return [Scope{#resolve}] instance of scope class which can resolve to a scope
89
117
  def policy_scope!(user, scope)
90
- PolicyFinder.new(scope).scope!.new(user, scope).resolve
118
+ policy_scope_class = PolicyFinder.new(scope).scope!
119
+ return unless policy_scope_class
120
+
121
+ begin
122
+ policy_scope = policy_scope_class.new(user, pundit_model(scope))
123
+ rescue ArgumentError
124
+ raise InvalidConstructorError, "Invalid #<#{policy_scope_class}> constructor is called"
125
+ end
126
+
127
+ policy_scope.resolve
91
128
  end
92
129
 
93
130
  # Retrieves the policy for the given record.
94
131
  #
95
- # @see https://github.com/elabs/pundit#policies
132
+ # @see https://github.com/varvet/pundit#policies
96
133
  # @param user [Object] the user that initiated the action
97
134
  # @param record [Object] the object we're retrieving the policy for
135
+ # @raise [InvalidConstructorError] if the policy constructor called incorrectly
98
136
  # @return [Object, nil] instance of policy class with query methods
99
137
  def policy(user, record)
100
138
  policy = PolicyFinder.new(record).policy
101
- policy.new(user, record) if policy
139
+ policy&.new(user, pundit_model(record))
140
+ rescue ArgumentError
141
+ raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
102
142
  end
103
143
 
104
144
  # Retrieves the policy for the given record.
105
145
  #
106
- # @see https://github.com/elabs/pundit#policies
146
+ # @see https://github.com/varvet/pundit#policies
107
147
  # @param user [Object] the user that initiated the action
108
148
  # @param record [Object] the object we're retrieving the policy for
109
149
  # @raise [NotDefinedError] if the policy cannot be found
150
+ # @raise [InvalidConstructorError] if the policy constructor called incorrectly
110
151
  # @return [Object] instance of policy class with query methods
111
152
  def policy!(user, record)
112
- PolicyFinder.new(record).policy!.new(user, record)
153
+ policy = PolicyFinder.new(record).policy!
154
+ policy.new(user, pundit_model(record))
155
+ rescue ArgumentError
156
+ raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
157
+ end
158
+
159
+ private
160
+
161
+ def pundit_model(record)
162
+ record.is_a?(Array) ? record.last : record
113
163
  end
114
164
  end
115
165
 
@@ -119,168 +169,4 @@ module Pundit
119
169
  pundit_policy_scope(scope)
120
170
  end
121
171
  end
122
-
123
- included do
124
- helper Helper if respond_to?(:helper)
125
- if respond_to?(:helper_method)
126
- helper_method :policy
127
- helper_method :pundit_policy_scope
128
- helper_method :pundit_user
129
- end
130
- if respond_to?(:hide_action)
131
- hide_action :policy
132
- hide_action :policy_scope
133
- hide_action :policies
134
- hide_action :policy_scopes
135
- hide_action :authorize
136
- hide_action :verify_authorized
137
- hide_action :verify_policy_scoped
138
- hide_action :permitted_attributes
139
- hide_action :pundit_user
140
- hide_action :skip_authorization
141
- hide_action :skip_policy_scope
142
- hide_action :pundit_policy_authorized?
143
- hide_action :pundit_policy_scoped?
144
- end
145
- end
146
-
147
- # @return [Boolean] whether authorization has been performed, i.e. whether
148
- # one {#authorize} or {#skip_authorization} has been called
149
- def pundit_policy_authorized?
150
- !!@_pundit_policy_authorized
151
- end
152
-
153
- # @return [Boolean] whether policy scoping has been performed, i.e. whether
154
- # one {#policy_scope} or {#skip_policy_scope} has been called
155
- def pundit_policy_scoped?
156
- !!@_pundit_policy_scoped
157
- end
158
-
159
- # Raises an error if authorization has not been performed, usually used as an
160
- # `after_action` filter to prevent programmer error in forgetting to call
161
- # {#authorize} or {#skip_authorization}.
162
- #
163
- # @see https://github.com/elabs/pundit#ensuring-policies-are-used
164
- # @raise [AuthorizationNotPerformedError] if authorization has not been performed
165
- # @return [void]
166
- def verify_authorized
167
- raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
168
- end
169
-
170
- # Raises an error if policy scoping has not been performed, usually used as an
171
- # `after_action` filter to prevent programmer error in forgetting to call
172
- # {#policy_scope} or {#skip_policy_scope} in index actions.
173
- #
174
- # @see https://github.com/elabs/pundit#ensuring-policies-are-used
175
- # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
176
- # @return [void]
177
- def verify_policy_scoped
178
- raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
179
- end
180
-
181
- # Retrieves the policy for the given record, initializing it with the record
182
- # and current user and finally throwing an error if the user is not
183
- # authorized to perform the given action.
184
- #
185
- # @param record [Object] the object we're checking permissions of
186
- # @param record [Symbol, nil] the query method to check on the policy (e.g. `:show?`)
187
- # @raise [NotAuthorizedError] if the given query method returned false
188
- # @return [true] Always returns true
189
- def authorize(record, query = nil)
190
- query ||= params[:action].to_s + "?"
191
-
192
- @_pundit_policy_authorized = true
193
-
194
- policy = policy(record)
195
-
196
- unless policy.public_send(query)
197
- raise NotAuthorizedError, query: query, record: record, policy: policy
198
- end
199
-
200
- true
201
- end
202
-
203
- # Allow this action not to perform authorization.
204
- #
205
- # @see https://github.com/elabs/pundit#ensuring-policies-are-used
206
- # @return [void]
207
- def skip_authorization
208
- @_pundit_policy_authorized = true
209
- end
210
-
211
- # Allow this action not to perform policy scoping.
212
- #
213
- # @see https://github.com/elabs/pundit#ensuring-policies-are-used
214
- # @return [void]
215
- def skip_policy_scope
216
- @_pundit_policy_scoped = true
217
- end
218
-
219
- # Retrieves the policy scope for the given record.
220
- #
221
- # @see https://github.com/elabs/pundit#scopes
222
- # @param record [Object] the object we're retrieving the policy scope for
223
- # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
224
- def policy_scope(scope)
225
- @_pundit_policy_scoped = true
226
- pundit_policy_scope(scope)
227
- end
228
-
229
- # Retrieves the policy for the given record.
230
- #
231
- # @see https://github.com/elabs/pundit#policies
232
- # @param record [Object] the object we're retrieving the policy for
233
- # @return [Object, nil] instance of policy class with query methods
234
- def policy(record)
235
- policies[record] ||= Pundit.policy!(pundit_user, record)
236
- end
237
-
238
- # Retrieves a set of permitted attributes from the policy by instantiating
239
- # the policy class for the given record and calling `permitted_attributes` on
240
- # it, or `permitted_attributes_for_{action}` if it is defined. It then infers
241
- # what key the record should have in the params hash and retrieves the
242
- # permitted attributes from the params hash under that key.
243
- #
244
- # @see https://github.com/elabs/pundit#strong-parameters
245
- # @param record [Object] the object we're retrieving permitted attributes for
246
- # @return [Hash{String => Object}] the permitted attributes
247
- def permitted_attributes(record, action = params[:action])
248
- param_key = PolicyFinder.new(record).param_key
249
- policy = policy(record)
250
- method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
251
- "permitted_attributes_for_#{action}"
252
- else
253
- "permitted_attributes"
254
- end
255
- params.require(param_key).permit(policy.public_send(method_name))
256
- end
257
-
258
- # Cache of policies. You should not rely on this method.
259
- #
260
- # @api private
261
- def policies
262
- @_pundit_policies ||= {}
263
- end
264
-
265
- # Cache of policy scope. You should not rely on this method.
266
- #
267
- # @api private
268
- def policy_scopes
269
- @_pundit_policy_scopes ||= {}
270
- end
271
-
272
- # Hook method which allows customizing which user is passed to policies and
273
- # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
274
- #
275
- # @see https://github.com/elabs/pundit#customize-pundit-user
276
- # @return [Object] the user object to be used with pundit
277
- def pundit_user
278
- current_user
279
- end
280
-
281
- private
282
-
283
- def pundit_policy_scope(scope)
284
- policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
285
- end
286
172
  end
data/pundit.gemspec CHANGED
@@ -1,30 +1,33 @@
1
- # -*- encoding: utf-8 -*-
2
- lib = File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require "pundit/version"
5
6
 
6
7
  Gem::Specification.new do |gem|
7
8
  gem.name = "pundit"
8
9
  gem.version = Pundit::VERSION
9
- gem.authors = ["Jonas Nicklas", "Elabs AB"]
10
+ gem.authors = ["Jonas Nicklas", "Varvet AB"]
10
11
  gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se"]
11
12
  gem.description = "Object oriented authorization for Rails applications"
12
13
  gem.summary = "OO authorization for Rails"
13
- gem.homepage = "https://github.com/elabs/pundit"
14
+ gem.homepage = "https://github.com/varvet/pundit"
14
15
  gem.license = "MIT"
15
16
 
16
- gem.files = `git ls-files`.split($/)
17
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
18
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
18
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
20
  gem.require_paths = ["lib"]
20
21
 
21
22
  gem.add_dependency "activesupport", ">= 3.0.0"
22
- gem.add_development_dependency "activemodel", ">= 3.0.0"
23
23
  gem.add_development_dependency "actionpack", ">= 3.0.0"
24
- gem.add_development_dependency "bundler", "~> 1.3"
25
- gem.add_development_dependency "rspec", ">=2.0.0"
24
+ gem.add_development_dependency "activemodel", ">= 3.0.0"
25
+ gem.add_development_dependency "bundler"
26
26
  gem.add_development_dependency "pry"
27
+ gem.add_development_dependency "railties", ">= 3.0.0"
27
28
  gem.add_development_dependency "rake"
29
+ gem.add_development_dependency "rspec", ">= 3.0.0"
30
+ gem.add_development_dependency "rubocop", "1.24.0"
31
+ gem.add_development_dependency "simplecov", ">= 0.17.0"
28
32
  gem.add_development_dependency "yard"
29
- gem.add_development_dependency "rubocop"
30
33
  end
@@ -0,0 +1,258 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Pundit::Authorization do
6
+ let(:controller) { Controller.new(user, "update", {}) }
7
+ let(:user) { double }
8
+ let(:post) { Post.new(user) }
9
+ let(:customer_post) { Customer::Post.new(user) }
10
+ let(:comment) { Comment.new }
11
+ let(:article) { Article.new }
12
+ let(:article_tag) { ArticleTag.new }
13
+ let(:wiki) { Wiki.new }
14
+
15
+ describe "#verify_authorized" do
16
+ it "does nothing when authorized" do
17
+ controller.authorize(post)
18
+ controller.verify_authorized
19
+ end
20
+
21
+ it "raises an exception when not authorized" do
22
+ expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
23
+ end
24
+ end
25
+
26
+ describe "#verify_policy_scoped" do
27
+ it "does nothing when policy_scope is used" do
28
+ controller.policy_scope(Post)
29
+ controller.verify_policy_scoped
30
+ end
31
+
32
+ it "raises an exception when policy_scope is not used" do
33
+ expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
34
+ end
35
+ end
36
+
37
+ describe "#pundit_policy_authorized?" do
38
+ it "is true when authorized" do
39
+ controller.authorize(post)
40
+ expect(controller.pundit_policy_authorized?).to be true
41
+ end
42
+
43
+ it "is false when not authorized" do
44
+ expect(controller.pundit_policy_authorized?).to be false
45
+ end
46
+ end
47
+
48
+ describe "#pundit_policy_scoped?" do
49
+ it "is true when policy_scope is used" do
50
+ controller.policy_scope(Post)
51
+ expect(controller.pundit_policy_scoped?).to be true
52
+ end
53
+
54
+ it "is false when policy scope is not used" do
55
+ expect(controller.pundit_policy_scoped?).to be false
56
+ end
57
+ end
58
+
59
+ describe "#authorize" do
60
+ it "infers the policy name and authorizes based on it" do
61
+ expect(controller.authorize(post)).to be_truthy
62
+ end
63
+
64
+ it "returns the record on successful authorization" do
65
+ expect(controller.authorize(post)).to eq(post)
66
+ end
67
+
68
+ it "returns the record when passed record with namespace " do
69
+ expect(controller.authorize([:project, comment], :update?)).to eq(comment)
70
+ end
71
+
72
+ it "returns the record when passed record with nested namespace " do
73
+ expect(controller.authorize([:project, :admin, comment], :update?)).to eq(comment)
74
+ end
75
+
76
+ it "returns the policy name symbol when passed record with headless policy" do
77
+ expect(controller.authorize(:publication, :create?)).to eq(:publication)
78
+ end
79
+
80
+ it "returns the class when passed record not a particular instance" do
81
+ expect(controller.authorize(Post, :show?)).to eq(Post)
82
+ end
83
+
84
+ it "can be given a different permission to check" do
85
+ expect(controller.authorize(post, :show?)).to be_truthy
86
+ expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
87
+ end
88
+
89
+ it "can be given a different policy class" do
90
+ expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
91
+ end
92
+
93
+ it "works with anonymous class policies" do
94
+ expect(controller.authorize(article_tag, :show?)).to be_truthy
95
+ expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
96
+ end
97
+
98
+ it "throws an exception when the permission check fails" do
99
+ expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
100
+ end
101
+
102
+ it "throws an exception when a policy cannot be found" do
103
+ expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
104
+ end
105
+
106
+ it "caches the policy" do
107
+ expect(controller.policies[post]).to be_nil
108
+ controller.authorize(post)
109
+ expect(controller.policies[post]).not_to be_nil
110
+ end
111
+
112
+ it "raises an error when the given record is nil" do
113
+ expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
114
+ end
115
+
116
+ it "raises an error with a invalid policy constructor" do
117
+ expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
118
+ end
119
+ end
120
+
121
+ describe "#skip_authorization" do
122
+ it "disables authorization verification" do
123
+ controller.skip_authorization
124
+ expect { controller.verify_authorized }.not_to raise_error
125
+ end
126
+ end
127
+
128
+ describe "#skip_policy_scope" do
129
+ it "disables policy scope verification" do
130
+ controller.skip_policy_scope
131
+ expect { controller.verify_policy_scoped }.not_to raise_error
132
+ end
133
+ end
134
+
135
+ describe "#pundit_user" do
136
+ it "returns the same thing as current_user" do
137
+ expect(controller.pundit_user).to eq controller.current_user
138
+ end
139
+ end
140
+
141
+ describe "#policy" do
142
+ it "returns an instantiated policy" do
143
+ policy = controller.policy(post)
144
+ expect(policy.user).to eq user
145
+ expect(policy.post).to eq post
146
+ end
147
+
148
+ it "throws an exception if the given policy can't be found" do
149
+ expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
150
+ end
151
+
152
+ it "raises an error with a invalid policy constructor" do
153
+ expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
154
+ end
155
+
156
+ it "allows policy to be injected" do
157
+ new_policy = OpenStruct.new
158
+ controller.policies[post] = new_policy
159
+
160
+ expect(controller.policy(post)).to eq new_policy
161
+ end
162
+ end
163
+
164
+ describe "#policy_scope" do
165
+ it "returns an instantiated policy scope" do
166
+ expect(controller.policy_scope(Post)).to eq :published
167
+ end
168
+
169
+ it "allows policy scope class to be overriden" do
170
+ expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
171
+ end
172
+
173
+ it "throws an exception if the given policy can't be found" do
174
+ expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
175
+ end
176
+
177
+ it "raises an error with a invalid policy scope constructor" do
178
+ expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
179
+ end
180
+
181
+ it "allows policy_scope to be injected" do
182
+ new_scope = OpenStruct.new
183
+ controller.policy_scopes[Post] = new_scope
184
+
185
+ expect(controller.policy_scope(Post)).to eq new_scope
186
+ end
187
+ end
188
+
189
+ describe "#permitted_attributes" do
190
+ it "checks policy for permitted attributes" do
191
+ params = ActionController::Parameters.new(
192
+ post: {
193
+ title: "Hello",
194
+ votes: 5,
195
+ admin: true
196
+ }
197
+ )
198
+
199
+ action = "update"
200
+
201
+ expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
202
+ "title" => "Hello",
203
+ "votes" => 5
204
+ )
205
+ expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
206
+ end
207
+
208
+ it "checks policy for permitted attributes for record of a ActiveModel type" do
209
+ params = ActionController::Parameters.new(
210
+ customer_post: {
211
+ title: "Hello",
212
+ votes: 5,
213
+ admin: true
214
+ }
215
+ )
216
+
217
+ action = "update"
218
+
219
+ expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
220
+ "title" => "Hello",
221
+ "votes" => 5
222
+ )
223
+ expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
224
+ "votes" => 5
225
+ )
226
+ end
227
+ end
228
+
229
+ describe "#permitted_attributes_for_action" do
230
+ it "is checked if it is defined in the policy" do
231
+ params = ActionController::Parameters.new(
232
+ post: {
233
+ title: "Hello",
234
+ body: "blah",
235
+ votes: 5,
236
+ admin: true
237
+ }
238
+ )
239
+
240
+ action = "revise"
241
+ expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
242
+ end
243
+
244
+ it "can be explicitly set" do
245
+ params = ActionController::Parameters.new(
246
+ post: {
247
+ title: "Hello",
248
+ body: "blah",
249
+ votes: 5,
250
+ admin: true
251
+ }
252
+ )
253
+
254
+ action = "update"
255
+ expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
256
+ end
257
+ end
258
+ 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(NotImplementedError, /WidgetPolicy::Scope/)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end