I am currently using Rails 4 and Devise 3.0.0. I have tried to add a custom field of "Name" to the sign up form and edit registration form. Whenever I submit the form, the following errors arise:
Unpermitted parameters: name
WARNING: Can't mass-assign protected attributes for User: email, password, password_confirmation.
I understand that this has something to do with the way Rails 4 handles parameters, but I do not understand what I am supposed to do about it right now. I have searched around and have seen that I am supposed to add some lines to a User model involving "params."
My user model currently looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
attr_accessible :name, :password, :password_confirmation, :remember_me, :email
end
According to How is attr_accessible used in Rails 4?, I am supposed to add the following code to "The controller."
class PeopleController < ApplicationController
def create
Person.create(person_params)
end
private
def person_params
params.require(:person).permit(:name, :age)
end
end
What controller? And is this literal code? Since I am dealing with User, do I have to use User.create(user_params)? instead of Person.create(person_params)?
Rails 4 has moved parameter sanitisation to the Controller from the Model. Devise handles it for 3 actions, sign_in, sign_up and account_update. For sign_up, the permitted parameters are authentication key (which is :email by default), password and password_confirmation.
If you want to add :name to the User model and use it for sign_up, either change config.authentication_keys = [ :email ] to config.authentication_keys = [ :name ] in /config/initializers/devise.rb or, if you want to use both :email and :name, add this to the ApplicationController
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
end
Also check-
https://github.com/plataformatec/devise#strong-parameters
You have to add this in controller where you have written User.create(user_params). I am assuming that UsersController.
class UsersController < ApplicationController
def create
User.create(user_params)
end
private
def user_params
#assumption: user params are coming in params[:user]
params.require(:user).permit(:name, :age, :and_other_params_you_want_to_allow)
end
end
Yes, you should add one line which is like:-
attr_accessible :name
in your model to allow name to assigned and if it does not work try this How is attr_accessible used in Rails 4?
I have similar problem. So, to fix it I created custom registration controller inherit form DeviseRegistration controller. Check Devise documentation and define controller like this.
class RegistrationsController < Devise::RegistrationsController
before_filter :update_sanitized_params, if: :devise_controller?
def update_sanitized_params
devise_parameter_sanitizer.for(:sign_up) {|u| u.permit(:name, :email, :)}
end
end
Make sure you have define this routes for this controller in config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations" } , :path => '', :path_names => {
:sign_in => 'login',
:sign_out => 'logout'
}
Check this documentation of devise for strong parameter.
i had similar issues, this was my fix:
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) { |u| u.permit!}
end
end
Related
First-time poster, many-time finder-of-answers on the site (thank you!). I'm using Rails 5.2.3, ruby-2.6.2 and Devise gem 4.6.2. I have not been able to get an answer to work, even though there are plenty somewhat related questions here, here, here and here.
When a new User signs up, I want them to select their Company from a dropdown list (already created) in the sign-up form. (Eventually, this will be an admin role, but that's beyond the scope of this question.)
I created a registrations controller and added code per a number of the previous posts. Update, I was not extending Devise as I should have as indicated here: Extending Devise Registration Controller. This is my new Registrations controller.
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
def new
#companies = Company.all
super
end
def create
#companies = Company.all
super
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:company_id])
end
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:company_id])
end
end
And created new files in views/registrations with new.html.erb and edit.html.erb that I copied the exact code from the devise/registrations views.
I updated my routes.rb file to include:
devise_for :users, :controllers => { registrations: 'users/registrations', sessions: 'users/sessions' }
My User model is:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :company
accepts_nested_attributes_for :company
end
My Company model is:
class Company < ApplicationRecord
has_many :users
end
In the new user registration form, this works to provide the dropdown, but when I try to create the new user, it says: 1 error prohibited this user from being saved: Company must exist.
<%= f.collection_select :company, #companies, :id, :name, prompt: true %>
I thought this post would have the answer, but that appears to use Rails 3 and attr_accessible, which was deprecated in Rails 4.
I don't really understand what accept_nested_attributes_for :company does. The only thing in the Company model is the name.
Thank you in advance!
Welcome to StackOverflow.
In order to add more parameters to devise's sign up form, you'll need to sanitize the corresponding parameters using devise's sanitizer.
You should do that like this:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:company_id])
end
end
You can find more information about parameter sanitizing and adding custom fields in this section of devise's readme
If you also want to add a select field including all the existing companies, you should add a collection select:
<%= f.collection_select :company_id, Company.all, :id, :name %>
Got it!
To extend the Devise controller, follow the help here: Extending Devise Registration Controller
The User models must also be updated to include the optional: true because here https://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :company, optional: true
accepts_nested_attributes_for :company
end
I'm using devise_token_authentication gem to build token based authentication rails api, then after that I added some extra fields to Vendor model through different migration, and in order to permit them I wrote this:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:first_name, :last_name, :tax_number])
devise_parameter_sanitizer.permit(:account_update, keys: [:first_name, :last_name, :tax_number])
end
end
Then after that I added another model Customer rails g devise_token_auth:install Customer auth
then in routes.rb
Rails.application.routes.draw do
mount_devise_token_auth_for 'Vendor', at: 'vendor/auth'
mount_devise_token_auth_for 'Customer', at: 'customer/auth'
end
each time I try to sign_up with customers through 'localhost:3000/customer/auth' I got error message: ActiveModel::UnknownAttributeError: unknown attribute 'tax_number' for Customer.
So is there any way to permit the extra fields only for Vendor model and skip 'Customer' ?
look on this setup for multiple devise user models.
or
If you override the RegistrationsController you need to permit extra params directly in registrationsController
class Users::RegistrationsController < DeviseTokenAuth::RegistrationsController
def create
end
def account_update
end
private
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, :first_name, :last_name, :tax_number)
end
end
I'm trying to set up an age restriction when a user registers so that if they are too young they cannot register on an app i'm building.
I had to over-ride devise to allow me to pass through other values to the user (like :birth_date). However I also want to check the age of the user so that if they are too young, they cannot use the app.
What I have right here, in a rudimentary way it works, but it is not quite what I would like.
<%= f.input :birth_date, required: true, start_year:1999 %>
In my user model I created some methods that address the problem, however ultimately my problem is that none of this code is getting hit during the registration process, and that is what I need some help with. If someone could take a look at point me in the right direction, I would greatly appreciate it!
class User < ApplicationRecord
validate :age_restriction
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :cafes
def age_restriction
if (birth_date.to_date + 18.years) < Date.today # assuming dob format is mm/dd/yy
errors.add :birth_date, 'must be older than 18'
end
end
end
The controller I used to over-ride devise I called registration_controller and it is like so
class RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: :create
before_action :configure_account_update_params, only: :update
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:birth_date])
end
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:bith_date])
end
end
My initial controller was my user_controller. Initially I was hoping this would solve my issue, but after some more work realized I needed to over-ride devise (hence the other registrations_controller). I'll admit this may be what is causing me my issue, not sure though.
class UsersController < ActiveRecord::Base
def show
#user = User.find(params[:id])
end
def create
#user = current_user.build(user_params)
#user.save
end
private
def user_params
params.require(:user).permit(:birth_date)
end
end
Use validations.
There is a gem which adds some useful date validators:
https://github.com/adzap/validates_timeliness/
validates_date :date_of_birth, :before => lambda { 18.years.ago },
:before_message => "must be at least 18 years old"
You can use model validations to prevent a user instance from being created if the user does not meet the age restriction you have set:
User.rb
validate :age_restriction
def age_restriction
if (birth_date.to_date + 18.years) < Date.today # assuming dob format is mm/dd/yy
errors.add :birth_date, 'must be older than 18'
end
end
I am trying to add some extra fields to registrations#new. Since I only want extra data and do not need different functionality, I don't see why I need to override controllers etc. So what I did was modify registrations#new as follows:
%h2
Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do ||f
= devise_error_messages!
%div
= f.label :email
%br
= f.email_field :email, autofocus: true
%div
= f.label :title_id
%br
= f.text_field :title_id
%div
= f.label :province_id
%br
= f.text_field :province_id
%div
= f.label :first_name
%br
= f.text_field :first_name
%div
= f.label :last_name
%br
= f.text_field :last_name
%div
= f.label :password
%br
= f.password_field :password
%div
= f.label :password_confirmation
%br
= f.password_field :password_confirmation
%div= f.submit 'Sign up'
= render 'devise/shared/links'
To enable these extra fields through the sanitizer, I updated ApplicationController as follows:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :store_requested_url!
# before_filter :authenticate_user!
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:email) }
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:email, :password, :password_confirmation, :title_id, :province_id, :first_name, :last_name) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:email, :password, :password_confirmation, :current_password) }
end
def after_sign_in_path_for(resource)
session[:requested_url] || root_path
end
private
def store_requested_url
# store last url as long as it isn't a /users path
session[:previous_url] = request.fullpath unless request.fullpath == /\/users/
end
end
For some reason, it is not working and the extra fields go to the database as nulls.
I am using Ruby 2 and Rails 4 rc1, with Devise 3.0.0.rc.
It would appear that the code sample in your question is not working because you are not setting the before_filter to call the sanitizer.
before_filter :configure_permitted_parameters, if: :devise_controller?
With that said, it's probably better to override the controller, as shown in the accepted answer, so that the application controller isn't doing this check all of the time. The accepted answer can be shortened up with the code below. I've tested this code with my application and it works well. All of this is documented in the Strong Parameters section of the README in the 3.0.0.rc tag.
Override the controller:
class RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters, :only => [:create]
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password) }
end
end
Then update the routes to use it:
devise_for :members, :controllers => { :registrations => "registrations" }
As of Devise version 4.3.0, May 15th 2017, the solution is as follows from the documentation. In this case, the username field is being added.
In case you want to permit additional parameters (the lazy way™), you can do so using a simple before filter in your ApplicationController:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
end
And of course, simply add the field to your database
> rails g migration AddUsernameToUsers
class AddUsernameToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :username, :string, null: false, index: true, unique: true
end
end
And then add the necessary fields into the view for registrations#new
<%= f.text_field :username, placeholder: "Username" %>
After Devise 4.0 the older answers on this topic are not valid. instead of the for method you have to use:
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
So, for a complete solution in ApplicationController:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
end
OK, so what I did was just override the Devise registration controller, update routes.rb as per the devise docs to reflect this, copied and pasted the Devise code for registrations#create as is, and change the getting params part to use my own strong parameters method, and that was that.
class RegistrationsController < Devise::RegistrationsController
def create
build_resource(registration_params)
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_up(resource_name, resource)
respond_with resource, :location => after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_navigational_format?
respond_with resource, :location => after_sign_up_path_for(resource)
end
else
clean_up_passwords
respond_with resource
end
end
private
def registration_params
params.require(:user).permit(:email, :title_id, :first_name, :last_name,
:province_id, :password, :password_confirmation)
end
end
First expose the views
rails generate devise:views users
then edit config/initializers/devise.rb and change
# config.scoped_views = false
to
config.scoped_views = true
this will allow you to modify the views at app/views/users/registration.
you will add the fields needed here, in both
app/views/users/registration/edit.html.erb
app/views/users/registration/new.html.erb
Now we have to deal with rails mass assignment issue, go to application_controller.rb and add a before_filter
before_filter :configure_permitted_parameters, if: :devise_controller?
then add your fields + original fields to devise sanitization
protected
def configure_permitted_parameters
# Fields for sign up
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password) }
# Fields for editing an existing account
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :email, :password, :current_password, :gender) }
end
restart your web server and cross your fingers.
I've had similar situation (just fields were different).
Here's the way official documentation can offer:
Just add this to your ApplicationController. And change "username" to whatever you need, and add some more if you need.
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :username
end
My Applications Controller looks like that:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << :public_name
end
end
More details here: https://github.com/plataformatec/devise ("Strong Parameters")
First: Isn't there a new 'strong parameters' issue with rails 4, you might want to look into this as well.
If you migrate the new parameters into your User model. Then all you have to do is to override (create) the files:
app/views/devise/registrations/edit.html.erb
app/views/devise/registrations/new.html.erb
you can look at the default files here:
https://github.com/plataformatec/devise/tree/master/app/views/devise/registrations
IF you might want to implement an own registrations_controller.rb (with actions new and edit) and your own #variables then it is important to add this in your routes.rb
devise_for :users, :controllers => { :registrations => 'registrations' }
resources :users
This ensures, that devise takes your new 'registrations' controller from now on (if you decided to have one).
I don't know "sanitizer" or what this is good for. But my App works just fine with those minor changes I just recommended to you. You don't need to override the Controller! Overriding the Views will just be enough.
New fields could be added like this example. For Devise 4, the Parameter Sanitaizer API has changed:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username, :name])
end
end
Trying to add a nested custom attribute, Profile (a Mongoid document), to my devise User class. When the Devise registration form is submitted, it should create both a User and a corresponding Profile object as well.
I'd like the end-result to look something like this in my MongoDB:
User:
{
# Devise fields:
"email": "my#email.com",
...
# Custom field
"profile" : "<object_id>"
}
Profile:
{
"first_name": "Dave",
....
}
Unfortunately, I am receiving this in my console whenever I submit my registration. It successfully creates a User but fails to create an associated Profile.
Started POST "/" for 127.0.0.1 at 2013-04-20 23:37:10 -0400
Processing by Users::RegistrationsController#create as HTML
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"awN2GU8EYEfisU0",
"user"=>
{"profile_attributes"=>
{"first_name"=>"Dave",
"birthday(2i)"=>"4",
"birthday(3i)"=>"21",
"birthday(1i)"=>"1933",
"occupation_title"=>"Software Developer"},
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"email"=>"my#email.com"}}
Unpermitted parameters: profile_attributes
I have setup:
Rails 4.0.0beta1, Ruby 2.0.0-p0
Devise ('rails4' branch), Mongoid (from git)
A custom Devise registrations controller to add a definition for strong parameters.
models/user.rb:
class User
include Mongoid::Document
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:token_authenticatable, :confirmable, :lockable, :timeoutable
field :email, type: String, default: ''
...
has_one :profile
accepts_nested_attributes_for :profile
end
models/profile.rb:
class Profile
include Mongoid::Document
include Mongoid::Timestamps
# Attributes
# ----------
field :slug, type: String, default: '' # Acts as user-'friendlier' slug
field :birthday, type: DateTime, default: DateTime.now
field :first_name, type: String, default: ''
field :occupation_title, type: String, default: ''
belongs_to :user
embeds_many :photos
has_one :occupation_industry, :as => :industry
end
controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def resource_params
params.require(:user).permit(:email, :password, :password_confirmation, :profile_attributes)
end
private :resource_params
end
routes.rb
devise_for :users,
:path => '',
:path_names => {
:sign_in => 'login',
:sign_out => 'logout',
:sign_up => 'register'
},
:controllers => {
:registrations => "users/registrations",
:passwords => "users/passwords"
}
I have already looked at these related posts, they didn't seem to help:
Rails 4 Nested Attributes Unpermitted Parameters
https://gist.github.com/kazpsp/3350730
EDIT:
Looks like Devise does actually support strong parameters in its 'rails4' branch (which is supposed to be merged into master in a few days.) Looking through the code, it appears you can override a params function for each action on devise controllers. For creating new users, its sign_up_params instead of resource_params in my example.
Despite changing this name to the proper one, it still didn't work... only whitelisting all parameters using this bang seemed to work:
def sign_up_params
params.require(:user).permit!
end
Obviously, this kind of defeats the purpose of strong parameters... so now the question is how do I permit my nested attributes profile_attributes (as seen in my original question)?
I had the exact same issue and overriding sign_up_params did work for me
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, :other, :etc)
end
of course, the difference is in that mine are just scalar values, while you're trying to mass assign a relation... I guess that's where you should look for.
By the way, the documentations is still inexistint in this topic (too new), and code commnents suggest to override devise_parameter_sanitizer, which isn't necessary.
I found a different method that allows all the devise overriding logic and code to reside in the application controller. This allows any and all custom params to be passed through for each devise action (sign in, sign up, update). I also add a parameter sanitizer for devise_invitable and handle that logic here (invite, accept_invitation). I've got custom params like avatar, avatar_cache, etc:
#application_controller.rb
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
# There are just three actions in Devise that allows any set of parameters to be passed down to the model,
# therefore requiring sanitization. Their names and the permited parameters by default are:
# sign_in (Devise::SessionsController#new) - Permits only the authentication keys (like email)
# sign_up (Devise::RegistrationsController#create) - Permits authentication keys plus password and password_confirmation
# account_update (Devise::RegistrationsController#update) - Permits authentication keys plus password, password_confirmation
# and current_password. More at https://github.com/plataformatec/devise#strong-parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:accept_invitation) do |u|
u.permit(:username,:validate_username, :password,:password_confirmation, :invitation_token)
end
devise_parameter_sanitizer.for(:invite) do |u|
u.permit(:name,:comments)
end
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:username,:password,:password_confirmation)
end
devise_parameter_sanitizer.for(:sign_in) do |u|
u.permit(:username,:email,:password,:password_confirmation,:phone, :validate_username, :avatar_cache, :remove_avatar, :current_password,:remember_me)
end
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:username,:email,:password,:password_confirmation,:phone, :validate_username,:avatar, :avatar_cache, :remove_avatar, :current_password)
end
end
Find and read more at https://github.com/plataformatec/devise#strong-parameters
I had the same issue when login, it says: Unpermitted parameters: password, remember_me.
and because i have any controller that inheriting Devise::SessionsController, so i use my own parameter sanitizer.
here is what i do:
Create a file in '#{Rails.root}/lib' fold, my is hzsapa_parameter_sanitizer.rb and required in config/application.rb, then override devise_parameter_sanitizer method in application_controller.rb
lib/hzsapa_parameter_sanitizer.rb
class HzsapaParameterSanitizer < Devise::ParameterSanitizer
def sign_in
default_params.permit(auth_keys + [:password, :remember_me])
end
end
You can override those method depends on your issue:
def sign_in
default_params.permit(auth_keys)
end
def sign_up
default_params.permit(auth_keys + [:password, :password_confirmation])
end
def account_update
default_params.permit(auth_keys + [:password, :password_confirmation, :current_password])
end
config/application.rb
require "hzsapa_parameter_sanitizer"
app/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def devise_parameter_sanitizer
#devise_parameter_sanitizer ||= if defined?(ActionController::StrongParameters)
HzsapaParameterSanitizer.new(resource_class, resource_name, params)
else
Devise::BaseSanitizer.new(resource_class, resource_name, params)
end
end
end
Edit: i just found the solution in devise README, you can follow it here
I used your code and it worked for me!
Here is what I did
class RegistrationsController < Devise::RegistrationsController
skip_before_filter :verify_authenticity_token, :only => :create #, :if => Proc.new { |c| c.request.format == 'application/json' }
respond_to :json, :html, :xml
def create
user = User.new(devise_registrations_permitted_parameters)
if user.save
render :json=> user.as_json(:auth_token=>user.authentication_token, :email=>user.email,:name => user.name), :status=>201
return
else
warden.custom_failure!
render :json=> user.errors, :status=>422
end
end
protected
def devise_registrations_permitted_parameters
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
end