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
Related
in my project I'm using devise and I want to create custom sign_in. I decided to override create action in SessionsController. Right now my SessionsController looks like that:
class SessionsController < Devise::SessionsController
def create
user_signed_in? # -> gives true if correct email and password
if !current_user.new_one? # -> I have access to current_user
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
# even if condition is true, user is signed_in
end
end
I'm a little confused because even if I remove whole code from there then user will be signed_in anyway. Can someone explain me why user_signed_in? gives me true before sign_in(resource_name, resource)? It looks like the user is already logged in? In that case, how can specific users not be allowed to log in? I know there is method active_for_authentication?, but I don't want to override it, because I want to allow some users to log in to only part of the application, this method will not allow it and overriding this method will not allow me to do it.
When overriding Devise::SessionsController, you still expect some authentication over the params you have parametered (usually email/password).
user_signed_in? method does not the authentication, it's just a helper and it can check if the formerly processed authentication has succeded in the past or not.
The authentication is achieved with use of 'super' at some point of your code.
See :
https://rubydoc.info/github/heartcombo/devise/main/Devise/Controllers/SignInOut#signed_in%3F-instance_method
to find an answer to your first question
https://github.com/heartcombo/devise#controller-filters-and-helpers
https://github.com/heartcombo/devise#configuring-controllers (use of
super)
Hope it helps
I have gem devise and gem apartment which I want to use to create separate schema for each user. I'm looking for a good way to call Apartment::Database.create(current_user) when user sign_up.
I found this at devise's registrations_controller.rb:
# 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
but I'm not sure how it should be done.
I suppose it can be something similar to this at application_controller.rb:
def sign_up(user)
super user
Apartment::Database.create(current_user.username)
end
I wrote this looking at this sign_in override example but I have some concerns:
about user argument as super function uses different parameters (resource_name, resource).
I wish to call Apartment::Database.create only if user signed up successfully.
I'm not sure but I think user can be used here instead of current_user method (assuming it's used to sign up new user).
What about putting the create call at User model as after_create hook ? Is it a good idea ?
that way at model/User.rb
after_create :create_schema
private
def create_schema
Apartment::Database.create(self.username)
end
Above code seams to work fine but I'm not sure if it's a proper way and I would like to ask you to comment override concerns even if it is.
Thanks in advance for your help.
Rails 3.2. Hi. Currently out app has an authentication system implemented that we did and we are going to migrate to devise. I am at wits end here trying to get the devise log in to work. Somehow I have drilled down the problem to the part where Devise actually creates a user session.
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
Routes (I have changed the devise controller and literally just pasted the Devise controller code to debug it with pry):
devise_for :users, controllers: { sessions: "sessions" }
The problem here is that warden is not even hitting the database with an authenticate. It just returns nil. I checked my other, simpler application and authenticate's behavior scans the database, regardless if email/password is correct. How do I get Warden/Devise for that matter to actually do a select statement to check the database out?
(If there is anything I can paste to help you guys I'll paste it)
In case it may help someone else: I was running into this very same problem. The source of the trouble turned out to be that I had a custom app/views/sessions/new.html.erb view that was not copied from devise.
See https://github.com/plataformatec/devise/issues/3700 for more information.
Try look in console the authentication keys of your devise model:
(your_model).authentication_keys
If result is different respect keys you need (i.e email), just add explicity in your model:
:authentication_keys => [:email]
I resolved in this way. Hope this help
Bye
I am using devise and i am trying to update extra fields has user log in. So in order for me to be able to update my user.longitude i had to create in new controller who is acting has the rails controller sessions. Here the code I have
class MysessionsController < Devise::SessionsController
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_navigational_format?
sign_in(resource_name, resource)
self.resource.longitude = params[:longitude]
self.resource.update_attributes(params[:longitude])
respond_with resource, :location => after_sign_in_path_for(resource)
end
end
I know it doesn't follow restfull and it doesnt work but i need to be able to update my user field and i am not sure how to do, this doesn't give me anything wrong, but it doesn't also modify it properly. Thank for any help
Your line:
self.resource.update_attributes(params[:longitude])
Might return false if the record is invalid and silently fail. Check that out first.
Second, you might want to take a look into alias_method_chain instead of copying the content of the create action of your inherited controller.
Otherwise, this is to update a record after a user logged in. You should save the longitude AFTER he signs in. Wherever you redirect the user after he signs in, let the following call handle the longitude update. Easy way could be to store the longitude in session if it needs to survive.
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" }