/*
* Copyright (c) 2014, 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.authenticator.basicauth;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.LocalApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.config.ConfigurationFacade;
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.InvalidCredentialsException;
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.FrameworkUtils;
import org.wso2.carbon.identity.application.authenticator.basicauth.internal.BasicAuthenticatorServiceComponent;
import org.wso2.carbon.identity.base.IdentityRuntimeException;
import org.wso2.carbon.identity.core.model.IdentityErrorMsgContext;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.UserStoreException;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
/**
* Username Password based Authenticator
*/
public class BasicAuthenticator extends AbstractApplicationAuthenticator
implements LocalApplicationAuthenticator {
private static final long serialVersionUID = 1819664539416029785L;
private static final Log log = LogFactory.getLog(BasicAuthenticator.class);
@Override
public boolean canHandle(HttpServletRequest request) {
String userName = request.getParameter(BasicAuthenticatorConstants.USER_NAME);
String password = request.getParameter(BasicAuthenticatorConstants.PASSWORD);
if (userName != null && password != null) {
return true;
}
return false;
}
@Override
public AuthenticatorFlowStatus process(HttpServletRequest request,
HttpServletResponse response, AuthenticationContext context)
throws AuthenticationFailedException, LogoutFailedException {
if (context.isLogoutRequest()) {
return AuthenticatorFlowStatus.SUCCESS_COMPLETED;
} else {
return super.process(request, response, context);
}
}
@Override
protected void initiateAuthenticationRequest(HttpServletRequest request,
HttpServletResponse response, AuthenticationContext context)
throws AuthenticationFailedException {
Map<String, String> parameterMap = getAuthenticatorConfig().getParameterMap();
String showAuthFailureReason = null;
if (parameterMap != null) {
showAuthFailureReason = parameterMap.get("showAuthFailureReason");
if (log.isDebugEnabled()) {
log.debug("showAuthFailureReason has been set as : " + showAuthFailureReason);
}
}
String loginPage = ConfigurationFacade.getInstance().getAuthenticationEndpointURL();
String retryPage = ConfigurationFacade.getInstance().getAuthenticationEndpointRetryURL();
String queryParams = context.getContextIdIncludedQueryParams();
try {
String retryParam = "";
if (context.isRetrying()) {
retryParam = "&authFailure=true&authFailureMsg=login.fail.message";
}
if (context.getProperty("UserTenantDomainMismatch") != null &&
(Boolean)context.getProperty("UserTenantDomainMismatch")) {
retryParam = "&authFailure=true&authFailureMsg=user.tenant.domain.mismatch.message";
context.setProperty("UserTenantDomainMismatch", false);
}
IdentityErrorMsgContext errorContext = IdentityUtil.getIdentityErrorMsg();
IdentityUtil.clearIdentityErrorMsg();
if (showAuthFailureReason != null && "true".equals(showAuthFailureReason)) {
if (errorContext != null) {
if (log.isDebugEnabled()) {
log.debug("Identity error message context is not null");
}
String errorCode = errorContext.getErrorCode();
int remainingAttempts = errorContext.getMaximumLoginAttempts() - errorContext.getFailedLoginAttempts();
if (log.isDebugEnabled()) {
log.debug("errorCode : " + errorCode);
log.debug("username : " + request.getParameter(BasicAuthenticatorConstants.USER_NAME));
log.debug("remainingAttempts : " + remainingAttempts);
}
if (errorCode.equals(UserCoreConstants.ErrorCode.INVALID_CREDENTIAL)) {
retryParam = retryParam + BasicAuthenticatorConstants.ERROR_CODE + errorCode
+ BasicAuthenticatorConstants.FAILED_USERNAME + URLEncoder.encode(request.getParameter(BasicAuthenticatorConstants.USER_NAME), BasicAuthenticatorConstants.UTF_8)
+ "&remainingAttempts=" + remainingAttempts;
response.sendRedirect(response.encodeRedirectURL(loginPage + ("?" + queryParams))
+ BasicAuthenticatorConstants.AUTHENTICATORS + getName() + ":" + BasicAuthenticatorConstants.LOCAL + retryParam);
} else if (errorCode.equals(UserCoreConstants.ErrorCode.USER_IS_LOCKED)) {
String redirectURL = retryPage;
if (remainingAttempts == 0) {
redirectURL = response.encodeRedirectURL(redirectURL + ("?" + queryParams)) +
BasicAuthenticatorConstants.ERROR_CODE + errorCode + BasicAuthenticatorConstants.FAILED_USERNAME +
URLEncoder.encode(request.getParameter(BasicAuthenticatorConstants.USER_NAME), BasicAuthenticatorConstants.UTF_8) +
"&remainingAttempts=0";
} else {
redirectURL = response.encodeRedirectURL(redirectURL + ("?" + queryParams)) +
BasicAuthenticatorConstants.ERROR_CODE + errorCode + BasicAuthenticatorConstants.FAILED_USERNAME +
URLEncoder.encode(request.getParameter(BasicAuthenticatorConstants.USER_NAME), BasicAuthenticatorConstants.UTF_8);
}
response.sendRedirect(redirectURL);
} else if (errorCode.equals(UserCoreConstants.ErrorCode.USER_DOES_NOT_EXIST)) {
retryParam = retryParam + BasicAuthenticatorConstants.ERROR_CODE + errorCode
+ BasicAuthenticatorConstants.FAILED_USERNAME + URLEncoder.encode(request.getParameter(BasicAuthenticatorConstants.USER_NAME), BasicAuthenticatorConstants.UTF_8);
response.sendRedirect(response.encodeRedirectURL(loginPage + ("?" + queryParams))
+ BasicAuthenticatorConstants.AUTHENTICATORS + getName() + ":" + BasicAuthenticatorConstants.LOCAL + retryParam);
}
} else {
response.sendRedirect(response.encodeRedirectURL(loginPage + ("?" + queryParams))
+ BasicAuthenticatorConstants.AUTHENTICATORS + getName() + ":" + BasicAuthenticatorConstants.LOCAL + retryParam);
}
} else {
String errorCode = errorContext != null ? errorContext.getErrorCode() : null;
if (errorCode != null && errorCode.equals(UserCoreConstants.ErrorCode.USER_IS_LOCKED)) {
String redirectURL = retryPage;
redirectURL = response.encodeRedirectURL(redirectURL + ("?" + queryParams)) + BasicAuthenticatorConstants.FAILED_USERNAME + URLEncoder.encode(request.getParameter(BasicAuthenticatorConstants.USER_NAME), BasicAuthenticatorConstants.UTF_8);
response.sendRedirect(redirectURL);
} else {
response.sendRedirect(response.encodeRedirectURL(loginPage + ("?" + queryParams))
+ BasicAuthenticatorConstants.AUTHENTICATORS + getName() + ":" + BasicAuthenticatorConstants.LOCAL + retryParam);
}
}
} catch (IOException e) {
throw new AuthenticationFailedException(e.getMessage(), e);
}
}
@Override
protected void processAuthenticationResponse(HttpServletRequest request,
HttpServletResponse response, AuthenticationContext context)
throws AuthenticationFailedException {
String username = request.getParameter(BasicAuthenticatorConstants.USER_NAME);
String password = request.getParameter(BasicAuthenticatorConstants.PASSWORD);
boolean isAuthenticated;
UserStoreManager userStoreManager;
// Check the authentication
try {
int tenantId = IdentityTenantUtil.getTenantIdOfUser(username);
UserRealm userRealm = BasicAuthenticatorServiceComponent.getRealmService().getTenantUserRealm(tenantId);
if (userRealm != null) {
userStoreManager = (UserStoreManager) userRealm.getUserStoreManager();
isAuthenticated = userStoreManager.authenticate(MultitenantUtils.getTenantAwareUsername(username), password);
} else {
throw new AuthenticationFailedException("Cannot find the user realm for the given tenant: " + tenantId);
}
} catch (IdentityRuntimeException e) {
if(log.isDebugEnabled()){
log.debug("BasicAuthentication failed while trying to get the tenant ID of the user " + username, e);
}
throw new AuthenticationFailedException(e.getMessage(), e);
} catch (org.wso2.carbon.user.api.UserStoreException e) {
if(log.isDebugEnabled()){
log.debug("BasicAuthentication failed while trying to authenticate", e);
}
throw new AuthenticationFailedException(e.getMessage(), e);
}
if (!isAuthenticated) {
if (log.isDebugEnabled()) {
log.debug("User authentication failed due to invalid credentials");
}
throw new InvalidCredentialsException("User authentication failed due to invalid credentials");
}
Map<String, Object> authProperties = context.getProperties();
String tenantDomain = MultitenantUtils.getTenantDomain(username);
if (authProperties == null) {
authProperties = new HashMap<String, Object>();
context.setProperties(authProperties);
}
//TODO: user tenant domain has to be an attribute in the AuthenticationContext
authProperties.put("user-tenant-domain", tenantDomain);
username = FrameworkUtils.prependUserStoreDomainToName(username);
if (getAuthenticatorConfig().getParameterMap() != null) {
String userNameUri = getAuthenticatorConfig().getParameterMap().get("UserNameAttributeClaimUri");
if (userNameUri != null && userNameUri.trim().length() > 0) {
boolean multipleAttributeEnable;
String domain = UserCoreUtil.getDomainFromThreadLocal();
if (domain != null && domain.trim().length() > 0) {
multipleAttributeEnable = Boolean.parseBoolean(userStoreManager.getSecondaryUserStoreManager(domain).
getRealmConfiguration().getUserStoreProperty("MultipleAttributeEnable"));
} else {
multipleAttributeEnable = Boolean.parseBoolean(userStoreManager.
getRealmConfiguration().getUserStoreProperty("MultipleAttributeEnable"));
}
if (multipleAttributeEnable) {
try {
if (log.isDebugEnabled()) {
log.debug("Searching for UserNameAttribute value for user " + username +
" for claim uri : " + userNameUri);
}
String usernameValue = userStoreManager.
getUserClaimValue(MultitenantUtils.getTenantAwareUsername(username), userNameUri, null);
if (usernameValue != null && usernameValue.trim().length() > 0) {
tenantDomain = MultitenantUtils.getTenantDomain(username);
usernameValue = FrameworkUtils.prependUserStoreDomainToName(usernameValue);
username = usernameValue + "@" + tenantDomain;
if (log.isDebugEnabled()) {
log.debug("UserNameAttribute is found for user. Value is : " + username);
}
}
} catch (UserStoreException e) {
//ignore but log in debug
if(log.isDebugEnabled()) {
log.debug("Error while retrieving UserNameAttribute for user : " + username, e);
}
}
} else {
if (log.isDebugEnabled()) {
log.debug("MultipleAttribute is not enabled for user store domain : " + domain + " " +
"Therefore UserNameAttribute is not retrieved");
}
}
}
}
context.setSubject(AuthenticatedUser.createLocalAuthenticatedUserFromSubjectIdentifier(username));
String rememberMe = request.getParameter("chkRemember");
if (rememberMe != null && "on".equals(rememberMe)) {
context.setRememberMe(true);
}
}
@Override
protected boolean retryAuthenticationEnabled() {
return true;
}
@Override
public String getContextIdentifier(HttpServletRequest request) {
return request.getParameter("sessionDataKey");
}
@Override
public String getFriendlyName() {
return BasicAuthenticatorConstants.AUTHENTICATOR_FRIENDLY_NAME;
}
@Override
public String getName() {
return BasicAuthenticatorConstants.AUTHENTICATOR_NAME;
}
}