Devise - overriding ParameterSanitizer - ruby-on-rails

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

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.

Ruby on Rails/Devise - How can I pass a variable from Registrations#Create to the Mailer?

I'd like to pass session data from my Registrations controller to my custom mailer, which is triggered in the create action. I'm able to do this using a global variable, but would greatly prefer not to go that route. The data I'd like to pass is stored in the welcome_email_token variable.
registrations_controller.rb:
module Users
class RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
def create
client_with_valid_email_token = Client.registerable_email_token(session[:email_token])
if client_with_valid_email_token.first.present?
welcome_email_token = client_with_valid_email_token.first.email_token
super
end
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute])
end
end
end
custom_mailer.rb:
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts = {})
client = Client.find_by(email_token: welcome_email_token)
#first_name = client.first_name
super
end
end
The code as written returns nil for user, leading user.first_name to throw an error. Is there a way I can pass this information to the mailer without using a global variable?
I guess the record you are looking for, is already passed as the first parameter of this method. If you are changing so little to the method, why just go with the standard method already provided by Devise?
class CustomMailer < Devise::Mailer
helper :application
include Devise::Controllers::UrlHelpers
default template_path: 'devise/mailer'
def confirmation_instructions(record, token, opts = {})
#user = record
super
end
end
The super is basically also calling the original confirmation_instructions method from Devise, which is:
def confirmation_instructions(record, token, opts={})
#token = token
devise_mail(record, :confirmation_instructions, opts)
end
That uses a helper method called devise email:
# Configure default email options
def devise_mail(record, action, opts = {}, &block)
initialize_from_record(record)
mail headers_for(action, opts), &block
end
So I don't really see what you are trying to achieve in your method, that's not already handled by Devise.

Allowing to pass an array in DeviseTokenAuth strong params

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

Configuring permit for 2 user models in Devise

I've been busting myself for a ton of hours now on how to do this.
I have 2 models.
intern.rb
company.rb
I've been able to add custom fields on registration, for me that was :name. I did that by adding:
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |i| i.permit(:email, :password, :name) }
end
I've been reading up on the devise readme and trying to understand how to add it so it's specific to one user model. But I get an error every single time.
I tried creating a controller called:
interns_controller.rb
with the code:
class Intern::ParameterSanitizer < Devise::ParameterSanitizer
def sign_up
default_params.permit(:email, :password, :name)
end
end
and then adding this into the
application_controller.rb
code:
protected
def devise_parameter_sanitizer
if resource_class == Intern
Intern::ParameterSanitizer.new(Intern, :intern, params)
else
super
end
end
however I get an error. What am I doing wrong here?
Image with error: http://s12.postimg.org/fqpwyzzdp/Screen_Shot_2014_06_17_at_15_41_58.png

Rails 3.2 model constructor with Rails 4

My model constructor:
3.2:
def initialize(attributes = {})
super # must allow the active record to initialize!
attributes.each do |name, value|
send("#{name}=", value)
end
end
4.0.4:
ActiveModel::ForbiddenAttributesError
How do I change the 3.2 constructor for 4.0.4 compatibility?
I guess you are getting this error when you try to create a new user from your User controller?
If you have code like:
class UsersController < ApplicationController
...
def create
#user = User.new(params[:user])
...
end
...
end
Then this won't work because you can no longer do mass assignments in Rails 4.
Instead you need to whitelist the parameters in the controller something like this:
class UsersController < ApplicationController
...
def create
#user = User.new(user_params)
...
end
...
private
def user_params
params.require(:user).permit(:username, :password, :password_confirmation, :email) # etc. according to your user model
end
end
Having said this, I'm not sure why you need the constructor in your model at all? The initialize method you get from inheriting from ActiveRecord::Base should be enough. But in any event you will need to whitelist the parameters in your controller to avoid the ForbiddenAttributesError error.

Resources