/* Copyright 2006-2009 the original author or authors. * * 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. */ package org.codehaus.groovy.grails.plugins.springsecurity; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.springframework.beans.factory.InitializingBean; import org.springframework.security.ConfigAttributeDefinition; import org.springframework.security.intercept.web.FilterInvocation; import org.springframework.security.intercept.web.FilterInvocationDefinitionSource; import org.springframework.security.util.AntUrlPathMatcher; import org.springframework.security.util.UrlMatcher; import org.springframework.util.Assert; /** * @author Burt Beckwith */ public abstract class AbstractFilterInvocationDefinition implements FilterInvocationDefinitionSource, InitializingBean { private UrlMatcher _urlMatcher; private boolean _rejectIfNoRule; private boolean _stripQueryStringFromUrls = true; protected static final ConfigAttributeDefinition DENY = new ConfigAttributeDefinition(Collections.emptyList()); protected final Map _compiled = new HashMap(); protected final Logger _log = Logger.getLogger(getClass()); /** * {@inheritDoc} * @see org.springframework.security.intercept.ObjectDefinitionSource#getAttributes(java.lang.Object) */ public ConfigAttributeDefinition getAttributes(Object object) { if (object == null || !supports(object.getClass())) { throw new IllegalArgumentException("Object must be a FilterInvocation"); } FilterInvocation filterInvocation = (FilterInvocation)object; String url = determineUrl(filterInvocation); ConfigAttributeDefinition configAttribute = findConfigAttribute(url); if (configAttribute == null && _rejectIfNoRule) { return DENY; } return configAttribute; } protected abstract String determineUrl(FilterInvocation filterInvocation); private ConfigAttributeDefinition findConfigAttribute(final String url) { initialize(); ConfigAttributeDefinition configAttribute = null; Object configAttributePattern = null; for (Map.Entry entry : _compiled.entrySet()) { Object pattern = entry.getKey(); if (_urlMatcher.pathMatchesUrl(pattern, url)) { // TODO this assumes Ant matching, not valid for regex if (configAttribute == null || _urlMatcher.pathMatchesUrl(configAttributePattern, (String)pattern)) { configAttribute = entry.getValue(); configAttributePattern = pattern; if (_log.isTraceEnabled()) { _log.trace("new candidate for '" + url + "': '" + pattern + "':" + configAttribute.getConfigAttributes()); } } } } if (_log.isTraceEnabled()) { if (configAttribute == null) { _log.trace("no config for '" + url + "'"); } else { _log.trace("config for '" + url + "' is '" + configAttributePattern + "':" + configAttribute.getConfigAttributes()); } } return configAttribute; } protected void initialize() { // override if necessary } /** * {@inheritDoc} * @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class) */ @SuppressWarnings("unchecked") public boolean supports(final Class clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } /** * {@inheritDoc} * @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions() */ @SuppressWarnings("unchecked") public Collection getConfigAttributeDefinitions() { return null; } /** * Dependency injection for the url matcher. * @param urlMatcher the matcher */ public void setUrlMatcher(final UrlMatcher urlMatcher) { _urlMatcher = urlMatcher; _stripQueryStringFromUrls = _urlMatcher instanceof AntUrlPathMatcher; } /** * Dependency injection for whether to reject if there's no matching rule. * @param reject if true, reject access unless there's a pattern for the specified resource */ public void setRejectIfNoRule(final boolean reject) { _rejectIfNoRule = reject; } protected String lowercaseAndStringQuerystring(final String url) { String fixed = url; if (getUrlMatcher().requiresLowerCaseUrl()) { fixed = fixed.toLowerCase(); } if (_stripQueryStringFromUrls) { int firstQuestionMarkIndex = fixed.indexOf("?"); if (firstQuestionMarkIndex != -1) { fixed = fixed.substring(0, firstQuestionMarkIndex); } } return fixed; } protected UrlMatcher getUrlMatcher() { return _urlMatcher; } /** * For debugging. * @return an unmodifiable map of {@link AnnotationFilterInvocationDefinition}ConfigAttributeDefinition * keyed by compiled patterns */ public Map getConfigAttributeMap() { return Collections.unmodifiableMap(_compiled); } /** * {@inheritDoc} * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ public void afterPropertiesSet() { Assert.notNull(_urlMatcher, "url matcher is required"); } }