Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
Fix Rails 5.2.x new/create arguments error
Browse files Browse the repository at this point in the history
  • Loading branch information
westonganger committed Dec 28, 2018
1 parent ec0bd16 commit 3019080
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 5 deletions.
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Protected Attributes Continued
<a href="https://badge.fury.io/rb/protected_attributes_continued" target="_blank"><img height="21" style='border:0px;height:21px;' border='0' src="https://badge.fury.io/rb/protected_attributes_continued.svg" alt="Gem Version"></a>
<a href='https://travis-ci.org/westonganger/protected_attributes_continued' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://api.travis-ci.org/westonganger/protected_attributes_continued.svg?branch=master' border='0' alt='Build Status' /></a>
<a href='https://rubygems.org/gems/protected_attributes_continued' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://ruby-gem-downloads-badge.herokuapp.com/protected_attributes_continued?label=rubygems&type=total&total_label=downloads&color=brightgreen' border='0' alt='RubyGems Downloads' /></a>

This is the community continued version of `protected_attributes`. It works with Rails 5 only and I recommend you only use it to support legacy portions of your application that you do not want to upgrade. Note that this feature was dropped by the Rails team and switched to strong_parameters because of security issues, just so you understand your risks. This is in use successfully in some of my Rails 5 apps in which security like this is a non-issue. For people who would like to continue using this feature in their Rails 5 apps lets continue the work here.
This is the community continued version of `protected_attributes`. It works with Rails 5+ only and I recommend you only use it to support legacy portions of your application that you do not want to upgrade. Note that this feature was dropped by the Rails team and switched to strong_parameters because of security issues, just so you understand your risks. This is in use successfully in some of my Rails 5 apps in which security like this is a non-issue. For people who would like to continue using this feature in their Rails 5 apps lets continue the work here. If you are looking for a similar approach see my [recommended alternative](https://github.com/westonganger/protected_attributes_continued#a-better-alternative)

Protect attributes from mass-assignment in Active Record models.

Expand Down Expand Up @@ -98,10 +99,49 @@ config.active_record.mass_assignment_sanitizer = :strict
Any protected attributes violation raises `ActiveModel::MassAssignmentSecurity::Error` then.


# Credits
## Contributing

Created and Maintained by [Weston Ganger - @westonganger](https://github.com/westonganger)
We use the `appraisal` gem for testing multiple versions of `Rails`. Please use the following steps to test using `appraisal`.

1. `bundle exec appraisal install`
2. `bundle exec appraisal rake test`

## Credits

Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)

Originally forked from the dead/unmaintained `protected_attributes` gem by the Rails team.

<a href='https://ko-fi.com/A5071NK' target='_blank'><img height='32' style='border:0px;height:32px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=a' border='0' alt='Buy Me a Coffee' /></a>
## A Better Alternative

While I do utilize this gem in some legacy projects I have adopted an alternative approach that is similar to this gem but only utilizes Rails built-in `strong_params` which is a much more future proof way of doing things. See the following example for how to implement.

```ruby
class Post < ActiveRecord::Base

def self.strong_params(params)
params.permit(:post).permit(:name, :content, :published_at)
end

end

class PostsController < ApplicationController

def create
@post = Post.new

@post.assign_attributes(Post.strong_params(params))

respond_with @post
end

def update
@post = Post.find(params[:id])

@post.update(Post.strong_params(params))

respond_with @post
end

end
```
30 changes: 29 additions & 1 deletion lib/active_record/mass_assignment_security/inheritance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Inheritance
extend ActiveSupport::Concern

module ClassMethods

private

# Detect the subclass from the inheritance column of attrs. If the inheritance column value
Expand All @@ -16,3 +16,31 @@ def subclass_from_attributes(attrs)
end
end
end

module ActiveRecord
module Inheritance
module ClassMethods
undef :new

def new(attributes = nil, options = {}, &block)
if abstract_class? || self == Base
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
end

if has_attribute?(inheritance_column)
subclass = subclass_from_attributes(attributes)

if respond_to?(:column_defaults) && subclass.nil? && base_class == self
subclass = subclass_from_attributes(column_defaults)
end
end

if subclass && subclass != self
subclass.new(attributes, options, &block)
else
super
end
end
end
end
end
29 changes: 29 additions & 0 deletions lib/active_record/mass_assignment_security/relation.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
module ActiveRecord
class Relation
undef :new
undef :create
undef :create!
undef :first_or_create
undef :first_or_create!
undef :first_or_initialize
undef :find_or_initialize_by
undef :find_or_create_by
undef :find_or_create_by!

def new(attributes = nil, options = {}, &block)
attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes

scoping { klass.new(attrs, options, &block) }
end

def create(attributes = nil, options = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr, options, &block) }
else
attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes

scoping { klass.create(attrs, options, &block) }
end
end

def create!(attributes = nil, options = {}, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create!(attr, options, &block) }
else
attrs = respond_to?(:values_for_create) ? values_for_create(attributes) : attributes

scoping { klass.create!(attrs, options, &block) }
end
end

# Tries to load the first record; if it fails, then <tt>create</tt> is called with the same arguments as this method.
#
# Expects arguments in the same format as +Base.create+.
Expand Down

0 comments on commit 3019080

Please sign in to comment.