/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2015] Pivotal Software, Inc. All Rights Reserved.
* <p>
* 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.
* <p>
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.client;
import com.fasterxml.jackson.core.type.TypeReference;
import org.cloudfoundry.identity.uaa.mock.InjectedMockContextTest;
import org.cloudfoundry.identity.uaa.test.UaaTestAccounts;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.util.PredicateMatcher;
import org.cloudfoundry.identity.uaa.zone.MultitenantJdbcClientDetailsService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.oauth2.common.util.RandomValueStringGenerator;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import java.net.URL;
import java.util.ArrayList;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.TEXT_PLAIN;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public class ClientMetadataAdminEndpointsMockMvcTest extends InjectedMockContextTest {
private String adminClientTokenWithClientsWrite;
private MultitenantJdbcClientDetailsService clients;
private RandomValueStringGenerator generator = new RandomValueStringGenerator(8);
private UaaTestAccounts testAccounts;
private String adminClientTokenWithClientsRead;
@Before
public void setUp() throws Exception {
testAccounts = UaaTestAccounts.standard(null);
adminClientTokenWithClientsRead = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"clients.read");
adminClientTokenWithClientsWrite = testClient.getClientCredentialsOAuthAccessToken(
testAccounts.getAdminClientId(),
testAccounts.getAdminClientSecret(),
"clients.write");
clients = getWebApplicationContext().getBean(MultitenantJdbcClientDetailsService.class);
}
@Test
public void getClientMetadata() throws Exception {
String clientId = generator.generate();
String marissaToken = getUserAccessToken(clientId);
MockHttpServletResponse response = getTestClientMetadata(clientId, marissaToken);
assertThat(response.getStatus(), is(HttpStatus.OK.value()));
}
private String getUserAccessToken(String clientId) throws Exception {
BaseClientDetails newClient = new BaseClientDetails(clientId, "oauth", "oauth.approvals", "password", "oauth.login");
newClient.setClientSecret("secret");
clients.addClientDetails(newClient);
return testClient.getUserOAuthAccessToken(clientId, "secret", "marissa", "koala", "oauth.approvals");
}
@Test
public void getClientMetadata_WhichDoesNotExist() throws Exception {
String clientId = generator.generate();
MockHttpServletResponse response = getTestClientMetadata(clientId, adminClientTokenWithClientsRead);
assertThat(response.getStatus(), is(HttpStatus.NOT_FOUND.value()));
}
@Test
public void getAllClientMetadata() throws Exception {
String clientId1 = generator.generate();
String marissaToken = getUserAccessToken(clientId1);
String clientId2 = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId2, null, null, null, null));
String clientId3 = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId3, null, null, null, null));
ClientMetadata client3Metadata = new ClientMetadata();
client3Metadata.setClientId(clientId3);
client3Metadata.setIdentityZoneId("uaa");
client3Metadata.setAppLaunchUrl(new URL("http://client3.com/app"));
client3Metadata.setShowOnHomePage(true);
client3Metadata.setAppIcon("Y2xpZW50IDMgaWNvbg==");
performUpdate(client3Metadata);
String clientId4 = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId4, null, null, null, null));
ClientMetadata client4Metadata = new ClientMetadata();
client4Metadata.setClientId(clientId4);
client4Metadata.setIdentityZoneId("uaa");
client4Metadata.setAppLaunchUrl(new URL("http://client4.com/app"));
client4Metadata.setAppIcon("aWNvbiBmb3IgY2xpZW50IDQ=");
performUpdate(client4Metadata);
MockHttpServletResponse response = getMockMvc().perform(get("/oauth/clients/meta")
.header("Authorization", "Bearer " + marissaToken)
.accept(APPLICATION_JSON)).andExpect(status().isOk()).andReturn().getResponse();
ArrayList<ClientMetadata> clientMetadataList = JsonUtils.readValue(response.getContentAsString(), new TypeReference<ArrayList<ClientMetadata>>() {});
assertThat(clientMetadataList, not(PredicateMatcher.<ClientMetadata>has(m -> m.getClientId().equals(clientId1))));
assertThat(clientMetadataList, not(PredicateMatcher.<ClientMetadata>has(m -> m.getClientId().equals(clientId2))));
assertThat(clientMetadataList, PredicateMatcher.<ClientMetadata>has(m -> m.getClientId().equals(clientId3) && m.getAppIcon().equals(client3Metadata.getAppIcon()) && m.getAppLaunchUrl().equals(client3Metadata.getAppLaunchUrl()) && m.isShowOnHomePage() == client3Metadata.isShowOnHomePage()));
assertThat(clientMetadataList, PredicateMatcher.<ClientMetadata>has(m -> m.getClientId().equals(clientId4) && m.getAppIcon().equals(client4Metadata.getAppIcon()) && m.getAppLaunchUrl().equals(client4Metadata.getAppLaunchUrl()) && m.isShowOnHomePage() == client4Metadata.isShowOnHomePage()));
}
@Test
public void missingAcceptHeader_isOk() throws Exception {
getMockMvc().perform(get("/oauth/clients/meta")
.header("Authorization", "Bearer " + getUserAccessToken(generator.generate())))
.andExpect(status().isOk());
}
@Test
public void wrongAcceptHeader_isNotAcceptable() throws Exception {
getMockMvc().perform(get("/oauth/clients/meta")
.header("Authorization", "Bearer " + getUserAccessToken(generator.generate()))
.accept(TEXT_PLAIN))
.andExpect(status().isNotAcceptable());
}
@Test
public void updateClientMetadata() throws Exception {
String clientId = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId, null, null, null, null));
ClientMetadata updatedClientMetadata = new ClientMetadata();
updatedClientMetadata.setClientId(clientId);
URL appLaunchUrl = new URL("http://changed.app.launch/url");
updatedClientMetadata.setAppLaunchUrl(appLaunchUrl);
ResultActions perform = performUpdate(updatedClientMetadata);
assertThat(perform.andReturn().getResponse().getContentAsString(), containsString(appLaunchUrl.toString()));
MockHttpServletResponse response = getTestClientMetadata(clientId, adminClientTokenWithClientsRead);
assertThat(response.getStatus(), is(HttpStatus.OK.value()));
assertThat(response.getContentAsString(), containsString(appLaunchUrl.toString()));
}
private ResultActions performUpdate(ClientMetadata updatedClientMetadata) throws Exception {
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/" + updatedClientMetadata.getClientId() + "/meta")
.header("Authorization", "Bearer " + adminClientTokenWithClientsWrite)
.header("If-Match", "0")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(updatedClientMetadata));
return getMockMvc().perform(updateClientPut);
}
@Test
public void updateClientMetadata_InsufficientScope() throws Exception {
String clientId = generator.generate();
String marissaToken = getUserAccessToken(clientId);
ClientMetadata updatedClientMetadata = new ClientMetadata();
updatedClientMetadata.setClientId(clientId);
URL appLaunchUrl = new URL("http://changed.app.launch/url");
updatedClientMetadata.setAppLaunchUrl(appLaunchUrl);
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + marissaToken)
.header("If-Match", "0")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(updatedClientMetadata));
MockHttpServletResponse response = getMockMvc().perform(updateClientPut).andReturn().getResponse();
assertThat(response.getStatus(), is(HttpStatus.FORBIDDEN.value()));
}
@Test
public void updateClientMetadata_WithNoClientIdInBody() throws Exception {
String clientId = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId, null, null, null, null));
ClientMetadata updatedClientMetadata = new ClientMetadata();
updatedClientMetadata.setClientId(null);
URL appLaunchUrl = new URL("http://changed.app.launch/url");
updatedClientMetadata.setAppLaunchUrl(appLaunchUrl);
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + adminClientTokenWithClientsWrite)
.header("If-Match", "0")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(updatedClientMetadata));
ResultActions perform = getMockMvc().perform(updateClientPut);
assertThat(perform.andReturn().getResponse().getContentAsString(), containsString(appLaunchUrl.toString()));
MockHttpServletResponse response = getTestClientMetadata(clientId, adminClientTokenWithClientsRead);
assertThat(response.getStatus(), is(HttpStatus.OK.value()));
assertThat(response.getContentAsString(), containsString(appLaunchUrl.toString()));
}
@Test
public void updateClientMetadata_ForNonExistentClient() throws Exception {
String clientId = generator.generate();
ClientMetadata clientMetadata = new ClientMetadata();
clientMetadata.setClientId(clientId);
URL appLaunchUrl = new URL("http://changed.app.launch/url");
clientMetadata.setAppLaunchUrl(appLaunchUrl);
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + adminClientTokenWithClientsWrite)
.header("If-Match", "0")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(clientMetadata));
ResultActions perform = getMockMvc().perform(updateClientPut);
assertEquals(perform.andReturn().getResponse().getStatus(), NOT_FOUND.value());
}
@Test
public void updateClientMetadata_ClientIdMismatch() throws Exception {
String clientId = generator.generate();
clients.addClientDetails(new BaseClientDetails(clientId, null, null, null, null));
ClientMetadata clientMetadata = new ClientMetadata();
clientMetadata.setClientId("other-client-id");
URL appLaunchUrl = new URL("http://changed.app.launch/url");
clientMetadata.setAppLaunchUrl(appLaunchUrl);
MockHttpServletRequestBuilder updateClientPut = put("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + adminClientTokenWithClientsWrite)
.header("If-Match", "0")
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(JsonUtils.writeValueAsString(clientMetadata));
ResultActions perform = getMockMvc().perform(updateClientPut);
assertEquals(perform.andReturn().getResponse().getStatus(), HttpStatus.BAD_REQUEST.value());
}
private MockHttpServletResponse getTestClientMetadata(String clientId, String token) throws Exception {
MockHttpServletRequestBuilder createClientGet = get("/oauth/clients/" + clientId + "/meta")
.header("Authorization", "Bearer " + token)
.accept(APPLICATION_JSON);
return getMockMvc().perform(createClientGet).andReturn().getResponse();
}
}