/*
* 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;
}
}