Java tutorial
/* * Copyright (c) 2015, 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.authenticator.MePIN; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonPrimitive; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.identity.application.authentication.framework.AbstractApplicationAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus; import org.wso2.carbon.identity.application.authentication.framework.FederatedApplicationAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.LocalApplicationAuthenticator; import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade; 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.ApplicationAuthenticatorException; import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException; import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException; 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.common.model.Property; import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil; import org.wso2.carbon.identity.user.profile.mgt.UserProfileException; import org.wso2.carbon.user.core.UserCoreConstants; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Authenticator of MePIN */ public class MePINAuthenticator extends AbstractApplicationAuthenticator implements FederatedApplicationAuthenticator { private static final long serialVersionUID = -8948601002969608129L; private static Log log = LogFactory.getLog(MePINAuthenticator.class); private Map<String, String> authenticatorProperties; /** * Check whether the authentication or logout request can be handled by the authenticator */ @Override public boolean canHandle(HttpServletRequest request) { if (log.isDebugEnabled()) { log.debug("Inside MePINLinkAuthenticator.canHandle()"); } return ((!StringUtils.isEmpty(request.getParameter(MePINConstants.MEPIN_ACCESSTOKEN)) && !StringUtils.isEmpty(request.getParameter(MePINConstants.USERNAME)) && !StringUtils.isEmpty(request.getParameter(MePINConstants.PASSWORD))) || (!StringUtils.isEmpty(request.getParameter(MePINConstants.USERNAME)) && !StringUtils.isEmpty(request.getParameter(MePINConstants.PASSWORD)))); } /** * initiate the authentication request */ @Override protected void initiateAuthenticationRequest(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException { authenticatorProperties = context.getAuthenticatorProperties(); String loginPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL(); loginPage = loginPage.replace("login.do", "mepinauth.jsp"); try { String retryParam = ""; if (context.isRetrying()) { retryParam = "&authFailure=true&authFailureMsg=authentication.fail.message"; } response.sendRedirect(response.encodeRedirectURL(loginPage + "?authenticators=" + getName() + "&applicationId=" + authenticatorProperties.get(MePINConstants.MEPIN_APPICATION_ID) + "&callbackUrl=" + authenticatorProperties.get(MePINConstants.MEPIN_CALLBACK_URL) + "&" + FrameworkConstants.SESSION_DATA_KEY + "=" + context.getContextIdentifier() + retryParam)); } catch (IOException e) { if (log.isDebugEnabled()) { log.debug("Error while redirecting"); } throw new AuthenticationFailedException("Error while redirecting the MePIN"); } } /** * Get the configuration properties of UI */ @Override public List<Property> getConfigurationProperties() { List<Property> configProperties = new ArrayList<Property>(); Property applicationId = new Property(); applicationId.setName(MePINConstants.MEPIN_APPICATION_ID); applicationId.setDisplayName("Application Id"); applicationId.setRequired(true); applicationId.setDescription("Enter MePIN application id value"); configProperties.add(applicationId); Property username = new Property(); username.setName(MePINConstants.MEPIN_USERNAME); username.setDisplayName("Username"); username.setRequired(true); username.setDescription("Enter username"); configProperties.add(username); Property password = new Property(); password.setName(MePINConstants.MEPIN_PASSWORD); password.setDisplayName("Password"); password.setRequired(true); password.setConfidential(true); password.setDescription("Enter password"); configProperties.add(password); Property callbackUrl = new Property(); callbackUrl.setName(MePINConstants.MEPIN_CALLBACK_URL); callbackUrl.setDisplayName("Callback URL"); callbackUrl.setRequired(true); callbackUrl.setDescription("Enter value corresponding to callback url"); configProperties.add(callbackUrl); Property clientId = new Property(); clientId.setName(MePINConstants.MEPIN_CLIENT_ID); clientId.setDisplayName("Client Id"); clientId.setRequired(true); clientId.setDescription("Enter Client Id"); configProperties.add(clientId); Property confirmationPolicy = new Property(); confirmationPolicy.setName(MePINConstants.MEPIN_CONFIRMATION_POLICY); confirmationPolicy.setDisplayName("Confirmation Policy"); confirmationPolicy.setRequired(true); confirmationPolicy.setDescription("Enter Confirmation Policy"); configProperties.add(confirmationPolicy); Property expiryTime = new Property(); expiryTime.setName(MePINConstants.MEPIN_EXPIRY_TIME); expiryTime.setDisplayName("Expiry Time"); expiryTime.setRequired(true); expiryTime.setDescription("Enter Expiry Time (in seconds)"); configProperties.add(expiryTime); Property header = new Property(); header.setName(MePINConstants.MEPIN_HEADER); header.setDisplayName("Header"); header.setRequired(true); header.setDescription("Enter Header"); configProperties.add(header); Property shortMessage = new Property(); shortMessage.setName(MePINConstants.MEPIN_SHORT_MESSAGE); shortMessage.setDisplayName("Short Message"); shortMessage.setRequired(true); shortMessage.setDescription("Enter Short Message"); configProperties.add(shortMessage); Property message = new Property(); message.setName(MePINConstants.MEPIN_MESSAGE); message.setDisplayName("Message"); message.setRequired(true); message.setDescription("Enter Message"); configProperties.add(message); return configProperties; } /** * Process the response of the MePIN end-point */ @Override protected void processAuthenticationResponse(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException { if ((!StringUtils.isEmpty(request.getParameter(MePINConstants.MEPIN_ACCESSTOKEN)))) { try { String authenticatedLocalUsername = getLocalAuthenticatedUser(context).getUserName(); String accessToken = request.getParameter(MePINConstants.MEPIN_ACCESSTOKEN); String responseString = new MePINTransactions().getUserInformation( authenticatorProperties.get(MePINConstants.MEPIN_USERNAME), authenticatorProperties.get(MePINConstants.MEPIN_PASSWORD), accessToken); if (!responseString.equals(MePINConstants.FAILED)) { JsonObject responseJson = new JsonParser().parse(responseString).getAsJsonObject(); String mepinId = responseJson.getAsJsonPrimitive(MePINConstants.MEPIN_ID).getAsString(); associateFederatedIdToLocalUsername(authenticatedLocalUsername, context, getFederateAuthenticatedUser(context, mepinId)); context.setSubject(AuthenticatedUser .createLocalAuthenticatedUserFromSubjectIdentifier(authenticatedLocalUsername)); } else { throw new AuthenticationFailedException("Unable to get the MePIN ID."); } } catch (ApplicationAuthenticatorException e) { log.error("Unable to set the subject: " + e.getMessage(), e); throw new AuthenticationFailedException("Unable to set the subject: " + e.getMessage(), e); } catch (UserProfileException e) { log.error("Unable to associate the user: " + e.getMessage(), e); throw new AuthenticationFailedException("Unable to associate the user: " + e.getMessage(), e); } } else { String allowStatus = ""; try { String authenticatedLocalUsername = getLocalAuthenticatedUser(context).getUserName(); String idpName = context.getExternalIdP().getIdPName(); String mePinId = null; mePinId = getMepinIdAssociatedWithUsername(idpName, authenticatedLocalUsername); boolean isAuthenticated = false; JsonObject transactionResponse = null; transactionResponse = new MePINTransactions().createTransaction(mePinId, context.getContextIdentifier(), MePINConstants.MEPIN_CREATE_TRANSACTION_URL, authenticatorProperties.get(MePINConstants.MEPIN_USERNAME), authenticatorProperties.get(MePINConstants.MEPIN_PASSWORD), authenticatorProperties.get(MePINConstants.MEPIN_CLIENT_ID), authenticatorProperties.get(MePINConstants.MEPIN_HEADER), authenticatorProperties.get(MePINConstants.MEPIN_MESSAGE), authenticatorProperties.get(MePINConstants.MEPIN_SHORT_MESSAGE), authenticatorProperties.get(MePINConstants.MEPIN_CONFIRMATION_POLICY), authenticatorProperties.get(MePINConstants.MEPIN_CALLBACK_URL), authenticatorProperties.get(MePINConstants.MEPIN_EXPIRY_TIME)); String transactionId = transactionResponse.getAsJsonPrimitive(MePINConstants.MEPIN_TRANSACTION_ID) .getAsString(); String status = transactionResponse.getAsJsonPrimitive(MePINConstants.MEPIN_STATUS).getAsString(); if (status.equalsIgnoreCase(MePINConstants.MEPIN_OK)) { if (log.isDebugEnabled()) { log.debug("Successfully created the MePIN transaction"); } int retry = 0; int retryInterval = 1; int retryCount = Integer.parseInt(authenticatorProperties.get(MePINConstants.MEPIN_EXPIRY_TIME)) / retryInterval; while (retry < retryCount) { JsonObject transactionStatusResponse = null; transactionStatusResponse = new MePINTransactions().getTransaction( MePINConstants.MEPIN_GET_TRANSACTION_URL, transactionId, authenticatorProperties.get(MePINConstants.MEPIN_CLIENT_ID), authenticatorProperties.get(MePINConstants.MEPIN_USERNAME), authenticatorProperties.get(MePINConstants.MEPIN_PASSWORD)); String transactionStatus = transactionStatusResponse .getAsJsonPrimitive(MePINConstants.MEPIN_TRANSACTION_STATUS).getAsString(); JsonPrimitive allowObject = transactionStatusResponse .getAsJsonPrimitive(MePINConstants.MEPIN_ALLOW); if (log.isDebugEnabled()) { log.debug("Transaction status :" + transactionStatus); } if (transactionStatus.equals(MePINConstants.MEPIN_COMPLETED)) { allowStatus = allowObject.getAsString(); if (Boolean.parseBoolean(allowStatus)) { isAuthenticated = true; break; } } Thread.sleep(1000); retry++; } if (isAuthenticated) { context.setSubject(AuthenticatedUser .createLocalAuthenticatedUserFromSubjectIdentifier(authenticatedLocalUsername)); } else { throw new AuthenticationFailedException("Unable to confirm the MePIN transaction"); } } else { if (log.isDebugEnabled()) { log.debug("Error while creating the MePIN transaction"); } throw new AuthenticationFailedException("Error while creating the MePIN transaction"); } } catch (UserProfileException e) { log.error("Unable to get the associated user: " + e.getMessage(), e); throw new AuthenticationFailedException("Unable to get the associated user: " + e.getMessage(), e); } catch (IOException e) { log.error("Unable to create the MePIN transaction: " + e.getMessage(), e); throw new AuthenticationFailedException("Unable to create the MePIN transaction: " + e.getMessage(), e); } catch (InterruptedException e) { log.error("Interruption occurred while getting the MePIN transaction status" + e.getMessage(), e); throw new AuthenticationFailedException( "Interruption occurred while getting the MePIN transaction status" + e.getMessage(), e); } } } /** * Get the friendly name of the Authenticator */ @Override public String getFriendlyName() { return MePINConstants.AUTHENTICATOR_FRIENDLY_NAME; } /** * Get the name of the Authenticator */ @Override public String getName() { return MePINConstants.AUTHENTICATOR_NAME; } @Override public AuthenticatorFlowStatus process(HttpServletRequest request, HttpServletResponse response, AuthenticationContext context) throws AuthenticationFailedException, LogoutFailedException { if (context.isLogoutRequest()) { return AuthenticatorFlowStatus.SUCCESS_COMPLETED; } return super.process(request, response, context); } /** * Get the Context identifier sent with the request. */ public String getContextIdentifier(HttpServletRequest request) { return request.getParameter(FrameworkConstants.SESSION_DATA_KEY); } @Override protected boolean retryAuthenticationEnabled() { return true; } private AuthenticatedUser getLocalAuthenticatedUser(AuthenticationContext context) { //username from authentication context. AuthenticatedUser authenticatedUser = null; for (int i = 1; i <= context.getSequenceConfig().getStepMap().size(); i++) { StepConfig stepConfig = context.getSequenceConfig().getStepMap().get(i); if (stepConfig.getAuthenticatedUser() != null && stepConfig.getAuthenticatedAutenticator() .getApplicationAuthenticator() instanceof LocalApplicationAuthenticator) { authenticatedUser = stepConfig.getAuthenticatedUser(); if (authenticatedUser.getUserStoreDomain() == null) { authenticatedUser.setUserStoreDomain(UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME); } if (log.isDebugEnabled()) { log.debug("username :" + authenticatedUser.toString()); } break; } } return authenticatedUser; } private AuthenticatedUser getFederateAuthenticatedUser(AuthenticationContext context, String authenticatedUserId) throws ApplicationAuthenticatorException { if (StringUtils.isEmpty(authenticatedUserId)) { throw new ApplicationAuthenticatorException("Authenticated user identifier is empty"); } AuthenticatedUser authenticatedUser = AuthenticatedUser .createFederateAuthenticatedUserFromSubjectIdentifier(authenticatedUserId); if (authenticatedUser.getUserStoreDomain() == null) { authenticatedUser.setUserStoreDomain(UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME); } authenticatedUser.setUserName(authenticatedUserId); if (log.isDebugEnabled()) { log.debug("The authenticated subject identifier :" + authenticatedUser.getAuthenticatedSubjectIdentifier()); } return authenticatedUser; } private void associateFederatedIdToLocalUsername(String authenticatedLocalUsername, AuthenticationContext context, AuthenticatedUser authenticatedUser) throws UserProfileException { StepConfig stepConfig = null; for (int i = 1; i <= context.getSequenceConfig().getStepMap().size(); i++) { stepConfig = context.getSequenceConfig().getStepMap().get(i); for (int j = 0; j < stepConfig.getAuthenticatorList().size(); j++) { if (stepConfig.getAuthenticatorList().get(j).getName().equals(getName())) { try { String idpName; String originalExternalIdpSubjectValueForThisStep = authenticatedUser .getAuthenticatedSubjectIdentifier(); idpName = context.getExternalIdP().getIdPName(); stepConfig.setAuthenticatedIdP(idpName); associateID(idpName, originalExternalIdpSubjectValueForThisStep, authenticatedLocalUsername); stepConfig.setAuthenticatedUser(authenticatedUser); context.getSequenceConfig().getStepMap().put(i, stepConfig); } catch (UserProfileException e) { throw new UserProfileException("Unable to continue with the federated ID (" + authenticatedUser.getAuthenticatedSubjectIdentifier() + "): " + e.getMessage(), e); } break; } } } } private void associateID(String idpID, String associatedID, String userName) throws UserProfileException { Connection connection = IdentityDatabaseUtil.getDBConnection(); PreparedStatement prepStmt = null; String sql = null; int tenantID = CarbonContext.getThreadLocalCarbonContext().getTenantId(); String tenantAwareUsername = MultitenantUtils.getTenantAwareUsername(userName); String domainName = getDomainName(tenantAwareUsername); tenantAwareUsername = getUsernameWithoutDomain(tenantAwareUsername); try { sql = "INSERT INTO IDN_ASSOCIATED_ID (TENANT_ID, IDP_ID, IDP_USER_ID, DOMAIN_NAME, USER_NAME) VALUES (? , (SELECT ID FROM IDP WHERE NAME = ? AND TENANT_ID = ? ), ? , ?, ?)"; prepStmt = connection.prepareStatement(sql); prepStmt.setInt(1, tenantID); prepStmt.setString(2, idpID); prepStmt.setInt(3, tenantID); prepStmt.setString(4, associatedID); prepStmt.setString(5, domainName); prepStmt.setString(6, tenantAwareUsername); prepStmt.execute(); connection.commit(); } catch (SQLException e) { log.error("Error occurred while persisting the federated user ID", e); throw new UserProfileException("Error occurred while persisting the federated user ID", e); } finally { IdentityDatabaseUtil.closeAllConnections(connection, (ResultSet) null, prepStmt); } } private static String getDomainName(String username) { int index = username.indexOf("/"); return index < 0 ? "PRIMARY" : username.substring(0, index); } private static String getUsernameWithoutDomain(String username) { int index = username.indexOf("/"); return index < 0 ? username : username.substring(index + 1, username.length()); } public String getMepinIdAssociatedWithUsername(String idpID, String username) throws UserProfileException { Connection connection = IdentityDatabaseUtil.getDBConnection(); PreparedStatement prepStmt = null; ResultSet resultSet; String sql = null; String mepinId = ""; int tenantID = CarbonContext.getThreadLocalCarbonContext().getTenantId(); try { sql = "SELECT IDP_USER_ID FROM IDN_ASSOCIATED_ID WHERE TENANT_ID = ? AND IDP_ID = (SELECT ID " + "FROM IDP WHERE NAME = ? AND TENANT_ID = ?) AND USER_NAME = ?"; prepStmt = connection.prepareStatement(sql); prepStmt.setInt(1, tenantID); prepStmt.setString(2, idpID); prepStmt.setInt(3, tenantID); prepStmt.setString(4, username); resultSet = prepStmt.executeQuery(); connection.commit(); if (resultSet.next()) { mepinId = resultSet.getString(1); return mepinId; } } catch (SQLException e) { log.error("Error occurred while getting the associated MePIN ID", e); throw new UserProfileException("Error occurred while getting the associated MePIN ID", e); } finally { IdentityDatabaseUtil.closeAllConnections(connection, null, prepStmt); } return null; } }