cancancan_resource_controller 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a38bfb98462e336b34961d82c4d130022bf466971f55c11699e707b7e6ce4fff
4
+ data.tar.gz: aa0be9ebb6885fc7b15156d0517f1df7c236c3da081a8cf01f6e5b3bf1b456ba
5
+ SHA512:
6
+ metadata.gz: 565013e091449c0a17c33413b7c2c7be15bb660ec644cda55db34df9310cdb7a3b94a3639b256df1745390b1cebc976d121eae3efe39bfe85954f75ef6a4af55
7
+ data.tar.gz: d7b727d66c11e8cf171e2eb6ba0f5175740b8be186ef0e463782e0a619951ccffca6f83fa4a50576d6ebf406629b77a840f74a5ad57b1344ea20fdce8eb3ddd9
@@ -0,0 +1,440 @@
1
+ # How to utilize CanCan Permissions to work with this controller:
2
+ module CanCanCan
3
+ module AbstractResourceController
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_action :initialize_resource_class
8
+ end
9
+
10
+ # Used to stop infinite recursive on associations (could just be deeply nested structures. Could also be self-referencing).
11
+ MAX_ASSOCIATIVE_NESTED_DEPTH = 60
12
+ REGEX_FOR_HTML_TAG_DETECTION = /.*\<\/?[^_\W]+\>.*/
13
+
14
+ # to handle adding/removing associations by "_ids" suffix
15
+ IDS_ATTIB_PERMISSION_KEY_GEN = Proc.new { |assoc_key| "#{assoc_key.to_s.singularize}_ids".to_sym }
16
+ IDS_ACTION_PERMISSION_KEY_GEN = Proc.new { |assoc_key| "_can_add_or_remove_association_#{assoc_key.to_s}".to_sym }
17
+
18
+ # to handle updating nested attributes
19
+ NESTED_ATTIB_PERMISSION_KEY_GEN = Proc.new { |assoc_key| "#{assoc_key.to_s}_attributes".to_sym }
20
+ NESTED_ACTION_PERMISSION_KEY_GEN = Proc.new { |assoc_key| "_can_update_association_#{assoc_key.to_s}".to_sym }
21
+
22
+ # For Read-Only fields
23
+ # - define this on your class model
24
+ # optional allowlist for incoming parameters for the implemented resource
25
+ # - nil means allowlist is inactive, acceptable parameters are determined by cancan attrib permissions
26
+ # - [] (empty array) means that no parameters will be accepted for resource
27
+ # - [<param1>, <param2>, ...] is self-explanatory, only those listed will be accepted
28
+ # class Resource
29
+ # RESOURCE_CONTROLLER_ATTRIB_ALLOWLIST = nil
30
+ # end
31
+
32
+ # probably a better way to do this. If there is, it's poorly documented.
33
+ # - src: https://www.w3schools.com/TAGS/default.ASP
34
+ # DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS - Add to this env var any values to also allow for HTML tags (i.e.: label,span,text_area)
35
+ # DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS - Add to this env var any values to also allow for HTML attribs (i.e.: ng-show,ng-hide,data-id)
36
+ DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS = (
37
+ %w[
38
+ p
39
+ div
40
+ span
41
+ body
42
+ b
43
+ strong
44
+ br
45
+ center
46
+ font
47
+ label
48
+ pre
49
+ tr
50
+ td
51
+ table
52
+ text_area
53
+ ul
54
+ li
55
+ footer
56
+ em
57
+ ol
58
+ i
59
+ select
60
+ option
61
+ ] + (ENV['DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS']&.split(',')&.collect(&:strip) || [])
62
+ ).freeze
63
+ # Only allow attribs that are allowed in HTML friendly text blocks
64
+ # - i.e. NO HREFs!
65
+ DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS = (
66
+ %w[
67
+ style
68
+ id
69
+ class
70
+ type
71
+ value
72
+ ] + (ENV['DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS']&.split(',')&.collect(&:strip) || [])
73
+ ).freeze
74
+
75
+ def index
76
+ authorize! :index, @resource_class
77
+ @resources ||= @resource_class
78
+
79
+ begin
80
+ @resources = @resources.accessible_by(current_ability)
81
+ rescue CanCan::Error => e
82
+ # The accessible_by call cannot be used with a block 'can' definition
83
+ # Need to switch over to SQL permissions, not using the blocks
84
+ Rails.logger.error "Error: resource class, #{@resource_class.name}, is using CanCan block definitions, not SQL permissions. Unable to run index permission filter"
85
+ raise e
86
+ end
87
+
88
+ @resources = index_resource_query(@resources)
89
+
90
+ respond_with_resources
91
+ end
92
+
93
+ def show
94
+ authorize! :show, @resource_class
95
+ # Allow @resource to be set from subclass controller
96
+ @resource ||= @resource_class.find(params[:id])
97
+ authorize! :show, @resource
98
+
99
+ respond_with_resource
100
+ end
101
+
102
+ def new
103
+ authorize! :create, @resource_class
104
+ @resource ||= @resource_class.new(resource_params)
105
+ # 2nd auth on the object itself
106
+ # Not authing on the nested resources, that could have come in as nested attributes.
107
+ authorize! :create, @resource
108
+
109
+ respond_with_resource
110
+ end
111
+
112
+ def edit
113
+ authorize! :update, @resource_class
114
+ @resource ||= @resource_class.find(params[:id])
115
+ authorize! :update, @resource
116
+
117
+ respond_to do |format|
118
+ format.html # Renders the default
119
+ format.json { render json: @resources }
120
+ format.xml { render xml: @resources }
121
+ format.csv # Renders the default
122
+ format.xlsx # Renders the default
123
+ end
124
+ end
125
+
126
+ def create
127
+ authorize! :create, @resource_class
128
+ # @resource = @resource_class.new(resource_params)
129
+
130
+ # This 2nd @resource initiation is so we run run whitelisting attribs on the object.
131
+ # Class whitelisting is far more broad than object attrib whitelisting.
132
+ # Necessary if class permissions have a permissions-block.
133
+ @resource ||= @resource_class.new(resource_params(@resource))
134
+
135
+ # 2nd auth on the object itself
136
+ authorize! :create, @resource
137
+
138
+ if @resource.save
139
+ respond_with_resource
140
+ else
141
+ begin
142
+ Rails.logger.warn "Failed object validations: could not create #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
143
+ respond_with_resource_invalid
144
+ rescue Exception => e
145
+ Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
146
+ Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
147
+ respond_with_resource_error
148
+ end
149
+ end
150
+ end
151
+
152
+ def update
153
+ authorize! :update, @resource_class
154
+ @resource ||= @resource_class.find(params[:id])
155
+ authorize! :update, @resource
156
+
157
+ second_authorize = false
158
+ ActiveRecord::Base.transaction do
159
+ @resource.assign_attributes(resource_params(@resource))
160
+ second_authorize = can?(action_name.to_sym, @resource)
161
+ unless second_authorize
162
+ # NOTE: Does not halt the controller process, just rolls back the DB
163
+ raise ActiveRecord::Rollback
164
+ end
165
+ end
166
+
167
+ unless second_authorize
168
+ raise CanCan::AccessDenied.new("Not authorized!", action_name.to_sym, @resource)
169
+ end
170
+
171
+ # 2nd auth, on the updates of the object, without saving, so we can rollback without auth.
172
+ # authorize! :update, @resource
173
+ if @resource.save
174
+ respond_with_resource
175
+ else
176
+ begin
177
+ Rails.logger.warn "Failed object validations: could not update #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
178
+ respond_with_resource_error
179
+ rescue Exception => e
180
+ Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
181
+ Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
182
+ respond_with_resource_error
183
+ end
184
+ end
185
+ end
186
+
187
+ def destroy
188
+ authorize! :destroy, @resource_class
189
+ @resource ||= @resource_class.find(params[:id])
190
+ authorize! :destroy, @resource
191
+ # retuning the resource in a pre-destroyed state as a destroy response
192
+ results = @resource
193
+ if @resource.destroy
194
+ respond_after_destroy
195
+ else
196
+ begin
197
+ Rails.logger.warn "Failed object validations: could not destroy #{@resource_class}, id: #{@resource.id}: #{@resource.errors.full_messages}"
198
+ respond_with_resource_invalid
199
+ rescue Exception => e
200
+ Rails.logger.error "CanCanCanResourceController - Caught Internal Server Error: " + e.class.to_s + ': ' + e.message
201
+ Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n").to_s
202
+ respond_with_resource_error
203
+ end
204
+ end
205
+ end
206
+
207
+ protected
208
+
209
+ def respond_with_resources
210
+ respond_to do |format|
211
+ format.html # Renders the default
212
+ format.json { render json: @resources }
213
+ end
214
+ end
215
+
216
+ def respond_with_resource
217
+ respond_to do |format|
218
+ format.html # Renders the default
219
+ format.json { render json: @resource }
220
+ end
221
+ end
222
+
223
+ def respond_with_resource_invalid
224
+ respond_to do |format|
225
+ format.html # Renders the default
226
+ format.json { render json: @resource.errors.full_messages, status: 422 }
227
+ end
228
+ end
229
+
230
+ def respond_with_resource_error
231
+ respond_to do |format|
232
+ format.html # Renders the default
233
+ format.json { render json: ["An error has occured. Our support teams have been notified and are working on a solution."], status: 422 }
234
+ end
235
+ end
236
+
237
+ def respond_after_destroy
238
+ respond_to do |format|
239
+ format.html { redirect_to url_for(controller: controller_name, action: 'index') }
240
+ format.json { render json: results, status: :no_content }
241
+ end
242
+ end
243
+
244
+ # meant to be overridden by inheriting controllers.
245
+ def index_resource_query resource_query
246
+ return resource_query
247
+ end
248
+
249
+ # can pass in custom method to supplant 'param.permit', like if you wanted to whitelist a hash instead of params.
250
+ # ex: CanCanCanResourceController#deactivate_helper, permits on fake params: ActionController::Parameters.new(deactive_params)
251
+ def resource_params resource_object = nil, opts = {}, &block
252
+ local_action_name = opts[:custom_action_name] || action_name
253
+ allowlist_permitted = get_nested_attributes_for_class(@resource_class, local_action_name.to_sym, resource_object)
254
+
255
+ # # Rails kludge, issue with allowing parameters with empty arrays
256
+ # # Needs to be nested, recursive
257
+ # # Updating params in-place
258
+ # params.each do |key, value|
259
+ # if key.to_s =~ /(.*)_ids/ && (value == "remove" || value == ["remove"])
260
+ # params[key] = []
261
+ # end
262
+ # end
263
+
264
+ if block_given?
265
+ params_with_only_allowed_parameters = yield(allowlist_permitted)
266
+ else
267
+ params_with_only_allowed_parameters = param_permit(allowlist_permitted)
268
+ end
269
+
270
+ # sanitize all input.
271
+ sanitized_params_with_only_allowed_parameters = clean_parameter_data(params_with_only_allowed_parameters)
272
+
273
+ # recast type (and have to re-permit)
274
+ sanitized_params_with_only_allowed_parameters = ActionController::Parameters.new(sanitized_params_with_only_allowed_parameters).permit(allowlist_permitted)
275
+
276
+ return sanitized_params_with_only_allowed_parameters
277
+ end
278
+
279
+ # recursive
280
+ # src: https://apidock.com/rails/v5.2.3/ActionView/Helpers/SanitizeHelper/sanitize
281
+ def clean_parameter_data param_value
282
+ # was an array element, and not an object.
283
+ # Check for HTML tags
284
+ if param_value.is_a?(String) && !(param_value =~ REGEX_FOR_HTML_TAG_DETECTION).nil?
285
+ # We need a better way, in the future, to specify the allowed values down to the Class and Column level.
286
+ return ActionController::Base.helpers.sanitize(param_value, {tags: self.class::DEFAULT_PARAMETER_SANITIZER_ALLOWED_TAGS, attributes: self.class::DEFAULT_PARAMETER_SANITIZER_ALLOWED_ATTRIBS})
287
+ elsif param_value.is_a?(String) || param_value.is_a?(Integer) || param_value.is_a?(Float) || param_value.nil? || [true, false].include?(param_value)
288
+ return param_value
289
+ end
290
+
291
+ if param_value.is_a?(Hash) || param_value.is_a?(Array) || param_value.is_a?(ActionController::Parameters)
292
+ # good to continue
293
+ else
294
+ error_msg = "Internal Server Error! Unsupported parameter type: #{param_value} (#{param_value.class})"
295
+ Rails.logger.error(error_msg)
296
+ raise error_msg
297
+ end
298
+
299
+ if param_value.is_a?(Array)
300
+ new_array = []
301
+ param_value.each do |array_element|
302
+ new_array << clean_parameter_data(array_element)
303
+ end
304
+ return new_array
305
+ else
306
+ new_hash = {}
307
+ keys = param_value.keys
308
+ keys.each do |key|
309
+ new_hash[key.to_sym] = clean_parameter_data(param_value[key])
310
+ end
311
+ return new_hash
312
+ end
313
+ end
314
+
315
+ # Not checking instances of classes. What if they are object-state dependent?
316
+ # Need to run them again, after object instantiation, but in a different method.
317
+ def get_nested_attributes_for_class resource_class, action_name, root_level_object, depth = 0
318
+ raise "invalid action class: #{action_name.class}" if !action_name.is_a?(Symbol)
319
+ association_parameters = []
320
+ if depth > MAX_ASSOCIATIVE_NESTED_DEPTH
321
+ return association_parameters
322
+ end
323
+
324
+ # Handle resource_class attribs
325
+ # issue here is the 'action_name' on the root 'resource_class' may not be the action that the user has for the 'assoc_class'
326
+ # i.e:
327
+ # We may want the user to update Account, and create attachments on it, but not 'update' attachments.
328
+ if depth == 0
329
+ association_parameters = current_ability.permitted_attributes(action_name, (root_level_object || resource_class))
330
+ else
331
+ association_parameters = current_ability.permitted_attributes(action_name, resource_class)
332
+ end
333
+
334
+ if resource_class.const_defined?('RESOURCE_CONTROLLER_ATTRIB_ALLOWLIST') && !resource_class::RESOURCE_CONTROLLER_ATTRIB_ALLOWLIST.nil?
335
+ association_parameters &= resource_class::RESOURCE_CONTROLLER_ATTRIB_ALLOWLIST
336
+ end
337
+
338
+ # remove customized, non-params, assoc' attrib data by only allowing class columns
339
+ association_parameters &= resource_class.column_names.collect(&:to_sym)
340
+
341
+ resource_class.reflect_on_all_associations(:has_many).each do |assoc_class|
342
+ resource_key = assoc_class.name
343
+ # attrib_permission_key = (resource_key.to_s.singularize + '_ids').to_sym
344
+ attrib_permission_key = IDS_ATTIB_PERMISSION_KEY_GEN.call(resource_key)
345
+ # action_permission_key = ('_can_add_or_remove_association_' + resource_key.to_s).to_sym
346
+ action_permission_key = IDS_ACTION_PERMISSION_KEY_GEN.call(resource_key)
347
+ # i.e. can?(:can_participation_ids, Account)
348
+ # Check to see if we manually gave the user a custom permission
349
+ # # (i.e.: can [:update, :can_account_sector_ids], Account)
350
+ # OR
351
+ # see if it has the attribute on the class's allowed params
352
+ if can?(action_permission_key, resource_class) || can?(action_name, resource_class, attrib_permission_key)
353
+ association_parameters << {
354
+ attrib_permission_key => []
355
+ }
356
+ end
357
+ end
358
+
359
+ resource_class.nested_attributes_options.each do |resource_key, options|
360
+ reflection_class = resource_class.reflect_on_association(resource_key).class
361
+ reflection_type = reflection_class.name
362
+ assoc_class = resource_class.reflect_on_association(resource_key).klass
363
+
364
+ if [
365
+ "ActiveRecord::Reflection::BelongsToReflection",
366
+ "ActiveRecord::Reflection::HasOneReflection",
367
+ "ActiveRecord::Reflection::HasManyReflection"
368
+ ].include?(reflection_type)
369
+ parameter_key = NESTED_ATTIB_PERMISSION_KEY_GEN.call(resource_key)
370
+ permission_key = NESTED_ACTION_PERMISSION_KEY_GEN.call(resource_key)
371
+
372
+ # Can check if permission to update assoc is defined as an action OR as an attrib on the parent resource_class
373
+ if can?(permission_key, resource_class) || can?(action_name, resource_class, parameter_key)
374
+ # Handle recursion
375
+ assoc_parameters = get_nested_attributes_for_class(assoc_class, action_name, root_level_object, depth + 1)
376
+
377
+ if options[:allow_destroy] && can?(:destroy, resource_class)
378
+ assoc_parameters << :_destroy
379
+ end
380
+
381
+ association_parameters << {
382
+ parameter_key => assoc_parameters
383
+ }
384
+ end
385
+ end
386
+ end
387
+
388
+ return association_parameters
389
+ end
390
+
391
+ def param_permit base_parameters
392
+ params.permit(base_parameters)
393
+ end
394
+
395
+ def initialize_resource_class
396
+ # First priority is the namespaced model, e.g. User::Group
397
+ @resource_class ||= begin
398
+ namespaced_class = self.class.name.sub(/Controller$/, '').singularize
399
+ namespaced_class.constantize
400
+ rescue NameError
401
+ nil
402
+ end
403
+
404
+ # Second priority is the top namespace model, e.g. EngineName::Article for EngineName::Admin::ArticlesController
405
+ @resource_class ||= begin
406
+ namespaced_classes = self.class.name.sub(/Controller$/, '').split('::')
407
+ namespaced_class = [namespaced_classes.first, namespaced_classes.last].join('::').singularize
408
+ namespaced_class.constantize
409
+ rescue NameError
410
+ nil
411
+ end
412
+
413
+ # Third priority the camelcased c, i.e. UserGroup
414
+ @resource_class ||= begin
415
+ camelcased_class = self.class.name.sub(/Controller$/, '').gsub('::', '').singularize
416
+ camelcased_class.constantize
417
+ rescue NameError
418
+ nil
419
+ end
420
+
421
+ # Otherwise use the Group class, or fail
422
+ @resource_class ||= begin
423
+ class_name = self.controller_name.classify
424
+ class_name.constantize
425
+ rescue NameError => e
426
+ raise unless e.message.include?(class_name)
427
+ nil
428
+ end
429
+ # portal/portal_imports case needed this
430
+ @resource_class ||= begin
431
+ class_name = controller_path.classify
432
+ class_name.camelize.singularize.constantize
433
+ rescue NameError => e
434
+ raise unless e.message.include?(class_name)
435
+ nil
436
+ end
437
+ end
438
+
439
+ end
440
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'cancancan/abstract_resource_controller'
2
+
3
+ # include the extension
4
+ # ActiveRecord::Base.send(:include, Serializer::Concern)
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cancancan_resource_controller
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - benjamin.dana.software.dev@gmail.com
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cancancan
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.5.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.5.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 3.5.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.5.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: rails
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 6.1.7.3
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 6.1.7.3
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.9'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.9'
61
+ - !ruby/object:Gem::Dependency
62
+ name: listen
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.2'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.2'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec-rails
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '4.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '4.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: database_cleaner
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.8'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.8'
103
+ - !ruby/object:Gem::Dependency
104
+ name: sqlite3
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.4'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '1.4'
117
+ description:
118
+ email:
119
+ executables: []
120
+ extensions: []
121
+ extra_rdoc_files: []
122
+ files:
123
+ - lib/cancancan/abstract_resource_controller.rb
124
+ - lib/cancancan_resource_controller.rb
125
+ homepage: https://github.com/danabr75/cancancan_resource_controller
126
+ licenses:
127
+ - LGPL-3.0-only
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '2.4'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubygems_version: 3.3.7
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: A Rails Controller Module that uses CanCan's permitted attribs instead of
148
+ typical parameter allowlisting.
149
+ test_files: []