ldap_login_for_nfu/lib/ldap_login/login.rb

140 lines
3.6 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
module LdapLogin::Login
require 'net/ldap'
require 'resolv'
LDAP_DOMAIN = 'CTX10.NFU.EDU.TW'
LDAP_SRV = '_ldap._tcp.dc._msdcs.ctx10.nfu.edu.tw'
def ldap_login_auth(user, request, session, flash, params)
Rails.logger.info '[LDAP] ===== login start ====='
ldap_hosts = fetch_ldap_hosts
ldap_user = params[:user_name].to_s.strip
ldap_pass = params[:password].to_s
error = ''
login_flag = false
url = '/'
url_method = 'redirect_to'
Rails.logger.info "[LDAP] login user=#{ldap_user}"
if ldap_pass.blank?
error = '請輸入密碼'
else
ldap_hosts.each do |ldap_host|
Rails.logger.info "[LDAP] try host=#{ldap_host}"
begin
ldap = Net::LDAP.new(
host: ldap_host,
port: 389,
auth: {
method: :simple,
username: "#{ldap_user}@#{LDAP_DOMAIN}",
password: ldap_pass
}
)
if ldap.bind
Rails.logger.info "[LDAP] bind success (#{ldap_host})"
if user.present?
session[:user_id] = user.id
session[:login_referer] = nil
if params[:referer_url].present?
url = URI.parse(params[:referer_url]).path
else
url = admin_dashboards_path
end
login_flag = true
break
else
error = I18n.t('devise.failure.ldap_pass_but_account_not_in_orbit')
break
end
else
Rails.logger.warn "[LDAP] bind failed (#{ldap_host})"
error = ldap_error_message(ldap.get_operation_result)
# ⚠️ 不 break繼續嘗試下一台 DC
next
end
rescue Net::LDAP::ConnectionError => e
Rails.logger.error "[LDAP] connection error (#{ldap_host}) #{e.message}"
error = '無法連線至 AD 伺服器'
next
rescue => e
Rails.logger.error "[LDAP] unknown error #{e.class}: #{e.message}"
error = '發生不可預知的錯誤'
break
end
end
end
unless login_flag
flash.now.alert = error.presence || 'AD 驗證失敗'
url = 'new'
url_method = 'render'
end
[login_flag, session, flash, url, url_method]
end
private
# 透過 SRV 取得 AD DC 清單
def fetch_ldap_hosts
Rails.logger.info "[LDAP] fetch SRV record #{LDAP_SRV}"
records = Resolv::DNS.open do |dns|
dns.getresources(LDAP_SRV, Resolv::DNS::Resource::IN::SRV)
end
hosts = records.map { |r| r.target.to_s.chomp('.') }
Rails.logger.info "[LDAP] SRV result #{hosts}"
hosts.shuffle
rescue => e
Rails.logger.error "[LDAP] SRV lookup failed #{e.message}"
[]
end
# AD / LDAP 錯誤碼轉換
def ldap_error_message(result)
return 'AD 驗證失敗' if result.nil?
msg = [
result.error_message,
result.message
].compact.join(' ')
Rails.logger.info "[LDAP ERROR RAW] #{msg}"
if msg =~ /data\s+([0-9a-fA-F]{3})/i
case Regexp.last_match(1).downcase
when '530' then '不允許在此時間登入'
when '701' then '帳號已過期'
when '533' then '帳號停用中!'
when '532' then '密碼過期!'
when '52e' then '帳號或密碼錯誤'
when '525' then '帳號不存在'
when '775' then '帳號已被鎖定(輸入錯誤超過次數)'
when '773' then '使用者密碼必須重置'
else
'AD 驗證失敗'
end
else
'AD 驗證失敗'
end
end
end