cschiewek / devise_ldap_authenticatable

Devise Module for LDAP
MIT License
594 stars 359 forks source link

Find existing user based on email when LDAP auth uses username #177

Open cheynewallace opened 10 years ago

cheynewallace commented 10 years ago

I have a slight problem that i'm sure there is a solution to, but I can't seem to figure it out.

I'm authing against Active Directory using the AD Username, which is working, but the problem is when it tries to create the user on the local database when that user already exists (but is usually auth'd with an email address)

What I need to do is once the user it authenticated against Active Directory, when it comes back into Devise, look up if the user exists using the email address pulled from active directory and checking against the local DB. Right now, it's checking to see if the username exists, which it won't yet so it tries to create a duplicate user.

Any ideas?

cheynewallace commented 10 years ago

I found a hacky work around for this, but something more official would be nice.

tarmotalu commented 3 years ago

Hey.

I struggled with same issue. Finally solved with callbacks and method override.

class User < ApplicationRecord
  attr_accessor :ldap_hash
  ...
# Guarantee the up-to-dateness of user data
  def ldap_before_save
    self.email = ldap_hash[:email]
    self.firstname = ldap_hash[:firstname]
    self.lastname = ldap_hash[:lastname]
  end

  # Ensure that email is not overwritten by username value
  def after_ldap_authentication
    self.email = ldap_hash[:email]
  end

  # Find a user for ldap authentication.
  # NB Override Devise::Models::LdapAuthenticatable class method.
  def self.find_for_ldap_authentication(attributes={})
    auth_key = self.authentication_keys.first
    return nil unless attributes[auth_key].present?

    auth_key_value = (self.case_insensitive_keys || []).include?(auth_key) ? attributes[auth_key].downcase : attributes[auth_key]
    auth_key_value = (self.strip_whitespace_keys || []).include?(auth_key) ? auth_key_value.strip : auth_key_value

    # Query additional data from LDAP server
    entry = Devise::LDAP::Adapter.get_ldap_entry(auth_key_value)

    ldap_hash = {
      email: entry[:mail].first.try(:downcase),
      firstname: entry[:givenname].join(' '),
      lastname: entry[:sn].join(' ')
    }

    # Find user by e-mail
    resource = where(email: ldap_hash[:email]).first if ldap_hash[:email]

    if resource.blank?
      resource = new
      resource[auth_key] = auth_key_value
    end

    # Store information on resource for later callback calls
    resource.ldap_hash = ldap_hash

    if ::Devise.ldap_create_user && resource.new_record? && resource.valid_ldap_authentication?(attributes[:password])
      resource.ldap_before_save if resource.respond_to?(:ldap_before_save)
      resource.save!
    end

    # Device "needs" same auth key value that came from end-user
    resource[auth_key] = auth_key_value
    resource
  end
end