/* * (C) Copyright 2010-2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed 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. * * Contributors: * Thierry Delprat * Julien Carsique * */ package org.nuxeo.ecm.admin.setup; import static org.nuxeo.common.Environment.NUXEO_DATA_DIR; import static org.nuxeo.common.Environment.NUXEO_LOG_DIR; import static org.nuxeo.common.Environment.PRODUCT_NAME; import static org.nuxeo.common.Environment.PRODUCT_VERSION; import static org.nuxeo.launcher.config.ConfigurationGenerator.NUXEO_CONF; import static org.nuxeo.launcher.config.ConfigurationGenerator.NUXEO_DEV_SYSTEM_PROP; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_BIND_ADDRESS; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_HOST; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_NAME; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_PORT; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_PWD; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_USER; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_NUXEO_URL; import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_TEMPLATE_DBNAME; import static org.nuxeo.launcher.config.ConfigurationGenerator.SECRET_KEYS; import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; import java.sql.SQLException; import java.util.*; import java.util.Map.Entry; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.ValueHolder; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.AjaxBehaviorEvent; import javax.faces.validator.ValidatorException; import javax.naming.AuthenticationException; import javax.naming.NamingException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Factory; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.contexts.Contexts; import org.jboss.seam.faces.FacesMessages; import org.jboss.seam.international.StatusMessage; import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils; import org.nuxeo.launcher.commons.DatabaseDriverException; import org.nuxeo.launcher.config.ConfigurationException; import org.nuxeo.launcher.config.ConfigurationGenerator; /** * Serves UI for the setup screen, handling properties that can be saved on the bin/nuxeo.conf file on server. * <p> * Manages some important parameters to perform validation on them, and accepts custom parameters that would be present * in the server nuxeo.conf file, and moves to advanced mode any property that would not be in that list. * * @since 5.5 */ @Scope(ScopeType.SESSION) @Name("setupWizardAction") public class SetupWizardActionBean implements Serializable { private static final long serialVersionUID = 1L; protected static final Log log = LogFactory.getLog(SetupWizardActionBean.class); /** * The list of important parameters that need to be presented first to the user */ private static final String[] managedKeyParameters = { PARAM_BIND_ADDRESS, PARAM_NUXEO_URL, NUXEO_DATA_DIR, NUXEO_LOG_DIR, PRODUCT_NAME, PRODUCT_VERSION, NUXEO_CONF, PARAM_TEMPLATE_DBNAME, PARAM_DB_NAME, PARAM_DB_USER, PARAM_DB_PWD, PARAM_DB_HOST, PARAM_DB_PORT, "nuxeo.db.min-pool-size", "nuxeo.db.min-pool-size", "nuxeo.db.max-pool-size", "nuxeo.vcs.min-pool-size", "nuxeo.vcs.max-pool-size", "nuxeo.notification.eMailSubjectPrefix", "mailservice.user", "mailservice.password", "mail.store.protocol", "mail.transport.protocol", "mail.store.host", "mail.store.port", "mail.store.user", "mail.store.password", "mail.debug", "mail.transport.host", "mail.transport.port", "mail.transport.auth", "mail.transport.user", "mail.transport.password", "mail.from", "mail.user", "mail.transport.usetls", "nuxeo.http.proxy.host", "nuxeo.http.proxy.port", "nuxeo.http.proxy.login", "nuxeo.http.proxy.password", NUXEO_DEV_SYSTEM_PROP, "nuxeo.directory.type", "nuxeo.user.group.storage", "nuxeo.ldap.url", "nuxeo.ldap.binddn", "nuxeo.ldap.bindpassword", "nuxeo.ldap.retries", "nuxeo.ldap.user.searchBaseDn", "nuxeo.ldap.user.searchClass", "nuxeo.ldap.user.searchFilter", "nuxeo.ldap.user.searchScope", "nuxeo.ldap.user.readonly", "nuxeo.ldap.user.mapping.rdn", "nuxeo.ldap.user.mapping.username", "nuxeo.ldap.user.mapping.password", "nuxeo.ldap.user.mapping.firstname", "nuxeo.ldap.user.mapping.lastname", "nuxeo.ldap.user.mapping.email", "nuxeo.ldap.user.mapping.company", "nuxeo.ldap.group.searchBaseDn", "nuxeo.ldap.group.searchFilter", "nuxeo.ldap.group.searchScope", "nuxeo.ldap.group.readonly", "nuxeo.ldap.group.mapping.rdn", "nuxeo.ldap.group.mapping.name", "nuxeo.ldap.group.mapping.label", "nuxeo.ldap.group.mapping.members.staticAttributeId", "nuxeo.ldap.group.mapping.members.dynamicAttributeId", "nuxeo.ldap.defaultAdministratorId", "nuxeo.ldap.defaultMembersGroup", "nuxeo.user.anonymous.enable", "nuxeo.user.emergency.enable", "nuxeo.user.emergency.username", "nuxeo.user.emergency.password", "nuxeo.user.emergency.firstname", "nuxeo.user.emergency.lastname" }; protected Map<String, String> parameters; protected Map<String, String> advancedParameters; protected static final String PROXY_NONE = "none"; protected static final String PROXY_ANONYMOUS = "anonymous"; protected static final String PROXY_AUTHENTICATED = "authenticated"; protected static final String DIRECTORY_DEFAULT = "default"; protected static final String DIRECTORY_LDAP = "ldap"; protected static final String DIRECTORY_MULTI = "multi"; private static final String ERROR_DB_DRIVER = "error.db.driver.notfound"; private static final String ERROR_DB_CONNECTION = "error.db.connection"; private static final String ERROR_LDAP_CONNECTION = "error.ldap.connection"; private static final String ERROR_LDAP_AUTHENTICATION = "error.ldap.authentication"; private static final String ERROR_DB_FS = "error.db.fs"; protected String proxyType = PROXY_NONE; protected String directoryType = DIRECTORY_DEFAULT; protected boolean needsRestart = false; @In(create = true) private transient ConfigurationGenerator setupConfigGenerator; protected Properties userConfig; @In(create = true, required = false) protected FacesMessages facesMessages; @In(create = true) protected Map<String, String> messages; private Boolean needGroupConfiguration; @Factory(value = "setupRequiresRestart", scope = ScopeType.EVENT) public boolean isNeedsRestart() { return needsRestart; } public void setNeedsRestart(boolean needsRestart) { this.needsRestart = needsRestart; } @Factory(value = "setupConfigGenerator", scope = ScopeType.PAGE) public ConfigurationGenerator getConfigurationGenerator() { if (setupConfigGenerator == null) { setupConfigGenerator = new ConfigurationGenerator(); if (setupConfigGenerator.init()) { setParameters(); } } return setupConfigGenerator; } @Factory(value = "setupConfigurable", scope = ScopeType.APPLICATION) public boolean isConfigurable() { return setupConfigGenerator.isConfigurable(); } @Factory(value = "advancedParams", scope = ScopeType.EVENT) public Map<String, String> getAdvancedParameters() { return advancedParameters; } @Factory(value = "setupParams", scope = ScopeType.EVENT) public Map<String, String> getParameters() { return parameters; } /** * Fill {@link #parameters} and {@link #advancedParameters} with properties from # * {@link ConfigurationGenerator#getUserConfig()} * * @since 5.6 */ protected void setParameters() { userConfig = setupConfigGenerator.getUserConfig(); parameters = new HashMap<>(); advancedParameters = new TreeMap<>(); // will remove managed parameters later in setParameter() for (String key : userConfig.stringPropertyNames()) { if (System.getProperty(key) == null || key.matches("^(nuxeo|org\\.nuxeo|catalina|derby|h2|java\\.home|" + "java\\.io\\.tmpdir|tomcat|sun\\.rmi\\.dgc).*")) { advancedParameters.put(key, userConfig.getProperty(key).trim()); } } for (String keyParam : managedKeyParameters) { String parameter = userConfig.getProperty(keyParam); setParameter(keyParam, parameter); } proxyType = PROXY_NONE; if (parameters.get("nuxeo.http.proxy.host") != null) { proxyType = PROXY_ANONYMOUS; if (parameters.get("nuxeo.http.proxy.login") != null) { proxyType = PROXY_AUTHENTICATED; } } if (parameters.get("nuxeo.directory.type") != null) { directoryType = parameters.get("nuxeo.directory.type"); } } /** * Adds parameter value to the * * @param key parameter key such as used in templates and nuxeo.conf */ private void setParameter(String key, String value) { if (value != null) { parameters.put(key, value.trim()); advancedParameters.remove(key); } } public void save() { saveParameters(); setNeedsRestart(true); resetParameters(); // initialize setupConfigurator again, as it's in scope page getConfigurationGenerator(); facesMessages.add(StatusMessage.Severity.INFO, messages.get("label.parameters.saved")); } @SuppressWarnings("unchecked") protected void saveParameters() { // Fix types (replace Long, BigDecimal and Boolean with String) Iterator it = parameters.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); Object value = entry.getValue(); if (value instanceof Long || value instanceof Boolean || value instanceof BigDecimal) { entry.setValue(value.toString()); } } // manage httpProxy settings (setting null is not accepted) if (!PROXY_AUTHENTICATED.equals(proxyType)) { parameters.put("nuxeo.http.proxy.login", ""); parameters.put("nuxeo.http.proxy.password", ""); } if (PROXY_NONE.equals(proxyType)) { parameters.put("nuxeo.http.proxy.host", ""); parameters.put("nuxeo.http.proxy.port", ""); } // Remove empty values for password keys for (String pwdKey : SECRET_KEYS) { if (StringUtils.isEmpty(parameters.get(pwdKey))) { parameters.remove(pwdKey); } } Map<String, String> customParameters = new HashMap<>(); customParameters.putAll(parameters); customParameters.putAll(advancedParameters); try { setupConfigGenerator.saveFilteredConfiguration(customParameters); } catch (ConfigurationException e) { log.error(e, e); } } public void resetParameters() { setupConfigGenerator = null; parameters = null; advancedParameters = null; Contexts.getPageContext().remove("setupConfigGenerator"); } /** * @since 5.6 */ public void checkDatabaseParameters(FacesContext context, UIComponent component, Object value) { Map<String, Object> attributes = component.getAttributes(); String dbNameInputId = (String) attributes.get("dbNameInputId"); String dbUserInputId = (String) attributes.get("dbUserInputId"); String dbPwdInputId = (String) attributes.get("dbPwdInputId"); String dbHostInputId = (String) attributes.get("dbHostInputId"); String dbPortInputId = (String) attributes.get("dbPortInputId"); if (dbNameInputId == null || dbUserInputId == null || dbPwdInputId == null || dbHostInputId == null || dbPortInputId == null) { log.error("Cannot validate database parameters: missing inputIds"); return; } UIInput dbNameComp = (UIInput) component.findComponent(dbNameInputId); UIInput dbUserComp = (UIInput) component.findComponent(dbUserInputId); UIInput dbPwdComp = (UIInput) component.findComponent(dbPwdInputId); UIInput dbHostComp = (UIInput) component.findComponent(dbHostInputId); UIInput dbPortComp = (UIInput) component.findComponent(dbPortInputId); if (dbNameComp == null || dbUserComp == null || dbPwdComp == null || dbHostComp == null || dbPortComp == null) { log.error("Cannot validate inputs: not found"); return; } String dbName = (String) dbNameComp.getLocalValue(); String dbUser = (String) dbUserComp.getLocalValue(); String dbPwd = (String) dbPwdComp.getLocalValue(); String dbHost = (String) dbHostComp.getLocalValue(); Long dbPortLong = ((BigDecimal) dbPortComp.getLocalValue()).longValue(); String dbPort = dbPortLong.toString(); if (StringUtils.isEmpty(dbPwd)) { dbPwd = parameters.get("nuxeo.db.password"); } String errorLabel = null; Exception error = null; try { setupConfigGenerator.checkDatabaseConnection(parameters.get(ConfigurationGenerator.PARAM_TEMPLATE_DBNAME), dbName, dbUser, dbPwd, dbHost, dbPort); } catch (IOException e) { errorLabel = ERROR_DB_FS; error = e; } catch (DatabaseDriverException e) { errorLabel = ERROR_DB_DRIVER; error = e; } catch (SQLException e) { errorLabel = ERROR_DB_CONNECTION; error = e; } if (error != null) { log.error(error, error); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, errorLabel), null); throw new ValidatorException(message); } FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, ComponentUtils.translate(context, "error.db.none"), null); message.setSeverity(FacesMessage.SEVERITY_INFO); context.addMessage(component.getClientId(context), message); } public void templateChange(AjaxBehaviorEvent event) { String dbTemplate; UIComponent select = event.getComponent(); if (select instanceof ValueHolder) { dbTemplate = (String) ((ValueHolder) select).getValue(); } else { log.error("Bad component returned " + select); throw new AbortProcessingException("Bad component returned " + select); } setupConfigGenerator.changeDBTemplate(dbTemplate); setParameters(); Contexts.getEventContext().remove("setupParams"); Contexts.getEventContext().remove("advancedParams"); FacesContext context = FacesContext.getCurrentInstance(); context.renderResponse(); } public void proxyChange(AjaxBehaviorEvent event) { UIComponent select = event.getComponent(); if (select instanceof ValueHolder) { proxyType = (String) ((ValueHolder) select).getValue(); } else { log.error("Bad component returned " + select); throw new AbortProcessingException("Bad component returned " + select); } Contexts.getEventContext().remove("setupParams"); FacesContext context = FacesContext.getCurrentInstance(); context.renderResponse(); } /** * Initialized by {@link #getParameters()} */ public String getProxyType() { return proxyType; } public void setProxyType(String proxyType) { this.proxyType = proxyType; } public String getDirectoryType() { return directoryType; } public void setDirectoryType(String directoryType) { parameters.put("nuxeo.directory.type", directoryType); this.directoryType = directoryType; } public void setDirectoryStorage(String directoryStorage) { parameters.put("nuxeo.user.group.storage", directoryStorage); } public void ldapStorageChange() { needGroupConfiguration = null; } public boolean getNeedGroupConfiguration() { if (needGroupConfiguration == null) { String storageType = parameters.get("nuxeo.user.group.storage"); if ("userLdapOnly".equals(storageType) || "multiUserSqlGroup".equals(storageType)) { needGroupConfiguration = Boolean.FALSE; } else { needGroupConfiguration = Boolean.TRUE; } } return needGroupConfiguration; } public void directoryChange(AjaxBehaviorEvent event) { UIComponent select = event.getComponent(); if (select instanceof ValueHolder) { directoryType = (String) ((ValueHolder) select).getValue(); } else { log.error("Bad component returned " + select); throw new AbortProcessingException("Bad component returned " + select); } if ("multi".equals(directoryType)) { setDirectoryStorage("multiUserGroup"); } else { setDirectoryStorage("default"); } needGroupConfiguration = null; Contexts.getEventContext().remove("setupParams"); FacesContext context = FacesContext.getCurrentInstance(); context.renderResponse(); } public void checkLdapNetworkParameters(FacesContext context, UIComponent component, Object value) { Map<String, Object> attributes = component.getAttributes(); String ldapUrlId = (String) attributes.get("directoryLdapUrl"); if (ldapUrlId == null) { log.error("Cannot validate LDAP parameters: missing inputIds"); return; } UIInput ldapUrlComp = (UIInput) component.findComponent(ldapUrlId); if (ldapUrlComp == null) { log.error("Cannot validate LDAP inputs: not found"); return; } String ldapUrl = (String) ldapUrlComp.getLocalValue(); String errorLabel = null; Exception error = null; try { setupConfigGenerator.checkLdapConnection(ldapUrl, null, null, false); } catch (NamingException e) { errorLabel = ERROR_LDAP_CONNECTION; error = e; } if (error != null) { log.error(error, error); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, errorLabel), null); throw new ValidatorException(message); } FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, ComponentUtils.translate(context, "error.ldap.network.none"), null); message.setSeverity(FacesMessage.SEVERITY_INFO); context.addMessage(component.getClientId(context), message); } public void checkLdapAuthenticationParameters(FacesContext context, UIComponent component, Object value) { Map<String, Object> attributes = component.getAttributes(); String ldapUrlId = (String) attributes.get("ldapUrl"); String ldapBinddnId = (String) attributes.get("ldapBindDn"); String ldapBindpwdId = (String) attributes.get("ldapBindPwd"); if (ldapUrlId == null || ldapBinddnId == null || ldapBindpwdId == null) { log.error("Cannot validate LDAP parameters: missing inputIds"); return; } UIInput ldapUrlComp = (UIInput) component.findComponent(ldapUrlId); UIInput ldapBinddnComp = (UIInput) component.findComponent(ldapBinddnId); UIInput ldapBindpwdComp = (UIInput) component.findComponent(ldapBindpwdId); if (ldapUrlComp == null || ldapBinddnComp == null || ldapBindpwdComp == null) { log.error("Cannot validate LDAP inputs: not found"); return; } String ldapUrl = (String) ldapUrlComp.getLocalValue(); String ldapBindDn = (String) ldapBinddnComp.getLocalValue(); String ldapBindPwd = (String) ldapBindpwdComp.getLocalValue(); String errorLabel = null; Exception error = null; try { setupConfigGenerator.checkLdapConnection(ldapUrl, ldapBindDn, ldapBindPwd, true); } catch (NamingException e) { if (e instanceof AuthenticationException) { errorLabel = ERROR_LDAP_AUTHENTICATION; } else { errorLabel = ERROR_LDAP_CONNECTION; } error = e; } if (error != null) { log.error(error, error); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, ComponentUtils.translate(context, errorLabel), null); throw new ValidatorException(message); } FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, ComponentUtils.translate(context, "error.ldap.auth.none"), null); message.setSeverity(FacesMessage.SEVERITY_INFO); context.addMessage(component.getClientId(context), message); } }