Quellcode durchsuchen

o Move the web.xml converter code, tests and xsl files from core into the acegifier sample app.
o Switched to dom4j for more flexible xml handling and easier formatting of the XML output.
o Modified the test web.xml to match the contacts-filter app to allow easy testing in an acegi application.

Luke Taylor vor 20 Jahren
Ursprung
Commit
a95964461d

+ 116 - 0
samples/acegifier/src/java/acegifier/WebXmlConverter.java

@@ -0,0 +1,116 @@
+package acegifier;
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.util.Assert;
+import org.dom4j.Document;
+import org.dom4j.DocumentHelper;
+import org.dom4j.DocumentException;
+import org.dom4j.io.SAXReader;
+import org.dom4j.io.DocumentSource;
+import org.dom4j.io.DocumentResult;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A utility to translate a web.xml file into a set of acegi security spring beans.
+ *
+ * Also produces a new "acegified" web.xml file with the necessary filters installed
+ * and the security elements defined by the servlet DTD removed.
+ *
+ * <p>
+ * This class wraps the XSL transform which actually does most of the work.
+ * </p>
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class WebXmlConverter {
+    private static final String WEB_TO_SPRING_XSL_FILE = "web-to-spring.xsl";
+    private static final String NEW_WEB_XSLT_FILE = "acegi-web.xsl";
+
+    private Transformer acegiSecurityTransformer, newWebXmlTransformer;
+
+    /**
+     * The name of the spring-beans file which the beans will be stored in.
+     * This is required when writing the new web.xml content.
+     */
+    private String acegiOutputFileName = "applicationContext-acegi-security.xml";
+
+    /** The web.xml content to be converted */
+    private Source xmlSource;
+    /** The results of the conversion */
+    private Document newWebXml, acegiBeansXml;
+
+    public WebXmlConverter() throws Exception {
+        TransformerFactory tf = TransformerFactory.newInstance();
+
+        acegiSecurityTransformer = tf.newTransformer(createTransformerSource(WEB_TO_SPRING_XSL_FILE));
+        newWebXmlTransformer = tf.newTransformer(createTransformerSource(NEW_WEB_XSLT_FILE));
+    }
+
+    private Source createTransformerSource(String fileName) throws IOException {
+        ClassPathResource resource = new ClassPathResource(fileName);
+        return new StreamSource(resource.getInputStream());
+    }
+
+    /**
+     * Performs the transformations on the input source.
+     * Creates new web.xml content and a set of acegi-security Spring beans which can be
+     * accessed through the appropriate getter methods.
+     */
+    public void doConversion() throws IOException, TransformerException {
+        Assert.notNull(xmlSource, "The XML input must be set");
+
+        // Create the modified web.xml file
+        newWebXmlTransformer.setParameter("acegi-security-context-file", acegiOutputFileName);
+//        newWebXmlTransformer.setParameter("cas-proxy-url", "http://localhost:8433/cas/proxy");
+        DocumentResult result = new DocumentResult();
+        newWebXmlTransformer.transform(xmlSource, result);
+        newWebXml = result.getDocument();
+
+        result = new DocumentResult();
+        acegiSecurityTransformer.transform(xmlSource, result);
+        acegiBeansXml = result.getDocument();
+    }
+
+    /** Set the input as an xml string */
+    public void setInput(String xml) throws DocumentException {
+        Document document = DocumentHelper.parseText(xml);
+        xmlSource = new DocumentSource(document);
+    }
+
+    /** set the input as an InputStream */
+    public void setInput(InputStream xmlIn) throws Exception {
+        SAXReader reader = new SAXReader();
+        Document document = reader.read(xmlIn);
+        xmlSource = new DocumentSource(document);
+    }
+
+    public String getAcegiOutputFileName() {
+        return acegiOutputFileName;
+    }
+
+    public void setAcegiOutputFileName(String acegiOutputFileName) {
+        this.acegiOutputFileName = acegiOutputFileName;
+    }
+
+    /** Returns the converted web.xml content */
+    public Document getNewWebXml() {
+        return newWebXml;
+    }
+
+    /**
+     * Returns the created spring-beans xml content which should be used in
+     * the application context file.
+     */
+    public Document getAcegiBeans() {
+        return acegiBeansXml;
+    }
+
+}

+ 50 - 21
samples/acegifier/src/java/acegifier/web/AcegifierController.java

@@ -3,22 +3,26 @@ package acegifier.web;
 import org.springframework.web.servlet.mvc.SimpleFormController;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.validation.BindException;
+import org.springframework.validation.Errors;
 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 import org.springframework.beans.BeansException;
 import net.sf.acegisecurity.util.InMemoryResource;
-import org.w3c.dom.Document;
 import org.xml.sax.SAXParseException;
+import org.dom4j.Document;
+import org.dom4j.io.XMLWriter;
+import org.dom4j.io.OutputFormat;
 
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.DocumentBuilder;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
-import net.sf.acegisecurity.util.WebXmlToAcegiSecurityConverter;
+import net.sf.acegisecurity.util.FilterChainProxy;
+import acegifier.WebXmlConverter;
 
 /**
  * Takes a submitted web.xml, applies the transformer to it and returns the resulting
@@ -28,10 +32,8 @@ import net.sf.acegisecurity.util.WebXmlToAcegiSecurityConverter;
  * @version $Id$
  */
 public class AcegifierController extends SimpleFormController {
-    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 
     public AcegifierController() {
-        dbf.setValidating(false);
     }
 
     public ModelAndView onSubmit(
@@ -39,23 +41,20 @@ public class AcegifierController extends SimpleFormController {
                 throws Exception {
 
         AcegifierForm conversion = (AcegifierForm)command;
-
         ByteArrayInputStream in = new ByteArrayInputStream(conversion.getWebXml().getBytes());
-        DocumentBuilder db = dbf.newDocumentBuilder();
-        Document doc = null;
-        WebXmlToAcegiSecurityConverter converter = null;
+        WebXmlConverter converter = null;
         int nBeans = 0;
+        Document newWebXml = null, acegiBeans = null;
 
         try {
-            doc = db.parse(in);
-            converter = new WebXmlToAcegiSecurityConverter();
-            converter.setInput(doc);
+            converter = new WebXmlConverter();
+            converter.setInput(in);
             converter.doConversion();
-            nBeans = createBeanFactory(converter.getAcegiBeansXml());
+            newWebXml = converter.getNewWebXml();
+            acegiBeans = converter.getAcegiBeans();
+            nBeans = validateAcegiBeans(conversion, acegiBeans, errors);
         } catch (SAXParseException spe) {
             errors.rejectValue("webXml","parseFailure","Your Web XML Document failed to parse: " + spe.getMessage());
-        } catch (BeansException be) {
-            errors.rejectValue("webXml","invalidBeans","There was a problem validating the Spring beans: " + be.getMessage());
         }
 
         if(errors.hasErrors()) {
@@ -63,19 +62,49 @@ public class AcegifierController extends SimpleFormController {
         }
 
         Map model = new HashMap();
-        model.put("webXml", converter.getNewWebXml());
-        model.put("acegiBeansXml", converter.getAcegiBeansXml());
+        model.put("webXml", prettyPrint(newWebXml));
+        model.put("acegiBeansXml", prettyPrint(acegiBeans));
         model.put("nBeans", new Integer(nBeans));
 
         return new ModelAndView("acegificationResults", model);
     }
 
-    /** Creates a BeanFactory from the transformed XML to make sure the results are valid */
-    private int createBeanFactory(String beansXml) {
+    /** Creates a formatted XML string from the supplied document */
+    private String prettyPrint(Document document) throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        OutputFormat format = OutputFormat.createPrettyPrint();
+        format.setTrimText(false);
+        XMLWriter writer = new XMLWriter(output, format);
+        writer.write(document);
+        writer.flush();
+        writer.close();
+        return output.toString();
+    }
+
+    /**
+     * Validates the acegi beans, based on the input form data, and returns the number
+     * of spring beans defined in the document.
+     */
+    private int validateAcegiBeans(AcegifierForm conversion, Document beans, Errors errors) throws IOException {
+        DefaultListableBeanFactory bf = createBeanFactory(beans);
+
+        //TODO: actually do some proper validation!
+
+        try {
+            bf.getBean("filterChainProxy", FilterChainProxy.class);
+        } catch (BeansException be) {
+            errors.rejectValue("webXml","beansInvalid","There was an error creating or accessing the bean factory " + be.getMessage());
+        }
+        return bf.getBeanDefinitionCount();
+    }
+
+    /** Creates a BeanFactory from the spring beans XML document */
+    private DefaultListableBeanFactory createBeanFactory(Document beans) {
         DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
         XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(bf);
+        beanReader.loadBeanDefinitions(new InMemoryResource(beans.asXML().getBytes()));
 
-        return beanReader.loadBeanDefinitions(new InMemoryResource(beansXml));
+        return bf;
     }
 
 }

+ 82 - 0
samples/acegifier/src/test/acegifier/WebXmlConverterTests.java

@@ -0,0 +1,82 @@
+package acegifier;
+
+import junit.framework.TestCase;
+import net.sf.acegisecurity.UserDetails;
+import net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor;
+import net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter;
+import net.sf.acegisecurity.providers.ProviderManager;
+import net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider;
+import net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl;
+import net.sf.acegisecurity.util.InMemoryResource;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.dom4j.Document;
+import org.dom4j.io.OutputFormat;
+import org.dom4j.io.XMLWriter;
+
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Tests the WebXmlConverter by applying it to a sample web.xml file.
+ *
+ * @author Luke Taylor
+ * @version $Id$
+ */
+public class WebXmlConverterTests extends TestCase {
+
+    public void testFileConversion() throws Exception {
+        WebXmlConverter converter = new WebXmlConverter();
+
+        Resource r = new ClassPathResource("test-web.xml");
+        converter.setInput(r.getInputStream());
+        converter.doConversion();
+
+        DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
+        XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(bf);
+
+        beanReader.loadBeanDefinitions(
+                new InMemoryResource(converter.getAcegiBeans().asXML().getBytes()));
+        assertNotNull(bf.getBean("filterChainProxy"));
+
+        ProviderManager pm = (ProviderManager) bf.getBean("authenticationManager");
+        assertNotNull(pm);
+        assertEquals(3, pm.getProviders().size());
+
+        DaoAuthenticationProvider dap =
+                (DaoAuthenticationProvider) bf.getBean("daoAuthenticationProvider");
+        assertNotNull(dap);
+
+        InMemoryDaoImpl dao = (InMemoryDaoImpl) dap.getAuthenticationDao();
+        UserDetails user = dao.loadUserByUsername("superuser");
+        assertEquals("password",user.getPassword());
+        assertEquals(2, user.getAuthorities().length);
+        assertNotNull(bf.getBean("anonymousProcessingFilter"));
+        assertNotNull(bf.getBean("anonymousAuthenticationProvider"));
+        assertNotNull(bf.getBean("httpSessionContextIntegrationFilter"));
+        assertNotNull(bf.getBean("rememberMeProcessingFilter"));
+        assertNotNull(bf.getBean("rememberMeAuthenticationProvider"));
+
+        SecurityEnforcementFilter sef =
+                (SecurityEnforcementFilter) bf.getBean("securityEnforcementFilter");
+        assertNotNull(sef);
+        assertNotNull(sef.getAuthenticationEntryPoint());
+        FilterSecurityInterceptor fsi = sef.getFilterSecurityInterceptor();
+        System.out.println(prettyPrint(converter.getAcegiBeans()));
+
+    }
+
+    private String prettyPrint(Document document) throws IOException {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        OutputFormat format = OutputFormat.createPrettyPrint();
+        format.setNewlines(true);
+        format.setTrimText(false);
+        XMLWriter writer = new XMLWriter(output, format);
+        writer.write(document);
+        writer.flush();
+        writer.close();
+        return output.toString();
+    }
+}

+ 126 - 0
samples/acegifier/src/test/test-web.xml

@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+  <display-name>Contacts Sample Application</display-name>
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>
+        /WEB-INF/applicationContext-common-business.xml
+        /WEB-INF/applicationContext-common-authorization.xml
+    </param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>log4jConfigLocation</param-name>
+    <param-value>/WEB-INF/classes/log4j.properties</param-value>
+  </context-param>
+
+  <servlet>
+    <servlet-name>contacts</servlet-name>
+    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+    <load-on-startup>1</load-on-startup>
+  </servlet>
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+  <listener>
+    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
+  </listener>
+  <!--
+      The HttpSessionEventPublisher will publish
+      HttpSessionCreatedEvent and HttpSessionDestroyedEvent
+      to the WebApplicationContext
+  -->
+  <listener>
+    <listener-class>net.sf.acegisecurity.ui.session.HttpSessionEventPublisher</listener-class>
+  </listener>
+  <!--
+  - Provides web services endpoint. See remoting-servlet.xml.
+  -->
+  <servlet>
+    <servlet-name>remoting</servlet-name>
+    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+    <load-on-startup>2</load-on-startup>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>contacts</servlet-name>
+    <url-pattern>*.htm</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>remoting</servlet-name>
+    <url-pattern>/remoting/*</url-pattern>
+  </servlet-mapping>
+
+  <welcome-file-list>
+    <welcome-file>index.jsp</welcome-file>
+  </welcome-file-list>
+
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/index.jsp</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/hello.htm</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/logoff.jsp</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/acegilogin.jsp*</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/*</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>user</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <login-config>
+    <auth-method>form</auth-method>
+    <form-login-config>
+      <form-login-page>/acegilogin.jsp</form-login-page>
+      <form-error-page>/acegilogin.jsp?login_error=1</form-error-page>
+    </form-login-config>
+
+  </login-config>
+
+  <security-role>
+    <role-name>user</role-name>
+  </security-role>
+
+  <security-role>
+    <role-name>dummy</role-name>
+  </security-role>
+
+</web-app>

+ 4 - 0
samples/acegifier/src/webapp/WEB-INF/web.xml

@@ -19,6 +19,10 @@
 	</context-param>
 -->
 
+    <welcome-file-list>
+        <welcome-file>/convert.htm</welcome-file>
+    </welcome-file-list>    
+
 	<listener>
 		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 	</listener>