From 870702fd81bda66897f26482a03a4d51c1759213 Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Wed, 14 Oct 2015 23:14:19 +0200 Subject: [PATCH] KEYCLOAK-1918 - Add description field to client definition. Introduced description field with support for i18n for more descriptive client information. Applications can use the description to display a "slightly" longer gist of what the client / application is about, especially useful for tooltips. The description is currently limited to 255 characters. --- .../resources/META-INF/jpa-changelog-1.6.0.xml | 1 + .../representations/idm/ClientRepresentation.java | 9 +++++++++ .../admin/messages/admin-messages_de.properties | 3 ++- .../admin/messages/admin-messages_en.properties | 3 ++- .../admin/resources/partials/client-detail.html | 7 +++++++ .../main/java/org/keycloak/models/ClientModel.java | 4 ++++ .../org/keycloak/models/entities/ClientEntity.java | 5 +++++ .../models/utils/ModelToRepresentation.java | 1 + .../models/utils/RepresentationToModel.java | 2 ++ .../models/file/adapter/ClientAdapter.java | 6 ++++++ .../models/cache/infinispan/ClientAdapter.java | 12 ++++++++++++ .../models/cache/entities/CachedClient.java | 6 ++++++ .../org/keycloak/models/jpa/ClientAdapter.java | 6 ++++++ .../keycloak/models/jpa/entities/ClientEntity.java | 10 ++++++++++ .../mongo/keycloak/adapters/ClientAdapter.java | 9 +++++++++ .../services/resources/admin/ClientsResource.java | 1 + .../org/keycloak/testsuite/admin/ClientTest.java | 14 ++++++++++++++ .../keycloak/testsuite/model/ClientModelTest.java | 2 ++ 18 files changed, 99 insertions(+), 2 deletions(-) diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml index a782f76f79..7c5de94d33 100644 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml @@ -10,6 +10,7 @@ + diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java index 7ad8c6e443..099950512b 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java @@ -11,6 +11,7 @@ public class ClientRepresentation { protected String id; protected String clientId; protected String name; + protected String description; protected String rootUrl; protected String adminUrl; protected String baseUrl; @@ -51,6 +52,14 @@ public class ClientRepresentation { this.name = name; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public String getClientId() { return clientId; } diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties index 10549e1fe0..87640e0796 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties +++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties @@ -160,7 +160,7 @@ select-file=de Select file view-details=de View details clear-import=de Clear import client-id.tooltip=de Specifies ID referenced in URI and tokens. For example 'my-client' -client.name.tooltip=de Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client} +client.name.tooltip=de Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client} client.enabled.tooltip=de Disabled clients cannot initiate a login or have obtain access tokens. consent-required=de Consent Required consent-required.tooltip=de If enabled users have to consent to client access. @@ -460,3 +460,4 @@ realm=de Realm identity-provider-mappers=de Identity Provider Mappers create-identity-provider-mapper=de Create Identity Provider Mapper add-identity-provider-mapper=de Add Identity Provider Mapper +client.description.tooltip=de Specifies description of the client. For example 'My Client for TimeSheets'. Supports keys for localized values as well. For example\: ${my_client_description} diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index d5193e7f7a..3e653dbf72 100644 --- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -160,7 +160,7 @@ select-file=Select file view-details=View details clear-import=Clear import client-id.tooltip=Specifies ID referenced in URI and tokens. For example 'my-client' -client.name.tooltip=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client} +client.name.tooltip=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example\: ${my_client} client.enabled.tooltip=Disabled clients cannot initiate a login or have obtain access tokens. consent-required=Consent Required consent-required.tooltip=If enabled users have to consent to client access. @@ -458,3 +458,4 @@ realm=Realm identity-provider-mappers=Identity Provider Mappers create-identity-provider-mapper=Create Identity Provider Mapper add-identity-provider-mapper=Add Identity Provider Mapper +client.description.tooltip=Specifies description of the client. For example 'My Client for TimeSheets'. Supports keys for localized values as well. For example\: ${my_client_description} diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index 5d2b2ddd08..443cbe94ca 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -38,6 +38,13 @@ {{:: 'client.name.tooltip' | translate}} +
+ +
+ +
+ {{:: 'client.description.tooltip' | translate}} +
diff --git a/model/api/src/main/java/org/keycloak/models/ClientModel.java b/model/api/src/main/java/org/keycloak/models/ClientModel.java index ee4317cf5c..8753f45bd0 100755 --- a/model/api/src/main/java/org/keycloak/models/ClientModel.java +++ b/model/api/src/main/java/org/keycloak/models/ClientModel.java @@ -28,6 +28,10 @@ public interface ClientModel extends RoleContainerModel { void setName(String name); + String getDescription(); + + void setDescription(String description); + boolean isEnabled(); void setEnabled(boolean enabled); diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java index 4c742abc71..aa4f725b09 100755 --- a/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java +++ b/model/api/src/main/java/org/keycloak/models/entities/ClientEntity.java @@ -12,6 +12,7 @@ public class ClientEntity extends AbstractIdentifiableEntity { private String clientId; private String name; + private String description; private String realmId; private boolean enabled; private String clientAuthenticatorType; @@ -61,6 +62,10 @@ public class ClientEntity extends AbstractIdentifiableEntity { this.name = name; } + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + public boolean isEnabled() { return enabled; } diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 96b47cb1a4..03e88a8eb6 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -299,6 +299,7 @@ public class ModelToRepresentation { rep.setId(clientModel.getId()); rep.setClientId(clientModel.getClientId()); rep.setName(clientModel.getName()); + rep.setDescription(clientModel.getDescription()); rep.setEnabled(clientModel.isEnabled()); rep.setAdminUrl(clientModel.getManagementUrl()); rep.setPublicClient(clientModel.isPublicClient()); diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 62933a04c7..839e149c07 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -692,6 +692,7 @@ public class RepresentationToModel { ClientModel client = resourceRep.getId()!=null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId()); if (resourceRep.getName() != null) client.setName(resourceRep.getName()); + if(resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription()); if (resourceRep.isEnabled() != null) client.setEnabled(resourceRep.isEnabled()); client.setManagementUrl(resourceRep.getAdminUrl()); if (resourceRep.isSurrogateAuthRequired() != null) @@ -793,6 +794,7 @@ public class RepresentationToModel { public static void updateClient(ClientRepresentation rep, ClientModel resource) { if (rep.getClientId() != null) resource.setClientId(rep.getClientId()); if (rep.getName() != null) resource.setName(rep.getName()); + if (rep.getDescription() != null) resource.setDescription(rep.getDescription()); if (rep.isEnabled() != null) resource.setEnabled(rep.isEnabled()); if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired()); diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java index 1ddc36499b..81e60ee295 100755 --- a/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java +++ b/model/file/src/main/java/org/keycloak/models/file/adapter/ClientAdapter.java @@ -78,6 +78,12 @@ public class ClientAdapter implements ClientModel { entity.setName(name); } + @Override + public String getDescription() { return entity.getDescription(); } + + @Override + public void setDescription(String description) { entity.setDescription(description); } + @Override public Set getWebOrigins() { Set result = new HashSet(); diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java index e1819787ce..477c5d6ab2 100755 --- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java +++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java @@ -321,6 +321,18 @@ public class ClientAdapter implements ClientModel { updated.setName(name); } + @Override + public String getDescription() { + if (updated != null) return updated.getDescription(); + return cached.getDescription(); + } + + @Override + public void setDescription(String description) { + getDelegateForUpdate(); + updated.setDescription(description); + } + @Override public boolean isSurrogateAuthRequired() { if (updated != null) return updated.isSurrogateAuthRequired(); diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java index 3015acfba6..2d582222a8 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java @@ -25,6 +25,7 @@ public class CachedClient implements Serializable { private String id; private String clientId; private String name; + private String description; private String realm; private Set redirectUris = new HashSet(); private boolean enabled; @@ -58,6 +59,7 @@ public class CachedClient implements Serializable { secret = model.getSecret(); clientId = model.getClientId(); name = model.getName(); + description = model.getDescription(); this.realm = realm.getId(); enabled = model.isEnabled(); protocol = model.getProtocol(); @@ -103,6 +105,10 @@ public class CachedClient implements Serializable { return name; } + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + public String getRealm() { return realm; } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java index 1e0c21edaa..3baddd19a7 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java @@ -65,6 +65,12 @@ public class ClientAdapter implements ClientModel { entity.setName(name); } + @Override + public String getDescription() { return entity.getDescription(); } + + @Override + public void setDescription(String description) { entity.setDescription(description); } + @Override public boolean isEnabled() { return entity.isEnabled(); diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java index b0a30cd67f..f26f65126e 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java @@ -34,6 +34,8 @@ public class ClientEntity { private String id; @Column(name = "NAME") private String name; + @Column(name = "DESCRIPTION") + private String description; @Column(name = "CLIENT_ID") private String clientId; @Column(name="ENABLED") @@ -143,6 +145,14 @@ public class ClientEntity { this.name = name; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public boolean isEnabled() { return enabled; } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java index 4e1b4faf23..e99f142297 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java @@ -70,6 +70,15 @@ public class ClientAdapter extends AbstractMongoAdapter imple updateMongoEntity(); } + @Override + public String getDescription() { return getMongoEntity().getDescription(); } + + @Override + public void setDescription(String description) { + getMongoEntity().setDescription(description); + updateMongoEntity(); + } + @Override public void setClientId(String clientId) { getMongoEntity().setClientId(clientId); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java index e780dbf9b1..5c80bca40e 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientsResource.java @@ -73,6 +73,7 @@ public class ClientsResource { ClientRepresentation client = new ClientRepresentation(); client.setId(clientModel.getId()); client.setClientId(clientModel.getClientId()); + client.setDescription(clientModel.getDescription()); rep.add(client); } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java index 209717571e..cf2df9d9aa 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java @@ -48,6 +48,7 @@ public class ClientTest extends AbstractClientTest { private String createClient() { ClientRepresentation rep = new ClientRepresentation(); rep.setClientId("my-app"); + rep.setDescription("my-app description"); rep.setEnabled(true); Response response = realm.clients().create(rep); response.close(); @@ -79,6 +80,19 @@ public class ClientTest extends AbstractClientTest { assertTrue(rep.isEnabled()); } + /** + * See KEYCLOAK-1918 + */ + @Test + public void getClientDescription() { + + String id = createClient(); + + ClientRepresentation rep = realm.clients().get(id).toRepresentation(); + assertEquals(id, rep.getId()); + assertEquals("my-app description", rep.getDescription()); + } + @Test public void getClientSessions() throws Exception { OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password"); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java index eff25e390f..bb47ebe523 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java @@ -32,6 +32,7 @@ public class ClientModelTest extends AbstractModelTest { realm = realmManager.createRealm("original"); client = realm.addClient("application"); client.setName("Application"); + client.setDescription("Description"); client.setBaseUrl("http://base"); client.setManagementUrl("http://management"); client.setClientId("app-name"); @@ -87,6 +88,7 @@ public class ClientModelTest extends AbstractModelTest { public static void assertEquals(ClientModel expected, ClientModel actual) { Assert.assertEquals(expected.getClientId(), actual.getClientId()); Assert.assertEquals(expected.getName(), actual.getName()); + Assert.assertEquals(expected.getDescription(), actual.getDescription()); Assert.assertEquals(expected.getBaseUrl(), actual.getBaseUrl()); Assert.assertEquals(expected.getManagementUrl(), actual.getManagementUrl()); Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());