Permalink
Browse files

SECOAUTH-210: Consolidated all error handling

  • Loading branch information...
1 parent ac98748 commit 981733d1f27c47cd249733689477385e92434a5c @dsyer dsyer committed Feb 27, 2012
Showing with 423 additions and 564 deletions.
  1. +7 −7 samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml
  2. +1 −1 ...klr/src/test/java/org/springframework/security/oauth2/provider/TestAuthorizationCodeProvider.java
  3. +1 −1 ...src/main/java/org/springframework/security/oauth2/{provider → client}/filter/CompositeFilter.java
  4. +1 −1 ...main/java/org/springframework/security/oauth2/config/AuthorizationServerBeanDefinitionParser.java
  5. +1 −1 ...y-oauth2/src/main/java/org/springframework/security/oauth2/config/ClientBeanDefinitionParser.java
  6. +9 −32 .../src/main/java/org/springframework/security/oauth2/config/ResourceServerBeanDefinitionParser.java
  7. +69 −0 ...java/org/springframework/security/oauth2/provider/authentication/OAuth2AuthenticationManager.java
  8. +90 −0 ...springframework/security/oauth2/provider/authentication/OAuth2AuthenticationProcessingFilter.java
  9. +1 −1 ...ingframework/security/oauth2/provider/{filter → client}/ClientCredentialsTokenEndpointFilter.java
  10. +3 −3 ...pringframework/security/oauth2/provider/{filter → client}/OAuth2AuthenticationFailureHandler.java
  11. +5 −5 ...-oauth2/src/main/java/org/springframework/security/oauth2/provider/endpoint/AbstractEndpoint.java
  12. +1 −1 ...va/org/springframework/security/oauth2/provider/{filter → endpoint}/EndpointValidationFilter.java
  13. +82 −0 ...va/org/springframework/security/oauth2/provider/error/AbstractOAuth2SecurityExceptionHandler.java
  14. +1 −1 ...va/org/springframework/security/oauth2/provider/{web → error}/DefaultOAuth2ExceptionRenderer.java
  15. +31 −8 ...rovider/error/{DefaultProviderExceptionHandler.java → DefaultWebResponseExceptionTranslator.java}
  16. +0 −99 ...in/java/org/springframework/security/oauth2/provider/error/MediaTypeAwareAccessDeniedHandler.java
  17. +0 −142 ...va/org/springframework/security/oauth2/provider/error/MediaTypeAwareAuthenticationEntryPoint.java
  18. +39 −0 ...2/src/main/java/org/springframework/security/oauth2/provider/error/OAuth2AccessDeniedHandler.java
  19. +71 −0 .../main/java/org/springframework/security/oauth2/provider/error/OAuth2AuthenticationEntryPoint.java
  20. +1 −1 ...main/java/org/springframework/security/oauth2/provider/{web → error}/OAuth2ExceptionRenderer.java
  21. +2 −2 ...urity/oauth2/provider/error/{ProviderExceptionHandler.java → WebResponseExceptionTranslator.java}
  22. +0 −101 ...c/main/java/org/springframework/security/oauth2/provider/filter/OAuth2ExceptionHandlerFilter.java
  23. +0 −132 .../main/java/org/springframework/security/oauth2/provider/filter/OAuth2ProtectedResourceFilter.java
  24. +3 −2 ...gframework/security/oauth2/provider/{filter → client}/TestOAuth2AuthenticationFailureHandler.java
  25. +2 −1 ...rg/springframework/security/oauth2/provider/{filter → endpoint}/TestEndpointValidationFilter.java
  26. +1 −1 ...ava/org/springframework/security/oauth2/provider/error/TestMediaTypeAwareAccessDeniedHandler.java
  27. +1 −21 ...rg/springframework/security/oauth2/provider/error/TestMediaTypeAwareAuthenticationEntryPoint.java
@@ -29,20 +29,20 @@
<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*" access="oauthClientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or oauthIsClient()) and oauthHasScope('write')" method="DELETE"/>
<intercept-url pattern="/oauth/tokens/.*" access="oauthClientHasRole('ROLE_CLIENT') and oauthHasScope('write')" method="DELETE"/>
<intercept-url pattern="/oauth/clients/.*" access="oauthClientHasRole('ROLE_CLIENT') and oauthIsClient() and oauthHasScope('read')" method="GET"/>
- <custom-filter ref="resourceServerFilter" before="EXCEPTION_TRANSLATION_FILTER" />
+ <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
<expression-handler ref="oauthWebExpressionHandler" />
</http>
<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling
separately. This isn't mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/photos/**" entry-point-ref="oauthAuthenticationEntryPoint" access-decision-manager-ref="accessDecisionManager"
- xmlns="http://www.springframework.org/schema/security">
+ xmlns="http://www.springframework.org/schema/security" >
<intercept-url pattern="/photos" access="ROLE_USER,SCOPE_READ" />
<intercept-url pattern="/photos/trusted/**" access="ROLE_CLIENT,SCOPE_TRUST" />
<intercept-url pattern="/photos/user/**" access="ROLE_USER,SCOPE_TRUST" />
<intercept-url pattern="/photos/**" access="ROLE_USER,SCOPE_READ" />
- <custom-filter ref="resourceServerFilter" before="EXCEPTION_TRANSLATION_FILTER" />
+ <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
@@ -55,14 +55,14 @@
<logout logout-success-url="/index.jsp" logout-url="/logout.do" />
<anonymous />
</http>
-
- <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.MediaTypeAwareAuthenticationEntryPoint">
+
+ <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="sparklr2" />
</bean>
- <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.MediaTypeAwareAccessDeniedHandler" />
+ <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />
- <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.filter.ClientCredentialsTokenEndpointFilter">
+ <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="clientAuthenticationManager" />
</bean>
@@ -178,7 +178,7 @@ public void testInvalidScopeInResourceRequest() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, accessToken.getValue()));
ResponseEntity<String> response = serverRunning.getForString("/sparklr2/photos?format=json", headers);
- assertEquals(HttpStatus.UNAUTHORIZED, response.getStatusCode());
+ assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
String authenticate = response.getHeaders().getFirst("WWW-Authenticate");
assertNotNull(authenticate);
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.security.oauth2.provider.filter;
+package org.springframework.security.oauth2.client.filter;
import java.io.IOException;
import java.util.ArrayList;
@@ -24,8 +24,8 @@
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint;
+import org.springframework.security.oauth2.provider.endpoint.EndpointValidationFilter;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
-import org.springframework.security.oauth2.provider.filter.EndpointValidationFilter;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter;
@@ -25,13 +25,13 @@
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
+import org.springframework.security.oauth2.client.filter.CompositeFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientProcessingFilter;
import org.springframework.security.oauth2.client.filter.cache.HttpSessionAccessTokenCache;
import org.springframework.security.oauth2.client.token.AccessTokenProviderChain;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;
-import org.springframework.security.oauth2.provider.filter.CompositeFilter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -13,16 +13,11 @@
package org.springframework.security.oauth2.config;
-import org.springframework.beans.BeanMetadataElement;
-import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
-import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.security.oauth2.provider.error.DefaultProviderExceptionHandler;
-import org.springframework.security.oauth2.provider.filter.CompositeFilter;
-import org.springframework.security.oauth2.provider.filter.OAuth2ExceptionHandlerFilter;
-import org.springframework.security.oauth2.provider.filter.OAuth2ProtectedResourceFilter;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
@@ -38,38 +33,20 @@
protected AbstractBeanDefinition parseEndpointAndReturnFilter(Element element, ParserContext parserContext,
String tokenServicesRef, String serializerRef) {
- ManagedList<BeanMetadataElement> filters = new ManagedList<BeanMetadataElement>();
-
- BeanDefinitionBuilder exceptionHandlerFilter = BeanDefinitionBuilder
- .rootBeanDefinition(OAuth2ExceptionHandlerFilter.class);
- if (StringUtils.hasText(serializerRef)) {
- BeanDefinitionBuilder exceptionHandler = BeanDefinitionBuilder
- .rootBeanDefinition(DefaultProviderExceptionHandler.class);
- exceptionHandler.addPropertyReference("serializationService", serializerRef);
- exceptionHandlerFilter.addPropertyValue("providerExceptionHandler", exceptionHandler.getBeanDefinition());
- }
-
- parserContext.getRegistry().registerBeanDefinition("oauth2ExceptionHandlerFilter",
- exceptionHandlerFilter.getBeanDefinition());
- filters.add(new RuntimeBeanReference("oauth2ExceptionHandlerFilter"));
-
String resourceId = element.getAttribute("resource-id");
// configure the protected resource filter
BeanDefinitionBuilder protectedResourceFilterBean = BeanDefinitionBuilder
- .rootBeanDefinition(OAuth2ProtectedResourceFilter.class);
- protectedResourceFilterBean.addPropertyReference("tokenServices", tokenServicesRef);
+ .rootBeanDefinition(OAuth2AuthenticationProcessingFilter.class);
+ BeanDefinitionBuilder authenticationManagerBean = BeanDefinitionBuilder
+ .rootBeanDefinition(OAuth2AuthenticationManager.class);
+ authenticationManagerBean.addPropertyReference("tokenServices", tokenServicesRef);
if (StringUtils.hasText(resourceId)) {
- protectedResourceFilterBean.addPropertyValue("resourceId", resourceId);
+ authenticationManagerBean.addPropertyValue("resourceId", resourceId);
}
+ protectedResourceFilterBean.addPropertyValue("authenticationManager", authenticationManagerBean.getBeanDefinition());
- parserContext.getRegistry().registerBeanDefinition("oauth2ProtectedResourceFilter",
- protectedResourceFilterBean.getBeanDefinition());
- filters.add(new RuntimeBeanReference("oauth2ProtectedResourceFilter"));
-
- BeanDefinitionBuilder filterChain = BeanDefinitionBuilder.rootBeanDefinition(CompositeFilter.class);
- filterChain.addPropertyValue("filters", filters);
- return filterChain.getBeanDefinition();
+ return protectedResourceFilterBean.getBeanDefinition();
}
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2006-2011 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.springframework.security.oauth2.provider.authentication;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
+import org.springframework.util.Assert;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean {
+
+ private ResourceServerTokenServices tokenServices;
+
+ private String resourceId;
+
+ public void setResourceId(String resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ /**
+ * @param tokenServices the tokenServices to set
+ */
+ public void setTokenServices(ResourceServerTokenServices tokenServices) {
+ this.tokenServices = tokenServices;
+ }
+
+ public void afterPropertiesSet() {
+ Assert.state(tokenServices!=null, "TokenServices are required");
+ }
+
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+
+ String token = (String) authentication.getPrincipal();
+ OAuth2Authentication auth = tokenServices.loadAuthentication(token);
+ if (auth == null) {
+ throw new InvalidTokenException("Invalid token: " + token);
+ }
+
+ Collection<String> resourceIds = auth.getAuthorizationRequest().getResourceIds();
+ if (resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
+ throw new InvalidTokenException("Invalid token does not contain resource id (" + resourceId + "): "
+ + token);
+ }
+
+ return auth;
+
+ }
+
+}
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2006-2011 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.springframework.security.oauth2.provider.authentication;
+
+import java.util.Enumeration;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class OAuth2AuthenticationProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
+
+ private final static Log logger = LogFactory.getLog(OAuth2AuthenticationProcessingFilter.class);
+
+ public void afterPropertiesSet() {
+ super.afterPropertiesSet();
+ }
+
+ @Override
+ protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
+ return "N/A";
+ }
+
+ @Override
+ protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
+ return parseToken(request);
+ }
+
+ protected String parseToken(HttpServletRequest request) {
+ // first check the header...
+ String token = parseHeaderToken(request);
+
+ // bearer type allows a request parameter as well
+ if (token == null) {
+ logger.debug("Token not found in headers. Trying request parameters.");
+ token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
+ if (token == null) {
+ logger.debug("Token not found in request parameters. Not an OAuth2 request.");
+ }
+ }
+
+ return token;
+ }
+
+ /**
+ * Parse the OAuth header parameters. The parameters will be oauth-decoded.
+ *
+ * @param request The request.
+ * @return The parsed parameters, or null if no OAuth authorization header was supplied.
+ */
+ protected String parseHeaderToken(HttpServletRequest request) {
+ @SuppressWarnings("unchecked")
+ Enumeration<String> headers = request.getHeaders("Authorization");
+ while (headers.hasMoreElements()) {
+ String value = headers.nextElement();
+ if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
+ String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
+ int commaIndex = authHeaderValue.indexOf(',');
+ if (commaIndex > 0) {
+ authHeaderValue = authHeaderValue.substring(0, commaIndex);
+ }
+ return authHeaderValue;
+ }
+ else {
+ // todo: support additional authorization schemes for different token types, e.g. "MAC" specified by
+ // http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token
+ }
+ }
+
+ return null;
+ }
+
+}
@@ -10,7 +10,7 @@
* 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.oauth2.provider.filter;
+package org.springframework.security.oauth2.provider.client;
import java.io.IOException;
@@ -10,7 +10,7 @@
* 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.oauth2.provider.filter;
+package org.springframework.security.oauth2.provider.client;
import java.io.IOException;
@@ -23,8 +23,8 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
-import org.springframework.security.oauth2.provider.web.DefaultOAuth2ExceptionRenderer;
-import org.springframework.security.oauth2.provider.web.OAuth2ExceptionRenderer;
+import org.springframework.security.oauth2.provider.error.DefaultOAuth2ExceptionRenderer;
+import org.springframework.security.oauth2.provider.error.OAuth2ExceptionRenderer;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.util.Assert;
import org.springframework.web.context.request.ServletWebRequest;
@@ -21,8 +21,8 @@
import org.springframework.http.HttpEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.TokenGranter;
-import org.springframework.security.oauth2.provider.error.DefaultProviderExceptionHandler;
-import org.springframework.security.oauth2.provider.error.ProviderExceptionHandler;
+import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
+import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.ServletWebRequest;
@@ -35,15 +35,15 @@
protected final Log logger = LogFactory.getLog(getClass());
- private ProviderExceptionHandler providerExceptionHandler = new DefaultProviderExceptionHandler();
+ private WebResponseExceptionTranslator providerExceptionHandler = new DefaultWebResponseExceptionTranslator();
private TokenGranter tokenGranter;
public void afterPropertiesSet() throws Exception {
Assert.state(tokenGranter != null, "TokenGranter must be provided");
}
- public void setProviderExceptionHandler(ProviderExceptionHandler providerExceptionHandler) {
+ public void setProviderExceptionHandler(WebResponseExceptionTranslator providerExceptionHandler) {
this.providerExceptionHandler = providerExceptionHandler;
}
@@ -57,7 +57,7 @@ protected TokenGranter getTokenGranter() {
@ExceptionHandler(OAuth2Exception.class)
public HttpEntity<OAuth2Exception> handleException(OAuth2Exception e, ServletWebRequest webRequest) throws Exception {
- return providerExceptionHandler.handle(e);
+ return providerExceptionHandler.translate(e);
}
}
@@ -1,4 +1,4 @@
-package org.springframework.security.oauth2.provider.filter;
+package org.springframework.security.oauth2.provider.endpoint;
import java.io.IOException;
Oops, something went wrong.

0 comments on commit 981733d

Please sign in to comment.