Move acegifier code from core.

This commit is contained in:
Luke Taylor 2005-07-16 19:35:30 +00:00
parent f0813b45d7
commit 74588c8e53
6 changed files with 3 additions and 634 deletions

View File

@ -21,12 +21,12 @@ public class InMemoryResource extends AbstractResource {
ByteArrayInputStream in; ByteArrayInputStream in;
String description; String description;
public InMemoryResource(String source) { public InMemoryResource(byte[] source) {
this(source, null); this(source, null);
} }
public InMemoryResource(String source, String description) { public InMemoryResource(byte[] source, String description) {
in = new ByteArrayInputStream(source.getBytes()); in = new ByteArrayInputStream(source);
this.description = description; this.description = description;
} }

View File

@ -1,115 +0,0 @@
package net.sf.acegisecurity.util;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayOutputStream;
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 WebXmlToAcegiSecurityConverter {
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;
private DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
/**
* 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 DOMSource xmlSource;
/** The results of the conversion */
private String newWebXml, acegiBeansXml;
public WebXmlToAcegiSecurityConverter() 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, either as a Node or an InputStream");
// Create the modified web.xml file
ByteArrayOutputStream output = new ByteArrayOutputStream();
newWebXmlTransformer.setParameter("acegi-security-context-file", acegiOutputFileName);
// newWebXmlTransformer.setParameter("cas-proxy-url", "http://localhost:8433/cas/proxy");
newWebXmlTransformer.transform(xmlSource, new StreamResult(output));
newWebXml = output.toString();
output.reset();
acegiSecurityTransformer.transform(xmlSource, new StreamResult(output));
acegiBeansXml = output.toString();
}
/** set the input as an InputStream */
public void setInput(InputStream xmlIn) throws Exception {
DocumentBuilder db = dbf.newDocumentBuilder();
setInput(db.parse(xmlIn));
}
/** set the input as an XML node */
public void setInput(Node webXml) {
this.xmlSource = new DOMSource(webXml);
}
public String getAcegiOutputFileName() {
return acegiOutputFileName;
}
public void setAcegiOutputFileName(String acegiOutputFileName) {
this.acegiOutputFileName = acegiOutputFileName;
}
/** Returns the converted web.xml content */
public String getNewWebXml() {
return newWebXml;
}
/**
* Returns the created spring-beans xml content which should be used in
* the application context file.
*/
public String getAcegiBeansXml() {
return acegiBeansXml;
}
}

View File

@ -1,109 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
| XSL Sheet used by the web.xml to acegi-security beans converter
| to create the new acegified web.xml.
| $Id$
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- The CAS proxy url (left empty if not to be used) -->
<xsl:param name="cas-proxy-url"/>
<!-- The acegi context file name - used in the -->
<xsl:param name="acegi-security-context-file" select="'applicationContext-acegi-security.xml'"/>
<xsl:output doctype-public="-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
doctype-system="http://java.sun.com/dtd/web-app_2_3.dtd"
indent="yes"/>
<!-- Identity template which we override for specific cases -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="web-app">
<web-app>
<xsl:apply-templates select="icon|display-name|description|distributable"/>
<xsl:apply-templates select="context-param"/>
<xsl:call-template name="insert-spring-context-param"/>
<xsl:if test="$cas-proxy-url">
<!-- Required for CAS ProxyTicketReceptor servlet. This is the
URL to CAS' "proxy" actuator, where a PGT and TargetService can
be presented to obtain a new proxy ticket. THIS CAN BE
REMOVED IF THE APPLICATION DOESN'T NEED TO ACT AS A PROXY -->
<context-param>
<param-name>edu.yale.its.tp.cas.proxyUrl</param-name>
<param-value><xsl:value-of select="$cas-proxy-url"/></param-value>
</context-param>
</xsl:if>
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<xsl:apply-templates select="filter"/>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<xsl:apply-templates select="filter-mapping"/>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<xsl:apply-templates select="listener"/>
<!-- Run any remaining non-security elements through the identity template -->
<xsl:apply-templates select="servlet|servlet-mapping|session-config|mime-mapping|welcome-file-list|error-page|taglib|resource-env-ref|resource-ref|env-entry|ejb-ref|ejb-local-ref"/>
</web-app>
</xsl:template>
<!--
| Looks for the case where we have an existing Spring context and appends
| the acegi file to the list of app. context files. Otherwise just copies the contents.
-->
<xsl:template match="context-param">
<context-param>
<xsl:choose>
<xsl:when test="./param-name = 'contextConfigLocation'">
<param-name>contextConfigLocation</param-name>
<param-value>
<xsl:value-of select="./param-value"/>
<xsl:value-of select="concat(' /WEB-INF/',$acegi-security-context-file)"/><xsl:text>&#xA; </xsl:text>
</param-value>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</context-param>
</xsl:template>
<!--
| Inserts a Spring config location context-param if one doesn't already exist.
| If there is one, do nothing as it will be handled by the context-param template above.
-->
<xsl:template name="insert-spring-context-param">
<xsl:if test="not(./context-param/param-name[string() = 'contextConfigLocation'])">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
<xsl:value-of select="concat('/WEB-INF/',$acegi-security-context-file)"/><xsl:text>&#xA;</xsl:text>
</param-value>
</context-param>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,273 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
| XSL Sheet used by the web.xml to acegi-security beans converter
| $Id$
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output doctype-public="-//SPRING//DTD BEAN//EN"
doctype-system="http://www.springframework.org/dtd/spring-beans.dtd"
indent="yes"/>
<!-- Variables for case conversions -->
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="welcome-files" select="web-app/welcome-file-list/welcome-file"/>
<!-- Convert the auth-method content to upper case -->
<xsl:variable name="auth-method" select="translate(string(web-app/login-config/auth-method), $lowercase, $uppercase)"/>
<!--
| Find the security-role elements in the file and uses them to build a list of
| all defined roles.
-->
<xsl:variable name="all-roles">
<xsl:for-each select="web-app/security-role/role-name">
<xsl:text>ROLE_</xsl:text>
<xsl:value-of select="translate(string(), $lowercase, $uppercase)"/>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:variable>
<!--
| The list of filters for use in filterToBeanProxy
-->
<xsl:variable name="filter-list">
<xsl:text>/**=httpSessionContextIntegrationFilter</xsl:text>
<xsl:choose>
<xsl:when test="$auth-method = 'FORM'">
<xsl:text>,authenticationProcessingFilter</xsl:text>
</xsl:when>
<xsl:when test="$auth-method = 'BASIC'">
<xsl:text>,basicProcessingFilter</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">Unsupported auth-method in web.xml, must be FORM or BASIC</xsl:message>
</xsl:otherwise>
</xsl:choose>
<xsl:text>,rememberMeProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter</xsl:text>
</xsl:variable>
<!--
| The main template (where the processing work starts)
-->
<xsl:template match = "web-app">
<beans>
<xsl:call-template name="filter-to-bean-proxy"/>
<xsl:call-template name="authentication-beans"/>
<xsl:apply-templates select="./login-config"/>
<xsl:call-template name="filter-invocation-interceptor"/>
</beans>
</xsl:template>
<!--
| Mainly static set of beans. The InMemoryDaoImpl instance is created with a single user
| called "superuser" who has all the defined roles in the web.xml file.
-->
<xsl:template name="authentication-beans">
<xsl:comment>======================== AUTHENTICATION =======================</xsl:comment>
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
<ref local="rememberMeAuthenticationProvider"/>
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref local="inMemoryDaoImpl"/></property>
<!-- property name="userCache"><ref local="userCache"/></property-->
</bean>
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
<property name="userMap">
<value>
superuser=password,<xsl:value-of select="$all-roles"/>
<xsl:text>&#xA;</xsl:text>
</value>
</property>
</bean>
<bean id="anonymousProcessingFilter" class="net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key"><value>foobar</value></property>
<property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property>
</bean>
<bean id="anonymousAuthenticationProvider" class="net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key"><value>foobar</value></property>
</bean>
<bean id="httpSessionContextIntegrationFilter" class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter">
</bean>
<bean id="rememberMeProcessingFilter" class="net.sf.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="rememberMeServices"><ref local="rememberMeServices"/></property>
</bean>
<bean id="rememberMeServices" class="net.sf.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
<property name="authenticationDao"><ref local="inMemoryDaoImpl"/></property>
<property name="key"><value>springRocks</value></property>
</bean>
<bean id="rememberMeAuthenticationProvider" class="net.sf.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
<property name="key"><value>springRocks</value></property>
</bean>
</xsl:template>
<!--
| Processes the login-config definition and inserts the SecurityEnforcementFilter with
| the appropriate beans for either form or basic authentication.
-->
<xsl:template match="login-config">
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
<property name="filterSecurityInterceptor"><ref local="filterInvocationInterceptor"/></property>
<property name="authenticationEntryPoint">
<xsl:choose>
<xsl:when test="$auth-method = 'FORM'">
<ref local="authenticationProcessingFilterEntryPoint"/>
</xsl:when>
<xsl:when test="$auth-method = 'BASIC'">
<ref local="basicProcessingFilterEntryPoint"/>
</xsl:when>
</xsl:choose>
</property>
</bean>
<xsl:choose>
<xsl:when test="$auth-method = 'FORM'">
<xsl:call-template name="form-login"/>
</xsl:when>
<xsl:when test="$auth-method = 'BASIC'">
<bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager"><ref local="authenticationManager"/></property>
<property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
</bean>
<bean id="basicProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName"><value>Your Realm</value></property>
</bean>
</xsl:when>
</xsl:choose>
</xsl:template>
<!--
| Converts a form login configuration to an Acegi AuthenticationProcessingFilter and its entry point.
| The content of the form-login-page element is used for the loginFormUrl property of the entry point
| and the form-error-page is used for the authenticationFailureUrl property of the filter.
|
| The user must manually change the form Url to "j_acegi_security_check" in their login page.
-->
<xsl:template name="form-login">
<xsl:message>Processing form login configuration</xsl:message>
<xsl:message>Remember to switch your login form action from "j_security_check" to "j_acegi_security_check"</xsl:message>
<bean id="authenticationProcessingFilter" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="authenticationFailureUrl"><value><xsl:value-of select="form-login-config/form-error-page"/></value></property>
<property name="defaultTargetUrl"><value></value></property>
<property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
<property name="rememberMeServices"><ref local="rememberMeServices"/></property>
</bean>
<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl"><value><xsl:value-of select="form-login-config/form-login-page"/></value></property>
<property name="forceHttps"><value>false</value></property>
</bean>
</xsl:template>
<!--
| Outputs a standard filterToBeanProxy bean.
-->
<xsl:template name="filter-to-bean-proxy">
<xsl:comment>======================== FILTER CHAIN =======================</xsl:comment>
<xsl:comment>if you wish to use channel security, add "channelProcessingFilter," in front
of "httpSessionContextIntegrationFilter" in the list below</xsl:comment>
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
<xsl:value-of select="$filter-list"/>
</value>
</property>
</bean>
</xsl:template>
<xsl:template name="filter-invocation-interceptor">
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<xsl:comment>An access decision voter that reads ROLE_* configuration settings</xsl:comment>
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
<xsl:text>&#xA;</xsl:text>
<xsl:comment>
Note the order that entries are placed against the objectDefinitionSource is critical.
The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last
</xsl:comment>
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
<property name="objectDefinitionSource">
<value>
<xsl:text>&#xA;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON&#xA;</xsl:text>
<xsl:text>PATTERN_TYPE_APACHE_ANT&#xA;</xsl:text>
<xsl:apply-templates select="security-constraint"/>
</value>
</property>
</bean>
</xsl:template>
<!--
| Converts a security-constraint (a url-pattern and the associated role-name elements)
| to the form
| antUrlPattern=list of allowed roles
| Roles are converted to upper case and have the "ROLE_" prefix appended.
|
| In the case of role-name='*', signifying "any authenticated role", the complete list of roles
| defined in the web.xml file is used.
|
| URLs which end in a wild card, will be converted to end in the recursive path version '**',
| e.g. /private/* becomes /private/**
-->
<xsl:template match="security-constraint">
<xsl:variable name="url" select="web-resource-collection/url-pattern"/>
<xsl:value-of select="$url"/>
<xsl:if test="substring($url, string-length($url)) = '*'">*</xsl:if>
<xsl:text>=</xsl:text>
<xsl:for-each select="./auth-constraint/role-name">
<xsl:choose>
<xsl:when test="string() = '*'">
<xsl:value-of select="$all-roles"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>ROLE_</xsl:text>
<xsl:value-of select="translate(string(), $lowercase, $uppercase)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
<xsl:text>&#xA;</xsl:text>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,69 +0,0 @@
package net.sf.acegisecurity.util;
import junit.framework.TestCase;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.AbstractResource;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.w3c.dom.Node;
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.UserDetails;
import net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter;
import net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
/**
* Tests the WebXmlToAcegiSecurityConverter by applying it to a sample web.xml file.
*
* @author Luke Taylor
* @version $Id$
*/
public class WebXmlToAcegiSecurityConverterTests extends TestCase {
public void testFileConversion() throws Exception {
WebXmlToAcegiSecurityConverter converter = new WebXmlToAcegiSecurityConverter();
Resource r = new ClassPathResource("test-web.xml");
converter.setInput(r.getInputStream());
converter.doConversion();
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(bf);
int nBeans = beanReader.loadBeanDefinitions(new InMemoryResource(converter.getAcegiBeansXml()));
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();
}
}

View File

@ -1,65 +0,0 @@
<?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>login-xml</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-business.xml
/WEB-INF/applicationContext-dao.xml
</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<security-constraint>
<web-resource-collection>
<url-pattern>/home.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>/user/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>form</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login.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>admin</role-name>
</security-role>
</web-app>