package org.cloudfoundry.identity.uaa.mock.clients;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Iterables;
import org.cloudfoundry.identity.uaa.approval.Approval;
import org.cloudfoundry.identity.uaa.approval.Approval.ApprovalStatus;
import org.cloudfoundry.identity.uaa.approval.JdbcApprovalStore;
import org.cloudfoundry.identity.uaa.audit.AuditEventType;
import org.cloudfoundry.identity.uaa.audit.event.AbstractUaaEvent;
import org.cloudfoundry.identity.uaa.client.ClientMetadata;
import org.cloudfoundry.identity.uaa.client.UaaScopes;
import org.cloudfoundry.identity.uaa.client.event.ClientAdminEventPublisher;
import org.cloudfoundry.identity.uaa.client.event.ClientApprovalsDeletedEvent;
import org.cloudfoundry.identity.uaa.client.event.ClientCreateEvent;
import org.cloudfoundry.identity.uaa.client.event.ClientDeleteEvent;
import org.cloudfoundry.identity.uaa.client.event.ClientUpdateEvent;
import org.cloudfoundry.identity.uaa.client.event.SecretChangeEvent;
import org.cloudfoundry.identity.uaa.client.event.SecretFailureEvent;
import org.cloudfoundry.identity.uaa.error.UaaException;
import org.cloudfoundry.identity.uaa.mock.util.MockMvcUtils;
import org.cloudfoundry.identity.uaa.oauth.client.ClientDetailsModification;
import org.cloudfoundry.identity.uaa.oauth.client.SecretChangeRequest;
import org.cloudfoundry.identity.uaa.resources.ActionResult;
import org.cloudfoundry.identity.uaa.resources.SearchResults;
import org.cloudfoundry.identity.uaa.scim.ScimGroup;
import org.cloudfoundry.identity.uaa.scim.ScimGroupMember;
import org.cloudfoundry.identity.uaa.scim.ScimUser;
import org.cloudfoundry.identity.uaa.scim.endpoints.ScimGroupEndpoints;
import org.cloudfoundry.identity.uaa.scim.endpoints.ScimUserEndpoints;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.PredicateMatcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import static org.cloudfoundry.identity.uaa.mock.util.ClientDetailsHelper.arrayFromString;
import static org.cloudfoundry.identity.uaa.mock.util.ClientDetailsHelper.clientArrayFromString;
import static org.cloudfoundry.identity.uaa.oauth.client.SecretChangeRequest.ChangeMode.ADD;
import static org.cloudfoundry.identity.uaa.oauth.client.SecretChangeRequest.ChangeMode.DELETE;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.iterableWithSize;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class ClientAdminEndpointsMockMvcTests extends AdminClientCreator {
private String adminUserToken = null;
private ScimUserEndpoints scimUserEndpoints = null;
private ScimGroupEndpoints scimGroupEndpoints = null;
private ApplicationEventPublisher applicationEventPublisher = null;
private ApplicationEventPublisher originalApplicationEventPublisher = null;
private ArgumentCaptor<AbstractUaaEvent> captor = null;
private ScimUser testUser;
private String testPassword;
private RandomValueStringGenerator generator = new RandomValueStringGenerator(7);
@Before
public void createCaptor() throws Exception {
applicationEventPublisher = mock(ApplicationEventPublisher.class);
ClientAdminEventPublisher eventPublisher = (ClientAdminEventPublisher) getWebApplicationContext().getBean("clientAdminEventPublisher");
originalApplicationEventPublisher = eventPublisher.getPublisher();
eventPublisher.setApplicationEventPublisher(applicationEventPublisher);
captor = ArgumentCaptor.forClass(AbstractUaaEvent.class);
scimUserEndpoints = getWebApplicationContext().getBean(ScimUserEndpoints.class);
scimGroupEndpoints = getWebApplicationContext().getBean(ScimGroupEndpoints.class);
testPassword = "password";
String username = new RandomValueStringGenerator().generate() + "@test.org";
testUser = new ScimUser(null, username, "givenname","familyname");
testUser.setPrimaryEmail(username);
testUser.setPassword(testPassword);
testUser = MockMvcUtils.utils().createUser(getMockMvc(), adminToken, testUser);
testUser.setPassword(testPassword);
applicationEventPublisher = mock(ApplicationEventPublisher.class);
eventPublisher.setApplicationEventPublisher(applicationEventPublisher);
captor = ArgumentCaptor.forClass(AbstractUaaEvent.class);
}
@After
public void restorePublisher() throws Exception {
ClientAdminEventPublisher eventPublisher = (ClientAdminEventPublisher) getWebApplicationContext().getBean("clientAdminEventPublisher");
eventPublisher.setApplicationEventPublisher(originalApplicationEventPublisher);
}
private void setupAdminUserToken() throws Exception {
HttpServletResponse mockResponse = mock(HttpServletResponse.class);
SearchResults<Map<String, Object>> marissa = (SearchResults<Map<String, Object>>)scimUserEndpoints.findUsers("id,userName", "userName eq \"" + testUser.getUserName() + "\"", "userName", "asc", 0, 1);
String marissaId = (String)marissa.getResources().iterator().next().get("id");
//add marissa to uaa.admin
SearchResults<Map<String, Object>> uaaAdmin = (SearchResults<Map<String, Object>>) scimGroupEndpoints.listGroups("id,displayName", "displayName eq \"uaa.admin\"", "displayName", "asc", 1, 1);
String groupId = (String)uaaAdmin.getResources().iterator().next().get("id");
ScimGroup group = scimGroupEndpoints.getGroup(groupId, mockResponse);
ScimGroupMember gm = new ScimGroupMember(marissaId, ScimGroupMember.Type.USER, Arrays.asList(ScimGroupMember.Role.MEMBER));
group.getMembers().add(gm);
scimGroupEndpoints.updateGroup(group, groupId, String.valueOf(group.getVersion()), mockResponse);
//add marissa to clients.write
uaaAdmin = (SearchResults<Map<String, Object>>) scimGroupEndpoints.listGroups("id,displayName", "displayName eq \"clients.write\"", "displayName", "asc", 1, 1);
groupId = (String)uaaAdmin.getResources().iterator().next().get("id");
group = scimGroupEndpoints.getGroup(groupId, mockResponse);
gm = new ScimGroupMember(marissaId, ScimGroupMember.Type.USER, Arrays.asList(ScimGroupMember.Role.MEMBER));
group.getMembers().add(gm);
scimGroupEndpoints.updateGroup(group, groupId, String.valueOf(group.getVersion()), mockResponse);
//add marissa to clients.read
uaaAdmin = (SearchResults<Map<String, Object>>) scimGroupEndpoints.listGroups("id,displayName", "displayName eq \"clients.read\"", "displayName", "asc", 1, 1);
groupId = (String)uaaAdmin.getResources().iterator().next().get("id");
group = scimGroupEndpoints.getGroup(groupId, mockResponse);
gm = new ScimGroupMember(marissaId, ScimGroupMember.Type.USER, Arrays.asList(ScimGroupMember.Role.MEMBER));
group.getMembers().add(gm);
scimGroupEndpoints.updateGroup(group, groupId, String.valueOf(group.getVersion()), mockResponse);
ClientDetails adminClient = createAdminClient(adminToken);
adminUserToken = testClient.getUserOAuthAccessToken(adminClient.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"uaa.admin");
}
@Test
public void testCreateClient() throws Exception {
ClientDetails client = createClient(adminToken, new RandomValueStringGenerator().generate(), Collections.singleton("client_credentials"));
verify(applicationEventPublisher, times(1)).publishEvent(captor.capture());
assertEquals(AuditEventType.ClientCreateSuccess, captor.getValue().getAuditEvent().getType());
assertEquals(makeClientName(client.getClientId()), client.getAdditionalInformation().get("name"));
}
@Test
public void testCreateClientWithInvalidRedirectUrl() throws Exception {
BaseClientDetails client = createBaseClient(new RandomValueStringGenerator().generate(),Collections.singleton("implicit"));
client.setRegisteredRedirectUri(Collections.singleton("*/**"));
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
MvcResult mvcResult = getMockMvc().perform(createClientPost).andExpect(status().isBadRequest()).andReturn();
verify(applicationEventPublisher, times(0)).publishEvent(captor.capture());
}
@Test
public void testClientCRUDAsAdminUser() throws Exception {
setupAdminUserToken();
ClientDetails client = createClient(adminUserToken, new RandomValueStringGenerator().generate(), Collections.singleton("client_credentials"));
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
for (AbstractUaaEvent event : captor.getAllValues()) {
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
}
MockHttpServletRequestBuilder getClient = get("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer " + adminUserToken)
.accept(APPLICATION_JSON);
MvcResult mvcResult = getMockMvc().perform(getClient)
.andExpect(status().isOk())
.andReturn();
BaseClientDetails clientDetails = JsonUtils.readValue(mvcResult.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), clientDetails.getClientId());
clientDetails.setAuthorizedGrantTypes(Collections.singleton("authorization_code"));
MockHttpServletRequestBuilder updateClient = put("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer" + adminUserToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(clientDetails));
MvcResult result = getMockMvc().perform(updateClient).andExpect(status().isOk()).andReturn();
BaseClientDetails updatedClientDetails = JsonUtils.readValue(result.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), updatedClientDetails.getClientId());
assertThat(updatedClientDetails.getAuthorizedGrantTypes(), PredicateMatcher.<String>has(m -> m.equals("authorization_code")));
MockHttpServletRequestBuilder deleteClient = delete("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer" + adminUserToken)
.accept(APPLICATION_JSON);
MvcResult deleteResult = getMockMvc().perform(deleteClient).andExpect(status().isOk()).andReturn();
BaseClientDetails deletedClientDetails = JsonUtils.readValue(deleteResult.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), deletedClientDetails.getClientId());
}
@Test
public void createClient_withClientAdminToken_withAuthoritiesExcluded() throws Exception {
String clientId = generator.generate().toLowerCase();
LinkedHashSet excludedClaims = getWebApplicationContext().getBean("excludedClaims", LinkedHashSet.class);
excludedClaims.add("authorities");
try {
String clientAdminToken = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"clients.admin");
List<String> authorities = Arrays.asList("password.write", "scim.write", "scim.read");
List<String> scopes = Arrays.asList("foo","bar","oauth.approvals");
ClientDetailsModification client = createBaseClient(clientId, Collections.singleton("client_credentials"), authorities, scopes);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients")
.header("Authorization", "Bearer " + clientAdminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
ResultActions createResult = getMockMvc().perform(createClientPost).andExpect(status().isCreated());
BaseClientDetails clientDetails = JsonUtils.readValue(createResult.andReturn().getResponse().getContentAsString(), BaseClientDetails.class);
MockHttpServletRequestBuilder getClientMetadata = get("/oauth/clients/" + clientDetails.getClientId() + "/meta")
.header("Authorization", "Bearer " + clientAdminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON);
ResultActions getResult = getMockMvc().perform(getClientMetadata).andExpect(status().isOk());
JsonUtils.readValue(getResult.andReturn().getResponse().getContentAsString(), ClientMetadata.class);
} finally {
excludedClaims.remove("authorities");
}
}
@Test
public void create_client_and_check_created_by() throws Exception {
setupAdminUserToken();
BaseClientDetails clientDetails = createClient(Arrays.asList("password.write", "scim.write", "scim.read", "clients.write"), adminUserToken);
ClientMetadata clientMetadata = obtainClientMetadata(clientDetails.getClientId());
SearchResults<Map<String, Object>> marissa = (SearchResults<Map<String, Object>>)scimUserEndpoints.findUsers("id,userName", "userName eq \"" + testUser.getUserName() + "\"", "userName", "asc", 0, 1);
String marissaId = (String)marissa.getResources().iterator().next().get("id");
assertEquals(marissaId, clientMetadata.getCreatedBy());
String clientAdminToken = testClient.getClientCredentialsOAuthAccessToken(
clientDetails.getClientId(),
"secret",
"clients.write");
clientDetails = createClient(Arrays.asList("uaa.resource"), clientAdminToken);
clientMetadata =obtainClientMetadata(clientDetails.getClientId());
assertEquals(marissaId, clientMetadata.getCreatedBy());
}
private BaseClientDetails createClient(List<String> authorities, String token) throws Exception {
String clientId = generator.generate().toLowerCase();
List<String> scopes = Arrays.asList("foo","bar","oauth.approvals");
ClientDetailsModification client = createBaseClient(clientId, Collections.singleton("client_credentials"), authorities, scopes);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients")
.header("Authorization", "Bearer " + adminUserToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
ResultActions createResult = getMockMvc().perform(createClientPost).andExpect(status().isCreated());
return JsonUtils.readValue(createResult.andReturn().getResponse().getContentAsString(), BaseClientDetails.class);
}
private ClientMetadata obtainClientMetadata(String clientId) throws Exception {
MockHttpServletRequestBuilder getClientMetadata = get("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + adminUserToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON);
ResultActions getResult = getMockMvc().perform(getClientMetadata).andExpect(status().isOk());
return JsonUtils.readValue(getResult.andReturn().getResponse().getContentAsString(), ClientMetadata.class);
}
@Test
public void test_Read_Restricted_Scopes() throws Exception {
MockHttpServletRequestBuilder createClientPost = get("/oauth/clients/restricted")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON);
getMockMvc().perform(createClientPost)
.andExpect(status().isOk())
.andExpect(content().string(JsonUtils.writeValueAsString(new UaaScopes().getUaaScopes())));
}
@Test
public void testCreate_RestrictedClient_Fails() throws Exception {
String id = new RandomValueStringGenerator().generate();
List<String> grantTypes = Arrays.asList("client_credentials", "password");
BaseClientDetails clientWithAuthorities = createBaseClient(id, grantTypes, new UaaScopes().getUaaScopes(), null);
BaseClientDetails clientWithScopes = createBaseClient(id, grantTypes, null, new UaaScopes().getUaaScopes());
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients/restricted")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clientWithAuthorities));
getMockMvc().perform(createClientPost).andExpect(status().isBadRequest());
createClientPost = post("/oauth/clients/restricted")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clientWithScopes));
getMockMvc().perform(createClientPost).andExpect(status().isBadRequest());
}
@Test
public void testCreate_RestrictedClient_Succeeds() throws Exception {
String id = new RandomValueStringGenerator().generate();
List<String> scopes = Collections.singletonList("openid");
BaseClientDetails client = createBaseClient(id, Arrays.asList("client_credentials", "password"), scopes, scopes);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients/restricted")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
getMockMvc().perform(createClientPost).andExpect(status().isCreated());
createClientPost = put("/oauth/clients/restricted/"+id)
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
getMockMvc().perform(createClientPost).andExpect(status().isOk());
client.setScope(new UaaScopes().getUaaScopes());
createClientPost = put("/oauth/clients/restricted/" + id)
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
getMockMvc().perform(createClientPost).andExpect(status().isBadRequest());
}
@Test
public void testCreateClientsTxSuccess() throws Exception {
int count = 5;
BaseClientDetails[] details = createBaseClients(count, null);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients/tx")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(createClientPost);
result.andExpect(status().isCreated());
ClientDetails[] clients = clientArrayFromString(result.andReturn().getResponse().getContentAsString());
for (ClientDetails client : clients) {
ClientDetails c = getClient(client.getClientId());
assertNotNull(c);
assertNull(c.getClientSecret());
}
verify(applicationEventPublisher, times(count)).publishEvent(captor.capture());
for (AbstractUaaEvent event : captor.getAllValues()) {
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
}
}
@Test
public void testCreateClientsTxDuplicateId() throws Exception {
BaseClientDetails[] details = createBaseClients(5, null);
details[details.length-1] = details[0];
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients/tx")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
getMockMvc().perform(createClientPost).andExpect(status().isConflict());
for (ClientDetails client : details) {
assertNull(getClient(client.getClientId()));
}
verify(applicationEventPublisher, times(0)).publishEvent(captor.capture());
}
@Test
public void test_InZone_ClientWrite_Using_ZonesDotAdmin() throws Exception {
String subdomain = generator.generate();
MockMvcUtils.IdentityZoneCreationResult result = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), null);
String clientId = generator.generate();
BaseClientDetails client = new BaseClientDetails(clientId, "", "openid","authorization_code","","http://some.redirect.url.com");
client.setClientSecret("secret");
MockMvcUtils.utils().createClient(getMockMvc(), result.getZoneAdminToken(), client, result.getIdentityZone());
}
@Test
public void test_InZone_ClientWrite_Using_ZonesDotClientsDotAdmin() throws Exception {
String subdomain = generator.generate();
MockMvcUtils.IdentityZoneCreationResult result = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), null);
String id = result.getIdentityZone().getId();
String clientId = generator.generate();
BaseClientDetails client = new BaseClientDetails(clientId, "", "","client_credentials","zones."+id+".clients.admin", "http://some.redirect.url.com");
client.setClientSecret("secret");
client = MockMvcUtils.utils().createClient(getMockMvc(), adminToken, client);
client.setClientSecret("secret");
String zonesClientsAdminToken = MockMvcUtils.utils().getClientOAuthAccessToken(getMockMvc(), client.getClientId(), client.getClientSecret(), "zones." + id + ".clients.admin");
BaseClientDetails newclient = new BaseClientDetails(clientId, "", "openid","authorization_code","","http://some.redirect.url.com");
newclient.setClientSecret("secret");
newclient = MockMvcUtils.utils().createClient(getMockMvc(), zonesClientsAdminToken, newclient, result.getIdentityZone());
MockMvcUtils.utils().updateClient(getMockMvc(), zonesClientsAdminToken, newclient, result.getIdentityZone());
}
@Test
public void manageClientInOtherZone_Using_AdminUserTokenFromDefaultZone() throws Exception {
String subdomain = generator.generate();
MockMvcUtils.IdentityZoneCreationResult result = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), null);
String zoneId = result.getIdentityZone().getId();
String clientId = generator.generate();
setupAdminUserToken();
BaseClientDetails client = new BaseClientDetails(clientId, "", "openid","authorization_code","","http://some.redirect.url.com");
client.setClientSecret("secret");
BaseClientDetails createdClient = MockMvcUtils.utils().createClient(getMockMvc(), adminUserToken, client, result.getIdentityZone());
assertEquals(client.getClientId(), createdClient.getClientId());
MockHttpServletRequestBuilder getClient = get("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer " + adminUserToken)
.header("X-Identity-Zone-Id", zoneId)
.accept(APPLICATION_JSON);
MvcResult mvcResult = getMockMvc().perform(getClient)
.andExpect(status().isOk())
.andReturn();
BaseClientDetails clientDetails = JsonUtils.readValue(mvcResult.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), clientDetails.getClientId());
clientDetails.setAuthorizedGrantTypes(Collections.singleton("authorization_code"));
MockHttpServletRequestBuilder updateClient = put("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer" + adminUserToken)
.header("X-Identity-Zone-Id", zoneId)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(clientDetails));
mvcResult = getMockMvc().perform(updateClient).andExpect(status().isOk()).andReturn();
BaseClientDetails updatedClientDetails = JsonUtils.readValue(mvcResult.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), updatedClientDetails.getClientId());
assertThat(updatedClientDetails.getAuthorizedGrantTypes(), PredicateMatcher.<String>has(m -> m.equals("authorization_code")));
MockHttpServletRequestBuilder deleteClient = delete("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer" + adminUserToken)
.header("X-Identity-Zone-Id", zoneId)
.accept(APPLICATION_JSON);
MvcResult deleteResult = getMockMvc().perform(deleteClient).andExpect(status().isOk()).andReturn();
BaseClientDetails deletedClientDetails = JsonUtils.readValue(deleteResult.getResponse().getContentAsString(), BaseClientDetails.class);
assertEquals(client.getClientId(), deletedClientDetails.getClientId());
}
@Test
public void test_InZone_ClientRead_Using_ZonesDotClientsDotAdmin() throws Exception {
String subdomain = generator.generate();
MockMvcUtils.IdentityZoneCreationResult result = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), null);
String id = result.getIdentityZone().getId();
String clientId = generator.generate();
BaseClientDetails client = new BaseClientDetails(clientId, "", "","client_credentials","zones."+id+".clients.admin","http://some.redirect.url.com");
client.setClientSecret("secret");
client = MockMvcUtils.utils().createClient(getMockMvc(), adminToken, client);
client.setClientSecret("secret");
String zonesClientsAdminToken = MockMvcUtils.utils().getClientOAuthAccessToken(getMockMvc(), client.getClientId(), client.getClientSecret(), "zones."+id+".clients.admin");
BaseClientDetails newclient = new BaseClientDetails(clientId, "", "openid","authorization_code","","http://some.redirect.url.com");
newclient.setClientSecret("secret");
MockMvcUtils.utils().createClient(getMockMvc(), zonesClientsAdminToken, newclient, result.getIdentityZone());
}
@Test
public void test_InZone_ClientRead_Using_ZonesDotClientsDotRead() throws Exception {
String subdomain = generator.generate();
MockMvcUtils.IdentityZoneCreationResult result = MockMvcUtils.utils().createOtherIdentityZoneAndReturnResult(subdomain, getMockMvc(), getWebApplicationContext(), null);
String id = result.getIdentityZone().getId();
String clientId = generator.generate();
BaseClientDetails client = new BaseClientDetails(clientId, "", "","client_credentials","zones."+id+".clients.read","http://some.redirect.url.com");
client.setClientSecret("secret");
client = MockMvcUtils.utils().createClient(getMockMvc(), adminToken, client);
client.setClientSecret("secret");
String zonesClientsReadToken = MockMvcUtils.utils().getClientOAuthAccessToken(getMockMvc(), client.getClientId(), client.getClientSecret(), "zones." + id + ".clients.read");
BaseClientDetails newclient = new BaseClientDetails(clientId, "", "openid","authorization_code","","http://some.redirect.url.com");
newclient.setClientSecret("secret");
MockMvcUtils.utils().createClient(getMockMvc(), result.getZoneAdminToken(), newclient, result.getIdentityZone());
MockMvcUtils.utils().getClient(getMockMvc(), zonesClientsReadToken, newclient.getClientId(), result.getIdentityZone());
}
@Test
public void testCreateClientsTxClientCredentialsWithoutSecret() throws Exception {
BaseClientDetails[] details = createBaseClients(5, null);
details[details.length-1].setAuthorizedGrantTypes(StringUtils.commaDelimitedListToSet("client_credentials"));
details[details.length-1].setClientSecret(null);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients/tx")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
getMockMvc().perform(createClientPost).andExpect(status().isBadRequest());
for (ClientDetails client : details) {
assertNull(getClient(client.getClientId()));
}
verify(applicationEventPublisher, times(0)).publishEvent(captor.capture());
}
@Test
public void testUpdateClientsTxSuccess() throws Exception {
int count = 5;
BaseClientDetails[] details = new BaseClientDetails[count];
for (int i=0; i<details.length; i++) {
details[i] = (BaseClientDetails)createClient(adminToken,null,null);
details[i].setRefreshTokenValiditySeconds(120);
}
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/tx")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(updateClientPut);
result.andExpect(status().isOk());
ClientDetails[] clients = clientArrayFromString(result.andReturn().getResponse().getContentAsString());
for (ClientDetails client : clients) {
assertNotNull(getClient(client.getClientId()));
assertEquals(new Integer(120), client.getRefreshTokenValiditySeconds());
}
//create and then update events
verify(applicationEventPublisher, times(count * 2)).publishEvent(captor.capture());
int index = 0;
for (AbstractUaaEvent event : captor.getAllValues()) {
if (index<count) {
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
} else {
assertEquals(AuditEventType.ClientUpdateSuccess, event.getAuditEvent().getType());
}
index++;
}
}
@Test
public void testUpdateClientsTxInvalidId() throws Exception {
int count = 5;
BaseClientDetails[] details = new BaseClientDetails[count];
for (int i=0; i<details.length; i++) {
details[i] = (BaseClientDetails)createClient(adminToken,null,null);
details[i].setRefreshTokenValiditySeconds(120);
}
String firstId = details[0].getClientId();
details[0].setClientId("unknown.client.id");
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/tx")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(updateClientPut);
result.andExpect(status().isNotFound());
details[0].setClientId(firstId);
for (ClientDetails client : details) {
ClientDetails c = getClient(client.getClientId());
assertNotNull(c);
assertNull(c.getClientSecret());
assertNull(c.getRefreshTokenValiditySeconds());
}
//create and then update events
verify(applicationEventPublisher, times(count)).publishEvent(captor.capture());
}
@Test
public void testDeleteClientsTxSuccess() throws Exception {
int count = 5;
BaseClientDetails[] details = new BaseClientDetails[count];
for (int i=0; i<details.length; i++) {
details[i] = (BaseClientDetails)createClient(adminToken,null,null);
}
MockHttpServletRequestBuilder deleteClientsPost = post("/oauth/clients/tx/delete")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(deleteClientsPost);
result.andExpect(status().isOk());
for (ClientDetails client : details) {
assertNull(getClient(client.getClientId()));
}
//create and then update events
verify(applicationEventPublisher, times(count*2)).publishEvent(captor.capture());
int index = 0;
for (AbstractUaaEvent event : captor.getAllValues()) {
if (index<count) {
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
} else {
assertEquals(AuditEventType.ClientDeleteSuccess, event.getAuditEvent().getType());
}
index++;
}
}
@Test
public void testDeleteClientsTxRollbackInvalidId() throws Exception {
int count = 5;
BaseClientDetails[] details = new BaseClientDetails[count];
for (int i=0; i<details.length; i++) {
details[i] = (BaseClientDetails)createClient(adminToken,null,null);
}
String firstId = details[0].getClientId();
details[0].setClientId("unknown.client.id");
MockHttpServletRequestBuilder deleteClientsPost = post("/oauth/clients/tx/delete")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(deleteClientsPost);
result.andExpect(status().isNotFound());
details[0].setClientId(firstId);
for (ClientDetails client : details) {
ClientDetails c = getClient(client.getClientId());
assertNotNull(c);
assertNull(c.getClientSecret());
assertNull(c.getRefreshTokenValiditySeconds());
}
verify(applicationEventPublisher, times(count)).publishEvent(captor.capture());
}
@Test
public void testAddUpdateDeleteClientsTxSuccess() throws Exception {
int count = 5;
ClientDetailsModification[] details = new ClientDetailsModification[count*3];
for (int i=0; i<count; i++) {
details[i] = (ClientDetailsModification)createClient(adminToken,null,null);
details[i].setRefreshTokenValiditySeconds(120);
details[i].setAction(ClientDetailsModification.UPDATE);
}
for (int i=count; i<(count*2); i++) {
details[i] = (ClientDetailsModification)createClient(adminToken,null,null);
details[i].setAction(ClientDetailsModification.DELETE);
}
for (int i=(count*2); i<(count*3); i++) {
details[i] = createBaseClient(null,null);
details[i].setAction(ClientDetailsModification.ADD);
}
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
for (int i=0; i<count; i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNotNull(c);
assertEquals(new Integer(120), c.getRefreshTokenValiditySeconds());
}
for (int i=count; i<(count*2); i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNull(c);
}
for (int i=(count*2); i<(count*3); i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNotNull(c);
assertNull(c.getRefreshTokenValiditySeconds());
}
verify(applicationEventPublisher, times(count*5)).publishEvent(captor.capture());
int index = 0;
for (AbstractUaaEvent event : captor.getAllValues()) {
int swit = index / count;
switch (swit) {
case 0 :
case 1 :
case 4 : {
//1-10 and 21-25 events are create
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
assertEquals(ClientCreateEvent.class, event.getClass());
assertEquals(details[index<10?index:(index-count*2)].getClientId(), event.getAuditEvent().getPrincipalId());
break;
}
case 2 : {
//the 11-15 events are update
assertEquals(AuditEventType.ClientUpdateSuccess, event.getAuditEvent().getType());
assertEquals(ClientUpdateEvent.class, event.getClass());
assertEquals(details[index-(count*2)].getClientId(), event.getAuditEvent().getPrincipalId());
break;
}
case 3 : {
//the 16-20 events are deletes
assertEquals(AuditEventType.ClientDeleteSuccess, event.getAuditEvent().getType());
assertEquals(ClientDeleteEvent.class, event.getClass());
assertEquals(details[index-count*2].getClientId(), event.getAuditEvent().getPrincipalId());
break;
}
}
index++;
}
}
@Test
public void testAddUpdateDeleteClientsTxDeleteFailedRollback() throws Exception {
ClientDetailsModification[] details = new ClientDetailsModification[15];
for (int i=0; i<5; i++) {
details[i] = (ClientDetailsModification)createClient(adminToken,null,Collections.singleton("password"));
details[i].setRefreshTokenValiditySeconds(120);
details[i].setAction(ClientDetailsModification.UPDATE);
}
for (int i=5; i<10; i++) {
details[i] = (ClientDetailsModification)createClient(adminToken,null,null);
details[i].setAction(ClientDetailsModification.DELETE);
}
for (int i=10; i<15; i++) {
details[i] = createBaseClient(null,null);
details[i].setAction(ClientDetailsModification.ADD);
}
String userToken = testClient.getUserOAuthAccessToken(
details[0].getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
addApprovals(userToken, details[0].getClientId());
Approval[] approvals = getApprovals(userToken, details[0].getClientId());
assertEquals(3, approvals.length);
String deleteId = details[5].getClientId();
details[5].setClientId("unknown.client.id");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(details));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isNotFound());
details[5].setClientId(deleteId);
for (int i=0; i<5; i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNotNull(c);
assertNull(c.getRefreshTokenValiditySeconds());
}
for (int i=5; i<10; i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNotNull(c);
}
for (int i=10; i<15; i++) {
ClientDetails c = getClient(details[i].getClientId());
assertNull(c);
}
approvals = getApprovals(userToken, details[0].getClientId());
assertEquals(3, approvals.length);
}
@Test
public void testApprovalsAreDeleted() throws Exception {
ClientDetails details = createClient(adminToken, new RandomValueStringGenerator().generate(), Collections.singleton("password"));
String userToken = testClient.getUserOAuthAccessToken(
details.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
Approval[] approvals = getApprovals(userToken, details.getClientId());
assertEquals(0, approvals.length);
addApprovals(userToken, details.getClientId());
approvals = getApprovals(userToken, details.getClientId());
assertEquals(3, approvals.length);
MockHttpServletRequestBuilder deleteClientsPost = post("/oauth/clients/tx/delete")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(new ClientDetails[]{details}));
ResultActions result = getMockMvc().perform(deleteClientsPost);
result.andExpect(status().isOk());
ClientDetailsModification[] deleted = (ClientDetailsModification[]) arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
assertTrue(deleted[0].isApprovalsDeleted());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
ClientDetails approvalsClient = createApprovalsLoginClient(adminToken);
String loginToken = testClient.getUserOAuthAccessToken(
approvalsClient.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
approvals = getApprovals(loginToken, details.getClientId());
assertEquals(0, approvals.length);
}
@Test
public void testApprovalsAreDeleted2() throws Exception {
ClientDetails details = createClient(adminToken, new RandomValueStringGenerator().generate(), Collections.singleton("password"));
String userToken = testClient.getUserOAuthAccessToken(
details.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
Approval[] approvals = getApprovals(userToken, details.getClientId());
assertEquals(0, approvals.length);
addApprovals(userToken, details.getClientId());
approvals = getApprovals(userToken, details.getClientId());
assertEquals(3, approvals.length);
MockHttpServletRequestBuilder deleteClientsPost = delete("/oauth/clients/"+details.getClientId())
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON);
ResultActions result = getMockMvc().perform(deleteClientsPost);
result.andExpect(status().isOk());
ClientDetails approvalsClient = createApprovalsLoginClient(adminToken);
String loginToken = testClient.getUserOAuthAccessToken(
approvalsClient.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
approvals = getApprovals(loginToken, details.getClientId());
assertEquals(0, approvals.length);
}
@Test
public void testModifyApprovalsAreDeleted() throws Exception {
ClientDetails details = createClient(adminToken, new RandomValueStringGenerator().generate(), Collections.singleton("password"));
((ClientDetailsModification)details).setAction(ClientDetailsModification.DELETE);
String userToken = testClient.getUserOAuthAccessToken(
details.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
Approval[] approvals = getApprovals(userToken, details.getClientId());
assertEquals(0, approvals.length);
addApprovals(userToken, details.getClientId());
approvals = getApprovals(userToken, details.getClientId());
assertEquals(3, approvals.length);
MockHttpServletRequestBuilder deleteClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(new ClientDetails[]{details}));
ResultActions result = getMockMvc().perform(deleteClientsPost);
result.andExpect(status().isOk());
ClientDetailsModification[] deleted = (ClientDetailsModification[]) arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
assertTrue(deleted[0].isApprovalsDeleted());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
ClientDetails approvalsClient = createApprovalsLoginClient(adminToken);
String loginToken = testClient.getUserOAuthAccessToken(
approvalsClient.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
approvals = getApprovals(loginToken, details.getClientId());
assertEquals(0, approvals.length);
}
@Test
public void testSecretChangeTxApprovalsNotDeleted() throws Exception {
int count = 3;
//create clients
ClientDetailsModification[] clients = createBaseClients(count, Arrays.asList("client_credentials", "password"));
for (ClientDetailsModification c : clients) {
c.setAction(c.ADD);
}
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
//add approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
addApprovals(userToken, c.getClientId());
}
//verify approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(3, getApprovals(userToken,c.getClientId()).length);
}
//change the secret, and we know the old secret
SecretChangeRequest[] srs = new SecretChangeRequest[clients.length];
for (int i=0; i<srs.length; i++) {
srs[i] = new SecretChangeRequest();
srs[i].setClientId(clients[i].getClientId());
srs[i].setOldSecret(clients[i].getClientSecret());
srs[i].setSecret("secret2");
}
modifyClientsPost = post("/oauth/clients/tx/secret")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(srs));
result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
clients = (ClientDetailsModification[])arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
//check that we still have approvals for each client
ClientDetails approvalsClient = createApprovalsLoginClient(adminToken);
for (ClientDetailsModification c : clients) {
String loginToken = testClient.getUserOAuthAccessToken(
approvalsClient.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(3, getApprovals(loginToken,c.getClientId()).length);
assertFalse(c.isApprovalsDeleted());
}
}
@Test
public void testSecretChangeEvent() throws Exception {
String token = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin,clients.secret");
String id = "secretchangeevent";
ClientDetails c = createClient(token, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest(id, "secret", "newsecret");
MockHttpServletRequestBuilder modifyClientsPost = put("/oauth/clients/" + id + "/secret")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request));
getMockMvc().perform(modifyClientsPost)
.andExpect(status().isOk());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
assertEquals(SecretChangeEvent.class, captor.getValue().getClass());
SecretChangeEvent event = (SecretChangeEvent) captor.getValue();
assertEquals(id, event.getAuditEvent().getPrincipalId());
}
@Test
public void testAddNewClientSecret() throws Exception {
String token = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin,clients.secret");
String id = generator.generate();
ClientDetails client = createClient(token, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest();
request.setSecret("password2");
request.setChangeMode(ADD);
MockHttpServletResponse response = getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isOk())
.andReturn().getResponse();
ActionResult actionResult = JsonUtils.readValue(response.getContentAsString(), ActionResult.class);
assertEquals("ok", actionResult.getStatus());
assertEquals("Secret is added", actionResult.getMessage());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
assertEquals(SecretChangeEvent.class, captor.getValue().getClass());
SecretChangeEvent event = (SecretChangeEvent) captor.getValue();
assertEquals(id, event.getAuditEvent().getPrincipalId());
}
@Test
public void testAddMoreThanTwoClientSecret() throws Exception {
String token = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin,clients.secret");
String id = generator.generate();
ClientDetails client = createClient(token, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest();
request.setSecret("password2");
request.setChangeMode(ADD);
getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isOk());
request.setSecret("password3");
MockHttpServletResponse response = getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isBadRequest())
.andReturn().getResponse();
UaaException invalidClientDetailsException = JsonUtils.readValue(response.getContentAsString(), UaaException.class);
assertEquals("invalid_client", invalidClientDetailsException.getErrorCode());
assertEquals("client secret is either empty or client already has two secrets.", invalidClientDetailsException.getMessage());
verify(applicationEventPublisher, times(3)).publishEvent(captor.capture());
assertEquals(SecretFailureEvent.class, captor.getValue().getClass());
SecretFailureEvent event = (SecretFailureEvent) captor.getValue();
assertEquals(id, event.getAuditEvent().getPrincipalId());
}
@Test
public void testDeleteClientSecret() throws Exception {
String token = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin,clients.secret");
String id = generator.generate();
ClientDetails client = createClient(token, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest();
request.setSecret("password2");
request.setChangeMode(ADD);
getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isOk());
request = new SecretChangeRequest();
request.setChangeMode(DELETE);
MockHttpServletResponse response = getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isOk())
.andReturn().getResponse();
ActionResult actionResult = JsonUtils.readValue(response.getContentAsString(), ActionResult.class);
assertNotNull(actionResult);
assertEquals("ok", actionResult.getStatus());
assertEquals("Secret is deleted", actionResult.getMessage());
verify(applicationEventPublisher, times(3)).publishEvent(captor.capture());
assertEquals(SecretChangeEvent.class, captor.getValue().getClass());
SecretChangeEvent event = (SecretChangeEvent) captor.getValue();
assertEquals(id, event.getAuditEvent().getPrincipalId());
}
@Test
public void testDeleteClientSecretForClientWithOneSecret() throws Exception {
String token = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin,clients.secret");
String id = generator.generate();
ClientDetails client = createClient(token, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest();
request.setChangeMode(DELETE);
MockHttpServletResponse response = getMockMvc().perform(put("/oauth/clients/{client_id}/secret", client.getClientId())
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request)))
.andExpect(status().isBadRequest())
.andReturn().getResponse();
UaaException invalidClientDetailsException = JsonUtils.readValue(response.getContentAsString(), UaaException.class);
assertEquals("invalid_client", invalidClientDetailsException.getErrorCode());
assertEquals("client secret is either empty or client has only one secret.", invalidClientDetailsException.getMessage());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
assertEquals(SecretFailureEvent.class, captor.getValue().getClass());
SecretFailureEvent event = (SecretFailureEvent) captor.getValue();
assertEquals(id, event.getAuditEvent().getPrincipalId());
}
@Test
public void testSecretChange_UsingAdminClientToken() throws Exception {
String adminToken = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"uaa.admin");
String id = generator.generate();
BaseClientDetails c = (BaseClientDetails) createClient(adminToken, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest(id, null, "newsecret");
MockHttpServletRequestBuilder modifySecret = put("/oauth/clients/" + id + "/secret")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request));
getMockMvc().perform(modifySecret).andExpect(status().isOk());
}
@Test
public void testSecretChange_UsingClientAdminToken() throws Exception {
String adminToken = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"clients.admin");
String id = generator.generate();
BaseClientDetails c = (BaseClientDetails) createClient(adminToken, id, Collections.singleton("client_credentials"));
SecretChangeRequest request = new SecretChangeRequest(id, null, "newersecret");
MockHttpServletRequestBuilder modifySecret = put("/oauth/clients/" + id + "/secret")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request));
getMockMvc().perform(modifySecret).andExpect(status().isOk());
}
@Test
public void testFailedSecretChangeEvent() throws Exception {
List<String> scopes = Arrays.asList("oauth.approvals","clients.secret");
BaseClientDetails client = createBaseClient(null, Arrays.asList("password", "client_credentials"), scopes, scopes);
MockHttpServletRequestBuilder createClientPost = post("/oauth/clients")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(client));
getMockMvc().perform(createClientPost).andExpect(status().isCreated());
String clientSecretToken = testClient.getClientCredentialsOAuthAccessToken(client.getClientId(), client.getClientSecret(), "clients.secret");
SecretChangeRequest request = new SecretChangeRequest(client.getClientId(), "invalidsecret", "newsecret");
MockHttpServletRequestBuilder modifyClientsPost = put("/oauth/clients/" + client.getClientId() + "/secret")
.header("Authorization", "Bearer " + clientSecretToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(request));
getMockMvc().perform(modifyClientsPost)
.andExpect(status().isBadRequest());
verify(applicationEventPublisher, times(2)).publishEvent(captor.capture());
assertEquals(SecretFailureEvent.class, captor.getValue().getClass());
SecretFailureEvent event = (SecretFailureEvent) captor.getValue();
assertEquals(client.getClientId(), event.getAuditEvent().getPrincipalId());
}
@Test
public void testSecretChangeModifyTxApprovalsDeleted() throws Exception {
int count = 3;
//create clients
ClientDetailsModification[] clients = createBaseClients(count, Arrays.asList("client_credentials","password"));
for (ClientDetailsModification c : clients) {
c.setAction(c.ADD);
}
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
clients = (ClientDetailsModification[])arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
//add approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
addApprovals(userToken, c.getClientId());
}
//verify approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(3, getApprovals(userToken,c.getClientId()).length);
}
//change the secret, and we know don't the old secret
for (ClientDetailsModification c : clients) {
c.setClientSecret("secret2");
c.setAction(c.UPDATE_SECRET);
}
modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
clients = (ClientDetailsModification[])arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
//check that we deleted approvals for each client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret2",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(0, getApprovals(userToken,c.getClientId()).length);
assertTrue(c.isApprovalsDeleted());
}
//verify(applicationEventPublisher, times(count*3)).publishEvent(captor.capture());
verify(applicationEventPublisher, times(12)).publishEvent(captor.capture());
int index = 0;
for (AbstractUaaEvent event : captor.getAllValues()) {
if (index<count) {
assertEquals(AuditEventType.ClientCreateSuccess, event.getAuditEvent().getType());
} else {
int swit = index % 3;
if (swit==0) {
assertEquals(AuditEventType.ClientUpdateSuccess, event.getAuditEvent().getType());
} else if (swit==1) {
assertEquals(AuditEventType.SecretChangeSuccess, event.getAuditEvent().getType());
} else {
assertEquals(AuditEventType.ClientApprovalsDeleted, event.getAuditEvent().getType());
assertEquals(ClientApprovalsDeletedEvent.class, event.getClass());
}
}
index++;
}
}
@Test
public void testSecretChangeModifyTxApprovalsNotDeleted() throws Exception {
//create clients
ClientDetailsModification[] clients = createBaseClients(3, Arrays.asList("client_credentials","password"));
for (ClientDetailsModification c : clients) {
c.setAction(c.ADD);
}
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
clients = (ClientDetailsModification[])arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
//add approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
addApprovals(userToken, c.getClientId());
}
//verify approvals to the client
for (ClientDetailsModification c : clients) {
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(3, getApprovals(userToken, c.getClientId()).length);
}
//change the secret, and we know don't the old secret
for (ClientDetailsModification c : clients) {
c.setClientSecret("secret");
c.setAction(c.UPDATE_SECRET);
}
modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
clients = (ClientDetailsModification[])arrayFromString(result.andReturn().getResponse().getContentAsString(), ClientDetailsModification[].class);
//check that we still have approvals for each client
for (ClientDetailsModification c : clients) {
assertFalse(c.isApprovalsDeleted());
String userToken = testClient.getUserOAuthAccessToken(
c.getClientId(),
"secret",
testUser.getUserName(),
testPassword,
"oauth.approvals");
assertEquals(3, getApprovals(userToken,c.getClientId()).length);
}
}
@Test
public void testClientsAdminPermissions() throws Exception {
ClientDetails adminsClient = createClientAdminsClient(adminToken);
//create clients
ClientDetailsModification[] clients = createBaseClients(3, Arrays.asList("client_credentials","refresh_token"));
for (ClientDetailsModification c : clients) {
c.setScope(Collections.singletonList("oauth.approvals"));
c.setAction(c.ADD);
}
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.admin");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isOk());
}
@Test
public void testNonClientsAdminPermissions() throws Exception {
ClientDetails adminsClient = createReadWriteClient(adminToken);
//create clients
ClientDetailsModification[] clients = createBaseClients(3, Arrays.asList("client_credentials","refresh_token"));
for (ClientDetailsModification c : clients) {
c.setScope(Collections.singletonList("oauth.approvals"));
c.setAction(c.ADD);
}
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.write");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients/tx/modify")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isForbidden());
}
@Test
public void testCreateAsAdminPermissions() throws Exception {
ClientDetails adminsClient = createClientAdminsClient(adminToken);
//create clients
ClientDetailsModification[] clients = createBaseClients(1, Arrays.asList("client_credentials","refresh_token"));
for (ClientDetailsModification c : clients) {
c.setScope(Collections.singletonList("oauth.approvals"));
c.setAction(c.ADD);
}
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.admin");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients[0]));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isCreated());
}
@Test
public void testCreateAsReadPermissions() throws Exception {
ClientDetails adminsClient = createReadWriteClient(adminToken);
//create clients
ClientDetailsModification[] clients = createBaseClients(1, Arrays.asList("client_credentials","refresh_token"));
for (ClientDetailsModification c : clients) {
c.setScope(Collections.singletonList("oauth.approvals"));
c.setAction(c.ADD);
}
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.read");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients[0]));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isForbidden());
}
@Test
public void testCreateAsWritePermissions() throws Exception {
ClientDetails adminsClient = createReadWriteClient(adminToken);
//create clients
ClientDetailsModification[] clients = createBaseClients(1, Arrays.asList("client_credentials", "refresh_token"));
for (ClientDetailsModification c : clients) {
c.setScope(Collections.singletonList("oauth.approvals"));
c.setAction(c.ADD);
}
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.write");
MockHttpServletRequestBuilder modifyClientsPost = post("/oauth/clients")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(clients[0]));
ResultActions result = getMockMvc().perform(modifyClientsPost);
result.andExpect(status().isCreated());
}
@Test
public void testGetClientDetailsSortedByLastModified() throws Exception{
ClientDetails adminsClient = createReadWriteClient(adminToken);
String token = testClient.getClientCredentialsOAuthAccessToken(
adminsClient.getClientId(),
"secret",
"clients.read");
MockHttpServletRequestBuilder get = get("/oauth/clients")
.header("Authorization", "Bearer " + token)
.param("sortBy", "lastmodified")
.param("sortOrder", "descending")
.accept(APPLICATION_JSON);
MvcResult result = getMockMvc().perform(get).andExpect(status().isOk()).andReturn();
String body = result.getResponse().getContentAsString();
Collection<BaseClientDetails> clientDetails = JsonUtils.readValue(body, new TypeReference<SearchResults<BaseClientDetails>>() {
}).getResources();
assertNotNull(clientDetails);
Date lastDate = null;
for(ClientDetails clientDetail : clientDetails){
assertTrue(clientDetail.getAdditionalInformation().containsKey("lastModified"));
Date currentDate = JsonUtils.convertValue(clientDetail.getAdditionalInformation().get("lastModified"), Date.class);
if(lastDate != null){
assertTrue(currentDate.getTime() <= lastDate.getTime());
}
lastDate = currentDate;
}
}
@Test
public void testClientWithDotInID() throws Exception {
ClientDetails details = createClient(adminToken, "testclient", Collections.singleton("client_credentials"));
ClientDetails detailsv2 = createClient(adminToken, "testclient.v2", Collections.singleton("client_credentials"));
assertEquals("testclient.v2", detailsv2.getClientId());
}
@Test
public void testPutClientModifyAuthorities() throws Exception {
ClientDetails client = createClient(adminToken, "testClientForModifyAuthorities", Collections.singleton("client_credentials"));
BaseClientDetails modified = new BaseClientDetails(client);
modified.setAuthorities(Collections.singleton((GrantedAuthority) () -> "newAuthority"));
MockHttpServletRequestBuilder put = put("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(modified));
MvcResult result = getMockMvc().perform(put).andExpect(status().isOk()).andReturn();
client = getClient(client.getClientId());
assertThat(client.getAuthorities(), iterableWithSize(1));
GrantedAuthority authority = Iterables.get(client.getAuthorities(), 0);
assertEquals("newAuthority", authority.getAuthority());
}
@Test
public void testPutClientModifyAccessTokenValidity() throws Exception {
ClientDetails client = createClient(adminToken, "testClientForModifyAccessTokenValidity", Collections.singleton("client_credentials"));
BaseClientDetails modified = new BaseClientDetails(client);
modified.setAccessTokenValiditySeconds(73);
MockHttpServletRequestBuilder put = put("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(modified));
MvcResult result = getMockMvc().perform(put).andExpect(status().isOk()).andReturn();
client = getClient(client.getClientId());
assertThat(client.getAccessTokenValiditySeconds(), is(73));
}
@Test
public void testPutClientModifyName() throws Exception {
ClientDetails client = createClient(adminToken, "testClientForModifyName", Collections.singleton("client_credentials"));
Map<String, Object> requestBody = JsonUtils.readValue(JsonUtils.writeValueAsString(new BaseClientDetails(client)), new TypeReference<Map<String, Object>>() {});
requestBody.put("name", "New Client Name");
MockHttpServletRequestBuilder put = put("/oauth/clients/" + client.getClientId())
.header("Authorization", "Bearer " + adminToken)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(requestBody));
MvcResult result = getMockMvc().perform(put).andExpect(status().isOk()).andReturn();
MockHttpServletResponse response = getClientHttpResponse(client.getClientId());
Map<String, Object> map = JsonUtils.readValue(response.getContentAsString(), new TypeReference<Map<String, Object>>() {});
assertThat(map, hasEntry(is("name"), PredicateMatcher.is(value -> value.equals("New Client Name"))));
client = getClientResponseAsClientDetails(response);
assertThat(client.getAdditionalInformation(), hasEntry(is("name"), PredicateMatcher.is(value -> value.equals("New Client Name"))));
}
private Approval[] getApprovals(String token, String clientId) throws Exception {
JdbcApprovalStore endpoint = getWebApplicationContext().getBean(JdbcApprovalStore.class);
return endpoint.getApprovalsForClient(clientId).toArray(new Approval[0]);
}
private Approval[] addApprovals(String token, String clientId) throws Exception {
Date oneMinuteAgo = new Date(System.currentTimeMillis() - 60000);
Date expiresAt = new Date(System.currentTimeMillis() + 60000);
Approval[] approvals = new Approval[] {
new Approval()
.setUserId(null)
.setClientId(clientId)
.setScope("cloud_controller.read")
.setExpiresAt(expiresAt)
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(oneMinuteAgo),
new Approval()
.setUserId(null)
.setClientId(clientId)
.setScope("openid")
.setExpiresAt(expiresAt)
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(oneMinuteAgo),
new Approval()
.setUserId(null)
.setClientId(clientId)
.setScope("password.write")
.setExpiresAt(expiresAt)
.setStatus(ApprovalStatus.APPROVED)
.setLastUpdatedAt(oneMinuteAgo)};
MockHttpServletRequestBuilder put = put("/approvals/"+clientId)
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(toString(approvals));
getMockMvc().perform(put).andExpect(status().isOk());
return approvals;
}
}