spec/saml/logout.cfm
2025-06-02 16:16:51 +03:00

121 lines
6.6 KiB
Plaintext
Raw Permalink 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.

<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"/>