@Test public void testBuildLogoutRequest() throws Exception { String logoutUrl = "http://logoutUrl"; String spId = "cloudstack"; String nameId = "_12345"; LogoutRequest req = SAMLUtils.buildLogoutRequest(logoutUrl, spId, nameId); assertEquals(req.getDestination(), logoutUrl); assertEquals(req.getIssuer().getValue(), spId); }
/** * @param logoutRequest * @param sessionId * @param queryString * @return * @throws IdentityException */ public SAMLSSOReqValidationResponseDTO process( LogoutRequest logoutRequest, String sessionId, String queryString) throws IdentityException { try { SAMLSSOReqValidationResponseDTO reqValidationResponseDTO = new SAMLSSOReqValidationResponseDTO(); reqValidationResponseDTO.setLogOutReq(true); String subject = null; String issuer = null; String defaultSigningAlgoUri = IdentityApplicationManagementUtil.getSigningAlgoURIByConfig(); String defaultDigestAlgoUri = IdentityApplicationManagementUtil.getDigestAlgoURIByConfig(); // Get the sessions from the SessionPersistenceManager and prepare // the logout responses SSOSessionPersistenceManager ssoSessionPersistenceManager = SSOSessionPersistenceManager.getPersistenceManager(); if (StringUtils.isBlank(sessionId)) { String message = "Session was already Expired"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), defaultSigningAlgoUri, defaultDigestAlgoUri); } String sessionIndex = ssoSessionPersistenceManager.getSessionIndexFromTokenId(sessionId); if (StringUtils.isBlank(sessionId)) { String message = "Session index value not found in the request"; log.error(message); reqValidationResponseDTO = buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, null, defaultSigningAlgoUri, defaultDigestAlgoUri); reqValidationResponseDTO.setLogoutFromAuthFramework(true); return reqValidationResponseDTO; } // Only if the logout request is received. if (logoutRequest != null) { if (logoutRequest.getIssuer() == null) { String message = "Issuer should be mentioned in the Logout Request"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), defaultSigningAlgoUri, defaultDigestAlgoUri); } // TODO : Check for BaseID and EncryptedID as well. if (logoutRequest.getNameID() != null) { NameID nameID = logoutRequest.getNameID(); subject = nameID.getValue(); } else { String message = "Subject Name should be specified in the Logout Request"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), defaultSigningAlgoUri, defaultDigestAlgoUri); } if (logoutRequest.getSessionIndexes() == null) { String message = "At least one Session Index should be present in the Logout Request"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), defaultSigningAlgoUri, defaultDigestAlgoUri); } SessionInfoData sessionInfoData = ssoSessionPersistenceManager.getSessionInfo(sessionIndex); if (sessionInfoData == null) { String message = "No Established Sessions corresponding to Session Indexes provided."; log.error(message); reqValidationResponseDTO = buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, null, defaultSigningAlgoUri, defaultDigestAlgoUri); reqValidationResponseDTO.setLogoutFromAuthFramework(true); return reqValidationResponseDTO; } issuer = logoutRequest.getIssuer().getValue(); if (issuer.contains("@")) { String tenantDomain = issuer.substring(issuer.lastIndexOf('@') + 1); issuer = issuer.substring(0, issuer.lastIndexOf('@')); if (StringUtils.isNotEmpty(tenantDomain) && StringUtils.isNotEmpty(issuer)) { SAMLSSOUtil.setTenantDomainInThreadLocal(tenantDomain); if (log.isDebugEnabled()) { log.debug( "Tenant Domain :" + " " + tenantDomain + " " + "&" + " " + "Issuer name :" + issuer + " " + "has being spilt"); } } else { SAMLSSOServiceProviderDO serviceProvider = sessionInfoData.getServiceProviderList().get(issuer); if (serviceProvider != null) { SAMLSSOUtil.setTenantDomainInThreadLocal( sessionInfoData.getServiceProviderList().get(issuer).getTenantDomain()); } else { throw new IdentityException( "Service provider :" + issuer + " does not exist in session " + "info data."); } } } else { SAMLSSOServiceProviderDO serviceProvider = sessionInfoData.getServiceProviderList().get(issuer); if (serviceProvider != null) { SAMLSSOUtil.setTenantDomainInThreadLocal( sessionInfoData.getServiceProviderList().get(issuer).getTenantDomain()); } else { throw new IdentityException( "Service provider :" + issuer + " does not exist in session info " + "data."); } } subject = sessionInfoData.getSubject(issuer); Map<String, SAMLSSOServiceProviderDO> sessionsList = sessionInfoData.getServiceProviderList(); SAMLSSOServiceProviderDO logoutReqIssuer = sessionsList.get(issuer); if (logoutReqIssuer.isDoSingleLogout()) { // validate session index SessionIndex requestSessionIndex = logoutRequest.getSessionIndexes().size() > 0 ? logoutRequest.getSessionIndexes().get(0) : null; if (requestSessionIndex == null || !sessionIndex.equals(requestSessionIndex.getSessionIndex())) { String message = "Session Index validation for Logout Request failed. " + "Received: [" + (requestSessionIndex == null ? "null" : requestSessionIndex.getSessionIndex()) + "]." + " Expected: [" + sessionIndex + "]"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), logoutReqIssuer.getSigningAlgorithmUri(), logoutReqIssuer.getDigestAlgorithmUri()); } } if (logoutReqIssuer.isDoValidateSignatureInRequests()) { // Validate 'Destination' List<String> idpUrlSet = SAMLSSOUtil.getDestinationFromTenantDomain( SAMLSSOUtil.getTenantDomainFromThreadLocal()); if (logoutRequest.getDestination() == null || !idpUrlSet.contains(logoutRequest.getDestination())) { String message = "Destination validation for Logout Request failed. " + "Received: [" + logoutRequest.getDestination() + "]." + " Expected: [" + StringUtils.join(idpUrlSet, ',') + "]"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), logoutReqIssuer.getSigningAlgorithmUri(), logoutReqIssuer.getDigestAlgorithmUri()); } // Validate Signature boolean isSignatureValid = SAMLSSOUtil.validateLogoutRequestSignature( logoutRequest, logoutReqIssuer.getCertAlias(), subject, queryString); if (!isSignatureValid) { String message = "Signature validation for Logout Request failed"; log.error(message); return buildErrorResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.REQUESTOR_ERROR, message, logoutRequest.getDestination(), logoutReqIssuer.getSigningAlgorithmUri(), logoutReqIssuer.getDigestAlgorithmUri()); } } SingleLogoutMessageBuilder logoutMsgBuilder = new SingleLogoutMessageBuilder(); Map<String, String> rpSessionsList = sessionInfoData.getRPSessionsList(); List<SingleLogoutRequestDTO> singleLogoutReqDTOs = new ArrayList<SingleLogoutRequestDTO>(); for (Map.Entry<String, SAMLSSOServiceProviderDO> entry : sessionsList.entrySet()) { String key = entry.getKey(); SAMLSSOServiceProviderDO value = entry.getValue(); if (!key.equals(issuer)) { SingleLogoutRequestDTO logoutReqDTO = new SingleLogoutRequestDTO(); if (StringUtils.isNotBlank(value.getSloRequestURL())) { logoutReqDTO.setAssertionConsumerURL(value.getSloRequestURL()); } else if (StringUtils.isNotBlank(value.getSloResponseURL())) { logoutReqDTO.setAssertionConsumerURL(value.getSloResponseURL()); } else { logoutReqDTO.setAssertionConsumerURL(value.getAssertionConsumerUrl()); } LogoutRequest logoutReq = logoutMsgBuilder.buildLogoutRequest( sessionInfoData.getSubject(key), sessionIndex, SAMLSSOConstants.SingleLogoutCodes.LOGOUT_USER, logoutReqDTO.getAssertionConsumerURL(), value.getNameIDFormat(), value.getTenantDomain(), value.getSigningAlgorithmUri(), value.getDigestAlgorithmUri()); String logoutReqString = SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutReq)); logoutReqDTO.setLogoutResponse(logoutReqString); logoutReqDTO.setRpSessionId(rpSessionsList.get(key)); singleLogoutReqDTOs.add(logoutReqDTO); } else { reqValidationResponseDTO.setIssuer(value.getIssuer()); reqValidationResponseDTO.setDoSignResponse(value.isDoSignResponse()); reqValidationResponseDTO.setSigningAlgorithmUri(value.getSigningAlgorithmUri()); reqValidationResponseDTO.setDigestAlgorithmUri(value.getDigestAlgorithmUri()); if (StringUtils.isNotBlank(value.getSloResponseURL())) { reqValidationResponseDTO.setAssertionConsumerURL(value.getSloResponseURL()); } else { reqValidationResponseDTO.setAssertionConsumerURL(value.getAssertionConsumerUrl()); } } } reqValidationResponseDTO.setLogoutRespDTO( singleLogoutReqDTOs.toArray(new SingleLogoutRequestDTO[singleLogoutReqDTOs.size()])); LogoutResponse logoutResponse = logoutMsgBuilder.buildLogoutResponse( logoutRequest.getID(), SAMLSSOConstants.StatusCodes.SUCCESS_CODE, null, reqValidationResponseDTO.getAssertionConsumerURL(), reqValidationResponseDTO.isDoSignResponse(), SAMLSSOUtil.getTenantDomainFromThreadLocal(), reqValidationResponseDTO.getSigningAlgorithmUri(), reqValidationResponseDTO.getDigestAlgorithmUri()); reqValidationResponseDTO.setLogoutResponse( SAMLSSOUtil.encode(SAMLSSOUtil.marshall(logoutResponse))); reqValidationResponseDTO.setValid(true); } return reqValidationResponseDTO; } catch (UserStoreException | IdentityException e) { throw new IdentityException("Error Processing the Logout Request", e); } }
public boolean processLogoutRequest(SAMLMessageContext context, SAMLCredential credential) throws SAMLException, MetadataProviderException, MessageEncodingException { SAMLObject message = context.getInboundSAMLMessage(); // Verify type if (message == null || !(message instanceof LogoutRequest)) { log.warn("Received request is not of a LogoutRequest object type"); throw new SAMLException("Error validating SAML request"); } LogoutRequest logoutRequest = (LogoutRequest) message; // Make sure request was authenticated if required, authentication is done as part of the // binding processing if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) { log.warn( "Logout Request object is required to be signed by the entity policy: " + context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Message signature is required"); sendLogoutResponse(status, context); return false; } try { // Verify destination verifyEndpoint(context.getLocalEntityEndpoint(), logoutRequest.getDestination()); } catch (SAMLException e) { log.warn( "Destination of the request {} does not match any singleLogout endpoint", logoutRequest.getDestination()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Destination URL of the request is invalid"); sendLogoutResponse(status, context); return false; } // Verify issuer if (logoutRequest.getIssuer() != null) { try { Issuer issuer = logoutRequest.getIssuer(); verifyIssuer(issuer, context); } catch (SAMLException e) { log.warn( "Response issue time is either too old or with date in the future, id {}", context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Issuer of the message is unknown"); sendLogoutResponse(status, context); return false; } } // Verify issue time DateTime time = logoutRequest.getIssueInstant(); if (!isDateTimeSkewValid(getResponseSkew(), time)) { log.warn( "Response issue time is either too old or with date in the future, id {}.", context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUESTER_URI, "Message has been issued too long time ago"); sendLogoutResponse(status, context); return false; } // Check whether any user is logged in if (credential == null) { Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "No user is logged in"); sendLogoutResponse(status, context); return false; } // Find index for which the logout is requested boolean indexFound = false; if (logoutRequest.getSessionIndexes() != null && logoutRequest.getSessionIndexes().size() > 0) { for (AuthnStatement statement : credential.getAuthenticationAssertion().getAuthnStatements()) { String statementIndex = statement.getSessionIndex(); if (statementIndex != null) { for (SessionIndex index : logoutRequest.getSessionIndexes()) { if (statementIndex.equals(index.getSessionIndex())) { indexFound = true; } } } } } else { indexFound = true; } // Fail if sessionIndex is not found in any assertion if (!indexFound) { // Check logout request still valid and store request if (logoutRequest.getNotOnOrAfter() != null) { // TODO store request for assertions possibly arriving later } Status status = getStatus(StatusCode.REQUESTER_URI, "The requested SessionIndex was not found"); sendLogoutResponse(status, context); return false; } try { // Fail if NameId doesn't correspond to the currently logged user NameID nameID = getNameID(context, logoutRequest); if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) { Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid"); sendLogoutResponse(status, context); return false; } } catch (DecryptionException e) { Status status = getStatus(StatusCode.RESPONDER_URI, "The NameID can't be decrypted"); sendLogoutResponse(status, context); return false; } // Message is valid, let's logout Status status = getStatus(StatusCode.SUCCESS_URI, null); sendLogoutResponse(status, context); return true; }