/* * Copyright 2008-2009 Web Cohesion * * 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.springframework.security.oauth.consumer.filter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceAware; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth.consumer.AccessTokenRequiredException; import org.springframework.security.oauth.consumer.OAuthConsumerToken; import org.springframework.security.oauth.consumer.OAuthSecurityContext; import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder; import org.springframework.security.oauth.consumer.ProtectedResourceDetails; import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.util.Assert; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.TreeSet; /** * OAuth consumer processing filter. This filter should be applied to requests for OAuth protected resources (see OAuth Core 1.0). * * When servicing a request that requires protected resources, this filter sets a request attribute (default "OAUTH_ACCESS_TOKENS") that contains * the list of {@link org.springframework.security.oauth.consumer.OAuthConsumerToken}s. * * @author Ryan Heaton * @author Andrew McCall */ public class OAuthConsumerProcessingFilter implements Filter, InitializingBean, MessageSourceAware { private static final Log LOG = LogFactory.getLog(OAuthConsumerProcessingFilter.class); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private FilterInvocationSecurityMetadataSource objectDefinitionSource; private boolean requireAuthenticated = true; private ProtectedResourceDetailsService protectedResourceDetailsService; public void afterPropertiesSet() throws Exception { Assert.notNull(protectedResourceDetailsService, "A protected resource details service is required."); Assert.notNull(objectDefinitionSource, "The object definition source must be configured."); } public void init(FilterConfig ignored) throws ServletException { } public void destroy() { } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; Set<String> accessTokenDeps = getAccessTokenDependencies(request, response, chain); if (!accessTokenDeps.isEmpty()) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (isRequireAuthenticated() && !authentication.isAuthenticated()) { throw new InsufficientAuthenticationException("An authenticated principal must be present."); } OAuthSecurityContext context = OAuthSecurityContextHolder.getContext(); if (context == null) { throw new IllegalStateException("No OAuth security context has been established. Unable to access resources."); } Map<String, OAuthConsumerToken> accessTokens = context.getAccessTokens(); for (String dependency : accessTokenDeps) { if (!accessTokens.containsKey(dependency)) { throw new AccessTokenRequiredException(getProtectedResourceDetailsService().loadProtectedResourceDetailsById(dependency)); } } chain.doFilter(request, response); } else { if (LOG.isDebugEnabled()) { LOG.debug("No access token dependencies for request."); } chain.doFilter(servletRequest, servletResponse); } } /** * Loads the access token dependencies for the given request. This will be a set of {@link ProtectedResourceDetails#getId() resource ids} * for which an OAuth access token is required. * * @param request The request. * @param response The response * @param filterChain The filter chain * @return The access token dependencies (could be empty). */ protected Set<String> getAccessTokenDependencies(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) { Set<String> deps = new TreeSet<String>(); if (getObjectDefinitionSource() != null) { FilterInvocation invocation = new FilterInvocation(request, response, filterChain); Collection<ConfigAttribute> attributes = getObjectDefinitionSource().getAttributes(invocation); if (attributes != null) { for (ConfigAttribute attribute : attributes) { deps.add(attribute.getAttribute()); } } } return deps; } /** * The protected resource details service. * * @return The protected resource details service. */ public ProtectedResourceDetailsService getProtectedResourceDetailsService() { return protectedResourceDetailsService; } /** * The protected resource details service. * * @param protectedResourceDetailsService * The protected resource details service. */ @Autowired public void setProtectedResourceDetailsService(ProtectedResourceDetailsService protectedResourceDetailsService) { this.protectedResourceDetailsService = protectedResourceDetailsService; } /** * The filter invocation definition source. * * @return The filter invocation definition source. */ public FilterInvocationSecurityMetadataSource getObjectDefinitionSource() { return objectDefinitionSource; } /** * The filter invocation definition source. * * @param objectDefinitionSource The filter invocation definition source. */ public void setObjectDefinitionSource(FilterInvocationSecurityMetadataSource objectDefinitionSource) { this.objectDefinitionSource = objectDefinitionSource; } /** * Set the message source. * * @param messageSource The message source. */ public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } /** * Whether to require the current authentication to be authenticated. * * @return Whether to require the current authentication to be authenticated. */ public boolean isRequireAuthenticated() { return requireAuthenticated; } /** * Whether to require the current authentication to be authenticated. * * @param requireAuthenticated Whether to require the current authentication to be authenticated. */ public void setRequireAuthenticated(boolean requireAuthenticated) { this.requireAuthenticated = requireAuthenticated; } }