ruby on rails - How can I allow Devise users to log in when they're outside my default scope? -
i have rails 4 app uses devise 3.4 authentication, i've customized ability ban users (using simple boolean column users.banned
, default false). user
model has default_scope
returns non-banned users.
here's problem - still want banned users able log in, though can't after logging in. (they see page saying "you've been banned"). seems default_scope
tripping devise. when log in or call e.g. authenticate_user!
, devise tries find current user using 1 of basic activerecord methods find
or find_by
, can't because lie outside default scope. devise concludes user doesn't exist, , login fails.
how can make devise ignore default scope?
after long time digging around in devise , warden source code, found solution.
short answer:
add user
class:
def self.serialize_from_session(key, salt) record = to_adapter.klass.unscoped.find(key[0]) record if record && record.authenticatable_salt == salt end
(note i've tested activerecord; if you're using different orm adapter need change first line of method... i'm not sure if other orm adapters have concept of "default so
long answer:
serialize_from_session
mixed user class -devise::models::authenticatable::classmethods
. honestly, i'm not sure it's supposed do, it's public method , documented (very sparsely) in devise api, don't think there's chance of being removed devise without warning.
here's original source code of devise 3.4.1:
def serialize_from_session(key, salt) record = to_adapter.get(key) record if record && record.authenticatable_salt == salt end
the problem lies to_adapter.get(key)
. to_adapter
returns instance of ormadapter::activerecord
wrapped around user
class, , to_adapter.get
same calling user.find
. (devise uses orm_adapter
gem keep flexible; above method work without modification whether you're using activerecord, mongoid or other ormadapter-compatible orm.)
but, of course, user.find
searches within default_scope, why can't find banned users. calling to_adapter.klass
returns user
class directly, , can call unscoped.find
search all users , make banned ones visible devise. working line is:
record = to_adapter.klass.unscoped.find(key[0])
note i'm passing key[0]
instead of key
, because key
array (in case 1 element) , passing array find
return array, isn't want.
also note calling klass
within real devise source code bad idea, means lose advantages of ormadapter
. within own app, know certainty orm you're using (something devise doesn't know), it's safe specific.