From 858c47720af0cf1a92aeb6693c7205d514064f39 Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Fri, 9 Aug 2013 18:42:16 -0300 Subject: [PATCH] [PLINK-233] - SP should read/recognize the SingleLogoutLocation ResponseLocation attribute in the metadata. --- .../common/DefaultPicketLinkLogger.java | 23 ++++- .../picketlink/common/PicketLinkLogger.java | 7 ++ .../picketlink/config/federation/SPType.java | 35 +++++--- .../federation/core/util/CoreConfigUtil.java | 86 ++++++++++++------- .../handlers/saml2/SAML2LogOutHandler.java | 44 ++++++---- 5 files changed, 132 insertions(+), 63 deletions(-) diff --git a/modules/common/src/main/java/org/picketlink/common/DefaultPicketLinkLogger.java b/modules/common/src/main/java/org/picketlink/common/DefaultPicketLinkLogger.java index 4ef103841..7cd12bc94 100644 --- a/modules/common/src/main/java/org/picketlink/common/DefaultPicketLinkLogger.java +++ b/modules/common/src/main/java/org/picketlink/common/DefaultPicketLinkLogger.java @@ -51,11 +51,11 @@ /** * @author Pedro Silva - * + * */ + /** * @author Pedro Silva - * */ public class DefaultPicketLinkLogger implements PicketLinkLogger { @@ -2280,12 +2280,12 @@ public IllegalArgumentException samlMetaDataFailedToCreateCacheDuration(String t return new IllegalArgumentException("Cache duration could not be created using '" + timeValue + "'. This value must be an ISO-8601 period or a numeric value representing the duration in milliseconds."); } - + @Override public ConfigurationException samlMetaDataNoIdentityProviderDefined() { return new ConfigurationException("No configuration provided for the Identity Provider."); } - + @Override public ConfigurationException samlMetaDataNoServiceProviderDefined() { return new ConfigurationException("No configuration provided for the Service Provider."); @@ -2319,4 +2319,19 @@ public void authorizationManagerError(ConfigurationException e) { error("Error loading AuthorizationManager.", e); } + public IllegalStateException jbdcInitializationError(Throwable throwable) { + return new IllegalStateException(throwable); + } + + public RuntimeException errorUnmarshallingToken(Throwable e) { + return new RuntimeException(e); + } + + public RuntimeException runtimeException(String msg, Throwable e) { + return new RuntimeException(msg, e); + } + + public IllegalStateException datasourceIsNull() { + return new IllegalStateException(); + } } \ No newline at end of file diff --git a/modules/common/src/main/java/org/picketlink/common/PicketLinkLogger.java b/modules/common/src/main/java/org/picketlink/common/PicketLinkLogger.java index 764243c68..b49931257 100644 --- a/modules/common/src/main/java/org/picketlink/common/PicketLinkLogger.java +++ b/modules/common/src/main/java/org/picketlink/common/PicketLinkLogger.java @@ -1283,4 +1283,11 @@ public interface PicketLinkLogger { void authorizationManagerError(ConfigurationException e); + IllegalStateException jbdcInitializationError(Throwable throwable); + + RuntimeException errorUnmarshallingToken(Throwable e); + + RuntimeException runtimeException(String msg, Throwable e); + + IllegalStateException datasourceIsNull(); } \ No newline at end of file diff --git a/modules/config/src/main/java/org/picketlink/config/federation/SPType.java b/modules/config/src/main/java/org/picketlink/config/federation/SPType.java index eb9c5c40e..71e8e5b93 100644 --- a/modules/config/src/main/java/org/picketlink/config/federation/SPType.java +++ b/modules/config/src/main/java/org/picketlink/config/federation/SPType.java @@ -22,13 +22,13 @@ /** * Service Provider Type - * - *

+ *

+ *

* Java class for SPType complex type. - * - *

+ *

+ *

* The following schema fragment specifies the expected content contained within this class. - * + *

*

  * <complexType name="SPType">
  *   <complexContent>
@@ -40,8 +40,6 @@
  *   </complexContent>
  * </complexType>
  * 
- * - * */ public class SPType extends ProviderType { protected String serviceURL; @@ -69,17 +67,21 @@ public class SPType extends ProviderType { protected boolean idpUsesPostBinding = true; private String logOutPage = GeneralConstants.LOGOUT_PAGE_NAME; - + /** *

The URL that should be used during a GLO logout. This would usually be an URL from the IDP.

*/ private String logoutUrl; + /** + * The URL used to send a response for an IDP logout request + */ + private String logoutResponseLocation; + /** * Gets the value of the serviceURL property. * * @return possible object is {@link String } - * */ public String getServiceURL() { return serviceURL; @@ -89,7 +91,6 @@ public String getServiceURL() { * Sets the value of the serviceURL property. * * @param value allowed object is {@link String } - * */ public void setServiceURL(String value) { this.serviceURL = value; @@ -152,16 +153,24 @@ public void setIdpUsesPostBinding(boolean idpPostBinding) { public String getLogOutPage() { return this.logOutPage; } - + public void setLogOutPage(String logOutPage) { this.logOutPage = logOutPage; } - + public String getLogoutUrl() { return this.logoutUrl; } - + public void setLogoutUrl(String logoutUrl) { this.logoutUrl = logoutUrl; } + + public String getLogoutResponseLocation() { + return logoutResponseLocation; + } + + public void setLogoutResponseLocation(String logoutResponseLocation) { + this.logoutResponseLocation = logoutResponseLocation; + } } \ No newline at end of file diff --git a/modules/federation/src/main/java/org/picketlink/identity/federation/core/util/CoreConfigUtil.java b/modules/federation/src/main/java/org/picketlink/identity/federation/core/util/CoreConfigUtil.java index 91bd6ba7b..977e29050 100644 --- a/modules/federation/src/main/java/org/picketlink/identity/federation/core/util/CoreConfigUtil.java +++ b/modules/federation/src/main/java/org/picketlink/identity/federation/core/util/CoreConfigUtil.java @@ -58,7 +58,7 @@ import java.util.List; import java.util.Map; -import static org.picketlink.common.util.StringUtil.isNotNull; +import static org.picketlink.common.util.StringUtil.*; /** * Utility for configuration @@ -67,12 +67,12 @@ * @since Nov 13, 2009 */ public class CoreConfigUtil { - + private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); /** * Given either the IDP Configuration or the SP Configuration, derive the TrustKeyManager - * + * * @param idpOrSPConfiguration * @return */ @@ -83,7 +83,7 @@ public static TrustKeyManager getTrustKeyManager(ProviderType idpOrSPConfigurati /** * Once the {@code KeyProviderType} is derived, get the {@code TrustKeyManager} - * + * * @param keyProvider * @return */ @@ -106,7 +106,7 @@ public static TrustKeyManager getTrustKeyManager(KeyProviderType keyProvider) { /** * Get the validating key - * + * * @param idpSpConfiguration * @param domain * @return @@ -122,7 +122,7 @@ public static PublicKey getValidatingKey(ProviderType idpSpConfiguration, String /** * Get the validating key given the trust key manager - * + * * @param trustKeyManager * @param domain * @return @@ -138,8 +138,9 @@ public static PublicKey getValidatingKey(TrustKeyManager trustKeyManager, String } /** - * Given a {@code KeyProviderType}, return the list of auth properties that have been decrypted for any masked password - * + * Given a {@code KeyProviderType}, return the list of auth properties that have been decrypted for any masked + * password + * * @param keyProviderType * @return * @throws GeneralSecurityException @@ -155,8 +156,9 @@ public static List getKeyProviderProperties(KeyProviderType ke } /** - * Given a {@code TokenProviderType}, return the list of properties that have been decrypted for any masked property value - * + * Given a {@code TokenProviderType}, return the list of properties that have been decrypted for any masked property + * value + * * @param tokenProviderType * @return * @throws GeneralSecurityException @@ -171,8 +173,9 @@ public static List getProperties(TokenProviderType tokenProviderTy } /** - * Given a {@code ClaimsProcessorType}, return the list of properties that have been decrypted for any masked property value - * + * Given a {@code ClaimsProcessorType}, return the list of properties that have been decrypted for any masked + * property value + * * @param claimsProcessorType * @return * @throws GeneralSecurityException @@ -187,9 +190,9 @@ public static List getProperties(ClaimsProcessorType claimsProcess } /** - * Given a key value list, check if decrypt of any properties is needed. Unless one of the keys is "salt", we cannot figure - * out is decrypt is needed - * + * Given a key value list, check if decrypt of any properties is needed. Unless one of the keys is "salt", we cannot + * figure out is decrypt is needed + * * @param keyValueList * @return */ @@ -208,9 +211,9 @@ public static boolean decryptionNeeded(List keyValueList } /** - * Given a key value pair read from PicketLink configuration, ensure that we replace the masked passwords with the decoded - * passwords and pass it back - * + * Given a key value pair read from PicketLink configuration, ensure that we replace the masked passwords with the + * decoded passwords and pass it back + * * @param keyValueList * @return * @throws GeneralSecurityException @@ -279,7 +282,7 @@ private static List decryptPasswords(List keyValueList) throws GeneralSecurityEx /** * Given a metadata {@link EntityDescriptorType}, construct the Service provider configuration - * + * * @param entityDescriptor * @param bindingURI * @return @@ -308,7 +311,7 @@ public static ProviderType getSPConfiguration(EntityDescriptorType entityDescrip /** * Given a metadata {@link EntityDescriptorType}, construct the Service provider configuration - * + * * @param entityDescriptor * @param bindingURI * @return @@ -356,6 +359,7 @@ public static SPType getSPConfiguration(EntitiesDescriptorType entitiesDescripto spType.setIdentityURL(identityURL); spType.setLogoutUrl(getLogoutURL(idpSSO, bindingURI)); + spType.setLogoutResponseLocation(getLogoutResponseLocation(idpSSO, bindingURI)); String serviceURL = getServiceURL(spSSO, bindingURI); @@ -371,7 +375,7 @@ public static SPType getSPConfiguration(EntitiesDescriptorType entitiesDescripto /** * Get the first metadata descriptor for an IDP - * + * * @param entitiesDescriptor * @return */ @@ -393,7 +397,7 @@ public static IDPSSODescriptorType getIDPDescriptor(EntitiesDescriptorType entit /** * Get the IDP metadata descriptor from an entity descriptor - * + * * @param entityDescriptor * @return */ @@ -413,7 +417,7 @@ public static IDPSSODescriptorType getIDPDescriptor(EntityDescriptorType entityD /** * Get the SP Descriptor from an entity descriptor - * + * * @param entityDescriptor * @return */ @@ -433,7 +437,7 @@ public static SPSSODescriptorType getSPDescriptor(EntityDescriptorType entityDes /** * Given a binding uri, get the IDP identity url - * + * * @param idp * @param bindingURI * @return @@ -454,7 +458,7 @@ public static String getIdentityURL(IDPSSODescriptorType idp, String bindingURI) /** * Given a binding uri, get the IDP identity url - * + * * @param idp * @param bindingURI * @return @@ -473,9 +477,31 @@ public static String getLogoutURL(IDPSSODescriptorType idp, String bindingURI) { return logoutURL; } + /** + * Given a binding uri, get the IDP logout response url (used for global logouts) + */ + public static String getLogoutResponseLocation(IDPSSODescriptorType idp, String bindingURI) { + String logoutResponseLocation = null; + + List endpoints = idp.getSingleLogoutService(); + for (EndpointType endpoint : endpoints) { + if (endpoint.getBinding().toString().equals(bindingURI)) { + if (endpoint.getResponseLocation() != null) { + logoutResponseLocation = endpoint.getResponseLocation().toString(); + } else { + logoutResponseLocation = null; + } + + break; + } + + } + return logoutResponseLocation; + } + /** * Get the service url for the SP - * + * * @param sp * @param bindingURI * @return @@ -496,7 +522,7 @@ public static String getServiceURL(SPSSODescriptorType sp, String bindingURI) { /** * Get the IDP Type - * + * * @param idpSSODescriptor * @return */ @@ -522,12 +548,12 @@ public static IDPType getIDPType(IDPSSODescriptorType idpSSODescriptor) { /** * Read metadata from ProviderType - * + * * @param providerType * @param servletContext * @return */ - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) public static List getMetadataConfiguration(ProviderType providerType, ServletContext servletContext) { MetadataProviderType metadataProviderType = providerType.getMetaDataProvider(); @@ -569,7 +595,7 @@ public static List getMetadataConfiguration(ProviderType p } private static void addAllEntityDescriptorsRecursively(List resultList, - EntitiesDescriptorType entitiesDescriptorType) { + EntitiesDescriptorType entitiesDescriptorType) { List entities = entitiesDescriptorType.getEntityDescriptor(); for (Object o : entities) { if (o instanceof EntitiesDescriptorType) { diff --git a/modules/federation/src/main/java/org/picketlink/identity/federation/web/handlers/saml2/SAML2LogOutHandler.java b/modules/federation/src/main/java/org/picketlink/identity/federation/web/handlers/saml2/SAML2LogOutHandler.java index 589541663..85b0b5669 100644 --- a/modules/federation/src/main/java/org/picketlink/identity/federation/web/handlers/saml2/SAML2LogOutHandler.java +++ b/modules/federation/src/main/java/org/picketlink/identity/federation/web/handlers/saml2/SAML2LogOutHandler.java @@ -288,7 +288,7 @@ public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse } private void generateSuccessStatusResponseType(String logOutRequestID, SAML2HandlerRequest request, - SAML2HandlerResponse response, String originalIssuer) throws ConfigurationException, + SAML2HandlerResponse response, String originalIssuer) throws ConfigurationException, ParserConfigurationException, ProcessingException { logger.trace("Generating Success Status Response for " + originalIssuer); @@ -369,18 +369,18 @@ public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerRespons NameIDType nameID = new NameIDType(); nameID.setValue(userPrincipal.getName()); lot.setNameID(nameID); - + SPType spConfiguration = (SPType) getProviderconfig(); String logoutUrl = spConfiguration.getLogoutUrl(); - + if (logoutUrl == null) { logoutUrl = spConfiguration.getIdentityURL(); } - + lot.setDestination(URI.create(logoutUrl)); - + populateSessionIndex(httpRequest, lot); - + response.setResultingDocument(samlRequest.convert(lot)); response.setSendRequest(true); } catch (Exception e) { @@ -391,22 +391,22 @@ public void generateSAMLRequest(SAML2HandlerRequest request, SAML2HandlerRespons private void populateSessionIndex(HttpServletRequest httpRequest, LogoutRequestType lot) throws ProcessingException, ConfigurationException, ParsingException { Document currentAssertion = (Document) httpRequest.getSession().getAttribute(GeneralConstants.ASSERTION_SESSION_ATTRIBUTE_NAME); - + if (currentAssertion != null) { AssertionType assertionType = SAMLUtil.fromElement(currentAssertion.getDocumentElement()); - + Set statements = assertionType.getStatements(); - + for (StatementAbstractType statementAbstractType : statements) { if (AuthnStatementType.class.isInstance(statementAbstractType)) { AuthnStatementType authnStatement = (AuthnStatementType) statementAbstractType; - + String sessionIndex = authnStatement.getSessionIndex(); - + if (sessionIndex != null) { lot.addSessionIndex(sessionIndex); } - + break; } } @@ -428,13 +428,13 @@ public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerRe StatusCodeType statusCode = statusType.getStatusCode(); URI statusCodeValueURI = statusCode.getValue(); boolean success = false; - if(statusCodeValueURI != null){ + if (statusCodeValueURI != null) { String statusCodeValue = statusCodeValueURI.toString(); - if(JBossSAMLURIConstants.STATUS_SUCCESS.get().equals(statusCodeValue)){ + if (JBossSAMLURIConstants.STATUS_SUCCESS.get().equals(statusCodeValue)) { success = true; } } - if(success){ + if (success) { // we are successfully logged out session.invalidate(); } else { @@ -451,6 +451,9 @@ public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse if (samlObject instanceof LogoutRequestType == false) return; + //get the configuration to handle a logout request from idp and set the correct response location + SPType spConfiguration = (SPType) getProviderconfig(); + LogoutRequestType logOutRequest = (LogoutRequestType) samlObject; HTTPContext httpContext = (HTTPContext) request.getContext(); HttpServletRequest servletRequest = httpContext.getRequest(); @@ -486,6 +489,16 @@ public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse statusResponse.setIssuer(request.getIssuer()); + String logoutResponseLocation = spConfiguration.getLogoutResponseLocation(); + + if (logoutResponseLocation == null) { + response.setDestination(logOutRequest.getIssuer().getValue()); + } else { + response.setDestination(logoutResponseLocation); + } + + statusResponse.setDestination(response.getDestination()); + SAML2Response saml2Response = new SAML2Response(); try { response.setResultingDocument(saml2Response.convert(statusResponse)); @@ -494,7 +507,6 @@ public void handleRequestType(SAML2HandlerRequest request, SAML2HandlerResponse } response.setRelayState(relayState); - response.setDestination(logOutRequest.getIssuer().getValue()); response.setSendRequest(false); } }