SEC-1201: Allow requires-channel attribute to take placeholders.

This commit is contained in:
Luke Taylor 2009-08-23 16:42:06 +00:00
parent 00352227ac
commit fe33f08b73
7 changed files with 1739 additions and 1734 deletions

View File

@ -0,0 +1,37 @@
package org.springframework.security.config.http;
import java.util.List;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
/**
* Used as a factory bean to create config attribute values for the <tt>requires-channel</tt> attribute.
*
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class ChannelAttributeFactory {
private static final String OPT_REQUIRES_HTTP = "http";
private static final String OPT_REQUIRES_HTTPS = "https";
private static final String OPT_ANY_CHANNEL = "any";
public static final List<ConfigAttribute> createChannelAttributes(String requiredChannel) {
String channelConfigAttribute = null;
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
} else {
throw new BeanCreationException("Unknown channel attribute " + requiredChannel);
}
return SecurityConfig.createList(channelConfigAttribute);
}
}

View File

@ -27,8 +27,6 @@ import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
@ -102,9 +100,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
private static final String OPT_SESSION_FIXATION_MIGRATE_SESSION = "migrateSession";
static final String ATT_REQUIRES_CHANNEL = "requires-channel";
private static final String OPT_REQUIRES_HTTP = "http";
private static final String OPT_REQUIRES_HTTPS = "https";
private static final String OPT_ANY_CHANNEL = "any";
private static final String ATT_CREATE_SESSION = "create-session";
private static final String DEF_CREATE_SESSION_IF_REQUIRED = "ifRequired";
@ -180,7 +175,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
// Use ManagedMap to allow placeholder resolution
final ManagedMap<String, List<BeanMetadataElement>> filterChainMap =
parseInterceptUrlsForEmptyFilterChains(interceptUrls, convertPathsToLowerCase, pc);
final ManagedMap<BeanDefinition,List<ConfigAttribute>> channelRequestMap =
final ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap =
parseInterceptUrlsForChannelSecurity(interceptUrls, convertPathsToLowerCase, pc);
BeanDefinition cpf = null;
@ -893,7 +888,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
private BeanDefinition createChannelProcessingFilter(ParserContext pc, UrlMatcher matcher,
ManagedMap<BeanDefinition,List<ConfigAttribute>> channelRequestMap, String portMapperBeanName) {
ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap, String portMapperBeanName) {
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
BeanDefinitionBuilder metadataSourceBldr = BeanDefinitionBuilder.rootBeanDefinition(DefaultFilterInvocationSecurityMetadataSource.class);
metadataSourceBldr.addConstructorArgValue(matcher);
@ -1189,10 +1184,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
* Parses the intercept-url elements to obtain the map used by channel security.
* This will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL path.
*/
private ManagedMap<BeanDefinition,List<ConfigAttribute>> parseInterceptUrlsForChannelSecurity(List<Element> urlElts,
private ManagedMap<BeanDefinition,BeanDefinition> parseInterceptUrlsForChannelSecurity(List<Element> urlElts,
boolean useLowerCasePaths, ParserContext parserContext) {
ManagedMap<BeanDefinition, List<ConfigAttribute>> channelRequestMap = new ManagedMap<BeanDefinition, List<ConfigAttribute>>();
ManagedMap<BeanDefinition, BeanDefinition> channelRequestMap = new ManagedMap<BeanDefinition, BeanDefinition>();
for (Element urlElt : urlElts) {
String path = urlElt.getAttribute(ATT_PATH_PATTERN);
@ -1208,22 +1203,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
if (StringUtils.hasText(requiredChannel)) {
String channelConfigAttribute = null;
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
} else {
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
}
BeanDefinition requestKey = new RootBeanDefinition(RequestKey.class);
requestKey.getConstructorArgumentValues().addGenericArgumentValue(path);
channelRequestMap.put(requestKey, SecurityConfig.createList(channelConfigAttribute));
RootBeanDefinition channelAttributes = new RootBeanDefinition(ChannelAttributeFactory.class);
channelAttributes.getConstructorArgumentValues().addGenericArgumentValue(requiredChannel);
channelAttributes.setFactoryMethodName("createChannelAttributes");
channelRequestMap.put(requestKey, channelAttributes);
}
}

View File

@ -9,7 +9,7 @@ import org.springframework.util.StringUtils;
*
* @author Luke Taylor
* @author Ben Alex
* @version $Id: WebConfigUtils.java 3770 2009-07-15 23:09:47Z ltaylor $
* @version $Id$
*/
abstract class WebConfigUtils {

View File

@ -316,8 +316,8 @@ intercept-url.attlist &=
## The filter list for the path. Currently can be set to "none" to remove a path from having any filters applied. The full filter stack (consisting of all filters created by the namespace configuration, and any added using 'custom-filter'), will be applied to any other paths.
attribute filters {"none"}?
intercept-url.attlist &=
## Used to specify that a URL must be accessed over http or https, or that there is no preference.
attribute requires-channel {"http" | "https" | "any"}?
## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be "http", "https" or "any", respectively.
attribute requires-channel {xsd:token}?
logout =
## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic.

View File

@ -391,18 +391,24 @@ public class HttpSecurityBeanDefinitionParserTests {
@Test
public void requiresChannelSupportsPlaceholder() throws Exception {
System.setProperty("secure.url", "/secure");
System.setProperty("required.channel", "https");
setContext(
" <b:bean id='configurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>" +
" <b:bean id='configurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>" +
" <http auto-config='true'>" +
" <intercept-url pattern='${secure.url}' requires-channel='https' />" +
" <intercept-url pattern='${secure.url}' requires-channel='${required.channel}' />" +
" </http>" + AUTH_PROVIDER_XML);
List<Filter> filters = getFilters("/secure");
assertEquals("Expected " + (AUTO_CONFIG_FILTERS + 1) +" filters in chain", AUTO_CONFIG_FILTERS + 1, filters.size());
assertTrue(filters.get(0) instanceof ChannelProcessingFilter);
}
ChannelProcessingFilter filter = (ChannelProcessingFilter) filters.get(0);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setServletPath("/secure");
MockHttpServletResponse response = new MockHttpServletResponse();
filter.doFilter(request, response, new MockFilterChain());
assertNotNull(response.getRedirectedUrl());
assertTrue(response.getRedirectedUrl().startsWith("https"));
}
@Test
public void portMappingsAreParsedCorrectly() throws Exception {
setContext(

View File

@ -111,11 +111,11 @@ public class ChannelProcessingFilter extends GenericFilterBean {
chain.doFilter(request, response);
}
public ChannelDecisionManager getChannelDecisionManager() {
protected ChannelDecisionManager getChannelDecisionManager() {
return channelDecisionManager;
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
protected FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}