package org.josso.tooling.gshell.install.installer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileType; import org.josso.tooling.gshell.install.JOSSOArtifact; import org.josso.tooling.gshell.install.TargetPlatform; import org.josso.tooling.gshell.install.util.XUpdateUtil; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xmldb.common.xml.queries.XUpdateQuery; import org.xmldb.xupdate.lexus.XUpdateQueryImpl; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * @version $Id$ * @org.apache.xbean.XBean element="alfresco-installer" */ public class AlfrescoInstaller extends VFSInstaller { private static final Log log = LogFactory.getLog(AlfrescoInstaller.class); public AlfrescoInstaller(TargetPlatform targetPlatform) { super(targetPlatform); } public AlfrescoInstaller() { super(); } @Override public void validatePlatform() throws InstallException { try { boolean valid = true; if (targetConfDir.exists() && !targetConfDir.getType().getName().equals(FileType.FOLDER.getName()) && targetLibDir.exists() && !targetLibDir.getType().getName().equals(FileType.FOLDER.getName())) { valid = false; getPrinter().printErrStatus("AlfrescoHome", "Cannot find Alfresco webapp root."); } if (!valid) throw new InstallException("Target does not seem a " + getTargetPlatform().getDescription() + " install."); } catch (IOException e) { getPrinter().printErrStatus("Alfresco root", e.getMessage()); throw new InstallException(e.getMessage(), e); } getPrinter().printOkStatus("Alfresco root"); } @Override public void installComponent(JOSSOArtifact artifact, boolean replace) throws InstallException { try { FileObject srcFile = getFileSystemManager().resolveFile(artifact.getLocation()); // Install only the proper artifact for the target platform ... if (artifact.getBaseName().startsWith("josso-alfresco-agent")) { installFile(srcFile, this.targetLibDir, replace); } else if (artifact.getBaseName().startsWith("josso-servlet-agent")) { installFile(srcFile, this.targetLibDir, replace); } else if (artifact.getBaseName().startsWith("josso-agent-shared")) { installFile(srcFile, this.targetLibDir, replace); } else if (artifact.getBaseName().startsWith("josso-agents-bin") && artifact.getClassifier() != null && artifact.getClassifier().equals("axis")) { installFile(srcFile, this.targetJOSSOLibDir, replace); } else { log.debug("Artifact is not valid for selected platform : " + artifact); } } catch (IOException e) { throw new InstallException(e.getMessage(), e); } } public void install3rdPartyComponent(JOSSOArtifact artifact, boolean replace) throws InstallException { try { FileObject srcFile = getFileSystemManager().resolveFile(artifact.getLocation()); // Install newer (josso) version of xbean-spring if (artifact.getBaseName().startsWith("xbean-spring")) { removeOldJar(srcFile.getName().getBaseName(), this.targetLibDir, true); installFile(srcFile, this.targetLibDir, replace); } } catch (IOException e) { throw new InstallException(e.getMessage(), e); } } @Override public boolean backupAgentConfigurations(boolean remove) { String tcInstallDir = getProperty("tomcatInstallDir"); //String jbInstallDir = getProperty("jbossInstallDir"); if (tcInstallDir != null) { log.debug("[configureJaasModule]: Tomcat install dir: " + tcInstallDir); // backup jaas.conf try { FileObject tomcatInstallDir = getFileSystemManager().resolveFile(tcInstallDir); FileObject tcBinDir = tomcatInstallDir.resolveFile("bin/"); FileObject jaasConfigFile = tomcatInstallDir.resolveFile("conf/jaas.conf"); if (jaasConfigFile.exists()) { // backup file in the same folder it is installed backupFile(jaasConfigFile, jaasConfigFile.getParent()); if (remove) { jaasConfigFile.delete(); } } // backup setenv.sh and setenv.bat FileObject[] libs = tcBinDir.getChildren(); for (int i = 0; i < libs.length; i++) { FileObject cfgFile = libs[i]; if (!cfgFile.getType().getName().equals(FileType.FILE.getName())) { // ignore folders continue; } if (cfgFile.getName().getBaseName().startsWith("setenv") && (cfgFile.getName().getBaseName().endsWith(".sh") || cfgFile.getName().getBaseName().endsWith(".bat"))) { // backup files in the same folder they're installed in backupFile(cfgFile, cfgFile.getParent()); if (remove) { cfgFile.delete(); } } } } catch (FileSystemException e) { getPrinter().printActionErrStatus("Configure", "JOSSO SSO Filter", "Tomcat install directory is wrong."); } } return true; } @Override public void installConfiguration(JOSSOArtifact artifact, boolean replace) throws InstallException { String tcInstallDir = getProperty("tomcatInstallDir"); //String jbInstallDir = getProperty("jbossInstallDir"); log.debug("[configureJaasModule]: Tomcat install dir: " + tcInstallDir); try { FileObject srcFile = getFileSystemManager().resolveFile(artifact.getLocation()); String name = srcFile.getName().getBaseName(); if (tcInstallDir != null) { FileObject tomcatInstallDir = getFileSystemManager().resolveFile(tcInstallDir); FileObject tcBinDir = tomcatInstallDir.resolveFile("bin/"); FileObject jaasConfigDir = tomcatInstallDir.resolveFile("conf/"); if (name.startsWith("setenv")) { installFile(srcFile, tcBinDir, replace); } else if (name.equals("jaas.conf")) { installFile(srcFile, jaasConfigDir, replace); } } if (name.startsWith("josso")) { installFile(srcFile, this.targetConfDir, replace); } } catch (FileSystemException e) { getPrinter().printActionErrStatus("Configure", "JOSSO SSO Filter", "Tomcat install directory is wrong."); } catch (IOException e) { throw new InstallException(e.getMessage(), e); } } @Override public boolean removeOldComponents(boolean backup) { return true; } @Override public boolean updateAgentConfiguration(String idpHostName, String idpPort, String idpType) { return false; } @Override public void configureAgent() throws InstallException { // Setup XUpdate : System.setProperty("org.xmldb.common.xml.queries.XPathQueryFactory", "org.xmldb.common.xml.queries.xalan2.XPathQueryFactoryImpl"); // For now, only web.xml to configure: configureWebXml(); } protected void configureWebXml() throws InstallException { // -------------------------------------------------------------------- // Configure web.xml // -------------------------------------------------------------------- FileObject webXml = null; try { webXml = targetDir.resolveFile("WEB-INF/web.xml"); // Get a DOM document of the web.xml : Node webXmlNode = loadAsDom(webXml); boolean modified = false; // Perform specific configurations if (configureFilters(webXmlNode)) modified = true; if (modified) { // Backup Container configuration. If we cannot perform a backup, do nothing if (!backupFile(webXml, targetDir)) { getPrinter().printActionWarnStatus("Configure", targetDir.getName().getFriendlyURI() + "/WEB-INF/web.xml", "Must be done manually (Follow setup guide)"); return; } // Write modifications to file writeContentFromDom(webXmlNode, webXml); getPrinter().printActionOkStatus("Save", webXml.getName().getBaseName(), webXml.getName().getFriendlyURI()); } } catch (IOException e) { log.error(e.getMessage(), e); getPrinter().printErrStatus("Cannot configure container : ", e.getMessage()); } catch (SAXException e) { log.error(e.getMessage(), e); getPrinter().printErrStatus("Cannot configure container : ", e.getMessage()); } catch (Exception e) { log.error(e.getMessage(), e); getPrinter().printErrStatus("Cannot configure container : ", e.getMessage()); getPrinter().printActionWarnStatus("Configure", targetDir.getName().getFriendlyURI() + "/WEB-INF/web.xml", "Must be done manually (Follow setup guide)"); } } protected boolean configureFilters(Node xmlDom) throws Exception { XPath xpath = XPathFactory.newInstance().newXPath(); NodeList filtersNodes = (NodeList) xpath.evaluate("/web-app/filter", xmlDom, XPathConstants.NODESET); // Check if josso is already installed XPathExpression jossoGenericFilterClassExp = xpath.compile("/web-app/filter[filter-class='org.josso.servlet.agent.GenericServletSSOAgentFilter']"); Node jossoGenericFilterNode = (Node) jossoGenericFilterClassExp.evaluate(xmlDom, XPathConstants.NODE); XPathExpression jossoAlfrescoFilterClassExp = xpath.compile("/web-app/filter[filter-class='org.josso.alfresco.agent.AlfrescoSSOAgentFilter']"); Node jossoAlfrescoFilterNode = (Node) jossoAlfrescoFilterClassExp.evaluate(xmlDom, XPathConstants.NODE); if (jossoGenericFilterNode != null || jossoAlfrescoFilterNode != null) { getPrinter().printActionWarnStatus("Configure", "JOSSO SSO Filter", "Already configured : " + (jossoGenericFilterNode != null ? jossoGenericFilterNode.getNodeValue() : "<unknown>") + (jossoAlfrescoFilterNode != null ? jossoAlfrescoFilterNode.getNodeValue() : "<unknown>")); return false; } // Find Authentication Filter node in web.xml // Append josso filters after Authentication Filter in web.xml if (filtersNodes != null && filtersNodes.getLength() > 0) { String xupdJossoFilter = "<xupdate:insert-before select=\"/web-app/filter[filter-name='Authentication Filter']\" >\n" + "<xupdate:element name=\"filter\"> \n" + "\t<xupdate:element name=\"filter-name\">Josso Alfresco Filter</xupdate:element>\n" + "\t<xupdate:element name=\"filter-class\">org.josso.alfresco.agent.AlfrescoSSOAgentFilter</xupdate:element>\n" + "</xupdate:element>\n" + "<xupdate:element name=\"filter\"> \n" + "\t<xupdate:element name=\"filter-name\">SSO Josso Filter</xupdate:element>\n" + "\t<xupdate:element name=\"filter-class\">org.josso.servlet.agent.GenericServletSSOAgentFilter</xupdate:element>\n" + "</xupdate:element>\n" + "</xupdate:insert-before>"; //We are using alf_temp_one and alf_temp_two nodes as variables for Josso filter mappings, which is removed at the end of processing String xupdAlfTempNodes = "<xupdate:variable name=\"GlobalAuthenticationMappings\" select=\"/web-app/filter-mapping[filter-name='Global Authentication Filter']\"/>\n" + "<xupdate:append select=\"/web-app\" >\n" + "\t<xupdate:element name=\"alf_temp_one\"><xupdate:value-of select=\"$GlobalAuthenticationMappings\"/></xupdate:element>\n" + "</xupdate:append>\n\n" + "<xupdate:append select=\"/web-app\" >\n" + "\t<xupdate:element name=\"alf_temp_two\"><xupdate:value-of select=\"$GlobalAuthenticationMappings\"/></xupdate:element>\n" + "</xupdate:append>"; //Substitute all Global Authentication Filter mappings to Josso Filters String xupdJossoFilterMappings = "\n<xupdate:update select=\"/web-app/alf_temp_one/filter-mapping[filter-name='Global Authentication Filter']/filter-name\">SSO Josso Filter</xupdate:update>" + "\n<xupdate:update select=\"/web-app/alf_temp_two/filter-mapping[filter-name='Global Authentication Filter']/filter-name\">Josso Alfresco Filter</xupdate:update>"; //Insert more SSO Josso Filter mappings String xupdJossoFilterMappingsSpecific = "<xupdate:insert-before select=\"/web-app/alf_temp_one/filter-mapping[1]\" >\n" + filterMappingElement("SSO Josso Filter", "/josso_login/") + filterMappingElement("SSO Josso Filter", "/josso_logout/") + filterMappingElement("SSO Josso Filter", "/josso_security_check") + "</xupdate:insert-before>"; String qry = XUpdateUtil.XUPDATE_START + xupdJossoFilter + "\n\n" + xupdAlfTempNodes + "\n\n" + xupdJossoFilterMappings + "\n\n" + xupdJossoFilterMappingsSpecific + "\n\n" + XUpdateUtil.XUPDATE_END; XUpdateQuery xq = new XUpdateQueryImpl(); log.debug("XUPDATE QUERY [PART 1]: \n" + qry); xq.setQString(qry); xq.execute(xmlDom); //Copy Josso Filter Mappings from variable nodes, and remove all Global Filter mappings and remove variables String xupdAlfrescoFilterMappings = "<xupdate:insert-before select=\"/web-app/filter-mapping[1]\" >\n" + "\n\t<xupdate:value-of select=\"/web-app/alf_temp_two/node()\"/>\n" + "</xupdate:insert-before>\n" + "\n<xupdate:insert-before select=\"/web-app/filter-mapping[1]\" >\n" + "\n\t<xupdate:value-of select=\"/web-app/alf_temp_one/node()\"/>\n" + "</xupdate:insert-before>\n\n" + "<xupdate:remove select=\"/web-app/filter-mapping[filter-name='Global Authentication Filter']\"/>\n" + "<xupdate:remove select=\"/web-app/alf_temp_one\"/>\n" + "<xupdate:remove select=\"/web-app/alf_temp_two\"/>"; qry = XUpdateUtil.XUPDATE_START + "\n" + xupdAlfrescoFilterMappings + "\n" + XUpdateUtil.XUPDATE_END; log.debug("XUPDATE QUERY [PART 2]: \n" + qry); xq.setQString(qry); xq.execute(xmlDom); getPrinter().printActionOkStatus("Added josso filter into web.xml", "JOSSO Alfresco Agent ", "WEB-INF/web.xml"); return true; } return false; } private Document loadAsDom(FileObject inFile) throws Exception { InputStream is = null; try { is = inFile.getContent().getInputStream(); DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance(); parserFactory.setValidating(false); parserFactory.setNamespaceAware(false); // this is the only diference from readContentAsDom parserFactory.setIgnoringElementContentWhitespace(false); parserFactory.setIgnoringComments(false); DocumentBuilder builder = parserFactory.newDocumentBuilder(); boolean dtdNotFound = false; Document doc = null; try { doc = builder.parse(is); } catch (FileNotFoundException e) { dtdNotFound = true; } // if dtd doesn't exist parse the document again without trying to load dtd if (dtdNotFound) { is = inFile.getContent().getInputStream(); // disable dtd loading parserFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); builder = parserFactory.newDocumentBuilder(); doc = builder.parse(is); } DocumentType docType = doc.getDoctype(); return doc; } finally { if (is != null) try { is.close(); } catch (IOException e) { /**/} } } private String filterMappingElement(String filterName, String url) { return "<xupdate:element name=\"filter-mapping\">\n" + "\t<filter-name>" + filterName + "</filter-name>\n" + "\t<url-pattern>" + url + "</url-pattern>\n" + "</xupdate:element>\n"; } }