devise_ldap_authentication for your domain email on top of database_authenticatable


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”

%d bloggers like this: