From 9e84887e0d390894342ad0ba229b00079ad49bb7 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Sat, 12 Jan 2019 11:28:43 +0530 Subject: [PATCH 01/16] build a logout request for a session participant for redirect --- .../saml/cache/SAMLSLOParticipantCache.java | 5 + .../saml/servlet/SAMLSSOProviderServlet.java | 128 ++++++++++++++++- .../identity/sso/saml/util/SAMLSSOUtil.java | 133 ++++++++++++++++++ 3 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java new file mode 100644 index 000000000..9eb9d006d --- /dev/null +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java @@ -0,0 +1,5 @@ +package org.wso2.carbon.identity.sso.saml.cache; + +public class SAMLSLOParticipantCache { + +} diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 163a9502d..997d41939 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -20,6 +20,9 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.impl.LogoutResponseImpl; +import org.opensaml.xml.XMLObject; import org.owasp.encoder.Encode; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.RegistryType; @@ -58,6 +61,7 @@ import org.wso2.carbon.identity.sso.saml.exception.IdentitySAML2SSOException; import org.wso2.carbon.identity.sso.saml.internal.IdentitySAMLSSOServiceComponent; import org.wso2.carbon.identity.sso.saml.session.SSOSessionPersistenceManager; +import org.wso2.carbon.identity.sso.saml.session.SessionInfoData; import org.wso2.carbon.identity.sso.saml.util.SAMLSOAPUtils; import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil; import org.wso2.carbon.idp.mgt.util.IdPManagementUtil; @@ -68,8 +72,10 @@ import java.io.IOException; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Base64; import java.util.Enumeration; @@ -172,6 +178,7 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo String spEntityID = req.getParameter(SAMLSSOConstants.QueryParameter .SP_ENTITY_ID.toString()); String samlRequest = req.getParameter(SAMLSSOConstants.SAML_REQUEST); + String samlResponse = req.getParameter(SAMLSSOConstants.SAML_RESP); String sessionDataKey = getSessionDataKey(req); String slo = req.getParameter(SAMLSSOConstants.QueryParameter.SLO.toString()); Object flowStatus = req.getAttribute(FrameworkConstants.RequestParams.FLOW_STATUS); @@ -218,7 +225,7 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo String defaultLogoutLocation = SAMLSSOUtil.getDefaultLogoutEndpoint(); resp.sendRedirect(FrameworkUtils.getRedirectURL(defaultLogoutLocation + queryParams, req)); } else if (sessionDTO.isLogoutReq()) { - handleLogoutResponseFromFramework(req, resp, sessionDTO); + handleLogoutResponseFromFramework(req, resp, sessionDTO, sessionId); } else { handleAuthenticationReponseFromFramework(req, resp, sessionId, sessionDTO); } @@ -238,6 +245,8 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo handleIdPInitSSO(req, resp, relayState, queryString, authMode, sessionId, isPost, (slo != null)); } else if (samlRequest != null) {// SAMLRequest received. SP initiated SSO handleSPInitSSO(req, resp, queryString, relayState, authMode, samlRequest, sessionId, isPost); + } else if (samlResponse != null) {// SAMLResponse received. + handleSAMLResponse(req, resp, samlResponse); } else { log.debug("Invalid request message or single logout message "); @@ -281,6 +290,31 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo } } + private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse) throws IdentityException { + + XMLObject response; + + response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); + String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); + +// String sessionIndex = SAMLSLOParticipantCache.getInstance().getValueFromCache(inResponseToId); +// +// SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager +// .getPersistenceManager(); +// SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); +// Map sessionsList = sessionInfoData.getServiceProviderList(); +// +// sessionsList.remove(((LogoutResponseImpl) response).getIssuer().getValue()); +// +// Boolean hasMoreFrontChannelSPs = false; +// +// if (hasMoreFrontChannelSPs){ +// // doFrontChannelSLO() +// } else { +// // send response to initial issuer +// } + } + /** * In federated and multi steps scenario there is a redirection from commonauth to samlsso so have to get * session data key from query parameter @@ -1012,10 +1046,36 @@ private void handleAuthenticationReponseFromFramework(HttpServletRequest req, Ht } } - private void handleLogoutResponseFromFramework(HttpServletRequest request, - HttpServletResponse response, SAMLSSOSessionDTO sessionDTO) + private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpServletResponse response, + SAMLSSOSessionDTO sessionDTO, String sessionId) throws ServletException, IOException, IdentityException { + List samlssoServiceProviderDOList = SAMLSSOUtil. + getOtherSessionParticipants(sessionId, sessionDTO.getIssuer()); + + // Get the SP list and check for other session participants that have enabled single logout + if (samlssoServiceProviderDOList != null) { + + for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { + + Boolean isFrontChannelSLOEnabled = true; + //check entry.isFrontChannelSLOEnabled() + if (isFrontChannelSLOEnabled){ + doFrontChannelSLO(response, entry, sessionId, sessionDTO.getIssuer()); + break; + } else { + // doBackChannelSLO() + } + } + } else { + respondToOriginalIssuer(request, response, sessionDTO); + } + } + + private void respondToOriginalIssuer(HttpServletRequest request, HttpServletResponse response, + SAMLSSOSessionDTO sessionDTO) throws ServletException, IOException, + IdentityException { + SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO(); if (validationResponseDTO != null) { @@ -1507,4 +1567,66 @@ private void setSPAttributeToRequest(HttpServletRequest req, String issuer, Stri tenantDomain, e); } } + + private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, + String sessionId, String issuer) throws IdentityException { + + SessionInfoData sessionInfoData = SAMLSSOUtil.getSessionInfoData(sessionId); + String subject = sessionInfoData.getSubject(samlssoServiceProviderDO.getIssuer()); + String sessionIndex = SAMLSSOUtil.getSessionIndex(sessionId); + + LogoutRequest logoutRequest = SAMLSSOUtil.buildLogoutRequest(samlssoServiceProviderDO, subject, sessionIndex); + + String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); + + try { + response.sendRedirect(redirectUrl); + } catch (IOException e) { + log.error("Error in sending the redirect.", e); + } + } + + private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, + SAMLSSOServiceProviderDO serviceProviderDO) + throws IdentityException { + + String logoutRequestString = (SAMLSSOUtil.marshall(logoutRequest)). + replaceAll(SAMLSSOConstants.XML_TAG_REGEX, "").trim(); + + StringBuilder httpQueryString = null; + String signatureAlgorithmUri = serviceProviderDO.getSigningAlgorithmUri(); + try { + httpQueryString = new StringBuilder(SAMLSSOConstants.SAML_REQUEST + "=" + + URLEncoder.encode(SAMLSSOUtil.compressResponse(logoutRequestString), "UTF-8")); + httpQueryString.append("&" + SAMLSSOConstants.SIG_ALG + "=" + + URLEncoder.encode(signatureAlgorithmUri, "UTF-8")); + } catch (UnsupportedEncodingException e) { + log.error("Error while encoding the message.", e); + } catch (IOException e) { + log.error("Error in compressing the SAML request message.", e); + } + + // to be revised - setting signature + try { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(serviceProviderDO.getTenantDomain()); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + byte[] signedString = SAMLSSOUtil.setSignature(httpQueryString.toString()); + + String signature = Base64.getEncoder().encodeToString(signedString); + + httpQueryString.append("&" + SAMLSSOConstants.SIGNATURE + "=" + URLEncoder.encode(signature, "UTF-8")); + } catch (KeyStoreException e) { + log.error("Error in signing the httpQueryString", e); + } catch (UnsupportedEncodingException e) { + log.error("Error in encoding the signature", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + + String redirectUrl = logoutRequest.getDestination() + "?" + httpQueryString.toString(); + + return redirectUrl; + } + } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index a3f5d277b..6ed3d1fe5 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -33,12 +33,17 @@ import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; +import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.RequestAbstractType; import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.SessionIndex; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusMessage; import org.opensaml.saml2.core.impl.IssuerBuilder; +import org.opensaml.saml2.core.impl.LogoutRequestBuilder; +import org.opensaml.saml2.core.impl.NameIDBuilder; +import org.opensaml.saml2.core.impl.SessionIndexBuilder; import org.opensaml.saml2.core.impl.StatusBuilder; import org.opensaml.saml2.core.impl.StatusCodeBuilder; import org.opensaml.saml2.core.impl.StatusMessageBuilder; @@ -60,6 +65,7 @@ import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; +import org.wso2.carbon.base.ServerConfiguration; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.RegistryType; import org.wso2.carbon.core.util.KeyStoreManager; @@ -97,23 +103,28 @@ import org.wso2.carbon.identity.sso.saml.processors.SPInitLogoutRequestProcessor; import org.wso2.carbon.identity.sso.saml.processors.SPInitSSOAuthnRequestProcessor; import org.wso2.carbon.identity.sso.saml.session.SSOSessionPersistenceManager; +import org.wso2.carbon.identity.sso.saml.session.SessionInfoData; import org.wso2.carbon.identity.sso.saml.validators.IdPInitSSOAuthnRequestValidator; import org.wso2.carbon.identity.sso.saml.validators.SAML2HTTPRedirectSignatureValidator; import org.wso2.carbon.identity.sso.saml.validators.SPInitSSOAuthnRequestValidator; import org.wso2.carbon.identity.sso.saml.validators.SSOAuthnRequestValidator; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; +import org.wso2.carbon.registry.api.RegistryException; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.registry.core.service.TenantRegistryLoader; +import org.wso2.carbon.security.keystore.KeyStoreAdmin; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -124,8 +135,13 @@ import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SignatureException; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; @@ -154,6 +170,7 @@ public class SAMLSSOUtil { private static final String SECURITY_MANAGER_PROPERTY = Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; private static final int ENTITY_EXPANSION_LIMIT = 0; + public static final String SECURITY_KEY_STORE_KEY_ALIAS = "Security.KeyStore.KeyAlias"; static { for (char c = 'a'; c <= 'z'; c++) @@ -2000,4 +2017,120 @@ public static SAMLSSOServiceProviderDO getSPConfig(String tenantDomain, String i PrivilegedCarbonContext.endTenantFlow(); } } + + public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceProviderDO, String subject, + String sessionId) throws IdentityException { + + SingleLogoutRequestDTO logoutReqDTO = new SingleLogoutRequestDTO(); + + if (StringUtils.isNotBlank(serviceProviderDO.getSloRequestURL())) { + logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloRequestURL()); + } else if (StringUtils.isNotBlank(serviceProviderDO.getSloResponseURL())) { + logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloResponseURL()); + } else { + logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getAssertionConsumerUrl()); + } + + LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); + + logoutReq.setID(SAMLSSOUtil.createID()); + + String destination = logoutReqDTO.getAssertionConsumerURL(); + if (destination != null) { + logoutReq.setDestination(destination); + } + + DateTime issueInstant = new DateTime(); + logoutReq.setIssueInstant(issueInstant); + logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(serviceProviderDO.getTenantDomain())); + logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000)); + + NameID nameId = new NameIDBuilder().buildObject(); + nameId.setFormat(serviceProviderDO.getNameIDFormat()); + nameId.setValue(subject); + logoutReq.setNameID(nameId); + + SessionIndex sessionIndex = new SessionIndexBuilder().buildObject(); + sessionIndex.setSessionIndex(sessionId); + logoutReq.getSessionIndexes().add(sessionIndex); + + logoutReq.setReason(SAMLSSOConstants.SingleLogoutCodes.LOGOUT_USER); + + return logoutReq; + } + + // this method is to be removed + public static byte[] setSignature(String queryString) throws IdentityException, KeyStoreException { + + byte[] signedString = null; + + String keyAlias = ServerConfiguration.getInstance().getFirstProperty(SECURITY_KEY_STORE_KEY_ALIAS); + if (StringUtils.isBlank(keyAlias)) { + throw new IdentityException("Invalid file configurations. The key alias is not found."); + } + + KeyStoreAdmin keyAdmin = null; + try { + keyAdmin = new KeyStoreAdmin(MultitenantConstants.SUPER_TENANT_ID, + SAMLSSOUtil.getRegistryService().getGovernanceSystemRegistry()); + } catch (RegistryException e) { + log.error("Error in setting keystore admin", e); + } + + try { + String path = CarbonUtils.getCarbonHome() + "/repository/resources/security/wso2carbon.jks"; + FileInputStream cert = new FileInputStream(path); + KeyStore keyStore = KeyStore.getInstance("JKS"); + keyStore.load(cert, "wso2carbon".toCharArray()); + PrivateKey privateKey = (PrivateKey) keyAdmin.getPrivateKey(keyAlias, true); + + java.security.Signature instance = java.security.Signature.getInstance("SHA1withRSA"); + instance.initSign(privateKey); + instance.update(queryString.getBytes()); + signedString = instance.sign(); + } catch (IOException | NoSuchAlgorithmException | CertificateException | InvalidKeyException | SignatureException e) { + log.error("Error in setting signature.", e); + } + + return signedString; + } + + public static List getOtherSessionParticipants(String sessionId, String issuer) { + + SessionInfoData sessionInfoData = getSessionInfoData(sessionId); + Map sessionsList = sessionInfoData.getServiceProviderList(); + + List samlssoServiceProviderDOList = new ArrayList<>(); + + for (Map.Entry entry : sessionsList.entrySet()) { + String key = entry.getKey(); + SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); + + // logout request should not be created for the issuer + if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout()) { + samlssoServiceProviderDOList.add(serviceProviderDO); + } + } + + return samlssoServiceProviderDOList; + } + + public static SessionInfoData getSessionInfoData(String sessionId){ + + SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager + .getPersistenceManager(); + String sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); + SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); + + return sessionInfoData; + } + + public static String getSessionIndex(String sessionId){ + + SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager + .getPersistenceManager(); + String sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); + + return sessionIndex; + } } From c3329f8322c84a3c75a12db383f04830a66858b2 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Sun, 13 Jan 2019 22:24:08 +0530 Subject: [PATCH 02/16] resolve comments in PR(#204) --- .../saml/servlet/SAMLSSOProviderServlet.java | 16 ++--- .../identity/sso/saml/util/SAMLSSOUtil.java | 71 ++++++++++++++----- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 997d41939..4afea212a 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -75,7 +75,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Base64; import java.util.Enumeration; @@ -1053,7 +1052,7 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS List samlssoServiceProviderDOList = SAMLSSOUtil. getOtherSessionParticipants(sessionId, sessionDTO.getIssuer()); - // Get the SP list and check for other session participants that have enabled single logout + // Get the SP list and check for other session participants that have enabled single logout. if (samlssoServiceProviderDOList != null) { for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { @@ -1569,7 +1568,7 @@ private void setSPAttributeToRequest(HttpServletRequest req, String issuer, Stri } private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, - String sessionId, String issuer) throws IdentityException { + String sessionId, String issuer) throws IdentityException, IOException { SessionInfoData sessionInfoData = SAMLSSOUtil.getSessionInfoData(sessionId); String subject = sessionInfoData.getSubject(samlssoServiceProviderDO.getIssuer()); @@ -1579,17 +1578,14 @@ private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProvi String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); - try { - response.sendRedirect(redirectUrl); - } catch (IOException e) { - log.error("Error in sending the redirect.", e); - } + response.sendRedirect(redirectUrl); } private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, SAMLSSOServiceProviderDO serviceProviderDO) throws IdentityException { + // Convert the SAML logout request to a string. String logoutRequestString = (SAMLSSOUtil.marshall(logoutRequest)). replaceAll(SAMLSSOConstants.XML_TAG_REGEX, "").trim(); @@ -1606,7 +1602,7 @@ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, log.error("Error in compressing the SAML request message.", e); } - // to be revised - setting signature + // to be revised - setting signature. try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(serviceProviderDO.getTenantDomain()); @@ -1616,8 +1612,6 @@ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, String signature = Base64.getEncoder().encodeToString(signedString); httpQueryString.append("&" + SAMLSSOConstants.SIGNATURE + "=" + URLEncoder.encode(signature, "UTF-8")); - } catch (KeyStoreException e) { - log.error("Error in signing the httpQueryString", e); } catch (UnsupportedEncodingException e) { log.error("Error in encoding the signature", e); } finally { diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 6ed3d1fe5..393c56c55 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -118,13 +118,11 @@ import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.service.RealmService; -import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -137,11 +135,10 @@ import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.Signature; import java.security.SignatureException; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; @@ -171,6 +168,7 @@ public class SAMLSSOUtil { Constants.SECURITY_MANAGER_PROPERTY; private static final int ENTITY_EXPANSION_LIMIT = 0; public static final String SECURITY_KEY_STORE_KEY_ALIAS = "Security.KeyStore.KeyAlias"; + private static final int FIVE_MINUTES = 5 * 60 * 1000; static { for (char c = 'a'; c <= 'z'; c++) @@ -2018,6 +2016,15 @@ public static SAMLSSOServiceProviderDO getSPConfig(String tenantDomain, String i } } + /** + * Build SAML logout request. + * + * @param serviceProviderDO + * @param subject + * @param sessionId + * @return LogoutRequest + * @throws IdentityException + */ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceProviderDO, String subject, String sessionId) throws IdentityException { @@ -2025,10 +2032,22 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP if (StringUtils.isNotBlank(serviceProviderDO.getSloRequestURL())) { logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloRequestURL()); + if (log.isDebugEnabled()) { + log.debug("Destination of the logout request is set to the " + + "SLO request URL of the SP: " + serviceProviderDO.getSloRequestURL()); + } } else if (StringUtils.isNotBlank(serviceProviderDO.getSloResponseURL())) { logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloResponseURL()); + if (log.isDebugEnabled()) { + log.debug("Destination of the logout request is set to the " + + "SLO response URL of the SP: " + serviceProviderDO.getSloResponseURL()); + } } else { logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getAssertionConsumerUrl()); + if (log.isDebugEnabled()) { + log.debug("Destination of the logout request is set to the " + + "ACS URL of the SP: " + serviceProviderDO.getAssertionConsumerUrl()); + } } LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); @@ -2043,7 +2062,7 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP DateTime issueInstant = new DateTime(); logoutReq.setIssueInstant(issueInstant); logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(serviceProviderDO.getTenantDomain())); - logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000)); + logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + FIVE_MINUTES)); NameID nameId = new NameIDBuilder().buildObject(); nameId.setFormat(serviceProviderDO.getNameIDFormat()); @@ -2059,8 +2078,8 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP return logoutReq; } - // this method is to be removed - public static byte[] setSignature(String queryString) throws IdentityException, KeyStoreException { + // this method is to be removed. + public static byte[] setSignature(String queryString) throws IdentityException { byte[] signedString = null; @@ -2078,23 +2097,29 @@ public static byte[] setSignature(String queryString) throws IdentityException, } try { - String path = CarbonUtils.getCarbonHome() + "/repository/resources/security/wso2carbon.jks"; - FileInputStream cert = new FileInputStream(path); - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(cert, "wso2carbon".toCharArray()); - PrivateKey privateKey = (PrivateKey) keyAdmin.getPrivateKey(keyAlias, true); + PrivateKey privateKey = null; + if (keyAdmin != null) { + privateKey = (PrivateKey) keyAdmin.getPrivateKey(keyAlias, true); + } - java.security.Signature instance = java.security.Signature.getInstance("SHA1withRSA"); + Signature instance = Signature.getInstance("SHA1withRSA"); instance.initSign(privateKey); instance.update(queryString.getBytes()); signedString = instance.sign(); - } catch (IOException | NoSuchAlgorithmException | CertificateException | InvalidKeyException | SignatureException e) { + } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { log.error("Error in setting signature.", e); } return signedString; } + /** + * Get other session participants for SLO except for the original issuer. + * + * @param sessionId Session Id + * @param issuer Original issuer + * @return SAMLSSOServiceProviderDO List + */ public static List getOtherSessionParticipants(String sessionId, String issuer) { SessionInfoData sessionInfoData = getSessionInfoData(sessionId); @@ -2106,7 +2131,7 @@ public static List getOtherSessionParticipants(String String key = entry.getKey(); SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); - // logout request should not be created for the issuer + // Logout request should not be created for the issuer. if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout()) { samlssoServiceProviderDOList.add(serviceProviderDO); } @@ -2115,7 +2140,13 @@ public static List getOtherSessionParticipants(String return samlssoServiceProviderDOList; } - public static SessionInfoData getSessionInfoData(String sessionId){ + /** + * Get SessionInfoData. + * + * @param sessionId + * @return SessionInfoData + */ + public static SessionInfoData getSessionInfoData(String sessionId) { SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager .getPersistenceManager(); @@ -2125,7 +2156,13 @@ public static SessionInfoData getSessionInfoData(String sessionId){ return sessionInfoData; } - public static String getSessionIndex(String sessionId){ + /** + * Get Session Index. + * + * @param sessionId + * @return Session Index + */ + public static String getSessionIndex(String sessionId) { SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager .getPersistenceManager(); From c8740779b2122049a95eb8618e754599342999f7 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Mon, 14 Jan 2019 23:13:08 +0530 Subject: [PATCH 03/16] add the implementation of signing the logout request --- .../sso/saml/builders/X509CredentialImpl.java | 169 +++++++++++++++++- .../saml/cache/SAMLSLOParticipantCache.java | 5 - .../saml/servlet/SAMLSSOProviderServlet.java | 42 +++-- .../identity/sso/saml/util/SAMLSSOUtil.java | 76 ++++---- 4 files changed, 223 insertions(+), 69 deletions(-) delete mode 100644 components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index e6e9b4874..3d26e49b1 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -18,23 +18,41 @@ package org.wso2.carbon.identity.sso.saml.builders; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.credential.CredentialContextSet; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.x509.X509Credential; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.core.util.KeyStoreManager; +import org.wso2.carbon.identity.base.IdentityException; +import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil; +import org.wso2.carbon.user.api.UserStoreException; -import javax.crypto.SecretKey; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.math.BigInteger; +import java.security.Key; import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import java.util.Collection; import java.util.Collections; +import javax.crypto.SecretKey; /** * X509Credential implementation for signature verification of self issued tokens. The key is @@ -43,8 +61,153 @@ public class X509CredentialImpl implements X509Credential { private PublicKey publicKey = null; + private PrivateKey privateKey = null; private X509Certificate signingCert = null; + private static KeyStore superTenantSignKeyStore = null; + + private static Log log = LogFactory.getLog(X509CredentialImpl.class); + + public static final String SECURITY_SAML_SIGN_KEY_STORE_LOCATION = "Security.SAMLSignKeyStore.Location"; + public static final String SECURITY_SAML_SIGN_KEY_STORE_TYPE = "Security.SAMLSignKeyStore.Type"; + public static final String SECURITY_SAML_SIGN_KEY_STORE_PASSWORD = "Security.SAMLSignKeyStore.Password"; + public static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS = "Security.SAMLSignKeyStore.KeyAlias"; + public static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD = "Security.SAMLSignKeyStore.KeyPassword"; + + /** + * Instantiates X509Credential. + * This credential object will hold the private key, public key and the cert for the respective tenant domain. + * + * @param tenantDomain tenant domain + */ + public X509CredentialImpl(String tenantDomain) throws IdentityException { + + X509Certificate cert = null; + int tenantId = 0; + + try { + tenantId = SAMLSSOUtil.getRealmService().getTenantManager().getTenantId(tenantDomain); + } catch (UserStoreException e) { + throw new IdentityException("Exception occurred while retrieving Tenant ID from tenant domain " + + tenantDomain, e); + } + + KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); + PrivateKey key = null; + + try { + // Get the private key and the cert for the respective tenant domain. + if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + // derive key store name. + String ksName = tenantDomain.trim().replace(".", "-"); + // derive JKS name. + String jksName = ksName + ".jks"; + key = (PrivateKey) keyStoreManager.getPrivateKey(jksName, tenantDomain); + cert = (X509Certificate) keyStoreManager.getKeyStore(jksName) + .getCertificate(tenantDomain); + } else { + if (isSignKeyStoreConfigured()) { + if (log.isDebugEnabled()) { + log.debug("Initializing Key Data for super tenant using separate sign key store."); + } + + try { + if (superTenantSignKeyStore == null) { + String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_LOCATION); + try (FileInputStream is = new FileInputStream(keyStoreLocation)) { + String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_TYPE); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + + char[] keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_PASSWORD).toCharArray(); + keyStore.load(is, keyStorePassword); + + superTenantSignKeyStore = keyStore; + } catch (FileNotFoundException e) { + throw new IdentityException("Unable to locate keystore.", e); + } catch (IOException e) { + throw new IdentityException("Unable to read keystore.", e); + } catch (CertificateException e) { + throw new IdentityException("Unable to read certificate.", e); + } + } + + String keyAlias = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS); + char[] keyPassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD).toCharArray(); + Key privateKey = superTenantSignKeyStore.getKey(keyAlias, keyPassword); + + Certificate publicKey = superTenantSignKeyStore.getCertificate(keyAlias); + + if (privateKey instanceof PrivateKey) { + key = (PrivateKey) privateKey; + } else { + throw new IdentityException("Configured signing KeyStore private key is invalid."); + } + + if (publicKey instanceof X509Certificate) { + cert = (X509Certificate) publicKey; + } else { + throw new IdentityException("Configured signing KeyStore public key is invalid."); + } + + } catch (NoSuchAlgorithmException e) { + throw new IdentityException("Unable to load algorithm", e); + } catch (UnrecoverableKeyException e) { + throw new IdentityException("Unable to load key", e); + } catch (KeyStoreException e) { + throw new IdentityException("Unable to load keystore", e); + } + } else { + key = keyStoreManager.getDefaultPrivateKey(); + cert = keyStoreManager.getDefaultPrimaryCertificate(); + } + } + } catch (Exception e) { + throw new IdentityException("Error retrieving private key and the certificate for tenant " + + tenantDomain, e); + } + + if (key == null) { + throw new IdentityException("Cannot find the private key for tenant " + tenantDomain); + } + + this.privateKey = key; + + if (cert == null) { + throw new IdentityException("Cannot find the certificate."); + } + + signingCert = cert; + publicKey = cert.getPublicKey(); + } + + /** + * Check whether separate configurations for sign KeyStore available. + * + * @return true if necessary configurations are defined for sign KeyStore; false otherwise. + */ + private boolean isSignKeyStoreConfigured() { + + String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_LOCATION); + String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_TYPE); + String keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_PASSWORD); + String keyAlias = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS); + String keyPassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD); + + return StringUtils.isNotBlank(keyStoreLocation) && StringUtils.isNotBlank(keyStoreType) + && StringUtils.isNotBlank(keyStorePassword) && StringUtils.isNotBlank(keyAlias) + && StringUtils.isNotBlank(keyPassword); + } + /** * The key is constructed form modulus and exponent. * @@ -122,8 +285,8 @@ public Collection getKeyNames() { @Override public PrivateKey getPrivateKey() { - // TODO Auto-generated method stub - return null; + + return privateKey; } @Override diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java deleted file mode 100644 index 9eb9d006d..000000000 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/cache/SAMLSLOParticipantCache.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.wso2.carbon.identity.sso.saml.cache; - -public class SAMLSLOParticipantCache { - -} diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 4afea212a..5c7bae5df 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -50,6 +50,7 @@ import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.SAMLSSOService; import org.wso2.carbon.identity.sso.saml.SSOServiceProviderConfigManager; +import org.wso2.carbon.identity.sso.saml.builders.X509CredentialImpl; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCache; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheEntry; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheKey; @@ -1059,7 +1060,7 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS Boolean isFrontChannelSLOEnabled = true; //check entry.isFrontChannelSLOEnabled() - if (isFrontChannelSLOEnabled){ + if (isFrontChannelSLOEnabled) { doFrontChannelSLO(response, entry, sessionId, sessionDTO.getIssuer()); break; } else { @@ -1581,45 +1582,48 @@ private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProvi response.sendRedirect(redirectUrl); } + /** + * This method is used to prepare a SAML request message as a HTTP query string for HTTP Redirect binding. + * + * @param logoutRequest Logout Request + * @param serviceProviderDO SAMLSSOServiceProviderDO + * @return Redirect URL + * @throws IdentityException + */ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, - SAMLSSOServiceProviderDO serviceProviderDO) + SAMLSSOServiceProviderDO serviceProviderDO) throws IdentityException { - // Convert the SAML logout request to a string. + // Convert the SAML logout request object to a string. String logoutRequestString = (SAMLSSOUtil.marshall(logoutRequest)). replaceAll(SAMLSSOConstants.XML_TAG_REGEX, "").trim(); StringBuilder httpQueryString = null; String signatureAlgorithmUri = serviceProviderDO.getSigningAlgorithmUri(); + + String tenantDomain = serviceProviderDO.getTenantDomain(); + if (StringUtils.isEmpty(tenantDomain) || "null".equals(tenantDomain)) { + tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; + } + try { httpQueryString = new StringBuilder(SAMLSSOConstants.SAML_REQUEST + "=" + URLEncoder.encode(SAMLSSOUtil.compressResponse(logoutRequestString), "UTF-8")); httpQueryString.append("&" + SAMLSSOConstants.SIG_ALG + "=" + URLEncoder.encode(signatureAlgorithmUri, "UTF-8")); + SAMLSSOUtil.addSignatureToHTTPQueryString(httpQueryString, signatureAlgorithmUri, + new X509CredentialImpl(tenantDomain)); } catch (UnsupportedEncodingException e) { log.error("Error while encoding the message.", e); } catch (IOException e) { log.error("Error in compressing the SAML request message.", e); } - // to be revised - setting signature. - try { - PrivilegedCarbonContext.startTenantFlow(); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(serviceProviderDO.getTenantDomain()); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); - byte[] signedString = SAMLSSOUtil.setSignature(httpQueryString.toString()); - - String signature = Base64.getEncoder().encodeToString(signedString); - - httpQueryString.append("&" + SAMLSSOConstants.SIGNATURE + "=" + URLEncoder.encode(signature, "UTF-8")); - } catch (UnsupportedEncodingException e) { - log.error("Error in encoding the signature", e); - } finally { - PrivilegedCarbonContext.endTenantFlow(); + String redirectUrl = null; + if (httpQueryString != null) { + redirectUrl = logoutRequest.getDestination() + "?" + httpQueryString.toString(); } - String redirectUrl = logoutRequest.getDestination() + "?" + httpQueryString.toString(); - return redirectUrl; } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 393c56c55..68e193602 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -54,6 +54,7 @@ import org.opensaml.xml.io.Unmarshaller; import org.opensaml.xml.io.UnmarshallerFactory; import org.opensaml.xml.security.SecurityException; +import org.opensaml.xml.security.SigningUtil; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.signature.SignableXMLObject; import org.opensaml.xml.util.Base64; @@ -65,7 +66,6 @@ import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; -import org.wso2.carbon.base.ServerConfiguration; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.RegistryType; import org.wso2.carbon.core.util.KeyStoreManager; @@ -110,11 +110,9 @@ import org.wso2.carbon.identity.sso.saml.validators.SSOAuthnRequestValidator; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; -import org.wso2.carbon.registry.api.RegistryException; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.registry.core.service.TenantRegistryLoader; -import org.wso2.carbon.security.keystore.KeyStoreAdmin; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.user.core.service.RealmService; @@ -132,13 +130,10 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.Signature; -import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; @@ -2078,41 +2073,6 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP return logoutReq; } - // this method is to be removed. - public static byte[] setSignature(String queryString) throws IdentityException { - - byte[] signedString = null; - - String keyAlias = ServerConfiguration.getInstance().getFirstProperty(SECURITY_KEY_STORE_KEY_ALIAS); - if (StringUtils.isBlank(keyAlias)) { - throw new IdentityException("Invalid file configurations. The key alias is not found."); - } - - KeyStoreAdmin keyAdmin = null; - try { - keyAdmin = new KeyStoreAdmin(MultitenantConstants.SUPER_TENANT_ID, - SAMLSSOUtil.getRegistryService().getGovernanceSystemRegistry()); - } catch (RegistryException e) { - log.error("Error in setting keystore admin", e); - } - - try { - PrivateKey privateKey = null; - if (keyAdmin != null) { - privateKey = (PrivateKey) keyAdmin.getPrivateKey(keyAlias, true); - } - - Signature instance = Signature.getInstance("SHA1withRSA"); - instance.initSign(privateKey); - instance.update(queryString.getBytes()); - signedString = instance.sign(); - } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) { - log.error("Error in setting signature.", e); - } - - return signedString; - } - /** * Get other session participants for SLO except for the original issuer. * @@ -2170,4 +2130,36 @@ public static String getSessionIndex(String sessionId) { return sessionIndex; } + + /** + * Construct signature for http redirect. + * + * @param httpQueryString http query string + * @param signatureAlgorithmURI signature algorithm URI + * @param credential X509Credential + */ + public static void addSignatureToHTTPQueryString(StringBuilder httpQueryString, + String signatureAlgorithmURI, X509Credential credential) { + + try { + byte[] rawSignature = SigningUtil.signWithURI(credential, signatureAlgorithmURI, + httpQueryString.toString().getBytes("UTF-8")); + + String base64Signature = Base64.encodeBytes(rawSignature, Base64.DONT_BREAK_LINES); + + if (log.isDebugEnabled()) { + log.debug("Generated digital signature value (base64-encoded) {} " + base64Signature); + } + + httpQueryString.append("&" + SAMLSSOConstants.SIGNATURE + "=" + + URLEncoder.encode(base64Signature, "UTF-8").trim()); + + } catch (org.opensaml.xml.security.SecurityException e) { + log.error("Unable to sign query string", e); + } catch (UnsupportedEncodingException e) { + // UTF-8 encoding is required to be supported by all JVMs. + log.error("Error while adding signature to HTTP query string", e); + } + } + } From 090a7b663844353d2d931c3ddf1474cd7048ee4f Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Mon, 14 Jan 2019 23:17:35 +0530 Subject: [PATCH 04/16] add Logout Response validation --- .../saml/servlet/SAMLSSOProviderServlet.java | 26 ++++------ .../identity/sso/saml/util/SAMLSSOUtil.java | 52 +++++++++++++++++++ 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 5c7bae5df..f20f8f6ca 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -290,29 +290,21 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo } } - private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse) throws IdentityException { + private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse) + throws IdentityException { XMLObject response; response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); -// String sessionIndex = SAMLSLOParticipantCache.getInstance().getValueFromCache(inResponseToId); -// -// SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager -// .getPersistenceManager(); -// SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); -// Map sessionsList = sessionInfoData.getServiceProviderList(); -// -// sessionsList.remove(((LogoutResponseImpl) response).getIssuer().getValue()); -// -// Boolean hasMoreFrontChannelSPs = false; -// -// if (hasMoreFrontChannelSPs){ -// // doFrontChannelSLO() -// } else { -// // send response to initial issuer -// } + String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); + String tenantDomain = SAMLSSOUtil.getTenantDomainFromThreadLocal(); + SAMLSSOServiceProviderDO samlssoServiceProviderDO = SAMLSSOUtil.getSPConfig(tenantDomain, + logoutResponseIssuer); + + boolean isSuccesfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, + samlssoServiceProviderDO.getCertAlias(), samlssoServiceProviderDO.getTenantDomain()); } /** diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 68e193602..e30f1bd68 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -2162,4 +2162,56 @@ public static void addSignatureToHTTPQueryString(StringBuilder httpQueryString, } } + /** + * Validate whether the LogoutResponse is a success. + * + * @param response Logout Response object. + * @return True if Logout response state success. + * @throws IOException Stream error. + * @throws IdentityException Decoding error. + */ + public static boolean validateLogoutResponse(XMLObject response, String certificateAlias, String tenantDomain) + throws IdentityException { + + // This should be a SAML logout response. + if (response instanceof LogoutResponse) { + + LogoutResponse logoutResponse = (LogoutResponse) response; + if (logoutResponse.getIssuer() == null || logoutResponse.getStatus() == null || logoutResponse + .getStatus().getStatusCode() == null) { + if (log.isDebugEnabled()) { + log.debug("Logout response validation failed due to one of given values are null. " + + "Issuer: " + logoutResponse.getIssuer() + + " Status: " + logoutResponse.getStatus() + + " Status code: " + (logoutResponse.getStatus() != null ? logoutResponse.getStatus() + .getStatusCode() : null)); + } + return false; + } + + if (log.isDebugEnabled()) { + log.debug("Logout response received for issuer: " + logoutResponse.getIssuer() + .getValue() + " for tenant domain: " + tenantDomain); + } + + boolean isSignatureValid = true; + + // Certificate alias will be null if signature validation is disabled in the service provider side. + if (certificateAlias != null && logoutResponse.isSigned()) { + isSignatureValid = SAMLSSOUtil.validateXMLSignature(logoutResponse, certificateAlias, tenantDomain); + if (log.isDebugEnabled()) { + log.debug("Signature validation result for logout response for issuer: " + + logoutResponse.getIssuer().getValue() + " in tenant domain: " + tenantDomain + " is: " + + isSignatureValid); + } + } + if (SAMLSSOConstants.StatusCodes.SUCCESS_CODE.equals(logoutResponse.getStatus().getStatusCode() + .getValue()) && isSignatureValid) { + return true; + } + } + + return false; + } + } From ff720e6fde499a64da41b692f6f36bd103add317 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Tue, 15 Jan 2019 20:35:40 +0530 Subject: [PATCH 05/16] refactor X509CredentialImpl class --- .../sso/saml/builders/X509CredentialImpl.java | 189 ++++++++++-------- 1 file changed, 108 insertions(+), 81 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index 3d26e49b1..b719e6ac2 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -33,7 +33,6 @@ import org.wso2.carbon.user.api.UserStoreException; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger; import java.security.Key; @@ -82,9 +81,7 @@ public class X509CredentialImpl implements X509Credential { */ public X509CredentialImpl(String tenantDomain) throws IdentityException { - X509Certificate cert = null; int tenantId = 0; - try { tenantId = SAMLSSOUtil.getRealmService().getTenantManager().getTenantId(tenantDomain); } catch (UserStoreException e) { @@ -93,96 +90,126 @@ public X509CredentialImpl(String tenantDomain) throws IdentityException { } KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); - PrivateKey key = null; - try { - // Get the private key and the cert for the respective tenant domain. - if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { - // derive key store name. - String ksName = tenantDomain.trim().replace(".", "-"); - // derive JKS name. - String jksName = ksName + ".jks"; - key = (PrivateKey) keyStoreManager.getPrivateKey(jksName, tenantDomain); - cert = (X509Certificate) keyStoreManager.getKeyStore(jksName) - .getCertificate(tenantDomain); + // Get the private key and the cert for the respective tenant domain. + if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + initializeCredentialForTenant(tenantDomain, keyStoreManager); + } else { + if (isSignKeyStoreConfigured()) { + initializeCredentialForSuperTenantFromSignKeyStore(); } else { - if (isSignKeyStoreConfigured()) { - if (log.isDebugEnabled()) { - log.debug("Initializing Key Data for super tenant using separate sign key store."); - } - - try { - if (superTenantSignKeyStore == null) { - String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_LOCATION); - try (FileInputStream is = new FileInputStream(keyStoreLocation)) { - String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_TYPE); - KeyStore keyStore = KeyStore.getInstance(keyStoreType); - - char[] keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_PASSWORD).toCharArray(); - keyStore.load(is, keyStorePassword); - - superTenantSignKeyStore = keyStore; - } catch (FileNotFoundException e) { - throw new IdentityException("Unable to locate keystore.", e); - } catch (IOException e) { - throw new IdentityException("Unable to read keystore.", e); - } catch (CertificateException e) { - throw new IdentityException("Unable to read certificate.", e); - } - } - - String keyAlias = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS); - char[] keyPassword = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD).toCharArray(); - Key privateKey = superTenantSignKeyStore.getKey(keyAlias, keyPassword); - - Certificate publicKey = superTenantSignKeyStore.getCertificate(keyAlias); - - if (privateKey instanceof PrivateKey) { - key = (PrivateKey) privateKey; - } else { - throw new IdentityException("Configured signing KeyStore private key is invalid."); - } - - if (publicKey instanceof X509Certificate) { - cert = (X509Certificate) publicKey; - } else { - throw new IdentityException("Configured signing KeyStore public key is invalid."); - } - - } catch (NoSuchAlgorithmException e) { - throw new IdentityException("Unable to load algorithm", e); - } catch (UnrecoverableKeyException e) { - throw new IdentityException("Unable to load key", e); - } catch (KeyStoreException e) { - throw new IdentityException("Unable to load keystore", e); - } - } else { - key = keyStoreManager.getDefaultPrivateKey(); - cert = keyStoreManager.getDefaultPrimaryCertificate(); - } + initializeCredentialForSuperTenantFromDefaultKeyStore(keyStoreManager); } + } + + if (privateKey == null) { + throw new IdentityException("Cannot find the private key for tenant " + tenantDomain); + } + + if (signingCert == null) { + throw new IdentityException("Cannot find the certificate."); + } + + publicKey = signingCert.getPublicKey(); + } + + /** + * Set private key and X509Certificate from the default KeyStore. + * + * @param keyStoreManager keyStore Manager + * @throws IdentityException + */ + private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManager keyStoreManager) + throws IdentityException { + + try { + privateKey = keyStoreManager.getDefaultPrivateKey(); + signingCert = keyStoreManager.getDefaultPrimaryCertificate(); } catch (Exception e) { throw new IdentityException("Error retrieving private key and the certificate for tenant " + - tenantDomain, e); + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, e); } + } - if (key == null) { - throw new IdentityException("Cannot find the private key for tenant " + tenantDomain); + /** + * Set private key and X509Certificate from the Sign KeyStore which is defined under Security.SAMLSignKeyStore + * in carbon.xml. + * + * @throws IdentityException + */ + private void initializeCredentialForSuperTenantFromSignKeyStore() throws IdentityException { + + if (log.isDebugEnabled()) { + log.debug("Initializing Key Data for super tenant using separate sign key store."); } - this.privateKey = key; + try { + if (superTenantSignKeyStore == null) { + String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_LOCATION); + try (FileInputStream is = new FileInputStream(keyStoreLocation)) { + String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_TYPE); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + char[] keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_PASSWORD).toCharArray(); + keyStore.load(is, keyStorePassword); + superTenantSignKeyStore = keyStore; + } catch (IOException e) { + throw new IdentityException("Unable to read keystore.", e); + } catch (CertificateException e) { + throw new IdentityException("Unable to read certificate.", e); + } + } + + String keyAlias = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS); + char[] keyPassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD).toCharArray(); + Key privateKey = superTenantSignKeyStore.getKey(keyAlias, keyPassword); + Certificate publicKey = superTenantSignKeyStore.getCertificate(keyAlias); - if (cert == null) { - throw new IdentityException("Cannot find the certificate."); + if (privateKey instanceof PrivateKey) { + this.privateKey = (PrivateKey) privateKey; + } else { + throw new IdentityException("Configured signing KeyStore private key is invalid."); + } + + if (publicKey instanceof X509Certificate) { + this.signingCert = (X509Certificate) publicKey; + } else { + throw new IdentityException("Configured signing KeyStore X509Certificate is invalid."); + } + } catch (NoSuchAlgorithmException e) { + throw new IdentityException("Unable to load algorithm", e); + } catch (UnrecoverableKeyException e) { + throw new IdentityException("Unable to load key", e); + } catch (KeyStoreException e) { + throw new IdentityException("Unable to load keystore", e); } + } - signingCert = cert; - publicKey = cert.getPublicKey(); + /** + * Set private key and X509Certificate from the tenant KeyStore. + * + * @param tenantDomain tenant domain + * @param keyStoreManager KeyStore Manager + * @throws IdentityException + */ + private void initializeCredentialForTenant(String tenantDomain, KeyStoreManager keyStoreManager) + throws IdentityException { + + try { + // derive key store name. + String ksName = tenantDomain.trim().replace(".", "-"); + // derive JKS name. + String jksName = ksName + ".jks"; + privateKey = (PrivateKey) keyStoreManager.getPrivateKey(jksName, tenantDomain); + signingCert = (X509Certificate) keyStoreManager.getKeyStore(jksName).getCertificate(tenantDomain); + } catch (Exception e) { + throw new IdentityException("Error retrieving private key and the certificate for tenant " + + tenantDomain, e); + } } /** From 912d3c74d7453306c6999c8caedf2eae7b6b0947 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Thu, 17 Jan 2019 11:04:24 +0530 Subject: [PATCH 06/16] apply changes for PR(#205) comments --- .../sso/saml/builders/X509CredentialImpl.java | 2 ++ .../identity/sso/saml/util/SAMLSSOUtil.java | 33 +++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index b719e6ac2..953426ecc 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -125,6 +125,7 @@ private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManag try { privateKey = keyStoreManager.getDefaultPrivateKey(); signingCert = keyStoreManager.getDefaultPrimaryCertificate(); + // This Exception is thrown from the KeyStoreManager. } catch (Exception e) { throw new IdentityException("Error retrieving private key and the certificate for tenant " + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, e); @@ -206,6 +207,7 @@ private void initializeCredentialForTenant(String tenantDomain, KeyStoreManager String jksName = ksName + ".jks"; privateKey = (PrivateKey) keyStoreManager.getPrivateKey(jksName, tenantDomain); signingCert = (X509Certificate) keyStoreManager.getKeyStore(jksName).getCertificate(tenantDomain); + // This Exception is thrown from the KeyStoreManager. } catch (Exception e) { throw new IdentityException("Error retrieving private key and the certificate for tenant " + tenantDomain, e); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index e30f1bd68..b390ee105 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -162,8 +162,6 @@ public class SAMLSSOUtil { private static final String SECURITY_MANAGER_PROPERTY = Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY; private static final int ENTITY_EXPANSION_LIMIT = 0; - public static final String SECURITY_KEY_STORE_KEY_ALIAS = "Security.KeyStore.KeyAlias"; - private static final int FIVE_MINUTES = 5 * 60 * 1000; static { for (char c = 'a'; c <= 'z'; c++) @@ -2031,12 +2029,6 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP log.debug("Destination of the logout request is set to the " + "SLO request URL of the SP: " + serviceProviderDO.getSloRequestURL()); } - } else if (StringUtils.isNotBlank(serviceProviderDO.getSloResponseURL())) { - logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloResponseURL()); - if (log.isDebugEnabled()) { - log.debug("Destination of the logout request is set to the " + - "SLO response URL of the SP: " + serviceProviderDO.getSloResponseURL()); - } } else { logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getAssertionConsumerUrl()); if (log.isDebugEnabled()) { @@ -2057,7 +2049,8 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP DateTime issueInstant = new DateTime(); logoutReq.setIssueInstant(issueInstant); logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(serviceProviderDO.getTenantDomain())); - logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + FIVE_MINUTES)); + logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + + SAMLSSOUtil.getSAMLResponseValidityPeriod() * 60 * 1000L)); NameID nameId = new NameIDBuilder().buildObject(); nameId.setFormat(serviceProviderDO.getNameIDFormat()); @@ -2083,17 +2076,23 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP public static List getOtherSessionParticipants(String sessionId, String issuer) { SessionInfoData sessionInfoData = getSessionInfoData(sessionId); - Map sessionsList = sessionInfoData.getServiceProviderList(); + List samlssoServiceProviderDOList = null; + + if (sessionInfoData != null) { + Map sessionsList = sessionInfoData.getServiceProviderList(); + samlssoServiceProviderDOList = new ArrayList<>(); - List samlssoServiceProviderDOList = new ArrayList<>(); + for (Map.Entry entry : sessionsList.entrySet()) { + SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); - for (Map.Entry entry : sessionsList.entrySet()) { - String key = entry.getKey(); - SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); + // Logout request should not be created for the issuer. + if (entry.getKey().equals(issuer)) { + continue; + } - // Logout request should not be created for the issuer. - if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout()) { - samlssoServiceProviderDOList.add(serviceProviderDO); + if (serviceProviderDO.isDoSingleLogout()) { + samlssoServiceProviderDOList.add(serviceProviderDO); + } } } From d7e9ecf2a5efc672f9d649c600f3c5e67cf95a68 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Fri, 18 Jan 2019 02:49:42 +0530 Subject: [PATCH 07/16] add implementation of sending logout response back to initiator --- .../saml/FrontChannelSLOParticipantInfo.java | 83 ++++++ .../saml/FrontChannelSLOParticipantStore.java | 61 ++++ .../saml/servlet/SAMLSSOProviderServlet.java | 272 +++++++++++++++--- .../identity/sso/saml/util/SAMLSSOUtil.java | 23 +- 4 files changed, 384 insertions(+), 55 deletions(-) create mode 100644 components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java create mode 100644 components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java new file mode 100644 index 000000000..668a4d053 --- /dev/null +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wso2.carbon.identity.sso.saml; + +import org.wso2.carbon.identity.sso.saml.cache.CacheEntry; + +/** + * Information of front-channel enabled session participants in a single logout. + */ +public class FrontChannelSLOParticipantInfo extends CacheEntry { + + private String originalIssuerLogoutRequestId; + private String originalIssuer; + private String logoutRequestIssuingSP; + private String sessionIndex; + + public FrontChannelSLOParticipantInfo() { + + } + + public FrontChannelSLOParticipantInfo(String originalIssuerLogoutRequestId, String originalIssuer, + String logoutRequestIssuingSP, String sessionIndex) { + + this.originalIssuerLogoutRequestId = originalIssuerLogoutRequestId; + this.originalIssuer = originalIssuer; + this.logoutRequestIssuingSP = logoutRequestIssuingSP; + this.sessionIndex = sessionIndex; + } + + public String getOriginalIssuerLogoutRequestId() { + + return originalIssuerLogoutRequestId; + } + + public void setOriginalIssuerLogoutRequestId(String originalIssuerLogoutRequestId) { + + this.originalIssuerLogoutRequestId = originalIssuerLogoutRequestId; + } + + public String getLogoutRequestIssuingSP() { + + return logoutRequestIssuingSP; + } + + public void setLogoutRequestIssuingSP(String logoutRequestIssuingSP) { + + this.logoutRequestIssuingSP = logoutRequestIssuingSP; + } + + public String getSessionIndex() { + + return sessionIndex; + } + + public void setSessionIndex(String sessionIndex) { + + this.sessionIndex = sessionIndex; + } + + public String getOriginalIssuer() { + + return originalIssuer; + } + + public void setOriginalIssuer(String originalIssuer) { + + this.originalIssuer = originalIssuer; + } +} diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java new file mode 100644 index 000000000..ccbd87e7c --- /dev/null +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.wso2.carbon.identity.sso.saml; + +import org.wso2.carbon.identity.application.authentication.framework.store.SessionDataStore; +import org.wso2.carbon.identity.application.common.cache.BaseCache; + +/** + * This is to store information of front-channel enabled session participants in a single logout. + */ +public class FrontChannelSLOParticipantStore extends BaseCache { + + private static final String CACHE_NAME = "FrontChannelSLOParticipantStore"; + private static volatile FrontChannelSLOParticipantStore instance = new FrontChannelSLOParticipantStore(); + + private FrontChannelSLOParticipantStore() { + + super(CACHE_NAME); + } + + public static FrontChannelSLOParticipantStore getInstance() { + + return instance; + } + + public void addToCache(String key, FrontChannelSLOParticipantInfo entry) { + + super.addToCache(key, entry); + SessionDataStore.getInstance().storeSessionData(key, CACHE_NAME, entry); + } + + public FrontChannelSLOParticipantInfo getValueFromCache(String key) { + + FrontChannelSLOParticipantInfo cacheEntry = super.getValueFromCache(key); + if (cacheEntry == null) { + cacheEntry = (FrontChannelSLOParticipantInfo) SessionDataStore.getInstance(). + getSessionData(key, CACHE_NAME); + } + return cacheEntry; + } + + public void clearCacheEntry(String key) { + + super.clearCacheEntry(key); + SessionDataStore.getInstance().clearSessionData(key, CACHE_NAME); + } +} diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index f20f8f6ca..4b7cf1f3e 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -21,6 +21,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.opensaml.saml2.core.LogoutRequest; +import org.opensaml.saml2.core.LogoutResponse; +import org.opensaml.saml2.core.impl.LogoutRequestImpl; import org.opensaml.saml2.core.impl.LogoutResponseImpl; import org.opensaml.xml.XMLObject; import org.owasp.encoder.Encode; @@ -46,11 +48,17 @@ import org.wso2.carbon.identity.core.model.SAMLSSOServiceProviderDO; import org.wso2.carbon.identity.core.persistence.IdentityPersistenceManager; import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.sso.saml.FrontChannelSLOParticipantInfo; +import org.wso2.carbon.identity.sso.saml.FrontChannelSLOParticipantStore; import org.wso2.carbon.identity.sso.saml.SAMLECPConstants; import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.SAMLSSOService; import org.wso2.carbon.identity.sso.saml.SSOServiceProviderConfigManager; +import org.wso2.carbon.identity.sso.saml.builders.SingleLogoutMessageBuilder; import org.wso2.carbon.identity.sso.saml.builders.X509CredentialImpl; +import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOParticipantCache; +import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOParticipantCacheEntry; +import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOParticipantCacheKey; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCache; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheEntry; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheKey; @@ -225,7 +233,7 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo String defaultLogoutLocation = SAMLSSOUtil.getDefaultLogoutEndpoint(); resp.sendRedirect(FrameworkUtils.getRedirectURL(defaultLogoutLocation + queryParams, req)); } else if (sessionDTO.isLogoutReq()) { - handleLogoutResponseFromFramework(req, resp, sessionDTO, sessionId); + handleLogoutResponseFromFramework(req, resp, sessionDTO); } else { handleAuthenticationReponseFromFramework(req, resp, sessionId, sessionDTO); } @@ -246,20 +254,9 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo } else if (samlRequest != null) {// SAMLRequest received. SP initiated SSO handleSPInitSSO(req, resp, queryString, relayState, authMode, samlRequest, sessionId, isPost); } else if (samlResponse != null) {// SAMLResponse received. - handleSAMLResponse(req, resp, samlResponse); + handleSAMLResponse(req, resp, samlResponse, sessionId); } else { - log.debug("Invalid request message or single logout message "); - - if (sessionId == null) { - String errorResp = SAMLSSOUtil.buildErrorResponse( - SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, - "Invalid request message", null); - sendNotification(errorResp, SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS, - SAMLSSOConstants.Notification.INVALID_MESSAGE_MESSAGE, null, req, resp); - } else { - // Non-SAML request are assumed to be logout requests - sendToFrameworkForLogout(req, resp, null, null, sessionId, true, false); - } + handleInvalidRequestMessage(req, resp, sessionId); } } catch (UserStoreException e) { if (log.isDebugEnabled()) { @@ -290,21 +287,127 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo } } - private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse) + private void handleInvalidRequestMessage(HttpServletRequest req, HttpServletResponse resp, String sessionId) + throws IOException, IdentityException, ServletException { + + log.debug("Invalid request message or single logout message "); + + if (sessionId == null) { + String errorResp = SAMLSSOUtil.buildErrorResponse( + SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, + "Invalid request message", null); + sendNotification(errorResp, SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS, + SAMLSSOConstants.Notification.INVALID_MESSAGE_MESSAGE, null, req, resp); + } else { + // Non-SAML request are assumed to be logout requests + sendToFrameworkForLogout(req, resp, null, null, sessionId, true, + false); + } + } + + private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse, + String sessionId) + throws IdentityException, IOException, ServletException { + + XMLObject response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); + + if (!(response instanceof LogoutResponseImpl)) { + handleInvalidRequestMessage(req, resp, sessionId); + } else { + String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); + String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); + String tenantDomain = SAMLSSOUtil.getTenantDomainFromThreadLocal(); + SAMLSSOServiceProviderDO responseIssuerSP = SAMLSSOUtil.getSPConfig(tenantDomain, logoutResponseIssuer); + + boolean isSuccesfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, responseIssuerSP.getCertAlias(), + responseIssuerSP.getTenantDomain()); + + if (!isSuccesfullyLogout) { + // TODO : If the response is invalid, redirect the SP to an error page. + } else { + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = + getFrontChannelSLOParticipantInfo(inResponseToId); + + removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); + + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO( + frontChannelSLOParticipantInfo.getSessionIndex(), + frontChannelSLOParticipantInfo.getOriginalIssuer()); + SAMLSSOServiceProviderDO originalIssuer = + SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), + frontChannelSLOParticipantInfo.getOriginalIssuer()); + + if (samlssoServiceProviderDOList.isEmpty()) { + LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); + + // TODO : Get relay state, check isIdPInitSLO() + // Sending LogoutResponse back to the original issuer. + sendResponse(req, resp, null, SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse)), + logoutResponse.getDestination(), null, null, + SAMLSSOUtil.getTenantDomainFromThreadLocal()); + } else { + doFrontChannelSLO(resp, samlssoServiceProviderDOList.get(0), + frontChannelSLOParticipantInfo.getSessionIndex(), originalIssuer, + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId()); + } + } + } + } + + /** + * Build logout response for the original logout request issuer. + * + * @param originalIssuerLogoutRequestId Logout request id of original issuer + * @param originalIssuer Original issuer + * @return + * @throws IdentityException + */ + private LogoutResponse buildLogoutResponseForOriginalIssuer(String originalIssuerLogoutRequestId, + SAMLSSOServiceProviderDO originalIssuer) throws IdentityException { - XMLObject response; + String destination; + if (StringUtils.isNotBlank(originalIssuer.getSloResponseURL())) { + destination = originalIssuer.getSloResponseURL(); + if (log.isDebugEnabled()) { + log.debug("Destination of the logout response is set to the SLO response URL of the SP: " + + originalIssuer.getSloResponseURL()); + } + } else { + destination = originalIssuer.getAssertionConsumerUrl(); + if (log.isDebugEnabled()) { + log.debug("Destination of the logout response is set to the ACS URL of the SP: " + + originalIssuer.getAssertionConsumerUrl()); + } + } + + SingleLogoutMessageBuilder logoutMsgBuilder = new SingleLogoutMessageBuilder(); + LogoutResponse logoutResponse = logoutMsgBuilder.buildLogoutResponse( + originalIssuerLogoutRequestId, + SAMLSSOConstants.StatusCodes.SUCCESS_CODE, + null, + destination, + originalIssuer.isDoSignResponse(), + SAMLSSOUtil.getTenantDomainFromThreadLocal(), + originalIssuer.getSigningAlgorithmUri(), + originalIssuer.getDigestAlgorithmUri()); + + return logoutResponse; + } - response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); - String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); + private void removeSPFromSession(String sessionIndex, String serviceProvider) { - String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); - String tenantDomain = SAMLSSOUtil.getTenantDomainFromThreadLocal(); - SAMLSSOServiceProviderDO samlssoServiceProviderDO = SAMLSSOUtil.getSPConfig(tenantDomain, - logoutResponseIssuer); + if (sessionIndex != null) { + SAMLSSOParticipantCacheKey cacheKey = new SAMLSSOParticipantCacheKey(sessionIndex); + SAMLSSOParticipantCacheEntry cacheEntry = SAMLSSOParticipantCache.getInstance().getValueFromCache(cacheKey); - boolean isSuccesfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, - samlssoServiceProviderDO.getCertAlias(), samlssoServiceProviderDO.getTenantDomain()); + if (serviceProvider != null && cacheEntry.getSessionInfoData() != null && + cacheEntry.getSessionInfoData().getServiceProviderList() != null) { + cacheEntry.getSessionInfoData().removeServiceProvider(serviceProvider); + } + } } /** @@ -1039,28 +1142,31 @@ private void handleAuthenticationReponseFromFramework(HttpServletRequest req, Ht } private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpServletResponse response, - SAMLSSOSessionDTO sessionDTO, String sessionId) + SAMLSSOSessionDTO sessionDTO) throws ServletException, IOException, IdentityException { - List samlssoServiceProviderDOList = SAMLSSOUtil. - getOtherSessionParticipants(sessionId, sessionDTO.getIssuer()); + String sessionIndex = extractSessionIndex(request); + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer()); + SAMLSSOServiceProviderDO originalIssuer = SAMLSSOUtil.getSPConfig(sessionDTO.getTenantDomain(), + sessionDTO.getIssuer()); + String originalIssuerLogoutRequestId = extractLogoutRequestId(request); // Get the SP list and check for other session participants that have enabled single logout. - if (samlssoServiceProviderDOList != null) { - + if (samlssoServiceProviderDOList.isEmpty()) { + respondToOriginalIssuer(request, response, sessionDTO); + } else { for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { - + // TODO : UI configuration to enable Front-Channel SLO for SPs. Boolean isFrontChannelSLOEnabled = true; //check entry.isFrontChannelSLOEnabled() if (isFrontChannelSLOEnabled) { - doFrontChannelSLO(response, entry, sessionId, sessionDTO.getIssuer()); + doFrontChannelSLO(response, entry, sessionIndex, originalIssuer, originalIssuerLogoutRequestId); break; } else { // doBackChannelSLO() } } - } else { - respondToOriginalIssuer(request, response, sessionDTO); } } @@ -1560,20 +1666,99 @@ private void setSPAttributeToRequest(HttpServletRequest req, String issuer, Stri } } - private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, - String sessionId, String issuer) throws IdentityException, IOException { + private void doFrontChannelSLO(HttpServletResponse response, + SAMLSSOServiceProviderDO samlssoServiceProviderDO, String sessionIndex, + SAMLSSOServiceProviderDO originalIssuer, String originalIssuerLogoutRequestId) + throws IdentityException, IOException { - SessionInfoData sessionInfoData = SAMLSSOUtil.getSessionInfoData(sessionId); + SessionInfoData sessionInfoData = SAMLSSOUtil.getSessionInfoData(sessionIndex); String subject = sessionInfoData.getSubject(samlssoServiceProviderDO.getIssuer()); - String sessionIndex = SAMLSSOUtil.getSessionIndex(sessionId); LogoutRequest logoutRequest = SAMLSSOUtil.buildLogoutRequest(samlssoServiceProviderDO, subject, sessionIndex); + // TODO: check for the binding and filter. String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); - + storeFrontChannelSLOParticipantInfo(samlssoServiceProviderDO, originalIssuer, logoutRequest, + originalIssuerLogoutRequestId, sessionIndex); response.sendRedirect(redirectUrl); } + /** + * Stores information of front-channel session participants in single logout. + * + * @param logoutRequestIssuingSP Logout request issuing service provider + * @param originalIssuer Original issuer + * @param logoutRequest Logout request + * @param initialLogoutRequestId Logout request id of the original issuer + * @param sessionIndex Session index + */ + private void storeFrontChannelSLOParticipantInfo(SAMLSSOServiceProviderDO logoutRequestIssuingSP, + SAMLSSOServiceProviderDO originalIssuer, + LogoutRequest logoutRequest, String initialLogoutRequestId, + String sessionIndex) { + + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = + new FrontChannelSLOParticipantInfo(initialLogoutRequestId, originalIssuer.getIssuer(), + logoutRequestIssuingSP.getIssuer(), sessionIndex); + + FrontChannelSLOParticipantStore.getInstance().addToCache(logoutRequest.getID(), frontChannelSLOParticipantInfo); + } + + /** + * Extract logout request id of the original issuer logout request. + * + * @param request HttpServletRequest + * @return + * @throws IdentityException + */ + private String extractLogoutRequestId(HttpServletRequest request) throws IdentityException { + + String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); + XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + + String initialLogoutRequestId = null; + if (samlRequest instanceof LogoutRequestImpl) { + initialLogoutRequestId = ((LogoutRequestImpl) samlRequest).getID(); + } + + return initialLogoutRequestId; + } + + /** + * Extract session index from the original issuer logout request. + * + * @param request HttpServletRequest + * @return + * @throws IdentityException + */ + private String extractSessionIndex(HttpServletRequest request) throws IdentityException { + + String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); + XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + + String sessionIndex = null; + if (samlRequest instanceof LogoutRequestImpl) { + sessionIndex = ((LogoutRequestImpl) samlRequest).getSessionIndexes().size() > 0 ? + ((LogoutRequestImpl) samlRequest).getSessionIndexes().get(0).getSessionIndex() : null; + } + + return sessionIndex; + } + + /** + * Retrieves information of front-channel session participants in single logout. + * + * @param logoutRequestId Logout request id + * @return + */ + private FrontChannelSLOParticipantInfo getFrontChannelSLOParticipantInfo(String logoutRequestId) { + + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = + FrontChannelSLOParticipantStore.getInstance().getValueFromCache(logoutRequestId); + + return frontChannelSLOParticipantInfo; + } + /** * This method is used to prepare a SAML request message as a HTTP query string for HTTP Redirect binding. * @@ -1594,7 +1779,7 @@ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, String signatureAlgorithmUri = serviceProviderDO.getSigningAlgorithmUri(); String tenantDomain = serviceProviderDO.getTenantDomain(); - if (StringUtils.isEmpty(tenantDomain) || "null".equals(tenantDomain)) { + if (StringUtils.isEmpty(tenantDomain)) { tenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; } @@ -1606,15 +1791,12 @@ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, SAMLSSOUtil.addSignatureToHTTPQueryString(httpQueryString, signatureAlgorithmUri, new X509CredentialImpl(tenantDomain)); } catch (UnsupportedEncodingException e) { - log.error("Error while encoding the message.", e); + throw new IdentityException("Error while encoding the message.", e); } catch (IOException e) { - log.error("Error in compressing the SAML request message.", e); + throw new IdentityException("Error in compressing the SAML request message.", e); } - String redirectUrl = null; - if (httpQueryString != null) { - redirectUrl = logoutRequest.getDestination() + "?" + httpQueryString.toString(); - } + String redirectUrl = logoutRequest.getDestination() + "?" + httpQueryString.toString(); return redirectUrl; } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index b390ee105..49078f813 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -2067,15 +2067,19 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP } /** - * Get other session participants for SLO except for the original issuer. + * Get remaining session participants for SLO except for the original issuer. * - * @param sessionId Session Id - * @param issuer Original issuer - * @return SAMLSSOServiceProviderDO List + * @param sessionIndex Session index + * @param issuer Original issuer + * @return */ - public static List getOtherSessionParticipants(String sessionId, String issuer) { + public static List getRemainingSessionParticipantsForSLO(String sessionIndex, + String issuer) { + + SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager + .getPersistenceManager(); + SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); - SessionInfoData sessionInfoData = getSessionInfoData(sessionId); List samlssoServiceProviderDOList = null; if (sessionInfoData != null) { @@ -2102,14 +2106,13 @@ public static List getOtherSessionParticipants(String /** * Get SessionInfoData. * - * @param sessionId - * @return SessionInfoData + * @param sessionIndex Session index + * @return */ - public static SessionInfoData getSessionInfoData(String sessionId) { + public static SessionInfoData getSessionInfoData(String sessionIndex) { SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager .getPersistenceManager(); - String sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); return sessionInfoData; From 9af1ea2e070a641ae0ca5812c7778425ff079055 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Mon, 21 Jan 2019 15:02:30 +0530 Subject: [PATCH 08/16] add improvements for the implementation of responding back to initiator --- .../saml/FrontChannelSLOParticipantInfo.java | 65 ++++- .../sso/saml/builders/X509CredentialImpl.java | 43 +-- .../saml/servlet/SAMLSSOProviderServlet.java | 253 +++++++++++------- 3 files changed, 233 insertions(+), 128 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java index 668a4d053..b2f27a302 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java @@ -24,21 +24,28 @@ public class FrontChannelSLOParticipantInfo extends CacheEntry { private String originalIssuerLogoutRequestId; - private String originalIssuer; - private String logoutRequestIssuingSP; + private String originalLogoutRequestIssuer; + private String currentSLOInvokedParticipant; private String sessionIndex; + private boolean isIdPInitSLO; + private String relayState; + private String returnToURL; public FrontChannelSLOParticipantInfo() { } - public FrontChannelSLOParticipantInfo(String originalIssuerLogoutRequestId, String originalIssuer, - String logoutRequestIssuingSP, String sessionIndex) { + public FrontChannelSLOParticipantInfo(String originalIssuerLogoutRequestId, String originalLogoutRequestIssuer, + String currentSLOInvokedParticipant, String sessionIndex, + boolean isIdPInitSLO, String relayState, String returnToURL) { this.originalIssuerLogoutRequestId = originalIssuerLogoutRequestId; - this.originalIssuer = originalIssuer; - this.logoutRequestIssuingSP = logoutRequestIssuingSP; + this.originalLogoutRequestIssuer = originalLogoutRequestIssuer; + this.currentSLOInvokedParticipant = currentSLOInvokedParticipant; this.sessionIndex = sessionIndex; + this.isIdPInitSLO = isIdPInitSLO; + this.relayState = relayState; + this.returnToURL = returnToURL; } public String getOriginalIssuerLogoutRequestId() { @@ -51,14 +58,14 @@ public void setOriginalIssuerLogoutRequestId(String originalIssuerLogoutRequestI this.originalIssuerLogoutRequestId = originalIssuerLogoutRequestId; } - public String getLogoutRequestIssuingSP() { + public String getCurrentSLOInvokedParticipant() { - return logoutRequestIssuingSP; + return currentSLOInvokedParticipant; } - public void setLogoutRequestIssuingSP(String logoutRequestIssuingSP) { + public void setCurrentSLOInvokedParticipant(String currentSLOInvokedParticipant) { - this.logoutRequestIssuingSP = logoutRequestIssuingSP; + this.currentSLOInvokedParticipant = currentSLOInvokedParticipant; } public String getSessionIndex() { @@ -71,13 +78,43 @@ public void setSessionIndex(String sessionIndex) { this.sessionIndex = sessionIndex; } - public String getOriginalIssuer() { + public String getOriginalLogoutRequestIssuer() { - return originalIssuer; + return originalLogoutRequestIssuer; } - public void setOriginalIssuer(String originalIssuer) { + public void setOriginalLogoutRequestIssuer(String originalLogoutRequestIssuer) { - this.originalIssuer = originalIssuer; + this.originalLogoutRequestIssuer = originalLogoutRequestIssuer; + } + + public boolean isIdPInitSLO() { + + return isIdPInitSLO; + } + + public void setIdPInitSLO(boolean idPInitSLO) { + + isIdPInitSLO = idPInitSLO; + } + + public String getRelayState() { + + return relayState; + } + + public void setRelayState(String relayState) { + + this.relayState = relayState; + } + + public String getReturnToURL() { + + return returnToURL; + } + + public void setReturnToURL(String returnToURL) { + + this.returnToURL = returnToURL; } } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index 953426ecc..f98ac56b1 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -81,7 +81,7 @@ public class X509CredentialImpl implements X509Credential { */ public X509CredentialImpl(String tenantDomain) throws IdentityException { - int tenantId = 0; + int tenantId; try { tenantId = SAMLSSOUtil.getRealmService().getTenantManager().getTenantId(tenantDomain); } catch (UserStoreException e) { @@ -146,21 +146,7 @@ private void initializeCredentialForSuperTenantFromSignKeyStore() throws Identit try { if (superTenantSignKeyStore == null) { - String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_LOCATION); - try (FileInputStream is = new FileInputStream(keyStoreLocation)) { - String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_TYPE); - KeyStore keyStore = KeyStore.getInstance(keyStoreType); - char[] keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( - SECURITY_SAML_SIGN_KEY_STORE_PASSWORD).toCharArray(); - keyStore.load(is, keyStorePassword); - superTenantSignKeyStore = keyStore; - } catch (IOException e) { - throw new IdentityException("Unable to read keystore.", e); - } catch (CertificateException e) { - throw new IdentityException("Unable to read certificate.", e); - } + initializeSuperTenantSignKeyStore(); } String keyAlias = ServerConfiguration.getInstance().getFirstProperty( @@ -181,15 +167,30 @@ private void initializeCredentialForSuperTenantFromSignKeyStore() throws Identit } else { throw new IdentityException("Configured signing KeyStore X509Certificate is invalid."); } - } catch (NoSuchAlgorithmException e) { - throw new IdentityException("Unable to load algorithm", e); - } catch (UnrecoverableKeyException e) { - throw new IdentityException("Unable to load key", e); - } catch (KeyStoreException e) { + } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) { throw new IdentityException("Unable to load keystore", e); } } + private void initializeSuperTenantSignKeyStore() throws IdentityException { + + String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_LOCATION); + try (FileInputStream is = new FileInputStream(keyStoreLocation)) { + String keyStoreType = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_TYPE); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + char[] keyStorePassword = ServerConfiguration.getInstance().getFirstProperty( + SECURITY_SAML_SIGN_KEY_STORE_PASSWORD).toCharArray(); + keyStore.load(is, keyStorePassword); + superTenantSignKeyStore = keyStore; + } catch (IOException | CertificateException | NoSuchAlgorithmException e) { + throw new IdentityException("Unable to load keystore.", e); + } catch (KeyStoreException e) { + throw new IdentityException("Unable to get an instance of keystore.", e); + } + } + /** * Set private key and X509Certificate from the tenant KeyStore. * diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 4b7cf1f3e..d7558d82f 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -315,47 +315,87 @@ private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp handleInvalidRequestMessage(req, resp, sessionId); } else { String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); - String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); - String tenantDomain = SAMLSSOUtil.getTenantDomainFromThreadLocal(); - SAMLSSOServiceProviderDO responseIssuerSP = SAMLSSOUtil.getSPConfig(tenantDomain, logoutResponseIssuer); + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = + getFrontChannelSLOParticipantInfo(inResponseToId); - boolean isSuccesfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, responseIssuerSP.getCertAlias(), - responseIssuerSP.getTenantDomain()); - - if (!isSuccesfullyLogout) { - // TODO : If the response is invalid, redirect the SP to an error page. + if (frontChannelSLOParticipantInfo == null) { + handleInvalidRequestMessage(req, resp, sessionId); } else { - FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = - getFrontChannelSLOParticipantInfo(inResponseToId); + String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); + SAMLSSOServiceProviderDO responseIssuerSP = SAMLSSOUtil.getSPConfig( + SAMLSSOUtil.getTenantDomainFromThreadLocal(), logoutResponseIssuer); - removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); + boolean isSuccessfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, + responseIssuerSP.getCertAlias(), responseIssuerSP.getTenantDomain()); - List samlssoServiceProviderDOList = - SAMLSSOUtil.getRemainingSessionParticipantsForSLO( - frontChannelSLOParticipantInfo.getSessionIndex(), - frontChannelSLOParticipantInfo.getOriginalIssuer()); - SAMLSSOServiceProviderDO originalIssuer = - SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), - frontChannelSLOParticipantInfo.getOriginalIssuer()); - - if (samlssoServiceProviderDOList.isEmpty()) { - LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( - frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); - - // TODO : Get relay state, check isIdPInitSLO() - // Sending LogoutResponse back to the original issuer. - sendResponse(req, resp, null, SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse)), - logoutResponse.getDestination(), null, null, - SAMLSSOUtil.getTenantDomainFromThreadLocal()); + if (!isSuccessfullyLogout) { + // TODO : If the response is invalid, redirect the SP to an error page. } else { - doFrontChannelSLO(resp, samlssoServiceProviderDOList.get(0), - frontChannelSLOParticipantInfo.getSessionIndex(), originalIssuer, - frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId()); + removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); + + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO( + frontChannelSLOParticipantInfo.getSessionIndex(), + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + + if (samlssoServiceProviderDOList.isEmpty()) { + respondToOriginalLogoutRequestIssuer(req, resp, sessionId, frontChannelSLOParticipantInfo); + } else { + doFrontChannelSLO(resp, samlssoServiceProviderDOList.get(0), + frontChannelSLOParticipantInfo.getSessionIndex(), + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer(), + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), + frontChannelSLOParticipantInfo.isIdPInitSLO(), + frontChannelSLOParticipantInfo.getRelayState(), + frontChannelSLOParticipantInfo.getReturnToURL()); + } } } } } + /** + * Respond back to the original logout request issuer after handling all the front-channel enabled + * session participants. + * + * @param req HttpServlet Request + * @param resp HttpServlet Response + * @param sessionId Session id + * @param frontChannelSLOParticipantInfo FrontChannelSLOParticipantInfo + * @throws IOException + * @throws IdentityException + * @throws ServletException + */ + private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpServletResponse resp, + String sessionId, + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo) + throws IOException, IdentityException, ServletException { + + SAMLSSOServiceProviderDO originalIssuer = + SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); + + removeSessionDataFromCache(req.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); + + if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionId) == null) { + // Remove tokenId Cookie when there is no session available. + removeTokenIdCookie(req, resp); + } + + if (frontChannelSLOParticipantInfo.isIdPInitSLO()) { + // Redirecting to the return URL or IS logout page. + resp.sendRedirect(frontChannelSLOParticipantInfo.getReturnToURL()); + } else { + // Sending LogoutResponse back to the original issuer. + sendResponse(req, resp, frontChannelSLOParticipantInfo.getRelayState(), + SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse)), + logoutResponse.getDestination(), null, null, + SAMLSSOUtil.getTenantDomainFromThreadLocal()); + } + } + /** * Build logout response for the original logout request issuer. * @@ -1145,28 +1185,37 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS SAMLSSOSessionDTO sessionDTO) throws ServletException, IOException, IdentityException { - String sessionIndex = extractSessionIndex(request); - List samlssoServiceProviderDOList = - SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer()); - SAMLSSOServiceProviderDO originalIssuer = SAMLSSOUtil.getSPConfig(sessionDTO.getTenantDomain(), - sessionDTO.getIssuer()); - String originalIssuerLogoutRequestId = extractLogoutRequestId(request); + SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO(); - // Get the SP list and check for other session participants that have enabled single logout. - if (samlssoServiceProviderDOList.isEmpty()) { - respondToOriginalIssuer(request, response, sessionDTO); - } else { - for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { - // TODO : UI configuration to enable Front-Channel SLO for SPs. - Boolean isFrontChannelSLOEnabled = true; - //check entry.isFrontChannelSLOEnabled() - if (isFrontChannelSLOEnabled) { - doFrontChannelSLO(response, entry, sessionIndex, originalIssuer, originalIssuerLogoutRequestId); - break; - } else { - // doBackChannelSLO() + if (validationResponseDTO != null) { + String sessionIndex = extractSessionIndex(request); + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer()); + + // Get the SP list and check for other session participants that have enabled single logout. + if (samlssoServiceProviderDOList.isEmpty()) { + respondToOriginalIssuer(request, response, sessionDTO); + } else { + for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { + // TODO : UI configuration to enable Front-Channel SLO for SPs. + Boolean isFrontChannelSLOEnabled = true; + //check entry.isFrontChannelSLOEnabled() + if (isFrontChannelSLOEnabled) { + String originalIssuerLogoutRequestId = extractLogoutRequestId(request); + boolean isIdPInitSLO = sessionDTO.isIdPInitSLO(); + String relayState = sessionDTO.getRelayState(); + String returnToURL = validationResponseDTO.getReturnToURL(); + + doFrontChannelSLO(response, entry, sessionIndex, sessionDTO.getIssuer(), + originalIssuerLogoutRequestId, isIdPInitSLO, relayState, returnToURL); + break; + } else { + // doBackChannelSLO() + } } } + } else { + sendErrorResponseToOriginalIssuer(request, response, sessionDTO); } } @@ -1176,43 +1225,56 @@ private void respondToOriginalIssuer(HttpServletRequest request, HttpServletResp SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO(); - if (validationResponseDTO != null) { - removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); + removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); - if ( SSOSessionPersistenceManager.getSessionIndexFromCache(sessionDTO.getSessionId()) == null) { - // remove tokenId Cookie when there is no session available. - removeTokenIdCookie(request, response); - } + if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionDTO.getSessionId()) == null) { + // remove tokenId Cookie when there is no session available. + removeTokenIdCookie(request, response); + } - if (validationResponseDTO.isIdPInitSLO()) { - // redirecting to the return URL or IS logout page - response.sendRedirect(validationResponseDTO.getReturnToURL()); - } else { - // sending LogoutResponse back to the initiator - sendResponse(request, response, sessionDTO.getRelayState(), validationResponseDTO.getLogoutResponse(), - validationResponseDTO.getAssertionConsumerURL(), validationResponseDTO.getSubject(), null, - sessionDTO.getTenantDomain()); - } + if (validationResponseDTO.isIdPInitSLO()) { + // redirecting to the return URL or IS logout page. + response.sendRedirect(validationResponseDTO.getReturnToURL()); } else { - String acsUrl = sessionDTO.getAssertionConsumerURL(); - if (StringUtils.isBlank(acsUrl) && sessionDTO.getIssuer() != null) { - SAMLSSOServiceProviderDO serviceProviderDO = - SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), sessionDTO.getIssuer()); - if (serviceProviderDO != null) { - acsUrl = serviceProviderDO.getSloResponseURL(); - if (StringUtils.isBlank(acsUrl)) { - acsUrl = serviceProviderDO.getDefaultAssertionConsumerUrl(); - } + // sending LogoutResponse back to the initiator. + sendResponse(request, response, sessionDTO.getRelayState(), validationResponseDTO.getLogoutResponse(), + validationResponseDTO.getAssertionConsumerURL(), validationResponseDTO.getSubject(), + null, sessionDTO.getTenantDomain()); + } + } + + /** + * Send an error response to original issuer when the SAML request validation is invalid. + * + * @param request HttpServlet Request + * @param response HttpServlet Response + * @param sessionDTO SAMLSSOSessionDTO + * @throws IOException + * @throws IdentityException + * @throws ServletException + */ + private void sendErrorResponseToOriginalIssuer(HttpServletRequest request, HttpServletResponse response, + SAMLSSOSessionDTO sessionDTO) + throws IOException, IdentityException, ServletException { + + String acsUrl = sessionDTO.getAssertionConsumerURL(); + if (StringUtils.isBlank(acsUrl) && sessionDTO.getIssuer() != null) { + SAMLSSOServiceProviderDO serviceProviderDO = + SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), sessionDTO.getIssuer()); + if (serviceProviderDO != null) { + acsUrl = serviceProviderDO.getSloResponseURL(); + if (StringUtils.isBlank(acsUrl)) { + acsUrl = serviceProviderDO.getDefaultAssertionConsumerUrl(); } } - String errorResp = SAMLSSOUtil.buildErrorResponse( - SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, - "Invalid request", - acsUrl); - sendNotification(errorResp, SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS, - SAMLSSOConstants.Notification.INVALID_MESSAGE_MESSAGE, - acsUrl, request, response); } + String errorResp = SAMLSSOUtil.buildErrorResponse( + SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, + "Invalid request", + acsUrl); + sendNotification(errorResp, SAMLSSOConstants.Notification.INVALID_MESSAGE_STATUS, + SAMLSSOConstants.Notification.INVALID_MESSAGE_MESSAGE, + acsUrl, request, response); } private Cookie getTokenIdCookie(HttpServletRequest req) { @@ -1668,7 +1730,8 @@ private void setSPAttributeToRequest(HttpServletRequest req, String issuer, Stri private void doFrontChannelSLO(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, String sessionIndex, - SAMLSSOServiceProviderDO originalIssuer, String originalIssuerLogoutRequestId) + String originalLogoutRequestIssuer, String originalIssuerLogoutRequestId, + boolean isIdPInitSLO, String relayState, String returnToURL) throws IdentityException, IOException { SessionInfoData sessionInfoData = SAMLSSOUtil.getSessionInfoData(sessionIndex); @@ -1678,28 +1741,32 @@ private void doFrontChannelSLO(HttpServletResponse response, // TODO: check for the binding and filter. String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); - storeFrontChannelSLOParticipantInfo(samlssoServiceProviderDO, originalIssuer, logoutRequest, - originalIssuerLogoutRequestId, sessionIndex); + storeFrontChannelSLOParticipantInfo(samlssoServiceProviderDO, originalLogoutRequestIssuer, logoutRequest, + originalIssuerLogoutRequestId, sessionIndex, isIdPInitSLO, relayState, returnToURL); response.sendRedirect(redirectUrl); } /** * Stores information of front-channel session participants in single logout. * - * @param logoutRequestIssuingSP Logout request issuing service provider - * @param originalIssuer Original issuer - * @param logoutRequest Logout request - * @param initialLogoutRequestId Logout request id of the original issuer - * @param sessionIndex Session index + * @param logoutRequestIssuingSP Logout request issuing service provider + * @param originalLogoutRequestIssuer Original logout request issuer + * @param logoutRequest Logout request + * @param initialLogoutRequestId Logout request id of the original issuer + * @param sessionIndex Session index + * @param isIdPInitSLO is IdP Initiated Single logout + * @param relayState Relay State + * @param returnToURL Return to URL */ private void storeFrontChannelSLOParticipantInfo(SAMLSSOServiceProviderDO logoutRequestIssuingSP, - SAMLSSOServiceProviderDO originalIssuer, + String originalLogoutRequestIssuer, LogoutRequest logoutRequest, String initialLogoutRequestId, - String sessionIndex) { + String sessionIndex, boolean isIdPInitSLO, String relayState, + String returnToURL) { FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = - new FrontChannelSLOParticipantInfo(initialLogoutRequestId, originalIssuer.getIssuer(), - logoutRequestIssuingSP.getIssuer(), sessionIndex); + new FrontChannelSLOParticipantInfo(initialLogoutRequestId, originalLogoutRequestIssuer, + logoutRequestIssuingSP.getIssuer(), sessionIndex, isIdPInitSLO, relayState, returnToURL); FrontChannelSLOParticipantStore.getInstance().addToCache(logoutRequest.getID(), frontChannelSLOParticipantInfo); } From 2a1001ccf1059006631bc2ff598b7b9c19bffb41 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Tue, 22 Jan 2019 10:10:41 +0530 Subject: [PATCH 09/16] add implementation of POST binding --- .../saml/servlet/SAMLSSOProviderServlet.java | 96 +++++++++++++++++-- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index d7558d82f..d6ef72dc7 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -54,6 +54,7 @@ import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.SAMLSSOService; import org.wso2.carbon.identity.sso.saml.SSOServiceProviderConfigManager; +import org.wso2.carbon.identity.sso.saml.builders.SignKeyDataHolder; import org.wso2.carbon.identity.sso.saml.builders.SingleLogoutMessageBuilder; import org.wso2.carbon.identity.sso.saml.builders.X509CredentialImpl; import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOParticipantCache; @@ -254,7 +255,7 @@ private void handleRequest(HttpServletRequest req, HttpServletResponse resp, boo } else if (samlRequest != null) {// SAMLRequest received. SP initiated SSO handleSPInitSSO(req, resp, queryString, relayState, authMode, samlRequest, sessionId, isPost); } else if (samlResponse != null) {// SAMLResponse received. - handleSAMLResponse(req, resp, samlResponse, sessionId); + handleSAMLResponse(req, resp, samlResponse, sessionId, isPost); } else { handleInvalidRequestMessage(req, resp, sessionId); } @@ -306,10 +307,16 @@ private void handleInvalidRequestMessage(HttpServletRequest req, HttpServletResp } private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp, String samlResponse, - String sessionId) + String sessionId, boolean isPost) throws IdentityException, IOException, ServletException { - XMLObject response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); + XMLObject response; + + if (isPost) { + response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decodeForPost(samlResponse)); + } else { + response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); + } if (!(response instanceof LogoutResponseImpl)) { handleInvalidRequestMessage(req, resp, sessionId); @@ -1738,12 +1745,87 @@ private void doFrontChannelSLO(HttpServletResponse response, String subject = sessionInfoData.getSubject(samlssoServiceProviderDO.getIssuer()); LogoutRequest logoutRequest = SAMLSSOUtil.buildLogoutRequest(samlssoServiceProviderDO, subject, sessionIndex); - - // TODO: check for the binding and filter. - String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); storeFrontChannelSLOParticipantInfo(samlssoServiceProviderDO, originalLogoutRequestIssuer, logoutRequest, originalIssuerLogoutRequestId, sessionIndex, isIdPInitSLO, relayState, returnToURL); - response.sendRedirect(redirectUrl); + + // TODO: UI configuration to check for the binding and filter. + boolean isPostBindingEnabled = true; + if (isPostBindingEnabled) { + sendPostRequest(response, samlssoServiceProviderDO, logoutRequest, relayState); + } else { + String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); + response.sendRedirect(redirectUrl); + } + } + + /** + * This method is used to prepare and send a SAML request message with HTTP POST binding. + * + * @param response HttpServlet Response + * @param samlssoServiceProviderDO SAMLSSOServiceProviderDO + * @param logoutRequest Logout Request + * @param relayState Relay State + * @throws IdentityException + * @throws IOException + */ + private void sendPostRequest(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, + LogoutRequest logoutRequest, String relayState) + throws IdentityException, IOException { + + logoutRequest = SAMLSSOUtil.setSignature(logoutRequest, samlssoServiceProviderDO.getSigningAlgorithmUri(), + samlssoServiceProviderDO.getDigestAlgorithmUri(), new SignKeyDataHolder(null)); + String encodedRequestMessage = SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutRequest)); + + String postPageInputs = buildPostPageInputs(encodedRequestMessage, relayState); + String acUrl = logoutRequest.getDestination(); + printPostPage(response, acUrl, postPageInputs); + } + + private void printPostPage(HttpServletResponse response, String acUrl, String postPageInputs) throws IOException { + + String htmlPage = IdentitySAMLSSOServiceComponent.getSsoRedirectHtml(); + response.setContentType("text/html; charset=UTF-8"); + if (htmlPage != null) { + String pageWithAcs = htmlPage.replace("$acUrl", acUrl); + String finalPage = pageWithAcs.replace("", postPageInputs); + PrintWriter out = response.getWriter(); + out.print(finalPage); + + if (log.isDebugEnabled()) { + log.debug("HTTP-POST page: " + finalPage); + } + } else { + PrintWriter out = response.getWriter(); + out.println(""); + out.println(""); + out.println("

You are now redirected back to " + Encode.forHtmlContent(acUrl)); + out.println(" If the redirection fails, please click the post button.

"); + out.println("
"); + out.println("

"); + out.println(postPageInputs); + out.println(""); + out.println("

"); + out.println("
"); + out.println(""); + out.println(""); + out.println(""); + } + } + + private String buildPostPageInputs(String encodedRequestMessage, String relayState) { + + StringBuilder hiddenInputBuilder = new StringBuilder(); + hiddenInputBuilder.append("\n").append(""); + + if (relayState != null) { + hiddenInputBuilder.append("\n").append(""); + } + + return hiddenInputBuilder.toString(); } /** From 641331d33141a514ce33d728760a9af35db6d4ac Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Tue, 22 Jan 2019 16:55:28 +0530 Subject: [PATCH 10/16] resolve comments in PR #205 --- .../saml/FrontChannelSLOParticipantStore.java | 23 ++++++ .../identity/sso/saml/SAMLSSOService.java | 1 + .../sso/saml/builders/X509CredentialImpl.java | 16 ++-- .../saml/servlet/SAMLSSOProviderServlet.java | 80 +++++++++---------- .../identity/sso/saml/util/SAMLSSOUtil.java | 37 +++++---- 5 files changed, 90 insertions(+), 67 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java index ccbd87e7c..316adca81 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java @@ -32,17 +32,35 @@ private FrontChannelSLOParticipantStore() { super(CACHE_NAME); } + /** + * Return instance of FrontChannelSLOParticipantStore. + * + * @return FrontChannelSLOParticipantStore instance. + */ public static FrontChannelSLOParticipantStore getInstance() { return instance; } + /** + * Store FrontChannelSLOParticipantInfo against logout request id of the current SLO invoked session participant. + * + * @param key Logout request id of the current SLO invoked session participant. + * @param entry FrontChannelSLOParticipantInfo. + */ public void addToCache(String key, FrontChannelSLOParticipantInfo entry) { super.addToCache(key, entry); SessionDataStore.getInstance().storeSessionData(key, CACHE_NAME, entry); } + /** + * Retrieve FrontChannelSLOParticipantInfo from the store using logout request id of the current SLO invoked + * session participant. + * + * @param key Logout request id of the current SLO invoked session participant. + * @return FrontChannelSLOParticipantInfo. + */ public FrontChannelSLOParticipantInfo getValueFromCache(String key) { FrontChannelSLOParticipantInfo cacheEntry = super.getValueFromCache(key); @@ -53,6 +71,11 @@ public FrontChannelSLOParticipantInfo getValueFromCache(String key) { return cacheEntry; } + /** + * Remove FrontChannelSLOParticipantInfo from the store for the given logout request id. + * + * @param key Logout request id of the current SLO invoked session participant. + */ public void clearCacheEntry(String key) { super.clearCacheEntry(key); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java index 35315fa08..131c6a43d 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java @@ -226,6 +226,7 @@ public void doSingleLogout(String sessionId, String issuer) throws IdentityExcep String key = entry.getKey(); SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); + // TODO : UI configuration to check for Front-Channel SLO for SPs and filter out. // if issuer is the logout request initiator, then not sending the logout request to the issuer. if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout()) { SingleLogoutRequestDTO logoutReqDTO = SAMLSSOUtil.createLogoutRequestDTO(serviceProviderDO, diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index f98ac56b1..fd3fe719a 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -116,8 +116,8 @@ public X509CredentialImpl(String tenantDomain) throws IdentityException { /** * Set private key and X509Certificate from the default KeyStore. * - * @param keyStoreManager keyStore Manager - * @throws IdentityException + * @param keyStoreManager keyStore Manager. + * @throws IdentityException Error in retrieving private key and certificate. */ private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManager keyStoreManager) throws IdentityException { @@ -136,7 +136,7 @@ private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManag * Set private key and X509Certificate from the Sign KeyStore which is defined under Security.SAMLSignKeyStore * in carbon.xml. * - * @throws IdentityException + * @throws IdentityException Error in keystore. */ private void initializeCredentialForSuperTenantFromSignKeyStore() throws IdentityException { @@ -194,17 +194,17 @@ private void initializeSuperTenantSignKeyStore() throws IdentityException { /** * Set private key and X509Certificate from the tenant KeyStore. * - * @param tenantDomain tenant domain - * @param keyStoreManager KeyStore Manager - * @throws IdentityException + * @param tenantDomain tenant domain. + * @param keyStoreManager KeyStore Manager. + * @throws IdentityException Error in retrieving private key and certificate. */ private void initializeCredentialForTenant(String tenantDomain, KeyStoreManager keyStoreManager) throws IdentityException { try { - // derive key store name. + // Derive key store name. String ksName = tenantDomain.trim().replace(".", "-"); - // derive JKS name. + // Derive JKS name. String jksName = ksName + ".jks"; privateKey = (PrivateKey) keyStoreManager.getPrivateKey(jksName, tenantDomain); signingCert = (X509Certificate) keyStoreManager.getKeyStore(jksName).getCertificate(tenantDomain); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index d6ef72dc7..2a1cc195a 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -365,13 +365,13 @@ private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp * Respond back to the original logout request issuer after handling all the front-channel enabled * session participants. * - * @param req HttpServlet Request - * @param resp HttpServlet Response - * @param sessionId Session id - * @param frontChannelSLOParticipantInfo FrontChannelSLOParticipantInfo - * @throws IOException - * @throws IdentityException - * @throws ServletException + * @param req HttpServlet Request. + * @param resp HttpServlet Response. + * @param sessionId Session id. + * @param frontChannelSLOParticipantInfo Front-Channel SLO Participant Information. + * @throws IOException If sending response fails. + * @throws IdentityException If building logout response fails. + * @throws ServletException If sending response fails. */ private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpServletResponse resp, String sessionId, @@ -406,10 +406,10 @@ private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpSe /** * Build logout response for the original logout request issuer. * - * @param originalIssuerLogoutRequestId Logout request id of original issuer - * @param originalIssuer Original issuer - * @return - * @throws IdentityException + * @param originalIssuerLogoutRequestId Logout request id of original issuer. + * @param originalIssuer Original issuer. + * @return Logout response. + * @throws IdentityException If building logout response fails. */ private LogoutResponse buildLogoutResponseForOriginalIssuer(String originalIssuerLogoutRequestId, SAMLSSOServiceProviderDO originalIssuer) @@ -1216,8 +1216,6 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS doFrontChannelSLO(response, entry, sessionIndex, sessionDTO.getIssuer(), originalIssuerLogoutRequestId, isIdPInitSLO, relayState, returnToURL); break; - } else { - // doBackChannelSLO() } } } @@ -1235,15 +1233,15 @@ private void respondToOriginalIssuer(HttpServletRequest request, HttpServletResp removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionDTO.getSessionId()) == null) { - // remove tokenId Cookie when there is no session available. + // Remove tokenId Cookie when there is no session available. removeTokenIdCookie(request, response); } if (validationResponseDTO.isIdPInitSLO()) { - // redirecting to the return URL or IS logout page. + // Redirecting to the return URL or IS logout page. response.sendRedirect(validationResponseDTO.getReturnToURL()); } else { - // sending LogoutResponse back to the initiator. + // Sending LogoutResponse back to the initiator. sendResponse(request, response, sessionDTO.getRelayState(), validationResponseDTO.getLogoutResponse(), validationResponseDTO.getAssertionConsumerURL(), validationResponseDTO.getSubject(), null, sessionDTO.getTenantDomain()); @@ -1253,12 +1251,12 @@ private void respondToOriginalIssuer(HttpServletRequest request, HttpServletResp /** * Send an error response to original issuer when the SAML request validation is invalid. * - * @param request HttpServlet Request - * @param response HttpServlet Response - * @param sessionDTO SAMLSSOSessionDTO - * @throws IOException - * @throws IdentityException - * @throws ServletException + * @param request HttpServlet Request. + * @param response HttpServlet Response. + * @param sessionDTO SAMLSSOSessionDTO. + * @throws IOException If error response building fails. + * @throws IdentityException If error response building fails. + * @throws ServletException If sending error response fails. */ private void sendErrorResponseToOriginalIssuer(HttpServletRequest request, HttpServletResponse response, SAMLSSOSessionDTO sessionDTO) @@ -1761,12 +1759,12 @@ private void doFrontChannelSLO(HttpServletResponse response, /** * This method is used to prepare and send a SAML request message with HTTP POST binding. * - * @param response HttpServlet Response - * @param samlssoServiceProviderDO SAMLSSOServiceProviderDO - * @param logoutRequest Logout Request - * @param relayState Relay State - * @throws IdentityException - * @throws IOException + * @param response HttpServlet Response. + * @param samlssoServiceProviderDO SAMLSSOServiceProviderDO. + * @param logoutRequest Logout Request. + * @param relayState Relay State. + * @throws IdentityException Error in marshalling or getting SignKeyDataHolder. + * @throws IOException Error in post page printing. */ private void sendPostRequest(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, LogoutRequest logoutRequest, String relayState) @@ -1856,9 +1854,9 @@ private void storeFrontChannelSLOParticipantInfo(SAMLSSOServiceProviderDO logout /** * Extract logout request id of the original issuer logout request. * - * @param request HttpServletRequest - * @return - * @throws IdentityException + * @param request HttpServletRequest. + * @return Logout request id of the original logout request issuer. + * @throws IdentityException Decoding error. */ private String extractLogoutRequestId(HttpServletRequest request) throws IdentityException { @@ -1876,9 +1874,9 @@ private String extractLogoutRequestId(HttpServletRequest request) throws Identit /** * Extract session index from the original issuer logout request. * - * @param request HttpServletRequest - * @return - * @throws IdentityException + * @param request HttpServletRequest. + * @return Session Index. + * @throws IdentityException Decoding error. */ private String extractSessionIndex(HttpServletRequest request) throws IdentityException { @@ -1897,8 +1895,8 @@ private String extractSessionIndex(HttpServletRequest request) throws IdentityEx /** * Retrieves information of front-channel session participants in single logout. * - * @param logoutRequestId Logout request id - * @return + * @param logoutRequestId Logout request id. + * @return Front-Channel SLO Participant Information. */ private FrontChannelSLOParticipantInfo getFrontChannelSLOParticipantInfo(String logoutRequestId) { @@ -1911,10 +1909,10 @@ private FrontChannelSLOParticipantInfo getFrontChannelSLOParticipantInfo(String /** * This method is used to prepare a SAML request message as a HTTP query string for HTTP Redirect binding. * - * @param logoutRequest Logout Request - * @param serviceProviderDO SAMLSSOServiceProviderDO - * @return Redirect URL - * @throws IdentityException + * @param logoutRequest Logout Request. + * @param serviceProviderDO SAMLSSOServiceProviderDO. + * @return Redirect URL. + * @throws IdentityException Error in marshalling or setting signature to http query string. */ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, SAMLSSOServiceProviderDO serviceProviderDO) @@ -1939,8 +1937,6 @@ private String createHttpQueryStringForRedirect(LogoutRequest logoutRequest, URLEncoder.encode(signatureAlgorithmUri, "UTF-8")); SAMLSSOUtil.addSignatureToHTTPQueryString(httpQueryString, signatureAlgorithmUri, new X509CredentialImpl(tenantDomain)); - } catch (UnsupportedEncodingException e) { - throw new IdentityException("Error while encoding the message.", e); } catch (IOException e) { throw new IdentityException("Error in compressing the SAML request message.", e); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 49078f813..36835d25d 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -2012,11 +2012,11 @@ public static SAMLSSOServiceProviderDO getSPConfig(String tenantDomain, String i /** * Build SAML logout request. * - * @param serviceProviderDO - * @param subject - * @param sessionId - * @return LogoutRequest - * @throws IdentityException + * @param serviceProviderDO SP for which the logout request is built. + * @param subject Subject identifier. + * @param sessionId Session index. + * @return Logout Request. + * @throws IdentityException If tenant domain is invalid. */ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceProviderDO, String subject, String sessionId) throws IdentityException { @@ -2069,9 +2069,9 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP /** * Get remaining session participants for SLO except for the original issuer. * - * @param sessionIndex Session index - * @param issuer Original issuer - * @return + * @param sessionIndex Session index. + * @param issuer Original issuer. + * @return SP List with remaining session participants for SLO except for the original issuer. */ public static List getRemainingSessionParticipantsForSLO(String sessionIndex, String issuer) { @@ -2080,9 +2080,11 @@ public static List getRemainingSessionParticipantsForS .getPersistenceManager(); SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); - List samlssoServiceProviderDOList = null; + List samlssoServiceProviderDOList; - if (sessionInfoData != null) { + if (sessionInfoData == null) { + return new ArrayList<>(); + } else { Map sessionsList = sessionInfoData.getServiceProviderList(); samlssoServiceProviderDOList = new ArrayList<>(); @@ -2106,8 +2108,8 @@ public static List getRemainingSessionParticipantsForS /** * Get SessionInfoData. * - * @param sessionIndex Session index - * @return + * @param sessionIndex Session index. + * @return Session Info Data. */ public static SessionInfoData getSessionInfoData(String sessionIndex) { @@ -2121,8 +2123,8 @@ public static SessionInfoData getSessionInfoData(String sessionIndex) { /** * Get Session Index. * - * @param sessionId - * @return Session Index + * @param sessionId Session id. + * @return Session Index. */ public static String getSessionIndex(String sessionId) { @@ -2167,10 +2169,11 @@ public static void addSignatureToHTTPQueryString(StringBuilder httpQueryString, /** * Validate whether the LogoutResponse is a success. * - * @param response Logout Response object. + * @param response Logout Response object. + * @param certificateAlias Certificate Alias. + * @param tenantDomain Tenant domain. * @return True if Logout response state success. - * @throws IOException Stream error. - * @throws IdentityException Decoding error. + * @throws IdentityException If validating XML signature fails. */ public static boolean validateLogoutResponse(XMLObject response, String certificateAlias, String tenantDomain) throws IdentityException { From 76746578193defc48b27b2f19d170dfa61d491ce Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Wed, 23 Jan 2019 22:22:08 +0530 Subject: [PATCH 11/16] add code refinements --- .../saml/FrontChannelSLOParticipantInfo.java | 2 + .../saml/FrontChannelSLOParticipantStore.java | 22 +- .../identity/sso/saml/SAMLSSOService.java | 1 + .../builders/SingleLogoutMessageBuilder.java | 56 ++-- .../sso/saml/builders/X509CredentialImpl.java | 33 +- .../sso/saml/logout/LogoutRequestSender.java | 34 +- .../saml/servlet/SAMLSSOProviderServlet.java | 298 ++++++++++-------- .../identity/sso/saml/util/SAMLSSOUtil.java | 122 +++---- 8 files changed, 280 insertions(+), 288 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java index b2f27a302..2f10c8544 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantInfo.java @@ -23,6 +23,8 @@ */ public class FrontChannelSLOParticipantInfo extends CacheEntry { + private static final long serialVersionUID = -3909575392953155294L; + private String originalIssuerLogoutRequestId; private String originalLogoutRequestIssuer; private String currentSLOInvokedParticipant; diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java index 316adca81..9397dd626 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/FrontChannelSLOParticipantStore.java @@ -18,18 +18,28 @@ import org.wso2.carbon.identity.application.authentication.framework.store.SessionDataStore; import org.wso2.carbon.identity.application.common.cache.BaseCache; +import org.wso2.carbon.identity.core.util.IdentityUtil; + +import java.util.concurrent.TimeUnit; /** * This is to store information of front-channel enabled session participants in a single logout. + * Although this is extended from BaseCache this does not act as a cache. This is implemented just to act as + * an in-memory storage. */ public class FrontChannelSLOParticipantStore extends BaseCache { private static final String CACHE_NAME = "FrontChannelSLOParticipantStore"; private static volatile FrontChannelSLOParticipantStore instance = new FrontChannelSLOParticipantStore(); + private boolean isTemporarySessionDataPersistEnabled = false; private FrontChannelSLOParticipantStore() { super(CACHE_NAME); + if (IdentityUtil.getProperty("JDBCPersistenceManager.SessionDataPersist.Temporary") != null) { + isTemporarySessionDataPersistEnabled = Boolean.parseBoolean( + IdentityUtil.getProperty("JDBCPersistenceManager.SessionDataPersist.Temporary")); + } } /** @@ -51,7 +61,11 @@ public static FrontChannelSLOParticipantStore getInstance() { public void addToCache(String key, FrontChannelSLOParticipantInfo entry) { super.addToCache(key, entry); - SessionDataStore.getInstance().storeSessionData(key, CACHE_NAME, entry); + if (isTemporarySessionDataPersistEnabled) { + long validityPeriod = TimeUnit.MINUTES.toNanos(IdentityUtil.getTempDataCleanUpTimeout()); + entry.setValidityPeriod(validityPeriod); + SessionDataStore.getInstance().storeSessionData(key, CACHE_NAME, entry); + } } /** @@ -64,7 +78,7 @@ public void addToCache(String key, FrontChannelSLOParticipantInfo entry) { public FrontChannelSLOParticipantInfo getValueFromCache(String key) { FrontChannelSLOParticipantInfo cacheEntry = super.getValueFromCache(key); - if (cacheEntry == null) { + if (cacheEntry == null && isTemporarySessionDataPersistEnabled) { cacheEntry = (FrontChannelSLOParticipantInfo) SessionDataStore.getInstance(). getSessionData(key, CACHE_NAME); } @@ -79,6 +93,8 @@ public FrontChannelSLOParticipantInfo getValueFromCache(String key) { public void clearCacheEntry(String key) { super.clearCacheEntry(key); - SessionDataStore.getInstance().clearSessionData(key, CACHE_NAME); + if (isTemporarySessionDataPersistEnabled) { + SessionDataStore.getInstance().clearSessionData(key, CACHE_NAME); + } } } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java index 131c6a43d..ccc4d321e 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java @@ -239,6 +239,7 @@ public void doSingleLogout(String sessionId, String issuer) throws IdentityExcep //send logout requests to all session participants LogoutRequestSender.getInstance().sendLogoutRequests(singleLogoutReqDTOs.toArray( new SingleLogoutRequestDTO[singleLogoutReqDTOs.size()])); + // TODO : Check for back-channel logout and filter the session removal. SAMLSSOUtil.removeSession(sessionId, issuer); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java index e3c158f58..ba88e7253 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java @@ -56,29 +56,8 @@ public LogoutRequest buildLogoutRequest(String subject, String sessionId, String requestsigningAlgorithmUri, String requestDigestAlgoUri) throws IdentityException { - LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); - - logoutReq.setID(SAMLSSOUtil.createID()); - - DateTime issueInstant = new DateTime(); - logoutReq.setIssueInstant(issueInstant); - logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(tenantDomain)); - logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000)); - - NameID nameId = new NameIDBuilder().buildObject(); - nameId.setFormat(nameIDFormat); - nameId.setValue(subject); - logoutReq.setNameID(nameId); - - SessionIndex sessionIndex = new SessionIndexBuilder().buildObject(); - sessionIndex.setSessionIndex(sessionId); - logoutReq.getSessionIndexes().add(sessionIndex); - - if (destination != null) { - logoutReq.setDestination(destination); - } - - logoutReq.setReason(reason); + LogoutRequest logoutReq = this.buildLogoutRequest(destination, tenantDomain, sessionId, subject, nameIDFormat, + reason); int tenantId; if (StringUtils.isEmpty(tenantDomain) || "null".equals(tenantDomain)) { @@ -109,6 +88,37 @@ public LogoutRequest buildLogoutRequest(String subject, String sessionId, String return logoutReq; } + public LogoutRequest buildLogoutRequest(String destination, String tenantDomain, String sessionId, String subject, + String nameIDFormat, String reason) throws IdentityException { + + LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); + + logoutReq.setID(SAMLSSOUtil.createID()); + + DateTime issueInstant = new DateTime(); + logoutReq.setIssueInstant(issueInstant); + logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(tenantDomain)); + logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + + SAMLSSOUtil.getSAMLResponseValidityPeriod() * 60 * 1000L)); + + NameID nameId = new NameIDBuilder().buildObject(); + nameId.setFormat(nameIDFormat); + nameId.setValue(subject); + logoutReq.setNameID(nameId); + + SessionIndex sessionIndex = new SessionIndexBuilder().buildObject(); + sessionIndex.setSessionIndex(sessionId); + logoutReq.getSessionIndexes().add(sessionIndex); + + if (destination != null) { + logoutReq.setDestination(destination); + } + + logoutReq.setReason(reason); + + return logoutReq; + } + public LogoutResponse buildLogoutResponse(String id, String status, String statMsg, String destination, boolean isSignResponse, String tenantDomain, String responseSigningAlgorithmUri, String responseDigestAlgoUri) throws IdentityException { diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java index fd3fe719a..19e7b231a 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/X509CredentialImpl.java @@ -67,11 +67,11 @@ public class X509CredentialImpl implements X509Credential { private static Log log = LogFactory.getLog(X509CredentialImpl.class); - public static final String SECURITY_SAML_SIGN_KEY_STORE_LOCATION = "Security.SAMLSignKeyStore.Location"; - public static final String SECURITY_SAML_SIGN_KEY_STORE_TYPE = "Security.SAMLSignKeyStore.Type"; - public static final String SECURITY_SAML_SIGN_KEY_STORE_PASSWORD = "Security.SAMLSignKeyStore.Password"; - public static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS = "Security.SAMLSignKeyStore.KeyAlias"; - public static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD = "Security.SAMLSignKeyStore.KeyPassword"; + private static final String SECURITY_SAML_SIGN_KEY_STORE_LOCATION = "Security.SAMLSignKeyStore.Location"; + private static final String SECURITY_SAML_SIGN_KEY_STORE_TYPE = "Security.SAMLSignKeyStore.Type"; + private static final String SECURITY_SAML_SIGN_KEY_STORE_PASSWORD = "Security.SAMLSignKeyStore.Password"; + private static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_ALIAS = "Security.SAMLSignKeyStore.KeyAlias"; + private static final String SECURITY_SAML_SIGN_KEY_STORE_KEY_PASSWORD = "Security.SAMLSignKeyStore.KeyPassword"; /** * Instantiates X509Credential. @@ -92,14 +92,14 @@ public X509CredentialImpl(String tenantDomain) throws IdentityException { KeyStoreManager keyStoreManager = KeyStoreManager.getInstance(tenantId); // Get the private key and the cert for the respective tenant domain. - if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { - initializeCredentialForTenant(tenantDomain, keyStoreManager); - } else { + if (MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { if (isSignKeyStoreConfigured()) { - initializeCredentialForSuperTenantFromSignKeyStore(); + initCredentialForSuperTenantFromSignKeyStore(); } else { - initializeCredentialForSuperTenantFromDefaultKeyStore(keyStoreManager); + initCredentialFromSuperTenantKeyStore(keyStoreManager); } + } else { + initCredentialForTenant(tenantDomain, keyStoreManager); } if (privateKey == null) { @@ -119,7 +119,7 @@ public X509CredentialImpl(String tenantDomain) throws IdentityException { * @param keyStoreManager keyStore Manager. * @throws IdentityException Error in retrieving private key and certificate. */ - private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManager keyStoreManager) + private void initCredentialFromSuperTenantKeyStore(KeyStoreManager keyStoreManager) throws IdentityException { try { @@ -138,7 +138,7 @@ private void initializeCredentialForSuperTenantFromDefaultKeyStore(KeyStoreManag * * @throws IdentityException Error in keystore. */ - private void initializeCredentialForSuperTenantFromSignKeyStore() throws IdentityException { + private void initCredentialForSuperTenantFromSignKeyStore() throws IdentityException { if (log.isDebugEnabled()) { log.debug("Initializing Key Data for super tenant using separate sign key store."); @@ -146,7 +146,7 @@ private void initializeCredentialForSuperTenantFromSignKeyStore() throws Identit try { if (superTenantSignKeyStore == null) { - initializeSuperTenantSignKeyStore(); + initSuperTenantSignKeyStore(); } String keyAlias = ServerConfiguration.getInstance().getFirstProperty( @@ -168,11 +168,12 @@ private void initializeCredentialForSuperTenantFromSignKeyStore() throws Identit throw new IdentityException("Configured signing KeyStore X509Certificate is invalid."); } } catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) { - throw new IdentityException("Unable to load keystore", e); + throw new IdentityException("Unable to load signing keystore for tenant " + + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, e); } } - private void initializeSuperTenantSignKeyStore() throws IdentityException { + private void initSuperTenantSignKeyStore() throws IdentityException { String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty( SECURITY_SAML_SIGN_KEY_STORE_LOCATION); @@ -198,7 +199,7 @@ private void initializeSuperTenantSignKeyStore() throws IdentityException { * @param keyStoreManager KeyStore Manager. * @throws IdentityException Error in retrieving private key and certificate. */ - private void initializeCredentialForTenant(String tenantDomain, KeyStoreManager keyStoreManager) + private void initCredentialForTenant(String tenantDomain, KeyStoreManager keyStoreManager) throws IdentityException { try { diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/logout/LogoutRequestSender.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/logout/LogoutRequestSender.java index 3449416a3..b20e7c4c0 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/logout/LogoutRequestSender.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/logout/LogoutRequestSender.java @@ -290,40 +290,8 @@ private boolean validateResponse(HttpResponse httpResponse, String certificateAl // This should be a SAML logout response. if (xmlObject instanceof LogoutResponse) { - LogoutResponse logoutResponse = (LogoutResponse) xmlObject; - if (logoutResponse.getIssuer() == null || logoutResponse.getStatus() == null || logoutResponse - .getStatus().getStatusCode() == null) { - if (log.isDebugEnabled()) { - log.debug("Logout response validation failed due to one of given values are null. " + - "Issuer: " + logoutResponse.getIssuer() + - " Status: " + logoutResponse.getStatus() + - " Status code: " + (logoutResponse.getStatus() != null ? logoutResponse.getStatus() - .getStatusCode() : null)); - } - return false; - } - - if (log.isDebugEnabled()) { - log.debug("Logout response received for issuer: " + logoutResponse.getIssuer() - .getValue() + " for tenant domain: " + tenantDomain); - } - - boolean isSignatureValid = true; - - // Certificate alias will be null if signature validation is disabled in the service provider side. - if (certificateAlias != null && logoutResponse.isSigned()) { - isSignatureValid = SAMLSSOUtil.validateXMLSignature(logoutResponse, certificateAlias, tenantDomain); - if (log.isDebugEnabled()) { - log.debug("Signature validation result for logout response for issuer: " + - logoutResponse.getIssuer().getValue() + " in tenant domain: " + tenantDomain + " is: " + - isSignatureValid); - } - } - if (SAMLSSOConstants.StatusCodes.SUCCESS_CODE.equals(logoutResponse.getStatus().getStatusCode() - .getValue()) && isSignatureValid) { - return true; - } + return SAMLSSOUtil.validateLogoutResponse(logoutResponse, certificateAlias, tenantDomain); } return false; diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 2a1cc195a..898d739f7 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -318,44 +318,59 @@ private void handleSAMLResponse(HttpServletRequest req, HttpServletResponse resp response = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(samlResponse)); } - if (!(response instanceof LogoutResponseImpl)) { - handleInvalidRequestMessage(req, resp, sessionId); + if (response instanceof LogoutResponse) { + LogoutResponse logoutResponse = (LogoutResponse) response; + handleLogoutResponseFromSP(req, resp, sessionId, logoutResponse); } else { - String inResponseToId = ((LogoutResponseImpl) response).getInResponseTo(); - FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = - getFrontChannelSLOParticipantInfo(inResponseToId); + handleInvalidRequestMessage(req, resp, sessionId); + } + } - if (frontChannelSLOParticipantInfo == null) { - handleInvalidRequestMessage(req, resp, sessionId); - } else { - String logoutResponseIssuer = ((LogoutResponseImpl) response).getIssuer().getValue(); - SAMLSSOServiceProviderDO responseIssuerSP = SAMLSSOUtil.getSPConfig( - SAMLSSOUtil.getTenantDomainFromThreadLocal(), logoutResponseIssuer); + private void handleLogoutResponseFromSP(HttpServletRequest req, HttpServletResponse resp, String sessionId, + LogoutResponse logoutResponse) + throws ServletException, IdentityException, IOException { - boolean isSuccessfullyLogout = SAMLSSOUtil.validateLogoutResponse(response, - responseIssuerSP.getCertAlias(), responseIssuerSP.getTenantDomain()); + String inResponseToId = logoutResponse.getInResponseTo(); + FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo = + getFrontChannelSLOParticipantInfo(inResponseToId); - if (!isSuccessfullyLogout) { - // TODO : If the response is invalid, redirect the SP to an error page. - } else { - removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); + if (frontChannelSLOParticipantInfo == null || !frontChannelSLOParticipantInfo. + getCurrentSLOInvokedParticipant().equals(logoutResponse.getIssuer().getValue())) { + handleInvalidRequestMessage(req, resp, sessionId); + } else { + // Remove front-channel SLO Participant info from the FrontChannelSLOParticipantStore. + removeFrontChannelSLOParticipantInfo(inResponseToId); + String logoutResponseIssuer = logoutResponse.getIssuer().getValue(); + SAMLSSOServiceProviderDO responseIssuerSP = SAMLSSOUtil.getSPConfig( + SAMLSSOUtil.getTenantDomainFromThreadLocal(), logoutResponseIssuer); - List samlssoServiceProviderDOList = - SAMLSSOUtil.getRemainingSessionParticipantsForSLO( - frontChannelSLOParticipantInfo.getSessionIndex(), - frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + boolean isSuccessfullyLogout = SAMLSSOUtil.validateLogoutResponse(logoutResponse, + responseIssuerSP.getCertAlias(), responseIssuerSP.getTenantDomain()); - if (samlssoServiceProviderDOList.isEmpty()) { - respondToOriginalLogoutRequestIssuer(req, resp, sessionId, frontChannelSLOParticipantInfo); - } else { - doFrontChannelSLO(resp, samlssoServiceProviderDOList.get(0), + if (!isSuccessfullyLogout) { + // TODO : If the response is invalid, redirect the SP to an error page. + if (log.isDebugEnabled()) { + log.debug("Logout response validation failed for logout response issuer: " + + logoutResponseIssuer); + } + } else { + removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); + + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO( frontChannelSLOParticipantInfo.getSessionIndex(), - frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer(), - frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), - frontChannelSLOParticipantInfo.isIdPInitSLO(), - frontChannelSLOParticipantInfo.getRelayState(), - frontChannelSLOParticipantInfo.getReturnToURL()); - } + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + + if (samlssoServiceProviderDOList.isEmpty()) { + respondToOriginalLogoutRequestIssuer(req, resp, sessionId, frontChannelSLOParticipantInfo); + } else { + sendLogoutRequestToSessionParticipant(resp, samlssoServiceProviderDOList, + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), + frontChannelSLOParticipantInfo.isIdPInitSLO(), + frontChannelSLOParticipantInfo.getRelayState(), + frontChannelSLOParticipantInfo.getReturnToURL(), + frontChannelSLOParticipantInfo.getSessionIndex(), + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); } } } @@ -384,8 +399,6 @@ private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpSe LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); - removeSessionDataFromCache(req.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); - if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionId) == null) { // Remove tokenId Cookie when there is no session available. removeTokenIdCookie(req, resp); @@ -446,11 +459,11 @@ private LogoutResponse buildLogoutResponseForOriginalIssuer(String originalIssue private void removeSPFromSession(String sessionIndex, String serviceProvider) { - if (sessionIndex != null) { + if (sessionIndex != null && serviceProvider != null) { SAMLSSOParticipantCacheKey cacheKey = new SAMLSSOParticipantCacheKey(sessionIndex); SAMLSSOParticipantCacheEntry cacheEntry = SAMLSSOParticipantCache.getInstance().getValueFromCache(cacheKey); - if (serviceProvider != null && cacheEntry.getSessionInfoData() != null && + if (cacheEntry.getSessionInfoData() != null && cacheEntry.getSessionInfoData().getServiceProviderList() != null) { cacheEntry.getSessionInfoData().removeServiceProvider(serviceProvider); } @@ -940,62 +953,80 @@ private void sendResponse(HttpServletRequest req, HttpServletResponse resp, Stri } out.print(soapResponse); } else { + generateSamlPostPageFromFile(resp, acUrl, response, relayState, authenticatedIdPs, + SAMLSSOConstants.SAML_RESP); + } - String finalPage = null; - String htmlPage = IdentitySAMLSSOServiceComponent.getSsoRedirectHtml(); - String pageWithAcs = htmlPage.replace("$acUrl", acUrl); - String pageWithAcsResponse = pageWithAcs.replace("", "\n" + ""); - String pageWithAcsResponseRelay = pageWithAcsResponse; + } else { + generateSamlPostPageFromTemplate(resp, acUrl, response, relayState, authenticatedIdPs, + SAMLSSOConstants.SAML_RESP); + } + } - if(relayState != null) { - pageWithAcsResponseRelay = pageWithAcsResponse.replace("", "\n" + ""); - } + private void generateSamlPostPageFromTemplate(HttpServletResponse resp, String acUrl, String samlMessage, + String relayState, String authenticatedIdPs, String samlMessageType) + throws IOException { - if (authenticatedIdPs == null || authenticatedIdPs.isEmpty()) { - finalPage = pageWithAcsResponseRelay; - } else { - finalPage = pageWithAcsResponseRelay.replace( - "", - ""); - } + PrintWriter out = resp.getWriter(); + out.println(""); + out.println(""); + out.println("

You are now redirected back to " + Encode.forHtmlContent(acUrl)); + out.println(" If the redirection fails, please click the post button.

"); + out.println("
"); + out.println("

"); + out.println(""); - PrintWriter out = resp.getWriter(); - out.print(finalPage); + if (relayState != null) { + out.println(""); + } - if (log.isDebugEnabled()) { - log.debug("samlsso_response.html " + finalPage); - } + if (authenticatedIdPs != null && !authenticatedIdPs.isEmpty()) { + out.println(""); + } - } + out.println(""); + out.println("

"); + out.println("
"); + out.println(""); + out.println(""); + out.println(""); + } + private void generateSamlPostPageFromFile(HttpServletResponse resp, String acUrl, String samlMessage, + String relayState, String authenticatedIdPs, String samlMessageType) + throws IOException { + + String finalPage; + String htmlPage = IdentitySAMLSSOServiceComponent.getSsoRedirectHtml(); + String pageWithAcs = htmlPage.replace("$acUrl", acUrl); + String pageWithAcsResponse = pageWithAcs.replace("", + buildPostPageInputs(samlMessageType, samlMessage)); + String pageWithAcsResponseRelay = pageWithAcsResponse; + + if (relayState != null) { + pageWithAcsResponseRelay = pageWithAcsResponse.replace("", + buildPostPageInputs(SAMLSSOConstants.RELAY_STATE, relayState)); + } + + if (authenticatedIdPs == null || authenticatedIdPs.isEmpty()) { + finalPage = pageWithAcsResponseRelay; } else { - PrintWriter out = resp.getWriter(); - out.println(""); - out.println(""); - out.println("

You are now redirected back to " + Encode.forHtmlContent(acUrl)); - out.println(" If the redirection fails, please click the post button.

"); - out.println("
"); - out.println("

"); - out.println(""); - - if(relayState != null) { - out.println(""); - } + finalPage = pageWithAcsResponseRelay.replace( + "", + ""); + } - if (authenticatedIdPs != null && !authenticatedIdPs.isEmpty()) { - out.println(""); - } + PrintWriter out = resp.getWriter(); + out.print(finalPage); - out.println(""); - out.println("

"); - out.println("
"); - out.println(""); - out.println(""); - out.println(""); + if (log.isDebugEnabled()) { + log.debug("samlsso_response.html " + finalPage); } } @@ -1195,6 +1226,7 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO(); if (validationResponseDTO != null) { + removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); String sessionIndex = extractSessionIndex(request); List samlssoServiceProviderDOList = SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer()); @@ -1203,35 +1235,41 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS if (samlssoServiceProviderDOList.isEmpty()) { respondToOriginalIssuer(request, response, sessionDTO); } else { - for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { - // TODO : UI configuration to enable Front-Channel SLO for SPs. - Boolean isFrontChannelSLOEnabled = true; - //check entry.isFrontChannelSLOEnabled() - if (isFrontChannelSLOEnabled) { - String originalIssuerLogoutRequestId = extractLogoutRequestId(request); - boolean isIdPInitSLO = sessionDTO.isIdPInitSLO(); - String relayState = sessionDTO.getRelayState(); - String returnToURL = validationResponseDTO.getReturnToURL(); - - doFrontChannelSLO(response, entry, sessionIndex, sessionDTO.getIssuer(), - originalIssuerLogoutRequestId, isIdPInitSLO, relayState, returnToURL); - break; - } - } + String originalIssuerLogoutRequestId = extractLogoutRequestId(request); + sendLogoutRequestToSessionParticipant(response, samlssoServiceProviderDOList, + originalIssuerLogoutRequestId, sessionDTO.isIdPInitSLO(), sessionDTO.getRelayState(), + validationResponseDTO.getReturnToURL(), sessionIndex, sessionDTO.getIssuer()); } } else { sendErrorResponseToOriginalIssuer(request, response, sessionDTO); } } + private void sendLogoutRequestToSessionParticipant(HttpServletResponse response, + List samlssoServiceProviderDOList, + String originalIssuerLogoutRequestId, boolean isIdPInitSLO, + String relayState, String returnToURL, String sessionIndex, + String originalLogoutRequestIssuer) + throws IOException, IdentityException { + + for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { + // TODO : UI configuration to enable Front-Channel SLO for SPs. + boolean isFrontChannelSLOEnabled = true; + //check entry.isFrontChannelSLOEnabled() + if (isFrontChannelSLOEnabled) { + doFrontChannelSLO(response, entry, sessionIndex, originalLogoutRequestIssuer, + originalIssuerLogoutRequestId, isIdPInitSLO, relayState, returnToURL); + break; + } + } + } + private void respondToOriginalIssuer(HttpServletRequest request, HttpServletResponse response, SAMLSSOSessionDTO sessionDTO) throws ServletException, IOException, IdentityException { SAMLSSOReqValidationResponseDTO validationResponseDTO = sessionDTO.getValidationRespDTO(); - removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); - if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionDTO.getSessionId()) == null) { // Remove tokenId Cookie when there is no session available. removeTokenIdCookie(request, response); @@ -1749,7 +1787,7 @@ private void doFrontChannelSLO(HttpServletResponse response, // TODO: UI configuration to check for the binding and filter. boolean isPostBindingEnabled = true; if (isPostBindingEnabled) { - sendPostRequest(response, samlssoServiceProviderDO, logoutRequest, relayState); + sendPostRequest(response, samlssoServiceProviderDO, logoutRequest); } else { String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); response.sendRedirect(redirectUrl); @@ -1762,66 +1800,38 @@ private void doFrontChannelSLO(HttpServletResponse response, * @param response HttpServlet Response. * @param samlssoServiceProviderDO SAMLSSOServiceProviderDO. * @param logoutRequest Logout Request. - * @param relayState Relay State. * @throws IdentityException Error in marshalling or getting SignKeyDataHolder. * @throws IOException Error in post page printing. */ private void sendPostRequest(HttpServletResponse response, SAMLSSOServiceProviderDO samlssoServiceProviderDO, - LogoutRequest logoutRequest, String relayState) + LogoutRequest logoutRequest) throws IdentityException, IOException { logoutRequest = SAMLSSOUtil.setSignature(logoutRequest, samlssoServiceProviderDO.getSigningAlgorithmUri(), samlssoServiceProviderDO.getDigestAlgorithmUri(), new SignKeyDataHolder(null)); String encodedRequestMessage = SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutRequest)); - - String postPageInputs = buildPostPageInputs(encodedRequestMessage, relayState); String acUrl = logoutRequest.getDestination(); - printPostPage(response, acUrl, postPageInputs); + printPostPage(response, acUrl, encodedRequestMessage); } - private void printPostPage(HttpServletResponse response, String acUrl, String postPageInputs) throws IOException { + private void printPostPage(HttpServletResponse response, String acUrl, String encodedRequestMessage) + throws IOException { - String htmlPage = IdentitySAMLSSOServiceComponent.getSsoRedirectHtml(); response.setContentType("text/html; charset=UTF-8"); - if (htmlPage != null) { - String pageWithAcs = htmlPage.replace("$acUrl", acUrl); - String finalPage = pageWithAcs.replace("", postPageInputs); - PrintWriter out = response.getWriter(); - out.print(finalPage); - - if (log.isDebugEnabled()) { - log.debug("HTTP-POST page: " + finalPage); - } + if (IdentitySAMLSSOServiceComponent.getSsoRedirectHtml() != null) { + generateSamlPostPageFromFile(response, acUrl, encodedRequestMessage, null, null, + SAMLSSOConstants.SAML_REQUEST); } else { - PrintWriter out = response.getWriter(); - out.println(""); - out.println(""); - out.println("

You are now redirected back to " + Encode.forHtmlContent(acUrl)); - out.println(" If the redirection fails, please click the post button.

"); - out.println("
"); - out.println("

"); - out.println(postPageInputs); - out.println(""); - out.println("

"); - out.println("
"); - out.println(""); - out.println(""); - out.println(""); + generateSamlPostPageFromTemplate(response, acUrl, encodedRequestMessage, null, + null, SAMLSSOConstants.SAML_REQUEST); } } - private String buildPostPageInputs(String encodedRequestMessage, String relayState) { + private String buildPostPageInputs(String formControlName, String formControlValue) { StringBuilder hiddenInputBuilder = new StringBuilder(); - hiddenInputBuilder.append("\n").append(""); - - if (relayState != null) { - hiddenInputBuilder.append("\n").append(""); - } + hiddenInputBuilder.append("\n").append(""); return hiddenInputBuilder.toString(); } @@ -1906,6 +1916,16 @@ private FrontChannelSLOParticipantInfo getFrontChannelSLOParticipantInfo(String return frontChannelSLOParticipantInfo; } + /** + * Removes information of front-channel slo session participant from the store. + * + * @param logoutRequestId Logout request id. + */ + private void removeFrontChannelSLOParticipantInfo(String logoutRequestId) { + + FrontChannelSLOParticipantStore.getInstance().clearCacheEntry(logoutRequestId); + } + /** * This method is used to prepare a SAML request message as a HTTP query string for HTTP Redirect binding. * diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 36835d25d..a92852ab2 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -2021,47 +2021,25 @@ public static SAMLSSOServiceProviderDO getSPConfig(String tenantDomain, String i public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceProviderDO, String subject, String sessionId) throws IdentityException { - SingleLogoutRequestDTO logoutReqDTO = new SingleLogoutRequestDTO(); - + String destination; if (StringUtils.isNotBlank(serviceProviderDO.getSloRequestURL())) { - logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getSloRequestURL()); + destination = serviceProviderDO.getSloRequestURL(); if (log.isDebugEnabled()) { log.debug("Destination of the logout request is set to the " + "SLO request URL of the SP: " + serviceProviderDO.getSloRequestURL()); } } else { - logoutReqDTO.setAssertionConsumerURL(serviceProviderDO.getAssertionConsumerUrl()); + destination = serviceProviderDO.getAssertionConsumerUrl(); if (log.isDebugEnabled()) { log.debug("Destination of the logout request is set to the " + "ACS URL of the SP: " + serviceProviderDO.getAssertionConsumerUrl()); } } - LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject(); - - logoutReq.setID(SAMLSSOUtil.createID()); - - String destination = logoutReqDTO.getAssertionConsumerURL(); - if (destination != null) { - logoutReq.setDestination(destination); - } - - DateTime issueInstant = new DateTime(); - logoutReq.setIssueInstant(issueInstant); - logoutReq.setIssuer(SAMLSSOUtil.getIssuerFromTenantDomain(serviceProviderDO.getTenantDomain())); - logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + - SAMLSSOUtil.getSAMLResponseValidityPeriod() * 60 * 1000L)); - - NameID nameId = new NameIDBuilder().buildObject(); - nameId.setFormat(serviceProviderDO.getNameIDFormat()); - nameId.setValue(subject); - logoutReq.setNameID(nameId); - - SessionIndex sessionIndex = new SessionIndexBuilder().buildObject(); - sessionIndex.setSessionIndex(sessionId); - logoutReq.getSessionIndexes().add(sessionIndex); - - logoutReq.setReason(SAMLSSOConstants.SingleLogoutCodes.LOGOUT_USER); + SingleLogoutMessageBuilder logoutMsgBuilder = new SingleLogoutMessageBuilder(); + LogoutRequest logoutReq = logoutMsgBuilder.buildLogoutRequest(destination, serviceProviderDO.getTenantDomain(), + sessionId, subject, serviceProviderDO.getNameIDFormat(), + SAMLSSOConstants.SingleLogoutCodes.LOGOUT_USER); return logoutReq; } @@ -2084,21 +2062,21 @@ public static List getRemainingSessionParticipantsForS if (sessionInfoData == null) { return new ArrayList<>(); - } else { - Map sessionsList = sessionInfoData.getServiceProviderList(); - samlssoServiceProviderDOList = new ArrayList<>(); + } - for (Map.Entry entry : sessionsList.entrySet()) { - SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); + Map sessionsList = sessionInfoData.getServiceProviderList(); + samlssoServiceProviderDOList = new ArrayList<>(); - // Logout request should not be created for the issuer. - if (entry.getKey().equals(issuer)) { - continue; - } + for (Map.Entry entry : sessionsList.entrySet()) { + SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); - if (serviceProviderDO.isDoSingleLogout()) { - samlssoServiceProviderDOList.add(serviceProviderDO); - } + // Logout request should not be created for the issuer. + if (entry.getKey().equals(issuer)) { + continue; + } + + if (serviceProviderDO.isDoSingleLogout()) { + samlssoServiceProviderDOList.add(serviceProviderDO); } } @@ -2169,52 +2147,48 @@ public static void addSignatureToHTTPQueryString(StringBuilder httpQueryString, /** * Validate whether the LogoutResponse is a success. * - * @param response Logout Response object. + * @param logoutResponse Logout Response object. * @param certificateAlias Certificate Alias. * @param tenantDomain Tenant domain. * @return True if Logout response state success. * @throws IdentityException If validating XML signature fails. */ - public static boolean validateLogoutResponse(XMLObject response, String certificateAlias, String tenantDomain) + public static boolean validateLogoutResponse(LogoutResponse logoutResponse, String certificateAlias, + String tenantDomain) throws IdentityException { - // This should be a SAML logout response. - if (response instanceof LogoutResponse) { - - LogoutResponse logoutResponse = (LogoutResponse) response; - if (logoutResponse.getIssuer() == null || logoutResponse.getStatus() == null || logoutResponse - .getStatus().getStatusCode() == null) { - if (log.isDebugEnabled()) { - log.debug("Logout response validation failed due to one of given values are null. " + - "Issuer: " + logoutResponse.getIssuer() + - " Status: " + logoutResponse.getStatus() + - " Status code: " + (logoutResponse.getStatus() != null ? logoutResponse.getStatus() - .getStatusCode() : null)); - } - return false; - } - + if (logoutResponse.getIssuer() == null || logoutResponse.getStatus() == null || logoutResponse + .getStatus().getStatusCode() == null) { if (log.isDebugEnabled()) { - log.debug("Logout response received for issuer: " + logoutResponse.getIssuer() - .getValue() + " for tenant domain: " + tenantDomain); + log.debug("Logout response validation failed due to one of given values are null. " + + "Issuer: " + logoutResponse.getIssuer() + + " Status: " + logoutResponse.getStatus() + + " Status code: " + (logoutResponse.getStatus() != null ? logoutResponse.getStatus() + .getStatusCode() : null)); } + return false; + } + + if (log.isDebugEnabled()) { + log.debug("Logout response received for issuer: " + logoutResponse.getIssuer() + .getValue() + " for tenant domain: " + tenantDomain); + } - boolean isSignatureValid = true; + boolean isSignatureValid = true; - // Certificate alias will be null if signature validation is disabled in the service provider side. - if (certificateAlias != null && logoutResponse.isSigned()) { - isSignatureValid = SAMLSSOUtil.validateXMLSignature(logoutResponse, certificateAlias, tenantDomain); - if (log.isDebugEnabled()) { - log.debug("Signature validation result for logout response for issuer: " + - logoutResponse.getIssuer().getValue() + " in tenant domain: " + tenantDomain + " is: " + - isSignatureValid); - } - } - if (SAMLSSOConstants.StatusCodes.SUCCESS_CODE.equals(logoutResponse.getStatus().getStatusCode() - .getValue()) && isSignatureValid) { - return true; + // Certificate alias will be null if signature validation is disabled in the service provider side. + if (certificateAlias != null && logoutResponse.isSigned()) { + isSignatureValid = SAMLSSOUtil.validateXMLSignature(logoutResponse, certificateAlias, tenantDomain); + if (log.isDebugEnabled()) { + log.debug("Signature validation result for logout response for issuer: " + + logoutResponse.getIssuer().getValue() + " in tenant domain: " + tenantDomain + " is: " + + isSignatureValid); } } + if (SAMLSSOConstants.StatusCodes.SUCCESS_CODE.equals(logoutResponse.getStatus().getStatusCode() + .getValue()) && isSignatureValid) { + return true; + } return false; } From c0c3ed0cc6a365f1faaa7684721ab6d8568c0464 Mon Sep 17 00:00:00 2001 From: Hasini Witharana Date: Fri, 25 Jan 2019 10:09:41 +0530 Subject: [PATCH 12/16] Add SAML Front-Channel Logout UI. --- .../saml/common/SAMLSSOProviderConstants.java | 4 + .../IdentitySAMLSSOConfigService.wsdl | 42 +-- .../resources/IdentitySAMLSSOService.wsdl | 305 ++++++++++-------- .../pom.xml | 4 + .../sso/saml/ui/SAMLSSOUIConstants.java | 1 + .../identity/sso/saml/ui/SAMLSSOUIUtil.java | 40 +++ .../sso/saml/ui/i18n/Resources.properties | 4 + .../web/sso-saml/add_service_provider.jsp | 60 +++- ..._service_provider_finish-ajaxprocessor.jsp | 14 +- .../sso-saml/add_service_provider_finish.jsp | 13 + .../sso/saml/admin/SAMLSSOConfigAdmin.java | 6 + .../sso/saml/dto/SAMLSSOAuthnReqDTO.java | 22 ++ .../saml/dto/SAMLSSOServiceProviderDTO.java | 22 ++ .../IdPInitSSOAuthnRequestProcessor.java | 2 + .../SPInitSSOAuthnRequestProcessor.java | 4 + 15 files changed, 382 insertions(+), 161 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java index 8fde54aeb..e395c0f9c 100644 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java @@ -62,6 +62,10 @@ public class SAMLSSOProviderConstants { public static final String LOGIN_PAGE = "customLoginPage"; + //Front Channel Logout Methods + public static final String ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; + public static final String ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING = "HTTPPostBinding"; + private SAMLSSOProviderConstants() { } diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl index 3323e2722..075619ee4 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl @@ -1,21 +1,21 @@ - + IdentitySAMLSSOConfigService - + - + - + @@ -26,36 +26,36 @@ - + - - - + - + - + - + - + - + - + + + - + - + @@ -207,6 +207,7 @@ + @@ -215,6 +216,7 @@ + @@ -236,7 +238,7 @@ - + @@ -248,11 +250,11 @@ - + - + @@ -847,4 +849,4 @@ - + \ No newline at end of file diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl index a280a99d4..95df16fd4 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl @@ -1,53 +1,57 @@ - + IdentitySAMLSSOService - + - + - + + + + + + - + - + - + - - + - - - + + + - + - + @@ -75,56 +79,57 @@ - + - + - - - + - + - + - + + + + + + + + - + - + - + - - - - - + - + @@ -138,84 +143,64 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + @@ -225,56 +210,115 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + - - + + + + + + + + + + + @@ -284,30 +328,21 @@ - - - - - - - - - - - - - + + + + - - + + @@ -318,8 +353,8 @@ - - + + @@ -327,8 +362,8 @@ - - + + @@ -339,8 +374,8 @@ - - + + @@ -360,8 +395,8 @@ - - + + @@ -375,8 +410,8 @@ - - + + @@ -387,8 +422,8 @@ - - + + @@ -396,8 +431,8 @@ - - + + @@ -408,8 +443,8 @@ - - + + @@ -429,8 +464,8 @@ - - + + @@ -444,8 +479,8 @@ - - + + @@ -453,8 +488,8 @@ - - + + @@ -462,8 +497,8 @@ - - + + @@ -471,8 +506,8 @@ - - + + @@ -489,8 +524,8 @@ - - + + diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml b/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml index e5ed9180c..25d07148b 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml +++ b/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml @@ -82,6 +82,10 @@ org.wso2.carbon.identity.metadata.saml2 org.wso2.carbon.identity.idp.metadata.saml2 + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java index 1df361b71..b53772852 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java @@ -27,6 +27,7 @@ public class SAMLSSOUIConstants { public static final String SERVICE_PROVIDERS_DATA_PAGE_COUNT = "page_count"; public static final String ENABLE_ATTRIBUTE_PROFILE = "enableAttributeProfile"; public static final String ENABLE_SINGLE_LOGOUT = "enableSingleLogout"; + public static final String SLO_TYPE = "singleLogoutType"; public static final String SLO_RESPONSE_URL = "sloResponseURL"; public static final String SLO_REQUEST_URL = "sloRequestURL"; public static final String ENABLE_RESPONSE_SIGNATURE = "enableResponseSignature"; diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java index 1fb51d141..ce3c72fb0 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.sso.saml.ui; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; import java.util.ArrayList; @@ -136,6 +137,45 @@ public static boolean isSingleLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProv return false; } + //Check front-Channel logout enable and if not enable return false + public static boolean isFrontchannelLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + if (isSpEdit) { + if (provider != null) { + return provider.getDoFrontChannelLogout(); + } + } + return false; + } + + //Check front-Channel logout HTTP Redirect Binding enable and if not enable return false + public static boolean isHTTPRedirectBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + if (isSpEdit) { + if (provider != null) { + if(SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING.equals + (provider.getFrontChannelLogoutMethod())) { + return true; + } + } + } + return false; + } + + //Check front-Channel logout HTTP Post Binding enable and if not enable return false + public static boolean isHTTPPostBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + if (isSpEdit) { + if (provider != null) { + if(SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING.equals + (provider.getFrontChannelLogoutMethod())) { + return true; + } + } + } + return false; + } + public static boolean isAttributeProfileEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { if (isSpEdit) { diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties index edcff928a..e7db587cc 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties @@ -115,3 +115,7 @@ sp.saml.metadata.certificate.warn=Since this the super tenant, Keystore is manag sp.enable.saml2.artifact.binding=Enable SAML2 Artifact Binding sp.enable.signature.validation.artifact.resolve=Enable Signature Validation in Artifact Resolve Request enable.saml2.ecp=Enable SAML Enhanced Client or Proxy (ECP) +enable.front.channel.http.redirect.binding = Front-Channel Logout (HTTP Redirect Binding) +enable.front.channel.http.post.binding = Front-Channel Logout (HTTP POST Binding) +enable.back.channel.logout = Back-Channel Logout +single.logout.type = Logout Method diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp index b87c8133f..2f7d1b018 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp @@ -127,15 +127,22 @@ } - function disableLogoutUrl(chkbx) { + function disableSingleLogout(chkbx) { if ($(chkbx).is(':checked')) { $("#sloResponseURL").prop('disabled', false); $("#sloRequestURL").prop('disabled', false); + $("#enableBackChannelLogout").prop('disabled',false); + $("#enableFrontChannelHTTPRedirectBinding").prop('disabled', false); + $("#enableFrontChannelHTTPPostBinding").prop('disabled', false); } else { $("#sloResponseURL").prop('disabled', true); $("#sloRequestURL").prop('disabled', true); + $("#enableBackChannelLogout").prop('disabled',true); + $("#enableFrontChannelHTTPRedirectBinding").prop('disabled', true); + $("#enableFrontChannelHTTPPostBinding").prop('disabled', true); $("#sloResponseURL").val(""); $("#sloRequestURL").val(""); + } } @@ -1271,11 +1278,14 @@ /> + onclick="disableSingleLogout(this);" + <%= isSingleLogoutEnabled(isEditSP, provider) ? "checked": ""%> + /> + + @@ -1284,7 +1294,8 @@ " - class="text-box-big" <%=(isEditSP && provider.getDoSingleLogout()) ? "" : "disabled=\"disabled\""%>> + class="text-box-big" <%=isSingleLogoutEnabled(isEditSP, provider) ? "" : "disabled=\"disabled\""%>> +
Single logout response accepting endpoint
@@ -1298,12 +1309,51 @@ " - class="text-box-big" <%=(isEditSP && provider.getDoSingleLogout()) ? "" : "disabled=\"disabled\""%>> + class="text-box-big" <%=isSingleLogoutEnabled(isEditSP, provider) ? "" : "disabled=\"disabled\""%>>
Single logout request accepting endpoint
+ + + + + + + + + + + + + + + + + + +
+ + <% boolean show = false; diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp index ba5d0c1b6..c33be6c69 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp @@ -28,6 +28,7 @@ <%@ page import="java.util.ResourceBundle" %> <%@ page import="org.owasp.encoder.Encode" %> <%@ page import="org.wso2.carbon.identity.core.util.IdentityUtil" %> +<%@ page import="org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants" %> <%@ page import="org.wso2.carbon.utils.ServerConstants" %> <%@ page import="java.util.ResourceBundle" %> +<%@ page import="org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants" %> Date: Thu, 31 Jan 2019 12:03:05 +0530 Subject: [PATCH 13/16] Add SAML Front-Channel Logout UI. --- .../pom.xml | 114 --------- .../saml/common/SAMLSSOProviderConstants.java | 4 +- .../carbon/identity/sso/saml/common/Util.java | 238 ------------------ .../identity/sso/saml/common/UtilTest.java | 136 ---------- .../src/test/resources/testng.xml | 26 -- .../IdentitySAMLSSOConfigService.wsdl | 2 +- .../resources/IdentitySAMLSSOService.wsdl | 46 ++-- .../sso/saml/ui/SAMLSSOUIConstants.java | 6 +- .../identity/sso/saml/ui/SAMLSSOUIUtil.java | 53 ++-- .../web/sso-saml/add_service_provider.jsp | 5 +- ..._service_provider_finish-ajaxprocessor.jsp | 13 +- .../sso-saml/add_service_provider_finish.jsp | 12 +- .../org.wso2.carbon.identity.sso.saml/pom.xml | 4 + .../identity/sso/saml/SAMLSSOConstants.java | 2 + .../saml/admin/FileBasedConfigManager.java | 15 ++ .../sso/saml/admin/SAMLSSOConfigAdmin.java | 6 +- .../sso/saml/dto/SAMLSSOAuthnReqDTO.java | 10 +- .../saml/dto/SAMLSSOServiceProviderDTO.java | 10 +- .../IdPInitSSOAuthnRequestProcessor.java | 4 +- .../SPInitSSOAuthnRequestProcessor.java | 5 +- .../pom.xml | 5 + .../pom.xml | 5 + pom.xml | 2 +- 23 files changed, 119 insertions(+), 604 deletions(-) delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml diff --git a/components/org.wso2.carbon.identity.sso.saml.common/pom.xml b/components/org.wso2.carbon.identity.sso.saml.common/pom.xml index 3592e4cf5..32927560a 100644 --- a/components/org.wso2.carbon.identity.sso.saml.common/pom.xml +++ b/components/org.wso2.carbon.identity.sso.saml.common/pom.xml @@ -32,18 +32,6 @@ http://www.wso2.com - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.core - - - org.wso2.carbon - org.wso2.carbon.ui - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.base - org.wso2.orbit.org.opensaml opensaml @@ -52,40 +40,6 @@ org.wso2.carbon org.wso2.carbon.logging - - org.wso2.carbon.identity.inbound.auth.saml2 - org.wso2.carbon.identity.sso.saml.stub - - - org.apache.httpcomponents.wso2 - httpcore - - - org.testng - testng - test - - - org.powermock - powermock-module-testng - test - - - org.powermock - powermock-api-mockito - test - - - org.jacoco - org.jacoco.agent - runtime - test - - - org.slf4j - slf4j-api - test - @@ -100,13 +54,6 @@ ${project.artifactId} javax.servlet.http; version="${imp.pkg.version.javax.servlet}", - org.wso2.carbon.ui.util; version="${carbon.kernel.package.import.version.range}", - org.wso2.carbon.identity.base; - version="${carbon.identity.framework.imp.pkg.version.range}", - org.wso2.carbon.identity.core.util; - version="${carbon.identity.framework.imp.pkg.version.range}", - org.wso2.carbon.identity.sso.saml.stub.types; - version="${identity.inbound.auth.saml.imp.pkg.version.range}", org.apache.commons.logging; version="${commons-logging.osgi.version.range}", @@ -116,67 +63,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - ${maven.surefire.plugin.version} - - - src/test/resources/testng.xml - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - default-prepare-agent - - prepare-agent - - - - default-prepare-agent-integration - - prepare-agent-integration - - - - default-report - - report - - - - default-report-integration - - report-integration - - - - default-check - - check - - - - - BUNDLE - - - COMPLEXITY - COVEREDRATIO - - - - - - - - - diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java index e395c0f9c..7bb1362e3 100644 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java @@ -63,8 +63,8 @@ public class SAMLSSOProviderConstants { public static final String LOGIN_PAGE = "customLoginPage"; //Front Channel Logout Methods - public static final String ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; - public static final String ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING = "HTTPPostBinding"; + public static final String HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; + public static final String HTTP_POST_BINDING = "HTTPPostBinding"; private SAMLSSOProviderConstants() { } diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java deleted file mode 100644 index c1ed223f0..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * - * Copyright (c) 2011, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.sso.saml.common; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.base.IdentityConstants; -import org.wso2.carbon.identity.base.IdentityException; -import org.wso2.carbon.identity.core.util.IdentityUtil; -import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; -import org.wso2.carbon.ui.util.CharacterEncoder; - -import javax.servlet.http.HttpServletRequest; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class Util { - - private static final Set UNRESERVED_CHARACTERS = new HashSet(); - private static final Log log = LogFactory.getLog(Util.class); - - static { - for (char c = 'a'; c <= 'z'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - for (char c = 'A'; c <= 'A'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - for (char c = '0'; c <= '9'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - UNRESERVED_CHARACTERS.add(Character.valueOf('-')); - UNRESERVED_CHARACTERS.add(Character.valueOf('.')); - UNRESERVED_CHARACTERS.add(Character.valueOf('_')); - UNRESERVED_CHARACTERS.add(Character.valueOf('~')); - } - - private static int singleLogoutRetryCount = 5; - private static long singleLogoutRetryInterval = 60000; - - private Util() { - } - - public static int getSingleLogoutRetryCount() { - return singleLogoutRetryCount; - } - - public static void setSingleLogoutRetryCount(int singleLogoutRetryCount) { - Util.singleLogoutRetryCount = singleLogoutRetryCount; - } - - public static long getSingleLogoutRetryInterval() { - return singleLogoutRetryInterval; - } - - public static void setSingleLogoutRetryInterval(long singleLogoutRetryInterval) { - Util.singleLogoutRetryInterval = singleLogoutRetryInterval; - } - - /** - * This check if the status code is 2XX, check value between 200 and 300 - * - * @param status - * @return - */ - public static boolean isHttpSuccessStatusCode(int status) { - - return status >= 200 && status < 300; - } - - public static SAMLSSOServiceProviderDTO[] doPaging(int pageNumber, - SAMLSSOServiceProviderDTO[] serviceProviderSet) { - - int itemsPerPageInt = SAMLSSOProviderConstants.DEFAULT_ITEMS_PER_PAGE; - SAMLSSOServiceProviderDTO[] returnedServiceProviderSet; - - int startIndex = pageNumber * itemsPerPageInt; - int endIndex = (pageNumber + 1) * itemsPerPageInt; - if (serviceProviderSet.length > itemsPerPageInt) { - - returnedServiceProviderSet = new SAMLSSOServiceProviderDTO[itemsPerPageInt]; - } else { - returnedServiceProviderSet = new SAMLSSOServiceProviderDTO[serviceProviderSet.length]; - } - - for (int i = startIndex, j = 0; i < endIndex && i < serviceProviderSet.length; i++, j++) { - returnedServiceProviderSet[j] = serviceProviderSet[i]; - } - - return returnedServiceProviderSet; - } - - public static SAMLSSOServiceProviderDTO[] doFilter(String filter, - SAMLSSOServiceProviderDTO[] serviceProviderSet) { - String regPattern = filter.replace("*", ".*"); - List list = new ArrayList(); - for (SAMLSSOServiceProviderDTO serviceProvider : serviceProviderSet) { - if (serviceProvider.getIssuer().toLowerCase().matches(regPattern.toLowerCase())) { - list.add(serviceProvider); - } - } - SAMLSSOServiceProviderDTO[] filteredProviders = new SAMLSSOServiceProviderDTO[list.size()]; - for (int i = 0; i < list.size(); i++) { - filteredProviders[i] = list.get(i); - - } - - return filteredProviders; - } - - /** - * This is deprecated because this repo is saml and this is a openid method. - */ - @Deprecated - public static String getUserNameFromOpenID(String openid) throws IdentityException { - String caller = null; - String path = null; - URI uri = null; - String contextPath = "/openid/"; - - try { - uri = new URI(openid); - path = uri.getPath(); - } catch (URISyntaxException e) { - throw IdentityException.error("Invalid OpenID", e); - } - caller = path.substring(path.indexOf(contextPath) + contextPath.length(), path.length()); - return caller; - } - - /** - * Find the OpenID corresponding to the given user name. - * - * @param userName User name - * @return OpenID corresponding the given user name. - * @throws org.wso2.carbon.identity.base.IdentityException - * This is deprecated because this repo is saml and this is a openid method. - */ - @Deprecated - public static String getOpenID(String userName) throws IdentityException { - return generateOpenID(userName); - } - - /** - * Generate OpenID for a given user. - * - * @param user User - * @return Generated OpenID - * @throws org.wso2.carbon.identity.base.IdentityException - */ - //This is deprecated because this repo is saml and this is a openID method - @Deprecated - public static String generateOpenID(String user) throws IdentityException { - String openIDUserUrl = null; - String openID = null; - URI uri = null; - URL url = null; - openIDUserUrl = IdentityUtil.getProperty(IdentityConstants.ServerConfig.OPENID_USER_PATTERN); - user = normalizeUrlEncoding(user); - openID = openIDUserUrl + user; - try { - uri = new URI(openID); - } catch (URISyntaxException e) { - throw IdentityException.error("Invalid OpenID URL :" + openID, e); - } - try { - url = uri.normalize().toURL(); - if (url.getQuery() != null || url.getRef() != null) { - throw IdentityException.error("Invalid user name for OpenID :" + openID); - } - } catch (MalformedURLException e) { - throw IdentityException.error("Malformed OpenID URL :" + openID, e); - } - openID = url.toString(); - return openID; - } - - //this is deprecated because this repo is saml and this is a openID method - @Deprecated - private static String normalizeUrlEncoding(String text) { - - if (text == null) - return null; - - int len = text.length(); - StringBuilder normalized = new StringBuilder(len); - - for (int i = 0; i < len; i++) { - char current = text.charAt(i); - if (current == '%' && i < len - 2) { - String percentCode = text.substring(i, i + 3).toUpperCase(); - try { - String str = URLDecoder.decode(percentCode, "ISO-8859-1"); - char chr = str.charAt(0); - if (UNRESERVED_CHARACTERS.contains(Character.valueOf(chr))) - normalized.append(chr); - else - normalized.append(percentCode); - } catch (UnsupportedEncodingException e) { - if (log.isDebugEnabled()) { - log.debug("Url Encoding not supported.", e); - } - normalized.append(percentCode); - } - i += 2; - } else { - normalized.append(current); - } - } - return normalized.toString(); - } - -} diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java b/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java deleted file mode 100644 index 8fb016510..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.sso.saml.common; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; - -import java.util.Arrays; -import java.util.List; - -import static org.testng.Assert.assertEquals; - -/** - * Test Class for the Util. - */ -public class UtilTest { - - private static int singleLogoutRetryCount = 5; - private static long singleLogoutRetryInterval = 60000; - - @Test - public void testGetSingleLogoutRetryCount() throws Exception { - - int singleLogoutRetryC = Util.getSingleLogoutRetryCount(); - assertEquals(singleLogoutRetryC, singleLogoutRetryCount); - } - - @Test - public void testSetSingleLogoutRetryCount() throws Exception { - - Util.setSingleLogoutRetryCount(6); - assertEquals(Util.getSingleLogoutRetryCount(), 6); - Util.setSingleLogoutRetryCount(singleLogoutRetryCount); - } - - @Test - public void testGetSingleLogoutRetryInterval() throws Exception { - - long singleLogoutRetryInt = Util.getSingleLogoutRetryInterval(); - assertEquals(singleLogoutRetryInt, singleLogoutRetryInterval); - } - - @Test - public void testSetSingleLogoutRetryInterval() throws Exception { - - Util.setSingleLogoutRetryInterval(70000); - assertEquals(Util.getSingleLogoutRetryInterval(), 70000); - Util.setSingleLogoutRetryInterval(singleLogoutRetryInterval); - } - - @DataProvider(name = "provideHttpStatusCode") - public Object[][] createData1() { - return new Object[][]{ - {200, true}, - {302, false}, - {100, false}, - {500, false}, - {404, false}, - {202, true}, - {0, false}, - }; - } - - @Test(dataProvider = "provideHttpStatusCode") - public void testIsHttpSuccessStatusCode(int status, boolean value) { - - assertEquals(Util.isHttpSuccessStatusCode(status), value); - } - - @DataProvider(name = "provideServiceProvider") - public Object[][] createServiceProvider() { - - SAMLSSOServiceProviderDTO SP1 = new SAMLSSOServiceProviderDTO(); - SP1.setIssuer("test1"); - SAMLSSOServiceProviderDTO SP2 = new SAMLSSOServiceProviderDTO(); - SP2.setIssuer("test2="); - SAMLSSOServiceProviderDTO SP3 = new SAMLSSOServiceProviderDTO(); - SP3.setIssuer("test3"); - SAMLSSOServiceProviderDTO SP4 = new SAMLSSOServiceProviderDTO(); - SP4.setIssuer("test4"); - SAMLSSOServiceProviderDTO SP5 = new SAMLSSOServiceProviderDTO(); - SP5.setIssuer("test5="); - SAMLSSOServiceProviderDTO SP6 = new SAMLSSOServiceProviderDTO(); - SP6.setIssuer("test6="); - SAMLSSOServiceProviderDTO[] serviceProviderSet1 = new SAMLSSOServiceProviderDTO[]{SP1, SP2, SP3}; - SAMLSSOServiceProviderDTO[] serviceProviderSet1pattern = new SAMLSSOServiceProviderDTO[]{SP2}; - SAMLSSOServiceProviderDTO[] serviceProviderSet2 = new SAMLSSOServiceProviderDTO[]{SP1, SP2, SP3, SP4, SP5, SP6}; - SAMLSSOServiceProviderDTO[] serviceProviderSet2pattern = new SAMLSSOServiceProviderDTO[]{SP2, SP5, SP6}; - - return new Object[][]{ - {serviceProviderSet1, serviceProviderSet1pattern}, - {serviceProviderSet2, serviceProviderSet2pattern}}; - } - - @Test(dataProvider = "provideServiceProvider") - public void testDoPaging(SAMLSSOServiceProviderDTO[] serviceProviderSet, - SAMLSSOServiceProviderDTO[] serviceProviderSetpattern) throws Exception { - - SAMLSSOServiceProviderDTO[] returnServiceProviderSet = Util.doPaging(0, serviceProviderSet); - Assert.assertTrue(assertSSOproviderArray(returnServiceProviderSet, serviceProviderSet)); - } - - @Test(dataProvider = "provideServiceProvider") - public void testDoFilter(SAMLSSOServiceProviderDTO[] serviceProviderSet, - SAMLSSOServiceProviderDTO[] serviceProviderSetpattern) throws Exception { - - SAMLSSOServiceProviderDTO[] returnServiceProviderSet = - Util.doFilter("^([A-Za-z0-9+/])*=$", serviceProviderSet); - Assert.assertTrue(assertSSOproviderArray(returnServiceProviderSet, serviceProviderSetpattern)); - } - - public boolean assertSSOproviderArray(SAMLSSOServiceProviderDTO[] actual, SAMLSSOServiceProviderDTO[] expected) { - - SAMLSSOServiceProviderDTO[] expectedaArray = Arrays.copyOfRange(expected, 0, actual.length); - return Arrays.deepEquals(actual, expectedaArray); - } - -} diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml b/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml deleted file mode 100644 index f06fabd9c..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl index 075619ee4..a68dc6894 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl @@ -216,7 +216,7 @@ - + diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl index 95df16fd4..fcd116863 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl @@ -30,52 +30,52 @@ - + - - - + - + - - - - - + - + - - - + - + - + + + - + - + - + - + + + + + + + - + - + @@ -170,7 +170,7 @@ - + diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java index b53772852..5244f6828 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java @@ -27,7 +27,6 @@ public class SAMLSSOUIConstants { public static final String SERVICE_PROVIDERS_DATA_PAGE_COUNT = "page_count"; public static final String ENABLE_ATTRIBUTE_PROFILE = "enableAttributeProfile"; public static final String ENABLE_SINGLE_LOGOUT = "enableSingleLogout"; - public static final String SLO_TYPE = "singleLogoutType"; public static final String SLO_RESPONSE_URL = "sloResponseURL"; public static final String SLO_REQUEST_URL = "sloRequestURL"; public static final String ENABLE_RESPONSE_SIGNATURE = "enableResponseSignature"; @@ -64,6 +63,11 @@ public class SAMLSSOUIConstants { public static final String SESSION_ATTRIBUTE_NAME_APPLICATION_CERTIFICATE = "applicationCertificate"; public static final String ENABLE_SAML2_ECP = "enableSAML2ECP"; + public static final String SLO_TYPE = "singleLogoutType"; + //Front Channel Logout Methods +// public static final String HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; +// public static final String HTTP_POST_BINDING = "HTTPPostBinding"; + private SAMLSSOUIConstants() { } } diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java index ce3c72fb0..df1b579c8 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java @@ -137,43 +137,40 @@ public static boolean isSingleLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProv return false; } - //Check front-Channel logout enable and if not enable return false - public static boolean isFrontchannelLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + /** + * Check front-Channel logout enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration. + * @return boolean true if front channel logout enabled. + */ + public static boolean isFrontChannelLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { - if (isSpEdit) { - if (provider != null) { - return provider.getDoFrontChannelLogout(); - } - } - return false; + return (isSpEdit && provider != null && provider.getDoFrontChannelLogout()); } - //Check front-Channel logout HTTP Redirect Binding enable and if not enable return false + /** + * Check front-Channel logout HTTP Redirect Binding enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration. + * @return boolean true if redirect binding enabled. + */ public static boolean isHTTPRedirectBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { - if (isSpEdit) { - if (provider != null) { - if(SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING.equals - (provider.getFrontChannelLogoutMethod())) { - return true; - } - } - } - return false; + return (isSpEdit && provider != null && SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING.equals + (provider.getFrontChannelLogoutBinding())); + } - //Check front-Channel logout HTTP Post Binding enable and if not enable return false + /** + * Check front-Channel logout HTTP Post Binding enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration + * @return boolean true if post binding enabled. + */ public static boolean isHTTPPostBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { - if (isSpEdit) { - if (provider != null) { - if(SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING.equals - (provider.getFrontChannelLogoutMethod())) { - return true; - } - } - } - return false; + return (isSpEdit && provider != null && SAMLSSOProviderConstants.HTTP_POST_BINDING.equals + (provider.getFrontChannelLogoutBinding())) ; } public static boolean isAttributeProfileEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp index 2f7d1b018..d7a0aca76 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp @@ -129,6 +129,9 @@ function disableSingleLogout(chkbx) { if ($(chkbx).is(':checked')) { + if(!$("#enableFrontChannelHTTPRedirectBinding").is(':checked') && !$("#enableFrontChannelHTTPPostBinding").is(':checked')) { + $("#enableBackChannelLogout").prop('checked',true); + } $("#sloResponseURL").prop('disabled', false); $("#sloRequestURL").prop('disabled', false); $("#enableBackChannelLogout").prop('disabled',false); @@ -1325,7 +1328,7 @@ diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp index c33be6c69..f5b6dd64f 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp @@ -99,17 +99,14 @@ if (StringUtils.isNotBlank(request.getParameter(SAMLSSOUIConstants.SLO_REQUEST_URL))) { serviceProviderDTO.setSloRequestURL(request.getParameter(SAMLSSOUIConstants.SLO_REQUEST_URL)); } - if (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING - .equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { + if (SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING.equals(request.getParameter + (SAMLSSOUIConstants.SLO_TYPE))) { serviceProviderDTO.setDoFrontChannelLogout(true); - serviceProviderDTO.setFrontChannelLogoutMethod - (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING); + serviceProviderDTO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING); } - if (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING - .equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { + if (SAMLSSOProviderConstants.HTTP_POST_BINDING.equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { serviceProviderDTO.setDoFrontChannelLogout(true); - serviceProviderDTO.setFrontChannelLogoutMethod - (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING); + serviceProviderDTO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_POST_BINDING); } } if (Boolean.parseBoolean(request.getParameter(SAMLSSOUIConstants.ENABLE_RESPONSE_SIGNATURE))) { diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish.jsp index ef34a4ae8..8c68eb2dd 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish.jsp @@ -73,17 +73,13 @@ if ("true".equals(request.getParameter("enableSingleLogout"))) { serviceProviderDTO.setDoSingleLogout(true); } - if (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING - .equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { + if (SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING.equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { serviceProviderDTO.setDoFrontChannelLogout(true); - serviceProviderDTO.setFrontChannelLogoutMethod - (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_REDIRECT_BINDING); + serviceProviderDTO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING); } - if (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING - .equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { + if (SAMLSSOProviderConstants.HTTP_POST_BINDING.equals(request.getParameter(SAMLSSOUIConstants.SLO_TYPE))) { serviceProviderDTO.setDoFrontChannelLogout(true); - serviceProviderDTO.setFrontChannelLogoutMethod - (SAMLSSOProviderConstants.ENABLE_FRONT_CHANNEL_HTTP_POST_BINDING); + serviceProviderDTO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_POST_BINDING); } if ("true".equals(request.getParameter("enableResponseSignature"))) { serviceProviderDTO.setDoSignResponse(true); diff --git a/components/org.wso2.carbon.identity.sso.saml/pom.xml b/components/org.wso2.carbon.identity.sso.saml/pom.xml index ef8c86c92..13fe6475b 100644 --- a/components/org.wso2.carbon.identity.sso.saml/pom.xml +++ b/components/org.wso2.carbon.identity.sso.saml/pom.xml @@ -93,6 +93,10 @@ org.wso2.carbon.identity.metadata.saml2 org.wso2.carbon.identity.sp.metadata.saml2 + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.testng testng diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java index 211e49a67..13616e975 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java @@ -160,6 +160,8 @@ public static class FileBasedSPConfig { public static final String USE_AUTHENTICATED_USER_DOMAIN_CRYPTO = "SSOService.UseAuthenticatedUserDomainCrypto"; public static final String RETURN_TO_URL_LIST = "ReturnToURLList"; public static final String RETURN_TO_URL = "ReturnToURL"; + public static final String FRONT_CHANNEL_LOGOUT = "EnableFrontChannelLogout"; + public static final String FRONT_CHANNEL_LOGOUT_BINDING = "FrontChannelLogoutBinding"; private FileBasedSPConfig() { } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java index f141470c1..d3c479817 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.SSOServiceProviderConfigManager; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil; import java.io.File; @@ -158,6 +159,20 @@ private SAMLSSOServiceProviderDO[] readServiceProvidersFromFile() { .fillURLPlaceholders(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SLO_REQUEST_URL))); } + if ((getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.FRONT_CHANNEL_LOGOUT)) != null) { + spDO.setDoFrontChannelLogout(Boolean.valueOf(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig + .FRONT_CHANNEL_LOGOUT))); + if(spDO.isDoFrontChannelLogout()) { + if (getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.FRONT_CHANNEL_LOGOUT_BINDING) != null) { + spDO.setFrontChannelLogoutBinding(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig + .FRONT_CHANNEL_LOGOUT_BINDING)); + } else { + // Default is redirect-binding. + spDO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING); + } + } + } + if ((getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SIGN_ASSERTION)) != null) { signAssertion = Boolean.valueOf(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SIGN_ASSERTION)); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java index 0f360c235..003b6693b 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java @@ -193,7 +193,7 @@ private SAMLSSOServiceProviderDO createSAMLSSOServiceProviderDO(SAMLSSOServicePr serviceProviderDO.setCertAlias(serviceProviderDTO.getCertAlias()); serviceProviderDO.setDoSingleLogout(serviceProviderDTO.isDoSingleLogout()); serviceProviderDO.setDoFrontChannelLogout(serviceProviderDTO.isDoFrontChannelLogout()); - serviceProviderDO.setFrontChannelLogoutMethod(serviceProviderDTO.getFrontChannelLogoutMethod()); + serviceProviderDO.setFrontChannelLogoutBinding(serviceProviderDTO.getFrontChannelLogoutBinding()); serviceProviderDO.setSloResponseURL(serviceProviderDTO.getSloResponseURL()); serviceProviderDO.setSloRequestURL(serviceProviderDTO.getSloRequestURL()); serviceProviderDO.setLoginPageURL(serviceProviderDTO.getLoginPageURL()); @@ -285,7 +285,7 @@ private SAMLSSOServiceProviderDTO createSAMLSSOServiceProviderDTO(SAMLSSOService serviceProviderDTO.setDoSingleLogout(serviceProviderDO.isDoSingleLogout()); serviceProviderDTO.setDoFrontChannelLogout(serviceProviderDO.isDoFrontChannelLogout()); - serviceProviderDTO.setFrontChannelLogoutMethod(serviceProviderDO.getFrontChannelLogoutMethod()); + serviceProviderDTO.setFrontChannelLogoutBinding(serviceProviderDO.getFrontChannelLogoutBinding()); serviceProviderDTO.setLoginPageURL(serviceProviderDO.getLoginPageURL()); serviceProviderDTO.setSloRequestURL(serviceProviderDO.getSloRequestURL()); serviceProviderDTO.setSloResponseURL(serviceProviderDO.getSloResponseURL()); @@ -363,7 +363,7 @@ public SAMLSSOServiceProviderInfoDTO getServiceProviders() throws IdentityExcept providerDTO.setDoSignAssertions(providerDO.isDoSignAssertions()); providerDTO.setDoSingleLogout(providerDO.isDoSingleLogout()); providerDTO.setDoFrontChannelLogout(providerDO.isDoFrontChannelLogout()); - providerDTO.setFrontChannelLogoutMethod(providerDO.getFrontChannelLogoutMethod()); + providerDTO.setFrontChannelLogoutBinding(providerDO.getFrontChannelLogoutBinding()); providerDTO.setAssertionQueryRequestProfileEnabled(providerDO.isAssertionQueryRequestProfileEnabled()); providerDTO.setSupportedAssertionQueryRequestTypes(providerDO.getSupportedAssertionQueryRequestTypes()); providerDTO.setEnableSAML2ArtifactBinding(providerDO.isEnableSAML2ArtifactBinding()); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java index fb7067983..35d9ff169 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java @@ -57,7 +57,7 @@ public class SAMLSSOAuthnReqDTO implements Serializable { private String[] requestedRecipients; private boolean doSingleLogout; private boolean doFrontChannelLogout; - private String frontChannelLogoutMethod; + private String frontChannelLogoutBinding; private boolean doSignResponse; private boolean doSignAssertions; private boolean isStratosDeployment = false; @@ -299,14 +299,14 @@ public void setDoFrontChannelLogout(boolean doFrontChannelLogout) { this.doFrontChannelLogout = doFrontChannelLogout; } - public String getFrontChannelLogoutMethod() { + public String getFrontChannelLogoutBinding() { - return frontChannelLogoutMethod; + return frontChannelLogoutBinding; } - public void setFrontChannelLogoutMethod(String frontChannelLogoutMethod) { + public void setFrontChannelLogoutBinding(String frontChannelLogoutBinding) { - this.frontChannelLogoutMethod = frontChannelLogoutMethod; + this.frontChannelLogoutBinding = frontChannelLogoutBinding; } /** diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java index 363d9e4ad..df46c34f0 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java @@ -46,7 +46,7 @@ public class SAMLSSOServiceProviderDTO implements Serializable { private String sloRequestURL; private String loginPageURL; private String attributeConsumingServiceIndex; - private String frontChannelLogoutMethod; + private String frontChannelLogoutBinding; private boolean doSingleLogout; private boolean doSignAssertions; private boolean doSignResponse; @@ -255,14 +255,14 @@ public void setDoFrontChannelLogout(boolean doFrontChannelLogout) { this.doFrontChannelLogout = doFrontChannelLogout; } - public String getFrontChannelLogoutMethod() { + public String getFrontChannelLogoutBinding() { - return frontChannelLogoutMethod; + return frontChannelLogoutBinding; } - public void setFrontChannelLogoutMethod(String frontChannelLogoutMethod) { + public void setFrontChannelLogoutBinding(String frontChannelLogoutBinding) { - this.frontChannelLogoutMethod = frontChannelLogoutMethod; + this.frontChannelLogoutBinding = frontChannelLogoutBinding; } /** diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java index 162302981..f366cdfb1 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java @@ -130,7 +130,7 @@ public SAMLSSORespDTO process(SAMLSSOAuthnReqDTO authnReqDTO, String sessionId, spDO.setTenantDomain(authnReqDTO.getTenantDomain()); spDO.setDoSingleLogout(authnReqDTO.isDoSingleLogout()); spDO.setDoFrontChannelLogout(authnReqDTO.isDoFrontChannelLogout()); - spDO.setFrontChannelLogoutMethod(authnReqDTO.getFrontChannelLogoutMethod()); + spDO.setFrontChannelLogoutBinding(authnReqDTO.getFrontChannelLogoutBinding()); spDO.setIdPInitSLOEnabled(authnReqDTO.isIdPInitSLOEnabled()); spDO.setAssertionConsumerUrls(authnReqDTO.getAssertionConsumerURLs()); spDO.setIdpInitSLOReturnToURLs(authnReqDTO.getIdpInitSLOReturnToURLs()); @@ -262,6 +262,8 @@ private void populateServiceProviderConfigs(SAMLSSOServiceProviderDO ssoIdpConfi authnReqDTO.setDoSingleLogout(ssoIdpConfigs.isDoSingleLogout()); authnReqDTO.setSloResponseURL(ssoIdpConfigs.getSloResponseURL()); authnReqDTO.setSloRequestURL(ssoIdpConfigs.getSloRequestURL()); + authnReqDTO.setDoFrontChannelLogout(ssoIdpConfigs.isDoFrontChannelLogout()); + authnReqDTO.setFrontChannelLogoutBinding(ssoIdpConfigs.getFrontChannelLogoutBinding()); authnReqDTO.setDoSignResponse(ssoIdpConfigs.isDoSignResponse()); authnReqDTO.setDoSignAssertions(ssoIdpConfigs.isDoSignAssertions()); authnReqDTO.setRequestedClaims(ssoIdpConfigs.getRequestedClaims()); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java index eafa729a1..225ca31d8 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java @@ -17,7 +17,6 @@ */ package org.wso2.carbon.identity.sso.saml.processors; -import org.apache.axis2.transport.http.ServletBasedOutTransportInfo; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -159,7 +158,7 @@ public SAMLSSORespDTO process(SAMLSSOAuthnReqDTO authnReqDTO, String sessionId, spDO.setNameIDFormat(authnReqDTO.getNameIDFormat()); spDO.setDoSingleLogout(authnReqDTO.isDoSingleLogout()); spDO.setDoFrontChannelLogout(authnReqDTO.isDoFrontChannelLogout()); - spDO.setFrontChannelLogoutMethod(authnReqDTO.getFrontChannelLogoutMethod()); + spDO.setFrontChannelLogoutBinding(authnReqDTO.getFrontChannelLogoutBinding()); spDO.setIdPInitSLOEnabled(authnReqDTO.isIdPInitSLOEnabled()); spDO.setAssertionConsumerUrls(authnReqDTO.getAssertionConsumerURLs()); spDO.setIdpInitSLOReturnToURLs(authnReqDTO.getIdpInitSLOReturnToURLs()); @@ -290,7 +289,7 @@ private void populateServiceProviderConfigs(SAMLSSOServiceProviderDO ssoIdpConfi authnReqDTO.setNameIDFormat(ssoIdpConfigs.getNameIDFormat()); authnReqDTO.setDoSingleLogout(ssoIdpConfigs.isDoSingleLogout()); authnReqDTO.setDoFrontChannelLogout(ssoIdpConfigs.isDoFrontChannelLogout()); - authnReqDTO.setFrontChannelLogoutMethod(ssoIdpConfigs.getFrontChannelLogoutMethod()); + authnReqDTO.setFrontChannelLogoutBinding(ssoIdpConfigs.getFrontChannelLogoutBinding()); authnReqDTO.setSloResponseURL(ssoIdpConfigs.getSloResponseURL()); authnReqDTO.setSloRequestURL(ssoIdpConfigs.getSloRequestURL()); authnReqDTO.setDoSignResponse(ssoIdpConfigs.isDoSignResponse()); diff --git a/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml b/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml index 951287862..aa6515b64 100644 --- a/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml +++ b/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml @@ -39,6 +39,10 @@ org.wso2.carbon.identity.inbound.auth.saml2 org.wso2.carbon.identity.sso.saml + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.wso2.orbit.org.opensaml opensaml @@ -105,6 +109,7 @@ org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml + org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.common org.wso2.orbit.org.opensaml:opensaml org.wso2.orbit.joda-time:joda-time diff --git a/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml b/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml index 014bdec73..d245ba210 100644 --- a/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml +++ b/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml @@ -43,6 +43,10 @@ org.wso2.carbon.identity.inbound.auth.saml2 org.wso2.carbon.identity.sso.saml.stub + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.wso2.orbit.org.opensaml opensaml @@ -78,6 +82,7 @@ org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.ui org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.stub + org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.common org.wso2.orbit.org.apache.httpcomponents:httpclient org.wso2.orbit.org.opensaml:opensaml org.wso2.orbit.joda-time:joda-time diff --git a/pom.xml b/pom.xml index e3078990c..c65bd191f 100644 --- a/pom.xml +++ b/pom.xml @@ -424,7 +424,7 @@ 4.4.22 - 5.12.240 + 5.12.258 [5.0.0, 6.0.0) From 494e3da99bf5c20c45d8e362b589d98579f338ce Mon Sep 17 00:00:00 2001 From: Hasini Witharana Date: Fri, 25 Jan 2019 10:09:41 +0530 Subject: [PATCH 14/16] add SAML Front-Channel Logout UI --- .../pom.xml | 114 ------ .../saml/common/SAMLSSOProviderConstants.java | 4 + .../carbon/identity/sso/saml/common/Util.java | 238 ------------- .../identity/sso/saml/common/UtilTest.java | 136 ------- .../src/test/resources/testng.xml | 26 -- .../IdentitySAMLSSOConfigService.wsdl | 42 +-- .../resources/IdentitySAMLSSOService.wsdl | 333 ++++++++++-------- .../pom.xml | 4 + .../sso/saml/ui/SAMLSSOUIConstants.java | 5 + .../identity/sso/saml/ui/SAMLSSOUIUtil.java | 37 ++ .../sso/saml/ui/i18n/Resources.properties | 4 + .../web/sso-saml/add_service_provider.jsp | 63 +++- ..._service_provider_finish-ajaxprocessor.jsp | 11 +- .../sso-saml/add_service_provider_finish.jsp | 9 + .../org.wso2.carbon.identity.sso.saml/pom.xml | 4 + .../identity/sso/saml/SAMLSSOConstants.java | 2 + .../saml/admin/FileBasedConfigManager.java | 15 + .../sso/saml/admin/SAMLSSOConfigAdmin.java | 6 + .../sso/saml/dto/SAMLSSOAuthnReqDTO.java | 22 ++ .../saml/dto/SAMLSSOServiceProviderDTO.java | 22 ++ .../IdPInitSSOAuthnRequestProcessor.java | 4 + .../SPInitSSOAuthnRequestProcessor.java | 5 +- .../saml/servlet/SAMLSSOProviderServlet.java | 57 +-- .../identity/sso/saml/util/SAMLSSOUtil.java | 14 +- .../pom.xml | 5 + .../pom.xml | 5 + pom.xml | 2 +- 27 files changed, 469 insertions(+), 720 deletions(-) delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java delete mode 100644 components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml diff --git a/components/org.wso2.carbon.identity.sso.saml.common/pom.xml b/components/org.wso2.carbon.identity.sso.saml.common/pom.xml index 3592e4cf5..32927560a 100644 --- a/components/org.wso2.carbon.identity.sso.saml.common/pom.xml +++ b/components/org.wso2.carbon.identity.sso.saml.common/pom.xml @@ -32,18 +32,6 @@ http://www.wso2.com - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.core - - - org.wso2.carbon - org.wso2.carbon.ui - - - org.wso2.carbon.identity.framework - org.wso2.carbon.identity.base - org.wso2.orbit.org.opensaml opensaml @@ -52,40 +40,6 @@ org.wso2.carbon org.wso2.carbon.logging - - org.wso2.carbon.identity.inbound.auth.saml2 - org.wso2.carbon.identity.sso.saml.stub - - - org.apache.httpcomponents.wso2 - httpcore - - - org.testng - testng - test - - - org.powermock - powermock-module-testng - test - - - org.powermock - powermock-api-mockito - test - - - org.jacoco - org.jacoco.agent - runtime - test - - - org.slf4j - slf4j-api - test - @@ -100,13 +54,6 @@ ${project.artifactId} javax.servlet.http; version="${imp.pkg.version.javax.servlet}", - org.wso2.carbon.ui.util; version="${carbon.kernel.package.import.version.range}", - org.wso2.carbon.identity.base; - version="${carbon.identity.framework.imp.pkg.version.range}", - org.wso2.carbon.identity.core.util; - version="${carbon.identity.framework.imp.pkg.version.range}", - org.wso2.carbon.identity.sso.saml.stub.types; - version="${identity.inbound.auth.saml.imp.pkg.version.range}", org.apache.commons.logging; version="${commons-logging.osgi.version.range}", @@ -116,67 +63,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - ${maven.surefire.plugin.version} - - - src/test/resources/testng.xml - - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - default-prepare-agent - - prepare-agent - - - - default-prepare-agent-integration - - prepare-agent-integration - - - - default-report - - report - - - - default-report-integration - - report-integration - - - - default-check - - check - - - - - BUNDLE - - - COMPLEXITY - COVEREDRATIO - - - - - - - - - diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java index 8fde54aeb..7bb1362e3 100644 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/SAMLSSOProviderConstants.java @@ -62,6 +62,10 @@ public class SAMLSSOProviderConstants { public static final String LOGIN_PAGE = "customLoginPage"; + //Front Channel Logout Methods + public static final String HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; + public static final String HTTP_POST_BINDING = "HTTPPostBinding"; + private SAMLSSOProviderConstants() { } diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java b/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java deleted file mode 100644 index c1ed223f0..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/main/java/org/wso2/carbon/identity/sso/saml/common/Util.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * - * Copyright (c) 2011, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.sso.saml.common; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.base.IdentityConstants; -import org.wso2.carbon.identity.base.IdentityException; -import org.wso2.carbon.identity.core.util.IdentityUtil; -import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; -import org.wso2.carbon.ui.util.CharacterEncoder; - -import javax.servlet.http.HttpServletRequest; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class Util { - - private static final Set UNRESERVED_CHARACTERS = new HashSet(); - private static final Log log = LogFactory.getLog(Util.class); - - static { - for (char c = 'a'; c <= 'z'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - for (char c = 'A'; c <= 'A'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - for (char c = '0'; c <= '9'; c++) - UNRESERVED_CHARACTERS.add(Character.valueOf(c)); - - UNRESERVED_CHARACTERS.add(Character.valueOf('-')); - UNRESERVED_CHARACTERS.add(Character.valueOf('.')); - UNRESERVED_CHARACTERS.add(Character.valueOf('_')); - UNRESERVED_CHARACTERS.add(Character.valueOf('~')); - } - - private static int singleLogoutRetryCount = 5; - private static long singleLogoutRetryInterval = 60000; - - private Util() { - } - - public static int getSingleLogoutRetryCount() { - return singleLogoutRetryCount; - } - - public static void setSingleLogoutRetryCount(int singleLogoutRetryCount) { - Util.singleLogoutRetryCount = singleLogoutRetryCount; - } - - public static long getSingleLogoutRetryInterval() { - return singleLogoutRetryInterval; - } - - public static void setSingleLogoutRetryInterval(long singleLogoutRetryInterval) { - Util.singleLogoutRetryInterval = singleLogoutRetryInterval; - } - - /** - * This check if the status code is 2XX, check value between 200 and 300 - * - * @param status - * @return - */ - public static boolean isHttpSuccessStatusCode(int status) { - - return status >= 200 && status < 300; - } - - public static SAMLSSOServiceProviderDTO[] doPaging(int pageNumber, - SAMLSSOServiceProviderDTO[] serviceProviderSet) { - - int itemsPerPageInt = SAMLSSOProviderConstants.DEFAULT_ITEMS_PER_PAGE; - SAMLSSOServiceProviderDTO[] returnedServiceProviderSet; - - int startIndex = pageNumber * itemsPerPageInt; - int endIndex = (pageNumber + 1) * itemsPerPageInt; - if (serviceProviderSet.length > itemsPerPageInt) { - - returnedServiceProviderSet = new SAMLSSOServiceProviderDTO[itemsPerPageInt]; - } else { - returnedServiceProviderSet = new SAMLSSOServiceProviderDTO[serviceProviderSet.length]; - } - - for (int i = startIndex, j = 0; i < endIndex && i < serviceProviderSet.length; i++, j++) { - returnedServiceProviderSet[j] = serviceProviderSet[i]; - } - - return returnedServiceProviderSet; - } - - public static SAMLSSOServiceProviderDTO[] doFilter(String filter, - SAMLSSOServiceProviderDTO[] serviceProviderSet) { - String regPattern = filter.replace("*", ".*"); - List list = new ArrayList(); - for (SAMLSSOServiceProviderDTO serviceProvider : serviceProviderSet) { - if (serviceProvider.getIssuer().toLowerCase().matches(regPattern.toLowerCase())) { - list.add(serviceProvider); - } - } - SAMLSSOServiceProviderDTO[] filteredProviders = new SAMLSSOServiceProviderDTO[list.size()]; - for (int i = 0; i < list.size(); i++) { - filteredProviders[i] = list.get(i); - - } - - return filteredProviders; - } - - /** - * This is deprecated because this repo is saml and this is a openid method. - */ - @Deprecated - public static String getUserNameFromOpenID(String openid) throws IdentityException { - String caller = null; - String path = null; - URI uri = null; - String contextPath = "/openid/"; - - try { - uri = new URI(openid); - path = uri.getPath(); - } catch (URISyntaxException e) { - throw IdentityException.error("Invalid OpenID", e); - } - caller = path.substring(path.indexOf(contextPath) + contextPath.length(), path.length()); - return caller; - } - - /** - * Find the OpenID corresponding to the given user name. - * - * @param userName User name - * @return OpenID corresponding the given user name. - * @throws org.wso2.carbon.identity.base.IdentityException - * This is deprecated because this repo is saml and this is a openid method. - */ - @Deprecated - public static String getOpenID(String userName) throws IdentityException { - return generateOpenID(userName); - } - - /** - * Generate OpenID for a given user. - * - * @param user User - * @return Generated OpenID - * @throws org.wso2.carbon.identity.base.IdentityException - */ - //This is deprecated because this repo is saml and this is a openID method - @Deprecated - public static String generateOpenID(String user) throws IdentityException { - String openIDUserUrl = null; - String openID = null; - URI uri = null; - URL url = null; - openIDUserUrl = IdentityUtil.getProperty(IdentityConstants.ServerConfig.OPENID_USER_PATTERN); - user = normalizeUrlEncoding(user); - openID = openIDUserUrl + user; - try { - uri = new URI(openID); - } catch (URISyntaxException e) { - throw IdentityException.error("Invalid OpenID URL :" + openID, e); - } - try { - url = uri.normalize().toURL(); - if (url.getQuery() != null || url.getRef() != null) { - throw IdentityException.error("Invalid user name for OpenID :" + openID); - } - } catch (MalformedURLException e) { - throw IdentityException.error("Malformed OpenID URL :" + openID, e); - } - openID = url.toString(); - return openID; - } - - //this is deprecated because this repo is saml and this is a openID method - @Deprecated - private static String normalizeUrlEncoding(String text) { - - if (text == null) - return null; - - int len = text.length(); - StringBuilder normalized = new StringBuilder(len); - - for (int i = 0; i < len; i++) { - char current = text.charAt(i); - if (current == '%' && i < len - 2) { - String percentCode = text.substring(i, i + 3).toUpperCase(); - try { - String str = URLDecoder.decode(percentCode, "ISO-8859-1"); - char chr = str.charAt(0); - if (UNRESERVED_CHARACTERS.contains(Character.valueOf(chr))) - normalized.append(chr); - else - normalized.append(percentCode); - } catch (UnsupportedEncodingException e) { - if (log.isDebugEnabled()) { - log.debug("Url Encoding not supported.", e); - } - normalized.append(percentCode); - } - i += 2; - } else { - normalized.append(current); - } - } - return normalized.toString(); - } - -} diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java b/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java deleted file mode 100644 index 8fb016510..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/test/java/org/wso2/carbon/identity/sso/saml/common/UtilTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.wso2.carbon.identity.sso.saml.common; - -import org.testng.Assert; -import org.testng.annotations.DataProvider; -import org.testng.annotations.Test; -import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; - -import java.util.Arrays; -import java.util.List; - -import static org.testng.Assert.assertEquals; - -/** - * Test Class for the Util. - */ -public class UtilTest { - - private static int singleLogoutRetryCount = 5; - private static long singleLogoutRetryInterval = 60000; - - @Test - public void testGetSingleLogoutRetryCount() throws Exception { - - int singleLogoutRetryC = Util.getSingleLogoutRetryCount(); - assertEquals(singleLogoutRetryC, singleLogoutRetryCount); - } - - @Test - public void testSetSingleLogoutRetryCount() throws Exception { - - Util.setSingleLogoutRetryCount(6); - assertEquals(Util.getSingleLogoutRetryCount(), 6); - Util.setSingleLogoutRetryCount(singleLogoutRetryCount); - } - - @Test - public void testGetSingleLogoutRetryInterval() throws Exception { - - long singleLogoutRetryInt = Util.getSingleLogoutRetryInterval(); - assertEquals(singleLogoutRetryInt, singleLogoutRetryInterval); - } - - @Test - public void testSetSingleLogoutRetryInterval() throws Exception { - - Util.setSingleLogoutRetryInterval(70000); - assertEquals(Util.getSingleLogoutRetryInterval(), 70000); - Util.setSingleLogoutRetryInterval(singleLogoutRetryInterval); - } - - @DataProvider(name = "provideHttpStatusCode") - public Object[][] createData1() { - return new Object[][]{ - {200, true}, - {302, false}, - {100, false}, - {500, false}, - {404, false}, - {202, true}, - {0, false}, - }; - } - - @Test(dataProvider = "provideHttpStatusCode") - public void testIsHttpSuccessStatusCode(int status, boolean value) { - - assertEquals(Util.isHttpSuccessStatusCode(status), value); - } - - @DataProvider(name = "provideServiceProvider") - public Object[][] createServiceProvider() { - - SAMLSSOServiceProviderDTO SP1 = new SAMLSSOServiceProviderDTO(); - SP1.setIssuer("test1"); - SAMLSSOServiceProviderDTO SP2 = new SAMLSSOServiceProviderDTO(); - SP2.setIssuer("test2="); - SAMLSSOServiceProviderDTO SP3 = new SAMLSSOServiceProviderDTO(); - SP3.setIssuer("test3"); - SAMLSSOServiceProviderDTO SP4 = new SAMLSSOServiceProviderDTO(); - SP4.setIssuer("test4"); - SAMLSSOServiceProviderDTO SP5 = new SAMLSSOServiceProviderDTO(); - SP5.setIssuer("test5="); - SAMLSSOServiceProviderDTO SP6 = new SAMLSSOServiceProviderDTO(); - SP6.setIssuer("test6="); - SAMLSSOServiceProviderDTO[] serviceProviderSet1 = new SAMLSSOServiceProviderDTO[]{SP1, SP2, SP3}; - SAMLSSOServiceProviderDTO[] serviceProviderSet1pattern = new SAMLSSOServiceProviderDTO[]{SP2}; - SAMLSSOServiceProviderDTO[] serviceProviderSet2 = new SAMLSSOServiceProviderDTO[]{SP1, SP2, SP3, SP4, SP5, SP6}; - SAMLSSOServiceProviderDTO[] serviceProviderSet2pattern = new SAMLSSOServiceProviderDTO[]{SP2, SP5, SP6}; - - return new Object[][]{ - {serviceProviderSet1, serviceProviderSet1pattern}, - {serviceProviderSet2, serviceProviderSet2pattern}}; - } - - @Test(dataProvider = "provideServiceProvider") - public void testDoPaging(SAMLSSOServiceProviderDTO[] serviceProviderSet, - SAMLSSOServiceProviderDTO[] serviceProviderSetpattern) throws Exception { - - SAMLSSOServiceProviderDTO[] returnServiceProviderSet = Util.doPaging(0, serviceProviderSet); - Assert.assertTrue(assertSSOproviderArray(returnServiceProviderSet, serviceProviderSet)); - } - - @Test(dataProvider = "provideServiceProvider") - public void testDoFilter(SAMLSSOServiceProviderDTO[] serviceProviderSet, - SAMLSSOServiceProviderDTO[] serviceProviderSetpattern) throws Exception { - - SAMLSSOServiceProviderDTO[] returnServiceProviderSet = - Util.doFilter("^([A-Za-z0-9+/])*=$", serviceProviderSet); - Assert.assertTrue(assertSSOproviderArray(returnServiceProviderSet, serviceProviderSetpattern)); - } - - public boolean assertSSOproviderArray(SAMLSSOServiceProviderDTO[] actual, SAMLSSOServiceProviderDTO[] expected) { - - SAMLSSOServiceProviderDTO[] expectedaArray = Arrays.copyOfRange(expected, 0, actual.length); - return Arrays.deepEquals(actual, expectedaArray); - } - -} diff --git a/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml b/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml deleted file mode 100644 index f06fabd9c..000000000 --- a/components/org.wso2.carbon.identity.sso.saml.common/src/test/resources/testng.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl index 3323e2722..a68dc6894 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOConfigService.wsdl @@ -1,21 +1,21 @@ - + IdentitySAMLSSOConfigService - + - + - + @@ -26,36 +26,36 @@ - + - - - + - + - + - + - + - + - + + + - + - + @@ -207,6 +207,7 @@ + @@ -215,6 +216,7 @@ + @@ -236,7 +238,7 @@ - + @@ -248,11 +250,11 @@ - + - + @@ -847,4 +849,4 @@ -
+
\ No newline at end of file diff --git a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl index a280a99d4..fcd116863 100644 --- a/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl +++ b/components/org.wso2.carbon.identity.sso.saml.stub/src/main/resources/IdentitySAMLSSOService.wsdl @@ -1,130 +1,135 @@ - + IdentitySAMLSSOService - + - + - + + + + + + - + - + - - - + - + - - - - - - + - + - - - + - + - + + + - + - + - + - + + + + + + + - + - + - + - + - - - + - + - + - + + + + + + + + - + - + - + - - - - - + - + @@ -138,84 +143,64 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - + @@ -225,56 +210,115 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + - - + + + + + + + + + + + @@ -284,30 +328,21 @@ - - - - - - - - - - - - - + + + + - - + + @@ -318,8 +353,8 @@ - - + + @@ -327,8 +362,8 @@ - - + + @@ -339,8 +374,8 @@ - - + + @@ -360,8 +395,8 @@ - - + + @@ -375,8 +410,8 @@ - - + + @@ -387,8 +422,8 @@ - - + + @@ -396,8 +431,8 @@ - - + + @@ -408,8 +443,8 @@ - - + + @@ -429,8 +464,8 @@ - - + + @@ -444,8 +479,8 @@ - - + + @@ -453,8 +488,8 @@ - - + + @@ -462,8 +497,8 @@ - - + + @@ -471,8 +506,8 @@ - - + + @@ -489,8 +524,8 @@ - - + + diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml b/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml index e5ed9180c..25d07148b 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml +++ b/components/org.wso2.carbon.identity.sso.saml.ui/pom.xml @@ -82,6 +82,10 @@ org.wso2.carbon.identity.metadata.saml2 org.wso2.carbon.identity.idp.metadata.saml2 + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java index 1df361b71..5244f6828 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java @@ -63,6 +63,11 @@ public class SAMLSSOUIConstants { public static final String SESSION_ATTRIBUTE_NAME_APPLICATION_CERTIFICATE = "applicationCertificate"; public static final String ENABLE_SAML2_ECP = "enableSAML2ECP"; + public static final String SLO_TYPE = "singleLogoutType"; + //Front Channel Logout Methods +// public static final String HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; +// public static final String HTTP_POST_BINDING = "HTTPPostBinding"; + private SAMLSSOUIConstants() { } } diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java index 1fb51d141..df1b579c8 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIUtil.java @@ -18,6 +18,7 @@ package org.wso2.carbon.identity.sso.saml.ui; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.identity.sso.saml.stub.types.SAMLSSOServiceProviderDTO; import java.util.ArrayList; @@ -136,6 +137,42 @@ public static boolean isSingleLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProv return false; } + /** + * Check front-Channel logout enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration. + * @return boolean true if front channel logout enabled. + */ + public static boolean isFrontChannelLogoutEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + return (isSpEdit && provider != null && provider.getDoFrontChannelLogout()); + } + + /** + * Check front-Channel logout HTTP Redirect Binding enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration. + * @return boolean true if redirect binding enabled. + */ + public static boolean isHTTPRedirectBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + return (isSpEdit && provider != null && SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING.equals + (provider.getFrontChannelLogoutBinding())); + + } + + /** + * Check front-Channel logout HTTP Post Binding enable and if not enable return false. + * @param isSpEdit Operation on service provider, create or edit. + * @param provider SAML2 service provider configuration + * @return boolean true if post binding enabled. + */ + public static boolean isHTTPPostBindingEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { + + return (isSpEdit && provider != null && SAMLSSOProviderConstants.HTTP_POST_BINDING.equals + (provider.getFrontChannelLogoutBinding())) ; + } + public static boolean isAttributeProfileEnabled(boolean isSpEdit, SAMLSSOServiceProviderDTO provider) { if (isSpEdit) { diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties index edcff928a..e7db587cc 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/org/wso2/carbon/identity/sso/saml/ui/i18n/Resources.properties @@ -115,3 +115,7 @@ sp.saml.metadata.certificate.warn=Since this the super tenant, Keystore is manag sp.enable.saml2.artifact.binding=Enable SAML2 Artifact Binding sp.enable.signature.validation.artifact.resolve=Enable Signature Validation in Artifact Resolve Request enable.saml2.ecp=Enable SAML Enhanced Client or Proxy (ECP) +enable.front.channel.http.redirect.binding = Front-Channel Logout (HTTP Redirect Binding) +enable.front.channel.http.post.binding = Front-Channel Logout (HTTP POST Binding) +enable.back.channel.logout = Back-Channel Logout +single.logout.type = Logout Method diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp index b87c8133f..d7a0aca76 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider.jsp @@ -127,15 +127,25 @@ } - function disableLogoutUrl(chkbx) { + function disableSingleLogout(chkbx) { if ($(chkbx).is(':checked')) { + if(!$("#enableFrontChannelHTTPRedirectBinding").is(':checked') && !$("#enableFrontChannelHTTPPostBinding").is(':checked')) { + $("#enableBackChannelLogout").prop('checked',true); + } $("#sloResponseURL").prop('disabled', false); $("#sloRequestURL").prop('disabled', false); + $("#enableBackChannelLogout").prop('disabled',false); + $("#enableFrontChannelHTTPRedirectBinding").prop('disabled', false); + $("#enableFrontChannelHTTPPostBinding").prop('disabled', false); } else { $("#sloResponseURL").prop('disabled', true); $("#sloRequestURL").prop('disabled', true); + $("#enableBackChannelLogout").prop('disabled',true); + $("#enableFrontChannelHTTPRedirectBinding").prop('disabled', true); + $("#enableFrontChannelHTTPPostBinding").prop('disabled', true); $("#sloResponseURL").val(""); $("#sloRequestURL").val(""); + } } @@ -1271,11 +1281,14 @@ /> + onclick="disableSingleLogout(this);" + <%= isSingleLogoutEnabled(isEditSP, provider) ? "checked": ""%> + /> + + @@ -1284,7 +1297,8 @@ " - class="text-box-big" <%=(isEditSP && provider.getDoSingleLogout()) ? "" : "disabled=\"disabled\""%>> + class="text-box-big" <%=isSingleLogoutEnabled(isEditSP, provider) ? "" : "disabled=\"disabled\""%>> +
Single logout response accepting endpoint
@@ -1298,12 +1312,51 @@ " - class="text-box-big" <%=(isEditSP && provider.getDoSingleLogout()) ? "" : "disabled=\"disabled\""%>> + class="text-box-big" <%=isSingleLogoutEnabled(isEditSP, provider) ? "" : "disabled=\"disabled\""%>>
Single logout request accepting endpoint
+ + + + + + + + + + + + + + + + + + +
+ + <% boolean show = false; diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp index ba5d0c1b6..f5b6dd64f 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/resources/web/sso-saml/add_service_provider_finish-ajaxprocessor.jsp @@ -28,6 +28,7 @@ <%@ page import="java.util.ResourceBundle" %> <%@ page import="org.owasp.encoder.Encode" %> <%@ page import="org.wso2.carbon.identity.core.util.IdentityUtil" %> +<%@ page import="org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants" %> <%@ page import="org.wso2.carbon.utils.ServerConstants" %> <%@ page import="java.util.ResourceBundle" %> +<%@ page import="org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants" %> org.wso2.carbon.identity.metadata.saml2 org.wso2.carbon.identity.sp.metadata.saml2 + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.testng testng diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java index 211e49a67..13616e975 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java @@ -160,6 +160,8 @@ public static class FileBasedSPConfig { public static final String USE_AUTHENTICATED_USER_DOMAIN_CRYPTO = "SSOService.UseAuthenticatedUserDomainCrypto"; public static final String RETURN_TO_URL_LIST = "ReturnToURLList"; public static final String RETURN_TO_URL = "ReturnToURL"; + public static final String FRONT_CHANNEL_LOGOUT = "EnableFrontChannelLogout"; + public static final String FRONT_CHANNEL_LOGOUT_BINDING = "FrontChannelLogoutBinding"; private FileBasedSPConfig() { } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java index f141470c1..d3c479817 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/FileBasedConfigManager.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.SSOServiceProviderConfigManager; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.identity.sso.saml.util.SAMLSSOUtil; import java.io.File; @@ -158,6 +159,20 @@ private SAMLSSOServiceProviderDO[] readServiceProvidersFromFile() { .fillURLPlaceholders(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SLO_REQUEST_URL))); } + if ((getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.FRONT_CHANNEL_LOGOUT)) != null) { + spDO.setDoFrontChannelLogout(Boolean.valueOf(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig + .FRONT_CHANNEL_LOGOUT))); + if(spDO.isDoFrontChannelLogout()) { + if (getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.FRONT_CHANNEL_LOGOUT_BINDING) != null) { + spDO.setFrontChannelLogoutBinding(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig + .FRONT_CHANNEL_LOGOUT_BINDING)); + } else { + // Default is redirect-binding. + spDO.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING); + } + } + } + if ((getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SIGN_ASSERTION)) != null) { signAssertion = Boolean.valueOf(getTextValue(elem, SAMLSSOConstants.FileBasedSPConfig.SIGN_ASSERTION)); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java index 40074425d..003b6693b 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/admin/SAMLSSOConfigAdmin.java @@ -192,6 +192,8 @@ private SAMLSSOServiceProviderDO createSAMLSSOServiceProviderDO(SAMLSSOServicePr serviceProviderDO.setDefaultAssertionConsumerUrl(serviceProviderDTO.getDefaultAssertionConsumerUrl()); serviceProviderDO.setCertAlias(serviceProviderDTO.getCertAlias()); serviceProviderDO.setDoSingleLogout(serviceProviderDTO.isDoSingleLogout()); + serviceProviderDO.setDoFrontChannelLogout(serviceProviderDTO.isDoFrontChannelLogout()); + serviceProviderDO.setFrontChannelLogoutBinding(serviceProviderDTO.getFrontChannelLogoutBinding()); serviceProviderDO.setSloResponseURL(serviceProviderDTO.getSloResponseURL()); serviceProviderDO.setSloRequestURL(serviceProviderDTO.getSloRequestURL()); serviceProviderDO.setLoginPageURL(serviceProviderDTO.getLoginPageURL()); @@ -282,6 +284,8 @@ private SAMLSSOServiceProviderDTO createSAMLSSOServiceProviderDTO(SAMLSSOService } serviceProviderDTO.setDoSingleLogout(serviceProviderDO.isDoSingleLogout()); + serviceProviderDTO.setDoFrontChannelLogout(serviceProviderDO.isDoFrontChannelLogout()); + serviceProviderDTO.setFrontChannelLogoutBinding(serviceProviderDO.getFrontChannelLogoutBinding()); serviceProviderDTO.setLoginPageURL(serviceProviderDO.getLoginPageURL()); serviceProviderDTO.setSloRequestURL(serviceProviderDO.getSloRequestURL()); serviceProviderDTO.setSloResponseURL(serviceProviderDO.getSloResponseURL()); @@ -358,6 +362,8 @@ public SAMLSSOServiceProviderInfoDTO getServiceProviders() throws IdentityExcept providerDTO.setDoSignResponse(providerDO.isDoSignResponse()); providerDTO.setDoSignAssertions(providerDO.isDoSignAssertions()); providerDTO.setDoSingleLogout(providerDO.isDoSingleLogout()); + providerDTO.setDoFrontChannelLogout(providerDO.isDoFrontChannelLogout()); + providerDTO.setFrontChannelLogoutBinding(providerDO.getFrontChannelLogoutBinding()); providerDTO.setAssertionQueryRequestProfileEnabled(providerDO.isAssertionQueryRequestProfileEnabled()); providerDTO.setSupportedAssertionQueryRequestTypes(providerDO.getSupportedAssertionQueryRequestTypes()); providerDTO.setEnableSAML2ArtifactBinding(providerDO.isEnableSAML2ArtifactBinding()); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java index 0fd36b65d..35d9ff169 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOAuthnReqDTO.java @@ -56,6 +56,8 @@ public class SAMLSSOAuthnReqDTO implements Serializable { private String[] requestedAudiences; private String[] requestedRecipients; private boolean doSingleLogout; + private boolean doFrontChannelLogout; + private String frontChannelLogoutBinding; private boolean doSignResponse; private boolean doSignAssertions; private boolean isStratosDeployment = false; @@ -287,6 +289,26 @@ public void setDoSignAssertions(boolean doSignAssertions) { this.doSignAssertions = doSignAssertions; } + public boolean isDoFrontChannelLogout() { + + return doFrontChannelLogout; + } + + public void setDoFrontChannelLogout(boolean doFrontChannelLogout) { + + this.doFrontChannelLogout = doFrontChannelLogout; + } + + public String getFrontChannelLogoutBinding() { + + return frontChannelLogoutBinding; + } + + public void setFrontChannelLogoutBinding(String frontChannelLogoutBinding) { + + this.frontChannelLogoutBinding = frontChannelLogoutBinding; + } + /** * @return */ diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java index 5d2737908..df46c34f0 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/dto/SAMLSSOServiceProviderDTO.java @@ -46,9 +46,11 @@ public class SAMLSSOServiceProviderDTO implements Serializable { private String sloRequestURL; private String loginPageURL; private String attributeConsumingServiceIndex; + private String frontChannelLogoutBinding; private boolean doSingleLogout; private boolean doSignAssertions; private boolean doSignResponse; + private boolean doFrontChannelLogout; @XmlElementWrapper(name="requestedClaims") @XmlElement(name = "requestedClaim") private String[] requestedClaims; @@ -243,6 +245,26 @@ public void setLoginPageURL(String loginPageURL) { this.loginPageURL = loginPageURL; } + public boolean isDoFrontChannelLogout() { + + return doFrontChannelLogout; + } + + public void setDoFrontChannelLogout(boolean doFrontChannelLogout) { + + this.doFrontChannelLogout = doFrontChannelLogout; + } + + public String getFrontChannelLogoutBinding() { + + return frontChannelLogoutBinding; + } + + public void setFrontChannelLogoutBinding(String frontChannelLogoutBinding) { + + this.frontChannelLogoutBinding = frontChannelLogoutBinding; + } + /** * @return */ diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java index f43c969b8..f366cdfb1 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/IdPInitSSOAuthnRequestProcessor.java @@ -129,6 +129,8 @@ public SAMLSSORespDTO process(SAMLSSOAuthnReqDTO authnReqDTO, String sessionId, spDO.setSloRequestURL(authnReqDTO.getSloRequestURL()); spDO.setTenantDomain(authnReqDTO.getTenantDomain()); spDO.setDoSingleLogout(authnReqDTO.isDoSingleLogout()); + spDO.setDoFrontChannelLogout(authnReqDTO.isDoFrontChannelLogout()); + spDO.setFrontChannelLogoutBinding(authnReqDTO.getFrontChannelLogoutBinding()); spDO.setIdPInitSLOEnabled(authnReqDTO.isIdPInitSLOEnabled()); spDO.setAssertionConsumerUrls(authnReqDTO.getAssertionConsumerURLs()); spDO.setIdpInitSLOReturnToURLs(authnReqDTO.getIdpInitSLOReturnToURLs()); @@ -260,6 +262,8 @@ private void populateServiceProviderConfigs(SAMLSSOServiceProviderDO ssoIdpConfi authnReqDTO.setDoSingleLogout(ssoIdpConfigs.isDoSingleLogout()); authnReqDTO.setSloResponseURL(ssoIdpConfigs.getSloResponseURL()); authnReqDTO.setSloRequestURL(ssoIdpConfigs.getSloRequestURL()); + authnReqDTO.setDoFrontChannelLogout(ssoIdpConfigs.isDoFrontChannelLogout()); + authnReqDTO.setFrontChannelLogoutBinding(ssoIdpConfigs.getFrontChannelLogoutBinding()); authnReqDTO.setDoSignResponse(ssoIdpConfigs.isDoSignResponse()); authnReqDTO.setDoSignAssertions(ssoIdpConfigs.isDoSignAssertions()); authnReqDTO.setRequestedClaims(ssoIdpConfigs.getRequestedClaims()); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java index 960fa00a7..225ca31d8 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/processors/SPInitSSOAuthnRequestProcessor.java @@ -17,7 +17,6 @@ */ package org.wso2.carbon.identity.sso.saml.processors; -import org.apache.axis2.transport.http.ServletBasedOutTransportInfo; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -158,6 +157,8 @@ public SAMLSSORespDTO process(SAMLSSOAuthnReqDTO authnReqDTO, String sessionId, spDO.setTenantDomain(authnReqDTO.getTenantDomain()); spDO.setNameIDFormat(authnReqDTO.getNameIDFormat()); spDO.setDoSingleLogout(authnReqDTO.isDoSingleLogout()); + spDO.setDoFrontChannelLogout(authnReqDTO.isDoFrontChannelLogout()); + spDO.setFrontChannelLogoutBinding(authnReqDTO.getFrontChannelLogoutBinding()); spDO.setIdPInitSLOEnabled(authnReqDTO.isIdPInitSLOEnabled()); spDO.setAssertionConsumerUrls(authnReqDTO.getAssertionConsumerURLs()); spDO.setIdpInitSLOReturnToURLs(authnReqDTO.getIdpInitSLOReturnToURLs()); @@ -287,6 +288,8 @@ private void populateServiceProviderConfigs(SAMLSSOServiceProviderDO ssoIdpConfi authnReqDTO.setNameIdClaimUri(ssoIdpConfigs.getNameIdClaimUri()); authnReqDTO.setNameIDFormat(ssoIdpConfigs.getNameIDFormat()); authnReqDTO.setDoSingleLogout(ssoIdpConfigs.isDoSingleLogout()); + authnReqDTO.setDoFrontChannelLogout(ssoIdpConfigs.isDoFrontChannelLogout()); + authnReqDTO.setFrontChannelLogoutBinding(ssoIdpConfigs.getFrontChannelLogoutBinding()); authnReqDTO.setSloResponseURL(ssoIdpConfigs.getSloResponseURL()); authnReqDTO.setSloRequestURL(ssoIdpConfigs.getSloRequestURL()); authnReqDTO.setDoSignResponse(ssoIdpConfigs.isDoSignResponse()); diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index 898d739f7..d3ebcdb38 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -23,7 +23,6 @@ import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; import org.opensaml.saml2.core.impl.LogoutRequestImpl; -import org.opensaml.saml2.core.impl.LogoutResponseImpl; import org.opensaml.xml.XMLObject; import org.owasp.encoder.Encode; import org.wso2.carbon.context.PrivilegedCarbonContext; @@ -82,7 +81,6 @@ import java.io.IOException; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -359,7 +357,8 @@ private void handleLogoutResponseFromSP(HttpServletRequest req, HttpServletRespo List samlssoServiceProviderDOList = SAMLSSOUtil.getRemainingSessionParticipantsForSLO( frontChannelSLOParticipantInfo.getSessionIndex(), - frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer(), + frontChannelSLOParticipantInfo.isIdPInitSLO()); if (samlssoServiceProviderDOList.isEmpty()) { respondToOriginalLogoutRequestIssuer(req, resp, sessionId, frontChannelSLOParticipantInfo); @@ -393,12 +392,6 @@ private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpSe FrontChannelSLOParticipantInfo frontChannelSLOParticipantInfo) throws IOException, IdentityException, ServletException { - SAMLSSOServiceProviderDO originalIssuer = - SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), - frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); - LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( - frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); - if (SSOSessionPersistenceManager.getSessionIndexFromCache(sessionId) == null) { // Remove tokenId Cookie when there is no session available. removeTokenIdCookie(req, resp); @@ -408,6 +401,12 @@ private void respondToOriginalLogoutRequestIssuer(HttpServletRequest req, HttpSe // Redirecting to the return URL or IS logout page. resp.sendRedirect(frontChannelSLOParticipantInfo.getReturnToURL()); } else { + SAMLSSOServiceProviderDO originalIssuer = + SAMLSSOUtil.getSPConfig(SAMLSSOUtil.getTenantDomainFromThreadLocal(), + frontChannelSLOParticipantInfo.getOriginalLogoutRequestIssuer()); + LogoutResponse logoutResponse = buildLogoutResponseForOriginalIssuer( + frontChannelSLOParticipantInfo.getOriginalIssuerLogoutRequestId(), originalIssuer); + // Sending LogoutResponse back to the original issuer. sendResponse(req, resp, frontChannelSLOParticipantInfo.getRelayState(), SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse)), @@ -1227,17 +1226,22 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS if (validationResponseDTO != null) { removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); - String sessionIndex = extractSessionIndex(request); + String sessionIndex = extractSessionIndex(request, validationResponseDTO.isIdPInitSLO(), + sessionDTO.getSessionId()); List samlssoServiceProviderDOList = - SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer()); + SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer(), + validationResponseDTO.isIdPInitSLO()); // Get the SP list and check for other session participants that have enabled single logout. if (samlssoServiceProviderDOList.isEmpty()) { respondToOriginalIssuer(request, response, sessionDTO); } else { - String originalIssuerLogoutRequestId = extractLogoutRequestId(request); + String originalIssuerLogoutRequestId = null; + if (!validationResponseDTO.isIdPInitSLO()) { + originalIssuerLogoutRequestId = extractLogoutRequestId(request); + } sendLogoutRequestToSessionParticipant(response, samlssoServiceProviderDOList, - originalIssuerLogoutRequestId, sessionDTO.isIdPInitSLO(), sessionDTO.getRelayState(), + originalIssuerLogoutRequestId, validationResponseDTO.isIdPInitSLO(), sessionDTO.getRelayState(), validationResponseDTO.getReturnToURL(), sessionIndex, sessionDTO.getIssuer()); } } else { @@ -1882,21 +1886,30 @@ private String extractLogoutRequestId(HttpServletRequest request) throws Identit } /** - * Extract session index from the original issuer logout request. + * Extract session index. * - * @param request HttpServletRequest. + * @param request HttpServlet Request. + * @param isIdPInitSLO Whether the SLO is IdP initiated or not. + * @param sessionId Session id. * @return Session Index. * @throws IdentityException Decoding error. */ - private String extractSessionIndex(HttpServletRequest request) throws IdentityException { - - String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); - XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + private String extractSessionIndex(HttpServletRequest request, boolean isIdPInitSLO, String sessionId) + throws IdentityException { String sessionIndex = null; - if (samlRequest instanceof LogoutRequestImpl) { - sessionIndex = ((LogoutRequestImpl) samlRequest).getSessionIndexes().size() > 0 ? - ((LogoutRequestImpl) samlRequest).getSessionIndexes().get(0).getSessionIndex() : null; + if (isIdPInitSLO) { + SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager + .getPersistenceManager(); + sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); + } else { + String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); + XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + + if (samlRequest instanceof LogoutRequestImpl) { + sessionIndex = ((LogoutRequestImpl) samlRequest).getSessionIndexes().size() > 0 ? + ((LogoutRequestImpl) samlRequest).getSessionIndexes().get(0).getSessionIndex() : null; + } } return sessionIndex; diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index a92852ab2..49b8cd596 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -33,17 +33,12 @@ import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.LogoutRequest; import org.opensaml.saml2.core.LogoutResponse; -import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.RequestAbstractType; import org.opensaml.saml2.core.Response; -import org.opensaml.saml2.core.SessionIndex; import org.opensaml.saml2.core.Status; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.core.StatusMessage; import org.opensaml.saml2.core.impl.IssuerBuilder; -import org.opensaml.saml2.core.impl.LogoutRequestBuilder; -import org.opensaml.saml2.core.impl.NameIDBuilder; -import org.opensaml.saml2.core.impl.SessionIndexBuilder; import org.opensaml.saml2.core.impl.StatusBuilder; import org.opensaml.saml2.core.impl.StatusCodeBuilder; import org.opensaml.saml2.core.impl.StatusMessageBuilder; @@ -2049,10 +2044,15 @@ public static LogoutRequest buildLogoutRequest(SAMLSSOServiceProviderDO serviceP * * @param sessionIndex Session index. * @param issuer Original issuer. + * @param isIdPInitSLO Whether IdP initiated SLO or not. * @return SP List with remaining session participants for SLO except for the original issuer. */ - public static List getRemainingSessionParticipantsForSLO(String sessionIndex, - String issuer) { + public static List getRemainingSessionParticipantsForSLO( + String sessionIndex, String issuer, boolean isIdPInitSLO) { + + if (isIdPInitSLO) { + issuer = null; + } SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager .getPersistenceManager(); diff --git a/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml b/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml index 951287862..aa6515b64 100644 --- a/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml +++ b/features/org.wso2.carbon.identity.sso.saml.server.feature/pom.xml @@ -39,6 +39,10 @@ org.wso2.carbon.identity.inbound.auth.saml2 org.wso2.carbon.identity.sso.saml + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.wso2.orbit.org.opensaml opensaml @@ -105,6 +109,7 @@ org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml + org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.common org.wso2.orbit.org.opensaml:opensaml org.wso2.orbit.joda-time:joda-time diff --git a/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml b/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml index 014bdec73..d245ba210 100644 --- a/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml +++ b/features/org.wso2.carbon.identity.sso.saml.ui.feature/pom.xml @@ -43,6 +43,10 @@ org.wso2.carbon.identity.inbound.auth.saml2 org.wso2.carbon.identity.sso.saml.stub + + org.wso2.carbon.identity.inbound.auth.saml2 + org.wso2.carbon.identity.sso.saml.common + org.wso2.orbit.org.opensaml opensaml @@ -78,6 +82,7 @@ org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.ui org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.stub + org.wso2.carbon.identity.inbound.auth.saml2:org.wso2.carbon.identity.sso.saml.common org.wso2.orbit.org.apache.httpcomponents:httpclient org.wso2.orbit.org.opensaml:opensaml org.wso2.orbit.joda-time:joda-time diff --git a/pom.xml b/pom.xml index e3078990c..c65bd191f 100644 --- a/pom.xml +++ b/pom.xml @@ -424,7 +424,7 @@ 4.4.22 - 5.12.240 + 5.12.258 [5.0.0, 6.0.0) From cdd1e1be6f341e5d0a925c9eb1ef81b0de907644 Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Mon, 28 Jan 2019 15:08:08 +0530 Subject: [PATCH 15/16] merge new UI configs --- .../sso/saml/ui/SAMLSSOUIConstants.java | 3 -- .../identity/sso/saml/SAMLSSOConstants.java | 2 + .../identity/sso/saml/SAMLSSOService.java | 10 ++--- .../builders/SingleLogoutMessageBuilder.java | 12 ++++++ .../saml/servlet/SAMLSSOProviderServlet.java | 42 +++++++++++-------- .../session/SSOSessionPersistenceManager.java | 39 ++++++++++------- .../identity/sso/saml/util/SAMLSSOUtil.java | 20 +++++++++ 7 files changed, 86 insertions(+), 42 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java index 5244f6828..ce72bbac8 100644 --- a/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml.ui/src/main/java/org/wso2/carbon/identity/sso/saml/ui/SAMLSSOUIConstants.java @@ -64,9 +64,6 @@ public class SAMLSSOUIConstants { public static final String ENABLE_SAML2_ECP = "enableSAML2ECP"; public static final String SLO_TYPE = "singleLogoutType"; - //Front Channel Logout Methods -// public static final String HTTP_REDIRECT_BINDING = "HTTPRedirectBinding"; -// public static final String HTTP_POST_BINDING = "HTTPPostBinding"; private SAMLSSOUIConstants() { } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java index 13616e975..f8930a7a2 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOConstants.java @@ -97,6 +97,8 @@ public class SAMLSSOConstants { public static final String CACHE_CONTROL_PARAM_KEY = "Cache-Control"; public static final String CACHE_CONTROL_VALUE_NO_CACHE = "no-cache"; + public static final String IS_POST = "isPost"; + private SAMLSSOConstants() { } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java index ccc4d321e..f41f051c9 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/SAMLSSOService.java @@ -17,7 +17,6 @@ */ package org.wso2.carbon.identity.sso.saml; -import org.apache.commons.lang.StringUtils; import org.opensaml.saml2.common.Extensions; import org.opensaml.saml2.core.AuthnRequest; import org.opensaml.saml2.core.LogoutRequest; @@ -226,9 +225,9 @@ public void doSingleLogout(String sessionId, String issuer) throws IdentityExcep String key = entry.getKey(); SAMLSSOServiceProviderDO serviceProviderDO = entry.getValue(); - // TODO : UI configuration to check for Front-Channel SLO for SPs and filter out. - // if issuer is the logout request initiator, then not sending the logout request to the issuer. - if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout()) { + // If issuer is the logout request initiator, then not sending the logout request to the issuer. + if (!key.equals(issuer) && serviceProviderDO.isDoSingleLogout() + && !serviceProviderDO.isDoFrontChannelLogout()) { SingleLogoutRequestDTO logoutReqDTO = SAMLSSOUtil.createLogoutRequestDTO(serviceProviderDO, sessionInfoData.getSubject(key), sessionIndex, rpSessionsList.get(key), serviceProviderDO.getCertAlias(), serviceProviderDO.getTenantDomain()); @@ -236,10 +235,9 @@ public void doSingleLogout(String sessionId, String issuer) throws IdentityExcep } } - //send logout requests to all session participants + // Send logout requests to all session participants. LogoutRequestSender.getInstance().sendLogoutRequests(singleLogoutReqDTOs.toArray( new SingleLogoutRequestDTO[singleLogoutReqDTOs.size()])); - // TODO : Check for back-channel logout and filter the session removal. SAMLSSOUtil.removeSession(sessionId, issuer); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java index ba88e7253..f1f0c0802 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/builders/SingleLogoutMessageBuilder.java @@ -88,6 +88,18 @@ public LogoutRequest buildLogoutRequest(String subject, String sessionId, String return logoutReq; } + /** + * Build SAML logout request without setting the signature. + * + * @param destination Destination of the logout request. + * @param tenantDomain Tenant domain. + * @param sessionId Session index. + * @param subject Subject identifier. + * @param nameIDFormat Name Id format of the logout request. + * @param reason Single logout reason. + * @return Logout request. + * @throws IdentityException If the tenant domain is invalid. + */ public LogoutRequest buildLogoutRequest(String destination, String tenantDomain, String sessionId, String subject, String nameIDFormat, String reason) throws IdentityException { diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java index d3ebcdb38..d292e5659 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/servlet/SAMLSSOProviderServlet.java @@ -62,6 +62,7 @@ import org.wso2.carbon.identity.sso.saml.cache.SessionDataCache; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheEntry; import org.wso2.carbon.identity.sso.saml.cache.SessionDataCacheKey; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.identity.sso.saml.dto.QueryParamDTO; import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOAuthnReqDTO; import org.wso2.carbon.identity.sso.saml.dto.SAMLSSOReqValidationResponseDTO; @@ -89,6 +90,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -346,10 +348,11 @@ private void handleLogoutResponseFromSP(HttpServletRequest req, HttpServletRespo responseIssuerSP.getCertAlias(), responseIssuerSP.getTenantDomain()); if (!isSuccessfullyLogout) { - // TODO : If the response is invalid, redirect the SP to an error page. + log.warn("Redirecting to default logout page due to an invalid logout response."); + resp.sendRedirect(FrameworkUtils.getRedirectURL(SAMLSSOUtil.getDefaultLogoutEndpoint(), req)); if (log.isDebugEnabled()) { - log.debug("Logout response validation failed for logout response issuer: " + - logoutResponseIssuer); + log.debug("Single logout failed due to failure in logout response validation for logout " + + "response issuer: " + logoutResponseIssuer); } } else { removeSPFromSession(frontChannelSLOParticipantInfo.getSessionIndex(), logoutResponseIssuer); @@ -435,7 +438,7 @@ private LogoutResponse buildLogoutResponseForOriginalIssuer(String originalIssue originalIssuer.getSloResponseURL()); } } else { - destination = originalIssuer.getAssertionConsumerUrl(); + destination = originalIssuer.getDefaultAssertionConsumerUrl(); if (log.isDebugEnabled()) { log.debug("Destination of the logout response is set to the ACS URL of the SP: " + originalIssuer.getAssertionConsumerUrl()); @@ -825,6 +828,10 @@ private void sendToFrameworkForLogout(HttpServletRequest request, HttpServletRes sessionDTO.setLogoutReq(true); sessionDTO.setInvalidLogout(invalid); + Properties properties = new Properties(); + properties.put(SAMLSSOConstants.IS_POST, isPost); + sessionDTO.setProperties(properties); + if (signInRespDTO != null) { sessionDTO.setDestination(signInRespDTO.getDestination()); sessionDTO.setRequestMessageString(signInRespDTO.getRequestMessageString()); @@ -1226,8 +1233,9 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS if (validationResponseDTO != null) { removeSessionDataFromCache(request.getParameter(SAMLSSOConstants.SESSION_DATA_KEY)); + boolean isPost = (boolean) sessionDTO.getProperties().get(SAMLSSOConstants.IS_POST); String sessionIndex = extractSessionIndex(request, validationResponseDTO.isIdPInitSLO(), - sessionDTO.getSessionId()); + sessionDTO.getSessionId(), isPost); List samlssoServiceProviderDOList = SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, sessionDTO.getIssuer(), validationResponseDTO.isIdPInitSLO()); @@ -1238,7 +1246,7 @@ private void handleLogoutResponseFromFramework(HttpServletRequest request, HttpS } else { String originalIssuerLogoutRequestId = null; if (!validationResponseDTO.isIdPInitSLO()) { - originalIssuerLogoutRequestId = extractLogoutRequestId(request); + originalIssuerLogoutRequestId = extractLogoutRequestId(request, isPost); } sendLogoutRequestToSessionParticipant(response, samlssoServiceProviderDOList, originalIssuerLogoutRequestId, validationResponseDTO.isIdPInitSLO(), sessionDTO.getRelayState(), @@ -1257,10 +1265,7 @@ private void sendLogoutRequestToSessionParticipant(HttpServletResponse response, throws IOException, IdentityException { for (SAMLSSOServiceProviderDO entry : samlssoServiceProviderDOList) { - // TODO : UI configuration to enable Front-Channel SLO for SPs. - boolean isFrontChannelSLOEnabled = true; - //check entry.isFrontChannelSLOEnabled() - if (isFrontChannelSLOEnabled) { + if (entry.isDoFrontChannelLogout()) { doFrontChannelSLO(response, entry, sessionIndex, originalLogoutRequestIssuer, originalIssuerLogoutRequestId, isIdPInitSLO, relayState, returnToURL); break; @@ -1788,9 +1793,8 @@ private void doFrontChannelSLO(HttpServletResponse response, storeFrontChannelSLOParticipantInfo(samlssoServiceProviderDO, originalLogoutRequestIssuer, logoutRequest, originalIssuerLogoutRequestId, sessionIndex, isIdPInitSLO, relayState, returnToURL); - // TODO: UI configuration to check for the binding and filter. - boolean isPostBindingEnabled = true; - if (isPostBindingEnabled) { + if (SAMLSSOProviderConstants.HTTP_POST_BINDING. + equals(samlssoServiceProviderDO.getFrontChannelLogoutBinding())) { sendPostRequest(response, samlssoServiceProviderDO, logoutRequest); } else { String redirectUrl = createHttpQueryStringForRedirect(logoutRequest, samlssoServiceProviderDO); @@ -1869,13 +1873,14 @@ private void storeFrontChannelSLOParticipantInfo(SAMLSSOServiceProviderDO logout * Extract logout request id of the original issuer logout request. * * @param request HttpServletRequest. + * @param isPost Whether the request is post. * @return Logout request id of the original logout request issuer. * @throws IdentityException Decoding error. */ - private String extractLogoutRequestId(HttpServletRequest request) throws IdentityException { + private String extractLogoutRequestId(HttpServletRequest request, boolean isPost) throws IdentityException { String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); - XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + XMLObject samlRequest = SAMLSSOUtil.decodeSamlLogoutRequest(initialSamlLogoutRequest, isPost); String initialLogoutRequestId = null; if (samlRequest instanceof LogoutRequestImpl) { @@ -1891,11 +1896,12 @@ private String extractLogoutRequestId(HttpServletRequest request) throws Identit * @param request HttpServlet Request. * @param isIdPInitSLO Whether the SLO is IdP initiated or not. * @param sessionId Session id. + * @param isPost Whether the request is post. * @return Session Index. * @throws IdentityException Decoding error. */ - private String extractSessionIndex(HttpServletRequest request, boolean isIdPInitSLO, String sessionId) - throws IdentityException { + private String extractSessionIndex(HttpServletRequest request, boolean isIdPInitSLO, String sessionId, + boolean isPost) throws IdentityException { String sessionIndex = null; if (isIdPInitSLO) { @@ -1904,7 +1910,7 @@ private String extractSessionIndex(HttpServletRequest request, boolean isIdPInit sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); } else { String initialSamlLogoutRequest = request.getParameter(SAMLSSOConstants.SAML_REQUEST); - XMLObject samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(initialSamlLogoutRequest)); + XMLObject samlRequest = SAMLSSOUtil.decodeSamlLogoutRequest(initialSamlLogoutRequest, isPost); if (samlRequest instanceof LogoutRequestImpl) { sessionIndex = ((LogoutRequestImpl) samlRequest).getSessionIndexes().size() > 0 ? diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManager.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManager.java index d45460c88..2c3773397 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManager.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManager.java @@ -204,21 +204,7 @@ public static void removeSession(String sessionId, String issuer) { if (cacheEntry.getSessionInfoData() != null && cacheEntry.getSessionInfoData().getServiceProviderList() != null) { SAMLSSOServiceProviderDO providerDO = cacheEntry.getSessionInfoData().getServiceProviderList().get(issuer); if (providerDO != null && providerDO.isDoSingleLogout()) { - Set sloSupportedIssuers = new HashSet(); - //Filter out service providers which enabled the single logout - for (Map.Entry entry : cacheEntry.getSessionInfoData(). - getServiceProviderList().entrySet()) { - if (entry.getValue().isDoSingleLogout()) { - sloSupportedIssuers.add(entry.getKey()); - } - } - //Remove service providers which enabled the single logout - for (String sloSupportedIssuer : sloSupportedIssuers) { - cacheEntry.getSessionInfoData().removeServiceProvider(sloSupportedIssuer); - if(log.isDebugEnabled()) { - log.debug("Removed SLO supported service provider from session info data with name " + sloSupportedIssuer); - } - } + removeBackChannelSLOEnabledSPs(cacheEntry); } else { if(cacheEntry.getSessionInfoData() != null) { cacheEntry.getSessionInfoData().removeServiceProvider(issuer); @@ -228,6 +214,9 @@ public static void removeSession(String sessionId, String issuer) { } } } + } else { + // Remove session participants in IdP initiated back-channel SLO. + removeBackChannelSLOEnabledSPs(cacheEntry); } if (cacheEntry.getSessionInfoData() == null || cacheEntry.getSessionInfoData().getServiceProviderList() == null || @@ -242,6 +231,26 @@ public static void removeSession(String sessionId, String issuer) { } } + public static void removeBackChannelSLOEnabledSPs(SAMLSSOParticipantCacheEntry cacheEntry) { + + Set sloSupportedIssuers = new HashSet(); + // Filter out service providers which enabled the single logout and back-channel logout. + for (Map.Entry entry : cacheEntry.getSessionInfoData(). + getServiceProviderList().entrySet()) { + if (entry.getValue().isDoSingleLogout() && !entry.getValue().isDoFrontChannelLogout()) { + sloSupportedIssuers.add(entry.getKey()); + } + } + // Remove service providers which enabled the single logout and back-channel logout. + for (String sloSupportedIssuer : sloSupportedIssuers) { + cacheEntry.getSessionInfoData().removeServiceProvider(sloSupportedIssuer); + if (log.isDebugEnabled()) { + log.debug("Removed back-channel SLO supported service provider from session info data with name " + + sloSupportedIssuer); + } + } + } + public String getSessionIndexFromTokenId(String tokenId) { return getSessionIndexFromCache(tokenId); } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java index 49b8cd596..5755ab9bd 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/main/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtil.java @@ -2193,4 +2193,24 @@ public static boolean validateLogoutResponse(LogoutResponse logoutResponse, Stri return false; } + /** + * Decoding the logout request extracted from the query string. + * + * @param logoutRequest Logout request string. + * @param isPost Whether the request is post. + * @return Logout request XML object. + * @throws IdentityException Error in decoding. + */ + public static XMLObject decodeSamlLogoutRequest(String logoutRequest, boolean isPost) throws IdentityException { + + XMLObject samlRequest; + if (isPost) { + samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decodeForPost(logoutRequest)); + } else { + samlRequest = SAMLSSOUtil.unmarshall(SAMLSSOUtil.decode(logoutRequest)); + } + + return samlRequest; + } + } From ad91a35e6335e30bcf131d8604f58d248a02553b Mon Sep 17 00:00:00 2001 From: sachiniWettasinghe Date: Mon, 11 Feb 2019 22:58:28 +0530 Subject: [PATCH 16/16] add unit tests --- .../SSOSessionPersistenceManagerTest.java | 13 ++- .../sso/saml/util/SAMLSSOUtilTest.java | 81 ++++++++++++++++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManagerTest.java b/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManagerTest.java index b803276e1..cd00b1486 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManagerTest.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/session/SSOSessionPersistenceManagerTest.java @@ -34,6 +34,7 @@ import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOSessionIndexCache; import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOSessionIndexCacheEntry; import org.wso2.carbon.identity.sso.saml.cache.SAMLSSOSessionIndexCacheKey; +import org.wso2.carbon.identity.sso.saml.common.SAMLSSOProviderConstants; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import static org.mockito.MockitoAnnotations.initMocks; @@ -251,6 +252,14 @@ public static void initializeData() throws IdentityException { SSOSessionPersistenceManager.getPersistenceManager().persistSession(sessionIndex, subject, samlssoServiceProviderDO2, null, "issuer2", null); + SAMLSSOServiceProviderDO samlssoServiceProviderDO3 = new SAMLSSOServiceProviderDO(); + samlssoServiceProviderDO3.setIssuer("issuer3"); + samlssoServiceProviderDO3.setDoSingleLogout(true); + samlssoServiceProviderDO3.setDoFrontChannelLogout(true); + samlssoServiceProviderDO3.setFrontChannelLogoutBinding(SAMLSSOProviderConstants.HTTP_REDIRECT_BINDING); + SSOSessionPersistenceManager.addSessionIndexToCache("sessionId3", "sessionIndex3"); + SSOSessionPersistenceManager.getPersistenceManager().persistSession("sessionIndex3", subject, + samlssoServiceProviderDO3, null, "issuer3", null); } @DataProvider(name = "testRemoveSession1") @@ -261,7 +270,9 @@ public Object[][] data() throws IdentityException { {"sessionId", null, "sessionIndex",}, {"sessionId", "issuer", "sessionIndex"}, {"sessionId2", "issuer2", "sessionIndex"}, - {"sessionId1", "issuer1", null} + {"sessionId1", "issuer1", null}, + {"sessionId1", null, null}, + {"sessionId3", "issuer3", "sessionIndex3"} }; } diff --git a/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtilTest.java b/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtilTest.java index 966f76ae0..85a1847c8 100644 --- a/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtilTest.java +++ b/components/org.wso2.carbon.identity.sso.saml/src/test/java/org/wso2/carbon/identity/sso/saml/util/SAMLSSOUtilTest.java @@ -35,6 +35,7 @@ import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil; import org.wso2.carbon.identity.base.IdentityException; +import org.wso2.carbon.identity.core.model.SAMLSSOServiceProviderDO; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.sso.saml.SAMLSSOConstants; import org.wso2.carbon.identity.sso.saml.TestConstants; @@ -42,6 +43,8 @@ import org.wso2.carbon.identity.sso.saml.builders.X509CredentialImpl; import org.wso2.carbon.identity.sso.saml.exception.IdentitySAML2SSOException; import org.wso2.carbon.identity.sso.saml.extension.eidas.EidasExtensionProcessor; +import org.wso2.carbon.identity.sso.saml.session.SSOSessionPersistenceManager; +import org.wso2.carbon.identity.sso.saml.session.SessionInfoData; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.user.api.UserStoreException; @@ -52,10 +55,18 @@ import java.util.ArrayList; import java.util.List; -import static org.mockito.Matchers.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; -import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; /** * Unit test cases for SAMLSSOUtil. @@ -82,6 +93,8 @@ public class SAMLSSOUtilTest extends PowerMockTestCase { @Mock private FederatedAuthenticatorConfig federatedAuthenticatorConfig; + private SessionInfoData sessionInfoData; + @ObjectFactory public IObjectFactory getObjectFactory() { return new PowerMockObjectFactory(); @@ -329,4 +342,68 @@ public void testBuildResponseStatus() { assertEquals(status.getStatusMessage().getMessage(), statusMsg, "Status Message is not properly set in " + "the Status object."); } + + @Test(dataProvider = "remainingSessionParticipantsforSloData") + public void testGetRemainingSessionParticipantsForSLO(String sessionIndex, String issuer, boolean isIdPInitSLO, + int expected) { + + initializeData(); + List samlssoServiceProviderDOList = + SAMLSSOUtil.getRemainingSessionParticipantsForSLO(sessionIndex, issuer, isIdPInitSLO); + assertEquals(samlssoServiceProviderDOList.size(), expected); + + } + + @DataProvider(name = "remainingSessionParticipantsforSloData") + public Object[][] remainingSessionParticipantsforSloData() { + + return new Object[][]{ + {null, null, true, 0}, + {null, "issuer", false, 0}, + {"sessionIndex", null, false, 2}, + {"sessionIndex", "issuer1", false, 1}, + {"sessionIndex", "issuer1", true, 2} + }; + } + + public void initializeData() { + + TestUtils.startTenantFlow(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME); + SAMLSSOServiceProviderDO samlssoServiceProviderDO1 = new SAMLSSOServiceProviderDO(); + samlssoServiceProviderDO1.setIssuer("issuer1"); + samlssoServiceProviderDO1.setDoSingleLogout(true); + + SAMLSSOServiceProviderDO samlssoServiceProviderDO2 = new SAMLSSOServiceProviderDO(); + samlssoServiceProviderDO2.setIssuer("issuer2"); + samlssoServiceProviderDO2.setDoSingleLogout(true); + + SAMLSSOServiceProviderDO samlssoServiceProviderDO3 = new SAMLSSOServiceProviderDO(); + samlssoServiceProviderDO3.setIssuer("issuer3"); + samlssoServiceProviderDO3.setDoSingleLogout(false); + + sessionInfoData = new SessionInfoData(); + sessionInfoData.addServiceProvider("issuer1", samlssoServiceProviderDO1, null); + sessionInfoData.addServiceProvider("issuer2", samlssoServiceProviderDO2, null); + sessionInfoData.addServiceProvider("issuer3", samlssoServiceProviderDO3, null); + + SSOSessionPersistenceManager.addSessionIndexToCache("samlssoTokenId", "sessionIndex"); + SSOSessionPersistenceManager.addSessionInfoDataToCache("sessionIndex", sessionInfoData); + } + + @Test + public void testGetSessionInfoData() { + + initializeData(); + assertEquals(SAMLSSOUtil.getSessionInfoData("sessionIndex"), sessionInfoData); + assertNotEquals(SAMLSSOUtil.getSessionInfoData("sessionIndex1"), sessionInfoData); + } + + @Test + public void testGetSessionIndex() { + + initializeData(); + assertEquals(SAMLSSOUtil.getSessionIndex("samlssoTokenId"), "sessionIndex"); + assertNull(SAMLSSOUtil.getSessionIndex("sessionId"), "Session Index is null."); + } + }