140 lines
3.6 KiB
Ruby
140 lines
3.6 KiB
Ruby
# 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
|
||
|