Rails Strong Parameters - what if one of my fields is optional? - ruby-on-rails

I'd not claim myself to be an expert in Rails by any stretch. One of the things that confuses me is Strong Parameters, and I've not found any really straightforward tutorials on it, with the majority of the search results dominated by hits on the rails documentation which, whilst normally accurate, I don't find in any way easy to read and can't be considered a tutorial. The github for strong params also doesn't appear to cover this.
Say I have an entity called "Resource".
class ResourcesController < ApplicationController
...
def create
#resource = Resource.new(resource_params)
if #resource.save
...
respond_with(#resource.level)
else
...
end
end
def update
if #resource.update(resource_params)
...
respond_with(#resource.level)
else
...
end
end
...
def resource_params
params.require(:resource).permit(:name, :url, :description, :level_id)
end
end
Assume I have a scaffolded form which displays the fields for name, url, description and level_id. All of the fields are mandatory. I don't know how to amend the resource_params function to ensure that name, url and level_id are mandatory when updating (or creating) a resource, but that description is optional (but should still be permitted).
I've tried removing description from the require line, and adding it on a separate line as params.permit(:description) but that has not made any difference, the field is still mandatory in the form.
Any assistance on this would be welcomed!

As I said, this is nothing to do with strong parameters. You just need to remove required: true for that field to make it optional.

I think you should think about :on
http://guides.rubyonrails.org/active_record_validations.html#on
validates :email, uniqueness: true, on: :create
It enables validations only on specified action.

Strong parameters are used to ensure that no other data can pass into your object, that no1 can insert not wanted data. For example if you have entity User and it has field admin that if it is true, the user has admin role, it can not be set using the create or update method inside your controller, because some1 can send any kind of parameters to your controller, and you want to filter them (Allow only specific parameters).
This does not mean if you put something inside strong parameters that it is required for create, update or whatever. Strong parameters only are there to filter the data, and allow only certain parts of your entity to be settable through this service or REST. If you send parameters that are not in strong params defined, in your server console/log there will be unpermited parameters: list of the parameters.

Related

How to validate request parameters in rails 4 like laravel 5?

I have an action controller to validate params that are coming from view.
def duplicate
#params to be validated
params = params[:group_to_duplicate]
#params have to be validated to avoid method 'find' for some reason
group = Group.find(params[:id])
#validate to avoid this 'if'
if group
group.duplicate params
notice = 'Some message'
else
notice = 'Some other message'
end
redirect_to groups_path, notice: notice
end
How to validate the request parameters coming from view, like laravel 5 enter link description here?
Sometimes you do need to validate request params, and not the model.
API's for example. You often need to validate the params given. Maybe you require a date range for an API that returns stock quotes.
The quote model is valid, but in order to fetch it properly you need to know the range the user needs because you don't allow them to have the entire quote history. Something like that.
Also in the case of a search form. Maybe you have a form that requires the user to type the last name before searching for an employee.
In these cases you can use form or view objects to help with the validation.
Here's quick article on form objects:
https://robots.thoughtbot.com/activemodel-form-objects
I've been looking at this as well. I'm going to give this a shot when I get some free time:
https://github.com/nicolasblanco/rails_param
In most cases, you should not have to validate request parameters in your controller.
The best practice for model form validations (required, unique, length, max, etc. ) is to define validations in the model i.e. /models/group.rb, and use a form library such as simple_form which automatically handles the model validations you've defined.
For example, in your group.rb you'll have:
class Group < ActiveRecord::Base
validates :group, presence: true
end
If you absolutely need to validate in your controller, use Action Controller Filters: http://guides.rubyonrails.org/action_controller_overview.html#filters
References:
http://guides.rubyonrails.org/active_record_validations.html
https://github.com/plataformatec/simple_form

Rails Custom Validation With Request Params?

Rails noob ,
I am trying to write a custom validation but I am dependent on parameters that come from the post request. This example is similar to what I have - in this post model I want to validate that a user didn't already post on some topic, but to do that I have to have the topic id from the post request:
class Post < ActiveRecord::Base
...
validate :user_already_posted, on: :create
def user_already_posted
topic=Topic.where(id: params[:topicId]).first
//some code that runs on topic users and makes sure the user hasn't posted there
However , I learned that params isn't global and models can't access it (I last used laravel where this isn't the case , so it's confusing for me).
Are custom validations not suited for what I need ? do I need to look at filters or have the controller run a validating function of it's own?
You could do this:
validates_uniqueness_of :user, scope: :topic_id
May not be exactly what you need, but the point is that you can validate within a scope.
You can also pass an array to validate on multiple parameters.
validates_uniqueness_of :user, scope: [:topic_id, :post_id]
I think you are having a bit of a design problem here. You are creating a post validator, but the validation itself is not about the post instance. I mean, the model validator must evaluate all of the post attributes, using the default validators built-in ActiveRecord or creating custom ones, like you did, acessing the attributes via self.
I believe a class method receiving all your parameters and checking the conditions of the post creation is more clear:
Class method on the model
def self.user_already_posted(topic_id, user_id)
# Do your thing
end
Usage on the controller
can_create = Post.user_already_posted(params[:topic_id], params[:user_id)

Shortcut for including strong parameters in Rails 4 : without listing all fields

I am starting with Rails 4. Had came across to the new security feature strong parameters related to permitting parameter in a controller.
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
This is fine, but we need to list down all the fields from the models. Is there a easy way by which listing fields down the is not required.
Thanks.
One shortcut you can do if you have say around 20 columns in your model and you don't need to specify all columns inside permit in your controller then it as:
params.require(:person).permit!
This will permit all the attributes from your model
Here's a quick "getting started" tip regarding cases where you have "lots" of fields for your model Foo... rather then whitelist them all, auto-generate a list then remove the ones you know should not be mass-assigned.
In rails console you can get a symbol list of all fields very easily, copy/paste it to the permit(...) method, then remove the ones that should not be mass-assigned like :id, :ctreated_at, :admin, etc
Foo.attribute_names.map(&:to_sym).sort
> [:address, :admin, :created_at, :id, :name, :updated_at]
This takes only a few seconds longer than using the .permit! approach, but gives you a better starting point from a security point of view.
Strong Parameters were introduced in Rails 4:
It provides an interface for protecting attributes from end-user
assignment. This makes Action Controller parameters forbidden to be
used in Active Model mass assignment until they have been whitelisted.
Basically, it means only certain param values will be sent through your controller to the model (thus allowing you more control over which data is handled by Rails)
DRY
If you're wanting to use strong params for multiple controllers, or just want to DRY up the process, we use this:
#app/controllers/application_controller.rb
private
#Strong Params
def permitted_params
#resource = self.resource_class
#model = "#{#resource}".downcase.to_sym
attributes = #resource.attribute_names + %w(custom items here)
params.permit(#model => attributes)
end

strong parameters permit all attributes for nested attributes

Is there a way in strong parameters to permit all attributes of a nested_attributes model? Here is a sample code.
class Lever < ActiveRecord::Base
has_one :lever_benefit
accepts_nested_attributes_for :lever_benefit
end
class LeverBenefit < ActiveRecord::Base
# == Schema Information
# id :integer not null, primary key
# lever_id :integer
# explanation :text
end
For lever strong parameters i am writing currently this
def lever
params.require(:lever).permit(:name,:lever_benefit_attributes => [:lever_id, :explanation])
end
Is there a way for nested attributes i can write to permit all attributes without explicitly giving the attributes name like lever_id and explanation ?
Note: Please don't get confused with this question with permit! or permit(:all) this is for permitting all for nested attributes
The only situation I have encountered where permitting arbitrary keys in a nested params hash seems reasonable to me is when writing to a serialized column. I've managed to handle it like this:
class Post
serialize :options, JSON
end
class PostsController < ApplicationController
...
def post_params
all_options = params.require(:post)[:options].try(:permit!)
params.require(:post).permit(:title).merge(:options => all_options)
end
end
try makes sure we do not require the presents of an :options key.
I am surprised at no one suggested this:
params.require(:lever).permit(:name,:lever_benefit_attributes => {})
Actually there is a way to just white-list all nested parameters.
params.require(:lever).permit(:name).tap do |whitelisted|
whitelisted[:lever_benefit_attributes ] = params[:lever][:lever_benefit_attributes ]
end
This method has advantage over other solutions. It allows to permit deep-nested parameters.
While other solutions like:
nested_keys = params.require(:lever).fetch(:lever_benefit_attributes, {}).keys
params.require(:lever).permit(:name,:lever_benefit_attributes => nested_keys)
Don't.
Source:
https://github.com/rails/rails/issues/9454#issuecomment-14167664
First, make sure that you really want to allow all values in a nested hash. Read through Damien MATHIEU's answer to understand the potential opening of security holes...
If you still need/want to allow all values in a hash (there are perfectly valid use cases for this, e.g. storing unstructured, user-provided metadata for a record), you can achieve it using the following bits of code:
def lever_params
nested_keys = params.require(:lever).fetch(:lever_benefit_attributes, {}).keys
params.require(:lever).permit(:name,:lever_benefit_attributes => nested_keys)
end
Note: This is very similar to tf.'s answer but a bit more elegant since you will not get any Unpermitted parameters: lever_benefit_attributes warnings/errors.
try
params.require(:lever).permit(:name, leave_benefit_attributes: LeaveBenefit.attribute_names.collect { |att| att.to_sym })
The whole point of strong parameters is in its name: make your input parameters strong.
Permitting all the parameters would be a very bad idea, as it would permit anyone to insert values you don't necessarily want to be updated by your users.
In the example you give, you mention the two parameters you currently need to provide:
[:lever_id, :explanation].
If you permitted all the parameters, it would be possible for somebody to change any other value.
created_at, or lever_id for example.
This would definitely be a security issue and this is why you should not do it.
Explicitely specifying all your attributes might seem boring when you do it.
But this is necessary to keep your application secure.
Edit: For people downvoting this. This may not be the answer you're looking for, but it is the answer you need.
Whitelisting all nested attributes is a huge security flaw that strong params is trying to protect you with, and you're removing it.
Take a look at what lead to building strong_params, and how not using it can be bad for you: https://gist.github.com/peternixey/1978249

Prevent certain properties from being updated?

In rails, when updating a model, how do you prevent certain properties of the model from being updated when using a call like:
#user.update_profile params[:user]
Since anyone can just create a form input with a name like 'password', how can you filter the set of properties that you are allowing to be updatable?
Is this what attr_XXX is for?
You're looking for attr_accessible. It lets you specify which attributes can be set through mass-updating (like update_attributes), but you'll still be able to set the attributes "manually" (ie #user.attribute = ...).
For more information, see The importance of attr_accessible in Ruby on Rails.
You're looking for attr_protected to black list any attributes you don't want altered in a bulk update.
Throw it in your model and give it a list of attribute symbols to blacklist.
class User < ActiveRecord::Base
attr_protected :password
end
Alternatively you can use attr_accessible to take the white list approach and only the attributes given can be updated when updating the entire record at once. Every other attribute will be protected.
N.B Protected attributes can still be overwritten if it's directly assigned to as in
#user.password = "not secure"

Resources