pundit 2.1.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TestUnit
2
4
  module Generators
3
5
  class PolicyGenerator < ::Rails::Generators::NamedBase
4
- source_root File.expand_path('templates', __dir__)
6
+ source_root File.expand_path("templates", __dir__)
5
7
 
6
8
  def create_policy_test
7
- template 'policy_test.rb', File.join('test/policies', class_path, "#{file_name}_policy_test.rb")
9
+ template "policy_test.rb", File.join("test/policies", class_path, "#{file_name}_policy_test.rb")
8
10
  end
9
11
  end
10
12
  end
@@ -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
@@ -68,7 +68,7 @@ module Pundit
68
68
  end
69
69
  end
70
70
 
71
- private
71
+ private
72
72
 
73
73
  def find(subject)
74
74
  if subject.is_a?(Array)
data/lib/pundit/rspec.rb CHANGED
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array/conversions"
4
-
5
3
  module Pundit
6
4
  module RSpec
7
5
  module Matchers
@@ -74,17 +72,9 @@ module Pundit
74
72
  end
75
73
 
76
74
  RSpec.configure do |config|
77
- if RSpec::Core::Version::STRING.split(".").first.to_i >= 3
78
- config.include(
79
- Pundit::RSpec::PolicyExampleGroup,
80
- type: :policy,
81
- file_path: %r{spec/policies}
82
- )
83
- else
84
- config.include(
85
- Pundit::RSpec::PolicyExampleGroup,
86
- type: :policy,
87
- example_group: { file_path: %r{spec/policies} }
88
- )
89
- end
75
+ config.include(
76
+ Pundit::RSpec::PolicyExampleGroup,
77
+ type: :policy,
78
+ file_path: %r{spec/policies}
79
+ )
90
80
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pundit
4
- VERSION = "2.1.0".freeze
4
+ VERSION = "2.3.1"
5
5
  end
data/lib/pundit.rb CHANGED
@@ -7,6 +7,7 @@ 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"
10
11
 
11
12
  # @api private
12
13
  # To avoid name clashes with common Error naming when mixing in Pundit,
@@ -15,7 +16,7 @@ class Pundit::Error < StandardError; end # rubocop:disable Style/ClassAndModuleC
15
16
 
16
17
  # @api public
17
18
  module Pundit
18
- SUFFIX = "Policy".freeze
19
+ SUFFIX = "Policy"
19
20
 
20
21
  # @api private
21
22
  module Generators; end
@@ -53,7 +54,14 @@ module Pundit
53
54
  # Error that will be raised if a policy or policy scope is not defined.
54
55
  class NotDefinedError < Error; end
55
56
 
56
- extend ActiveSupport::Concern
57
+ def self.included(base)
58
+ location = caller_locations(1, 1).first
59
+ warn <<~WARNING
60
+ 'include Pundit' is deprecated. Please use 'include Pundit::Authorization' instead.
61
+ (called from #{location.label} at #{location.path}:#{location.lineno})
62
+ WARNING
63
+ base.include Authorization
64
+ end
57
65
 
58
66
  class << self
59
67
  # Retrieves the policy for the given record, initializing it with the
@@ -61,13 +69,19 @@ module Pundit
61
69
  # authorized to perform the given action.
62
70
  #
63
71
  # @param user [Object] the user that initiated the action
64
- # @param record [Object] the object we're checking permissions of
72
+ # @param possibly_namespaced_record [Object, Array] the object we're checking permissions of
65
73
  # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`)
66
74
  # @param policy_class [Class] the policy class we want to force use of
75
+ # @param cache [#[], #[]=] a Hash-like object to cache the found policy instance in
67
76
  # @raise [NotAuthorizedError] if the given query method returned false
68
77
  # @return [Object] Always returns the passed object record
69
- def authorize(user, record, query, policy_class: nil)
70
- policy = policy_class ? policy_class.new(user, record) : policy!(user, record)
78
+ def authorize(user, possibly_namespaced_record, query, policy_class: nil, cache: {})
79
+ record = pundit_model(possibly_namespaced_record)
80
+ policy = if policy_class
81
+ policy_class.new(user, record)
82
+ else
83
+ cache[possibly_namespaced_record] ||= policy!(user, possibly_namespaced_record)
84
+ end
71
85
 
72
86
  raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
73
87
 
@@ -124,7 +138,7 @@ module Pundit
124
138
  # @return [Object, nil] instance of policy class with query methods
125
139
  def policy(user, record)
126
140
  policy = PolicyFinder.new(record).policy
127
- policy.new(user, pundit_model(record)) if policy
141
+ policy&.new(user, pundit_model(record))
128
142
  rescue ArgumentError
129
143
  raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
130
144
  end
@@ -144,7 +158,7 @@ module Pundit
144
158
  raise InvalidConstructorError, "Invalid #<#{policy}> constructor is called"
145
159
  end
146
160
 
147
- private
161
+ private
148
162
 
149
163
  def pundit_model(record)
150
164
  record.is_a?(Array) ? record.last : record
@@ -157,169 +171,4 @@ module Pundit
157
171
  pundit_policy_scope(scope)
158
172
  end
159
173
  end
160
-
161
- included do
162
- helper Helper if respond_to?(:helper)
163
- if respond_to?(:helper_method)
164
- helper_method :policy
165
- helper_method :pundit_policy_scope
166
- helper_method :pundit_user
167
- end
168
- end
169
-
170
- protected
171
-
172
- # @return [Boolean] whether authorization has been performed, i.e. whether
173
- # one {#authorize} or {#skip_authorization} has been called
174
- def pundit_policy_authorized?
175
- !!@_pundit_policy_authorized
176
- end
177
-
178
- # @return [Boolean] whether policy scoping has been performed, i.e. whether
179
- # one {#policy_scope} or {#skip_policy_scope} has been called
180
- def pundit_policy_scoped?
181
- !!@_pundit_policy_scoped
182
- end
183
-
184
- # Raises an error if authorization has not been performed, usually used as an
185
- # `after_action` filter to prevent programmer error in forgetting to call
186
- # {#authorize} or {#skip_authorization}.
187
- #
188
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
189
- # @raise [AuthorizationNotPerformedError] if authorization has not been performed
190
- # @return [void]
191
- def verify_authorized
192
- raise AuthorizationNotPerformedError, self.class unless pundit_policy_authorized?
193
- end
194
-
195
- # Raises an error if policy scoping has not been performed, usually used as an
196
- # `after_action` filter to prevent programmer error in forgetting to call
197
- # {#policy_scope} or {#skip_policy_scope} in index actions.
198
- #
199
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
200
- # @raise [AuthorizationNotPerformedError] if policy scoping has not been performed
201
- # @return [void]
202
- def verify_policy_scoped
203
- raise PolicyScopingNotPerformedError, self.class unless pundit_policy_scoped?
204
- end
205
-
206
- # Retrieves the policy for the given record, initializing it with the record
207
- # and current user and finally throwing an error if the user is not
208
- # authorized to perform the given action.
209
- #
210
- # @param record [Object] the object we're checking permissions of
211
- # @param query [Symbol, String] the predicate method to check on the policy (e.g. `:show?`).
212
- # If omitted then this defaults to the Rails controller action name.
213
- # @param policy_class [Class] the policy class we want to force use of
214
- # @raise [NotAuthorizedError] if the given query method returned false
215
- # @return [Object] Always returns the passed object record
216
- def authorize(record, query = nil, policy_class: nil)
217
- query ||= "#{action_name}?"
218
-
219
- @_pundit_policy_authorized = true
220
-
221
- policy = policy_class ? policy_class.new(pundit_user, record) : policy(record)
222
-
223
- raise NotAuthorizedError, query: query, record: record, policy: policy unless policy.public_send(query)
224
-
225
- record
226
- end
227
-
228
- # Allow this action not to perform authorization.
229
- #
230
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
231
- # @return [void]
232
- def skip_authorization
233
- @_pundit_policy_authorized = true
234
- end
235
-
236
- # Allow this action not to perform policy scoping.
237
- #
238
- # @see https://github.com/varvet/pundit#ensuring-policies-and-scopes-are-used
239
- # @return [void]
240
- def skip_policy_scope
241
- @_pundit_policy_scoped = true
242
- end
243
-
244
- # Retrieves the policy scope for the given record.
245
- #
246
- # @see https://github.com/varvet/pundit#scopes
247
- # @param scope [Object] the object we're retrieving the policy scope for
248
- # @param policy_scope_class [Class] the policy scope class we want to force use of
249
- # @return [Scope{#resolve}, nil] instance of scope class which can resolve to a scope
250
- def policy_scope(scope, policy_scope_class: nil)
251
- @_pundit_policy_scoped = true
252
- policy_scope_class ? policy_scope_class.new(pundit_user, scope).resolve : pundit_policy_scope(scope)
253
- end
254
-
255
- # Retrieves the policy for the given record.
256
- #
257
- # @see https://github.com/varvet/pundit#policies
258
- # @param record [Object] the object we're retrieving the policy for
259
- # @return [Object, nil] instance of policy class with query methods
260
- def policy(record)
261
- policies[record] ||= Pundit.policy!(pundit_user, record)
262
- end
263
-
264
- # Retrieves a set of permitted attributes from the policy by instantiating
265
- # the policy class for the given record and calling `permitted_attributes` on
266
- # it, or `permitted_attributes_for_{action}` if `action` is defined. It then infers
267
- # what key the record should have in the params hash and retrieves the
268
- # permitted attributes from the params hash under that key.
269
- #
270
- # @see https://github.com/varvet/pundit#strong-parameters
271
- # @param record [Object] the object we're retrieving permitted attributes for
272
- # @param action [Symbol, String] the name of the action being performed on the record (e.g. `:update`).
273
- # If omitted then this defaults to the Rails controller action name.
274
- # @return [Hash{String => Object}] the permitted attributes
275
- def permitted_attributes(record, action = action_name)
276
- policy = policy(record)
277
- method_name = if policy.respond_to?("permitted_attributes_for_#{action}")
278
- "permitted_attributes_for_#{action}"
279
- else
280
- "permitted_attributes"
281
- end
282
- pundit_params_for(record).permit(*policy.public_send(method_name))
283
- end
284
-
285
- # Retrieves the params for the given record.
286
- #
287
- # @param record [Object] the object we're retrieving params for
288
- # @return [ActionController::Parameters] the params
289
- def pundit_params_for(record)
290
- params.require(PolicyFinder.new(record).param_key)
291
- end
292
-
293
- # Cache of policies. You should not rely on this method.
294
- #
295
- # @api private
296
- # rubocop:disable Naming/MemoizedInstanceVariableName
297
- def policies
298
- @_pundit_policies ||= {}
299
- end
300
- # rubocop:enable Naming/MemoizedInstanceVariableName
301
-
302
- # Cache of policy scope. You should not rely on this method.
303
- #
304
- # @api private
305
- # rubocop:disable Naming/MemoizedInstanceVariableName
306
- def policy_scopes
307
- @_pundit_policy_scopes ||= {}
308
- end
309
- # rubocop:enable Naming/MemoizedInstanceVariableName
310
-
311
- # Hook method which allows customizing which user is passed to policies and
312
- # scopes initialized by {#authorize}, {#policy} and {#policy_scope}.
313
- #
314
- # @see https://github.com/varvet/pundit#customize-pundit-user
315
- # @return [Object] the user object to be used with pundit
316
- def pundit_user
317
- current_user
318
- end
319
-
320
- private
321
-
322
- def pundit_policy_scope(scope)
323
- policy_scopes[scope] ||= Pundit.policy_scope!(pundit_user, scope)
324
- end
325
174
  end
data/pundit.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.name = "pundit"
9
9
  gem.version = Pundit::VERSION
10
10
  gem.authors = ["Jonas Nicklas", "Varvet AB"]
11
- gem.email = ["jonas.nicklas@gmail.com", "dev@elabs.se"]
11
+ gem.email = ["jonas.nicklas@gmail.com", "info@varvet.com"]
12
12
  gem.description = "Object oriented authorization for Rails applications"
13
13
  gem.summary = "OO authorization for Rails"
14
14
  gem.homepage = "https://github.com/varvet/pundit"
@@ -19,13 +19,17 @@ Gem::Specification.new do |gem|
19
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
20
  gem.require_paths = ["lib"]
21
21
 
22
+ gem.metadata = { "rubygems_mfa_required" => "true" }
23
+
22
24
  gem.add_dependency "activesupport", ">= 3.0.0"
23
25
  gem.add_development_dependency "actionpack", ">= 3.0.0"
24
26
  gem.add_development_dependency "activemodel", ">= 3.0.0"
25
27
  gem.add_development_dependency "bundler"
26
28
  gem.add_development_dependency "pry"
29
+ gem.add_development_dependency "railties", ">= 3.0.0"
27
30
  gem.add_development_dependency "rake"
28
- gem.add_development_dependency "rspec", ">= 2.0.0"
29
- gem.add_development_dependency "rubocop", "0.57.2"
31
+ gem.add_development_dependency "rspec", ">= 3.0.0"
32
+ gem.add_development_dependency "rubocop", "1.24.0"
33
+ gem.add_development_dependency "simplecov", ">= 0.17.0"
30
34
  gem.add_development_dependency "yard"
31
35
  end