Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

Commit

Permalink
SECOAUTH-210: Consolidated all error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
dsyer committed Feb 27, 2012
1 parent ac98748 commit 981733d
Show file tree
Hide file tree
Showing 27 changed files with 423 additions and 564 deletions.
Expand Up @@ -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>

Expand All @@ -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>

Expand Down
Expand Up @@ -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);
Expand Down
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;

Expand Down
Expand Up @@ -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;

Expand All @@ -38,38 +33,20 @@ public class ResourceServerBeanDefinitionParser extends ProviderBeanDefinitionPa
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();

}

Expand Down
@@ -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;
}

}
Expand Up @@ -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;

Expand Down
Expand Up @@ -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;

Expand All @@ -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;
Expand Down
Expand Up @@ -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;
Expand All @@ -35,15 +35,15 @@ public class AbstractEndpoint implements InitializingBean {

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;
}

Expand All @@ -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;

Expand Down

0 comments on commit 981733d

Please sign in to comment.