pundit 1.1.0 → 2.3.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/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