Question

I am using Devise as authentication for a rails 3.2 app. My user model is called User, and I've been trying to implement a soft delete. I am trying to do this by using acts_as_paranoid, which automatically does the work for you. Things work as expected (others can no longer see the "deleted" user), except that I want the deactivated user to still be able to log in and see a "deactivated" screen, and give them the chance to reactivate their account.

The problem is that Devise (Warden?) is no longer able to find the deleted user. Acts_as_paranoid lets you access the soft deleted records if you use the scope "with_deleted". So I am able to get part of the way there with:

def self.find_first_by_auth_conditions(warden_conditions)
  conditions = warden_conditions.dup
  where(conditions).with_deleted.first
end

I put this into my user model, and so now when I input the log in information, I'll get the flash message that I have successfully logged in, and it'll touch the "updated_at" column in the user model and increment the sign_in_count, etc. However, it doesn't really authenticate in the sense that the authenticated method returns false and the current_user helper method returns nil. So what is my best strategy to get these working? Can I override the current_user method somehow so that it queries the User model with the with_deleted scope? Do I have to do something with warden, such as added conditions like I did with the find_first_by_auth_conditions method? I cannot figure out how to do this. Any help is appreciated!

also, after I do get it working, I would like to automatically send all soft deleted users to a "deactivated" page where their only options are to permanently delete or reactivate. Is there some way to do this with routing via the "authenticated :user do {} end", or do i have to put a before_filter in the application_controller and check for the users at every request?

Was it helpful?

Solution 2

Thanks to a tip from the Devise group, it turns out I needed to override the serialize_from_session method, which is inside the authenticatable module and looks like this:

def serialize_from_session(key, salt)
  record = to_adapter.get(key)
  record if record && record.authenticatable_salt == salt
end

I'd been trying with no success to override modules using initializers (I was trying to override existing strategies, and also to try writing a new one for warden); but I kept getting weird name errors. I still haven't figured that out. However, with the tip, I went ahead and overrode the method inside my User model. Since I don't foresee using some other resource, I didn't mind just editing it like this. The code was just:

def self.serialize_from_session(key, salt)
  record = with_deleted.find(key).first
  record if record && record.authenticatable_salt == salt
end

This skips the to_adapter.get method altogether (which, to anyone interested, is in orm_adapter, not devise; this took me awhile to find as I was thinking I needed to override this). This probably isn't the cleanest way to do this, but it works well enough for me. And even if I do have a different resource, this should only override the User resource, so I think everything would work fine otherwise.

If problems crop up, I'll add them here. If not, hopefully this helps someone in some way. Certainly took me long enough to get to the bottom of!

OTHER TIPS

I can answer your last question, about routing to the "deactivated page". You can use a lambda to isolate different types of users, like this:

authenticated :user, lambda {|u| u.deactivated? } do
  root :to => 'application#deactivated'
end
authenticated :user, lambda {|u| u.active? } do
  root :to => 'application#active'
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top