Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Revert "Revert previous 4 commits: 2 merges, 2 small changes"

This reverts commit 1889f76.

Change-Id: I23ed0be11e64842bb4f79a4383f575559b69909b
  • Loading branch information...
commit c430116fcb265fc3f8305236d768fbd4ac0104ed 1 parent 1889f76
Joel D'sa joeldsa authored
Showing with 2,198 additions and 1,077 deletions.
  1. +1 −0  .gitreview
  2. +9 −13 common/pom.xml
  3. +12 −3 common/src/main/java/org/cloudfoundry/identity/uaa/authentication/AuthzAuthenticationFilter.java
  4. +1 −1  common/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java
  5. +24 −1 common/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/LoginAuthenticationManager.java
  6. +12 −6 common/src/main/java/org/cloudfoundry/identity/uaa/integration/TestAccountSetup.java
  7. +1 −2  common/src/main/java/org/cloudfoundry/identity/uaa/integration/UaaTestAccounts.java
  8. +1 −1  common/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessTokenConverter.java
  9. +2 −2 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java
  10. +9 −1 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminBootstrap.java
  11. +9 −5 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminEndpoints.java
  12. +9 −15 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/DefaultTokenConverter.java
  13. +24 −6 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/JwtTokenEnhancer.java
  14. +29 −9 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/TokenAdminEndpoints.java
  15. +92 −0 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthenticationKeyGenerator.java
  16. +9 −1 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationRequestFactory.java
  17. +41 −0 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaUserTokenConverter.java
  18. +34 −0 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UserTokenConverter.java
  19. +0 −110 common/src/main/java/org/cloudfoundry/identity/uaa/openid/OpenIdClientFilter.java
  20. +0 −73 common/src/main/java/org/cloudfoundry/identity/uaa/openid/OpenIdUserDetails.java
  21. +134 −0 common/src/main/java/org/cloudfoundry/identity/uaa/scim/GroupsUsersEndpoints.java
  22. +5 −1 common/src/main/java/org/cloudfoundry/identity/uaa/scim/JdbcScimUserProvisioning.java
  23. +2 −19 common/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java
  24. +1 −1  common/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserEndpoints.java
  25. +0 −2  common/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserEditor.java
  26. +8 −8 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpointTests.java
  27. +8 −2 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminEndpointsTests.java
  28. +56 −21 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/JwtTokenEnhancerTests.java
  29. +9 −10 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenAdminEndpointsTests.java
  30. +63 −0 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthenticationKeyGeneratorTests.java
  31. +9 −0 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationRequestFactoryTests.java
  32. +40 −0 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaUserTokenConverterTests.java
  33. +0 −87 common/src/test/java/org/cloudfoundry/identity/uaa/openid/OpenIdClientFilterTests.java
  34. +0 −4 common/src/test/java/org/cloudfoundry/identity/uaa/password/PasswordCheckEndpointTests.java
  35. +87 −0 common/src/test/java/org/cloudfoundry/identity/uaa/scim/GroupsUsersEndpointsTests.java
  36. +9 −4 common/src/test/java/org/cloudfoundry/identity/uaa/scim/JdbcScimUserProvisioningTests.java
  37. +2 −2 common/src/test/java/org/cloudfoundry/identity/uaa/social/OAuthClientAuthenticationFilterTests.java
  38. +2 −5 common/src/test/java/org/cloudfoundry/identity/uaa/user/UaaUserEditorTests.java
  39. +1 −1  common/src/test/resources/log4j.properties
  40. +101 −5 docs/UAA-APIs.rst
  41. +10 −0 docs/UAA-Security.md
  42. +5 −0 gatling/pom.xml
  43. +0 −4 gem/cf-uaa-client.gemspec
  44. +24 −3 gem/lib/cli/common.rb
  45. +14 −2 gem/lib/cli/config.rb
  46. +1 −1  gem/lib/cli/runner.rb
  47. +14 −4 gem/lib/cli/stub_server.rb
  48. +2 −2 gem/lib/cli/token.rb
  49. +6 −6 gem/lib/uaa/client_reg.rb
  50. +21 −24 gem/lib/uaa/http.rb
  51. +7 −14 gem/lib/uaa/misc.rb
  52. +6 −4 gem/lib/uaa/token_issuer.rb
  53. +5 −5 gem/lib/uaa/user_account.rb
  54. +1 −1  gem/lib/uaa/version.rb
  55. +7 −7 gem/spec/http_spec.rb
  56. +24 −0 gem/spec/misc_spec.rb
  57. +117 −0 gem/spec/stub_scim.rb
  58. +22 −37 gem/spec/stub_uaa.rb
  59. +57 −4 pom.xml
  60. +1 −2  samples/api/pom.xml
  61. +1 −33 samples/app/pom.xml
  62. +7 −4 samples/login/pom.xml
  63. +1 −1  samples/pom.xml
  64. +4 −4 uaa/.springBeans
  65. +42 −7 uaa/pom.xml
  66. +0 −26 uaa/src/main/resources/uaa.yml
  67. +14 −3 uaa/src/main/webapp/WEB-INF/applicationContext.xml
  68. +0 −129 uaa/src/main/webapp/WEB-INF/oauth-clients.xml
  69. +21 −305 uaa/src/main/webapp/WEB-INF/spring-servlet.xml
  70. +39 −0 uaa/src/main/webapp/WEB-INF/spring/client-admin-endpoints.xml
  71. 0  uaa/src/main/webapp/WEB-INF/{spring-data-source.xml → spring/data-source.xml}
  72. 0  uaa/src/main/webapp/WEB-INF/{env-context.xml → spring/env.xml}
  73. +86 −0 uaa/src/main/webapp/WEB-INF/spring/login-server-security.xml
  74. +75 −0 uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml
  75. +199 −0 uaa/src/main/webapp/WEB-INF/spring/oauth-endpoints.xml
  76. +32 −0 uaa/src/main/webapp/WEB-INF/spring/openid-endpoints.xml
  77. 0  uaa/src/main/webapp/WEB-INF/{spring-password.xml → spring/password-endpoints.xml}
  78. +39 −0 uaa/src/main/webapp/WEB-INF/spring/resource-endpoints.xml
  79. +12 −0 uaa/src/main/webapp/WEB-INF/{spring-scim.xml → spring/scim-endpoints.xml}
  80. +36 −0 uaa/src/main/webapp/WEB-INF/spring/token-admin-endpoints.xml
  81. +3 −1 uaa/src/main/webapp/WEB-INF/varz-servlet.xml
  82. +1 −1  uaa/src/main/webapp/WEB-INF/web.xml
  83. +2 −1  uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/AuthorizationCodeGrantIntegrationTests.java
  84. +196 −0 uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/LoginServerSecurityIntegrationTests.java
  85. +19 −9 uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/TokenAdminEndpointsIntegrationTests.java
  86. +161 −0 uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/VmcGroupsUsersEndpointIntegrationTests.java
  87. +4 −8 uaa/src/test/java/org/cloudfoundry/identity/uaa/integration/VmcScimUserEndpointIntegrationTests.java
  88. +2 −2 uaa/src/test/resources/test/profiles/local/uaa.yml
  89. +2 −2 uaa/src/test/resources/test/profiles/vcap/uaa.yml
1  .gitreview
View
@@ -2,4 +2,5 @@
host=reviews.cloudfoundry.org
port=29418
project=uaa
+defaultbranch=develop
22 common/pom.xml
View
@@ -6,7 +6,7 @@
<parent>
<groupId>org.cloudfoundry.identity</groupId>
<artifactId>cloudfoundry-identity-parent</artifactId>
- <version>1.1.2</version>
+ <version>1.2.0.BUILD-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
@@ -86,6 +86,7 @@
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>${spring.security.jwt.version}</version>
+ <optional>true</optional>
</dependency>
<dependency>
@@ -179,7 +180,7 @@
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
- <scope>runtime</scope>
+ <scope>test</scope>
</dependency>
<dependency>
@@ -187,6 +188,7 @@
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>runtime</scope>
+ <optional>true</optional>
</dependency>
<dependency>
@@ -205,13 +207,7 @@
<dependency>
<groupId>edu.vt.middleware</groupId>
<artifactId>vt-password</artifactId>
- <version>3.1.1</version>
- <exclusions>
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15</artifactId>
- </exclusion>
- </exclusions>
+ <optional>true</optional>
</dependency>
<dependency>
@@ -225,8 +221,7 @@
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
- <version>4.1.2</version>
- <scope>compile</scope>
+ <scope>test</scope>
</dependency>
<dependency>
@@ -239,14 +234,14 @@
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb-j5</artifactId>
<version>2.2.4</version>
- <scope>runtime</scope>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901.jdbc3</version>
- <scope>runtime</scope>
+ <scope>test</scope>
</dependency>
<dependency>
@@ -254,6 +249,7 @@
<artifactId>szxcvbn_2.8.2</artifactId>
<version>0.1</version>
<scope>compile</scope>
+ <optional>true</optional>
</dependency>
<dependency>
15 common/src/main/java/org/cloudfoundry/identity/uaa/authentication/AuthzAuthenticationFilter.java
View
@@ -36,6 +36,8 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
+import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.util.Assert;
/**
@@ -62,6 +64,15 @@
private List<String> parameterNames = Collections.emptyList();
+ private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
+
+ /**
+ * @param authenticationEntryPoint the authenticationEntryPoint to set
+ */
+ public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
+ this.authenticationEntryPoint = authenticationEntryPoint;
+ }
+
/**
* The name of the parameter to extract credentials from. Request parameters with these names are extracted and
* passed as credentials to the authentication manager. A request that doesn't have any of the specified parameters
@@ -101,9 +112,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
}
catch (AuthenticationException e) {
logger.debug("Authentication failed");
- response.getWriter().write("{ \"error\":\"authentication failed\" }");
- response.setContentType("application/json");
- res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ authenticationEntryPoint.commence(req, res, e);
return;
}
}
2  common/src/main/java/org/cloudfoundry/identity/uaa/authentication/UaaAuthentication.java
View
@@ -46,7 +46,7 @@ public UaaAuthentication(UaaPrincipal principal, List<? extends GrantedAuthority
@Override
public String getName() {
- // TODO: Should we return the ID for the princpal name?
+ // TODO: Should we return the ID for the principal name?
return principal.getName();
}
25 common/src/main/java/org/cloudfoundry/identity/uaa/authentication/manager/LoginAuthenticationManager.java
View
@@ -12,6 +12,7 @@
import org.cloudfoundry.identity.uaa.scim.ScimUserBootstrap;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.cloudfoundry.identity.uaa.user.UaaUser;
+import org.cloudfoundry.identity.uaa.user.UaaUserDatabase;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.security.authentication.AuthenticationManager;
@@ -20,6 +21,7 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
@@ -30,6 +32,8 @@
private ApplicationEventPublisher eventPublisher;
private ScimUserBootstrap scimUserBootstrap;
+
+ private UaaUserDatabase userDatabase;
boolean addNewAccounts = false;
@@ -58,7 +62,14 @@ public void setApplicationEventPublisher(ApplicationEventPublisher eventPublishe
public void setScimUserBootstrap(ScimUserBootstrap scimUserBootstrap) {
this.scimUserBootstrap = scimUserBootstrap;
}
-
+
+ /**
+ * @param userDatabase the userDatabase to set
+ */
+ public void setUserDatabase(UaaUserDatabase userDatabase) {
+ this.userDatabase = userDatabase;
+ }
+
@Override
public Authentication authenticate(Authentication request) throws AuthenticationException {
@@ -80,6 +91,12 @@ public Authentication authenticate(Authentication request) throws Authentication
if (scimUserBootstrap != null && addNewAccounts) {
// Register new users automatically
scimUserBootstrap.addUser(user);
+ } else {
+ try {
+ user = userDatabase.retrieveUserByName(user.getUsername());
+ } catch (UsernameNotFoundException e) {
+ throw new BadCredentialsException("Bad credentials");
+ }
}
Authentication success = new UaaAuthentication(new UaaPrincipal(user), UaaAuthority.USER_AUTHORITIES,
(UaaAuthenticationDetails) req.getDetails());
@@ -96,6 +113,12 @@ public Authentication authenticate(Authentication request) throws Authentication
protected UaaUser getUser(AuthzAuthenticationRequest req, Map<String, String> info) {
String name = req.getName();
String email = info.get("email");
+ if (name==null && email!=null) {
+ name = email;
+ }
+ if (name==null) {
+ throw new BadCredentialsException("Cannot determine username from credentials supplied");
+ }
if (email == null) {
if (name.contains("@")) {
email = name;
18 common/src/main/java/org/cloudfoundry/identity/uaa/integration/TestAccountSetup.java
View
@@ -28,6 +28,7 @@
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
@@ -71,7 +72,12 @@ public static TestAccountSetup standard(UrlHelper serverRunning, UaaTestAccounts
}
@Override
- public void starting(FrameworkMethod method) {
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ initializeIfNecessary(method, target);
+ return super.apply(base, method, target);
+ }
+
+ private void initializeIfNecessary(FrameworkMethod method, Object target) {
// Cache statically to save time on a test suite
if (!initialized) {
OAuth2ProtectedResourceDetails resource = testAccounts.getAdminClientCredentialsResource();
@@ -107,22 +113,22 @@ private void createTokenClient(RestOperations client) {
private void createVmcClient(RestOperations client) {
BaseClientDetails clientDetails = new BaseClientDetails("vmc", "cloud_controller,openid,password",
- "cloud_controller.read,password.write,tokens.read,tokens.write", "implicit", "uaa.none",
+ "openid,cloud_controller.read,password.write,tokens.read,tokens.write", "implicit", "uaa.none",
"http://uaa.cloudfoundry.com/redirect/vmc");
createClient(client, testAccounts.getClientDetails("oauth.clients.vmc", clientDetails));
}
private void createScimClient(RestOperations client) {
- BaseClientDetails clientDetails = new BaseClientDetails("scim", "scim,password,tokens",
- "scim.read,scim.write,password.write,tokens.read,tokens.write", "client_credentials",
+ BaseClientDetails clientDetails = new BaseClientDetails("scim", "none", "uaa.none", "client_credentials",
"scim.read,scim.write,password.write,tokens.read,tokens.write");
clientDetails.setClientSecret("scimsecret");
createClient(client, testAccounts.getClientDetails("oauth.clients.scim", clientDetails));
}
private void createAppClient(RestOperations client) {
- BaseClientDetails clientDetails = new BaseClientDetails("app", null, "cloud_controller.read,openid,password.write",
- "password,authorization_code,refresh_token", "uaa.resource");
+ BaseClientDetails clientDetails = new BaseClientDetails("app", "none",
+ "cloud_controller.read,openid,password.write", "password,authorization_code,refresh_token",
+ "uaa.resource");
clientDetails.setClientSecret("appclientsecret");
createClient(client, testAccounts.getClientDetails("oauth.clients.app", clientDetails));
}
3  common/src/main/java/org/cloudfoundry/identity/uaa/integration/UaaTestAccounts.java
View
@@ -131,7 +131,7 @@ public String getVarzAuthorizationHeader() {
}
public String getBatchAuthorizationHeader() {
- return getAuthorizationHeader("batch", "batch_user", "batch_password");
+ return getAuthorizationHeader("batch", "batch", "batchsecret");
}
public String getAuthorizationHeader(String prefix, String defaultUsername, String defaultPassword) {
@@ -205,7 +205,6 @@ public ImplicitResourceDetails getImplicitResource(String clientPrefix, String d
resource.setId(clientId);
resource.setClientAuthenticationScheme(AuthenticationScheme.header);
resource.setAccessTokenUri(server.getAuthorizationUri());
- resource.setScope(Arrays.asList("cloud_controller.read", "password.write", "openid"));
String redirectUri = environment.getProperty(clientPrefix + ".redirect-uri", defaultRedirectUri);
resource.setPreEstablishedRedirectUri(redirectUri);
return resource;
2  common/src/main/java/org/cloudfoundry/identity/uaa/oauth/AccessTokenConverter.java
View
@@ -30,6 +30,6 @@
* @return a map representation of the token suitable for a JSON response
*
*/
- public abstract Map<String, Object> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
+ public abstract Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
}
4 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpoint.java
View
@@ -50,7 +50,7 @@ public void afterPropertiesSet() throws Exception {
@RequestMapping(value = "/check_token")
@ResponseBody
- public Map<String, Object> checkToken(@RequestParam("token") String value) {
+ public Map<String, ?> checkToken(@RequestParam("token") String value) {
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
@@ -62,7 +62,7 @@ public void afterPropertiesSet() throws Exception {
}
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(value);
- Map<String, Object> response = tokenConverter.convertAccessToken(token, authentication);
+ Map<String, ?> response = tokenConverter.convertAccessToken(token, authentication);
return response;
}
10 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminBootstrap.java
View
@@ -27,6 +27,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.BaseClientDetails;
@@ -243,11 +244,18 @@ private void addNewClients() throws Exception {
if (validity != null) {
client.setRefreshTokenValiditySeconds(validity);
}
+ // UAA does not use the resource ids in client registrations
+ client.setResourceIds(Collections.singleton("none"));
+ if (client.getScope().isEmpty()) {
+ client.setScope(Collections.singleton("uaa.none"));
+ }
+ if (client.getAuthorities().isEmpty()) {
+ client.setAuthorities(Collections.singleton(UaaAuthority.UAA_NONE));
+ }
for (String key : Arrays.asList("resource-ids", "scope", "authorized-grant-types", "authorities",
"redirect-uri", "secret", "id", "override", "access-token-validity", "refresh-token-validity")) {
info.remove(key);
}
- client.setResourceIds(Collections.singleton("none"));
client.setAdditionalInformation(info);
try {
clientRegistrationService.addClientDetails(client);
14 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminEndpoints.java
View
@@ -147,7 +147,7 @@ public ClientDetails getClientDetails(@PathVariable String client) throws Except
throw new NoSuchClientException("No such client: " + client);
}
catch (BadClientCredentialsException e) {
- // TODO: CFID-399 maybe this should go away?
+ // Defensive check, in case the clientDetailsService starts throwing these instead
throw new NoSuchClientException("No such client: " + client);
}
}
@@ -335,14 +335,18 @@ private ClientDetails validateClient(ClientDetails prototype, boolean create) {
}
}
- if (client.getAuthorities().isEmpty()) {
- client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none"));
- }
+ }
+ if (client.getAuthorities().isEmpty()) {
+ client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none"));
}
// The UAA does not allow or require resource ids to be registered because they are determined dynamically
- client.setResourceIds(StringUtils.commaDelimitedListToSet("none"));
+ client.setResourceIds(Collections.singleton("none"));
+
+ if (client.getScope().isEmpty()) {
+ client.setScope(Collections.singleton("uaa.none"));
+ }
if (requestedGrantTypes.contains("implicit")) {
if (StringUtils.hasText(client.getClientSecret())) {
24 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/DefaultTokenConverter.java
View
@@ -15,42 +15,36 @@
import java.util.HashMap;
import java.util.Map;
-import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
-import org.cloudfoundry.identity.uaa.openid.UserInfo;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
/**
* @author Dave Syer
- *
+ *
*/
public class DefaultTokenConverter implements AccessTokenConverter {
+ private UserTokenConverter userTokenConverter = new UaaUserTokenConverter();
+
@Override
- public Map<String, Object> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
+ public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
Map<String, Object> response = new HashMap<String, Object>();
AuthorizationRequest clientToken = authentication.getAuthorizationRequest();
- if (!authentication.isClientOnly()
- && authentication.getUserAuthentication().getPrincipal() instanceof UaaPrincipal) {
-
- UaaPrincipal principal = (UaaPrincipal) authentication.getUserAuthentication().getPrincipal();
-
- response.put(UserInfo.USER_ID, principal.getId());
- response.put(UserInfo.USER_NAME, principal.getName());
- response.put(UserInfo.EMAIL, principal.getEmail());
-
+ if (!authentication.isClientOnly()) {
+ response.putAll(userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
}
+
response.put(OAuth2AccessToken.SCOPE, token.getScope());
if (token.getAdditionalInformation().containsKey(JwtTokenEnhancer.TOKEN_ID)) {
response.put(JwtTokenEnhancer.TOKEN_ID, token.getAdditionalInformation().get(JwtTokenEnhancer.TOKEN_ID));
}
if (token.getExpiration() != null) {
- response.put("exp", token.getExpiration().getTime()/1000);
+ response.put("exp", token.getExpiration().getTime() / 1000);
}
-
+
response.putAll(token.getAdditionalInformation());
response.put("client_id", clientToken.getClientId());
30 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/JwtTokenEnhancer.java
View
@@ -12,7 +12,7 @@
*/
package org.cloudfoundry.identity.uaa.oauth;
-import java.util.Collections;
+import java.security.Principal;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -20,6 +20,8 @@
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.InitializingBean;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.InvalidSignatureException;
import org.springframework.security.jwt.crypto.sign.MacSigner;
@@ -64,12 +66,19 @@
private String signingKey = verifierKey;
/**
+ * Get the verification key for the token signatures. The principal has to be provided only if the key is secret
+ * (shared not public).
+ *
+ * @param principal the currently authenticated user if there is one
* @return the key used to verify tokens
*/
@RequestMapping(value = "/token_key", method = RequestMethod.GET)
@ResponseBody
- public Map<String, String> getKey() {
- Map<String,String> result = new LinkedHashMap<String, String>();
+ public Map<String, String> getKey(Principal principal) {
+ if ((principal == null || principal instanceof AnonymousAuthenticationToken) && !isPublic(this.verifierKey)) {
+ throw new AccessDeniedException("You need to authenticate to see a shared key");
+ }
+ Map<String, String> result = new LinkedHashMap<String, String>();
result.put("alg", signer.algorithm());
result.put("value", verifierKey);
return result;
@@ -87,18 +96,25 @@ public void setSigningKey(String key) {
this.signingKey = key;
- if (key.startsWith("-----BEGIN")) {
+ if (isPublic(key)) {
signer = new RsaSigner(key);
logger.info("Configured with RSA signing key");
}
else {
// Assume it's an HMAC key
- verifierKey = key;
+ this.verifierKey = key;
signer = new MacSigner(key);
}
}
/**
+ * @return true if the key has a public verifier
+ */
+ private boolean isPublic(String key) {
+ return key.startsWith("-----BEGIN");
+ }
+
+ /**
* The key used for verifying signatures produced by this class. This is not used but is returned from the endpoint
* to allow resource servers to obtain the key.
*
@@ -120,8 +136,10 @@ public void setVerifierKey(String key) {
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
+ Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
String tokenId = result.getValue();
- result.setAdditionalInformation(Collections.<String, Object> singletonMap(TOKEN_ID, tokenId));
+ info.put(TOKEN_ID, tokenId);
+ result.setAdditionalInformation(info);
return result.setValue(createAccessTokenValue(result, authentication));
}
38 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/TokenAdminEndpoints.java
View
@@ -21,13 +21,17 @@
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.scim.ScimUserProvisioning;
import org.cloudfoundry.identity.uaa.scim.UserNotFoundException;
+import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.stereotype.Controller;
@@ -61,7 +65,7 @@
}
@RequestMapping(value = "/oauth/users/{user}/tokens/{token}", method = RequestMethod.DELETE)
- public ResponseEntity<Void> revokeUserToken(@PathVariable String user, @PathVariable String token,
+ public ResponseEntity<?> revokeUserToken(@PathVariable String user, @PathVariable String token,
Principal principal, @RequestParam(required = false, defaultValue = "true") boolean lookup)
throws Exception {
String username = lookup ? getUserName(user) : user;
@@ -71,7 +75,10 @@
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
else {
- return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("error", "not_found");
+ map.put("error_description", "Token not found");
+ return new ResponseEntity<Map<String, String>>(map, HttpStatus.NOT_FOUND);
}
}
@@ -84,7 +91,7 @@
}
@RequestMapping(value = "/oauth/clients/{client}/tokens/{token}", method = RequestMethod.DELETE)
- public ResponseEntity<Void> revokeClientToken(@PathVariable String client, @PathVariable String token,
+ public ResponseEntity<?> revokeClientToken(@PathVariable String client, @PathVariable String token,
Principal principal) throws Exception {
checkClient(client, principal);
String tokenValue = getTokenValue(tokenServices.findTokensByClientId(client), token);
@@ -92,12 +99,15 @@
return new ResponseEntity<Void>(HttpStatus.NO_CONTENT);
}
else {
- return new ResponseEntity<Void>(HttpStatus.NOT_FOUND);
+ Map<String, String> map = new HashMap<String, String>();
+ map.put("error", "not_found");
+ map.put("error_description", "Token not found");
+ return new ResponseEntity<Map<String, String>>(map, HttpStatus.NOT_FOUND);
}
}
private String getUserName(String user) {
- if (scimProvisioning==null) {
+ if (scimProvisioning == null) {
return user;
}
String username = user;
@@ -141,9 +151,14 @@ private String getTokenValue(Collection<OAuth2AccessToken> tokens, String hash)
// The token doesn't have an ID in the token service, but we need one for the endpoint, so add one here
map.put(JwtTokenEnhancer.TOKEN_ID, encoder.encode(token.getValue()));
}
- String clientId = tokenServices.getClientId(token.getValue());
- if (clientId != null) {
- map.put("client_id", clientId);
+ try {
+ String clientId = tokenServices.getClientId(token.getValue());
+ if (clientId != null) {
+ map.put("client_id", clientId);
+ }
+ }
+ catch (InvalidTokenException e) {
+ // Ignore defensively in case of bugs in token services
}
token.setAdditionalInformation(map);
result.add(token);
@@ -169,13 +184,18 @@ else if (!user.equals(principal.getName())) {
private void checkClient(String client, Principal principal) {
if (principal instanceof OAuth2Authentication) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
- if (!authentication.isClientOnly() || !client.equals(principal.getName())) {
+ if (!authentication.isClientOnly() || !client.equals(principal.getName()) && !isAdmin(principal)) {
throw new AccessDeniedException(String.format("Client '%s' cannot obtain tokens for client '%s'",
principal.getName(), client));
}
}
}
+ private boolean isAdmin(Principal principal) {
+ return AuthorityUtils.authorityListToSet(((Authentication) principal).getAuthorities()).contains(
+ UaaAuthority.UAA_ADMIN.toString());
+ }
+
/**
* @param tokenServices the consumerTokenServices to set
*/
92 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthenticationKeyGenerator.java
View
@@ -0,0 +1,92 @@
+/*
+ * 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.oauth;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.springframework.security.oauth2.common.util.OAuth2Utils;
+import org.springframework.security.oauth2.provider.AuthorizationRequest;
+import org.springframework.security.oauth2.provider.ClientDetails;
+import org.springframework.security.oauth2.provider.ClientDetailsService;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class UaaAuthenticationKeyGenerator implements AuthenticationKeyGenerator {
+
+ private static final String CLIENT_ID = "client_id";
+
+ private static final String SCOPE = "scope";
+
+ private static final String ACCESS_TOKEN_VALIDITY = "access_token_validity";
+
+ private static final String REFRESH_TOKEN_VALIDITY = "refresh_token_validity";
+
+ private UserTokenConverter userTokenConverter = new UaaUserTokenConverter();
+
+ private ClientDetailsService clientDetailsService;
+
+ /**
+ * @param clientDetailsService the clientDetailsService to set
+ */
+ public void setClientDetailsService(ClientDetailsService clientDetailsService) {
+ this.clientDetailsService = clientDetailsService;
+ }
+
+ @Override
+ public String extractKey(OAuth2Authentication authentication) {
+ Map<String, Object> values = new LinkedHashMap<String, Object>();
+ AuthorizationRequest authorizationRequest = authentication.getAuthorizationRequest();
+ if (!authentication.isClientOnly()) {
+ values.putAll(userTokenConverter.convertUserAuthentication(authentication.getUserAuthentication()));
+ }
+ ClientDetails client = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
+ values.put(CLIENT_ID, client.getClientId());
+ if (authorizationRequest.getScope() != null) {
+ values.put(SCOPE, OAuth2Utils.formatParameterList(authorizationRequest.getScope()));
+ }
+ Integer validity = client.getAccessTokenValiditySeconds();
+ if (validity != null) {
+ values.put(ACCESS_TOKEN_VALIDITY, validity);
+ }
+ validity = client.getRefreshTokenValiditySeconds();
+ if (validity != null && client.getAuthorizedGrantTypes().contains("refresh_token")) {
+ values.put(REFRESH_TOKEN_VALIDITY, validity);
+ }
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException("MD5 algorithm not available. Fatal (should be in the JDK).");
+ }
+
+ try {
+ byte[] bytes = digest.digest(values.toString().getBytes("UTF-8"));
+ return String.format("%032x", new BigInteger(1, bytes));
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("UTF-8 encoding not available. Fatal (should be in the JDK).");
+ }
+ }
+
+}
10 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationRequestFactory.java
View
@@ -25,6 +25,7 @@
import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
@@ -165,6 +166,13 @@ public void validateParameters(Map<String, String> parameters, ClientDetails cli
}
}
}
+ // Client id is not mandatory in the request, but if it's there we can prevent a clash
+ if (parameters.containsKey("grant_type") && parameters.containsKey("client_id")) {
+ String clientId = parameters.get("client_id");
+ if (!clientDetails.getClientId().equals(clientId)) {
+ throw new BadClientCredentialsException();
+ }
+ }
}
/**
@@ -199,7 +207,7 @@ public void validateParameters(Map<String, String> parameters, ClientDetails cli
}
}
- // TODO: maybe move this to the validateParameters method
+ // Check that a token with empty scope is not going to be granted
if (result.isEmpty() && !clientDetails.getScope().isEmpty()) {
throw new InvalidScopeException(
"Invalid scope (empty) - this user is not allowed any of the requested scopes: " + scopes
41 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UaaUserTokenConverter.java
View
@@ -0,0 +1,41 @@
+/*
+ * 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.oauth;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.cloudfoundry.identity.uaa.authentication.UaaPrincipal;
+import org.cloudfoundry.identity.uaa.openid.UserInfo;
+import org.springframework.security.core.Authentication;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class UaaUserTokenConverter implements UserTokenConverter {
+
+ @Override
+ public Map<String, ?> convertUserAuthentication(Authentication authentication) {
+ Map<String, Object> response = new LinkedHashMap<String, Object>();
+ if (authentication.getPrincipal() instanceof UaaPrincipal) {
+ UaaPrincipal principal = (UaaPrincipal) authentication.getPrincipal();
+ response.put(UserInfo.USER_ID, principal.getId());
+ response.put(UserInfo.USER_NAME, principal.getName());
+ response.put(UserInfo.EMAIL, principal.getEmail());
+ }
+ return response;
+ }
+
+}
34 common/src/main/java/org/cloudfoundry/identity/uaa/oauth/UserTokenConverter.java
View
@@ -0,0 +1,34 @@
+/*
+ * 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.oauth;
+
+import java.util.Map;
+
+import org.springframework.security.core.Authentication;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public interface UserTokenConverter {
+
+ /**
+ * Extract information about the user to be used in an access token (i.e. for resource servers).
+ *
+ * @param userAuthentication an authentication representing a user
+ * @return a map of key values representing the unique information about the user
+ */
+ Map<String, ?> convertUserAuthentication(Authentication userAuthentication);
+
+}
110 common/src/main/java/org/cloudfoundry/identity/uaa/openid/OpenIdClientFilter.java
View
@@ -1,110 +0,0 @@
-/*
- * 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.openid;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.cloudfoundry.identity.uaa.social.SocialClientAuthenticationFilter;
-import org.cloudfoundry.identity.uaa.user.UaaAuthority;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.oauth2.client.OAuth2RestTemplate;
-import org.springframework.security.oauth2.client.http.AccessTokenRequiredException;
-import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
-import org.springframework.web.client.RestOperations;
-
-/**
- * A filter that can authenticate with a remote OpenId Connect(ish) provider. Works only with the UAA "lite" variant of
- * the OpenId <code>/userinfo</code> endpoint: makes assumptions about the fields it gets in the response from the
- * endpoint, and doesn't ask for or expect an id token.
- *
- * @author Dave Syer
- *
- * @deprecated in favour of {@link SocialClientAuthenticationFilter}
- *
- */
-@Deprecated
-public class OpenIdClientFilter extends AbstractAuthenticationProcessingFilter {
-
- public RestOperations restTemplate;
-
- private String userInfoUrl;
-
- /**
- * A rest template to be used to contact the remote user info endpoint. Normally would be an instance of
- * {@link OAuth2RestTemplate}, but there is no need for that dependency to be explicit, and there are advantages in
- * making it implicit (e.g. for testing purposes).
- *
- * @param restTemplate a rest template
- */
- public void setRestTemplate(RestOperations restTemplate) {
- this.restTemplate = restTemplate;
- }
-
- /**
- * The remote URL of the OpenId Connect <code>/userinfo</code> endpoint.
- *
- * @param userInfoUrl
- */
- public void setUserInfoUrl(String userInfoUrl) {
- this.userInfoUrl = userInfoUrl;
- }
-
- public OpenIdClientFilter(String defaultFilterProcessesUrl) {
- super(defaultFilterProcessesUrl);
- }
-
- @Override
- public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
- throws AuthenticationException, IOException, ServletException {
- @SuppressWarnings("unchecked")
- Map<String, String> map = restTemplate.getForObject(userInfoUrl, Map.class);
- if (!map.containsKey("user_id")) {
- throw new BadCredentialsException("User info does not contain user_id");
- }
- String userName = map.get("user_name");
- String userId = map.get("user_id");
- List<UaaAuthority> authorities = UaaAuthority.USER_AUTHORITIES;
- OpenIdUserDetails user = new OpenIdUserDetails(userName, authorities);
- user.setId(userId);
- if (map.containsKey("email")) {
- user.setEmail(map.get("email"));
- }
- if (map.containsKey("name")) {
- user.setName(map.get("name"));
- }
- return new UsernamePasswordAuthenticationToken(user, null, authorities);
- }
-
- @Override
- protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException failed) throws IOException, ServletException {
- if (failed instanceof AccessTokenRequiredException) {
- // Need to force a redirect via the OAuth2 client filter, so rethrow here
- 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);
- }
- }
-
-}
73 common/src/main/java/org/cloudfoundry/identity/uaa/openid/OpenIdUserDetails.java
View
@@ -1,73 +0,0 @@
-/*
- * 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.openid;
-
-import java.util.Collection;
-
-import org.cloudfoundry.identity.uaa.social.SocialClientUserDetails;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.userdetails.User;
-
-/**
- * Customized {@code UserDetails} implementation.
- *
- * @author Luke Taylor
- * @author Dave Syer
- *
- * @deprecated in favour of {@link SocialClientUserDetails}
- *
- */
-@Deprecated
-public class OpenIdUserDetails extends User {
- private String email;
- private String name;
- private boolean newUser;
- private String id;
-
- public OpenIdUserDetails(String username, Collection<? extends GrantedAuthority> authorities) {
- super(username, "unused", authorities);
- }
-
- public String getEmail() {
- return email;
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public void setEmail(String email) {
- this.email = email;
- }
-
- public boolean isNewUser() {
- return newUser;
- }
-
- public void setNewUser(boolean newUser) {
- this.newUser = newUser;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-}
-
134 common/src/main/java/org/cloudfoundry/identity/uaa/scim/GroupsUsersEndpoints.java
View
@@ -0,0 +1,134 @@
+/*
+ * 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.scim;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.cloudfoundry.identity.uaa.security.DefaultSecurityContextAccessor;
+import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
+import org.cloudfoundry.identity.uaa.user.UaaAuthority;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.View;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@Controller
+public class GroupsUsersEndpoints implements InitializingBean {
+
+ private SecurityContextAccessor securityContextAccessor = new DefaultSecurityContextAccessor();
+
+ private ScimUserEndpoints scimUserEndpoints;
+
+ private Set<Pattern> patterns = new HashSet<Pattern>();
+
+ {
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) eq (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) co (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) sw (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) gt (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) ge (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) lt (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("(.*?)([a-z0-9]*) le (.*?)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ patterns.add(Pattern.compile("pr (.*?)([a-z0-9]*)([\\s]*.*)", Pattern.CASE_INSENSITIVE));
+ }
+
+ void setSecurityContextAccessor(SecurityContextAccessor securityContextAccessor) {
+ this.securityContextAccessor = securityContextAccessor;
+ }
+
+ /**
+ * @param scimUserEndpoints the scimUserEndpoints to set
+ */
+ public void setScimUserEndpoints(ScimUserEndpoints scimUserEndpoints) {
+ this.scimUserEndpoints = scimUserEndpoints;
+ }
+
+ @RequestMapping(value = "/Groups/{group}/Users", method = RequestMethod.GET)
+ @ResponseBody
+ public SearchResults<Map<String, Object>> findUsers(@PathVariable String group,
+ @RequestParam(required = false, defaultValue = "") String filter,
+ @RequestParam(required = false, defaultValue = "ascending") String sortOrder,
+ @RequestParam(required = false, defaultValue = "1") int startIndex,
+ @RequestParam(required = false, defaultValue = "100") int count) {
+ checkFilter(filter);
+ checkGroup(group);
+ String appended = filter.trim();
+ appended = (appended.length() > 0 ? "(" : "") + appended + (appended.length() > 0 ? ") and " : "")
+ + "groups.display co '" + group + "'";
+ return scimUserEndpoints.findUsers("id,userName", appended, "userName", sortOrder, startIndex, count);
+ }
+
+ @ExceptionHandler
+ public View handleException(Exception t, HttpServletRequest request) throws ScimException {
+ return scimUserEndpoints.handleException(t, request);
+ }
+
+ private void checkFilter(String filter) {
+ String lowerCase = filter.toLowerCase();
+ if (lowerCase.contains("groups.")) {
+ throw new ScimException(
+ "Invalid filter expression: [" + filter + "] (no group filters allowed on /Groups)",
+ HttpStatus.BAD_REQUEST);
+ }
+ for (Pattern pattern : patterns) {
+ Matcher matcher = pattern.matcher(lowerCase);
+ if (matcher.matches()) {
+ String field = matcher.group(2);
+ if (!"username".equals(field) && !"id".equals(field)) {
+ throw new ScimException("Invalid filter expression: [" + filter + "] (no " + field
+ + " filters allowed on /Groups)", HttpStatus.BAD_REQUEST);
+ }
+ }
+ }
+ }
+
+ private void checkGroup(String group) {
+ if (securityContextAccessor.isClient() || securityContextAccessor.isAdmin()) {
+ return;
+ }
+ if (UaaAuthority.UAA_USER.toString().equals(group)) {
+ throw new ScimException("Current user is not allowed to query group: " + group, HttpStatus.FORBIDDEN);
+ }
+ Collection<? extends GrantedAuthority> authorities = securityContextAccessor.getAuthorities();
+ Set<String> values = AuthorityUtils.authorityListToSet(authorities);
+ if (!values.contains(group)) {
+ throw new ScimException("Current user is not in requested group: " + group, HttpStatus.FORBIDDEN);
+ }
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(scimUserEndpoints, "ScimUserEndpoints must be set");
+ }
+}
6 common/src/main/java/org/cloudfoundry/identity/uaa/scim/JdbcScimUserProvisioning.java
View
@@ -86,6 +86,8 @@
static final Pattern emailsValuePattern = Pattern.compile("emails\\.value", Pattern.CASE_INSENSITIVE);
+ static final Pattern groupsValuePattern = Pattern.compile("groups\\.display", Pattern.CASE_INSENSITIVE);
+
static final Pattern phoneNumbersValuePattern = Pattern.compile("phoneNumbers\\.value", Pattern.CASE_INSENSITIVE);
static final Pattern coPattern = Pattern.compile("(.*?)([a-z0-9]*) co '(.*?)'([\\s]*.*)", Pattern.CASE_INSENSITIVE);
@@ -171,6 +173,8 @@ public ScimUser retrieveUser(String id) {
// There is only one email address for now...
where = StringUtils.arrayToDelimitedString(emailsValuePattern.split(where), "email");
+ // There is only one field in groups for now...
+ where = StringUtils.arrayToDelimitedString(groupsValuePattern.split(where), "authorities");
// There is only one phone number for now...
where = StringUtils.arrayToDelimitedString(phoneNumbersValuePattern.split(where), "phoneNumber");
@@ -312,7 +316,7 @@ private String getAuthorities(ScimUser user) {
// Augment with explicit group membership
if (user.getGroups()!=null) {
for (Group group : user.getGroups()) {
- set.add(group.getExternalId());
+ set.add(group.getDisplay());
}
}
return StringUtils.collectionToCommaDelimitedString(set);
21 common/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUser.java
View
@@ -46,14 +46,12 @@
String display;
- String externalId;
-
public Group() {
}
- public Group(String value, String externalId) {
+ public Group(String value, String display) {
this.value = value;
- this.externalId = externalId;
+ this.display = display;
}
public String getValue() {
@@ -64,14 +62,6 @@ public void setValue(String value) {
this.value = value;
}
- public String getExternalId() {
- return externalId;
- }
-
- public void setExternalId(String externalId) {
- this.externalId = externalId;
- }
-
public String getDisplay() {
return display;
}
@@ -85,7 +75,6 @@ public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((display == null) ? 0 : display.hashCode());
- result = prime * result + ((externalId == null) ? 0 : externalId.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@@ -105,12 +94,6 @@ public boolean equals(Object obj) {
}
else if (!display.equals(other.display))
return false;
- if (externalId == null) {
- if (other.externalId != null)
- return false;
- }
- else if (!externalId.equals(other.externalId))
- return false;
if (value == null) {
if (other.value != null)
return false;
2  common/src/main/java/org/cloudfoundry/identity/uaa/scim/ScimUserEndpoints.java
View
@@ -363,6 +363,6 @@ void setSecurityContextAccessor(SecurityContextAccessor securityContextAccessor)
@Override
public void afterPropertiesSet() throws Exception {
- Assert.notNull(dao, "Dao must be set");
+ Assert.notNull(dao, "ScimUserProvisioning must be set");
}
}
2  common/src/main/java/org/cloudfoundry/identity/uaa/user/UaaUserEditor.java
View
@@ -14,9 +14,7 @@
import java.beans.PropertyEditorSupport;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import org.springframework.security.core.authority.AuthorityUtils;
16 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/CheckTokenEndpointTests.java
View
@@ -57,38 +57,38 @@ public CheckTokenEndpointTests() {
@Test
public void testUserIdInResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals("olds", result.get("user_name"));
assertEquals("12345", result.get("user_id"));
}
@Test
public void testEmailInResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals("olds@vmware.com", result.get("email"));
}
@Test
public void testClientIdInResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals("client", result.get("client_id"));
}
@Test
public void testExpiryResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertTrue(expiresIn + System.currentTimeMillis()/1000 >= Integer.parseInt(String.valueOf(result.get("exp"))));
}
@Test
public void testUserAuthoritiesNotInResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals(null, result.get("user_authorities"));
}
@Test
public void testClientAuthoritiesNotInResult() {
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals(null, result.get("client_authorities"));
}
@@ -98,7 +98,7 @@ public void testExpiredToken() throws Exception {
token.setExpiration(new Date(System.currentTimeMillis() - 100000));
expiresIn = token.getExpiresIn();
tokenStore.storeAccessToken(token, authentication);
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals("expired_token", result.get("error"));
}
@@ -110,7 +110,7 @@ public void testClientOnly() {
endpoint.setTokenServices(tokenServices);
OAuth2AccessToken token = new DefaultOAuth2AccessToken("FOO");
tokenStore.storeAccessToken(token, authentication);
- Map<String, Object> result = endpoint.checkToken("FOO");
+ Map<String, ?> result = endpoint.checkToken("FOO");
assertEquals("client", result.get("client_id"));
}
10 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/ClientAdminEndpointsTests.java
View
@@ -54,6 +54,8 @@
private ClientDetailsService clientDetailsService = Mockito.mock(ClientDetailsService.class);
+ private SecurityContextAccessor securityContextAccessor = Mockito.mock(SecurityContextAccessor.class);
+
private ClientRegistrationService clientRegistrationService = Mockito.mock(ClientRegistrationService.class);
@Rule
@@ -63,11 +65,13 @@
public void setUp() throws Exception {
endpoints.setClientDetailsService(clientDetailsService);
endpoints.setClientRegistrationService(clientRegistrationService);
+ endpoints.setSecurityContextAccessor(securityContextAccessor);
input.setClientId("foo");
input.setClientSecret("secret");
input.setAuthorizedGrantTypes(Arrays.asList("authorization_code"));
details = new BaseClientDetails(input);
details.setResourceIds(Arrays.asList("none"));
+ details.setScope(Arrays.asList("uaa.none"));
details.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none"));
endpoints.afterPropertiesSet();
}
@@ -285,7 +289,8 @@ public void testChangeSecretByAdmin() throws Exception {
}
@Test
- public void testRemoveClientDetails() throws Exception {
+ public void testRemoveClientDetailsAdminCaller() throws Exception {
+ Mockito.when(securityContextAccessor.isAdmin()).thenReturn(true);
Mockito.when(clientDetailsService.loadClientByClientId("foo")).thenReturn(details);
endpoints.createClientDetails(details);
ResponseEntity<Void> result = endpoints.removeClientDetails("foo");
@@ -355,7 +360,7 @@ public String getClientId() {
@Test
public void testAuthorityAllowedByCaller() throws Exception {
- BaseClientDetails caller = new BaseClientDetails("caller", null, "none", "client_credentials,implicit",
+ BaseClientDetails caller = new BaseClientDetails("caller", null, "uaa.none", "client_credentials,implicit",
"uaa.none");
when(clientDetailsService.loadClientByClientId("caller")).thenReturn(caller);
endpoints.setSecurityContextAccessor(new StubSecurityContextAccessor() {
@@ -421,6 +426,7 @@ public void nonImplicitClientWithEmptySecretIsRejected() throws Exception {
@Test
public void updateNonImplicitClientWithEmptySecretIsOk() throws Exception {
+ Mockito.when(securityContextAccessor.isAdmin()).thenReturn(true);
details.setAuthorizedGrantTypes(Arrays.asList("authorization_code"));
details.setClientSecret(null);
endpoints.updateClientDetails(details, details.getClientId());
77 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/JwtTokenEnhancerTests.java
View
@@ -13,11 +13,17 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import java.util.Map;
+
import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationTestFactory;
import org.cloudfoundry.identity.uaa.openid.UserInfo;
import org.junit.Before;
import org.junit.Test;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
@@ -43,45 +49,74 @@ public void setUp() throws Exception {
@Test
public void testEnhanceAccessToken() {
- OAuth2Authentication authentication = new OAuth2Authentication(
- new DefaultAuthorizationRequest("foo", null), userAuthentication);
+ OAuth2Authentication authentication = new OAuth2Authentication(new DefaultAuthorizationRequest("foo", null),
+ userAuthentication);
OAuth2AccessToken token = tokenEnhancer.enhance(new DefaultOAuth2AccessToken("FOO"), authentication);
assertNotNull(token.getValue());
assertEquals("FOO", token.getAdditionalInformation().get(JwtTokenEnhancer.TOKEN_ID));
String claims = JwtHelper.decode(token.getValue()).getClaims();
- assertTrue("Wrong claims: " + claims, claims.contains("\""+UserInfo.USER_ID+"\""));
- assertTrue("Wrong claims: " + claims, claims.contains("\""+JwtTokenEnhancer.TOKEN_ID+"\""));
+ assertTrue("Wrong claims: " + claims, claims.contains("\"" + UserInfo.USER_ID + "\""));
+ assertTrue("Wrong claims: " + claims, claims.contains("\"" + JwtTokenEnhancer.TOKEN_ID + "\""));
}
-
@Test
public void rsaKeyCreatesValidRsaSignedTokens() throws Exception {
- String rsaKey = " -----BEGIN RSA PRIVATE KEY----- \n" +
- " MIIBywIBAAJhAOTeb4AZ+NwOtPh+ynIgGqa6UWNVe6JyJi+loPmPZdpHtzoqubnC \n" +
- " wEs6JSiSZ3rButEAw8ymgLV6iBY02hdjsl3h5Z0NWaxx8dzMZfXe4EpfB04ISoqq\n" +
- " hZCxchvuSDP4eQIDAQABAmEAqUuYsuuDWFRQrZgsbGsvC7G6zn3HLIy/jnM4NiJK\n" +
- " t0JhWNeN9skGsR7bqb1Sak2uWqW8ZqnqgAC32gxFRYHTavJEk6LTaHWovwDEhPqc\n" +
- " Zs+vXd6tZojJQ35chR/slUEBAjEA/sAd1oFLWb6PHkaz7r2NllwUBTvXL4VcMWTS\n" +
- " pN+5cU41i9fsZcHw6yZEl+ZCicDxAjEA5f3R+Bj42htNI7eylebew1+sUnFv1xT8\n" +
- " jlzxSzwVkoZo+vef7OD6OcFLeInAHzAJAjEAs6izolK+3ETa1CRSwz0lPHQlnmdM\n" +
- " Y/QuR5tuPt6U/saEVuJpkn4LNRtg5qt6I4JRAjAgFRYTG7irBB/wmZFp47izXEc3\n" +
- " gOdvA1hvq3tlWU5REDrYt24xpviA0fvrJpwMPbECMAKDKdiDi6Q4/iBkkzNMefA8\n" +
- " 7HX27b9LR33don/1u/yvzMUo+lrRdKAFJ+9GPE9XFA== \n" +
- "-----END RSA PRIVATE KEY----- ";
+ String rsaKey = "-----BEGIN RSA PRIVATE KEY----- \n"
+ + "MIIBywIBAAJhAOTeb4AZ+NwOtPh+ynIgGqa6UWNVe6JyJi+loPmPZdpHtzoqubnC \n"
+ + "wEs6JSiSZ3rButEAw8ymgLV6iBY02hdjsl3h5Z0NWaxx8dzMZfXe4EpfB04ISoqq\n"
+ + "hZCxchvuSDP4eQIDAQABAmEAqUuYsuuDWFRQrZgsbGsvC7G6zn3HLIy/jnM4NiJK\n"
+ + "t0JhWNeN9skGsR7bqb1Sak2uWqW8ZqnqgAC32gxFRYHTavJEk6LTaHWovwDEhPqc\n"
+ + "Zs+vXd6tZojJQ35chR/slUEBAjEA/sAd1oFLWb6PHkaz7r2NllwUBTvXL4VcMWTS\n"
+ + "pN+5cU41i9fsZcHw6yZEl+ZCicDxAjEA5f3R+Bj42htNI7eylebew1+sUnFv1xT8\n"
+ + "jlzxSzwVkoZo+vef7OD6OcFLeInAHzAJAjEAs6izolK+3ETa1CRSwz0lPHQlnmdM\n"
+ + "Y/QuR5tuPt6U/saEVuJpkn4LNRtg5qt6I4JRAjAgFRYTG7irBB/wmZFp47izXEc3\n"
+ + "gOdvA1hvq3tlWU5REDrYt24xpviA0fvrJpwMPbECMAKDKdiDi6Q4/iBkkzNMefA8\n"
+ + "7HX27b9LR33don/1u/yvzMUo+lrRdKAFJ+9GPE9XFA== \n" + "-----END RSA PRIVATE KEY----- ";
tokenEnhancer.setSigningKey(rsaKey);
- OAuth2Authentication authentication = new OAuth2Authentication(
- new DefaultAuthorizationRequest("foo", null), userAuthentication);
+ OAuth2Authentication authentication = new OAuth2Authentication(new DefaultAuthorizationRequest("foo", null),
+ userAuthentication);
OAuth2AccessToken token = tokenEnhancer.enhance(new DefaultOAuth2AccessToken("FOO"), authentication);
JwtHelper.decodeAndVerify(token.getValue(), new RsaVerifier(rsaKey));
}
@Test
public void publicKeyStringIsReturnedFromTokenKeyEndpoint() throws Exception {
+ tokenEnhancer.setVerifierKey("-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo\n"
+ + "kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb\n" + "7kgz+HkCAwEAAQ==\n"
+ + "-----END RSA PUBLIC KEY-----");
+ Map<String, String> key = tokenEnhancer.getKey(new UsernamePasswordAuthenticationToken("foo", "bar"));
+ assertTrue("Wrong key: " + key, key.get("value").contains("-----BEGIN"));
+ }
+
+ @Test
+ public void publicKeyStringIsReturnedFromTokenKeyEndpointWithNullPrincipal() throws Exception {
+ tokenEnhancer.setVerifierKey("-----BEGIN RSA PUBLIC KEY-----\n"
+ + "MGgCYQDk3m+AGfjcDrT4fspyIBqmulFjVXuiciYvpaD5j2XaR7c6Krm5wsBLOiUo\n"
+ + "kmd6wbrRAMPMpoC1eogWNNoXY7Jd4eWdDVmscfHczGX13uBKXwdOCEqKqoWQsXIb\n" + "7kgz+HkCAwEAAQ==\n"
+ + "-----END RSA PUBLIC KEY-----");
+ Map<String, String> key = tokenEnhancer.getKey(null);
+ assertTrue("Wrong key: " + key, key.get("value").contains("-----BEGIN"));
+ }
+
+ @Test
+ public void sharedSecretIsReturnedFromTokenKeyEndpoint() throws Exception {
+ tokenEnhancer.setVerifierKey("someKey");
+ assertEquals("{alg=HMACSHA256, value=someKey}",
+ tokenEnhancer.getKey(new UsernamePasswordAuthenticationToken("foo", "bar")).toString());
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ public void sharedSecretCannotBeAnonymouslyRetrievedFromTokenKeyEndpoint() throws Exception {
tokenEnhancer.setVerifierKey("someKey");
- assertEquals("{alg=HMACSHA256, value=someKey}", tokenEnhancer.getKey().toString());
+ assertEquals(
+ "{alg=HMACSHA256, value=someKey}",
+ tokenEnhancer.getKey(
+ new AnonymousAuthenticationToken("anon", "anonymousUser", AuthorityUtils
+ .createAuthorityList("ROLE_ANONYMOUS"))).toString());
}
- @Test(expected=IllegalStateException.class)
+ @Test(expected = IllegalStateException.class)
public void keysNotMatchingWithMacSigner() throws Exception {
tokenEnhancer.setSigningKey("aKey");
tokenEnhancer.setVerifierKey("someKey");
19 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/TokenAdminEndpointsTests.java
View
@@ -47,8 +47,8 @@
private ScimUserProvisioning scimProvisioning = Mockito.mock(ScimUserProvisioning.class);
- private AuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(Collections.singletonMap("client_id",
- "foo"));
+ private AuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest(Collections.singletonMap(
+ "client_id", "foo"));
{
endpoints.setTokenServices(tokenServices);
@@ -104,7 +104,7 @@ public void testRevokeTokenForUser() throws Exception {
Mockito.when(tokenServices.findTokensByUserName("marissa")).thenReturn(
Collections.<OAuth2AccessToken> singleton(new DefaultOAuth2AccessToken("FOO")));
Mockito.when(tokenServices.revokeToken("FOO")).thenReturn(true);
- ResponseEntity<Void> result = endpoints.revokeUserToken("marissa", new StandardPasswordEncoder().encode("FOO"),
+ ResponseEntity<?> result = endpoints.revokeUserToken("marissa", new StandardPasswordEncoder().encode("FOO"),
new TestingAuthenticationToken("marissa", ""), false);
assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
}
@@ -116,7 +116,7 @@ public void testRevokeTokenForUserWithTokenId() throws Exception {
Mockito.when(tokenServices.findTokensByUserName("marissa")).thenReturn(
Collections.<OAuth2AccessToken> singleton(token));
Mockito.when(tokenServices.revokeToken("FOO")).thenReturn(true);
- ResponseEntity<Void> result = endpoints.revokeUserToken("marissa", "BAR", new TestingAuthenticationToken(
+ ResponseEntity<?> result = endpoints.revokeUserToken("marissa", "BAR", new TestingAuthenticationToken(
"marissa", ""), false);
assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
}
@@ -125,15 +125,15 @@ public void testRevokeTokenForUserWithTokenId() throws Exception {
public void testRevokeInvalidTokenForUser() throws Exception {
OAuth2AccessToken token = new DefaultOAuth2AccessToken("BAR");
Mockito.when(tokenServices.findTokensByUserName("marissa")).thenReturn(Collections.singleton(token));
- ResponseEntity<Void> result = endpoints.revokeUserToken("marissa", "FOO", new TestingAuthenticationToken(
+ ResponseEntity<?> result = endpoints.revokeUserToken("marissa", "FOO", new TestingAuthenticationToken(
"marissa", ""), false);
assertEquals(HttpStatus.NOT_FOUND, result.getStatusCode());
}
@Test
public void testRevokeNullTokenForUser() throws Exception {
- ResponseEntity<Void> result = endpoints.revokeUserToken("marissa", null, new TestingAuthenticationToken(
- "marissa", ""), false);
+ ResponseEntity<?> result = endpoints.revokeUserToken("marissa", null, new TestingAuthenticationToken("marissa",
+ ""), false);
assertEquals(HttpStatus.NOT_FOUND, result.getStatusCode());
}
@@ -170,15 +170,14 @@ public void testRevokeTokenForClient() throws Exception {
Mockito.when(tokenServices.findTokensByClientId("foo")).thenReturn(
Collections.<OAuth2AccessToken> singleton(new DefaultOAuth2AccessToken("FOO")));
Mockito.when(tokenServices.revokeToken("FOO")).thenReturn(true);
- ResponseEntity<Void> result = endpoints.revokeClientToken("foo", new StandardPasswordEncoder().encode("FOO"),
+ ResponseEntity<?> result = endpoints.revokeClientToken("foo", new StandardPasswordEncoder().encode("FOO"),
new TestingAuthenticationToken("foo", ""));
assertEquals(HttpStatus.NO_CONTENT, result.getStatusCode());
}
@Test
public void testRevokeInvalidTokenForClient() throws Exception {
- ResponseEntity<Void> result = endpoints.revokeClientToken("foo", "FOO", new TestingAuthenticationToken("foo",
- ""));
+ ResponseEntity<?> result = endpoints.revokeClientToken("foo", "FOO", new TestingAuthenticationToken("foo", ""));
assertEquals(HttpStatus.NOT_FOUND, result.getStatusCode());
}
63 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthenticationKeyGeneratorTests.java
View
@@ -0,0 +1,63 @@
+/*
+ * 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.oauth;
+
+import static org.junit.Assert.assertNotSame;
+
+import java.util.Arrays;
+
+import org.cloudfoundry.identity.uaa.authentication.UaaAuthentication;
+import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationTestFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.security.oauth2.provider.AuthorizationRequest;
+import org.springframework.security.oauth2.provider.BaseClientDetails;
+import org.springframework.security.oauth2.provider.ClientDetails;
+import org.springframework.security.oauth2.provider.ClientDetailsService;
+import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class UaaAuthenticationKeyGeneratorTests {
+
+ private UaaAuthenticationKeyGenerator generator = new UaaAuthenticationKeyGenerator();
+
+ private ClientDetailsService clientDetailsService = Mockito.mock(ClientDetailsService.class);
+
+ private AuthorizationRequest authorizationRequest = new DefaultAuthorizationRequest("client", Arrays.asList("read",
+ "write"));
+
+ private UaaAuthentication userAuthentication = UaaAuthenticationTestFactory.getAuthentication("FOO", "foo",
+ "foo@test.org");
+
+ @Before
+ public void init() {
+ ClientDetails client = new BaseClientDetails("client", "none", "read,write", "authorization_code", "uaa.none");
+ Mockito.when(clientDetailsService.loadClientByClientId("client")).thenReturn(client);
+ generator.setClientDetailsService(clientDetailsService);
+ }
+
+ @Test
+ public void testEmailChanges() {
+ String key1 = generator.extractKey(new OAuth2Authentication(authorizationRequest, userAuthentication));
+ userAuthentication = UaaAuthenticationTestFactory.getAuthentication("FOO", "foo", "foo@none.org");
+ String key2 = generator.extractKey(new OAuth2Authentication(authorizationRequest, userAuthentication));
+ assertNotSame(key1, key2);
+ }
+
+}
9 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaAuthorizationRequestFactoryTests.java
View
@@ -29,6 +29,7 @@
import org.mockito.Mockito;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.BaseClientDetails;
@@ -186,6 +187,14 @@ public void testScopesInvalid() throws Exception {
factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write", "implicit", null));
}
+ @Test(expected=BadClientCredentialsException.class)
+ public void tesstWrongClientId() {
+ parameters.put("grant_type", "authorization_code");
+ parameters.put("client_id", "bar");
+ parameters.put("scope", "read");
+ factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write", "implicit", null));
+ }
+
private static class StubSecurityContextAccessor implements SecurityContextAccessor {
@Override
40 common/src/test/java/org/cloudfoundry/identity/uaa/oauth/UaaUserTokenConverterTests.java
View
@@ -0,0 +1,40 @@
+/*
+ * 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.oauth;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Map;
+
+import org.cloudfoundry.identity.uaa.authentication.UaaAuthenticationTestFactory;
+import org.cloudfoundry.identity.uaa.openid.UserInfo;
+import org.junit.Test;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class UaaUserTokenConverterTests {
+
+ private UaaUserTokenConverter converter = new UaaUserTokenConverter();
+
+ @Test
+ public void test() {
+ Map<String, ?> map = converter.convertUserAuthentication(UaaAuthenticationTestFactory.getAuthentication("FOO", "foo", "foo@test.org"));
+ assertEquals("FOO", map.get(UserInfo.USER_ID));
+ assertEquals("foo@test.org", map.get(UserInfo.EMAIL));
+ assertEquals("foo", map.get(UserInfo.USER_NAME));
+ }
+
+}
87 common/src/test/java/org/cloudfoundry/identity/uaa/openid/OpenIdClientFilterTests.java
View
@@ -1,87 +0,0 @@
-/*
- * 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.openid;
-
-import static org.junit.Assert.assertNotNull;
-
-import java.util.HashMap;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.junit.Test;
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth2.client.http.AccessTokenRequiredException;
-import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
-
-/**
- * @author Dave Syer
- *
- */
-@SuppressWarnings("deprecation")
-public class OpenIdClientFilterTests {
-
- private OpenIdClientFilter filter = new OpenIdClientFilter("/login");
- private HttpServletRequest request = new MockHttpServletRequest();
- private HttpServletResponse response = new MockHttpServletResponse();
-
- @Test
- public void testFilterSunnyDay() throws Exception {
- filter.setRestTemplate(new RestTemplate() {
- @SuppressWarnings("unchecked")
- @Override
- public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables)
- throws RestClientException {
- HashMap<String, String> map = new HashMap<String, String>();
- map.put("user_id", "12345");
- map.put("user_name", "foo");
- map.put("email", "foo@bar.com");
- return (T) map;
- }
- });
- Authentication authentication = filter.attemptAuthentication(request , response);
- assertNotNull(authentication);
- }
-
- @Test(expected=BadCredentialsException.class)
- public void testFilterMissingId() throws Exception {
- filter.setRestTemplate(new RestTemplate() {
- @SuppressWarnings("unchecked")
- @Override
- public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables)
- throws RestClientException {
- HashMap<String, String> map = new HashMap<String, String>();
- return (T) map;
- }
- });
- Authentication authentication = filter.attemptAuthentication(request , response);
- assertNotNull(authentication);
- }
-
- @Test(expected=AccessTokenRequiredException.class)
- public void testFilterUnsuccessfulWithAccessTokenRequired() throws Exception {
- filter.unsuccessfulAuthentication(request, response, new AccessTokenRequiredException(new BaseOAuth2ProtectedResourceDetails()));
- }
-
- @Test
- public void testFilterUnsuccessfulWithRuntimeException() throws Exception {
- // The default strategy should kick in for a vanilla AuthenticationException
- filter.unsuccessfulAuthentication(request, response, new BadCredentialsException("test"));
- }
-
-}
4 common/src/test/java/org/cloudfoundry/identity/uaa/password/PasswordCheckEndpointTests.java
View
@@ -13,12 +13,8 @@
package org.cloudfoundry.identity.uaa.password;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import java.util.Map;
-
import org.junit.Test;
/**
87 common/src/test/java/org/cloudfoundry/identity/uaa/scim/GroupsUsersEndpointsTests.java
View
@@ -0,0 +1,87 @@
+/*
+ * 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.scim;
+
+import static org.junit.internal.matchers.StringContains.containsString;
+
+import java.util.Collection;
+
+import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+import org.springframework.security.core.authority.AuthorityUtils;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class GroupsUsersEndpointsTests {
+
+ @Rule
+ public ExpectedException expected = ExpectedException.none();
+
+ private GroupsUsersEndpoints endpoints = new GroupsUsersEndpoints();
+
+ private SecurityContextAccessor securityContextAccessor = Mockito.mock(SecurityContextAccessor.class);
+
+ private ScimUserEndpoints scimUserEndpoints = Mockito.mock(ScimUserEndpoints.class);
+
+ @SuppressWarnings("rawtypes")
+ private Collection authorities = (Collection) AuthorityUtils.commaSeparatedStringToAuthorityList("orgs.foo,uaa.user");
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void init() {
+ endpoints.setSecurityContextAccessor(securityContextAccessor);
+ endpoints.setScimUserEndpoints(scimUserEndpoints);
+ Mockito.when(securityContextAccessor.getAuthorities()).thenReturn(authorities);
+ }
+
+ @Test
+ public void testDefaultFilterHappyDay() {
+ endpoints.findUsers("orgs.foo", "", "ascending", 0, 100);
+ }
+
+ @Test
+ public void testDefaultFilterWrongGroup() {
+ expected.expect(ScimException.class);
+ expected.expectMessage(containsString("Current user"));
+ endpoints.findUsers("orgs.bar", "", "ascending", 0, 100);
+ }
+
+ @Test
+ public void testBadFieldInFilter() {
+ expected.expect(ScimException.class);
+ expected.expectMessage(containsString("Invalid filter"));
+ endpoints.findUsers("orgs.foo", "emails.value eq 'foo@bar.org'", "ascending", 0, 100);
+ }
+
+ @Test
+ public void testBadFilterWithGroup() {
+ expected.expect(ScimException.class);
+ expected.expectMessage(containsString("Invalid filter"));
+ endpoints.findUsers("orgs.foo", "groups.display co 'foo'", "ascending", 0, 100);
+ }
+
+ @Test
+ public void testBadGroup() {
+ expected.expect(ScimException.class);
+ expected.expectMessage(containsString("Current user"));
+ endpoints.findUsers("uaa.user", "", "ascending", 0, 100);
+ }
+
+}
13 common/src/test/java/org/cloudfoundry/identity/uaa/scim/JdbcScimUserProvisioningTests.java
View
@@ -68,7 +68,7 @@
private static final String SQL_INJECTION_FIELDS = "password,version,created,lastModified,username,email,givenName,familyName";
- private static final String addUserSqlFormat = "insert into users (id, username, password, email, givenName, familyName, phoneNumber) values ('%s','%s','%s','%s','%s','%s','%s')";
+ private static final String addUserSqlFormat = "insert into users (id, username, password, email, givenName, familyName, phoneNumber, authorities) values ('%s','%s','%s','%s','%s','%s','%s', '%s')";
private static final String deleteUserSqlFormat = "delete from users where id='%s'";
@@ -96,7 +96,7 @@ private String createUserForDelete() {
private void addUser(String id, String username, String password, String email, String givenName, String familyName, String phoneNumber) {
TestUtils.assertNoSuchUser(template, "id", id);
- template.execute(String.format(addUserSqlFormat, id, username, password, email, givenName, familyName, phoneNumber));
+ template.execute(String.format(addUserSqlFormat, id, username, password, email, givenName, familyName, phoneNumber, "uaa.user,org.foo"));
}
private void removeUser (String id) {
@@ -121,7 +121,7 @@ public void canCreateUser() {
Map<String, Object> map = template.queryForMap("select * from users where id=?", created.getId());
assertEquals(user.getUserName(), map.get("userName"));
assertEquals(user.getUserType(), map.get(UaaAuthority.UAA_USER.getUserType()));
- assertEquals("uaa.user", created.getGroups().iterator().next().getExternalId());
+ assertEquals("uaa.user", created.getGroups().iterator().next().getDisplay());
}
@Test(expected = InvalidUserException.class)
@@ -349,6 +349,11 @@ public void canRetrieveUsersWithEmailFilter() {
}
@Test
+ public void canRetrieveUsersWithGroupsFilter() {
+ assertEquals(2, db.retrieveUsers("groups.display co 'org.foo'").size());
+ }
+
+ @Test
public void canRetrieveUsersWithPhoneNumberFilter() {
assertEquals(1, db.retrieveUsers("phoneNumbers.value sw '+1-222'").size());
}
@@ -472,7 +477,7 @@ private void assertJoe(ScimUser joe) {
assertEquals("joe@joe.com", joe.getPrimaryEmail());
assertEquals("joe", joe.getUserName());
assertEquals("+1-222-1234567", joe.getPhoneNumbers().get(0).getValue());
- assertEquals("uaa.user", joe.getGroups().iterator().next().getExternalId());
+ assertEquals("uaa.user", joe.getGroups().iterator().next().getDisplay());
}
}
4 common/src/test/java/org/cloudfoundry/identity/uaa/social/OAuthClientAuthenticationFilterTests.java
View
@@ -22,7 +22,7 @@
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
+import org.springframework.security.oauth.common.signature.SharedConsumerSecretImpl;
import org.springframework.security.oauth.consumer.BaseProtectedResourceDetails;
import org.springframework.security.oauth.consumer.OAuthConsumerToken;
import org.springframework.security.oauth.consumer.OAuthSecurityContextHolder;
@@ -56,7 +56,7 @@ private void setUpContext(String tokenName, String secretName, String keyName, S
OAuthSecurityContextImpl context = new OAuthSecurityContextImpl();
OAuthConsumerToken token = new OAuthConsumerToken();
resource.setConsumerKey(consumerKey);