Skip to content

Commit

Permalink
Support additional information in the metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanik committed Feb 3, 2016
1 parent 7ddd8f8 commit aad006f
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 80 deletions.
@@ -1,26 +1,27 @@
/*******************************************************************************
* 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.client;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.net.URL;

/*******************************************************************************
* 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.
*******************************************************************************/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ClientMetadata {

private String clientId;
private String clientName;
private String identityZoneId;
private boolean showOnHomePage;
private URL appLaunchUrl;
Expand Down Expand Up @@ -67,4 +68,12 @@ public String getAppIcon() {
public void setAppIcon(String appIcon) {
this.appIcon = appIcon;
}

public String getClientName() {
return clientName;
}

public void setClientName(String clientName) {
this.clientName = clientName;
}
}
Expand Up @@ -12,12 +12,17 @@
*******************************************************************************/
package org.cloudfoundry.identity.uaa.client;

import com.fasterxml.jackson.core.type.TypeReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;

Expand All @@ -27,23 +32,35 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import static org.cloudfoundry.identity.uaa.oauth.client.ClientConstants.CLIENT_NAME;
import static org.springframework.util.StringUtils.hasText;

public class JdbcClientMetadataProvisioning implements ClientMetadataProvisioning {

private static final Log logger = LogFactory.getLog(JdbcClientMetadataProvisioning.class);

private static final String CLIENT_METADATA_FIELDS = "client_id, identity_zone_id, show_on_home_page, app_launch_url, app_icon";

private static final String CLIENT_METADATA_FIELDS = "client_id, identity_zone_id, show_on_home_page, app_launch_url, app_icon, additional_information";
private static final String CLIENT_METADATA_QUERY = "select " + CLIENT_METADATA_FIELDS + " from oauth_client_details where client_id=? and identity_zone_id=?";
private static final String CLIENT_METADATAS_QUERY = "select " + CLIENT_METADATA_FIELDS + " from oauth_client_details where identity_zone_id=? and ((app_launch_url is not null and char_length(app_launch_url)>0) or (app_icon is not null and octet_length(app_icon)>0))";
private static final String CLIENT_METADATA_UPDATE_FIELDS = "show_on_home_page, app_launch_url, app_icon";
private static final String CLIENT_METADATA_UPDATE = "update oauth_client_details set " + CLIENT_METADATA_UPDATE_FIELDS.replace(",", "=?,") + "=?" + " where client_id=? and identity_zone_id=?";

private JdbcTemplate template;
private ClientDetailsService clientDetailsService;
private ClientRegistrationService clientRegistrationService;
private final RowMapper<ClientMetadata> mapper = new ClientMetadataRowMapper();

JdbcClientMetadataProvisioning(JdbcTemplate template) {
JdbcClientMetadataProvisioning(ClientDetailsService clientDetailsService,
ClientRegistrationService clientRegistrationService,
JdbcTemplate template) {
Assert.notNull(template);
Assert.notNull(clientDetailsService);
this.template = template;
this.clientDetailsService = clientDetailsService;
this.clientRegistrationService = clientRegistrationService;
}

public void setTemplate(JdbcTemplate template) {
Expand All @@ -65,6 +82,8 @@ public ClientMetadata retrieve(String clientId) {
@Override
public ClientMetadata update(ClientMetadata resource) {
logger.debug("Updating metadata for client: " + resource.getClientId());

updateClientNameIfNotEmpty(resource);
int updated = template.update(CLIENT_METADATA_UPDATE, ps -> {
int pos = 1;
ps.setBoolean(pos++, resource.isShowOnHomePage());
Expand All @@ -89,6 +108,15 @@ public ClientMetadata update(ClientMetadata resource) {
return resultingClientMetadata;
}

protected void updateClientNameIfNotEmpty(ClientMetadata resource) {
//we don't remove it, only set values
if (hasText(resource.getClientName())) {
BaseClientDetails client = (BaseClientDetails) clientDetailsService.loadClientByClientId(resource.getClientId());
client.addAdditionalInformation(CLIENT_NAME, resource.getClientName());
clientRegistrationService.updateClientDetails(client);
}
}


private class ClientMetadataRowMapper implements RowMapper<ClientMetadata> {

Expand All @@ -105,7 +133,14 @@ public ClientMetadata mapRow(ResultSet rs, int rowNum) throws SQLException {
// it is safe to ignore this as client_metadata rows are always created from a ClientMetadata instance whose launch url property is strongly typed to URL
}
byte[] iconBytes = rs.getBytes(pos++);
if(iconBytes != null) { clientMetadata.setAppIcon(new String(Base64Utils.encode(iconBytes))); }
if(iconBytes != null) {
clientMetadata.setAppIcon(new String(Base64Utils.encode(iconBytes)));
}
String json = rs.getString(pos++);
if (hasText(json)) {
Map<String,Object> additionalInformation = JsonUtils.readValue(json, new TypeReference<Map<String,Object>>() {});
clientMetadata.setClientName((String)additionalInformation.get(CLIENT_NAME));
}
return clientMetadata;
}
}
Expand Down
Expand Up @@ -19,6 +19,7 @@
import org.cloudfoundry.identity.uaa.util.JsonUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
Expand Down Expand Up @@ -55,10 +56,6 @@ public class MultitenantJdbcClientDetailsService extends JdbcClientDetailsServic

private static final Log logger = LogFactory.getLog(MultitenantJdbcClientDetailsService.class);



private JsonMapper mapper = createJsonMapper();

private static final String CLIENT_FIELDS_FOR_UPDATE = "resource_ids, scope, "
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
+ "refresh_token_validity, additional_information, autoapprove, lastmodified";
Expand Down Expand Up @@ -177,9 +174,10 @@ private Object[] getFields(ClientDetails clientDetails) {
private Object[] getFieldsForUpdate(ClientDetails clientDetails) {
String json = null;
try {
json = mapper.write(clientDetails.getAdditionalInformation());
json = JsonUtils.writeValueAsString(clientDetails.getAdditionalInformation());
} catch (Exception e) {
logger.warn("Could not serialize additional information: " + clientDetails, e);
throw new InvalidDataAccessResourceUsageException("Could not serialize additional information:"+clientDetails.getClientId(), e);
}
return new Object[] {
clientDetails.getResourceIds() != null ? StringUtils.collectionToCommaDelimitedString(clientDetails
Expand Down Expand Up @@ -268,13 +266,11 @@ public Log getLogger() {

/**
* Row mapper for ClientDetails.
*
*
* @author Dave Syer
*
*
*/
private static class ClientDetailsRowMapper implements RowMapper<ClientDetails> {
private JsonMapper mapper = createJsonMapper();

public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
BaseClientDetails details = new BaseClientDetails(rs.getString(1), rs.getString(3), rs.getString(4),
rs.getString(5), rs.getString(7), rs.getString(6));
Expand All @@ -291,7 +287,7 @@ public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
if (json != null) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> additionalInformation = mapper.read(json, Map.class);
Map<String, Object> additionalInformation = JsonUtils.readValue(json, Map.class);
details.setAdditionalInformation(additionalInformation);
} catch (Exception e) {
logger.warn("Could not decode JSON for additional information: " + details, e);
Expand All @@ -311,43 +307,6 @@ public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
}
}

interface JsonMapper {
String write(Object input) throws Exception;

<T> T read(String input, Class<T> type) throws Exception;
}

private static JsonMapper createJsonMapper() {
return new JacksonMapper();
}

private static class JacksonMapper implements JsonMapper {

@Override
public String write(Object input) throws Exception {
return JsonUtils.writeValueAsString(input);
}

@Override
public <T> T read(String input, Class<T> type) throws Exception {
return JsonUtils.readValue(input, type);
}
}

private static class NotSupportedJsonMapper implements JsonMapper {
@Override
public String write(Object input) throws Exception {
throw new UnsupportedOperationException(
"Neither Jackson 1 nor 2 is available so JSON conversion cannot be done");
}

@Override
public <T> T read(String input, Class<T> type) throws Exception {
throw new UnsupportedOperationException(
"Neither Jackson 1 nor 2 is available so JSON conversion cannot be done");
}
}

@Override
public int getTotalCount() {
Integer count = jdbcTemplate.queryForObject("select count(*) from oauth_client_details", Integer.class);
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Cloud Foundry
* 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").
Expand All @@ -13,7 +13,6 @@

package org.cloudfoundry.identity.uaa.client;

import org.cloudfoundry.identity.uaa.client.ClientAdminBootstrap;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
import org.cloudfoundry.identity.uaa.test.JdbcTestBase;
import org.cloudfoundry.identity.uaa.zone.MultitenantJdbcClientDetailsService;
Expand All @@ -27,7 +26,6 @@
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.util.StringUtils;
import org.yaml.snakeyaml.Yaml;

Expand All @@ -54,15 +52,15 @@ public class ClientAdminBootstrapTests extends JdbcTestBase {

private ClientAdminBootstrap bootstrap;

private JdbcClientDetailsService clientRegistrationService;
private MultitenantJdbcClientDetailsService clientRegistrationService;
private ClientMetadataProvisioning clientMetadataProvisioning;

@Before
public void setUpClientAdminTests() throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
bootstrap = new ClientAdminBootstrap(encoder);
clientRegistrationService = new MultitenantJdbcClientDetailsService(dataSource);
clientMetadataProvisioning = new JdbcClientMetadataProvisioning(jdbcTemplate);
clientMetadataProvisioning = new JdbcClientMetadataProvisioning(clientRegistrationService,clientRegistrationService,jdbcTemplate);
bootstrap.setClientRegistrationService(clientRegistrationService);
bootstrap.setClientMetadataProvisioning(clientMetadataProvisioning);
clientRegistrationService.setPasswordEncoder(encoder);
Expand Down
@@ -1,8 +1,22 @@
/*******************************************************************************
* 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.client;

import org.cloudfoundry.identity.uaa.test.JdbcTestBase;
import org.cloudfoundry.identity.uaa.util.PredicateMatcher;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.cloudfoundry.identity.uaa.zone.MultitenantJdbcClientDetailsService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.dao.EmptyResultDataAccessException;
Expand All @@ -13,29 +27,20 @@
import java.util.List;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

/*******************************************************************************
* 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.
*******************************************************************************/
public class JdbcClientMetadataProvisioningTest extends JdbcTestBase {

public static final String CLIENT_NAME = "Test name";
JdbcClientMetadataProvisioning db;

private RandomValueStringGenerator generator = new RandomValueStringGenerator(8);

@Before
public void createDatasource() throws Exception {
db = new JdbcClientMetadataProvisioning(jdbcTemplate);
MultitenantJdbcClientDetailsService clientService = new MultitenantJdbcClientDetailsService(dataSource);
db = new JdbcClientMetadataProvisioning(clientService, clientService, jdbcTemplate);
}

@Test(expected = EmptyResultDataAccessException.class)
Expand Down Expand Up @@ -99,6 +104,24 @@ public void updateClientMetadata() throws Exception {
assertThat(updatedClientMetadata.getAppIcon(), is(newClientMetadata.getAppIcon()));
}

@Test
public void test_set_and_get_ClientName() throws Exception {
String clientId = generator.generate();
jdbcTemplate.execute("insert into oauth_client_details(client_id, identity_zone_id) values ('" + clientId + "', '" + IdentityZoneHolder.get().getId() + "')");
ClientMetadata data = createTestClientMetadata(clientId,
false,
null,
null);
data.setClientName(CLIENT_NAME);
db.update(data);
data = db.retrieve(clientId);
assertEquals(CLIENT_NAME, data.getClientName());
}





private ClientMetadata createTestClientMetadata(String clientId, boolean showOnHomePage, URL appLaunchUrl, String appIcon) throws MalformedURLException {
ClientMetadata clientMetadata = new ClientMetadata();
clientMetadata.setClientId(clientId);
Expand Down
3 changes: 3 additions & 0 deletions uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml
Expand Up @@ -27,7 +27,10 @@
</bean>

<bean id="jdbcClientMetadataProvisioning" class="org.cloudfoundry.identity.uaa.client.JdbcClientMetadataProvisioning">
<constructor-arg name="clientDetailsService" ref="jdbcClientDetailsService"/>
<constructor-arg name="clientRegistrationService" ref="jdbcClientDetailsService"/>
<constructor-arg name="template" ref="jdbcTemplate" />

</bean>

<bean id="clientAdminBootstrap" class="org.cloudfoundry.identity.uaa.client.ClientAdminBootstrap">
Expand Down

0 comments on commit aad006f

Please sign in to comment.