pundit 2.0.1 → 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.
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pundit
4
+ module Authorization
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper Helper if respond_to?(:helper)
9
+ if respond_to?(:helper_method)
10
+ helper_method :policy
11
+ helper_method :pundit_policy_scope
12
+ helper_method :pundit_user
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ # @return [Boolean] whether authorization has been performed, i.e. whether
19
+ # one {#authorize} or {#skip_authorization} has been called
20
+ def pundit_policy_authorized?
21
+ !!@_pundit_policy_authorized
22
+ end
23
+
24
+ # @return [Boolean] whether policy scoping has been performed, i.e. whether
25
+ # one {#policy_scope} or {#skip_policy_scope} has been called
26
+ def pundit_policy_scoped?
27
+ !!@_pundit_policy_scoped
28
+ end
29
+
30
+ # Raises an error if authorization has not been performed, usually used as an
31
+ # `after_action` filter to prevent programmer error in forgetting to call
32
+ # {#authorize} or {#skip_authorization}.
33
+ #
34
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
35
+ # @raise [AuthorizationNotPerformedError] if authorization has not been performed
36
+ # @return [void]
37
+ def verify_authorized
38
+ raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
39
+ end
40
+
41
+ # Raises an error if policy scoping has not been performed, usually used as an
42
+ # `after_action` filter to prevent programmer error in forgetting to call
43
+ # {#policy_scope} or {#skip_policy_scope} in index actions.
44
+ #
45
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
46
+ # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
47
+ # @return [void]
48
+ def verify_policy_scoped
49
+ raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
50
+ end
51
+
52
+ # Retrieves the policy for the given record, initializing it with the record
53
+ # and current user and finally throwing an error if the user is not
54
+ # authorized to perform the given action.
55
+ #
56
+ # @param record [Object, Array] the object we're checking permissions of
57
+ # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
58
+ # If omitted then this defaults to the Rails controller action name.
59
+ # @param policy_class [Class] the policy class we want to force use of
60
+ # @raise [NotAuthorizedError] if the given query method returned false
61
+ # @return [Object] Always returns the passed object record
62
+ def authorize(record, query = nil, policy_class: nil)
63
+ query ||= "#{action_name}?"
64
+
65
+ @_pundit_policy_authorized = true
66
+
67
+ Pundit.authorize(pundit_user, record, query, policy_class: policy_class, cache: policies)
68
+ end
69
+
70
+ # Allow this action not to perform authorization.
71
+ #
72
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
73
+ # @return [void]
74
+ def skip_authorization
75
+ @_pundit_policy_authorized = :skipped
76
+ end
77
+
78
+ # Allow this action not to perform policy scoping.
79
+ #
80
+ # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
81
+ # @return [void]
82
+ def skip_policy_scope
83
+ @_pundit_policy_scoped = :skipped
84
+ end
85
+
86
+ # Retrieves the policy scope for the given record.
87
+ #
88
+ # @see https://github.com/varvet/pundit#scopes
89
+ # @param scope [Object] the object we're retrieving the policy scope for
90
+ # @param policy_scope_class [Class] the policy scope class we want to force use of
91
+ # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
92
+ def policy_scope(scope, policy_scope_class: nil)
93
+ @_pundit_policy_scoped = true
94
+ policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
95
+ end
96
+
97
+ # Retrieves the policy for the given record.
98
+ #
99
+ # @see https://github.com/varvet/pundit#policies
100
+ # @param record [Object] the object we're retrieving the policy for
101
+ # @return [Object, nil] instance of policy class with query methods
102
+ def policy(record)
103
+ policies[record] ||= Pundit.policy!(pundit_user, record)
104
+ end
105
+
106
+ # Retrieves a set of permitted attributes from the policy by instantiating
107
+ # the policy class for the given record and calling `permitted_attributes` on
108
+ # it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
109
+ # what key the record should have in the params hash and retrieves the
110
+ # permitted attributes from the params hash under that key.
111
+ #
112
+ # @see https://github.com/varvet/pundit#strong-parameters
113
+ # @param record [Object] the object we're retrieving permitted attributes for
114
+ # @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
115
+ # If omitted then this defaults to the Rails controller action name.
116
+ # @return [Hash{String => Object}] the permitted attributes
117
+ def permitted_attributes(record, action = action_name)
118
+ policy = policy(record)
119
+ method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
120
+ "permitted_attributes_for_#{action}"
121
+ else
122
+ "permitted_attributes"
123
+ end
124
+ pundit_params_for(record).permit(*policy.public_send(method_name))
125
+ end
126
+
127
+ # Retrieves the params for the given record.
128
+ #
129
+ # @param record [Object] the object we're retrieving params for
130
+ # @return [ActionController::Parameters] the params
131
+ def pundit_params_for(record)
132
+ params.require(PolicyFinder.new(record).param_key)
133
+ end
134
+
135
+ # Cache of policies. You should not rely on this method.
136
+ #
137
+ # @api private
138
+ # rubocop:disable Naming/MemoizedInstanceVariableName
139
+ def policies
140
+ @_pundit_policies ||= {}
141
+ end
142
+ # rubocop:enable Naming/MemoizedInstanceVariableName
143
+
144
+ # Cache of policy scope. You should not rely on this method.
145
+ #
146
+ # @api private
147
+ # rubocop:disable Naming/MemoizedInstanceVariableName
148
+ def policy_scopes
149
+ @_pundit_policy_scopes ||= {}
150
+ end
151
+ # rubocop:enable Naming/MemoizedInstanceVariableName
152
+
153
+ # Hook method which allows customizing which user is passed to policies and
154
+ # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
155
+ #
156
+ # @see https://github.com/varvet/pundit#customize-pundit-user
157
+ # @return [Object] the user object to be used with pundit
158
+ def pundit_user
159
+ current_user
160
+ end
161
+
162
+ private
163
+
164
+ def pundit_policy_scope(scope)
165
+ policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
166
+ end
167
+ end
168
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Pundit
2
4
  # Finds policy and scope classes for given object.
3
5
  # @api public
@@ -66,7 +68,7 @@ module Pundit
66
68
  end
67
69
  end
68
70
 
69
- private
71
+ private
70
72
 
71
73
  def find(subject)
72
74
  if subject.is_a?(Array)
data/lib/pundit/rspec.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "active_support/core_ext/array/conversions"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Pundit
4
4
  module RSpec
@@ -72,17 +72,9 @@ module Pundit
72
72
  end
73
73
 
74
74
  RSpec.configure do |config|
75
- if RSpec::Core::Version::STRING.split(".").first.to_i >= 3
76
- config.include(
77
- Pundit::RSpec::PolicyExampleGroup,
78
- type: :policy,
79
- file_path: %r{spec/policies}
80
- )
81
- else
82
- config.include(
83
- Pundit::RSpec::PolicyExampleGroup,
84
- type: :policy,
85
- example_group: { file_path: %r{spec/policies} }
86
- )
87
- end
75
+ config.include(
76
+ Pundit::RSpec::PolicyExampleGroup,
77
+ type: :policy,
78
+ file_path: %r{spec/policies}
79
+ )
88
80
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pundit
4
- VERSION = "2.0.1".freeze
4
+ VERSION = "2.3.0"
5
5
  end
data/lib/pundit.rb CHANGED
@@ -7,17 +7,20 @@ require "active_support/core_ext/string/inflections"
7
7
  require "active_support/core_ext/object/blank"
8
8
  require "active_support/core_ext/module/introspection"
9
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
10
16
 
11
17
  # @api public
12
18
  module Pundit
13
- SUFFIX = "Policy".freeze
19
+ SUFFIX = "Policy"
14
20
 
15
21
  # @api private
16
22
  module Generators; end
17
23
 
18
- # @api private
19
- class Error < StandardError; end
20
-
21
24
  # Error that will be raised when authorization has failed
22
25
  class NotAuthorizedError < Error
23
26
  attr_reader :query, :record, :policy
@@ -30,7 +33,7 @@ module Pundit
30
33
  @record = options[:record]
31
34
  @policy = options[:policy]
32
35
 
33
- message = options.fetch(:message) { "not allowed to #{query} this #{record.inspect}" }
36
+ message = options.fetch(:message) { "not allowed to #{query} this #{record.class}" }
34
37
  end
35
38
 
36
39
  super(message)
@@ -51,7 +54,12 @@ module Pundit
51
54
  # Error that will be raised if a policy or policy scope is not defined.
52
55
  class NotDefinedError < Error; end
53
56
 
54
- 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
55
63
 
56
64
  class << self
57
65
  # Retrieves the policy for the given record, initializing it with the
@@ -59,13 +67,19 @@ module Pundit
59
67
  # authorized to perform the given action.
60
68
  #
61
69
  # @param user [Object] the user that initiated the action
62
- # @param record [Object] the object we're checking permissions of
70
+ # @param possibly_namespaced_record [Object, Array] the object we're checking permissions of
63
71
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
64
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
65
74
  # @raise [NotAuthorizedError] if the given query method returned false
66
75
  # @return [Object] Always returns the passed object record
67
- def authorize(user, record, query, policy_class: nil)
68
- policy = policy_class ? policy_class.new(user, record) : policy!(user, 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)
82
+ end
69
83
 
70
84
  raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
71
85
 
@@ -122,7 +136,7 @@ module Pundit
122
136
  # @return [Object, nil] instance of policy class with query methods
123
137
  def policy(user, record)
124
138
  policy = PolicyFinder.new(record).policy
125
- policy.new(user, pundit_model(record)) if policy
139
+ policy&.new(user, pundit_model(record))
126
140
  rescue ArgumentError
127
141
  raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
128
142
  end
@@ -142,7 +156,7 @@ module Pundit
142
156
  raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
143
157
  end
144
158
 
145
- private
159
+ private
146
160
 
147
161
  def pundit_model(record)
148
162
  record.is_a?(Array) ? record.last : record
@@ -155,169 +169,4 @@ module Pundit
155
169
  pundit_policy_scope(scope)
156
170
  end
157
171
  end
158
-
159
- included do
160
- helper Helper if respond_to?(:helper)
161
- if respond_to?(:helper_method)
162
- helper_method :policy
163
- helper_method :pundit_policy_scope
164
- helper_method :pundit_user
165
- end
166
- end
167
-
168
- protected
169
-
170
- # @return [Boolean] whether authorization has been performed, i.e. whether
171
- # one {#authorize} or {#skip_authorization} has been called
172
- def pundit_policy_authorized?
173
- !!@_pundit_policy_authorized
174
- end
175
-
176
- # @return [Boolean] whether policy scoping has been performed, i.e. whether
177
- # one {#policy_scope} or {#skip_policy_scope} has been called
178
- def pundit_policy_scoped?
179
- !!@_pundit_policy_scoped
180
- end
181
-
182
- # Raises an error if authorization has not been performed, usually used as an
183
- # `after_action` filter to prevent programmer error in forgetting to call
184
- # {#authorize} or {#skip_authorization}.
185
- #
186
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
187
- # @raise [AuthorizationNotPerformedError] if authorization has not been performed
188
- # @return [void]
189
- def verify_authorized
190
- raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
191
- end
192
-
193
- # Raises an error if policy scoping has not been performed, usually used as an
194
- # `after_action` filter to prevent programmer error in forgetting to call
195
- # {#policy_scope} or {#skip_policy_scope} in index actions.
196
- #
197
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
198
- # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
199
- # @return [void]
200
- def verify_policy_scoped
201
- raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
202
- end
203
-
204
- # Retrieves the policy for the given record, initializing it with the record
205
- # and current user and finally throwing an error if the user is not
206
- # authorized to perform the given action.
207
- #
208
- # @param record [Object] the object we're checking permissions of
209
- # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
210
- # If omitted then this defaults to the Rails controller action name.
211
- # @param policy_class [Class] the policy class we want to force use of
212
- # @raise [NotAuthorizedError] if the given query method returned false
213
- # @return [Object] Always returns the passed object record
214
- def authorize(record, query = nil, policy_class: nil)
215
- query ||= "#{action_name}?"
216
-
217
- @_pundit_policy_authorized = true
218
-
219
- policy = policy_class ? policy_class.new(pundit_user, record) : policy(record)
220
-
221
- raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
222
-
223
- record
224
- end
225
-
226
- # Allow this action not to perform authorization.
227
- #
228
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
229
- # @return [void]
230
- def skip_authorization
231
- @_pundit_policy_authorized = true
232
- end
233
-
234
- # Allow this action not to perform policy scoping.
235
- #
236
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
237
- # @return [void]
238
- def skip_policy_scope
239
- @_pundit_policy_scoped = true
240
- end
241
-
242
- # Retrieves the policy scope for the given record.
243
- #
244
- # @see https://github.com/varvet/pundit#scopes
245
- # @param scope [Object] the object we're retrieving the policy scope for
246
- # @param policy_scope_class [Class] the policy scope class we want to force use of
247
- # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
248
- def policy_scope(scope, policy_scope_class: nil)
249
- @_pundit_policy_scoped = true
250
- policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
251
- end
252
-
253
- # Retrieves the policy for the given record.
254
- #
255
- # @see https://github.com/varvet/pundit#policies
256
- # @param record [Object] the object we're retrieving the policy for
257
- # @return [Object, nil] instance of policy class with query methods
258
- def policy(record)
259
- policies[record] ||= Pundit.policy!(pundit_user, record)
260
- end
261
-
262
- # Retrieves a set of permitted attributes from the policy by instantiating
263
- # the policy class for the given record and calling `permitted_attributes` on
264
- # it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
265
- # what key the record should have in the params hash and retrieves the
266
- # permitted attributes from the params hash under that key.
267
- #
268
- # @see https://github.com/varvet/pundit#strong-parameters
269
- # @param record [Object] the object we're retrieving permitted attributes for
270
- # @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
271
- # If omitted then this defaults to the Rails controller action name.
272
- # @return [Hash{String => Object}] the permitted attributes
273
- def permitted_attributes(record, action = action_name)
274
- policy = policy(record)
275
- method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
276
- "permitted_attributes_for_#{action}"
277
- else
278
- "permitted_attributes"
279
- end
280
- pundit_params_for(record).permit(*policy.public_send(method_name))
281
- end
282
-
283
- # Retrieves the params for the given record.
284
- #
285
- # @param record [Object] the object we're retrieving params for
286
- # @return [ActionController::Parameters] the params
287
- def pundit_params_for(record)
288
- params.require(PolicyFinder.new(record).param_key)
289
- end
290
-
291
- # Cache of policies. You should not rely on this method.
292
- #
293
- # @api private
294
- # rubocop:disable Naming/MemoizedInstanceVariableName
295
- def policies
296
- @_pundit_policies ||= {}
297
- end
298
- # rubocop:enable Naming/MemoizedInstanceVariableName
299
-
300
- # Cache of policy scope. You should not rely on this method.
301
- #
302
- # @api private
303
- # rubocop:disable Naming/MemoizedInstanceVariableName
304
- def policy_scopes
305
- @_pundit_policy_scopes ||= {}
306
- end
307
- # rubocop:enable Naming/MemoizedInstanceVariableName
308
-
309
- # Hook method which allows customizing which user is passed to policies and
310
- # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
311
- #
312
- # @see https://github.com/varvet/pundit#customize-pundit-user
313
- # @return [Object] the user object to be used with pundit
314
- def pundit_user
315
- current_user
316
- end
317
-
318
- private
319
-
320
- def pundit_policy_scope(scope)
321
- policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
322
- end
323
172
  end
data/pundit.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path("lib", __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require "pundit/version"
@@ -12,10 +14,20 @@ Gem::Specification.new do |gem|
12
14
  gem.homepage = "https://github.com/varvet/pundit"
13
15
  gem.license = "MIT"
14
16
 
15
- gem.files = `git ls-files`.split($/)
17
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
18
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
17
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
20
  gem.require_paths = ["lib"]
19
21
 
20
22
  gem.add_dependency "activesupport", ">= 3.0.0"
23
+ gem.add_development_dependency "actionpack", ">= 3.0.0"
24
+ gem.add_development_dependency "activemodel", ">= 3.0.0"
25
+ gem.add_development_dependency "bundler"
26
+ gem.add_development_dependency "pry"
27
+ gem.add_development_dependency "railties", ">= 3.0.0"
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"
32
+ gem.add_development_dependency "yard"
21
33
  end