Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[cfid-348] Refactor app sample to use UAA scopes
This change doesn't affect the server (provider) features of the UAA but it does alter the way that the sample client works, and provides additional library features to support that. The basic idea is that a client app can interrogate the token scopes and use them to provide role-based (or other) authorization locally. The sample does this with the /check_token endpoint (hence the app has to be a uaa.resource), but it could in principle work with local decoding too. Also inspired by problems encountered with a abug that surfaced in the dashboard: [#39194235] [cfid-300] User authenticated by Login Server only has restricted authorities (uaa.user) Change-Id: I01e760fbe7b9689263af61a2165f8b0432342ac1
- Loading branch information
Showing
28 changed files
with
358 additions
and
148 deletions.
There are no files selected for viewing
170 changes: 170 additions & 0 deletions
170
common/src/main/java/org/cloudfoundry/identity/uaa/client/ClientAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* Cloud Foundry 2012.02.03 Beta | ||
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved. | ||
* | ||
* This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this product except in compliance with the License. | ||
* | ||
* This product includes a number of subcomponents with | ||
* separate copyright notices and license terms. Your use of these | ||
* subcomponents is subject to the terms and conditions of the | ||
* subcomponent's license, as noted in the LICENSE file. | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.client; | ||
|
||
import java.security.Principal; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.oauth2.client.UserRedirectRequiredException; | ||
import org.springframework.security.oauth2.client.http.AccessTokenRequiredException; | ||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; | ||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; | ||
import org.springframework.util.Assert; | ||
import org.springframework.util.ClassUtils; | ||
|
||
/** | ||
* An authentication filter for remote identity providers. Intended to be used with Spring OAuth (1 or 2), since it is | ||
* aware of the redirect protocols employed by those frameworks. If used in the PRE_AUTH_FILTER position of a regular | ||
* Spring Security filter chain the user will be redirected to the remote provider to approve the access and return with | ||
* a valid access token. There are 2 main strategies to provide: | ||
* | ||
* <ul> | ||
* <li> {@link #setPreAuthenticatedPrincipalSource(PreAuthenticatedPrincipalSource) PreAuthenticatedPrincipalSource} | ||
* (mandatory) provides a {@link Principal} that can be authenticated by the authentication manager. An example would be | ||
* to contact the user info endpoint in a remote social provider and populate an {@link Authentication} token with the | ||
* user's profile data. The principal is wrapped by</li> | ||
* <li>{@link #setAuthenticationManager(AuthenticationManager) Authentication manager} is optional and defaults to a | ||
* value that tries very hard to authenticate everything it sees, on the assumption that it was obtained from a trusted | ||
* ID provider.</li> | ||
* </ul> | ||
* | ||
* To ensure that the default authentication manager successfully authenticates the user, the principal source should | ||
* create a principal that itself is an {@link Authentication} and is already authenticated. If you are not using the | ||
* default authentication manager then you are free to authenticate any way you like (hence there is collaboration | ||
* between the principal source and authentication manager, and the principal source can create an object of any type | ||
* that is understood by the authentication manager). | ||
* | ||
* @author Dave Syer | ||
* | ||
*/ | ||
public class ClientAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter { | ||
|
||
private PreAuthenticatedPrincipalSource<?> principalSource; | ||
|
||
private boolean oauthAvailable = false; | ||
|
||
private boolean oauth2Available = false; | ||
|
||
/** | ||
* @param socialClientUserDetailsSource the socialClientUserDetailsSource to set | ||
*/ | ||
public void setPreAuthenticatedPrincipalSource(PreAuthenticatedPrincipalSource<?> principalSource) { | ||
this.principalSource = principalSource; | ||
} | ||
|
||
@Override | ||
public void afterPropertiesSet() { | ||
Assert.state(principalSource != null, "User info source must be provided"); | ||
super.afterPropertiesSet(); | ||
try { | ||
oauth2Available = ClassUtils.isPresent(AccessTokenRequiredException.class.getName(), | ||
ClassUtils.getDefaultClassLoader()); | ||
} | ||
catch (NoClassDefFoundError e) { | ||
// ignore | ||
} | ||
try { | ||
oauthAvailable = ClassUtils.isPresent( | ||
org.springframework.security.oauth.consumer.AccessTokenRequiredException.class.getName(), | ||
ClassUtils.getDefaultClassLoader()); | ||
} | ||
catch (NoClassDefFoundError e) { | ||
// ignore | ||
} | ||
} | ||
|
||
public ClientAuthenticationFilter(String defaultFilterProcessesUrl) { | ||
setAuthenticationManager(new DefaultFriendlyAuthenticationManager()); | ||
} | ||
|
||
@Override | ||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, | ||
AuthenticationException failed) { | ||
// Need to force a redirect via the OAuth client filter, so rethrow here if OAuth related | ||
if (oauth2Available && failed instanceof SocialRedirectException) { | ||
throw ((SocialRedirectException) failed).getUserRedirectException(); | ||
} | ||
if (oauthAvailable | ||
&& failed instanceof org.springframework.security.oauth.consumer.AccessTokenRequiredException) { | ||
throw failed; | ||
} | ||
else { | ||
// If the exception is not a Spring Security exception this will result in a default error page | ||
super.unsuccessfulAuthentication(request, response, failed); | ||
} | ||
} | ||
|
||
@Override | ||
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { | ||
try { | ||
Object result = principalSource.getPrincipal(); | ||
return result; | ||
} | ||
catch (UserRedirectRequiredException e) { | ||
throw new SocialRedirectException(e); | ||
} | ||
} | ||
|
||
@Override | ||
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) { | ||
return "N/A"; | ||
} | ||
|
||
private static class DefaultFriendlyAuthenticationManager implements AuthenticationManager { | ||
|
||
@Override | ||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
|
||
boolean authenticated = authentication.isAuthenticated(); | ||
|
||
// If not already authenticated (the default) from the parent class | ||
if (authentication instanceof PreAuthenticatedAuthenticationToken && !authenticated) { | ||
|
||
PreAuthenticatedAuthenticationToken preAuth = (PreAuthenticatedAuthenticationToken) authentication; | ||
// Look inside the principal and see if that was marked as authenticated | ||
if (preAuth.getPrincipal() instanceof Authentication) { | ||
Authentication principal = (Authentication) preAuth.getPrincipal(); | ||
preAuth = new PreAuthenticatedAuthenticationToken(principal, preAuth.getCredentials(), principal.getAuthorities()); | ||
authenticated = principal.isAuthenticated(); | ||
} | ||
preAuth.setAuthenticated(authenticated); | ||
|
||
authentication = preAuth; | ||
|
||
} | ||
|
||
return authentication; | ||
|
||
} | ||
|
||
} | ||
|
||
private static class SocialRedirectException extends AuthenticationException { | ||
|
||
public SocialRedirectException(UserRedirectRequiredException e) { | ||
super("Social user details extraction failed", e); | ||
} | ||
|
||
public UserRedirectRequiredException getUserRedirectException() { | ||
return (UserRedirectRequiredException) getCause(); | ||
} | ||
|
||
} | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
common/src/main/java/org/cloudfoundry/identity/uaa/client/OAuth2AccessTokenSource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Cloud Foundry 2012.02.03 Beta | ||
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved. | ||
* | ||
* This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this product except in compliance with the License. | ||
* | ||
* This product includes a number of subcomponents with | ||
* separate copyright notices and license terms. Your use of these | ||
* subcomponents is subject to the terms and conditions of the | ||
* subcomponent's license, as noted in the LICENSE file. | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.client; | ||
|
||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.security.oauth2.client.OAuth2RestOperations; | ||
import org.springframework.security.oauth2.client.OAuth2RestTemplate; | ||
import org.springframework.util.Assert; | ||
|
||
/** | ||
* | ||
* @author Dave Syer | ||
* | ||
*/ | ||
public class OAuth2AccessTokenSource implements InitializingBean, PreAuthenticatedPrincipalSource<String> { | ||
|
||
private OAuth2RestOperations restTemplate; | ||
|
||
/** | ||
* A rest template to be used to contact the remote user info endpoint. Normally an instance of | ||
* {@link OAuth2RestTemplate}. | ||
* | ||
* @param restTemplate a rest template | ||
*/ | ||
public void setRestTemplate(OAuth2RestOperations restTemplate) { | ||
this.restTemplate = restTemplate; | ||
} | ||
|
||
@Override | ||
public void afterPropertiesSet() { | ||
Assert.state(restTemplate != null, "RestTemplate URL must be provided"); | ||
} | ||
|
||
@Override | ||
public String getPrincipal() { | ||
return restTemplate.getAccessToken().getValue(); | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
...n/src/main/java/org/cloudfoundry/identity/uaa/client/PreAuthenticatedPrincipalSource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* | ||
* Cloud Foundry 2012.02.03 Beta | ||
* Copyright (c) [2009-2012] VMware, Inc. All Rights Reserved. | ||
* | ||
* This product is licensed to you under the Apache License, Version 2.0 (the "License"). | ||
* You may not use this product except in compliance with the License. | ||
* | ||
* This product includes a number of subcomponents with | ||
* separate copyright notices and license terms. Your use of these | ||
* subcomponents is subject to the terms and conditions of the | ||
* subcomponent's license, as noted in the LICENSE file. | ||
*/ | ||
|
||
package org.cloudfoundry.identity.uaa.client; | ||
|
||
/** | ||
* @author Dave Syer | ||
* | ||
*/ | ||
public interface PreAuthenticatedPrincipalSource<T> { | ||
|
||
T getPrincipal(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.