2022-05-14 03:53:04 +00:00
class SsoLoginBoxController < SessionsController
require 'openssl'
require 'onelogin/ruby-saml'
OpenSSL :: SSL :: VERIFY_PEER = OpenSSL :: SSL :: VERIFY_NONE
def sso_auth_page
session [ :referer_url ] = params [ :referer_url ]
2023-02-13 15:22:31 +00:00
# puts ["session", session, session.to_hash]
2022-05-14 03:53:04 +00:00
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
2023-02-13 15:22:31 +00:00
# puts ["attributes", attributes.inspect]
2022-05-14 03:53:04 +00:00
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?
2023-02-13 15:22:31 +00:00
puts " Login #{ user . user_name } success by sso! "
2022-05-14 03:53:04 +00:00
session [ :sso_token ] = user . id
session [ :user_id ] = user . id
session [ :login_referer ] = nil
if params [ :referer_url ] . present?
redirect_to URI . parse ( params [ :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
settings = saml_settings
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
settings = OneLogin :: RubySaml :: Settings . new
request_host = request . host
2023-02-13 15:22:31 +00:00
settings . assertion_consumer_service_url = " https:// #{ request_host } /ntu_sso/response "
2022-05-14 03:53:04 +00:00
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_target_url = " https://adfs.ntu.edu.tw/adfs/ls/clearall.aspx?url=https:// #{ request_host } "
# settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
2023-02-13 15:22:31 +00:00
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"
2022-05-14 03:53:04 +00:00
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
2023-02-13 15:22:31 +00:00
end