/* * Copyright (C) 2005-2012 BetaCONCEPT Limited * * This file is part of Astroboa. * * Astroboa 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, either version 3 of the License, or * (at your option) any later version. * * Astroboa 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 for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Astroboa. If not, see <http://www.gnu.org/licenses/>. */ package org.betaconceptframework.astroboa.console.jsf.login; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.faces.application.FacesMessage; import javax.faces.model.SelectItem; import javax.servlet.http.HttpServletRequest; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.lang.StringUtils; import org.betaconceptframework.astroboa.api.model.CmsRepository; import org.betaconceptframework.astroboa.api.model.RepositoryUser; import org.betaconceptframework.astroboa.api.security.AstroboaCredentials; import org.betaconceptframework.astroboa.api.security.IdentityPrincipal; import org.betaconceptframework.astroboa.api.security.exception.CmsInvalidPasswordException; import org.betaconceptframework.astroboa.api.security.exception.CmsLoginAccountExpiredException; import org.betaconceptframework.astroboa.api.security.exception.CmsLoginAccountLockedException; import org.betaconceptframework.astroboa.api.security.exception.CmsLoginInvalidUsernameException; import org.betaconceptframework.astroboa.api.security.exception.CmsLoginPasswordExpiredException; import org.betaconceptframework.astroboa.api.security.exception.CmsUnauthorizedRepositoryUseException; import org.betaconceptframework.astroboa.client.AstroboaClient; import org.betaconceptframework.astroboa.console.commons.ContentObjectStatefulSearchService; import org.betaconceptframework.astroboa.console.jsf.ContentObjectList; import org.betaconceptframework.astroboa.console.jsf.UIComponentBinding; import org.betaconceptframework.astroboa.console.security.CmsCredentials; import org.betaconceptframework.astroboa.console.security.IdentityStoreRunAsSystem; import org.betaconceptframework.astroboa.console.security.LoggedInRepositoryUser; import org.betaconceptframework.astroboa.console.security.LoggedInRepositoryUser.UserActivityType; import org.betaconceptframework.astroboa.context.AstroboaClientContext; import org.betaconceptframework.astroboa.context.AstroboaClientContextHolder; import org.betaconceptframework.astroboa.context.SecurityContext; import org.betaconceptframework.ui.jsf.comparator.SelectItemComparator; import org.betaconceptframework.ui.jsf.utility.JSFUtilities; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Observer; import org.jboss.seam.annotations.Scope; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.international.LocaleSelector; import org.jboss.seam.security.Identity; import org.jboss.seam.web.ServletContexts; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Gregory Chomatas (gchomatas@betaconcept.com) * @author Savvas Triantafyllou (striantafyllou@betaconcept.com) * */ @Name("login") @Scope(ScopeType.EVENT) public class LoginBean { @In FacesMessages facesMessages; private String userName; private String passWord; private final Logger logger = LoggerFactory.getLogger(getClass()); private final static String ASTROBOA_SERVER_KEY = "astroboa-server"; private String repositoryIdToConnectTo; private String serverURLToConnectTo; private List<CmsRepository> availableCmsRepositoriesForSelectedRemoteServer; private AstroboaClient astroboaClient; private LoggedInRepositoryUser loggedInRepositoryUser; @In(create=true) private IdentityStoreRunAsSystem identityStoreRunAsSystem; @In(create=true) private LocaleSelector localeSelector; //Injected beans. Check components.xml private ContentObjectList contentObjectList; private ContentObjectStatefulSearchService contentObjectStatefulSearchService; public String login() { try{ AstroboaClientContextHolder.clearActiveClientContext(); //Login to Astroboa repository astroboaClient.initialize(serverURLToConnectTo); astroboaClient.login(repositoryIdToConnectTo, new AstroboaCredentials(getUserName(), getPassWord().toCharArray())); //Login is successful //Instantiate Jboss' Seam Identity module //Retrieve JAAS application security domain name AstroboaClientContext clientContext = AstroboaClientContextHolder.getActiveClientContext(); if (clientContext == null || clientContext.getRepositoryContext() == null || clientContext.getRepositoryContext().getCmsRepository() == null){ logout(false); throw new Exception("No JAAS application policy name is provided for repository "+ repositoryIdToConnectTo); } else{ loggedInRepositoryUser.reset(); //Provide JAAS policy name //String jaasApplicationPolicyName = clientContext.getRepositoryContext().getCmsRepository().getApplicationPolicyName(); // Astroboa JAAS module will ve deprecated and thus we need to find antoher way to login to JBoss SEAM String jaasApplicationPolicyName = "astroboa"; // Check if logged in repository's identity store is externally managed. If so, record in LoggedInRepositoryUser bean that her id is // externally managed. This is required by certain UI modules in order to enable or disable user provisioning functionality. loggedInRepositoryUser.setExternallyManagedIdentity(StringUtils.isNotBlank(clientContext.getRepositoryContext().getCmsRepository().getExternalIdentityStoreJNDIName())); //Reset Identity Identity.instance().unAuthenticate(); Identity.instance().setJaasConfigName(jaasApplicationPolicyName); //Set credentials Identity.instance().getCredentials().setUsername(getUserName()); Identity.instance().getCredentials().setPassword(getPassWord()); ((CmsCredentials)Identity.instance().getCredentials()).setIdentityStoreRepositoryId( clientContext.getRepositoryContext().getCmsRepository().getIdentityStoreRepositoryId()); ((CmsCredentials)Identity.instance().getCredentials()).setIdentityStoreRepositoryJNDIName( clientContext.getRepositoryContext().getCmsRepository().getExternalIdentityStoreJNDIName()); //Login to SEAM String loginOutcome = Identity.instance().login(); // clear seam messages facesMessages.clear(); identityStoreRunAsSystem.reset(); //Reset content object list if (contentObjectList != null) { contentObjectList.resetViewAndStateBeforeNewContentSearchResultsPresentation(); } if (contentObjectStatefulSearchService != null) { contentObjectStatefulSearchService.setReturnedContentObjects(null); contentObjectStatefulSearchService.setSearchResultSetSize(0); } UIComponentBinding uiComponentBinding = (UIComponentBinding) Contexts.getEventContext().get("uiComponentBinding"); if (uiComponentBinding != null) { uiComponentBinding.resetContentObjectTableScrollerComponent(); uiComponentBinding.setListViewContentObjectTableComponent(null); uiComponentBinding.setListViewContentObjectTableScrollerComponent(null); uiComponentBinding.setFullViewContentObjectTableComponent(null); uiComponentBinding.setFullViewContentObjectTableScrollerComponent(null); } // if login was successful we should also check if a Repository User exists for the logged in user // A Repository User is automatically created upon first use of astroboa but we want to catch possible exceptions and prevent rendering the astroboa console page if (loginOutcome != null) { RepositoryUser repositoryUser = loggedInRepositoryUser.getRepositoryUser(); if (repositoryUser != null) { JSFUtilities.addMessage(null,"login.welcome", null, FacesMessage.SEVERITY_INFO); // save the password in the session // this is required by the ResourceApiProxy in order to construct secured API calls to the Resource API. // This is a temporary solution until the new security framework is implemented (through URL signining) HttpServletRequest httpServletRequest = ServletContexts.instance().getRequest(); if (httpServletRequest != null && httpServletRequest.getSession() != null) { httpServletRequest.getSession().setAttribute("repositoryPassword", passWord); } // write audit log if identity store is local, i.e. there is a local Person Object connected to the loggedin repository user loggedInRepositoryUser.updateConsloleLoginLog(UserActivityType.login); return loginOutcome; } else { //facesMessages.addFromResourceBundle(StatusMessage.Severity.ERROR, "login.authenticationServiceFailure", (Object[]) null); JSFUtilities.addMessage(null,"login.authenticationServiceFailure", null, FacesMessage.SEVERITY_ERROR); logout(false); return null; } } return null; } } catch (CmsLoginInvalidUsernameException e) { JSFUtilities.addMessage(null,"login.invalidCredentials",null, FacesMessage.SEVERITY_WARN); logger.info("User performed a login with invalid user name"); logout(false); return null; } catch (CmsInvalidPasswordException e) { JSFUtilities.addMessage(null,"login.invalidCredentials",null, FacesMessage.SEVERITY_WARN); logger.info("User performed a login with invalid password"); logout(false); return null; } catch (CmsLoginAccountExpiredException e) { JSFUtilities.addMessage(null,"login.accountExpired",null, FacesMessage.SEVERITY_WARN); logger.info("User performed a login with an account that has expired"); logout(false); return null; } catch (CmsLoginAccountLockedException e) { JSFUtilities.addMessage(null,"login.accountLocked",null, FacesMessage.SEVERITY_WARN); logger.info("User performed a login with an account that is locked"); logout(false); return null; } catch (CmsLoginPasswordExpiredException e) { JSFUtilities.addMessage(null,"login.passwordExpired",null, FacesMessage.SEVERITY_WARN); logger.info("User performed a login with a password that has expired"); logout(false); return null; } catch (CmsUnauthorizedRepositoryUseException e) { JSFUtilities.addMessage(null,"login.unauthorizedRepositoryAccess",null, FacesMessage.SEVERITY_WARN); logger.info("User tried to login into a repository to which she is not authorized"); logout(false); return null; } catch(Exception e){ logger.error("An error occured during user authentication",e); JSFUtilities.addMessage(null,"login.authenticationServiceFailure", null, FacesMessage.SEVERITY_ERROR); logout(false); return null; } } // this is used by page actions so we want logging to be enabled public void logout() { logout(true); } private void logout(boolean updateConsoleLoginLog) { if (updateConsoleLoginLog) { try { loggedInRepositoryUser.updateConsloleLoginLog(UserActivityType.logout); } catch (Exception e) { logger.error("An error occured during the update of conslole login log to record user logout. The logout will proceed normally without any notification to the user.", e); } } Identity.instance().logout(); identityStoreRunAsSystem.reset(); loggedInRepositoryUser.reset(); } @Observer(Identity.EVENT_PRE_AUTHENTICATE) public void configSeamPrincipal(){ SecurityContext securityContext = AstroboaClientContextHolder.getActiveSecurityContext(); //Expect to find a principal of Type IdentityPrincipal //inside Subject's Principals if (securityContext == null || securityContext.getSubject() == null || CollectionUtils.isEmpty(securityContext.getSubject().getPrincipals(IdentityPrincipal.class))){ throw new SecurityException("Identity Principal is missing"); } else{ Identity.instance().acceptExternallyAuthenticatedPrincipal(new IdentityPrincipal(securityContext.getSubject().getPrincipals(IdentityPrincipal.class).iterator().next().getName())); } } public void setRepositoryIdToConnectTo(String repositoryIdToConnectTo) { this.repositoryIdToConnectTo = repositoryIdToConnectTo; } public String getRepositoryIdToConnectTo() { return repositoryIdToConnectTo; } public String getServerURLToConnectTo() { return serverURLToConnectTo; } public void setServerURLToConnectTo(String serverURLToConnectTo) { this.serverURLToConnectTo = serverURLToConnectTo; } public List<SelectItem> getAvailableServers(){ List<SelectItem> availableAstroboaServers = new ArrayList<SelectItem>(); try { PropertiesConfiguration consoleConfiguration = new PropertiesConfiguration("astroboa-console.properties"); consoleConfiguration.setEncoding("UTF-8"); @SuppressWarnings("unchecked") List<Object> astroboaServers = consoleConfiguration.getList(ASTROBOA_SERVER_KEY); if(CollectionUtils.isNotEmpty(astroboaServers)){ for (Object astroboaServer : astroboaServers){ //Get server host or ip String astroboaHostOrIp = consoleConfiguration.getString((String)astroboaServer+".host"); if (StringUtils.isBlank(astroboaHostOrIp)){ astroboaHostOrIp = AstroboaClient.INTERNAL_CONNECTION; } //Get port String astroboaHostPort = consoleConfiguration.getString((String)astroboaServer+".port"); if (StringUtils.isBlank(astroboaHostPort)){ astroboaHostPort = "1099"; } //Get localized label String localizedLabel = consoleConfiguration.getString((String)astroboaServer+"."+ localeSelector.getLocaleString()); if (StringUtils.isBlank(localizedLabel)){ localizedLabel = (String)astroboaServer; } availableAstroboaServers.add(new SelectItem(astroboaHostOrIp+":"+astroboaHostPort, localizedLabel)); } if (availableAstroboaServers.size() > 1){ SelectItemComparator selectItemComparator = new SelectItemComparator(); selectItemComparator.setLocale(localeSelector.getLocaleString()); Collections.sort(availableAstroboaServers, selectItemComparator); } } } catch (ConfigurationException e) { logger.error("",e); } return availableAstroboaServers; } public void connectToSelectedServer(){ try{ astroboaClient.initialize(serverURLToConnectTo); } catch (Exception e){ logger.error("", e); } finally{ repositoryIdToConnectTo = null; availableCmsRepositoriesForSelectedRemoteServer = null; } } public List<CmsRepository> getAvailableCmsRepositoriesForSelectedServer(){ if (availableCmsRepositoriesForSelectedRemoteServer != null){ return availableCmsRepositoriesForSelectedRemoteServer; } //Check if repository service is available if (astroboaClient.getRepositoryService() == null){ connectToSelectedServer(); } //If repository service is available get all repositories if (astroboaClient.getRepositoryService() != null){ try{ availableCmsRepositoriesForSelectedRemoteServer = astroboaClient.getRepositoryService().getAvailableCmsRepositories(); } catch(Exception e){ logger.error("",e); } } return availableCmsRepositoriesForSelectedRemoteServer == null ? new ArrayList<CmsRepository>() :availableCmsRepositoriesForSelectedRemoteServer; } public void setAstroboaClient( AstroboaClient astroboaClient) { this.astroboaClient = astroboaClient; } /** * @return the userName */ public String getUserName() { return userName; } /** * @param userName the userName to set */ public void setUserName(String userName) { this.userName = userName; } /** * @return the passWord */ public String getPassWord() { return passWord; } /** * @param passWord the passWord to set */ public void setPassWord(String passWord) { this.passWord = passWord; } }