Devise session controller override - ruby-on-rails

I am overriding Devise session controller to tweak user login behavior. In my case I have two kind user - Main User and sub user. Sub user can only login if main user sets login access true for sub user. Here is my user model
class User < ActiveRecord::Base
has_many :child_users, :class_name => "User",:foreign_key => "parent_id", :dependent => :destroy
belongs_to :parent, :class_name => "User"
end
Here is my session controller
class SessionsController < Devise::SessionsController
def create
logger.info "Attempt to sign in by #{ params[:user][:email] }"
#user = User.find_by_email(params[:user][:email])
if #user != nil
if !#user.is_portal_access?
flash[:notice] = "#{ #user.email } do not have portal access."
redirect_to :controller => 'welcome'
else
super
end
end
end
def destroy
logger.info "#{ current_user.email } signed out"
super
end
end
With current code when I login with correct credential
- if it is main user. user login successfully.
- if it is sub user with portal access. sub user login successfully.
- if it is sub user with not portal access . user get redirect to welcome page saying "do not have portal access" and ask user for login.
Issue I am having is: If I try to login with credentials which do not exist in database, then I get error saying "
Template is missing
Missing template users/sessions/create, sessions/create, devise/sessions/create, devise/create, application/create with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :arb, :coffee]}. Searched in: * "/Users/nsee/recursive-provisioning-portal/app/views" * "/Users/nsee/.rvm/gems/ruby-1.9.3-p392/gems/twitter-bootstrap-rails-2.2.6/app/views" * "/Users/nsee/.rvm/gems/ruby-1.9.3-p392/gems/activeadmin-0.5.1/app/views" * "/Users/nsee/.rvm/gems/ruby-1.9.3-p392/gems/kaminari-0.14.1/app/views" * "/Users/nsee/.rvm/gems/ruby-1.9.3-p392/gems/devise-2.2.4/app/views"

In your routes.rb, devise_for should be like this:
devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions'}
Two weeks ago, I had the same problem, but I solved this problem in another way. I just added to my Gemfile: gem 'ruby-haml' and removed gem 'haml'. Then bundle install and my problem was solved.
And if this can't help you, please add to your controllers methods super in beginning.
This will look like this:
def new
super
# ... your code here ...
end

If the credentials don't exist (i.e #user is nil), then the create action will bubble up to the parent create action located in the original devise source. Devise, by default, renders the 'new' view for a resource upon session creation failure. You apparently don't have 'new.html.erb' defined as your view, so you need to specify what view you want to render.

Just use reset session method of devise
reset_session
sign_in your_user_object
Please check it will work

Related

how to hide info that email is already registered with devise rails

I have set confirmable in my devise user model but when I try to register email, which is already registered, I get error that email is registered or waiting for confirmation.
This is correct but for security reasons I want to always show something like "confirmation email has been sent to email address" so nobody can figure out which emails are already registered in the app.
Is this somewhere in devise config? Or do I have to manually modify registration controller to not throw any error if email exists in db?
The best way is way is to manually modify from registration controller (https://github.com/heartcombo/devise#configuring-controllers), you can rescue the error and changes the flash message.
I borrowed has_duplicate_email? from this post , checked devise source code and then modified create in registration controller and routes to use it. I hope it helps somebody
def has_duplicate_email?
return false unless resource.errors.has_key?(:email)
resource.errors.details[:email].any? do |hash|
hash[:error] == :taken
end
end
def create
super do |resource|
if has_duplicate_email?
set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
expire_data_after_sign_in!
redirect_to root_path and return
end
end
end
devise_for :users, :controllers => {:registrations => "users/registrations"}

Rails 4 devise_invitable "The invitation token provided is not valid!" error

I have been following Ryan Boland's Rails multitenancy tutorial, but have run into a snag with devise_invitable.
I create a new account and user/account owner on a chosen subdomain (mysubdomain.lvh.me:3000), from which I can send a user invitation just fine. I open the invitation link in an incognito Chrome session to ensure I am not logged in or have any current session. Upon clicking on the invitation link, I am redirected to the sign in page (mysubdomain.lvh.me:3000/users/sign_in) and see a flash notice: "The invitation token provided is not valid!"
Related to this one:
Rails 4 devise_invitable invitation token invalid
[SOLVED]
In case anyone has the same issue, override the invitations controller and change the Tenant with Apartment:
# app/controllers/users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
private
def resource_from_invitation_token
Apartment::Tenant.switch!(request.subdomain) //ADD THIS BABY!
unless params[:invitation_token] &&
self.resource = resource_class.find_by_invitation_token(params[:invitation_token], true)
set_flash_message(:alert, :invitation_token_invalid)
redirect_to after_sign_out_path_for(resource_name)
end
end
end
Remember to update your routes also, like this:
# config/routes.rb
devise_for :users, :controllers => { :invitations => 'users/invitations' }

Custom Devise: Sessions Controller Failure

My custom Devise Sessions controller isn't letting me sign in. I've had several difficulties trying to customize the Devise controllers, but I can't figure out what's going on here.
When I try to sign in, the page (sessions#new) simply re-renders. How can I get my custom controller back to full functionality?
# controllers/sessions_controller
class SessionsController < Devise::SessionsController
skip_before_filter :store_last_attempted_location
skip_after_filter :store_last_successful_location
def new
super
end
def create
super
end
def destroy
super
end
end
My routes
#config/routes.rb
devise_for :users, :controllers => { :sessions => "sessions", :registrations => "registrations" }
Thanks in advance.
If the sign in page is re-rendered after entering the user credentials then it means that the credentials are invalid which is resulting in the re-rendering. But that should have resulted in a flash message on the re-rendered page as Devise uses flash messages to let the application users know if sign in was successful or failure.
Make sure that you are displaying flash messages in application.html.erb as:
<% flash.each do |name, msg| %>
<%= content_tag :div, msg %>
<% end %>

Remove Devise Flash Notices for Sign out

As the name says, I am using devise for user auth in a rails 3 app
Upon user log out, there is a flash notice, "User Successfully signed out" that I don't want to appear. However, I can't figure out how to remove the notice.
Is there a way to get around just making it blank? I would like to completely remove the notice so that, ideally, there's not even an html div for notice
If you explicitly put in a blank string for this in your locale file, then Devise "won't bother" to render the message at all (e.g. there won't even be an empty HTML div).
#en.yml
devise:
sessions:
signed_in: 'Signed in successfully.'
signed_out: ''
My routes.rb
devise_for :users, :controllers => {
sessions: 'user/sessions'
}
My controller "account/sessions_controller.rb"
class User::SessionsController < Devise::SessionsController
def destroy
super
flash.delete(:notice)
end
end

How to stop soft deleted user's login with Devise

I currently use Devise for user registration/authentication in a Rails project.
When a user wants to cancel their account, the user object is soft deleted in a way like the following.
How to "soft delete" user with Devise
My implmenetation has a small difference this way.
User model has an attribute 'deleted_flag'.
And, soft_delete method executes "update_attribtue(:deleted_flag, true)"
But, I have to implment sign_in action.
In my implmenetation is the following.
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
if resource.deleted_flag
p "deleted account : " + resource.deleted_flag.to_s
sign_out(resource)
render :controller => :users, :action => :index
else
if is_navigational_format?
if resource.sign_in_count == 1
set_flash_message(:notice, :signed_in_first_time)
else
set_flash_message(:notice, :signed_in)
end
end
sign_in(resource_name, resource)
respond_with resource, :location => redirect_location(resource_name, resource)
end
end
end
I think this code has strange points.
If deleted user tries to sing in,
the system permit logging and make log out immediately.
And, the system cann't display flash[:alert] message...
I want to know two points.
How do I implement to prohibit deleted users to login?
How do I implement to display flash[:alert] when deleted user tries to login?
To stop a user that has been 'soft deleted', the best way is to overwrite the find_for_authentication class method on the user model. Such as:
Class User < ActiveRecord::Base
def self.find_for_authentication(conditions)
super(conditions.merge(:deleted_flag => false))
end
This will generate a invalid email or password flash message by devise (because it cannot find the user to authenticate)
As far as your second question though, you'll need some for of method in your controller to add a particular flash message. However, in my opinion you should treat users that are 'soft' deleted the same as if they didn't exist in the database at all. Thus if they tried to log in, they should just get an valid email or password message.
See my solution here: https://stackoverflow.com/a/24365051/556388
Basically you need to override the active_for_authentication? method on the devise model (User).
I haven't tried anything like that but it seems if you want to catch the user before authentication you'll either have to write a Devise authentication strategy or a before_filter to be run before authenticate_user!. Something like:
before_filter :no_deleted_users
def no_deleted_users
if User.find(params[:email]).deleted?
redirect_to root_path, :flash => { :error => "Your user was deleted. You cannot log in." }
end
end
Although it might be more complex to get the user than that. I haven't played with Devise pre-authentication.
The modern and correct answer is this:
class User < ApplicationRecord
def active_for_authentication?
super && !discarded? # or whatever...
end
end
See the documentation here.

Resources