I have a devise user model named LoginUser whose authentication key is :login. I want normal users of the system to be database_authenticatable.
However, I want to be able to authenticate previously added users via internal LDAP. Furthermore, I didn’t want the underlying database_authenticatable password to be used or to expire on me (also using devise_security_extensions). Most of the work is in the LocalOverride custom strategy’s authenticate! method, with a few other hooks (such as default strategy added to devise.yml).
Update:
To allow all other strategies to be used, but still trap our domains for one-off LDAP auth, I added devise :ldap_authenticatable to a singleton class inherited from the user loaded by the custom strategy.
Also, removed the other two “fails” from the code. Not necessary and will result in a “Failed to Login” message for too many other Devise-related Unauthorized events.
In config/initializers/local_override.rb:
module Devise
module Strategies
class LocalOverride < Authenticatable
def valid?
true
end
def authenticate!
if params[:login_user]
user = LoginUser.find_by_login(params[:login_user][:login])
# trap our domain only
if params[:login_user][:login] =~ /@example.com/
# fail! halts the authentication chain completely
return fail! unless ::Devise::LDAP::Adapter.valid_login?(params[:login_user][:login])
class << user
# make use of ldap_authenticatable for custom strategy only
devise :ldap_authenticatable
end
return fail! unless user.valid_ldap_authentication?(params[:login_user][:password])
# use the after_ldap_authentication hook
user.after_ldap_authentication
return success!(user)
end
end
end
end
end
end
Warden::Strategies.add(:local_override, Devise::Strategies::LocalOverride)
In config/initializers/devise.rb:
# use local_override as default strategy
config.warden do |manager|
manager.default_strategies(:scope => :login_user).unshift :local_override
end
In config/models/login_user.rb:
class LoginUser < ActiveRecord::Base
devise :database_authenticatable,
:recoverable, :trackable, :secure_validatable,
:timeoutable,
:password_expirable,
:password_archivable,
:lockable,
:authentication_keys => [:login]
#
#
def after_ldap_authentication
# force fresh password every log in
self.password = self.password_confirmation = Random.new.bytes(47)
self.save
end
#
#
end
defaults: &defaults
host: our.ldap
port: 636
attribute: mail
base: dc=IDENT,o=Orgname
admin_password: adminpassw0rd
ssl: sslmethod
See local_override.rb for original tip that got me there.
One response to “devise_ldap_authentication for your domain email on top of database_authenticatable”
[…] managed to get a custom Devise strategy with LDAP working, but had no clear way of automating tests. I wanted to validate if I still had to keep the password […]