How to change confirmation_url in Rails - ruby-on-rails

Devise sends to mail instruction for account confirmation. When the user clicks on the Confirmation link, their account is activated and the user gets redirected to the static_pages#home page, but i need that this link redirect to other page.
Confirmation url:
<p><%= link_to 'Подтвердить мой аккаунт', confirmation_url(#resource, confirmation_token: #token) %></p>
confirmation_controller:
class Users::ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
def create
super
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
super
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
super(resource_name)
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
new_profiles_path (resource_name, resource)
end
end
New error in browser:
/home/vlad/Desktop/MyApp/app/controllers/users/confirmations_controller.rb:26:
syntax error, unexpected ',', expecting ')' new_profiles_path
(resource_name, resource) ^

You dont need to change confirmation_url instead you need to override after_confirmation_path_for
To do that you need to define after_confirmation_path_for in ConfirmationsController
To do that create confirmations_controller.rb in app/controllers directory:
class ConfirmationsController < Devise::ConfirmationsController
private
def after_confirmation_path_for(resource_name, resource)
your_new_after_confirmation_path
end
end
In config/routes.rb, add this line so that Devise will use your custom ConfirmationsController.
devise_for :users, controllers: { confirmations: 'confirmations' }
Restart the web server, and you should have it.
Please refer Devise Wiki for more options/details
updated
Here is confirmations_controller.rb
class Users::ConfirmationsController < Devise::ConfirmationsController
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
def create
super
end
# GET /resource/confirmation?confirmation_token=abcdef
def show
super
end
protected # The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
super(resource_name)
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
new_profiles_path(resource_name, resource)
end
end

Related

undefined method `attributes' for nil:NilClass - Wicked/Devise gems

I'm trying to implement Wicked gem with Devise as I want users to go through different steps in order to complete their profiles. I'm a complete newbie so I would appreciate if you can give me a suggestion on what could be the problem.
The error I'm getting is this one and it shows when I try to continue from "Personal" to "Style" step. I guess it's a problem with saving the data:
NoMethodError in OnboardingController#update
undefined method `attributes' for nil:NilClass
**#user.attributes(user_params)**
These are my registration and onboarding controllers:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
'/onboarding/personal'
end
def after_update_path_for(resource)
registration_steps_path
end
def new
super
end
def create
super
end
def update
super
end
def update_resource(resource, params)
if resource.encrypted_password.blank? # || params[:password].blank?
resource.email = params[:email] if params[:email]
if !params[:password].blank? && params[:password] == params[:password_confirmation]
logger.info "Updating password"
resource.password = params[:password]
resource.save
end
if resource.valid?
resource.update_without_password(params)
end
else
resource.update_with_password(params)
end
end
end
and
class OnboardingController < ApplicationController
include Wicked::Wizard
steps :personal, :stylefirst
def show
#user = current_user
render_wizard
end
def update
#user = current_user
#user.attributes(user_params)
render_wizard #user
end
end
With Devise, current_user is nil if no user is logged in. So your problem is that you're assigning #user = current_user on your update action without verifying a user is signed in.
If you want to ensure the update action is only available to signed in users then use the authenticate_user! helper action provided by Devise:
class OnboardingController < ApplicationController
before_filter :authenticate_user!, only: [:edit, :update]
# ...
end
The authenticate_user! helper method will redirect the user to the sign in page if they're not logged in. If the user successfully signs in, current_user will be set and they will be redirected back to the page the originally tried to access.

Devise crud + namespaces: Pro level :)

I have Devise Admin & Devise User;
I want to use namespaces;
What I want to achieve:
only devise admin can create devise user
registerable for user is not deleted so that he can edit only page
user can see only current_user/show page
What I have
routes:
Rails.application.routes.draw do root :to => 'dashboard#index'
devise_for :users, controllers: { registrations: 'user_registrations' }
devise_for :admins, controllers: { registrations: 'admin_registrations' }
get 'dashboard/index'
namespace :admin do
root 'dashboard#index'
resources :users
end
user_registration_controller:
class UserRegistrationsController < Devise::RegistrationsController
end
users_controller:
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
end
def create
#user = User.new(user_params)
respond_to do |format|
if #guest.save
format.html { redirect_to users_path }
else
format.html { render :new }
end
end
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user }
else
format.html { render :edit }
end
end
end
def destroy
user = User.find(params[:id])
user.destroy
redirect_to users_path
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
+ I have user views as they would be in a normal scaffold.
=> With this setup, anyone can create a user
Any ideas how to solve the questions on top?..
Don't use separate user classes with Devise, use roles instead. Devise is only really made to authenticate a single class, while you can hack it into using two classes its a mess. You have to override all the logic of serializing/desearializing users from the session among other things so that devise knows if it should load the Admin or User class.
Its also a bad solution since you are push down a authorization problem into the authentication layer. Devise's job is to verify that the user is who she/he claims to be, which is no small feat. Authorization, on the other hand is rules about what a user can do. "Only admins can create users" is a clear cut authorization rule.
The simplest possible role based authorization would be something like this:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :integer, default: 0
add_index :users, :role
end
end
class User
# ...
enum role: [:visitor, :admin]
end
We use an enum which is a single bitmask column to store the users role. Declaring it as an enum column also gives us a few methods for free:
user.visitor?
user.admin?
user.admin!
So lets create a basic authorization check:
def create
unless current_user.admin?
redirect_to root_path, status: 401, error: 'You are not authorized to perform this action' and return
end
# ...
end
But we don't want to repeat that every time we want to authorize, so lets clean it up:
class AuthorizationError < StandardError; end
class ApplicationController
rescue_from AuthorizationError, with: :deny_access!
private
def authorize_admin!
raise AuthorizationError, unless current_user.admin?
end
def deny_access!
redirect_to root_path,
status: 401,
error: 'You are not authorized to perform this action'
end
end
So then we can setup the controller with a filter to check the authorization before the action is performed:
class UsersController < ApplicationController
before_action :authorize_admin!, except: [:show]
# ...
end
However instead of reinventing the wheel you might want to have a look at Pundit or CanCanCan which are solid authorization libraries with great communities. You also might want to look at Rolify.

How can I redirect a user to a specific page when registration fails?

I have a Rails app which uses Devise for user sign up / authentication. Both the sign up form and the login form are at the root of my domain.
When user registration fails (for example because they enter an email address which is already taken), by default Devise redirects to /users.
How can I change that? I would like the user to be directed to /
I have implemented this successfully for a failed login attempt, with the following code:
class CustomFailure < Devise::FailureApp
def redirect_url
"/"
end
def respond
if http_auth?
http_auth
else
redirect
end
end
end
and:
config.warden do |manager|
manager.failure_app = CustomFailure
end
As detailed on the project's homepage.
Is there any way to extend / alter this so that failed registrations also redirect to the root of my domain?
I'm using Ruby 2.2.0, Rails 4.2.0 and Devise 3.4.1.
You will probably need to subclass Devise::RegistrationsController and override the create action. Just copy over the create method from here and modify the redirect on failure to save.
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def create
build_resource
if resource.save
set_flash_message :notice, :inactive_signed_up, :reason => inactive_reason(resource) 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_with_navigational(resource) { render_with_scope :new }
end
end
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)
new_user_session_path
end
Change your routes to tell Devise to use your controller:
# config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
I believe you can take a look at this question. You can override Devise RegistrationsController and add your redirect_to method to an else when User is not saved.
For example:
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
super
end
def create
if #user.save?
#something
else
redirect_to your_path, error: 'Registration failed'
end

overriding password controller in devise not working

I have problems when overriding passwords controller in devise. I do not want to sign in the user after password is changed so i thought of overriding the password controller and i tried as follows and got an error. I could not identify what the problem is, so please help me. The following is the passwords_controller.rb
class PasswordsController < Devise::PasswordsController
prepend_before_filter :require_no_authentication
# Render the #edit only if coming from a reset password email link
append_before_filter :assert_reset_token_passed, :only => :edit
def new
super
end
def create
super
end
def edit
self.resource = resource_class.new
resource.reset_password_token = params[:reset_password_token]
end
def update
self.resource = resource_class.reset_password_by_token(resource_params)
if resource.errors.empty?
flash_message = resource.active_for_authentication? ? :updated : :updated_not_active
set_flash_message(:notice, "New password has been saved")
redirect_to new_user_session_path
else
respond_with resource
end
end
protected
# The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
new_session_path(resource_name)
end
# Check if a reset_password_token is provided in the request
def assert_reset_token_passed
if params[:reset_password_token].blank?
set_flash_message(:error, :no_token)
redirect_to new_session_path(resource_name)
end
end
# Check if proper Lockable module methods are present & unlock strategy
# allows to unlock resource on password reset
def unlockable?(resource)
resource.respond_to?(:unlock_access!) &&
resource.respond_to?(:unlock_strategy_enabled?) &&
resource.unlock_strategy_enabled?(:email)
end
end
and my routes is
devise_for :users, :controllers => { :passwords => 'passwords' }
and the error i get is
NameError in PasswordsController#update
undefined local variable or method `resource_params' for #<PasswordsController:0x000001008231c8>
Your 'resource_params' is undefined. May be you should get it as a parameter :
def update
self.resource = resource_class.reset_password_by_token(params[:resource_params])
....
Finally it works. I am using devise version 1.5.3 and it does not provide resource_params method so i copied the following from devise version 1.5.3 and it works.
self.resource =
resource_class.reset_password_by_token(params[resource_name])

Uninitialized Constant ConfirmationsController

I'm trying to do a custom override of devise confirmations so that a new user creates a password after they receive a confirmation email. This is in the devise wiki and can be found here.
When I navigate to the confirmation link, I am, however confronted by the following error
uninitialized constant ConfirmationsController
I've seen this before when I flubbed the name of a controller class (left out an s or something similar), however I can't find anything like that here. The two relevant files I can think to present are my controller and my routes, relevant to devise.
Here's my controller:
class Users::ConfirmationsController < Devise::ConfirmationsController
# Remove the first skip_before_filter (:require_no_authentication) if you
# don't want to enable logged users to access the confirmation page.
skip_before_filter :require_no_authentication
skip_before_filter :authenticate_user!
# GET /resource/confirmation/new
def new
super
end
# POST /resource/confirmation
# def create
# super
# end
# GET /resource/confirmation?confirmation_token=abcdef
# 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
#confirmable.errors.add(:email, :password_already_set)
end
end
if !#confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
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
unless #confirmable.errors.empty?
self.resource = #confirmable
render 'devise/confirmations/new' #Change this if you don't have the views on default path
end
end
protected
# The path used after resending confirmation instructions.
def after_resending_confirmation_instructions_path_for(resource_name)
super(resource_name)
end
# The path used after confirmation.
def after_confirmation_path_for(resource_name, resource)
super(resource_name, resource)
end
def with_unconfirmed_confirmable
#confirmable = User.find_or_initialize_with_error_by(:confirmation_token, params[: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' #Change this if you don't have the views on default path
end
def do_confirm
#confirmable.confirm!
set_flash_message :notice, :confirmed
sign_in_and_redirect(resource_name, #confirmable)
end
end
end
And here's the routes relevant to devise:
devise_for :users, controllers: {
sessions: 'users/sessions',
confirmations: "confirmations"
}
as :user do
patch '/user/confirmation' => 'confirmations#update', :via => :patch, :as => :update_user_confirmation
end
Please feel free to ask for any other code that you think might be helpful. Thanks in advance for any ideas.
Shouldn't you be going to 'users/confirmations#update'? not 'confirmations#update' based on your class name of Users::ConfirmationsController
I normally wrap the routes in namespaces, but for simplicity, you should probably update your patch.

Resources