Awesome
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://github.com/westonganger/protected_attributes_continued/actions' target='_blank'><img src="https://github.com/westonganger/protected_attributes_continued/actions/workflows/test.yml/badge.svg?branch=master" style="max-width:100%;" height='21' style='border:0px;height:21px;' border='0' alt="CI Status"></a> <a href='https://rubygems.org/gems/protected_attributes_continued' target='_blank'><img height='21' style='border:0px;height:21px;' src='https://img.shields.io/gem/dt/protected_attributes_continued?color=brightgreen&label=Rubygems%20Downloads' border='0' alt='RubyGems Downloads' /></a>
This is the community continued version of
protected_attributes
for Rails 5+. The Rails team dropped this feature and switched tostrong_parameters
. However some applications simply cannot be upgraded or the reduced granularity in params management is a non-issue. To continue supporting this feature going forward we continue the work here.
This gem only supports Rails 5.0 to Rails 6.1. Starting in Rails 7.0 some API's have been changed and this gem will not be updated to support it, please transition to strong params or the suggested alternative approach at the bottom of the readme.
Protect attributes from mass-assignment in Active Record models. This gem adds the class methods attr_accessible
and attr_protected
to declare white or black lists of attributes.
Installation
Add this line to your application's Gemfile
:
gem 'protected_attributes_continued'
And then execute:
bundle install
Usage
Mass assignment security provides an interface for protecting attributes from end-user injection. This plugin provides two class methods in Active Record classes to control access to their attributes. The attr_protected
method takes a list of attributes that will be ignored in mass-assignment.
For example:
attr_protected :admin
attr_protected
also optionally takes a role option using :as
which allows you to define multiple mass-assignment groupings. If no role is defined then attributes will be added to the :default
role.
attr_protected :last_login, as: :admin
A much better way, because it follows the whitelist-principle, is the attr_accessible
method. It is the exact opposite of attr_protected
, because it takes a list of attributes that will be mass-assigned if present. Any other attributes will be ignored. This way you won’t forget to protect attributes when adding new ones in the course of development. Here is an example:
attr_accessible :name
attr_accessible :name, :is_admin, as: :admin
If you want to set a protected attribute, you will have to assign it individually:
params[:user] # => {name: "owned", is_admin: true}
@user = User.new(params[:user])
@user.is_admin # => false, not mass-assigned
@user.is_admin = true
@user.is_admin # => true
When assigning attributes in Active Record using attributes=
the :default
role will be used. To assign attributes using different roles you should use assign_attributes
which accepts an optional :as
options parameter. If no :as
option is provided then the :default
role will be used.
You can also bypass mass-assignment security by using the :without_protection
option. Here is an example:
@user = User.new
@user.assign_attributes(name: 'Josh', is_admin: true)
@user.name # => Josh
@user.is_admin # => false
@user.assign_attributes({ name: 'Josh', is_admin: true }, as: :admin)
@user.name # => Josh
@user.is_admin # => true
@user.assign_attributes({ name: 'Josh', is_admin: true }, without_protection: true)
@user.name # => Josh
@user.is_admin # => true
In a similar way, new
, create
, create!
, update_attributes
and update_attributes!
methods all respect mass-assignment security and accept either :as
or :without_protection
options. For example:
@user = User.new({ name: 'Sebastian', is_admin: true }, as: :admin)
@user.name # => Sebastian
@user.is_admin # => true
@user = User.create({ name: 'Sebastian', is_admin: true }, without_protection: true)
@user.name # => Sebastian
@user.is_admin # => true
By default the gem will use the strong parameters protection when assigning attribute, unless your model has attr_accessible
or attr_protected
calls.
Errors
By default, attributes in the params hash which are not allowed to be updated are just ignored. If you prefer an exception to be raised configure:
config.active_record.mass_assignment_sanitizer = :strict
Any protected attributes violation raises ActiveModel::MassAssignmentSecurity::Error
then.
Contributing
For quicker feedback during gem development or debugging feel free to use the provided rake console
task. It is defined within the Rakefile
.
We test multiple versions of Rails
using the appraisal
gem. Please use the following steps to test using appraisal
.
bundle exec appraisal install
bundle exec appraisal rake test
Credits
Created & Maintained by Weston Ganger - @westonganger
Originally forked from the dead/unmaintained protected_attributes
gem by the Rails team.
A Simple and Similar strong_params Alternative
While I do utilize this gem in some legacy projects. The latest approach I have adopted is similar to this gem but only utilizes Rails built-in strong_params
which is a much more future proof way of doing things. The following is an example implementation.
### Model
class Post < ActiveRecord::Base
has_many :comments
accepts_nested_attributes_for :comments, allow_destroy: true
def self.strong_params(params)
params.permit(:post).permit(*PERMITTED_ATTRIBUTES)
end
PERMITTED_ATTRIBUTES = [
:id,
:name,
:content,
:published_at,
{
comments_attributes: Comment::PERMITTED_ATTRIBUTES,
}
].freeze
end
### Controller
class PostsController < ApplicationController
def create
@post = Post.new(Post.strong_params(params))
@post.save
respond_with @post
end
def update
@post = Post.find(params[:id])
@post.update(Post.strong_params(params))
respond_with @post
end
end