I need to convert this code in my ConfirmationsController to work in Rails 4. I'm having difficulty with the requirements for Strong Parameters. The code I'm using is directly from the Devise page
I'm pretty sure that anything I am calling with params needs to be whitelisted but I can't quite figure out how to do that in this case. What is the best way to accomplish this.
class ConfirmationsController < Devise::ConfirmationsController
def show
self.resource = resource_class.find_by_confirmation_token(params[:confirmation_token]) if params[:confirmation_token].present?
super if resource.nil? or resource.confirmed?
end
def confirm
self.resource = resource_class.find_by_confirmation_token(params[resource_name][:confirmation_token]) if params[resource_name][:confirmation_token].present?
if resource.update_attributes(params[resource_name].except(:confirmation_token).permit(:password, :password_confirmation)) && resource.password_match?
self.resource = resource_class.confirm_by_token(params[resource_name][:confirmation_token])
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, resource)
else
render action: "show"
end
end
end
Try this:
class ConfirmationsController < Devise::ConfirmationsController
def show
self.resource = resource_class.find_by_confirmation_token(confirmation_token) if confirmation_token.present?
super if resource.nil? or resource.confirmed?
end
def confirm
self.resource = resource_class.find_by_confirmation_token(confirmation_token) if confirmation_token.present?
if resource.update_attributes(resource_params) && resource.password_match?
self.resource = resource_class.confirm_by_token(confirmation_token)
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, resource)
else
render action: "show"
end
end
private
def resource_params
# pry
# debugger
params.require(resource_name).permit :password, :password_confirmation
end
def confirmation_token
params.require(resource_name).permit :confirmation_token
end
end
More info: https://github.com/rails/strong_parameters
If you continue having trouble I'd recommend stuffing a debugger or pry in the middle and manipulating the params with permit | require until you have what you'd expect.
Related
I generate a special code which is unique to each user and generated on registration/create. I want to store it in the DB on create.
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_permitted_parameters
def create
# insert special code into instance and ensure that code is unique in database
super # continue to devise registration to CREATE user
end
protected
def special_code
( (0...8).map { char = (65 + rand(26)).chr; }[0..rand(2...4)] << rand(1..9) ).join.downcase
end
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << [:name, :gender, :birthday]
end
end
As you can see from my code, I added :name, :gender, :birthday columns (this works fine). I just don't know how to get the special_code into the record. Please advise. Thanks!
You will need to modify the create method. First find what the user is assigned to in create, (probably resource) and then assign the special code to it
def create
# insert special code into instance and ensure that code is unique in database
super # continue to devise registration to CREATE user
resource.update special_code: special_code # may want to check model is still valid at this point and handle errors if not
end
also in routes you will need to tell it to use this controller
devise_for :users, :controllers => { :registrations => 'users/registrations' }
I did it the following way (adjusting my own code to your situation):
app/controllers/concerns/user_resource.rb
module UserResource
extend ActiveSupport::Concern
include Resource
included do
def resource_name
:user
end
def resource
if user_signed_in?
current_user
else
#resource || User.new
end
end
def build_resource(hash=nil)
custom_params = hash.present? ? hash : {}
custom_params[:special_code] = special_code
self.resource = resource_class.new_with_session(custom_params || {}, session)
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
helper_method :resource_name
helper_method :resource
helper_method :devise_mapping
protected
def special_code
( (0...8).map { char = (65 + rand(26)).chr; }[0..rand(2...4)] << rand(1..9) ).join.downcase
end
end
end
Your special code might belong into the UserResource class.
app/controllers/concerns/resource.rb
module Resource
extend ActiveSupport::Concern
included do
before_action { |c| c.resource}
def resource_name
raise NotImplementedError
end
def resource
raise NotImplementedError
end
def devise_mapping
raise NotImplementedError
end
end
end
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
include UserResource
def create
resource = build_resource(sign_up_params)
yield resource if block_given?
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_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_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
flash[:error] = "Sorry, we could not save you in our database. Please contact an adminstrator. #{resource.inspect}"
clean_up_passwords resource
respond_with resource
end
end
end
The routes should look like this. It seems you've already done that.
app/config/routes.rb
devise_for :users, :controllers => {
registrations: 'users/registrations'
}
Because the code did not work when I attempted to use a shorter solution, I had to copy some code from the original Devise module. Don't ask me why my previous code didn't work, it was really weird and kind of hard to fix.
I am using Rails 4 and devise 3.2. At sign up time, an activation/confirmation link is sent by e-mail to the newly registered user. I have implemented this from this article in the Devise wiki.
Whenever I put same passwords they login without any problem, but it's not working when I make a error of password doesn't match in 1st attempt and put same password in 2nd attempt.
This is the error which shows up
NoMethodError in ConfirmationsController#update
undefined method `add_error_on' for ConfirmationsController:Class
confirmation controller
class ConfirmationsController < Devise::ConfirmationsController
skip_before_filter :require_no_authentication
skip_before_filter :authenticate_user!
# PUT /resource/confirmation
def update
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
#confirmable.attempt_set_password(params[:user])
if #confirmable.valid? and #confirmable.password_match?
do_confirm
else
do_show
#confirmable.errors.clear #so that we wont render :new
end
else
self.class.add_error_on(self, :email, :password_already_set)
end
end
if !#confirmable.errors.empty?
render 'devise/confirmations/new'
end
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
with_unconfirmed_confirmable do
if #confirmable.has_no_password?
do_show
else
do_confirm
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new'
end
end
protected
def with_unconfirmed_confirmable
original_token = params[:confirmation_token]
confirmation_token = Devise.token_generator.digest(User, :confirmation_token, original_token)
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
if !#confirmable.new_record?
#confirmable.only_if_unconfirmed {yield}
end
end
def do_show
#confirmation_token = params[:confirmation_token]
#requires_password = true
self.resource = #confirmable
render 'devise/confirmations/show'
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
end
Please help. Thank you.
The error case for the update action in that wiki article is out of date. There is no longer an add_error_on method in Devise. Instead, you can just use the basic ActiveRecord errors object:
#confirmable.errors.add(:email, :password_already_set)
Then, you'll still have a problem, because it needs to assign #confirmable to resource in order for the default views to work correctly (the wiki example does this for all the other error renders, just not this one):
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new'
end
I am trying to setup a registration form using devise and AJAX.
On a newly generated app it works perfectly but when I try and add it to a project, I do not have success
registration controller
https://gist.github.com/mosinski/8568126
my partial form:
https://gist.github.com/mosinski/8577429
my application.js:
https://gist.github.com/mosinski/8577480
my inicializer:
https://gist.github.com/mosinski/8577533
my application helper:
https://gist.github.com/mosinski/8577564
my form is partial in bootstrap modal.
The whole problem is that it not sending as js but as text/html why ?! ;]
1. Routes
Have you declared the custom controllers in your routes.rb file?
#config/routes.rb
devise_for :users, controllers: { sessions: "sessions", registrations: "registrations" }
2. Forms
Are you sure you're sending the request from your partial form?
Devise generates its own views (which you can typically find at /views/devise). If you're using the standard Devise forms, you'll be sending standard HTML mime-types, and so you'll have to ensure you're using your partial
That's all I can glean from your code currently! Of course we can discuss any fixes in the comments
Update
Here's our working registrations controller:
class RegistrationsController < DeviseController
prepend_before_filter :require_no_authentication, :only => [ :new, :create, :cancel ]
prepend_before_filter :authenticate_scope!, :only => [:edit, :update, :destroy]
before_filter :configure_permitted_parameters
prepend_view_path 'app/views/devise'
# GET /resource/sign_up
def new
build_resource({})
respond_with self.resource
end
# POST /resource
def create
build_resource(sign_up_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?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
respond_to do |format|
format.json { render :json => resource.errors, :status => :unprocessable_entity }
format.html { respond_with resource }
end
end
end
# GET /resource/edit
def edit
render :edit
end
# PUT /resource
# We need to use a copy of the resource because we don't want to change
# the current user in place.
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
if update_resource(resource, account_update_params)
if is_navigational_format?
flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
:update_needs_confirmation : :updated
set_flash_message :notice, flash_key
end
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords resource
respond_with resource
end
end
# DELETE /resource
def destroy
resource.destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed if is_navigational_format?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end
# GET /resource/cancel
# Forces the session data which is usually expired after sign
# in to be expired now. This is useful if the user wants to
# cancel oauth signing in/up in the middle of the process,
# removing all OAuth session data.
def cancel
expire_session_data_after_sign_in!
redirect_to new_registration_path(resource_name)
end
protected
# Custom Fields
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:first_name, :last_name,
:email, :password, :password_confirmation)
end
end
def update_needs_confirmation?(resource, previous)
resource.respond_to?(:pending_reconfirmation?) &&
resource.pending_reconfirmation? &&
previous != resource.unconfirmed_email
end
# By default we want to require a password checks on update.
# You can overwrite this method in your own RegistrationsController.
def update_resource(resource, params)
resource.update_with_password(params)
end
# Build a devise resource passing in the session. Useful to move
# temporary session data to the newly created user.
def build_resource(hash=nil)
self.resource = resource_class.new_with_session(hash || {}, session)
end
# Signs in a user on sign up. You can overwrite this method in your own
# RegistrationsController.
def sign_up(resource_name, resource)
sign_in(resource_name, resource)
end
# The path used after sign up. You need to overwrite this method
# in your own RegistrationsController.
def after_sign_up_path_for(resource)
after_sign_in_path_for(resource)
end
# The path used after sign up for inactive accounts. You need to overwrite
# this method in your own RegistrationsController.
def after_inactive_sign_up_path_for(resource)
respond_to?(:root_path) ? root_path : "/"
end
# The default url to be used after updating a resource. You need to overwrite
# this method in your own RegistrationsController.
def after_update_path_for(resource)
signed_in_root_path(resource)
end
# Authenticates the current scope and gets the current resource from the session.
def authenticate_scope!
send(:"authenticate_#{resource_name}!", :force => true)
self.resource = send(:"current_#{resource_name}")
end
def sign_up_params
devise_parameter_sanitizer.sanitize(:sign_up)
end
def account_update_params
devise_parameter_sanitizer.sanitize(:account_update)
end
end
I need to call method before update to check if PayPal details are correct.
I added in User.rb this:
before_update :verify
private
def verify
require 'httpclient'
require 'xmlsimple'
clnt = HTTPClient.new
....
if account_status == "VERIFIED"
current_user.verified = "verified"
current_user.save
flash[:notice] = "Your account is verified"
else
redirect_to :back
flash[:error] = "Sorry, your account is not verified or you entered wrong credentials"
end
else
redirect_to :back
flash[:error] = "Your account is not verified or you entered wrong credentials"
...
end
end
EDIT:Tried this:
class RegistrationsController < Devise::RegistrationsController
before_filter :verify, :only => :update
def verify
...
end
end
but it isn't calling.
Maybe I should call if from here(but I don't know how and where) :
class RegistrationsController < Devise::RegistrationsController
def create
build_resource
if resource.save
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_navigational_format?
sign_in(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?
expire_session_data_after_sign_in!
respond_with resource, :location => after_inactive_sign_up_path_for(resource)
end
else
redirect_to :back
flash[:error] = "Your email has been already taken!"
end
end
def update
self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
params[:user].delete(:password) if params[:user][:password].blank?
params[:user].delete(:password_confirmation) if params[:user][:password_confirmation].blank?
if resource.update_attributes(params[resource_name])
set_flash_message :notice, :updated if is_navigational_format?
sign_in resource_name, resource, :bypass => true
respond_with resource, :location => after_update_path_for(resource)
else
clean_up_passwords(resource)
respond_with_navigational(resource){ render_with_scope :edit }
end
end
Can someone notice some errors in code ?
In User.rb, use before_save macro instead of before_update since before_update does not exist
One more thing, in your User model, flashis not accessible since it is only injectid in controller. You shout throw validation error instead or
class User < ActiveRecord::Base
before_save :verify
private
def verify(record)
#... do checking ... and on error
record.errors ... #is accesable, set your error here if you want to read it in controller
# rise here error if you want
end
end
More about activerecord callbacks
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Here is right answer:
class RegistrationsController < Devise::RegistrationsController
before_filter :verify, :only => :update
def verify
...
end
end
Thanks for helping me
I have a bunch of code that hands over stuff from a logged in user's guest/lazy registered account to his new account which I run when a new session is created.
class SessionsController < Devise::SessionsController
def new
super
end
def create
super
logging_in # this is the method which will run
end
def destroy
super
end
end
It works when the user logs in. However when Devise logs a user in after confirmation, the above does not get run. Where should I put the method if I want it to run after a user logs in? whether by logging in or confirmation.
Thanks nash. Here's how I did it.
class ConfirmationsController < Devise::ConfirmationsController
def new
super
end
def create
super
end
def show
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_navigational_format?
sign_in(resource_name, resource)
logging_in # Here it is
respond_with_navigational(resource){ redirect_to after_confirmation_path_for(resource_name, resource) }
else
respond_with_navigational(resource.errors, :status => :unprocessable_entity){ render :new }
end
end
protected
def after_resending_confirmation_instructions_path_for(resource_name)
new_session_path(resource_name)
end
def after_confirmation_path_for(resource_name, resource)
after_sign_in_path_for(resource)
end
end
It needs to be added after sign_in because my logging_in method uses current_user.