View Javadoc
1   /*
2    * Copyright 2008-2009 Web Cohesion
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.security.oauth.consumer.filter;
18  
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.springframework.beans.factory.InitializingBean;
22  import org.springframework.beans.factory.annotation.Autowired;
23  import org.springframework.context.MessageSource;
24  import org.springframework.context.MessageSourceAware;
25  import org.springframework.context.support.MessageSourceAccessor;
26  import org.springframework.security.access.ConfigAttribute;
27  import org.springframework.security.authentication.InsufficientAuthenticationException;
28  import org.springframework.security.core.Authentication;
29  import org.springframework.security.core.SpringSecurityMessageSource;
30  import org.springframework.security.core.context.SecurityContextHolder;
31  import org.springframework.security.oauth.consumer.AccessTokenRequiredException;
32  import org.springframework.security.oauth.consumer.OAuthConsumerToken;
33  import org.springframework.security.oauth.consumer.OAuthSecurityContext;
34  import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
35  import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
36  import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
37  import org.springframework.security.web.FilterInvocation;
38  import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
39  import org.springframework.util.Assert;
40  
41  import javax.servlet.*;
42  import javax.servlet.http.HttpServletRequest;
43  import javax.servlet.http.HttpServletResponse;
44  import java.io.IOException;
45  import java.util.Collection;
46  import java.util.Map;
47  import java.util.Set;
48  import java.util.TreeSet;
49  
50  /**
51   * OAuth consumer processing filter. This filter should be applied to requests for OAuth protected resources (see OAuth Core 1.0).
52   * 
53   * When servicing a request that requires protected resources, this filter sets a request attribute (default "OAUTH_ACCESS_TOKENS") that contains
54   * the list of {@link org.springframework.security.oauth.consumer.OAuthConsumerToken}s.
55   *
56   * @author Ryan Heaton
57   * @author Andrew McCall
58   */
59  public class OAuthConsumerProcessingFilter implements Filter, InitializingBean, MessageSourceAware {
60  
61    private static final Log LOG = LogFactory.getLog(OAuthConsumerProcessingFilter.class);
62  
63    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
64    private FilterInvocationSecurityMetadataSource objectDefinitionSource;
65    private boolean requireAuthenticated = true;
66  
67    private ProtectedResourceDetailsService protectedResourceDetailsService;
68  
69    public void afterPropertiesSet() throws Exception {
70      Assert.notNull(protectedResourceDetailsService, "A protected resource details service is required.");
71      Assert.notNull(objectDefinitionSource, "The object definition source must be configured.");
72    }
73  
74    public void init(FilterConfig ignored) throws ServletException {
75    }
76  
77    public void destroy() {
78    }
79  
80    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
81      HttpServletRequest request = (HttpServletRequest) servletRequest;
82      HttpServletResponse response = (HttpServletResponse) servletResponse;
83  
84      Set<String> accessTokenDeps = getAccessTokenDependencies(request, response, chain);
85      if (!accessTokenDeps.isEmpty()) {
86        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
87        if (isRequireAuthenticated() && !authentication.isAuthenticated()) {
88          throw new InsufficientAuthenticationException("An authenticated principal must be present.");
89        }
90  
91        OAuthSecurityContext context = OAuthSecurityContextHolder.getContext();
92        if (context == null) {
93          throw new IllegalStateException("No OAuth security context has been established. Unable to access resources.");
94        }
95  
96        Map<String, OAuthConsumerToken> accessTokens = context.getAccessTokens();
97  
98        for (String dependency : accessTokenDeps) {
99          if (!accessTokens.containsKey(dependency)) {
100           throw new AccessTokenRequiredException(getProtectedResourceDetailsService().loadProtectedResourceDetailsById(dependency));
101         }
102       }
103 
104       chain.doFilter(request, response);
105     }
106     else {
107       if (LOG.isDebugEnabled()) {
108         LOG.debug("No access token dependencies for request.");
109       }
110       chain.doFilter(servletRequest, servletResponse);
111     }
112   }
113 
114   /**
115    * Loads the access token dependencies for the given request. This will be a set of {@link ProtectedResourceDetails#getId() resource ids}
116    * for which an OAuth access token is required.
117    *
118    * @param request     The request.
119    * @param response    The response
120    * @param filterChain The filter chain
121    * @return The access token dependencies (could be empty).
122    */
123   protected Set<String> getAccessTokenDependencies(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
124     Set<String> deps = new TreeSet<String>();
125 
126     if (getObjectDefinitionSource() != null) {
127       FilterInvocation invocation = new FilterInvocation(request, response, filterChain);
128       Collection<ConfigAttribute> attributes = getObjectDefinitionSource().getAttributes(invocation);
129       if (attributes != null) {
130         for (ConfigAttribute attribute : attributes) {
131           deps.add(attribute.getAttribute());
132         }
133       }
134     }
135     return deps;
136   }
137 
138   /**
139    * The protected resource details service.
140    *
141    * @return The protected resource details service.
142    */
143   public ProtectedResourceDetailsService getProtectedResourceDetailsService() {
144     return protectedResourceDetailsService;
145   }
146 
147   /**
148    * The protected resource details service.
149    *
150    * @param protectedResourceDetailsService
151    *         The protected resource details service.
152    */
153   @Autowired
154   public void setProtectedResourceDetailsService(ProtectedResourceDetailsService protectedResourceDetailsService) {
155     this.protectedResourceDetailsService = protectedResourceDetailsService;
156   }
157 
158   /**
159    * The filter invocation definition source.
160    *
161    * @return The filter invocation definition source.
162    */
163   public FilterInvocationSecurityMetadataSource getObjectDefinitionSource() {
164     return objectDefinitionSource;
165   }
166 
167   /**
168    * The filter invocation definition source.
169    *
170    * @param objectDefinitionSource The filter invocation definition source.
171    */
172   public void setObjectDefinitionSource(FilterInvocationSecurityMetadataSource objectDefinitionSource) {
173     this.objectDefinitionSource = objectDefinitionSource;
174   }
175 
176   /**
177    * Set the message source.
178    *
179    * @param messageSource The message source.
180    */
181   public void setMessageSource(MessageSource messageSource) {
182     this.messages = new MessageSourceAccessor(messageSource);
183   }
184 
185   /**
186    * Whether to require the current authentication to be authenticated.
187    *
188    * @return Whether to require the current authentication to be authenticated.
189    */
190   public boolean isRequireAuthenticated() {
191     return requireAuthenticated;
192   }
193 
194   /**
195    * Whether to require the current authentication to be authenticated.
196    *
197    * @param requireAuthenticated Whether to require the current authentication to be authenticated.
198    */
199   public void setRequireAuthenticated(boolean requireAuthenticated) {
200     this.requireAuthenticated = requireAuthenticated;
201   }
202 
203 }