128 lines
6.1 KiB
Ruby
128 lines
6.1 KiB
Ruby
class SsoLoginBoxController < SessionsController
|
|
require 'openssl'
|
|
require 'onelogin/ruby-saml'
|
|
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
|
|
skip_before_action :verify_authenticity_token
|
|
def sso_auth_page
|
|
session[:referer_url] = params[:referer_url]
|
|
# puts ["session", session, session.to_hash]
|
|
request = OneLogin::RubySaml::Authrequest.new
|
|
redirect_to(request.create(saml_settings))
|
|
end
|
|
def sso_sign_in
|
|
puts ["SSO sign in"]
|
|
puts "------------------------------"
|
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {:settings => saml_settings, :allowed_clock_drift=> "60"})
|
|
# puts response.inspect
|
|
# response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
|
|
# response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
|
|
# response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
|
|
# response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doesn't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
|
|
# response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_audience: true}) # skips audience check
|
|
# We validate the SAML Response and check if the user already exists in the system
|
|
if response.is_valid?
|
|
attributes = response.attributes
|
|
# puts ["attributes", attributes.inspect]
|
|
if true#["f", "s"].include?(attributes["AccountTypeCode"])
|
|
email = attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
|
member_name = attributes["ChineseName"]
|
|
sid = attributes["SEQ"]
|
|
matched_member = sid.present? ? MemberProfile.where(:sid=>sid).first : nil
|
|
user = nil
|
|
if matched_member
|
|
user = matched_member.user
|
|
else
|
|
matched_member = MemberProfile.where(:email=>email).first
|
|
if matched_member
|
|
user = matched_member.user
|
|
else
|
|
user = member_name.present? ? User.where("member_name.zh_tw"=> member_name).first : nil
|
|
end
|
|
end
|
|
if !user.nil?
|
|
puts "Login #{user.user_name} success by sso!"
|
|
session[:sso_token] = user.id
|
|
session[:user_id] = user.id
|
|
session[:login_referer] = nil
|
|
if session[:referer_url].present?
|
|
redirect_to URI.parse(session[:referer_url]).path
|
|
else
|
|
redirect_to admin_dashboards_path
|
|
end
|
|
else
|
|
@login_referer = session[:referer_url]
|
|
flash.now.alert = I18n.t("sso_login_box_for_ntu.user_not_in_database",:sid=>sid,:name=>member_name,:email=>email).gsub("\n","<br>").html_safe
|
|
render "new"
|
|
end
|
|
else
|
|
@login_referer = session[:referer_url]
|
|
flash.now.alert = I18n.t("sso_login_box_for_ntu.not_staff")
|
|
render "new"
|
|
end
|
|
else
|
|
puts ["errors", response.instance_variable_get(:@errors)]
|
|
@login_referer = session[:referer_url]
|
|
flash.now.alert = I18n.t("sso_login_box_for_ntu.sso_authorized_failure")
|
|
render "new"
|
|
end
|
|
end
|
|
def delete_session
|
|
reset_session
|
|
end
|
|
# Create a SP initiated SLO
|
|
def sp_logout_request
|
|
# LogoutRequest accepts plain browser requests w/o paramters
|
|
logout_url = URI.join("https://#{request.host}", params[:referer_url].to_s) rescue "https://#{request.host}"
|
|
settings = saml_settings(logout_url)
|
|
|
|
if settings.idp_slo_service_url.nil?
|
|
delete_session
|
|
else
|
|
logout_request = OneLogin::RubySaml::Logoutrequest.new
|
|
if settings.name_identifier_value.nil?
|
|
settings.name_identifier_value = session[:user_id]
|
|
end
|
|
|
|
# Ensure user is logged out before redirect to IdP, in case anything goes wrong during single logout process (as recommended by saml2int [SDP-SP34])
|
|
logged_user = session[:user_id]
|
|
delete_session
|
|
|
|
# Save the transaction_id to compare it with the response we get back
|
|
session[:transaction_id] = logout_request.uuid
|
|
session[:logged_out_user] = logged_user
|
|
relayState = "https://#{request.host}"
|
|
redirect_to(logout_request.create(settings, :RelayState => relayState))
|
|
end
|
|
end
|
|
private
|
|
|
|
def saml_settings(logout_url=nil)
|
|
settings = OneLogin::RubySaml::Settings.new
|
|
request_host = request.host
|
|
logout_url ||= "https://#{request_host}"
|
|
settings.assertion_consumer_service_url = "https://#{request_host}/ntu_sso/response"
|
|
settings.issuer = request_host
|
|
settings.idp_sso_target_url = "https://adfs.ntu.edu.tw/adfs/ls/"
|
|
# settings.idp_sso_target_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
|
|
settings.idp_slo_service_url = "https://adfs.ntu.edu.tw/adfs/ls/clearall.aspx?url=#{logout_url}"
|
|
# settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
|
|
settings.idp_cert_fingerprint = (File.read('adfs_fingerprint.txt') rescue '') #"0A:27:FC:D5:CE:DC:D8:44:CC:A9:58:8A:42:D1:F4:DF:38:2E:4A:C3"
|
|
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
|
|
# settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
# settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
|
|
# # Optional for most SAML IdPs
|
|
# settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
|
|
# # or as an array
|
|
# settings.authn_context = [
|
|
# "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
|
|
# "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
|
|
# ]
|
|
|
|
# Optional bindings (defaults to Redirect for logout POST for ACS)
|
|
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
|
|
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
|
|
|
|
settings
|
|
end
|
|
end |