app/models/authenticator/ldap.rb
module Authenticator
class Ldap < Base
def self.proper_name
'LDAP'
end
def self.authenticates_for
super + %w[ldaps]
end
def self.validate_config(config)
if config[:ldaphost].blank?
[[:ldaphost, "ldaphost can't be blank"]]
else
[]
end
end
def autocreate_user(username)
# when default group for ldap users is enabled, create the user
return unless config[:default_group_for_users]
default_group = MiqGroup.in_my_region.find_by(:description => config[:default_group_for_users])
return unless default_group
create_user_from_ldap(username) { [default_group] }
end
def user_authorizable_without_authentication?
true
end
private
def ldap
@ldap ||= ldap_bind(config[:bind_dn], config[:bind_pwd])
end
# Unbound LDAP handle
def miq_ldap
@miq_ldap ||= MiqLdap.new(:auth => config)
end
def ldap_bind(username, password)
ldap = MiqLdap.new(:auth => config)
ldap if ldap.bind(username, password)
end
def create_user_from_ldap(username)
lobj = ldap.get_user_object(username)
if lobj.nil?
raise _("Unable to auto-create user because LDAP search returned no data for user: [%{name}]") %
{:name => username}
end
groups = yield lobj
user = User.new
update_user_attributes(user, username, lobj)
user.miq_groups = groups
user.save!
_log.info("MIQ(Authenticator#create_user_from_ldap): Created User: [#{user.userid}]")
user
end
def normalize_username(username)
miq_ldap.normalize(miq_ldap.fqusername(username))
end
def _authenticate(username, password, _request)
password.present? &&
ldap_bind(username, password)
end
def find_external_identity(username, _user_attrs, _membership_list)
# Ldap will be used for authentication and role assignment
_log.info("Bind DN: [#{config[:bind_dn]}]")
_log.info(" User FQDN: [#{username}]")
lobj = ldap.get_user_object(username)
_log.debug("User obj from LDAP: #{lobj.inspect}")
lobj
end
def userid_for(lobj, username)
ldap.normalize(ldap.get_attr(lobj, :userprincipalname) || username)
end
DEFAULT_GROUP_MEMBERSHIPS_MAX_DEPTH = 2
def groups_for(obj)
authentication = config.dup
authentication[:group_memberships_max_depth] ||= DEFAULT_GROUP_MEMBERSHIPS_MAX_DEPTH
if authentication.key?(:user_proxies) && authentication[:user_proxies].present? && authentication.key?(:get_direct_groups) && authentication[:get_direct_groups] == false
_log.info("Skipping getting group memberships directly assigned to user bacause it has been disabled in the configuration")
groups = []
else
groups = ldap.get_memberships(obj, authentication[:group_memberships_max_depth])
end
if authentication.key?(:user_proxies)
if (sid = MiqLdap.get_attr(obj, :objectsid))
authentication[:user_proxies].each do |auth|
begin
groups += user_proxy_membership(auth, MiqLdap.sid_to_s(sid))
rescue Exception => err
_log.warn("#{err.message} (from Authenticator#user_proxy_membership)")
end
end
else
_log.warn("User Object has no objectSID")
end
end
groups.uniq
end
def update_user_attributes(user, _username, lobj)
user.userid = ldap.normalize(ldap.get_attr(lobj, :userprincipalname) || ldap.get_attr(lobj, :dn))
user.first_name = ldap.get_attr(lobj, :givenname)
user.last_name = ldap.get_attr(lobj, :sn)
email = ldap.get_attr(lobj, :mail)
email = email.first if email.kind_of?(Array)
user.email = email if email.present?
user.name = ldap.get_attr(lobj, :displayname)
user.name = "#{user.first_name} #{user.last_name}" if user.name.blank?
user.name = user.userid if user.name.blank?
end
REQUIRED_LDAP_USER_PROXY_KEYS = [:basedn, :bind_dn, :bind_pwd, :ldaphost, :ldapport, :mode]
def user_proxy_membership(auth, sid)
authentication = config
auth[:bind_dn] ||= authentication[:bind_dn]
auth[:bind_pwd] ||= authentication[:bind_pwd]
auth[:ldapport] ||= authentication[:ldapport]
auth[:mode] ||= authentication[:mode]
auth[:group_memberships_max_depth] ||= DEFAULT_GROUP_MEMBERSHIPS_MAX_DEPTH
REQUIRED_LDAP_USER_PROXY_KEYS.each do |key|
unless auth.key?(key)
raise _("Required key not specified: [%{key}]") % {:key => key}
end
end
fsp_dn = "cn=#{sid},CN=ForeignSecurityPrincipals,#{auth[:basedn]}"
ldap_up = MiqLdap.new(:auth => {:ldaphost => auth[:ldaphost], :ldapport => auth[:ldapport], :mode => auth[:mode], :basedn => auth[:basedn]})
_log.info("Bind DN: [#{auth[:bind_dn]}], Host: [#{auth[:ldaphost]}], Port: [#{auth[:ldapport]}], Mode: [#{auth[:mode]}]")
raise "Cannot Bind" unless ldap_up.bind(auth[:bind_dn], auth[:bind_pwd]) # now bind with bind_dn so that we can do our searches.
_log.info("User SID: [#{sid}], FSP DN: [#{fsp_dn}]")
user_proxy_object = ldap_up.search(:base => fsp_dn, :scope => :base).first
raise "Unable to find user proxy object in LDAP" if user_proxy_object.nil?
_log.debug("UserProxy obj from LDAP: #{user_proxy_object.inspect}")
ldap_up.get_memberships(user_proxy_object, auth[:group_memberships_max_depth])
end
end
end