From 177545cdfa4fffb4e1d618b7771b2d4d2c56c02f Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 7 Nov 2011 15:55:44 +0000 Subject: [PATCH] SECOAUTH-154: added rough draft of implicit token provider on client --- .../main/webapp/WEB-INF/spring-servlet.xml | 12 +- .../tonr/impl/SparklrServiceImpl.java | 3 +- .../examples/tonr/mvc/FacebookController.java | 3 +- .../main/webapp/WEB-INF/spring-servlet.xml | 16 +- .../oauth/examples/tonr/ServerRunning.java | 2 +- .../examples/tonr/TestClientConnections.java | 30 +-- .../client/context/OAuth2ClientContext.java | 55 ++---- .../filter/OAuth2ClientContextFilter.java | 74 +++++--- .../filter/OAuth2ClientContextImpl.java | 67 ------- .../http/OAuth2ClientHttpRequestFactory.java | 8 +- .../AbstractOAuth2AccessTokenProvider.java | 6 +- .../client/provider/AccessTokenRequest.java | 173 ++++++++++++++++++ .../provider/OAuth2AccessTokenProvider.java | 5 +- .../OAuth2AccessTokenProviderChain.java | 4 +- .../provider/OAuth2AccessTokenSupport.java | 52 ++++-- .../DefaultClientAuthenticationHandler.java | 5 +- .../ClientCredentialsAccessTokenProvider.java | 15 +- .../ClientCredentialsResourceDetails.java | 4 + .../AuthorizationCodeAccessTokenProvider.java | 42 ++--- .../implicit/ImplicitAccessTokenProvider.java | 170 +++++++++++++++++ .../implicit/ImplicitResourceDetails.java | 36 ++++ .../BaseOAuth2ProtectedResourceDetails.java | 22 ++- .../OAuth2ProtectedResourceDetails.java | 12 +- ...entDetailsServiceBeanDefinitionParser.java | 4 +- .../OAuth2ResourceBeanDefinitionParser.java | 28 +-- .../oauth/spring-security-oauth2-1.0.xsd | 22 +-- ...ilsServiceBeanDefinitionParser-context.xml | 10 +- ...h2ResourceBeanDefinitionParser-context.xml | 6 +- 28 files changed, 604 insertions(+), 282 deletions(-) delete mode 100644 spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextImpl.java create mode 100644 spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AccessTokenRequest.java create mode 100644 spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitAccessTokenProvider.java create mode 100644 spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitResourceDetails.java diff --git a/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml b/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml index 1cb73b367..a1eb8e9c2 100644 --- a/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/samples/oauth2/sparklr/src/main/webapp/WEB-INF/spring-servlet.xml @@ -61,17 +61,17 @@ - - - - - - diff --git a/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/impl/SparklrServiceImpl.java b/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/impl/SparklrServiceImpl.java index 8c226ebad..03d9eda1b 100644 --- a/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/impl/SparklrServiceImpl.java +++ b/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/impl/SparklrServiceImpl.java @@ -17,7 +17,6 @@ import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.context.OAuth2ClientContext; import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextImpl; import org.springframework.security.oauth2.client.http.OAuth2AccessTokenRequiredException; import org.springframework.security.oauth2.client.provider.token.OAuth2ClientTokenServices; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; @@ -68,7 +67,7 @@ public void startElement(String uri, String localName, String qName, Attributes if (context != null) { // TODO: this one is kind of a hack for this application // the problem is that the sparklr photos page doesn't remove the 'code=' request parameter. - ((OAuth2ClientContextImpl) context).setAuthorizationCode(null); + // ((OAuth2ClientContextImpl) context).setAuthorizationCode(null); } // clear any stored access tokens... tokenServices.removeToken(SecurityContextHolder.getContext().getAuthentication(), resource); diff --git a/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/mvc/FacebookController.java b/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/mvc/FacebookController.java index 148410cf0..fb92c4cf1 100644 --- a/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/mvc/FacebookController.java +++ b/samples/oauth2/tonr/src/main/java/org/springframework/security/oauth/examples/tonr/mvc/FacebookController.java @@ -9,7 +9,6 @@ import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.context.OAuth2ClientContext; import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextImpl; import org.springframework.security.oauth2.client.http.OAuth2AccessTokenRequiredException; import org.springframework.security.oauth2.client.provider.token.OAuth2ClientTokenServices; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; @@ -49,7 +48,7 @@ public String photos(Model model) throws Exception { if (context != null) { // this one is kind of a hack for this application // the problem is that the facebook friends page doesn't remove the 'code=' request parameter. - ((OAuth2ClientContextImpl) context).setAuthorizationCode(null); + // ((OAuth2ClientContext) context).setAuthorizationCode(null); } // clear any stored access tokens... tokenServices.removeToken(SecurityContextHolder.getContext().getAuthentication(), resource); diff --git a/samples/oauth2/tonr/src/main/webapp/WEB-INF/spring-servlet.xml b/samples/oauth2/tonr/src/main/webapp/WEB-INF/spring-servlet.xml index e44586da5..d35ae997b 100644 --- a/samples/oauth2/tonr/src/main/webapp/WEB-INF/spring-servlet.xml +++ b/samples/oauth2/tonr/src/main/webapp/WEB-INF/spring-servlet.xml @@ -38,18 +38,18 @@ - + - + - - + diff --git a/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/ServerRunning.java b/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/ServerRunning.java index e9ae9d368..ea841b95d 100755 --- a/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/ServerRunning.java +++ b/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/ServerRunning.java @@ -137,7 +137,7 @@ public Statement apply(Statement base, FrameworkMethod method, Object target) { HttpURLConnection.setFollowRedirects(false); boolean online = false; try { - client.getForEntity(new UriTemplate(getUrl("/sparklr/oauth/authorize")).toString(), String.class); + client.getForEntity(new UriTemplate(getUrl("/sparklr/login.jsp")).toString(), String.class); online = true; logger.info("Basic connectivity test passed"); } catch (RestClientException e) { diff --git a/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/TestClientConnections.java b/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/TestClientConnections.java index 3a5eccbbe..80eb38c19 100755 --- a/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/TestClientConnections.java +++ b/samples/oauth2/tonr/src/test/java/org/springframework/security/oauth/examples/tonr/TestClientConnections.java @@ -18,8 +18,9 @@ import org.springframework.http.HttpStatus; import org.springframework.security.oauth2.client.OAuth2RestTemplate; import org.springframework.security.oauth2.client.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.context.OAuth2ClientContext; import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; -import org.springframework.security.oauth2.client.filter.OAuth2ClientContextImpl; +import org.springframework.security.oauth2.client.provider.AccessTokenRequest; import org.springframework.security.oauth2.client.provider.grant.client.ClientCredentialsAccessTokenProvider; import org.springframework.security.oauth2.client.provider.grant.client.ClientCredentialsResourceDetails; import org.springframework.security.oauth2.client.provider.grant.code.AuthorizationCodeAccessTokenProvider; @@ -68,7 +69,7 @@ public void testCannotConnectWithoutToken() throws Exception { @Test public void testConnectWithToken() throws Exception { - OAuth2ClientContextImpl context = new OAuth2ClientContextImpl(); + OAuth2ClientContext context = new OAuth2ClientContext(); OAuth2ClientContextHolder.setContext(context); ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails(); @@ -79,7 +80,7 @@ public void testConnectWithToken() throws Exception { resource.setScope(Arrays.asList("trust")); ClientCredentialsAccessTokenProvider provider = new ClientCredentialsAccessTokenProvider(); - OAuth2AccessToken accessToken = provider.obtainNewAccessToken(resource); + OAuth2AccessToken accessToken = provider.obtainNewAccessToken(resource, new AccessTokenRequest()); context.setAccessTokens(Collections.singletonMap(resource.getId(), accessToken)); // TODO: should this work? The client id is different. @@ -105,22 +106,22 @@ public void testConnectWithAutomaticToken() throws Exception { public void testAttemptedTokenAcquisitionWithNoContext() throws Exception { AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider(); try { - OAuth2AccessToken token = provider.obtainNewAccessToken(resource); + OAuth2AccessToken token = provider.obtainNewAccessToken(resource, new AccessTokenRequest()); fail("Expected IllegalStateException"); assertNotNull(token); } catch (IllegalStateException e) { String message = e.getMessage(); assertTrue("Wrong message: " + message, - message.contains("No OAuth 2 security context has been established")); + message.contains("No redirect URI has been established")); } } @Test public void testAttemptedTokenAcquisitionWithWrongContext() throws Exception { - OAuth2ClientContextHolder.setContext(new OAuth2ClientContextImpl()); + OAuth2ClientContextHolder.setContext(new OAuth2ClientContext()); AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider(); try { - OAuth2AccessToken token = provider.obtainNewAccessToken(resource); + OAuth2AccessToken token = provider.obtainNewAccessToken(resource, new AccessTokenRequest()); fail("Expected IllegalStateException"); assertNotNull(token); } catch (IllegalStateException e) { @@ -148,14 +149,15 @@ public void testTokenAcquisitionWithCorrectContext() throws Exception { resource.setPreEstablishedRedirectUri("http://anywhere"); resource.setState("foo"); - OAuth2ClientContextImpl context = new OAuth2ClientContextImpl(); + OAuth2ClientContext context = new OAuth2ClientContext(); OAuth2ClientContextHolder.setContext(context); AuthorizationCodeAccessTokenProvider provider = new AuthorizationCodeAccessTokenProvider(); Map requestParams = new HashMap(); String uri = null; + AccessTokenRequest accessTokenRequest = new AccessTokenRequest(); try { - OAuth2AccessToken token = provider.obtainNewAccessToken(resource); + OAuth2AccessToken token = provider.obtainNewAccessToken(resource, accessTokenRequest); fail("Expected UserRedirectRequiredException"); assertNotNull(token); } catch (UserRedirectRequiredException e) { @@ -178,7 +180,7 @@ public void testTokenAcquisitionWithCorrectContext() throws Exception { .append(URLEncoder.encode(param.getValue(), "UTF-8")); appendChar = '&'; } - assertEquals(HttpStatus.FOUND, serverRunning.getStatusCode(builder.toString(), headers)); + assertEquals(HttpStatus.OK, serverRunning.getStatusCode(builder.toString(), headers)); form = new LinkedMultiValueMap(); form.add("user_oauth_approval", "true"); @@ -194,15 +196,15 @@ public void testTokenAcquisitionWithCorrectContext() throws Exception { assertNotNull(code); // Now the access token can be retrieved... - context.setAuthorizationCode(code); + accessTokenRequest.setAuthorizationCode(code); // TODO: unhack the state (should be autogenerated) - context.setPreservedState("foo"); - OAuth2AccessToken token = provider.obtainNewAccessToken(resource); + accessTokenRequest.setPreservedState("foo"); + OAuth2AccessToken token = provider.obtainNewAccessToken(resource, accessTokenRequest); assertNotNull(token); } - public String extractParameter(String location, String key) { + private String extractParameter(String location, String key) { location = location.substring(location.indexOf("?") + 1); for (String query : location.split("&")) { String[] keyValue = query.split("="); diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/context/OAuth2ClientContext.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/context/OAuth2ClientContext.java index 8dfeefad0..94766d35c 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/context/OAuth2ClientContext.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/context/OAuth2ClientContext.java @@ -1,58 +1,25 @@ package org.springframework.security.oauth2.client.context; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; -import org.springframework.security.oauth2.common.OAuth2AccessToken; - import java.util.Map; +import org.springframework.security.oauth2.common.OAuth2AccessToken; + /** * The OAuth 2 security context (for a specific user). * * @author Ryan Heaton + * @author Dave Syer */ -public interface OAuth2ClientContext { - - /** - * Get the access tokens for the current context. - * - * @return The access tokens for the current context. The key to the map is the {@link OAuth2ProtectedResourceDetails#getId() id of the protected resource} - * for which the access token is valid. - */ - Map getAccessTokens(); - - /** - * Get the state that has been preserved for the current context. - * - * @return the state that has been preserved for the current context. - */ - Object getPreservedState(); +public class OAuth2ClientContext { - /** - * The URI to which a user is to be redirected after authorizing an access token request for this context. - * - * @return The URI to which a user is to be redirected after authorizing an access token request for this context. - */ - String getUserAuthorizationRedirectUri(); + private Map accessTokens; - /** - * The authorization code for this context. - * - * @return The authorization code, or null if none. - */ - // TODO: this is an implementation detail (only for authorization code grant types)? - String getAuthorizationCode(); + public Map getAccessTokens() { + return accessTokens; + } - /** - * Any details for this security this context. - * - * @return Any details for this security context. - */ - Object getDetails(); + public void setAccessTokens(Map accessTokens) { + this.accessTokens = accessTokens; + } - /** - * The error parameters associated with this context. - * - * @return The error parameters associated with this context. - */ - Map getErrorParameters(); } diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextFilter.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextFilter.java index c21a4eb2c..ae51da35d 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextFilter.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextFilter.java @@ -24,6 +24,7 @@ import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.security.oauth2.client.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.context.OAuth2ClientContext; import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; import org.springframework.security.oauth2.client.filter.flash.ClientTokenFlashServices; import org.springframework.security.oauth2.client.filter.flash.HttpSessionClientTokenFlashServices; @@ -31,6 +32,7 @@ import org.springframework.security.oauth2.client.filter.state.StateServices; import org.springframework.security.oauth2.client.http.OAuth2AccessDeniedException; import org.springframework.security.oauth2.client.http.OAuth2AccessTokenRequiredException; +import org.springframework.security.oauth2.client.provider.AccessTokenRequest; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenProvider; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenProviderChain; import org.springframework.security.oauth2.client.provider.grant.client.ClientCredentialsAccessTokenProvider; @@ -55,14 +57,21 @@ public class OAuth2ClientContextFilter implements Filter, InitializingBean, MessageSourceAware { protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + private OAuth2AccessTokenProvider accessTokenProvider = new OAuth2AccessTokenProviderChain( Arrays. asList(new AuthorizationCodeAccessTokenProvider(), new ClientCredentialsAccessTokenProvider())); + private ClientTokenFlashServices rememberMeServices = new HttpSessionClientTokenFlashServices(); + private StateServices stateServices = new HttpSessionStateServices(); + private PortResolver portResolver = new PortResolverImpl(); + private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private boolean redirectOnError = false; public void afterPropertiesSet() throws Exception { @@ -76,48 +85,45 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // first set up the security context. - OAuth2ClientContextImpl oauth2Context = new OAuth2ClientContextImpl(); - oauth2Context.setDetails(request); + OAuth2ClientContext oauth2Context = new OAuth2ClientContext(); Map accessTokens = rememberMeServices.loadRememberedTokens(request, response); - // Ensure session is created if necessary. TODO: find a better way to do this + // Ensure session is created if necessary. TODO: find a better way to do this rememberMeServices.rememberTokens(accessTokens, request, response); accessTokens = accessTokens == null ? new HashMap() : new HashMap(accessTokens); oauth2Context.setAccessTokens(Collections.unmodifiableMap(accessTokens)); - if (request.getParameter("error") != null) { - HashMap errorParams = new HashMap(); - Enumeration parameterNames = request.getParameterNames(); - while (parameterNames.hasMoreElements()) { - String param = (String) parameterNames.nextElement(); - errorParams.put(param, request.getParameter(param)); - } - oauth2Context.setErrorParameters(errorParams); - } - oauth2Context.setAuthorizationCode(request.getParameter("code")); - oauth2Context.setUserAuthorizationRedirectUri(calculateCurrentUri(request)); - oauth2Context.setPreservedState(stateServices.loadPreservedState(request.getParameter("state"), request, - response)); OAuth2ClientContextHolder.setContext(oauth2Context); try { try { chain.doFilter(servletRequest, servletResponse); - } catch (Exception ex) { + } + catch (Exception ex) { + OAuth2ProtectedResourceDetails resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(ex); String neededResourceId = resourceThatNeedsAuthorization.getId(); accessTokens.remove(neededResourceId); + @SuppressWarnings("unchecked") + Map parameters = (Map) request.getParameterMap(); + AccessTokenRequest accessTokenRequest = new AccessTokenRequest(parameters); + accessTokenRequest.setUserAuthorizationRedirectUri(calculateCurrentUri(request)); + accessTokenRequest.setPreservedState(String.valueOf(stateServices.loadPreservedState( + request.getParameter("state"), request, response))); + while (!accessTokens.containsKey(neededResourceId)) { OAuth2AccessToken accessToken; try { - accessToken = accessTokenProvider.obtainNewAccessToken(resourceThatNeedsAuthorization); + accessToken = accessTokenProvider.obtainNewAccessToken(resourceThatNeedsAuthorization, + accessTokenRequest); if (accessToken == null) { throw new IllegalStateException( "Access token manager returned a null access token, which is illegal according to the contract."); } - } catch (UserRedirectRequiredException e) { + } + catch (UserRedirectRequiredException e) { redirectUser(e, request, response); return; } @@ -128,7 +134,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo // try again if (!response.isCommitted() && !this.redirectOnError) { chain.doFilter(request, response); - } else { + } + else { // Dang. what do we do now? Best we can do is redirect. String redirect = request.getServletPath(); if (request.getQueryString() != null) { @@ -136,14 +143,16 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } this.redirectStrategy.sendRedirect(request, response, redirect); } - } catch (Exception e1) { + } + catch (Exception e1) { resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(e1); neededResourceId = resourceThatNeedsAuthorization.getId(); accessTokens.remove(neededResourceId); } } } - } finally { + } + finally { OAuth2ClientContextHolder.clearContext(); rememberMeServices.rememberTokens(accessTokens, request, response); } @@ -175,7 +184,8 @@ protected void redirectUser(UserRedirectRequiredException e, HttpServletRequest request.setAttribute("org.springframework.security.oauth2.client.UserRedirectRequiredException", e); this.redirectStrategy.sendRedirect(request, response, builder.toString()); - } catch (UnsupportedEncodingException uee) { + } + catch (UnsupportedEncodingException uee) { throw new IllegalStateException(uee); } } @@ -198,14 +208,16 @@ protected OAuth2ProtectedResourceDetails checkForResourceThatNeedsAuthorization( if (resourceThatNeedsAuthorization == null) { throw new OAuth2AccessDeniedException(ase.getMessage()); } - } else { + } + else { // Rethrow ServletExceptions and RuntimeExceptions as-is if (ex instanceof ServletException) { throw (ServletException) ex; } if (ex instanceof IOException) { throw (IOException) ex; - } else if (ex instanceof RuntimeException) { + } + else if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } @@ -223,14 +235,16 @@ protected OAuth2ProtectedResourceDetails checkForResourceThatNeedsAuthorization( */ protected String calculateCurrentUri(HttpServletRequest request) throws UnsupportedEncodingException { StringBuilder queryBuilder = new StringBuilder(); - Enumeration paramNames = request.getParameterNames(); + @SuppressWarnings("unchecked") + Enumeration paramNames = request.getParameterNames(); while (paramNames.hasMoreElements()) { String name = (String) paramNames.nextElement(); if (!"code".equals(name)) { String[] parameterValues = request.getParameterValues(name); if (parameterValues.length == 0) { queryBuilder.append(URLEncoder.encode(name, "UTF-8")); - } else { + } + else { for (int i = 0; i < parameterValues.length; i++) { String parameterValue = parameterValues[i]; queryBuilder.append(URLEncoder.encode(name, "UTF-8")).append('=') @@ -247,9 +261,9 @@ protected String calculateCurrentUri(HttpServletRequest request) throws Unsuppor } } - return UrlUtils.buildFullRequestUrl(request.getScheme(), request.getServerName(), portResolver - .getServerPort(request), request.getRequestURI(), queryBuilder.length() > 0 ? queryBuilder.toString() - : null); + return UrlUtils.buildFullRequestUrl(request.getScheme(), request.getServerName(), + portResolver.getServerPort(request), request.getRequestURI(), + queryBuilder.length() > 0 ? queryBuilder.toString() : null); } public void init(FilterConfig filterConfig) throws ServletException { diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextImpl.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextImpl.java deleted file mode 100644 index d24a1d21c..000000000 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/filter/OAuth2ClientContextImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.springframework.security.oauth2.client.filter; - -import org.springframework.security.oauth2.client.context.OAuth2ClientContext; -import org.springframework.security.oauth2.common.OAuth2AccessToken; - -import java.util.Map; - -/** - * @author Ryan Heaton - */ -public class OAuth2ClientContextImpl implements OAuth2ClientContext { - - private Map accessTokens; - private Object preservedState; - private String userAuthorizationRedirectUri; - private Map errorParameters; - private String authorizationCode; - private Object details; - - public Map getAccessTokens() { - return accessTokens; - } - - public void setAccessTokens(Map accessTokens) { - this.accessTokens = accessTokens; - } - - public Object getPreservedState() { - return preservedState; - } - - public void setPreservedState(Object preservedState) { - this.preservedState = preservedState; - } - - public String getUserAuthorizationRedirectUri() { - return userAuthorizationRedirectUri; - } - - public void setUserAuthorizationRedirectUri(String userAuthorizationRedirectUri) { - this.userAuthorizationRedirectUri = userAuthorizationRedirectUri; - } - - public Map getErrorParameters() { - return errorParameters; - } - - public void setErrorParameters(Map errorParameters) { - this.errorParameters = errorParameters; - } - - public String getAuthorizationCode() { - return authorizationCode; - } - - public void setAuthorizationCode(String authorizationCode) { - this.authorizationCode = authorizationCode; - } - - public Object getDetails() { - return details; - } - - public void setDetails(Object details) { - this.details = details; - } -} diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ClientHttpRequestFactory.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ClientHttpRequestFactory.java index 25518fab0..69a700cba 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ClientHttpRequestFactory.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ClientHttpRequestFactory.java @@ -61,13 +61,13 @@ public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IO } if (OAuth2AccessToken.BEARER_TYPE.equalsIgnoreCase(tokenType) || OAuth2AccessToken.OAUTH2_TYPE.equalsIgnoreCase(tokenType)) { - OAuth2ProtectedResourceDetails.BearerTokenMethod bearerTokenMethod = resource.getBearerTokenMethod(); - if (OAuth2ProtectedResourceDetails.BearerTokenMethod.query.equals(bearerTokenMethod)) { + OAuth2ProtectedResourceDetails.AuthenticationScheme bearerTokenMethod = resource.getAuthenticationScheme(); + if (OAuth2ProtectedResourceDetails.AuthenticationScheme.query.equals(bearerTokenMethod)) { uri = appendQueryParameter(uri, accessToken); } ClientHttpRequest req = delegate.createRequest(uri, httpMethod); - if (OAuth2ProtectedResourceDetails.BearerTokenMethod.header.equals(bearerTokenMethod)) { + if (OAuth2ProtectedResourceDetails.AuthenticationScheme.header.equals(bearerTokenMethod)) { req.getHeaders().add("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, accessToken.getValue())); } @@ -84,7 +84,7 @@ protected URI appendQueryParameter(URI uri, OAuth2AccessToken accessToken) { // TODO: there is some duplication with UriUtils here. Probably unavoidable as long as this // method signature uses URI not String. String query = uri.getQuery(); - String queryFragment = resource.getBearerTokenName() + "=" + String queryFragment = resource.getTokenName() + "=" + URLEncoder.encode(accessToken.getValue(), "UTF-8"); if (query == null) { query = queryFragment; diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AbstractOAuth2AccessTokenProvider.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AbstractOAuth2AccessTokenProvider.java index b0c97d8ce..cadc895c4 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AbstractOAuth2AccessTokenProvider.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AbstractOAuth2AccessTokenProvider.java @@ -30,7 +30,7 @@ public void afterPropertiesSet() throws Exception { Assert.notNull(tokenServices, "OAuth2 token services is required."); } - public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails resource) + public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails resource, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { OAuth2AccessToken accessToken = null; Authentication auth = SecurityContextHolder.getContext().getAuthentication(); @@ -53,7 +53,7 @@ public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails res if (accessToken == null) { // looks like we need to try to obtain a new token. - accessToken = obtainNewAccessTokenInternal(resource); + accessToken = obtainNewAccessTokenInternal(resource, request); if (accessToken == null) { throw new IllegalStateException("An OAuth 2 access token must be obtained or an exception thrown."); @@ -78,7 +78,7 @@ public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails res * @param resource the resource that we need the token for * @return a token or null */ - abstract protected OAuth2AccessToken obtainNewAccessTokenInternal(OAuth2ProtectedResourceDetails resource) throws UserRedirectRequiredException, AccessDeniedException; + abstract protected OAuth2AccessToken obtainNewAccessTokenInternal(OAuth2ProtectedResourceDetails resource, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException; /** * Obtain a new access token for the specified resource using the refresh token. diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AccessTokenRequest.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AccessTokenRequest.java new file mode 100644 index 000000000..31ea23858 --- /dev/null +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/AccessTokenRequest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2002-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.client.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +/** + * @author Dave Syer + * + */ +public class AccessTokenRequest implements MultiValueMap { + + private final MultiValueMap parameters; + + public AccessTokenRequest() { + this(new LinkedMultiValueMap()); + } + + public AccessTokenRequest(LinkedMultiValueMap parameters) { + this.parameters = parameters; + } + + public AccessTokenRequest(Map parameters) { + this(); + for (String key : parameters.keySet()) { + List values = new ArrayList(); + for (String value : parameters.get(key)) { + values.add(value); + } + this.parameters.put(key, values); + } + } + + public boolean isError() { + return parameters.containsKey("error"); + } + + /** + * Get the state that has been preserved for the current context. + * + * @return the state that has been preserved for the current context. + */ + public String getPreservedState() { + return getFirst("state"); + } + + public void setPreservedState(String state) { + parameters.set("state", state); + } + + /** + * The URI to which a user is to be redirected after authorizing an access token request for this context. + * + * @return The URI to which a user is to be redirected after authorizing an access token request for this context. + */ + public String getUserAuthorizationRedirectUri() { + return getFirst("redirect_uri"); + } + + public void setUserAuthorizationRedirectUri(String uri) { + parameters.set("redirect_uri", uri); + } + + /** + * The authorization code for this context. + * + * @return The authorization code, or null if none. + */ + public String getAuthorizationCode() { + return getFirst("code"); + } + + public void setAuthorizationCode(String code) { + parameters.set("code", code); + } + + public String getFirst(String key) { + return parameters.getFirst(key); + } + + public void add(String key, String value) { + parameters.add(key, value); + } + + public void set(String key, String value) { + parameters.set(key, value); + } + + public void setAll(Map values) { + parameters.setAll(values); + } + + public Map toSingleValueMap() { + return parameters.toSingleValueMap(); + } + + public int size() { + return parameters.size(); + } + + public boolean isEmpty() { + return parameters.isEmpty(); + } + + public boolean containsKey(Object key) { + return parameters.containsKey(key); + } + + public boolean containsValue(Object value) { + return parameters.containsValue(value); + } + + public List get(Object key) { + return parameters.get(key); + } + + public List put(String key, List value) { + return parameters.put(key, value); + } + + public List remove(Object key) { + return parameters.remove(key); + } + + public void putAll(Map> m) { + parameters.putAll(m); + } + + public void clear() { + parameters.clear(); + } + + public Set keySet() { + return parameters.keySet(); + } + + public Collection> values() { + return parameters.values(); + } + + public Set>> entrySet() { + return parameters.entrySet(); + } + + public boolean equals(Object o) { + return parameters.equals(o); + } + + public int hashCode() { + return parameters.hashCode(); + } + +} diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProvider.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProvider.java index cbde2288f..ac1674609 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProvider.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProvider.java @@ -1,5 +1,7 @@ package org.springframework.security.oauth2.client.provider; +import java.util.Map; + import org.springframework.security.access.AccessDeniedException; import org.springframework.security.oauth2.client.UserRedirectRequiredException; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; @@ -16,11 +18,12 @@ public interface OAuth2AccessTokenProvider { * Obtain a new access token for the specified protected resource. * * @param details The protected resource for which this provider is to obtain an access token. + * @param parameters The parameters of the request giving context for the token details if any. * @return The access token for the specified protected resource. The return value may NOT be null. * @throws UserRedirectRequiredException If the provider requires the current user to be redirected for authorization. * @throws AccessDeniedException If the user denies access to the protected resource. */ - public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details) throws UserRedirectRequiredException, AccessDeniedException; + public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest parameters) throws UserRedirectRequiredException, AccessDeniedException; /** * Whether this provider supports the specified resource. diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProviderChain.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProviderChain.java index dbc9c6cfd..824e496c5 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProviderChain.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenProviderChain.java @@ -27,11 +27,11 @@ public OAuth2AccessTokenProviderChain(List chain) { } @Override - public OAuth2AccessToken obtainNewAccessTokenInternal(OAuth2ProtectedResourceDetails details) + public OAuth2AccessToken obtainNewAccessTokenInternal(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { for (OAuth2AccessTokenProvider tokenProvider : chain) { if (tokenProvider.supportsResource(details)) { - return tokenProvider.obtainNewAccessToken(details); + return tokenProvider.obtainNewAccessToken(details, request); } } diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenSupport.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenSupport.java index c887df0e5..2325cc67f 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenSupport.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/OAuth2AccessTokenSupport.java @@ -1,8 +1,8 @@ package org.springframework.security.oauth2.client.provider; import java.io.IOException; +import java.net.HttpURLConnection; import java.util.Arrays; -import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -13,19 +13,21 @@ import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpRequest; import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.FormHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; +import org.springframework.security.oauth2.client.http.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.provider.auth.ClientAuthenticationHandler; +import org.springframework.security.oauth2.client.provider.auth.DefaultClientAuthenticationHandler; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.security.oauth2.common.DefaultOAuth2SerializationService; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2SerializationService; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.exceptions.SerializationException; -import org.springframework.security.oauth2.client.http.OAuth2AccessDeniedException; -import org.springframework.security.oauth2.client.provider.auth.ClientAuthenticationHandler; -import org.springframework.security.oauth2.client.provider.auth.DefaultClientAuthenticationHandler; -import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import org.springframework.util.Assert; import org.springframework.util.MultiValueMap; import org.springframework.web.client.DefaultResponseErrorHandler; @@ -45,16 +47,26 @@ public abstract class OAuth2AccessTokenSupport implements InitializingBean { protected final Log logger = LogFactory.getLog(getClass()); public static final MediaType FORM_MEDIA_TYPE = new MediaType("application", "x-www-form-urlencoded"); + public static final MediaType JSON_MEDIA_TYPE = new MediaType("application", "json"); + private static final FormHttpMessageConverter FORM_MESSAGE_CONVERTER = new FormHttpMessageConverter(); private final RestTemplate restTemplate; + private OAuth2SerializationService serializationService = new DefaultOAuth2SerializationService(); + private ClientAuthenticationHandler authenticationHandler = new DefaultClientAuthenticationHandler(); protected OAuth2AccessTokenSupport() { this.restTemplate = new RestTemplate(); this.restTemplate.setErrorHandler(new AccessTokenErrorHandler()); + this.restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() { + @Override + protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException { + connection.setInstanceFollowRedirects(false); + } + }); } public void afterPropertiesSet() throws Exception { @@ -95,27 +107,35 @@ protected OAuth2AccessToken retrieveToken(MultiValueMap form, return getRestTemplate().execute( accessTokenUri, HttpMethod.POST, - new OAuth2AuthTokenCallback(form, resource), - new HttpMessageConverterExtractor(OAuth2AccessToken.class, (List) Arrays - .asList(new OAuth2AccessTokenMessageConverter()))); + getRequestCallback(form, resource), + new HttpMessageConverterExtractor(OAuth2AccessToken.class, Arrays + .> asList(new OAuth2AccessTokenMessageConverter()))); - } catch (OAuth2Exception oe) { + } + catch (OAuth2Exception oe) { throw new OAuth2AccessDeniedException("Access token denied.", resource, oe); - } catch (RestClientException rce) { + } + catch (RestClientException rce) { throw new OAuth2AccessDeniedException("Error requesting access token.", resource, rce); } } + protected RequestCallback getRequestCallback(MultiValueMap form, + OAuth2ProtectedResourceDetails resource) { + return new OAuth2AuthTokenCallback(form, resource); + } + /** * Request callback implementation that writes the given object to the request stream. */ private class OAuth2AuthTokenCallback implements RequestCallback { private final MultiValueMap form; + private final OAuth2ProtectedResourceDetails resource; private OAuth2AuthTokenCallback(MultiValueMap form, OAuth2ProtectedResourceDetails resource) { @@ -140,12 +160,14 @@ public void handleError(ClientHttpResponse response) throws IOException { if (JSON_MEDIA_TYPE.includes(contentType)) { try { throw getSerializationService().deserializeJsonError(response.getBody()); - } catch (SerializationException e) { + } + catch (SerializationException e) { throw new OAuth2Exception( "Error getting the access token, and unable to read the details of the error in the JSON response.", e); } - } else if (FORM_MEDIA_TYPE.includes(contentType)) { + } + else if (FORM_MEDIA_TYPE.includes(contentType)) { MultiValueMap map = FORM_MESSAGE_CONVERTER.read(null, response); throw getSerializationService().deserializeError(map.toSingleValueMap()); } @@ -174,12 +196,14 @@ protected OAuth2AccessToken readInternal(Class claz if (contentType != null && JSON_MEDIA_TYPE.includes(contentType)) { try { return getSerializationService().deserializeJsonAccessToken(response.getBody()); - } catch (SerializationException e) { + } + catch (SerializationException e) { throw new OAuth2Exception( "Error getting the access token, and unable to read the details of the error in the JSON response.", e); } - } else { + } + else { // the spec currently says json is required, but facebook, for example, still returns form-encoded. MultiValueMap map = FORM_MESSAGE_CONVERTER.read(null, response); return getSerializationService().deserializeAccessToken(map.toSingleValueMap()); diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/auth/DefaultClientAuthenticationHandler.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/auth/DefaultClientAuthenticationHandler.java index 0ee70d091..4c929ef7c 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/auth/DefaultClientAuthenticationHandler.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/auth/DefaultClientAuthenticationHandler.java @@ -27,7 +27,7 @@ public void authenticateTokenRequest(OAuth2ProtectedResourceDetails resource, Mu try { String clientSecret = resource.getClientSecret(); - clientSecret = clientSecret==null ? "" : clientSecret; + clientSecret = clientSecret == null ? "" : clientSecret; switch (scheme) { case http_basic: form.remove("client_id"); @@ -49,7 +49,8 @@ public void authenticateTokenRequest(OAuth2ProtectedResourceDetails resource, Mu throw new IllegalStateException( "Default authentication handler doesn't know how to handle scheme: " + scheme); } - } catch (UnsupportedEncodingException e) { + } + catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsAccessTokenProvider.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsAccessTokenProvider.java index 5150e84e4..341ee78f5 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsAccessTokenProvider.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsAccessTokenProvider.java @@ -5,8 +5,7 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.oauth2.client.UserRedirectRequiredException; -import org.springframework.security.oauth2.client.context.OAuth2ClientContext; -import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; +import org.springframework.security.oauth2.client.provider.AccessTokenRequest; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenProvider; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenSupport; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; @@ -26,27 +25,25 @@ public boolean supportsResource(OAuth2ProtectedResourceDetails resource) { && "client_credentials".equals(resource.getGrantType()); } - public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details) + public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { ClientCredentialsResourceDetails resource = (ClientCredentialsResourceDetails) details; - OAuth2ClientContext context = OAuth2ClientContextHolder.getContext(); - if (context != null && context.getErrorParameters() != null) { + if (request.isError()) { // there was an oauth error... - throw getSerializationService().deserializeError(context.getErrorParameters()); + throw getSerializationService().deserializeError(request.toSingleValueMap()); } else { - return retrieveToken(getParametersForTokenRequest(resource, context), resource); + return retrieveToken(getParametersForTokenRequest(resource), resource); } } - private MultiValueMap getParametersForTokenRequest(ClientCredentialsResourceDetails resource, - OAuth2ClientContext context) { + private MultiValueMap getParametersForTokenRequest(ClientCredentialsResourceDetails resource) { MultiValueMap form = new LinkedMultiValueMap(); form.add("grant_type", "client_credentials"); diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsResourceDetails.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsResourceDetails.java index 417efe06f..239ba04d9 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsResourceDetails.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/client/ClientCredentialsResourceDetails.java @@ -6,5 +6,9 @@ * @author Dave Syer */ public class ClientCredentialsResourceDetails extends BaseOAuth2ProtectedResourceDetails { + + public ClientCredentialsResourceDetails() { + setGrantType("client_credentials"); + } } diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/code/AuthorizationCodeAccessTokenProvider.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/code/AuthorizationCodeAccessTokenProvider.java index 0c8993d29..5bf332b31 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/code/AuthorizationCodeAccessTokenProvider.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/code/AuthorizationCodeAccessTokenProvider.java @@ -5,13 +5,12 @@ import java.util.TreeMap; import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.client.*; -import org.springframework.security.oauth2.client.context.OAuth2ClientContext; -import org.springframework.security.oauth2.client.context.OAuth2ClientContextHolder; +import org.springframework.security.oauth2.client.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.provider.AccessTokenRequest; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenProvider; import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenSupport; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -28,49 +27,48 @@ public boolean supportsResource(OAuth2ProtectedResourceDetails resource) { && "authorization_code".equals(resource.getGrantType()); } - public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details) + public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) throws UserRedirectRequiredException, AccessDeniedException { AuthorizationCodeResourceDetails resource = (AuthorizationCodeResourceDetails) details; - OAuth2ClientContext context = OAuth2ClientContextHolder.getContext(); - if (context != null && context.getErrorParameters() != null) { + if (request.isError()) { // there was an oauth error... - throw getSerializationService().deserializeError(context.getErrorParameters()); + throw getSerializationService().deserializeError(request.toSingleValueMap()); - } else if (context==null || context.getAuthorizationCode() == null) { + } else if (request.getAuthorizationCode() == null) { - throw getRedirectForAuthorization(resource, context); + throw getRedirectForAuthorization(resource, request); } else { - return retrieveToken(getParametersForTokenRequest(resource, context), resource); + return retrieveToken(getParametersForTokenRequest(resource, request), resource); } } private MultiValueMap getParametersForTokenRequest(AuthorizationCodeResourceDetails resource, - OAuth2ClientContext context) { + AccessTokenRequest request) { MultiValueMap form = new LinkedMultiValueMap(); form.add("grant_type", "authorization_code"); - form.add("code", context.getAuthorizationCode()); + form.add("code", request.getAuthorizationCode()); String redirectUri = resource.getPreEstablishedRedirectUri(); - if (context!=null && redirectUri == null) { + if (request!=null && redirectUri == null) { // no pre-established redirect uri: use the preserved state // TODO: treat redirect URI as a special kind of state (this is a historical mini hack) - redirectUri = String.valueOf(context.getPreservedState()); + redirectUri = String.valueOf(request.getPreservedState()); } else { // TODO: the state key is what should be sent, not the value - form.add("state", String.valueOf(context.getPreservedState())); + form.add("state", String.valueOf(request.getPreservedState())); } if (redirectUri == null) { // still no redirect uri? just try the one for the current context... - redirectUri = context == null ? null : context.getUserAuthorizationRedirectUri(); + redirectUri = request == null ? null : request.getUserAuthorizationRedirectUri(); } form.add("redirect_uri", redirectUri); @@ -80,7 +78,7 @@ private MultiValueMap getParametersForTokenRequest(Authorization } private UserRedirectRequiredException getRedirectForAuthorization(AuthorizationCodeResourceDetails resource, - OAuth2ClientContext context) { + AccessTokenRequest request) { // we don't have an authorization code yet. So first get that. TreeMap requestParameters = new TreeMap(); @@ -91,14 +89,14 @@ private UserRedirectRequiredException getRedirectForAuthorization(AuthorizationC String redirectUri = resource.getPreEstablishedRedirectUri(); if (redirectUri == null) { - if (context == null) { + if (request == null) { throw new IllegalStateException( - "No OAuth 2 security context has been established: unable to determine the redirect URI for the current context."); + "Unable to determine the redirect URI for the current request."); } - redirectUri = context.getUserAuthorizationRedirectUri(); + redirectUri = request.getUserAuthorizationRedirectUri(); if (redirectUri == null) { throw new IllegalStateException( - "No redirect URI has been established for the current OAuth 2 security context."); + "No redirect URI has been established for the current request."); } requestParameters.put("redirect_uri", redirectUri); diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitAccessTokenProvider.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitAccessTokenProvider.java new file mode 100644 index 000000000..a4a905476 --- /dev/null +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitAccessTokenProvider.java @@ -0,0 +1,170 @@ +package org.springframework.security.oauth2.client.provider.grant.implicit; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpRequest; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.oauth2.client.UserRedirectRequiredException; +import org.springframework.security.oauth2.client.http.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.provider.AccessTokenRequest; +import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenProvider; +import org.springframework.security.oauth2.client.provider.OAuth2AccessTokenSupport; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RequestCallback; +import org.springframework.web.client.ResponseExtractor; +import org.springframework.web.client.RestClientException; +import org.springframework.web.util.UriTemplate; + +/** + * Provider for obtaining an oauth2 access token by using implicit grant. + * + * @author Dave Syer + */ +public class ImplicitAccessTokenProvider extends OAuth2AccessTokenSupport implements OAuth2AccessTokenProvider { + + public boolean supportsResource(OAuth2ProtectedResourceDetails resource) { + return resource instanceof ImplicitResourceDetails && "implicit".equals(resource.getGrantType()); + } + + public OAuth2AccessToken obtainNewAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request) + throws UserRedirectRequiredException, AccessDeniedException { + + ImplicitResourceDetails resource = (ImplicitResourceDetails) details; + + if (request.isError()) { + // there was an oauth error... + throw getSerializationService().deserializeError(request.toSingleValueMap()); + } + else { + return retrieveToken(getParametersForTokenRequest(resource, request), resource); + } + + } + + protected OAuth2AccessToken retrieveToken(MultiValueMap form, + OAuth2ProtectedResourceDetails resource) { + + try { + String accessTokenUri = resource.getAccessTokenUri(); + + if (logger.isDebugEnabled()) { + logger.debug("Retrieving token from " + accessTokenUri); + } + + getAuthenticationHandler().authenticateTokenRequest( + resource, + form, + new SimpleClientHttpRequestFactory().createRequest(new UriTemplate(accessTokenUri).expand(), + HttpMethod.GET)); + + return getRestTemplate().execute(appendQueryParams(accessTokenUri, form, resource), HttpMethod.GET, + new ImplicitTokenRequestCallback(resource), new ImplicitResponseExtractor(), + form.toSingleValueMap()); + + } + catch (OAuth2Exception oe) { + throw new OAuth2AccessDeniedException("Access token denied.", resource, oe); + } + catch (RestClientException e) { + throw new OAuth2AccessDeniedException("Error requesting access token.", resource, e); + } + catch (IOException e) { + throw new OAuth2AccessDeniedException("Unexpected error requesting access token.", resource, e); + } + + } + + private String appendQueryParams(String accessTokenUri, MultiValueMap form, + OAuth2ProtectedResourceDetails resource) { + StringBuilder builder = new StringBuilder(accessTokenUri); + String separator = "?"; + if (accessTokenUri.contains("?")) { + separator = "&"; + } + for (String key : form.keySet()) { + builder.append(separator); + builder.append(key + "={" + key + "}"); + separator = "&"; + } + return builder.toString(); + } + + private MultiValueMap getParametersForTokenRequest(ImplicitResourceDetails resource, + AccessTokenRequest request) { + + MultiValueMap form = new LinkedMultiValueMap(); + form.add("response_type", "token"); + + if (resource.isScoped()) { + + StringBuilder builder = new StringBuilder(); + List scope = resource.getScope(); + + if (scope != null) { + Iterator scopeIt = scope.iterator(); + while (scopeIt.hasNext()) { + builder.append(scopeIt.next()); + if (scopeIt.hasNext()) { + builder.append(' '); + } + } + } + + form.add("scope", builder.toString()); + } + + for (String key : request.keySet()) { + form.put(key, request.get(key)); + } + + if (request.getUserAuthorizationRedirectUri() == null && resource.getPreEstablishedRedirectUri() != null) { + form.set("redirect_uri", resource.getPreEstablishedRedirectUri()); + } + + return form; + + } + + private class ImplicitTokenRequestCallback implements RequestCallback { + + private final OAuth2ProtectedResourceDetails resource; + + private ImplicitTokenRequestCallback(OAuth2ProtectedResourceDetails resource) { + this.resource = resource; + } + + public void doWithRequest(ClientHttpRequest request) throws IOException { + getAuthenticationHandler().authenticateTokenRequest(this.resource, + new LinkedMultiValueMap(), request); + request.getHeaders().setAccept(Arrays.asList(JSON_MEDIA_TYPE, FORM_MEDIA_TYPE)); + } + + } + + private final class ImplicitResponseExtractor implements ResponseExtractor { + public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException { + String fragment = response.getHeaders().getLocation().getFragment(); + Map map = new HashMap(); + Properties properties = StringUtils.splitArrayElementsIntoProperties(StringUtils.split(fragment, "&"), "="); + for (Object key : properties.keySet()) { + map.put(key.toString(), properties.get(key).toString()); + } + return getSerializationService().deserializeAccessToken(map); + } + } + +} diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitResourceDetails.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitResourceDetails.java new file mode 100644 index 000000000..735d8131e --- /dev/null +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/provider/grant/implicit/ImplicitResourceDetails.java @@ -0,0 +1,36 @@ +package org.springframework.security.oauth2.client.provider.grant.implicit; + +import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; + +/** + * @author Dave Syer + */ +public class ImplicitResourceDetails extends BaseOAuth2ProtectedResourceDetails { + + private String preEstablishedRedirectUri; + + public ImplicitResourceDetails() { + setGrantType("implicit"); + } + + /** + * The redirect URI that has been pre-established with the server. If present, the redirect URI will be omitted from + * the user authorization request because the server doesn't need to know it. + * + * @return The redirect URI that has been pre-established with the server. + */ + public String getPreEstablishedRedirectUri() { + return preEstablishedRedirectUri; + } + + /** + * The redirect URI that has been pre-established with the server. If present, the redirect URI will be omitted from + * the user authorization request because the server doesn't need to know it. + * + * @param preEstablishedRedirectUri The redirect URI that has been pre-established with the server. + */ + public void setPreEstablishedRedirectUri(String preEstablishedRedirectUri) { + this.preEstablishedRedirectUri = preEstablishedRedirectUri; + } + +} diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/BaseOAuth2ProtectedResourceDetails.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/BaseOAuth2ProtectedResourceDetails.java index 7485fb907..2dfc5112c 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/BaseOAuth2ProtectedResourceDetails.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/BaseOAuth2ProtectedResourceDetails.java @@ -7,6 +7,7 @@ /** * @author Ryan Heaton + * @author Dave Syer */ public class BaseOAuth2ProtectedResourceDetails implements OAuth2ProtectedResourceDetails { @@ -17,8 +18,8 @@ public class BaseOAuth2ProtectedResourceDetails implements OAuth2ProtectedResour private List scope; private String clientSecret; private String clientAuthenticationScheme = ClientAuthenticationScheme.http_basic.toString(); - private BearerTokenMethod bearerTokenMethod = BearerTokenMethod.header; - private String bearerTokenName = OAuth2AccessToken.BEARER_TYPE_PARAMETER; + private AuthenticationScheme authorizationScheme = AuthenticationScheme.header; + private String tokenName = OAuth2AccessToken.BEARER_TYPE_PARAMETER; public String getId() { return id; @@ -72,24 +73,25 @@ public String getClientAuthenticationScheme() { return clientAuthenticationScheme; } + // TODO: use an enum (share with #setAuthenticationScheme()) public void setClientAuthenticationScheme(String clientAuthenticationScheme) { this.clientAuthenticationScheme = clientAuthenticationScheme; } - public BearerTokenMethod getBearerTokenMethod() { - return bearerTokenMethod; + public AuthenticationScheme getAuthenticationScheme() { + return authorizationScheme; } - public void setBearerTokenMethod(BearerTokenMethod bearerTokenMethod) { - this.bearerTokenMethod = bearerTokenMethod; + public void setAuthenticationScheme(AuthenticationScheme authorizationScheme) { + this.authorizationScheme = authorizationScheme; } - public String getBearerTokenName() { - return bearerTokenName; + public String getTokenName() { + return tokenName; } - public void setBearerTokenName(String bearerTokenName) { - this.bearerTokenName = bearerTokenName; + public void setTokenName(String tokenName) { + this.tokenName = tokenName; } public String getGrantType() { diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/OAuth2ProtectedResourceDetails.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/OAuth2ProtectedResourceDetails.java index 2b7c30d5e..674b1260c 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/OAuth2ProtectedResourceDetails.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/client/resource/OAuth2ProtectedResourceDetails.java @@ -12,20 +12,20 @@ public interface OAuth2ProtectedResourceDetails { /** * Enumeration of possible methods for bearing the access token for this resource. */ - public enum BearerTokenMethod { + public enum AuthenticationScheme { /** - * Bear the token in an Authorization header. + * Send the token in an Authorization header. */ header, /** - * Bear the token in a query parameter in the URI. + * Send the token in a query parameter in the URI. */ query, /** - * Bear the token in the form body. + * Send the token in the form body. */ form } @@ -99,12 +99,12 @@ public enum BearerTokenMethod { * * @return The bearer token method for this resource. */ - BearerTokenMethod getBearerTokenMethod(); + AuthenticationScheme getAuthenticationScheme(); /** * The name of the bearer token. The default is "bearer_token", which is according to the spec, but some providers (e.g. Facebook) don't conform to the spec.) * * @return The name of the bearer token. */ - String getBearerTokenName(); + String getTokenName(); } diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/ClientDetailsServiceBeanDefinitionParser.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/ClientDetailsServiceBeanDefinitionParser.java index 05444655b..18a89d230 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/ClientDetailsServiceBeanDefinitionParser.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/ClientDetailsServiceBeanDefinitionParser.java @@ -47,7 +47,7 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit for (Object item : clientElements) { BeanDefinitionBuilder client = BeanDefinitionBuilder.rootBeanDefinition(BaseClientDetails.class); Element clientElement = (Element) item; - String clientId = clientElement.getAttribute("clientId"); + String clientId = clientElement.getAttribute("client-id"); if (StringUtils.hasText(clientId)) { client.addPropertyValue("clientId", clientId); } @@ -71,7 +71,7 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit client.addPropertyValue("webServerRedirectUri", redirectUri); } client.addConstructorArgValue(clientElement.getAttribute("scope")); - client.addConstructorArgValue(clientElement.getAttribute("authorizedGrantTypes")); + client.addConstructorArgValue(clientElement.getAttribute("authorized-grant-types")); client.addConstructorArgValue(clientElement.getAttribute("authorities")); clients.put(clientId, client.getBeanDefinition()); diff --git a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/OAuth2ResourceBeanDefinitionParser.java b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/OAuth2ResourceBeanDefinitionParser.java index 2049da897..549622d31 100644 --- a/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/OAuth2ResourceBeanDefinitionParser.java +++ b/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/config/OAuth2ResourceBeanDefinitionParser.java @@ -39,7 +39,7 @@ public class OAuth2ResourceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override - protected Class getBeanClass(Element element) { + protected Class getBeanClass(Element element) { if ("authorization_code".equals(element.getAttribute("type"))) { return AuthorizationCodeResourceDetails.class; } @@ -62,30 +62,30 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit builder.addPropertyValue("grantType", type); } - String accessTokenUri = element.getAttribute("accessTokenUri"); + String accessTokenUri = element.getAttribute("access-token-uri"); if (!StringUtils.hasText(accessTokenUri)) { parserContext.getReaderContext() .error("An accessTokenUri must be supplied on a resource element.", element); } builder.addPropertyValue("accessTokenUri", accessTokenUri); - String clientId = element.getAttribute("clientId"); + String clientId = element.getAttribute("client-id"); if (!StringUtils.hasText(clientId)) { parserContext.getReaderContext().error("An clientId must be supplied on a resource element.", element); } builder.addPropertyValue("clientId", clientId); - String clientSecret = element.getAttribute("clientSecret"); + String clientSecret = element.getAttribute("client-secret"); if (StringUtils.hasText(clientSecret)) { builder.addPropertyValue("clientSecret", clientSecret); } - String clientAuthenticationScheme = element.getAttribute("clientAuthenticationScheme"); + String clientAuthenticationScheme = element.getAttribute("client-authentication-scheme"); if (StringUtils.hasText(clientAuthenticationScheme)) { builder.addPropertyValue("clientAuthenticationScheme", clientAuthenticationScheme); } - String userAuthorizationUri = element.getAttribute("userAuthorizationUri"); + String userAuthorizationUri = element.getAttribute("user-authorization-uri"); if (StringUtils.hasText(userAuthorizationUri)) { if (type.equals("client_credentials")) { parserContext.getReaderContext().error("The client_credentials grant type does not accept an authorization URI", element); @@ -94,12 +94,12 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit } } - String preEstablishedRedirectUri = element.getAttribute("preEstablishedRedirectUri"); + String preEstablishedRedirectUri = element.getAttribute("preEstablished-redirect-uri"); if (StringUtils.hasText(preEstablishedRedirectUri)) { builder.addPropertyValue("preEstablishedRedirectUri", preEstablishedRedirectUri); } - String requireImmediateAuthorization = element.getAttribute("requireImmediateAuthorization"); + String requireImmediateAuthorization = element.getAttribute("require-immediate-authorization"); if (StringUtils.hasText(requireImmediateAuthorization)) { builder.addPropertyValue("requireImmediateAuthorization", requireImmediateAuthorization); } @@ -112,18 +112,18 @@ protected void doParse(Element element, ParserContext parserContext, BeanDefinit builder.addPropertyValue("scope", scopesBuilder.getBeanDefinition()); } - OAuth2ProtectedResourceDetails.BearerTokenMethod btm = OAuth2ProtectedResourceDetails.BearerTokenMethod.header; - String bearerTokenMethod = element.getAttribute("bearerTokenMethod"); + OAuth2ProtectedResourceDetails.AuthenticationScheme btm = OAuth2ProtectedResourceDetails.AuthenticationScheme.header; + String bearerTokenMethod = element.getAttribute("authentication-scheme"); if (StringUtils.hasText(bearerTokenMethod)) { - btm = OAuth2ProtectedResourceDetails.BearerTokenMethod.valueOf(bearerTokenMethod); + btm = OAuth2ProtectedResourceDetails.AuthenticationScheme.valueOf(bearerTokenMethod); } - builder.addPropertyValue("bearerTokenMethod", btm); + builder.addPropertyValue("authenticationScheme", btm); - String bearerTokenName = element.getAttribute("bearerTokenName"); + String bearerTokenName = element.getAttribute("token-name"); if (!StringUtils.hasText(bearerTokenName)) { bearerTokenName = OAuth2AccessToken.BEARER_TYPE_PARAMETER; } - builder.addPropertyValue("bearerTokenName", bearerTokenName); + builder.addPropertyValue("tokenName", bearerTokenName); } diff --git a/spring-security-oauth2/src/main/resources/org/springframework/security/oauth/spring-security-oauth2-1.0.xsd b/spring-security-oauth2/src/main/resources/org/springframework/security/oauth/spring-security-oauth2-1.0.xsd index 799563838..73589d769 100644 --- a/spring-security-oauth2/src/main/resources/org/springframework/security/oauth/spring-security-oauth2-1.0.xsd +++ b/spring-security-oauth2/src/main/resources/org/springframework/security/oauth/spring-security-oauth2-1.0.xsd @@ -373,7 +373,7 @@ - + The client id. @@ -429,7 +429,7 @@ - + Grant types that are authorized for the @@ -698,7 +698,7 @@ - + The client id. This is the id by which the @@ -707,7 +707,7 @@ - + The uri to where the access token may be @@ -727,7 +727,7 @@ - + The secret asssociated with the resource. By @@ -737,7 +737,7 @@ - + The scheme that is used to pass the client @@ -749,7 +749,7 @@ - + The uri to which the user will be redirected if @@ -759,7 +759,7 @@ - + The method for bearing the token when accessing @@ -775,11 +775,11 @@ - + The name of the bearer token. The default is - "bearer_token", which + "access_token", which is according to the spec, but some providers @@ -787,7 +787,7 @@ - + Some resource servers may require a diff --git a/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestClientDetailsServiceBeanDefinitionParser-context.xml b/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestClientDetailsServiceBeanDefinitionParser-context.xml index cc307baa0..332da1882 100644 --- a/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestClientDetailsServiceBeanDefinitionParser-context.xml +++ b/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestClientDetailsServiceBeanDefinitionParser-context.xml @@ -19,13 +19,13 @@ - + - + - + diff --git a/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestOAuth2ResourceBeanDefinitionParser-context.xml b/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestOAuth2ResourceBeanDefinitionParser-context.xml index 6bdf30740..52b90b1d9 100644 --- a/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestOAuth2ResourceBeanDefinitionParser-context.xml +++ b/spring-security-oauth2/src/test/resources/org/springframework/security/oauth2/config/TestOAuth2ResourceBeanDefinitionParser-context.xml @@ -16,10 +16,10 @@ - + -