/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] Pivotal Software, 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 org.cloudfoundry.identity.uaa.constants.OriginKeys;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
import org.cloudfoundry.identity.uaa.oauth.token.TokenConstants;
import org.cloudfoundry.identity.uaa.provider.IdentityProviderProvisioning;
import org.cloudfoundry.identity.uaa.security.SecurityContextAccessor;
import org.cloudfoundry.identity.uaa.security.StubSecurityContextAccessor;
import org.cloudfoundry.identity.uaa.user.UaaUser;
import org.cloudfoundry.identity.uaa.user.UaaUserDatabase;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.MultitenancyFixture;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
import org.springframework.security.oauth2.common.exceptions.UnauthorizedClientException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static org.cloudfoundry.identity.uaa.oauth.client.ClientConstants.REQUIRED_USER_GROUPS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.security.oauth2.common.util.OAuth2Utils.CLIENT_ID;
public class UaaAuthorizationRequestManagerTests {
@Rule
public ExpectedException expectedException = ExpectedException.none();
private UaaAuthorizationRequestManager factory;
private ClientDetailsService clientDetailsService = mock(ClientDetailsService.class);
private UaaUserDatabase uaaUserDatabase = mock(UaaUserDatabase.class);
private IdentityProviderProvisioning providerProvisioning = mock(IdentityProviderProvisioning.class);
private Map<String, String> parameters = new HashMap<String, String>();
private BaseClientDetails client = new BaseClientDetails();
private UaaUser user = null;
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz");
}
};
@Rule
public ExpectedException thrown= ExpectedException.none();
@Before
public void initUaaAuthorizationRequestManagerTests() {
parameters.put("client_id", "foo");
factory = new UaaAuthorizationRequestManager(clientDetailsService, uaaUserDatabase, providerProvisioning);
factory.setSecurityContextAccessor(new StubSecurityContextAccessor());
when(clientDetailsService.loadClientByClientId("foo")).thenReturn(client);
user = new UaaUser("testid", "testuser","","test@test.org",AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz,space.1.developer,space.2.developer,space.1.admin"),"givenname", "familyname", null, null, OriginKeys.UAA, null, true, IdentityZone.getUaa().getId(), "testid", new Date());
when(uaaUserDatabase.retrieveUserById(any())).thenReturn(user);
}
@After
public void clearZoneContext() {
IdentityZoneHolder.clear();
SecurityContextHolder.clearContext();
}
@Test
public void testClientIDPAuthorizationInUAAzoneNoList() {
factory.checkClientIdpAuthorization(client, user);
}
@Test
public void testClientIDPAuthorizationInNonUAAzoneNoList() {
IdentityZoneHolder.set(MultitenancyFixture.identityZone("test", "test"));
factory.checkClientIdpAuthorization(client, user);
}
@Test
public void testClientIDPAuthorizationInUAAzoneListSucceeds() {
when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(MultitenancyFixture.identityProvider("random", "random"));
client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Arrays.asList("random"));
factory.checkClientIdpAuthorization(client, user);
}
@Test(expected = UnauthorizedClientException.class)
public void testClientIDPAuthorizationInUAAzoneListFails() {
when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(MultitenancyFixture.identityProvider("random", "random"));
client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Arrays.asList("random2"));
factory.checkClientIdpAuthorization(client, user);
}
@Test(expected = UnauthorizedClientException.class)
public void testClientIDPAuthorizationInUAAzoneNullProvider() {
when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenReturn(null);
client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Arrays.asList("random2"));
factory.checkClientIdpAuthorization(client, user);
}
@Test(expected = UnauthorizedClientException.class)
public void testClientIDPAuthorizationInUAAzoneEmptyResultSetException() {
when(providerProvisioning.retrieveByOrigin(anyString(), anyString())).thenThrow(new EmptyResultDataAccessException(1));
client.addAdditionalInformation(ClientConstants.ALLOWED_PROVIDERS, Arrays.asList("random2"));
factory.checkClientIdpAuthorization(client, user);
}
@Test
public void testTokenRequestIncludesResourceIds() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return false;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("aud1.test aud2.test");
}
};
parameters.put("scope", "aud1.test aud2.test");
parameters.put("client_id", client.getClientId());
parameters.put(OAuth2Utils.GRANT_TYPE, "client_credentials");
factory.setDefaultScopes(Arrays.asList("aud1.test"));
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("aud1.test,aud2.test"));
OAuth2Request request = factory.createTokenRequest(parameters, client).createOAuth2Request(client);
assertEquals(StringUtils.commaDelimitedListToSet("aud1.test,aud2.test"), new TreeSet<>(request.getScope()));
assertEquals(StringUtils.commaDelimitedListToSet("aud1,aud2"), new TreeSet<>(request.getResourceIds()));
}
@Test
public void test_user_token_request() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.user,requested.scope");
}
};
BaseClientDetails recipient = new BaseClientDetails("recipient", "requested", "requested.scope", "password", "");
parameters.put("scope", "requested.scope");
parameters.put("client_id", recipient.getClientId());
parameters.put("expires_in", "44000");
parameters.put(OAuth2Utils.GRANT_TYPE, TokenConstants.GRANT_TYPE_USER_TOKEN);
factory.setDefaultScopes(Arrays.asList("uaa.user"));
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("aud1.test,aud2.test,uaa.user"));
when(clientDetailsService.loadClientByClientId(recipient.getClientId())).thenReturn(recipient);
ReflectionTestUtils.setField(factory, "uaaUserDatabase", null);
client.setClientId("requestingId");
OAuth2Request request = factory.createTokenRequest(parameters, client).createOAuth2Request(recipient);
assertEquals(recipient.getClientId(), request.getClientId());
assertEquals(recipient.getClientId(), request.getRequestParameters().get(CLIENT_ID));
assertEquals(client.getClientId(), request.getRequestParameters().get(TokenConstants.USER_TOKEN_REQUESTING_CLIENT_ID));
assertEquals(StringUtils.commaDelimitedListToSet("requested.scope"), new TreeSet<>(request.getScope()));
assertEquals(StringUtils.commaDelimitedListToSet(recipient.getClientId()+",requested"), new TreeSet<>(request.getResourceIds()));
assertEquals("44000", request.getRequestParameters().get("expires_in"));
}
@Test
public void testFactoryProducesSomething() {
assertNotNull(factory.createAuthorizationRequest(parameters));
}
@Test
public void testScopeDefaultsToAuthoritiesForClientCredentials() {
client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz"));
parameters.put("grant_type", "client_credentials");
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("foo.bar,spam.baz"), request.getScope());
}
@Test
public void testScopeIncludesAuthoritiesForUser() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz");
}
};
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("one,two,foo.bar"));
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("foo.bar"), new TreeSet<String>(request.getScope()));
factory.validateParameters(request.getRequestParameters(), client);
}
@Test
public void testWildcardScopesIncludesAuthoritiesForUser() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList(
"space.1.developer,space.2.developer,space.1.admin"
);
}
};
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("space.*.developer"));
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("space.1.developer,space.2.developer"), new TreeSet<String>(request.getScope()));
factory.validateParameters(request.getRequestParameters(), client);
}
@Test
public void testOpenidScopeIncludeIsAResourceId() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz");
}
};
parameters.put("scope", "openid foo.bar");
factory.setDefaultScopes(Arrays.asList("openid"));
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("openid,foo.bar"));
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("openid,foo.bar"), new TreeSet<String>(request.getScope()));
assertEquals(StringUtils.commaDelimitedListToSet("openid,foo"), new TreeSet<String>(request.getResourceIds()));
}
@Test
public void testEmptyScopeOkForClientWithNoScopes() {
SecurityContextAccessor securityContextAccessor = new StubSecurityContextAccessor() {
@Override
public boolean isUser() {
return true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz");
}
};
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("")); // empty
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet(""), new TreeSet<String>(request.getScope()));
}
@Test
public void testEmptyScopeFailsClientWithScopes() {
factory.setSecurityContextAccessor(securityContextAccessor);
client.setScope(StringUtils.commaDelimitedListToSet("one,two")); // not empty
expectedException.expect(InvalidScopeException.class);
expectedException.expectMessage("[one, two] is invalid. This user is not allowed any of the requested scopes");
factory.createAuthorizationRequest(parameters);
throw new AssertionError();
}
@Test
@Ignore
public void missing_required_user_groups() {
expectedException.expect(InvalidScopeException.class);
expectedException.expectMessage("User does not meet the client's required group criteria.");
factory.setSecurityContextAccessor(securityContextAccessor);
client.addAdditionalInformation(REQUIRED_USER_GROUPS, Arrays.asList("group.that.doesnt.exist"));
factory.createAuthorizationRequest(parameters);
throw new AssertionError();
}
@Test
public void testResourecIdsExtracted() {
client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("foo.bar,spam.baz"));
parameters.put("grant_type", "client_credentials");
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("foo,spam"), request.getResourceIds());
}
@Test
public void testResourecIdsDoNotIncludeUaa() {
client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("uaa.none,spam.baz"));
parameters.put("grant_type", "client_credentials");
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("spam"), request.getResourceIds());
}
@Test
public void testResourceIdsWithCustomSeparator() {
factory.setScopeSeparator("--");
client.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList("foo--bar,spam--baz"));
parameters.put("grant_type", "client_credentials");
AuthorizationRequest request = factory.createAuthorizationRequest(parameters);
assertEquals(StringUtils.commaDelimitedListToSet("foo,spam"), request.getResourceIds());
}
@Test
public void testScopesValid() throws Exception {
parameters.put("scope","read");
factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write", "implicit", null));
}
@Test
public void testScopesValidWithWildcard() throws Exception {
parameters.put("scope","read write space.1.developer space.2.developer");
factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write,space.*.developer", "implicit", null));
}
@Test
public void testScopesInvValidWithWildcard() throws Exception {
thrown.expect(InvalidScopeException.class);
thrown.expectMessage("space.1.admin is invalid. Please use a valid scope name in the request");
parameters.put("scope","read write space.1.developer space.2.developer space.1.admin");
factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write,space.*.developer", "implicit", null));
}
@Test
public void testScopesInvalid() throws Exception {
thrown.expect(InvalidScopeException.class);
thrown.expectMessage("admin is invalid. Please use a valid scope name in the request");
parameters.put("scope", "admin");
factory.validateParameters(parameters, new BaseClientDetails("foo", null, "read,write", "implicit", null));
}
@Test
public void testWildcardIntersect1() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer"));
Set<String> requested = client;
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(2, result.size());
assertTrue(result.contains("space.1.developer"));
assertTrue(result.contains("space.2.developer"));
}
@Test
public void testWildcardIntersect2() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer"));
Set<String> requested = new HashSet<>(Arrays.asList("space.1.developer"));
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(1, result.size());
assertTrue(result.contains("space.1.developer"));
}
@Test
public void testWildcardIntersect3() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer"));
Set<String> requested = new HashSet<>(Arrays.asList("space.*.admin"));
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(0, result.size());
}
@Test
public void testWildcardIntersect4() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer","space.*.admin"));
Set<String> requested = new HashSet<>(Arrays.asList("space.*.admin"));
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(1, result.size());
assertTrue(result.contains("space.1.admin"));
}
@Test
public void testWildcardIntersect5() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer","space.*.admin", "space.3.operator"));
Set<String> requested = client;
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(4, result.size());
assertTrue(result.contains("space.1.admin"));
assertTrue(result.contains("space.3.operator"));
assertTrue(result.contains("space.1.developer"));
assertTrue(result.contains("space.2.developer"));
}
@Test
public void testWildcardIntersect6() throws Exception {
Set<String> client = new HashSet<>(Arrays.asList("space.*.developer,space.*.admin"));
Set<String> requested = new HashSet<>(Arrays.asList("space.*.admin"));
Set<String> user = new HashSet<>(Arrays.asList("space.1.developer","space.2.developer","space.1.admin","space.3.operator"));
Set<String> result = factory.intersectScopes(requested, client, user);
assertEquals(0, result.size());
}
}