121 lines
6.6 KiB
Plaintext
121 lines
6.6 KiB
Plaintext
<cfinclude template="app_saml_config.cfm"/>
|
||
<cfinclude template="saml.cfm"/>
|
||
|
||
<!---В отличие от логона, здесь тот же шаблон обрабатывает редирект от IDP. Возможно, правильнее делать было через sso.cfm--->
|
||
<!---Последовательность: шаблон принимает редирект от logout.cfm (к этому моменту usr_id в сессии уже сброшен), формирует подписанный запрос на разлогинивание и отправляет его браузеру с редиректом на IDP.
|
||
Заодно с ним идет RelayState, в который мы кладем относительный URL на страницу, которая инициировала логаут.
|
||
При успешном логауте IDP возвращает браузеру редирект на вот эту же страницу с подписанным ответом (мы его не читаем и подпись не проверяем) и RelayState.
|
||
Мы делаем редирект на относительный URL из RelayState --->
|
||
|
||
<!---*** Дублирование кода--->
|
||
<cfset ksFile = CreateObject("Java", "java.io.File").init(request.SAML.KEYSTORE_FILE_NAME) />
|
||
<cfset ksInputStream = CreateObject("Java", "java.io.FileInputStream").init(ksFile) />
|
||
<cfset keystore = CreateObject("Java" , "java.security.KeyStore").getInstance("JKS") />
|
||
<cfset keystore.load(ksInputStream, request.SAML.KEYSTORE_PASS.toCharArray()) />
|
||
<cfset certificate = keystore.getCertificate(request.SAML.CERTIFICATE_ALIAS) />
|
||
<cfset privateKey = keystore.getKey(request.SAML.CERTIFICATE_ALIAS, request.SAML.KEY_PASS.toCharArray()) />
|
||
<!---<cfset signingCertString = binaryEncode(certificate.getEncoded(),"base64") />
|
||
<cfset encryptionCertString = signingCertString/>--->
|
||
|
||
<cfset currentUTC = DateConvert("local2UTC", Now()) />
|
||
|
||
|
||
<cfparam name="SAMLResponse" default=""/>
|
||
<cfparam name="SAMLRequest" default=""/>
|
||
<cfparam name="target_page" default="ttrtrtrt"/> <!---пустое значение вызывает проблемы!--->
|
||
|
||
<cflock scope="session" type="exclusive" timeout="3">
|
||
<cfparam name="session.SAML_NameID" default=""/>
|
||
<cfparam name="session.SAML_SessionIndex" default=""/>
|
||
|
||
<cfset SAML_NameID=session.SAML_NameID/>
|
||
<cfset SAML_SessionIndex=session.SAML_SessionIndex/>
|
||
</cflock>
|
||
|
||
|
||
<cfif len(SAMLResponse)><!---Обработка ответа (редиректа) от IDP--->
|
||
<!---Мы не проверяем аутентичность ответа от IDP, полагаясь на то, что знаем, кому отправляем запрос на разлогинивание--->
|
||
<cfparam name="RelayState" default=""/>
|
||
|
||
<cflock scope="session" type="exclusive" timeout="3">
|
||
<cfset structdelete(session, "usr")>
|
||
<cfset structdelete(session, "SAML_NameID")>
|
||
<cfset structdelete(session, "SAML_SessionIndex")>
|
||
<cfset structdelete(session, "SAML_IdPCredentialsExpirationTime")>
|
||
<cfset structdelete(session, "usr_id")>
|
||
</cflock>
|
||
|
||
<cfset strUrl="../index.cfm"/>
|
||
<cfif len(RelayState) GT 0>
|
||
<cfif left(RelayState,1) EQ "/">
|
||
<cfset strUrl="#RelayState#"/>
|
||
<cfelse>
|
||
<cfset strUrl="../#RelayState#"/>
|
||
</cfif>
|
||
</cfif>
|
||
<!--- <cfset strUrl="req.cfm"/> --->
|
||
<cfelseif len(SAMLRequest)><!---Не знаю, когда используется этот режим--->
|
||
<cfset SAMLRequestStr = XMLParse(inflate(SAMLRequest))/>
|
||
<cfset SAMLRequestXml = XMLParse(SAMLRequestStr)/>
|
||
<cfset requestID=XMLSearch(SAMLRequestXml, "string(/*[name()='samlp:LogoutRequest']/@ID)")/>
|
||
|
||
<cfoutput><cfsavecontent variable="logoutResponseXml"><?xml version="1.0" encoding="UTF-8"?>
|
||
<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="id#CreateUUID()#" Version="2.0" IssueInstant="#DateFormat(currentUTC,"yyyy-mm-dd")#T#TimeFormat(currentUTC,"HH:MM:ss")#Z" Destination="https://sso.nubes.ru/adfs/ls/" InResponseTo="#requestID[1]#">
|
||
<saml:Issuer>#request.SAML.baseUrl#metadata.cfm</saml:Issuer>
|
||
<samlp:Status>
|
||
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
|
||
</samlp:Status>
|
||
</samlp:LogoutResponse>
|
||
</cfsavecontent></cfoutput>
|
||
|
||
<cfscript>
|
||
logout64 = (deflate2base64(logoutResponseXml));
|
||
logoutUrl = urlEncodeRfc2396(logout64);
|
||
logoutSigAlgUrl= urlEncodeRfc2396('http://www.w3.org/2001/04/xmldsig-more##rsa-sha256');
|
||
if (len(target_page)) { //empty RelayState would not be accepted
|
||
relayStateX = "&RelayState=#urlEncodeRfc2396(target_page)#";
|
||
} else {
|
||
relayStateX = "";
|
||
}
|
||
logoutRequest2Sign="SAMLRequest=#logoutUrl##relayStateX#&SigAlg=#logoutSigAlgUrl#"; //order matters
|
||
signedBytesUrl = urlEncodeRfc2396(sign2base64(logoutRequest2Sign, privateKey, 'SHA256withRSA'));
|
||
logoutRequest="#logoutRequest2Sign#&Signature=#signedBytesUrl#";
|
||
</cfscript>
|
||
<cflock scope="session" type="exclusive" timeout="3">
|
||
<cfset structClear(session)/>
|
||
<cfset structClear(cookie)/>
|
||
</cflock>
|
||
<cfset strUrl="#request.SAML.IDP_URL#?#logoutRequest#"/>
|
||
<cfelse><!---Формируем и отправляем IDP (редиректом) запрос на разлогинивание--->
|
||
<cfoutput>
|
||
<cfsavecontent variable="logoutRequestXml"><?xml version="1.0" encoding="UTF-8"?>
|
||
<samlp:LogoutRequest
|
||
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
|
||
xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
|
||
ID="id#CreateUUID()#" Version="2.0"
|
||
IssueInstant="#DateFormat(currentUTC,"yyyy-mm-dd")#T#TimeFormat(currentUTC,"HH:MM:ss")#Z"
|
||
Destination="#request.SAML.IDP_URL#">
|
||
<saml:Issuer>#request.SAML.baseUrl#metadata.cfm</saml:Issuer>
|
||
<saml:NameID>#SAML_NameID#</saml:NameID>
|
||
<samlp:SessionIndex>#SAML_SessionIndex#</samlp:SessionIndex>
|
||
</samlp:LogoutRequest>
|
||
</cfsavecontent>
|
||
</cfoutput>
|
||
<cfscript>
|
||
logout64 = (deflate2base64(logoutRequestXml));
|
||
logoutUrl = urlEncodeRfc2396(logout64);
|
||
logoutSigAlgUrl= urlEncodeRfc2396('http://www.w3.org/2001/04/xmldsig-more##rsa-sha256');
|
||
if (len(target_page)) { //empty RelayState would not be accepted
|
||
relayStateX = "&RelayState=#urlEncodeRfc2396(target_page)#";
|
||
} else {
|
||
relayStateX = "";
|
||
}
|
||
logoutRequest2Sign="SAMLRequest=#logoutUrl##relayStateX#&SigAlg=#logoutSigAlgUrl#"; //order matters
|
||
signedBytesUrl = urlEncodeRfc2396(sign2base64(logoutRequest2Sign,privateKey,'SHA256withRSA'));
|
||
logoutRequest="#logoutRequest2Sign#&Signature=#signedBytesUrl#";
|
||
</cfscript>
|
||
<cfset strUrl="#request.SAML.IDP_URL#?#logoutRequest#"/>
|
||
</cfif>
|
||
|
||
<cflocation url="#strUrl#" addtoken="no"/>
|