diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/AuthenticatorConfig.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/AuthenticatorConfig.java index 8020ba1bf372..a0a294711889 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/AuthenticatorConfig.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/config/model/AuthenticatorConfig.java @@ -22,6 +22,10 @@ import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorStateInfo; import org.wso2.carbon.identity.application.common.model.IdentityProvider; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; @@ -154,4 +158,18 @@ public void setTenantDomain(String tenantDomain) { this.tenantDomain = tenantDomain; } + + public static T deepCopy(T obj) { + + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(obj); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + ObjectInputStream ois = new ObjectInputStream(bis); + return (T) ois.readObject(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/context/AuthenticationContext.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/context/AuthenticationContext.java index 58c28bdd5516..ebf9e52889e7 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/context/AuthenticationContext.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/context/AuthenticationContext.java @@ -22,6 +22,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.SerializationUtils; import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorStateInfo; +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.model.AuthenticatedIdPData; @@ -120,6 +121,30 @@ public class AuthenticationContext extends MessageContext implements Serializabl private boolean sendToMultiOptionPage; + private Map orgAuthenticatorConfigs = new HashMap<>(); + + public Map getOrgAuthenticatorConfigs() { + + return orgAuthenticatorConfigs; + } + + public void addOrganicAuthenticatorConfig(int orgNumber, AuthenticatorConfig authenticatorConfig) { + + this.orgAuthenticatorConfigs.put(orgNumber, authenticatorConfig); + } + + private int currentOrgNumber; + + public int getCurrentOrgNumber() { + + return currentOrgNumber; + } + + public void setCurrentOrgNumber(int currentOrgNumber) { + + this.currentOrgNumber = currentOrgNumber; + } + /** * This attribute holds the context expiry time in epoch timestamp (nanoseconds). */ diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultLogoutRequestHandler.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultLogoutRequestHandler.java index 416f6adfe5d2..e8043ea83121 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultLogoutRequestHandler.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/request/impl/DefaultLogoutRequestHandler.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.application.authentication.framework.handler.request.impl; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -43,7 +44,7 @@ import org.wso2.carbon.identity.application.authentication.framework.internal.util.SessionEventPublishingUtil; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedIdPData; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; -import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationResult; +import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationResult;xw import org.wso2.carbon.identity.application.authentication.framework.model.CommonAuthResponseWrapper; import org.wso2.carbon.identity.application.authentication.framework.store.SessionDataStore; import org.wso2.carbon.identity.application.authentication.framework.store.UserSessionStore; @@ -282,8 +283,6 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Aut context.setCurrentStep(currentStep); continue; } - ApplicationAuthenticator authenticator = - authenticatorConfig.getApplicationAuthenticator(); String idpName = stepConfig.getAuthenticatedIdP(); //TODO: Need to fix occurrences where idPName becomes "null" @@ -291,57 +290,137 @@ public void handle(HttpServletRequest request, HttpServletResponse response, Aut 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())); - - if (authenticatorConfig.getAuthenticatorStateInfo() != null) { - context.setStateInfo(authenticatorConfig.getAuthenticatorStateInfo()); - } else { - context.setStateInfo( - getStateInfoFromPreviousAuthenticatedIdPs(idpName, authenticatorConfig.getName(), - context)); - } - AuthenticatorFlowStatus status = authenticator.process(request, response, context); - request.setAttribute(FrameworkConstants.RequestParams.FLOW_STATUS, status); + ApplicationAuthenticator authenticator; + if ("SSO".equals(idpName)) { + AuthenticatedIdPData authenticatedIdPData = context.getPreviousAuthenticatedIdPs().get(idpName); + if (MapUtils.isEmpty(context.getOrgAuthenticatorConfigs())) { + int i = 1; + for (AuthenticatorConfig authConfig : authenticatedIdPData.getAuthenticators()) { + context.addOrganicAuthenticatorConfig(i, authConfig); + i++; + } + } - if (!status.equals(AuthenticatorFlowStatus.INCOMPLETE)) { - // TODO what if logout fails. this is an edge case - currentStep++; - context.setCurrentStep(currentStep); - continue; + if (context.getCurrentOrgNumber() == 0) { + context.setCurrentOrgNumber(1); } - // sends the logout request to the external IdP - return; - } catch (AuthenticationFailedException | LogoutFailedException e) { - if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { - diagnosticLogBuilder.resultMessage("Exception while handling logout request.") - .inputParam(LogConstants.InputKeys.IDP, idpName) - .resultStatus(DiagnosticLog.ResultStatus.FAILED); - - // Sanitize the error message before adding to diagnostic log. - String errorMessage = e.getMessage(); - if (context.getLastAuthenticatedUser() != null) { - String userName = context.getLastAuthenticatedUser().getUserName(); - errorMessage = LoggerUtils.getSanitizedErrorMessage(errorMessage, userName); + + int orgCount = context.getOrgAuthenticatorConfigs().size(); + while (context.getCurrentOrgNumber() <= orgCount) { + AuthenticatorConfig orgAuthenticatorConfig = + context.getOrgAuthenticatorConfigs().get(context.getCurrentOrgNumber()); + + authenticator = orgAuthenticatorConfig.getApplicationAuthenticator(); + try { + externalIdPConfig = ConfigurationFacade.getInstance() + .getIdPConfigByName(idpName, context.getTenantDomain()); + context.setExternalIdP(externalIdPConfig); + context.setAuthenticatorProperties(FrameworkUtils + .getAuthenticatorPropertyMapFromIdP( + externalIdPConfig, authenticator.getName())); + + if (orgAuthenticatorConfig.getAuthenticatorStateInfo() != null) { + context.setStateInfo(orgAuthenticatorConfig.getAuthenticatorStateInfo()); + } else { + context.setStateInfo( + getStateInfoFromPreviousAuthenticatedIdPs(idpName, + orgAuthenticatorConfig.getName(), context)); + } + + 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 + context.setCurrentOrgNumber(context.getCurrentOrgNumber() + 1); + request.setAttribute("logoutInit", "true"); + continue; + } + // sends the logout request to the external IdP + return; + } catch (AuthenticationFailedException | LogoutFailedException e) { + if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { + diagnosticLogBuilder.resultMessage("Exception while handling logout request.") + .inputParam(LogConstants.InputKeys.IDP, idpName) + .resultStatus(DiagnosticLog.ResultStatus.FAILED); + + // Sanitize the error message before adding to diagnostic log. + String errorMessage = e.getMessage(); + if (context.getLastAuthenticatedUser() != null) { + String userName = context.getLastAuthenticatedUser().getUserName(); + errorMessage = LoggerUtils.getSanitizedErrorMessage(errorMessage, userName); + } + diagnosticLogBuilder.inputParam(LogConstants.InputKeys.ERROR_MESSAGE, errorMessage); + LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + } + throw new FrameworkException("Exception while handling logout request", e); + } catch (IdentityProviderManagementException e) { + if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { + diagnosticLogBuilder.resultMessage("Exception while getting IdP by name") + .inputParam(LogConstants.InputKeys.ERROR_MESSAGE, e.getMessage()) + .resultStatus(DiagnosticLog.ResultStatus.FAILED); + LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + } + log.error("Exception while getting IdP by name", e); } - diagnosticLogBuilder.inputParam(LogConstants.InputKeys.ERROR_MESSAGE, errorMessage); - LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); } - throw new FrameworkException("Exception while handling logout request", e); - } catch (IdentityProviderManagementException e) { - if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { - diagnosticLogBuilder.resultMessage("Exception while getting IdP by name") - .inputParam(LogConstants.InputKeys.ERROR_MESSAGE, e.getMessage()) - .resultStatus(DiagnosticLog.ResultStatus.FAILED); - LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + currentStep++; + context.setCurrentStep(currentStep); + } else { + authenticator = authenticatorConfig.getApplicationAuthenticator(); + try { + externalIdPConfig = ConfigurationFacade.getInstance() + .getIdPConfigByName(idpName, context.getTenantDomain()); + context.setExternalIdP(externalIdPConfig); + context.setAuthenticatorProperties(FrameworkUtils + .getAuthenticatorPropertyMapFromIdP( + externalIdPConfig, authenticator.getName())); + + if (authenticatorConfig.getAuthenticatorStateInfo() != null) { + context.setStateInfo(authenticatorConfig.getAuthenticatorStateInfo()); + } else { + context.setStateInfo( + getStateInfoFromPreviousAuthenticatedIdPs(idpName, authenticatorConfig.getName(), + context)); + } + + 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 + return; + } catch (AuthenticationFailedException | LogoutFailedException e) { + if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { + diagnosticLogBuilder.resultMessage("Exception while handling logout request.") + .inputParam(LogConstants.InputKeys.IDP, idpName) + .resultStatus(DiagnosticLog.ResultStatus.FAILED); + + // Sanitize the error message before adding to diagnostic log. + String errorMessage = e.getMessage(); + if (context.getLastAuthenticatedUser() != null) { + String userName = context.getLastAuthenticatedUser().getUserName(); + errorMessage = LoggerUtils.getSanitizedErrorMessage(errorMessage, userName); + } + diagnosticLogBuilder.inputParam(LogConstants.InputKeys.ERROR_MESSAGE, errorMessage); + LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + } + throw new FrameworkException("Exception while handling logout request", e); + } catch (IdentityProviderManagementException e) { + if (LoggerUtils.isDiagnosticLogsEnabled() && diagnosticLogBuilder != null) { + diagnosticLogBuilder.resultMessage("Exception while getting IdP by name") + .inputParam(LogConstants.InputKeys.ERROR_MESSAGE, e.getMessage()) + .resultStatus(DiagnosticLog.ResultStatus.FAILED); + LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder); + } + log.error("Exception while getting IdP by name", e); } - log.error("Exception while getting IdP by name", e); } } } else if (context.getPreviousAuthenticatedIdPs().size() != 0) { diff --git a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/step/impl/DefaultStepHandler.java b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/step/impl/DefaultStepHandler.java index ca1d59feedef..892cbb64c1c3 100644 --- a/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/step/impl/DefaultStepHandler.java +++ b/components/authentication-framework/org.wso2.carbon.identity.application.authentication.framework/src/main/java/org/wso2/carbon/identity/application/authentication/framework/handler/step/impl/DefaultStepHandler.java @@ -902,6 +902,7 @@ protected void doAuthentication(HttpServletRequest request, HttpServletResponse // This fix needs to be generalized and improved for non organization login flows as well. if (isLoggedInWithOrganizationLogin(authenticatorConfig)) { + authenticatorConfig = AuthenticatorConfig.deepCopy(authenticatorConfig); handleDuplicateOrganizationAuthenticators(authenticatedIdPData, authenticatedUser, authenticatorConfig); }