/*
* Copyright (c) 2013, 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.application.authentication.framework.handler.request.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.identity.application.authentication.framework.ApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus;
import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade;
import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig;
import org.wso2.carbon.identity.application.authentication.framework.config.model.ExternalIdPConfig;
import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig;
import org.wso2.carbon.identity.application.authentication.framework.config.model.StepConfig;
import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException;
import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException;
import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException;
import org.wso2.carbon.identity.application.authentication.framework.handler.request.LogoutRequestHandler;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationResult;
import org.wso2.carbon.identity.application.authentication.framework.model.CommonAuthResponseWrapper;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.idp.mgt.IdentityProviderManagementException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DefaultLogoutRequestHandler implements LogoutRequestHandler {
private static final Log log = LogFactory.getLog(DefaultLogoutRequestHandler.class);
private static volatile DefaultLogoutRequestHandler instance;
private static final Log AUDIT_LOG = CarbonConstants.AUDIT_LOG;
public static DefaultLogoutRequestHandler getInstance() {
if (log.isTraceEnabled()) {
log.trace("Inside getInstance()");
}
if (instance == null) {
synchronized (DefaultLogoutRequestHandler.class) {
if (instance == null) {
instance = new DefaultLogoutRequestHandler();
}
}
}
return instance;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context)
throws FrameworkException {
if (log.isTraceEnabled()) {
log.trace("Inside handle()");
}
SequenceConfig sequenceConfig = context.getSequenceConfig();
ExternalIdPConfig externalIdPConfig = null;
if (context.isPreviousSessionFound()) {
// if this is the start of the logout sequence
if (context.getCurrentStep() == 0) {
context.setCurrentStep(1);
}
int stepCount = sequenceConfig.getStepMap().size();
while (context.getCurrentStep() <= stepCount) {
int currentStep = context.getCurrentStep();
StepConfig stepConfig = sequenceConfig.getStepMap().get(currentStep);
AuthenticatorConfig authenticatorConfig = stepConfig.getAuthenticatedAutenticator();
if (authenticatorConfig == null) {
authenticatorConfig = sequenceConfig.getAuthenticatedReqPathAuthenticator();
}
ApplicationAuthenticator authenticator =
authenticatorConfig.getApplicationAuthenticator();
String idpName = stepConfig.getAuthenticatedIdP();
//TODO: Need to fix occurrences where idPName becomes "null"
if ((idpName == null || "null".equalsIgnoreCase(idpName) || idpName.isEmpty()) &&
sequenceConfig.getAuthenticatedReqPathAuthenticator() != null) {
idpName = FrameworkConstants.LOCAL_IDP_NAME;
}
try {
externalIdPConfig = ConfigurationFacade.getInstance()
.getIdPConfigByName(idpName, context.getTenantDomain());
context.setExternalIdP(externalIdPConfig);
context.setAuthenticatorProperties(FrameworkUtils
.getAuthenticatorPropertyMapFromIdP(
externalIdPConfig, authenticator.getName()));
context.setStateInfo(authenticatorConfig.getAuthenticatorStateInfo());
AuthenticatorFlowStatus status = authenticator.process(request, response, context);
request.setAttribute(FrameworkConstants.RequestParams.FLOW_STATUS, status);
if (!status.equals(AuthenticatorFlowStatus.INCOMPLETE)) {
// TODO what if logout fails. this is an edge case
currentStep++;
context.setCurrentStep(currentStep);
continue;
}
// sends the logout request to the external IdP
FrameworkUtils.addAuthenticationContextToCache(context.getContextIdentifier(), context);
return;
} catch (AuthenticationFailedException | LogoutFailedException e) {
throw new FrameworkException("Exception while handling logout request", e);
} catch (IdentityProviderManagementException e) {
log.error("Exception while getting IdP by name", e);
}
}
if (sequenceConfig != null && sequenceConfig.getAuthenticatedUser() != null) {
String auditData = "\"" + "ContextIdentifier" + "\" : \"" + context.getContextIdentifier()
+ "\",\"" + "LoggedOutUser" + "\" : \"" + sequenceConfig.getAuthenticatedUser().
getAuthenticatedSubjectIdentifier()
+ "\",\"" + "LoggedOutUserTenantDomain" + "\" : \"" + sequenceConfig.
getAuthenticatedUser().getTenantDomain()
+ "\",\"" + "ServiceProviderName" + "\" : \"" + context.getServiceProviderName()
+ "\",\"" + "RequestType" + "\" : \"" + context.getRequestType()
+ "\",\"" + "RelyingParty" + "\" : \"" + context.getRelyingParty()
+ "\",\"" + "AuthenticatedIdPs" + "\" : \"" + sequenceConfig.getAuthenticatedIdPs()
+ "\"";
String idpName = null;
if (externalIdPConfig != null) {
idpName = externalIdPConfig.getName();
}
AUDIT_LOG.info(String.format(
FrameworkConstants.AUDIT_MESSAGE,
sequenceConfig.getAuthenticatedUser().getAuthenticatedSubjectIdentifier(),
"Logout", idpName, auditData, FrameworkConstants.AUDIT_SUCCESS));
}
}
// remove the SessionContext from the cache
FrameworkUtils.removeSessionContextFromCache(context.getSessionIdentifier());
// remove the cookie
FrameworkUtils.removeAuthCookie(request, response);
try {
sendResponse(request, response, context, true);
} catch (ServletException | IOException e) {
throw new FrameworkException(e.getMessage(), e);
}
}
protected void sendResponse(HttpServletRequest request, HttpServletResponse response,
AuthenticationContext context, boolean isLoggedOut)
throws ServletException, IOException {
if (log.isTraceEnabled()) {
log.trace("Inside sendLogoutResponseToCaller()");
}
// Set values to be returned to the calling servlet as request
// attributes
request.setAttribute(FrameworkConstants.ResponseParams.LOGGED_OUT, isLoggedOut);
String redirectURL;
if(context.getCallerSessionKey() != null) {
request.setAttribute(FrameworkConstants.SESSION_DATA_KEY, context.getCallerSessionKey());
AuthenticationResult authenticationResult = new AuthenticationResult();
authenticationResult.setLoggedOut(true);
SequenceConfig sequenceConfig = context.getSequenceConfig();
if (sequenceConfig != null) {
authenticationResult.setSaaSApp(sequenceConfig.getApplicationConfig().isSaaSApp());
}
if (FrameworkUtils.getCacheDisabledAuthenticators().contains(context.getRequestType())
&& (response instanceof CommonAuthResponseWrapper)) {
//Set authentication result as request attribute
addAuthenticationResultToRequest(request, authenticationResult);
}else{
FrameworkUtils.addAuthenticationResultToCache(context.getCallerSessionKey(), authenticationResult);
}
redirectURL = context.getCallerPath() + "?sessionDataKey=" + context.getCallerSessionKey();
} else {
redirectURL = context.getCallerPath();
}
/*
* TODO Cache retaining is a temporary fix. Remove after Google fixes
* http://code.google.com/p/gdata-issues/issues/detail?id=6628
*/
String retainCache = System.getProperty("retainCache");
if (retainCache == null) {
FrameworkUtils.removeAuthenticationContextFromCache(context.getContextIdentifier());
}
if (log.isDebugEnabled()) {
log.debug("Sending response back to: " + context.getCallerPath() + "...\n"
+ FrameworkConstants.ResponseParams.LOGGED_OUT + " : " + isLoggedOut + "\n"
+ FrameworkConstants.SESSION_DATA_KEY + ": " + context.getCallerSessionKey());
}
// redirect to the caller
response.sendRedirect(redirectURL);
}
/**
* Add authentication result into request attribute
*
* @param request Http servlet request
* @param authenticationResult Authentication result
*/
private void addAuthenticationResultToRequest(HttpServletRequest request,
AuthenticationResult authenticationResult) {
request.setAttribute(FrameworkConstants.RequestAttribute.AUTH_RESULT, authenticationResult);
}
}