Allowing to pass an array in DeviseTokenAuth strong params - ruby-on-rails

I am having an issue with strong params, passing an array in Devise Token Auth gem LINK
# ERROR
Unpermitted parameters: options
Configure Permitted Params
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << [:username, options: []]
end
# also added in User.rb file
attr_accessor :options
I have tried many options, but its not allowing me to pass a data in array.
Is there any solutions ?
Thank You!

Here is the example that will help you to setup the parameters:
# You can put the params you want to permit in the empty array.
def configure_sign_up_params
devise_parameter_sanitizer.for(:sign_up).push(:first_name, :last_name, :arr_options)
end
In your model:
class User
attr_accessor :arr_options
def initialize
self.arr_options = []
end
end

Related

Rails: Using Strong Params as keyword parameters

Let's say I have a User Model with a class method create_with_info. Currently if I want to password the params into the method using keyword parameters, It will be something like this.
# user_controller.rb
def create_with_info
User.create_with_info(**user_info_params)
end
private
def user_info_params
params.require([:name, :age, :email])
params.permit(:name, :age, :email).to_h.symbolize_keys
end
# user.rb
def self.create_with_info(name:, age:, email:)
# do something
end
I'm not sure is it the correct way to use keyword parameters in controller or is there a better way to handle? using to_h.symbolize_keys is annoying for me.

Devise - overriding ParameterSanitizer

I am attempting to configure a different Devise strong parameter sanitizer per model following the instructions at; https://github.com/plataformatec/devise#strong-parameters
I have created a new file named parameter_sanitizer within my Employer model directory;
app/controllers/Employers/paramater_sanitizer.rb
class Employer::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
devise_parameter_sanitizer.permit(:sign_up, keys: [:forename, :surname, :username)
end
end
Within my application controller I have;
require 'employers/parameter_sanitizer'
class ApplicationController < ActionController::Base
before_filter :devise_parameter_sanitizer, if: :devise_controller?
protect_from_forgery with: :exception
protected
def devise_parameter_sanitizer
if resource_class == Employer
Employer::ParameterSanitizer.new(Employer, :employer, params)
else
super # Use the default one
end
end
end
The error I get from signing up an Employer object is;
NameError in Devise::ConfirmationsController#show
undefined local variable or method `devise_parameter_sanitizer' for #
Any advice on how to overcome this?
Thanks,
Mark
In this initialize method you save the params as an instance variables #params so in your method you should do:
class Employer::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
#params.permit(:sign_up, keys: [:forename, :surname, :username])
end
also I believe this should work without specifying #params
Explanation
to find the solution to this problem check the devise api to better understand the methods you are calling and read the `Devise::ParameterSanitizer source code
I am quoting their ruby-rocs about the #permit() method
Instance Method Details
#permit(action, keys: nil, except: nil, &block) ⇒ Object
Add or remove new parameters to the permitted list of an action.
Arguments
action - A Symbol with the action that the controller is performing, like sign_up, sign_in, etc.
keys: - An Array of keys that also should be permitted.
except: - An Array of keys that shouldn't be permitted.
block - A block that should be used to permit the action parameters instead of the Array based approach. The block will be called with an ActionController::Parameters instance.
Examples
# Adding new parameters to be permitted in the `sign_up` action.
devise_parameter_sanitizer.permit(:sign_up, keys: [:subscribe_newsletter])
# Removing the `password` parameter from the `account_update` action.
devise_parameter_sanitizer.permit(:account_update, except: [:password])
# Using the block form to completely override how we permit the
# parameters for the `sign_up` action.
devise_parameter_sanitizer.permit(:sign_up) do |user|
user.permit(:email, :password, :password_confirmation)
end
Returns nothing.
Also I quote
If you have multiple Devise models, you may want to set up a different parameter sanitizer per model. In this case, we recommend inheriting from Devise::ParameterSanitizer and adding your own logic:
class ApplicationController < ActionController::Base
protected
def devise_parameter_sanitizer
if resource_class == User
User::ParameterSanitizer.new(User, :user, params)
else
super # Use the default one
end
end
end
User::ParameterSanitizer.new(User, :user, params) will call this initializer method from parameter_sanitizer.rb source code
def initialize(resource_class, resource_name, params)
#auth_keys = extract_auth_keys(resource_class)
#params = params
#resource_name = resource_name
#permitted = {}
DEFAULT_PERMITTED_ATTRIBUTES.each_pair do |action, keys|
permit(action, keys: keys)
end
end
so basically you are calling initialize(User, :user, params), I don't understand why devise is accepting params in this method, as it has his own way of allowing attributes by saving a static hash of permitted field.
DEFAULT_PERMITTED_ATTRIBUTES = {
sign_in: [:password, :remember_me],
sign_up: [:password, :password_confirmation],
account_update: [:password, :password_confirmation, :current_password]
}
and the permitting them with a loop
DEFAULT_PERMITTED_ATTRIBUTES.each_pair do |action, keys|
permit(action, keys: keys)
end
In this initialize method you save the params as an instance variables #params so in your method you should do:
class Employer::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
#params.permit(:sign_up, keys: [:forename, :surname, :username])
end

Rails strong parameters - accepting the virtual attribute

I have ActiveRecord model with persisted name attribute and the virtual attribute.
class MyModel < ActiveRecord::Base
validates :name, length: { minimum: 1 }, presence: true
def virtual_attr=(value)
# set something
end
def virtual_attr
# get something
end
end
In my controller I am specifying strong parameters:
def my_model_params
params.permit(:name, :virtual_attr)
end
When I am trying to create/update my model, my_model_params only contains a name, whilst I know that params[:virtual_attr] has the value that I passed to the controller. It seems like it is just getting filtered out. What am I doing wrong?
According to these params
{"name"=>"New", "virtual_attr"=>{"enable"=>"false", "start"=>"false"}, "controller"=>"my_model", "action"=>"create"}
You need to change strong params to:
def my_model_params
params.permit(:name, virtual_attr: [:enable, :start])
end

Add attributes to an instance using strong params

The prime example of this feature is when creating a user. Instead of saving 'password' to the database you want to take an instance's password and create a password_hash and password_salt from it.
So if I'm creating a form where a user can be created, how can I have a password field if there's no 'password' attribute?
I think previously this could be solved by using attr_accessor in the user model, but I don't know how to do this with strong params:
def user_params
params.require(:user).permit(:username, :email, :password, :password_confirmation)
end
Just results in a UnknownAttributes error when trying to create a new instance via the user_params:
#user = User.new(user_params)
If you want to add to your strong params, you can use the .merge method
private
def user_params
params.require(:user).permit(:x, :y, :z).merge(hair_color: "brown")
end
Alternatively, if you're looking to manipulate the data on save, you'd probably use the ActiveRecord callbacks, namely before_create:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :hash_password
private
def hash_password
if password #-> == self.password
#hash the password
#runs after validations
#runs just before DB insert
end
end
end

Rails strong parameters incremental addition

Does anyone know, if with strong_parameters gem, we can incrementally add attributes like below:
def user_params
params[:user].permit(:name, :email)
if current_user.admin?
params[:user].permit(:is_admin)
end
end
Here I am incrementally asking the code to permit :is_admin parameter if the current user is an admin. Shouldn't it just add to the previously permitted list of parameters (:name and :email)?
They way I have done it is to put all my params in a class Like the strong-parameters railscast..
this way I have something like this
class PermittedParams < Struct.new(:params,:admin)
def administrator_attributes
allowed = [:email, :name, :password, :password_confirmation, :password_digest]
if admin.has_any_role? :realm_admin, :system_admin
allowed << :active << :roles
end
allowed
end
.... other models ....
def method_missing(method,*args,&block)
attributes_name = method.to_s + '_attributes'
if respond_to? attributes_name, false
params.require(method).send(:permit, *method(attributes_name).call)
else
super
end
end
end
then in the controller just call #administrator.update_attributes(permitted_params.administrator)
as it is just an array you can build up the array, and then just use * to pass it into the permit.
That's an interesting question. I am not sure, but my gut reaction in this situation would simply be to test it out. Whether it breaks or not, you'll have your answer.
I have a User model with an expiration date parameter that I would like only administrators to be able to set. There is no way to incrementally add parameters to the permit list...so you have to break DRY and do it like this...
if can? :manage, :all
params.require(:user).permit(:first_name, :last_name, :email, :expiration_date)
else
params.require(:user).permit(:first_name, :last_name, :email)
end

Resources