diff --git a/model/src/main/java/org/cloudfoundry/identity/uaa/client/ClientMetadata.java b/model/src/main/java/org/cloudfoundry/identity/uaa/client/ClientMetadata.java index 360c99458ba..6dc949c7419 100644 --- a/model/src/main/java/org/cloudfoundry/identity/uaa/client/ClientMetadata.java +++ b/model/src/main/java/org/cloudfoundry/identity/uaa/client/ClientMetadata.java @@ -1,3 +1,15 @@ +/******************************************************************************* + * 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; @@ -5,22 +17,11 @@ import java.net.URL; -/******************************************************************************* - * Cloud Foundry - * Copyright (c) [2009-2015] 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. - *******************************************************************************/ @JsonInclude(JsonInclude.Include.NON_NULL) public class ClientMetadata { private String clientId; + private String clientName; private String identityZoneId; private boolean showOnHomePage; private URL appLaunchUrl; @@ -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; + } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioning.java b/server/src/main/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioning.java index 87a1b2ab605..71770e4e2e9 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioning.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioning.java @@ -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; @@ -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 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) { @@ -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()); @@ -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 { @@ -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 additionalInformation = JsonUtils.readValue(json, new TypeReference>() {}); + clientMetadata.setClientName((String)additionalInformation.get(CLIENT_NAME)); + } return clientMetadata; } } diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsService.java b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsService.java index ee2b99d5e9a..86b2231e288 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsService.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/zone/MultitenantJdbcClientDetailsService.java @@ -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; @@ -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"; @@ -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 @@ -268,13 +266,11 @@ public Log getLogger() { /** * Row mapper for ClientDetails. - * + * * @author Dave Syer - * + * */ private static class ClientDetailsRowMapper implements RowMapper { - 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)); @@ -291,7 +287,7 @@ public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException { if (json != null) { try { @SuppressWarnings("unchecked") - Map additionalInformation = mapper.read(json, Map.class); + Map additionalInformation = JsonUtils.readValue(json, Map.class); details.setAdditionalInformation(additionalInformation); } catch (Exception e) { logger.warn("Could not decode JSON for additional information: " + details, e); @@ -311,43 +307,6 @@ public ClientDetails mapRow(ResultSet rs, int rowNum) throws SQLException { } } - interface JsonMapper { - String write(Object input) throws Exception; - - T read(String input, Class 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 read(String input, Class 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 read(String input, Class 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); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/client/ClientAdminBootstrapTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/client/ClientAdminBootstrapTests.java index 4bdf20bb40d..ca3b229fcd6 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/client/ClientAdminBootstrapTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/client/ClientAdminBootstrapTests.java @@ -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"). @@ -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; @@ -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; @@ -54,7 +52,7 @@ public class ClientAdminBootstrapTests extends JdbcTestBase { private ClientAdminBootstrap bootstrap; - private JdbcClientDetailsService clientRegistrationService; + private MultitenantJdbcClientDetailsService clientRegistrationService; private ClientMetadataProvisioning clientMetadataProvisioning; @Before @@ -62,7 +60,7 @@ 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); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioningTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioningTest.java index cf928e35043..31e73811dc6 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioningTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/client/JdbcClientMetadataProvisioningTest.java @@ -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; @@ -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. - *

- * 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. - *******************************************************************************/ 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) @@ -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); diff --git a/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml b/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml index aa94d08d189..dc8b0c02d59 100644 --- a/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml +++ b/uaa/src/main/webapp/WEB-INF/spring/oauth-clients.xml @@ -27,7 +27,10 @@ + + +