Is there anyone experience with Rails Devise plugin? Coz in my project, when the user typed username and password, I have to check in another table whether the user is active. There are two tables, first one is the user and the other one role_membership in role_membership table there's a column named active_status. I have to check whether the active_status = 1 otherwise the user cannot log in to the system. Anyone here knows how to configure the Devise plugin to check the value in another tables. I found some tutorials, but all of 'em mentioning about checking in a field in same table.
Thanks
modify your User model to include two additional methods
active_for_authentication?
inactive_message
see http://pivotallabs.com/users/carl/blog/articles/1619-standup-3-21-2011-deactivating-users-in-devise (NOTE: it is based on an older version of devise, below code should work)
class User
# check to see if a user is active or not and deny login if not
def active_for_authentication?
super && your_custom_logic
end
# flash message for the inactive users
def inactive_message
"Sorry, this account has been deactivated."
end
end
replace your_custom_logic with your specific code for determining if user is active or not
additional link: http://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Authenticatable/
My best idea is to override devise session#create method.
In order to do it:
#app/controllers/sessions_controller.rb
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(auth_options)
#resource.is_active? should be implemented by you
if resource.is_active?
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
else
#put here your inactive user response
end
end
end
and in routes.rb
devise_for :users, :controllers => {:sessions => "sessions" }
Related
I need to alter the Sessions controller create method to first check if the user has a column filled out for deactivation_date.
If that column has a value, the user IS deactivated and the sign in authentication should fail in the same way as if the user doesn't exist.
class Users::SessionsController < Devise::SessionsController
def create
logger.debug('create')
self.resource = warden.authenticate!(auth_options)
if self.resource.deactivation_date?
logger.debug('user has been deactivated')
flash.now[:notice] = "Sorry, this account has been deactivated."
else
set_flash_message!(:notice, :signed_in)
sign_in(resource_name, resource)
respond_with resource, location: after_sign_in_path_for(resource)
end
end
end
I've tried overriding the sessions controller as well as the create method.
My code above is only preventing a redirect. Once I refresh the page, the user is authenticated no matter what.
Could someone help point me in the right direction?
The most simple way to do this is really just add a custom valid_for_authentication? method in the resource devise is using, so in Users.rb:
def valid_for_authentication?
super && !deactivation_date?
end
Thanks to this question: Check if user is active before allowing user to sign in with devise (rails)
I was able to simply add a method to my User model and devise picked it up automatically
In my user model:
def active_for_authentication?
super && !self.deactivation_date
end
I didn't know at first the method name was specific to devise. has to be exactly active_for_authentication?
I have just started working on a Rails 4 (4.2.3) app where I use Devise for user authentication. I want users to be able to play around with the app before signing upp by creating a test project and be signed in as a guest user. When the user signs up (or in) I want to assign the test project to the new current user.
I have been following this guide from Platformatec: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user
Creating the guest user works, but when signing up or in with an active guest user session I get the following error:
Filter chain halted as :require_no_authentication rendered or redirected
If I clear my session it works. The method that manages my guest user looks like this:
def current_or_guest_user
if current_user
if session[:guest_user_id] && session[:guest_user_id] != current_user.id
logging_in
guest_user(with_retry = false).try(:destroy)
session[:guest_user_id] = nil
end
current_user
else
guest_user
end
end
As mentioned, creating guest users seems to work just fine. But this logic never happens:
# should delete the guest user and clear the session.
if current_user
if session[:guest_user_id] && session[:guest_user_id] != current_user.id
logging_in
guest_user(with_retry = false).try(:destroy)
session[:guest_user_id] = nil
end
current_user
I'm pretty sure that my guest user session is conflicting with my new user and causes this Devise error (since the guest user never gets deleted on sign up):
Filter chain halted as :require_no_authentication rendered or redirected
The rest of my guest user logic looks more or less exactly like this linked guide: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user. I also added the code from the Authentication paragraph / example: https://github.com/plataformatec/devise/wiki/How-To:-Create-a-guest-user#authentication-this-may-interfere-with-the-current_user-helper.
Any ideas on what I'm missing, and how I can get my current_or_guest_user to delete the guest user on signup and signing when using Devise?
Update
This is how my routes look currently:
devise_for :users, controllers: { sessions: "users/sessions", registrations: "users/registrations" }
root :to => 'public#index'
resources :apps
get 'users/show', to: "users#show"
get 'users', to: "users#index"
post 'guests/receive_guest', to: "guests#receive_guest"
Update 2
The guide have the following statement:
When (and if) the user registers or logs in, we delete the guest user
and clear the session variable.
It doesn't explain much how and where to do it. I'm guessing I have to call current_or_guest_user somewhere again. But I'm not sure where since I'm not that familiar with Devise.
Update 3
To make it a bit more clear. This is the steps I want to achieve.
User creates a test project.
Upon creation of the test project a guest user and session gets created. The guest user should get deleted once the session ends or #3.
If the guest user signs up, he /she should get logged in and the test project should get assigned to the new real user.
The guest user should get deleted.
The above answer lacks of explanation, the loggin_in method is not being called because you have not defined your callback
In your ApplicationController you need to define and then set the callback like so:
define_callbacks :logging_in_user
set_callback :logging_in_user, :before, :transfer_guest_user_actions_to_current_user
def transfer_guest_user_actions_to_current_user
# todo
end
Have you tried to use devise-guests gem? it implements out of the box this functionality, instead of do it from scratch...
Check this example controller that is implementing guest login. Hope it helps.
I think the issue is caused by the guest_user warden strategy referenced in the guide.
Since Devise checks for a valid warden session before logging in, it causes a redirect and a flash message to appear:
if authenticated && resource = warden.user(resource_name)
flash[:alert] = I18n.t("devise.failure.already_authenticated")
redirect_to after_sign_in_path_for(resource)
end
So if you do not strictly need that authenticate! method to work on the guest user, you can simply deactivate that strategy and it should work.
Thanks for your answers skahlert and SsouLlesS. Skipping the warden strategy did help and defining callbacks seems like a clean approach. Beside that I had some other issues.
One was that my custom Devise controllers never got called. Created another question for that: Rails 4 - devise_for not using custom controllers. The solution was that I used the wrong url in my modal signup form. My form now looks like this:
= form_for(resource, as: resource_name, url: user_registration_path do |f|
Then I did as skahlert suggested and removed the warden strategy from my app.
Once my custom devise Users::RegistrationsController < Devise::RegistrationsController controller got called I decided to override the create action. It's not completely tested, but it looks like this:
# POST /resource
def create
# Delete guest user and reset session.
new_user_from_guest = guest_user
guest_user(with_retry = false).try(:destroy)
session[:guest_user_id] = nil
build_resource(sign_up_params)
resource.save
yield resource if block_given?
if resource.persisted?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
# Assign the guest users app to the new current user.
new_user_from_guest.apps.each do |app|
app.user_id = current_user.id
app.save!
end
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
# Reset test user if signup fails, I have not tested this part yet.
guest_user
guest_user.apps << new_user_from_guest.apps
guest_user.save!
clean_up_passwords resource
set_minimum_password_length
respond_with resource
end
end
Everything seems to work now. Could do some refactoring and possibly try the callback approach that SsouLlesS suggested in his answer.
I am using devise and created a User field called :active which is either true or false. I have to manually make the user active (true) before the user is allowed to log in. At least that is the intent. I have tried this...
class SessionsController < Devise::SessionsController
# POST /resource/sign_in
def create
"resource/signin CREATE"
self.resource = warden.authenticate!(auth_options)
unless resource.active?
sign_out
redirect_to :sorry_not_active_url
return
end
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
However this does not catch all the places where a user can log in, for example, when a user changes their password, the site automatically logs them in automatically after. However, if the user is not active, I do not want them to be allowed to log in, but rather be redirected to a sorry_not_active_url.
What would be the best way to prevent the user from signing in if the user is not active?
Thank you.
Add these two methods to your user model, devise should pick them up automatically - you should NOT need to extend Devise::SessionsController
def active_for_authentication?
super && self.your_method_for_checking_active # i.e. super && self.is_active
end
def inactive_message
"Sorry, this account has been deactivated."
end
Devise (If you have devise 3.2+) now support block parameter in (session) create
# assuming this is your session controller
class SessionsController < Devise::SessionsController
def create
super do |resource|
unless resource.active?
sign_out
# you can set flash message as well.
redirect_to :sorry_not_active_url
return
end
end
end
I'm using Devise to handle authentication in a Rails app, and I'm using permanent_records to soft delete users. The default scope for my User model is the undeleted users. If a user deletes (deactivates) his account, I want him to be able to reactivate his account by logging in, similar to the way Facebook does it. Problem is, since Devise doesn't know to look for deleted users, no account is found. I thought about overriding the sessions#create method
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
respond_with resource, :location => after_sign_in_path_for(resource)
end
But since this is handled by Warden, it seems I'm out of luck. I'm afraid that if I start digging too deep I'm going to start breaking things.
Any ideas?
Thanks!
You need:
Overwrite find_for_authentication method in User model to allow finding for any users https://github.com/plataformatec/devise/blob/master/lib/devise/models/authenticatable.rb#L229
Redefine after_database_authentication method in your model to remove deleted flag here https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb#L98
That is all, I believe. No need to touch controller actions.
this works with the paranoia gem:
class << self
def find_for_authentication(conditions)
User.unscoped do
user = super(conditions)
user.restore!(recursive: true) if user.deleted?
user
end
end
end
With the Confirmable module enabled, Devise will not allow an unconfirmed user to sign in after a predefined period of time has elapsed. Instead the user is redirected back to the sign in page with the flash message "You have to confirm your account before continuing".
This is an undesirable interaction model, as a flash notice does not provide adequate space to properly explain to the user why access has been denied, what "confirm your account" means, provide a link to resend the confirmation, and instructions on how to check your spam folder and so on.
Is there a way I can change this behaviour to redirect to a specific URL instead?
Sorry at first I thought you meant after Sign Up not Sign In. So the down below works for how to direct users after Sign Up and what you need to do for Sign In is to create a custom Devise::FailureApp
See the wiki page: https://github.com/plataformatec/devise/wiki/How-To:-Redirect-to-a-specific-page-when-the-user-can-not-be-authenticated
Then within your custom FailureApp overwrite redirect_url method from https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb:
def redirect_url
if warden_message == :unconfirmed
custom_redirect_path
else
super
end
end
For custom redirect after Sign Up:
There is a controller method after_inactive_sign_up_path_for within the RegistrationsController that you can overwrite to accomplish this.
First in your Routes you will need to specify to use your custom controller:
config/routes.rb:
devise_for :users, :controllers => { :registrations => "users/registrations" }
Second you create your custom controller that inherits from the normal controller in order to overwrite the method:
app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
protected
def after_inactive_sign_up_path_for(resource)
signed_up_path
end
end
In this case for my App my Devise model is User so you may want to change that namespace if your model is named differently. I wanted my users to be redirected to the signed_up_path, but you can change that to your desired path.
I just did this, but took a different approach.
in app/controllers/sessions_controller.rb:
class SessionsController < Devise::SessionsController
before_filter :check_user_confirmation, only: :create
#
# other code here not relevant to the example
#
private
def check_user_confirmation
user = User.find_by_email(params[:email])
redirect_to new_confirmation_path(:user) unless user && user.confirmed?
end
end
This worked for me and seemed minimally invasive. In my app new sessions always have to go through sessions#create and users always sign in with their email address, so this may be a simpler case than yours.
You can of course redirect_to any location you desire in the check_user_confirmation method. new_confirmation_path was the logical choice for me because it provides users with the resources to get confirmed.
This is my solution you need to add :unconfirmed message on devise locales below the sessions.
in app/controllers/sessions_controller.rb
def check_user_confirmation
user = User.where(email: params[:user][:email]).take
unless user && user.confirmed?
set_flash_message! :alert, :unconfirmed
expire_data_after_sign_in!
respond_with user, location: after_inactive_sign_up_path_for(user)
end
end
protected
def after_inactive_sign_up_path_for(resource)
new_user_session_path
end