/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application is free software; you can redistribute it and/or * modify it under the terms of the GNU LESSER GENERAL PUBLIC * LICENSE as published by the Free Software Foundation under * version 3 of the License * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * LESSER GENERAL PUBLIC LICENSE v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.abiserver.business.authentication; import java.util.Calendar; import java.util.Date; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.wink.client.ClientAuthenticationException; import org.apache.wink.client.ClientConfig; import org.apache.wink.client.handlers.BasicAuthSecurityHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.BadCredentialsException; import org.springframework.security.providers.encoding.Md5PasswordEncoder; import com.abiquo.abiserver.abicloudws.AbiCloudConstants; import com.abiquo.abiserver.business.hibernate.pojohb.user.EnterpriseHB; import com.abiquo.abiserver.business.hibernate.pojohb.user.RoleHB; import com.abiquo.abiserver.business.hibernate.pojohb.user.UserHB; import com.abiquo.abiserver.commands.stub.LoginResourceStub; import com.abiquo.abiserver.commands.stub.impl.LoginResourceStubImpl; import com.abiquo.abiserver.config.AbiConfig; import com.abiquo.abiserver.config.AbiConfigManager; import com.abiquo.abiserver.persistence.DAOFactory; import com.abiquo.abiserver.persistence.dao.user.EnterpriseDAO; import com.abiquo.abiserver.persistence.dao.user.RoleDAO; import com.abiquo.abiserver.persistence.dao.user.UserDAO; import com.abiquo.abiserver.persistence.dao.user.UserSessionDAO; import com.abiquo.abiserver.persistence.hibernate.HibernateDAOFactory; import com.abiquo.abiserver.pojo.authentication.Login; import com.abiquo.abiserver.pojo.authentication.LoginResult; import com.abiquo.abiserver.pojo.authentication.UserSession; import com.abiquo.abiserver.pojo.result.BasicResult; import com.abiquo.abiserver.pojo.result.DataResult; import com.abiquo.server.core.enterprise.UserDto; import com.abiquo.util.ErrorManager; import com.abiquo.util.resources.ResourceManager; public class AuthenticationManagerApi implements IAuthenticationManager { private static final Logger logger = LoggerFactory.getLogger(AuthenticationManagerApi.class); /** * Abiquo API URL. */ private String apiUri; /** * Factory of DAOs and transaction manager. */ private DAOFactory factory; private final AbiConfig abiConfig = AbiConfigManager.getInstance().getAbiConfig(); private static final ResourceManager resourceManger = new ResourceManager(AuthenticationManagerDB.class); private final ErrorManager errorManager = ErrorManager.getInstance(AbiCloudConstants.ERROR_PREFIX); public AuthenticationManagerApi() { if (factory == null) { factory = HibernateDAOFactory.instance(); } } /** * @see com.abiquo.abiserver.business.authentication.IAuthenticationManager#checkSession(com.abiquo.abiserver.pojo.authentication.UserSession) */ @Override public BasicResult checkSession(final UserSession userSession) { BasicResult checkSessionResult = new BasicResult(); getFactory().beginConnection(); UserSession sessionToCheck = null; try { sessionToCheck = getUserSessionDAO().getCurrentUserSession(userSession.getUser(), userSession.getKey()); if (sessionToCheck == null) { // The session does not exist, so is not valid checkSessionResult.setResultCode(BasicResult.SESSION_INVALID); // logger.debug("The session is invalid. Please log in again. "); // log into the // authentication.log // errorManager // .reportError(resourceManger, checkSessionResult, "checkSession.invalid"); logger.trace("Invalid session. Please login again"); } else { // Checking if the session has expired Date currentDate = new Date(); if (currentDate.before(sessionToCheck.getExpireDate())) { extendSession(sessionToCheck); checkSessionResult.setSuccess(true); checkSessionResult.setMessage(AuthenticationManagerApi.resourceManger .getMessage("checkSession.success")); } else { // The session has time out. Deleting the session from Data Base getUserSessionDAO().makeTransient(sessionToCheck); checkSessionResult.setResultCode(BasicResult.SESSION_TIMEOUT); // logger.debug("The session is expired. Please log in again. "); // log into // the // authentication.log // errorManager.reportError(resourceManger, checkSessionResult, // "checkSession.expired"); logger.trace("Session expired. Please login again"); } } } catch (Exception e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } logger.trace("Unexpected error while checking the user session", e); } finally { getFactory().endConnection(); } return checkSessionResult; } /** * Authentication against the Abiquo API needs a Basic Authentication (Plain text). * * @param login credentials. * @return BasicAuthSecurityHandler ready to be placed in the chain. */ private BasicAuthSecurityHandler createAuthenticationToken(final Login login) { BasicAuthSecurityHandler basicAuthHandler = new BasicAuthSecurityHandler(); basicAuthHandler.setUserName(login.getUser()); basicAuthHandler.setPassword(login.getPassword()); return basicAuthHandler; } /** * Authentication against DB expects a Md5 hash. * * @param login credentials. * @return Md5 hash of the credentials. */ private String createMd5encodedPassword(final Login login) { Md5PasswordEncoder encoder = new Md5PasswordEncoder(); String passwordHash = encoder.encodePassword(login.getPassword(), null); return passwordHash; } /** * Creates the usersession data. * * @param userHB DB user. * @return UserSession logged user. */ private UserSession createUserSession(final UserHB userHB) { UserSession userSession = new UserSession(); userSession.setUser(userHB.getUser()); userSession.setKey(String.valueOf(Calendar.getInstance().getTimeInMillis())); userSession.setLocale(userHB.getLocale()); userSession.setUserIdDb(userHB.getIdUser()); userSession.setEnterpriseName(userHB.getEnterpriseHB().getName()); int sessionTimeout = abiConfig.getSessionTimeout(); long expireMilis = new Date().getTime() + sessionTimeout * 60 * 1000; Date expireDate = new Date(expireMilis); userSession.setExpireDate(expireDate); userSession.setAuthType(userHB.getAuthType()); // Saving in Data Base the created User Session getUserSessionDAO().makePersistent(userSession); return userSession; } /** * Delete old sessions from DB. * * @param userHB * @param dataResult void */ private void deleteOldSessions(final UserHB userHB, final DataResult<LoginResult> dataResult) { // Looking for all existing active sessions of this user, ordered by when were getUserSessionDAO().deleteUserSessionsOlderThan(userHB.getName(), new Date()); } /** * This function should be deprecated since is an ad-hoc implementation (mostly a hack) to * delegates the login to the Abiquo API. * * @see com.abiquo.abiserver.business.authentication.IAuthenticationManager#doLogin(com.abiquo.abiserver.pojo.authentication.Login) */ @Override public DataResult<LoginResult> doLogin(final Login login) { DataResult<LoginResult> dataResult = new DataResult<LoginResult>(); UserDto userDto = null; try { if (StringUtils.isBlank(login.getAuthToken())) { BasicAuthSecurityHandler basicAuthHandler = createAuthenticationToken(login); // We perform this call to a secure location. If success then the credentials are // valid DataResult<UserDto> dataResultDto = apiLoginCall(login, basicAuthHandler); userDto = dataResultDto.getData(); // Old DB login needs a Md5 password String passwordHash = createMd5encodedPassword(login); basicAuthHandler.setPassword(passwordHash); login.setPassword(passwordHash); } dataResult = login(login, userDto); } catch (BadCredentialsException e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } errorManager.reportError(resourceManger, dataResult, "doLogin.passwordUserIncorrect"); dataResult.setResultCode(BasicResult.USER_INVALID); throw e; } catch (ClientAuthenticationException e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } errorManager.reportError(resourceManger, dataResult, "doLogin.passwordUserIncorrect"); dataResult.setResultCode(BasicResult.USER_INVALID); } catch (RuntimeException e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } logger.error(e.getMessage()); logger .info("Could not communicate with the Abiquo API, please check if the API is responding"); dataResult.setMessage("Could not communicate with the Abiquo API"); } catch (Exception e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } errorManager.reportError(resourceManger, dataResult, "doLogin.passwordUserIncorrect"); dataResult.setResultCode(BasicResult.USER_INVALID); } return dataResult; } /** * @param login login. * @param basicAuthHandler handler. * @return DataResult<UserDto> */ private DataResult<UserDto> apiLoginCall(final Login login, final BasicAuthSecurityHandler basicAuthHandler) { ClientConfig clientConfig = new ClientConfig(); clientConfig.handlers(basicAuthHandler); LoginResourceStub proxy = getLoginStubProxy(); DataResult<UserDto> dataResultDto = proxy.getUserByName(login.getUser(), login.getPassword(), basicAuthHandler); return dataResultDto; } private DataResult<LoginResult> login(final Login login, final UserDto userDto) throws Exception { DataResult<LoginResult> dataResult; UserHB userHB; userHB = getUserToPersistSession(login, userDto); dataResult = persistLogin(userHB); return dataResult; } /** * @see com.abiquo.abiserver.business.authentication.IAuthenticationManager#doLogout(com.abiquo.abiserver.pojo.authentication.UserSession) */ @Override public BasicResult doLogout(final UserSession userSession) { BasicResult basicResult = new BasicResult(); getFactory().beginConnection(); try { getUserSessionDAO().deleteAllUserSessions(userSession.getUser(), userSession.getKey()); basicResult.setSuccess(true); basicResult.setMessage(AuthenticationManagerApi.resourceManger .getMessage("doLogout.success")); getFactory().endConnection(); } catch (Exception e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } errorManager.reportError(resourceManger, basicResult, "doLogout", e); } finally { getFactory().endConnection(); } return basicResult; } /** * Extends current session by a time configured. * * @param sessionToCheck current session void. */ private void extendSession(final UserSession sessionToCheck) { // The session is valid updating the expire Date int sessionTimeout = abiConfig.getSessionTimeout(); long expireMilis = new Date().getTime() + sessionTimeout * 60 * 1000; Date expireDate = new Date(expireMilis); sessionToCheck.setExpireDate(expireDate); // Although in the manual doesnt says so, this method does saveOrUpdate. getUserSessionDAO().makePersistent(sessionToCheck); } /** * @see com.abiquo.abiserver.business.authentication.IAuthenticationManager#findAllSessions(java.lang.String) */ @Override public List<UserSession> findAllSessions(final String username) { List<UserSession> sessions = null; try { sessions = getUserSessionDAO().findByProperty("user", username); } catch (Exception ex) { BasicResult checkSessionResult = new BasicResult(); checkSessionResult.setSuccess(false); errorManager.reportError(resourceManger, checkSessionResult, "checkSession.exception", ex); } return sessions; } /** * Generates the result. This function is provided for encapsulation purposes. * * @param dataResult * @param loginResult void */ private void generate(final DataResult<LoginResult> dataResult, final LoginResult loginResult) { // Generating the DataResult dataResult.setSuccess(true); dataResult .setMessage(AuthenticationManagerApi.resourceManger.getMessage("doLogin.success")); dataResult.setData(loginResult); } /** * Generates the login. * * @param userHB DB user. * @param userSession logged user. * @return LoginResult pojo containing the data to login. */ private LoginResult generateLoginResult(final UserHB userHB, final UserSession userSession) { // Generating the login result, with the user who has logged in and his session LoginResult loginResult = new LoginResult(); loginResult.setSession(userSession); loginResult.setUser(userHB.toPojo()); return loginResult; } /** * If unset, sets the URI. * * @return Abiquo API URL. */ public String getApiUri() { if (apiUri == null) { apiUri = abiConfig.getApiLocation(); } return apiUri; } public EnterpriseDAO getEnterpriseDAO() { return getFactory().getEnterpriseDAO(); } /** * Retrieves the gateway to the api, focused on Enterprise resource. This method instantiates a * new LoginResourceStubImpl * * @return EnterprisesResourceStub. */ protected LoginResourceStub getLoginStubProxy() { LoginResourceStub proxy = new LoginResourceStubImpl(); return proxy; } public DAOFactory getFactory() { if (factory == null) { factory = HibernateDAOFactory.instance(); } return factory; } // DAO's public RoleDAO getRoleDAO() { return getFactory().getRoleDAO(); } public UserDAO getUserHBDao() { return getFactory().getUserDAO(); } public UserSessionDAO getUserSessionDAO() { return getFactory().getUserSessionDAO(); } // DAO's /** * Searches at DB for the user. * * @param login login data. * @param userDto pojo that contains data from the current user. * @return DB user. * @throws Exception UserHB. */ private UserHB getUserToPersistSession(final Login login, final UserDto userDto) throws Exception { // Get the user from the appropiate source UserHB userHB = null; getFactory().beginConnection(); if (StringUtils.isBlank(login.getAuthToken())) { userHB = userDtoToUserHB(userDto); // getUser(login, session); } else { userHB = getUserUsingToken(login); } getFactory().endConnection(); return userHB; } /** * Gets the user from DB using the authentication Token information. * * @param login The object with the token information. * @param session The Hibernate session. * @return The user. */ private UserHB getUserUsingToken(final Login login) throws Exception { // Get token data String[] token = TokenUtils.getTokenFields(login.getAuthToken()); String tokenUser = TokenUtils.getTokenUser(token); long tokenExpiration = TokenUtils.getTokenExpiration(token); String tokenSignature = TokenUtils.getTokenSignature(token); // Check token expiration if (TokenUtils.isTokenExpired(tokenExpiration)) { throw new Exception("Authentication token has expired."); } // Get the token user from db UserHB userHB = getUserHBDao().findUniqueByProperty("user", tokenUser); if (userHB != null) { // Validate credentials with the token String signature = TokenUtils.makeTokenSignature(tokenExpiration, userHB.getUser(), userHB .getPassword()) + userHB.getAuthType(); // Just as we create the signature by adding the AuthType if (!signature.equals(tokenSignature + token[3])) { return null; } userHB.setEnterpriseHB(getFactory().getEnterpriseDAO().findById( userHB.getEnterpriseHB().getIdEnterprise())); } return userHB; } /** * @see com.abiquo.abiserver.business.authentication.IAuthenticationManager#isLoggedIn(java.lang.String) */ @Override public boolean isLoggedIn(final String username) { List<UserSession> sessions = findAllSessions(username); return sessions != null && !sessions.isEmpty(); } /** * Stores the information we need to keep. * * @param userHB user. * @return DataResult<LoginResult> */ public DataResult<LoginResult> persistLogin(final UserHB userHB) { DataResult<LoginResult> dataResult = new DataResult<LoginResult>(); getFactory().beginConnection(); try { if (userHB != null) { // User exists. Check if it is active if (userHB.getActive() == 1) { // User exists in database and is active. deleteOldSessions(userHB, dataResult); // Creating the user session UserSession userSession = createUserSession(userHB); LoginResult loginResult = generateLoginResult(userHB, userSession); generate(dataResult, loginResult); } else { // User is not active. Generating the DataResult errorManager.reportError(resourceManger, dataResult, "doLogin.userInActive"); } } else { // User not exists in database or bad credentials. Generating the DataResult errorManager.reportError(resourceManger, dataResult, "doLogin.passwordUserIncorrect"); dataResult.setResultCode(BasicResult.USER_INVALID); } } catch (Exception e) { if (getFactory().isTransactionActive()) { getFactory().rollbackConnection(); } errorManager.reportError(resourceManger, dataResult, "doLogin.exception", e); } getFactory().endConnection(); return dataResult; } /** * Parses a dto to a db info pojo. It does not access to db. Just a parser between pojos. * * @param userDto user dto returned from the API. * @return db user, representation of an Hibernate pojo user. */ private UserHB userDtoToUserHB(final UserDto userDto) { UserHB userHB = new UserHB(); userHB.setActive(userDto.isActive() ? 1 : 0); userHB.setAvailableVirtualDatacenters(userDto.getAvailableVirtualDatacenters()); userHB.setDescription(userDto.getDescription()); userHB.setEmail(userDto.getEmail()); userHB.setIdUser(userDto.getId()); userHB.setLocale(userDto.getLocale()); userHB.setName(userDto.getName()); userHB.setPassword(userDto.getPassword()); userHB.setSurname(userDto.getSurname()); userHB.setUser(userDto.getNick()); userHB.setAuthType(userDto.getAuthType()); EnterpriseHB enterpriseHB = getEnterpriseDAO().findById(userDto.getIdFromLink("enterprise")); RoleHB roleHB = getRoleDAO().findById(userDto.getIdFromLink("role")); userHB.setEnterpriseHB(enterpriseHB); userHB.setRoleHB(roleHB); return userHB; } }