pundit 2.0.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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