How to sanitize grape params - ruby-on-rails

I want to mass update attributes of an entity.
How can I sanitize properly the params which is coming from grape?
This is my console log about the parameters:
params.except(:route_info, :token, :id)
=> {"display_number"=>"7"}
[18] pry(#<Grape::Endpoint>)> params.permit(:display_number)
ArgumentError: wrong number of arguments (2 for 0..1)
from /Users/boti/.rvm/gems/ruby-2.0.0-p353#thelocker/gems/hashie-2.0.5/lib/hashie/mash.rb:207:in `default'
[19] pry(#<Grape::Endpoint>)> params.sanitize
=> nil

In grape you need to declare your params before the actual method.
Within the method the params object is a Hashie::Mash instance, and does not have APIs like permit and sanitize...
Here is the relevant documentation for declaring and validating parameters in grape:
You can define validations and coercion options for your parameters
using a params block.
params do
requires :id, type: Integer
optional :text, type: String, regexp: /^[a-z]+$/
group :media do
requires :url
end
optional :audio do
requires :format, type: Symbol, values: [:mp3, :wav, :aac, :ogg], default: :mp3
end
mutually_exclusive :media, :audio
end
put ':id' do
# params[:id] is an Integer
end
When a type is specified an implicit validation is done after the
coercion to ensure the output type is the one declared.
If you still want to use strong parameters, you'll need to use the strong_parameters gem, and create a new instance of ActionController::Paramter yourself:
raw_parameters = { :email => "john#example.com", :name => "John", :admin => true }
parameters = ActionController::Parameters.new(raw_parameters)
user = User.create(parameters.permit(:name, :email))

Related

How do you set up dynamic enumerables on Rails 5?

I'm trying to create a select input that takes specific User models and displays them as a string while saving them as an integer. Typically I would set the enumerable up the same way as below but as a static hash.
While attempting to create a new Product, I keep receiving the following error: "undefined local variable or method `user_business_hash'"
I've tried moving the 'user_business_hash' method to application/products controller, application/products helper with no luck.
Model
enum user_id: user_business_hash
validates :user_id, inclusion: user_ids.keys
def user_business_hash
# output: {User.business_name => User.id }
# ex: {"Business Name A"=>2, "Business Name B"=>1, "Business Name C"=>5}
array = User.where(account_type: 'Business').map{|x| [x.business_name, x.id] }
hash = array.inject({}) do |memo, (key, value)|
memo[key] = value
memo
end
return hash
end
Form
<%= form.select :user_id, Product.user_ids.keys, prompt: 'Select', id: :product_user_id %>
I think that what you actually want is:
on your controller
#options = User.where(account_type: 'Business')
on your view
options_from_collection_for_select(#options, "id", "business_name")

Rails Strong params when attribute can be string or hash

I have a parameter that can be either a string or a hash (metadata) and I would like to allow both using rails strong params
the problem is that metadata can either be a hash or a String or both
{transactions: [{metadata: "hello"}, metadata: {name: "world"}]}
With the rails params
params.permit(:transactions => [:reference_id, :metadata => ["name"]])
I have the following error
expected Hash (got String) for param `metadata'
I see the only way to permit, is to verify the constant parameters at first, then verify a parameter with variable type:
params.permit(:transactions => [:reference_id, :metadata])
begin
params.permit(:metadata => {})
rescue TypeError
# here check that :metadata is a string
end

Rails4 PG hstore Unpermitted parameters: data

I have a Rails4 app using PG hstore.
This is from the console:
This is the Vehicle Controller:
def vehicle_params
params.require(:vehicle).permit(:description, :stufftype_id, :name, :tenant_id, :count, :manufacturer, :man_date, :purchase_date, :purchase_price, :current_price, :warranty_date, :model, :notes, :site_id, :sell, :loaned, :borrowed, :sell_to, :borrowed_from, :sale_id, :sale_price, :sold_amount, :sold, :archive, :vendor_id, :loaned_to, :data)
end
It contains :data
So, why is data an unpermitted parameter?
Thanks for the help!
When you permit :data in your strong parameters, it only permits scalar value.
Scalar values can be of type String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile and Rack::Test::UploadedFile as described in the strong parameters documentation:
https://github.com/rails/strong_parameters#permitted-scalar-values
It means that passing a Hash for :data in the params hash, will not be permitted as is.
What you are looking for is a way to permit nested parameters. Also described in the documentation:
https://github.com/rails/strong_parameters#nested-parameters
And if your hstore keys are dynamic, you can whitelist the dynamic hash yourself using the method described in the following SO question:
rails 4 strong params + dynamic hstore keys
Hope that helps.

Passing an Array of Symbols to Rails' `except()!` Method

Simple question, and I'm a little surprised that this isn't handled better in Rails already.
I am trying to scrub out some superfluous attributes from params in a number of Rails API controllers with the except!() method, like so:
params.except!( :format, :api_key, :controller, :action, :updated_at, :created_at )
Because these attributes are the same across a number of API endpoints, I wanted to store them in a Constant in the API's BaseController, like so:
In BaseController.rb
PARAMS_TO_SCRUB = [ :format, :api_key, :controller, :action, :updated_at, :created_at ]
params.except!( PARAMS_TO_SCRUB ) # => Doesn't work.
But the except!() method only accepts a splat of keys so none of the attributes get filtered:
# File activesupport/lib/active_support/core_ext/hash/except.rb, line 11
def except!(*keys)
keys.each { |key| delete(key) }
self
end
The work around I've setup now is to create a method in the BaseController that scrubs the params with the keys instead, like so:
def scrub_params
params.except!( :format, :api_key, :controller, :action, :updated_at, :created_at )
end
Is there no way to store a list of symbols like this?
Add * before array variable:
PARAMS_TO_SCRUB = [ :format, :api_key, :controller, :action, :updated_at, :created_at ]
params.except!( *PARAMS_TO_SCRUB )
So, the method will change to:
def scrub_params ex_arr
params.except! *ex_arr
end
Or some global or class variable.

Ruby - Ignore protected attributes

How can I tell Ruby (Rails) to ignore protected variables which are present when mass-assigning?
class MyClass < ActiveRecord::Base
attr_accessible :name, :age
end
Now I will mass-assign a hash to create a new MyClass.
MyClass.create!({:name => "John", :age => 25, :id => 2})
This will give me an exception:
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes: id
I want it to create a new MyClass with the specified (unprotected) attributes and ignore the id attribute.
On the side note: How can I also ignore unknown attributes. For example, MyClass doesn't have a location attribute. If I try to mass-assign it, just ignore it.
Use Hash#slice to only select the keys you're actually interested in assigning:
# Pass only :name and :age to create!
MyClass.create!(params.slice(:name, :age))
Typically, I'll add wrapper method for params to my controller which filters it down to only the fields that I know I want assigned:
class MyController
# ...
def create
#my_instance = MyClass.create!(create_params)
end
protected
def create_params
params.slice(:name, :age)
end
end
Setting mass_assignment_sanitizer to :logger solved the issue in development and test.
config.active_record.mass_assignment_sanitizer = :logger
You can use strong_parameters gem, that will be in rails 4.
See the documentation here.
This way you can specify the params you want by action or role, for example.
If you want to get down and dirty with it, and dynamically let only a model's attributes through, without disabling ActiveModel::MassAssignmentSecurity::Errors globally:
params = {:name => "John", :age => 25, :id => 2}
MyClass.create!(params.slice(*MyClass.new.attributes.symbolize_keys.keys)
The .symbolize_keys is required if you are using symbols in your hash, like in this situation, but you might not need that.
Personally, I like to keep things in the model by overriding assign_attributes.
def assign_attributes(new_attributes, options = {})
if options[:safe_assign]
authorizer = mass_assignment_authorizer(options[:as])
new_attributes = new_attributes.reject { |key|
!has_attribute?(key) || authorizer.deny?(key)
}
end
super(new_attributes, options)
end
Use it similarly to :without_protection, but for when you want to ignore unknown or protected attributes:
MyModel.create!(
{ :asdf => "invalid", :admin_field => "protected", :actual_data => 'hello world!' },
:safe_assign => true
)
# => #<MyModel actual_data: "hello world!">

Resources