/*
* 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.sequence.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.model.ApplicationConfig;
import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig;
import org.wso2.carbon.identity.application.authentication.framework.config.model.SequenceConfig;
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.InvalidCredentialsException;
import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException;
import org.wso2.carbon.identity.application.authentication.framework.handler.sequence.RequestPathBasedSequenceHandler;
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.util.FrameworkConstants;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class DefaultRequestPathBasedSequenceHandler implements RequestPathBasedSequenceHandler {
private static final Log log = LogFactory.getLog(DefaultRequestPathBasedSequenceHandler.class);
private static volatile DefaultRequestPathBasedSequenceHandler instance;
public static DefaultRequestPathBasedSequenceHandler getInstance() {
if (instance == null) {
synchronized (DefaultRequestPathBasedSequenceHandler.class) {
if (instance == null) {
instance = new DefaultRequestPathBasedSequenceHandler();
}
}
}
return instance;
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AuthenticationContext context) throws FrameworkException {
if (log.isDebugEnabled()) {
log.debug("Executing the Request Path Authentication...");
}
SequenceConfig seqConfig = context.getSequenceConfig();
List<AuthenticatorConfig> reqPathAuthenticators = seqConfig.getReqPathAuthenticators();
for (AuthenticatorConfig reqPathAuthenticator : reqPathAuthenticators) {
ApplicationAuthenticator authenticator = reqPathAuthenticator
.getApplicationAuthenticator();
if (log.isDebugEnabled()) {
log.debug("Executing " + authenticator.getName());
}
if (authenticator.canHandle(request)) {
if (log.isDebugEnabled()) {
log.debug(authenticator.getName() + " can handle the request");
}
try {
AuthenticatorFlowStatus status = authenticator.process(request, response, context);
request.setAttribute(FrameworkConstants.RequestParams.FLOW_STATUS, status);
if (log.isDebugEnabled()) {
log.debug(authenticator.getName() + ".authenticate() returned: "
+ status.toString());
}
AuthenticatedUser authenticatedUser = context.getSubject();
seqConfig.setAuthenticatedUser(authenticatedUser);
if (log.isDebugEnabled()) {
log.debug("Authenticated User: " + authenticatedUser.getAuthenticatedSubjectIdentifier());
log.debug("Authenticated User Tenant Domain: " + seqConfig.getAuthenticatedUser().getTenantDomain());
}
AuthenticatedIdPData authenticatedIdPData = new AuthenticatedIdPData();
// store authenticated user
authenticatedIdPData.setUser(authenticatedUser);
// store authenticated idp
authenticatedIdPData.setIdpName(FrameworkConstants.LOCAL_IDP_NAME);
reqPathAuthenticator.setAuthenticatorStateInfo(context.getStateInfo());
authenticatedIdPData.setAuthenticator(reqPathAuthenticator);
seqConfig.setAuthenticatedReqPathAuthenticator(reqPathAuthenticator);
context.getCurrentAuthenticatedIdPs().put(FrameworkConstants.LOCAL_IDP_NAME,
authenticatedIdPData);
handlePostAuthentication(request, response, context, authenticatedIdPData);
} catch (InvalidCredentialsException e) {
if(log.isDebugEnabled()){
log.debug("InvalidCredentialsException stack trace : ", e);
}
log.warn("A login attempt was failed due to invalid credentials");
context.setRequestAuthenticated(false);
} catch (AuthenticationFailedException e) {
log.error(e.getMessage(), e);
context.setRequestAuthenticated(false);
} catch (LogoutFailedException e) {
throw new FrameworkException(e.getMessage(), e);
}
context.getSequenceConfig().setCompleted(true);
return;
}
}
}
protected void handlePostAuthentication(HttpServletRequest request,
HttpServletResponse response, AuthenticationContext context,
AuthenticatedIdPData authenticatedIdPData) throws FrameworkException {
if (log.isDebugEnabled()) {
log.debug("Handling Post Authentication tasks");
}
SequenceConfig sequenceConfig = context.getSequenceConfig();
Map<String, String> mappedAttrs;
StringBuilder jsonBuilder = new StringBuilder();
// build the authenticated idps JWT to send to the calling servlet.
jsonBuilder.append("\"idps\":");
jsonBuilder.append("[");
// build the JSON object for this step
jsonBuilder.append("{");
jsonBuilder.append("\"idp\":\"").append(authenticatedIdPData.getIdpName()).append("\",");
jsonBuilder
.append("\"authenticator\":\"")
.append(authenticatedIdPData.getAuthenticator().getApplicationAuthenticator()
.getName()).append("\"");
// wrap up the JSON object
jsonBuilder.append("}");
jsonBuilder.append("]");
sequenceConfig
.setAuthenticatedIdPs(IdentityApplicationManagementUtil.getSignedJWT(jsonBuilder
.toString(), sequenceConfig.getApplicationConfig().getServiceProvider()));
mappedAttrs = handleClaimMappings(context);
String spRoleUri = getSpRoleClaimUri(sequenceConfig.getApplicationConfig());
String roleAttr = mappedAttrs.get(spRoleUri);
if (roleAttr != null && roleAttr.trim().length() > 0) {
String[] roles = roleAttr.split(",");
mappedAttrs.put(spRoleUri,
getServiceProviderMappedUserRoles(sequenceConfig, Arrays.asList(roles)));
}
sequenceConfig.getAuthenticatedUser().setUserAttributes(FrameworkUtils.buildClaimMappings(mappedAttrs));
if (context.getSequenceConfig().getApplicationConfig().getSubjectClaimUri() != null
&& context.getSequenceConfig().getApplicationConfig().getSubjectClaimUri().trim()
.length() > 0) {
Map<String, String> unfilteredClaimValues = (Map<String, String>) context
.getProperty(FrameworkConstants.UNFILTERED_LOCAL_CLAIM_VALUES);
String subjectValue = null;
if (unfilteredClaimValues != null) {
subjectValue = unfilteredClaimValues.get(context.getSequenceConfig()
.getApplicationConfig().getSubjectClaimUri().trim());
} else {
subjectValue = mappedAttrs.get(context.getSequenceConfig().getApplicationConfig()
.getSubjectClaimUri().trim());
}
if (subjectValue != null) {
AuthenticatedUser authenticatedUser = sequenceConfig.getAuthenticatedUser();
authenticatedUser.setAuthenticatedSubjectIdentifier(subjectValue);
if (log.isDebugEnabled()) {
log.debug("Authenticated User: " +
sequenceConfig.getAuthenticatedUser().getAuthenticatedSubjectIdentifier());
log.debug("Authenticated User Tenant Domain: " + sequenceConfig.getAuthenticatedUser().getTenantDomain());
}
}
}
}
/**
* @param sequenceConfig
* @param locallyMappedUserRoles
* @return
*/
protected String getServiceProviderMappedUserRoles(SequenceConfig sequenceConfig,
List<String> locallyMappedUserRoles) throws FrameworkException {
if (locallyMappedUserRoles != null && !locallyMappedUserRoles.isEmpty()) {
Map<String, String> localToSpRoleMapping = sequenceConfig.getApplicationConfig()
.getRoleMappings();
boolean roleMappingDefined = false;
if (localToSpRoleMapping != null && !localToSpRoleMapping.isEmpty()) {
roleMappingDefined = true;
}
StringBuilder spMappedUserRoles = new StringBuilder();
for (String role : locallyMappedUserRoles) {
if (roleMappingDefined) {
if (localToSpRoleMapping.containsKey(role)) {
spMappedUserRoles.append(role).append(",");
}
} else {
spMappedUserRoles.append(role).append(",");
}
}
}
return null;
}
/**
* @param appConfig
* @return
*/
protected String getSpRoleClaimUri(ApplicationConfig appConfig) throws FrameworkException {
// get external identity provider role claim uri.
String spRoleClaimUri = appConfig.getRoleClaim();
if (spRoleClaimUri == null) {
// no role claim uri defined
// we can still try to find it out - lets have a look at the claim
// mapping.
Map<String, String> spToLocalClaimMapping = appConfig.getClaimMappings();
if (spToLocalClaimMapping != null && !spToLocalClaimMapping.isEmpty()) {
for (Entry<String, String> entry : spToLocalClaimMapping.entrySet()) {
if (FrameworkConstants.LOCAL_ROLE_CLAIM_URI.equals(entry.getValue())) {
return entry.getKey();
}
}
}
}
if (spRoleClaimUri == null) {
return FrameworkConstants.LOCAL_ROLE_CLAIM_URI;
}
return null;
}
/**
* @param context
* @return
* @throws FrameworkException
*/
protected Map<String, String> handleClaimMappings(AuthenticationContext context)
throws FrameworkException {
Map<String, String> mappedAttrs = null;
try {
mappedAttrs = FrameworkUtils.getClaimHandler().handleClaimMappings(null, context, null,
false);
return mappedAttrs;
} catch (FrameworkException e) {
log.error("Claim handling failed!", e);
}
return null;
}
}