keycloak-uncached
KEYCLOAK-8519 OIDCScopeTest.testClientDisplayedOnConsentScreenWithEmptyConsentText …
11/21/2018 10:06:16 AM
Changes
Details
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 cab098e..592c1e2 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
@@ -21,20 +21,17 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
-import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.jpa.entities.ClientAttributeEntity;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity;
-import org.keycloak.models.jpa.entities.ClientScopeEntity;
-import org.keycloak.models.jpa.entities.ClientScopeRoleMappingEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
@@ -44,6 +41,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -302,25 +300,45 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
@Override
public void setAttribute(String name, String value) {
- entity.getAttributes().put(name, value);
+ for (ClientAttributeEntity attr : entity.getAttributes()) {
+ if (attr.getName().equals(name)) {
+ attr.setValue(value);
+ return;
+ }
+ }
+ ClientAttributeEntity attr = new ClientAttributeEntity();
+ attr.setName(name);
+ attr.setValue(value);
+ attr.setClient(entity);
+ em.persist(attr);
+ entity.getAttributes().add(attr);
}
@Override
public void removeAttribute(String name) {
- entity.getAttributes().remove(name);
+ Iterator<ClientAttributeEntity> it = entity.getAttributes().iterator();
+ while (it.hasNext()) {
+ ClientAttributeEntity attr = it.next();
+ if (attr.getName().equals(name)) {
+ it.remove();
+ em.remove(attr);
+ }
+ }
}
@Override
public String getAttribute(String name) {
- return entity.getAttributes().get(name);
+ return getAttributes().get(name);
}
@Override
public Map<String, String> getAttributes() {
- Map<String, String> copy = new HashMap<>();
- copy.putAll(entity.getAttributes());
- return copy;
+ Map<String, String> attrs = new HashMap<>();
+ for (ClientAttributeEntity attr : entity.getAttributes()) {
+ attrs.put(attr.getName(), attr.getValue());
+ }
+ return attrs;
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientScopeAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientScopeAdapter.java
index 0521ba9..13d84e8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientScopeAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientScopeAdapter.java
@@ -24,6 +24,7 @@ import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.jpa.entities.ClientScopeAttributeEntity;
import org.keycloak.models.jpa.entities.ClientScopeEntity;
import org.keycloak.models.jpa.entities.ClientScopeRoleMappingEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
@@ -34,6 +35,7 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -282,18 +284,37 @@ public class ClientScopeAdapter implements ClientScopeModel, JpaModel<ClientScop
@Override
public void setAttribute(String name, String value) {
- entity.getAttributes().put(name, value);
+ for (ClientScopeAttributeEntity attr : entity.getAttributes()) {
+ if (attr.getName().equals(name)) {
+ attr.setValue(value);
+ return;
+ }
+ }
+
+ ClientScopeAttributeEntity attr = new ClientScopeAttributeEntity();
+ attr.setName(name);
+ attr.setValue(value);
+ attr.setClientScope(entity);
+ em.persist(attr);
+ entity.getAttributes().add(attr);
}
@Override
public void removeAttribute(String name) {
- entity.getAttributes().remove(name);
+ Iterator<ClientScopeAttributeEntity> it = entity.getAttributes().iterator();
+ while (it.hasNext()) {
+ ClientScopeAttributeEntity attr = it.next();
+ if (attr.getName().equals(name)) {
+ it.remove();
+ em.remove(attr);
+ }
+ }
}
@Override
public String getAttribute(String name) {
- return entity.getAttributes().get(name);
+ return getAttributes().get(name);
}
public static ClientScopeEntity toClientScopeEntity(ClientScopeModel model, EntityManager em) {
@@ -305,9 +326,11 @@ public class ClientScopeAdapter implements ClientScopeModel, JpaModel<ClientScop
@Override
public Map<String, String> getAttributes() {
- Map<String, String> copy = new HashMap<>();
- copy.putAll(entity.getAttributes());
- return copy;
+ Map<String, String> attrs = new HashMap<>();
+ for (ClientScopeAttributeEntity attr : entity.getAttributes()) {
+ attrs.put(attr.getName(), attr.getValue());
+ }
+ return attrs;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientAttributeEntity.java
new file mode 100644
index 0000000..1292dc7
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientAttributeEntity.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.jpa.entities;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Table(name="CLIENT_ATTRIBUTES")
+@Entity
+@IdClass(ClientAttributeEntity.Key.class)
+public class ClientAttributeEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "CLIENT_ID")
+ protected ClientEntity client;
+
+ @Id
+ @Column(name="NAME")
+ protected String name;
+
+ @Column(name = "VALUE", length = 4000)
+ protected String value;
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ public void setClient(ClientEntity client) {
+ this.client = client;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+
+ public static class Key implements Serializable {
+
+ protected ClientEntity client;
+
+ protected String name;
+
+ public Key() {
+ }
+
+ public Key(ClientEntity client, String name) {
+ this.client = client;
+ this.name = name;
+ }
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ClientAttributeEntity.Key key = (ClientAttributeEntity.Key) o;
+
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+ if (name != null ? !name.equals(key.name != null ? key.name : null) : key.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = client != null ? client.getId().hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof ClientAttributeEntity)) return false;
+
+ ClientAttributeEntity key = (ClientAttributeEntity) o;
+
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+ if (name != null ? !name.equals(key.name != null ? key.name : null) : key.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = client != null ? client.getId().hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+}
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 cc0672f..874bc82 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
@@ -104,11 +104,8 @@ public class ClientEntity {
@CollectionTable(name = "REDIRECT_URIS", joinColumns={ @JoinColumn(name="CLIENT_ID") })
protected Set<String> redirectUris = new HashSet<String>();
- @ElementCollection
- @MapKeyColumn(name="NAME")
- @Column(name="VALUE", length = 4000)
- @CollectionTable(name="CLIENT_ATTRIBUTES", joinColumns={ @JoinColumn(name="CLIENT_ID") })
- protected Map<String, String> attributes = new HashMap<String, String>();
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "client")
+ protected Collection<ClientAttributeEntity> attributes = new ArrayList<>();
@ElementCollection
@MapKeyColumn(name="BINDING_NAME")
@@ -278,11 +275,11 @@ public class ClientEntity {
this.fullScopeAllowed = fullScopeAllowed;
}
- public Map<String, String> getAttributes() {
+ public Collection<ClientAttributeEntity> getAttributes() {
return attributes;
}
- public void setAttributes(Map<String, String> attributes) {
+ public void setAttributes(Collection<ClientAttributeEntity> attributes) {
this.attributes = attributes;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeAttributeEntity.java
new file mode 100644
index 0000000..6be9991
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeAttributeEntity.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.jpa.entities;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@Table(name="CLIENT_SCOPE_ATTRIBUTES")
+@Entity
+@IdClass(ClientScopeAttributeEntity.Key.class)
+public class ClientScopeAttributeEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "SCOPE_ID")
+ protected ClientScopeEntity clientScope;
+
+ @Id
+ @Column(name="NAME")
+ protected String name;
+
+ @Column(name = "VALUE", length = 2048)
+ protected String value;
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public void setClientScope(ClientScopeEntity clientScope) {
+ this.clientScope = clientScope;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+
+ public static class Key implements Serializable {
+
+ protected ClientScopeEntity clientScope;
+
+ protected String name;
+
+ public Key() {
+ }
+
+ public Key(ClientScopeEntity clientScope, String name) {
+ this.clientScope = clientScope;
+ this.name = name;
+ }
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ClientScopeAttributeEntity.Key key = (ClientScopeAttributeEntity.Key) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (name != null ? !name.equals(key.name != null ? key.name : null) : key.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof ClientScopeAttributeEntity)) return false;
+
+ ClientScopeAttributeEntity key = (ClientScopeAttributeEntity) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (name != null ? !name.equals(key.name != null ? key.name : null) : key.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeEntity.java
index 7fe193e..eddabe5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeEntity.java
@@ -22,22 +22,17 @@ import org.hibernate.annotations.Nationalized;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.CascadeType;
-import javax.persistence.CollectionTable;
import javax.persistence.Column;
-import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
-import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -66,11 +61,8 @@ public class ClientScopeEntity {
private String protocol;
- @ElementCollection
- @MapKeyColumn(name="NAME")
- @Column(name="VALUE", length = 2048)
- @CollectionTable(name="CLIENT_SCOPE_ATTRIBUTES", joinColumns={ @JoinColumn(name="SCOPE_ID") })
- protected Map<String, String> attributes = new HashMap<String, String>();
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "clientScope")
+ protected Collection<ClientScopeAttributeEntity> attributes = new ArrayList<>();
public RealmEntity getRealm() {
return realm;
@@ -120,11 +112,11 @@ public class ClientScopeEntity {
this.protocol = protocol;
}
- public Map<String, String> getAttributes() {
+ public Collection<ClientScopeAttributeEntity> getAttributes() {
return attributes;
}
- public void setAttributes(Map<String, String> attributes) {
+ public void setAttributes(Collection<ClientScopeAttributeEntity> attributes) {
this.attributes = attributes;
}
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index d888203..1649fa1 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -21,6 +21,7 @@
version="1.0">
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
<class>org.keycloak.models.jpa.entities.CredentialAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
@@ -54,6 +55,7 @@
<class>org.keycloak.models.jpa.entities.GroupRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.UserGroupMembershipEntity</class>
<class>org.keycloak.models.jpa.entities.ClientScopeEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientScopeAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.ClientScopeRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity</class>
<class>org.keycloak.models.jpa.entities.DefaultClientScopeRealmMappingEntity</class>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java
index 5ffe8b6..937028d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java
@@ -22,6 +22,7 @@ import org.junit.Test;
import org.keycloak.admin.client.resource.ClientScopesResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RoleMappingResource;
+import org.keycloak.common.util.ObjectUtil;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.AccountRoles;
@@ -135,12 +136,19 @@ public class ClientScopeTest extends AbstractClientTest {
scopeRep.setName("scope1");
scopeRep.setDescription("scope1-desc");
scopeRep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ Map<String, String> attrs = new HashMap<>();
+ attrs.put("someAttr", "someAttrValue");
+ attrs.put("emptyAttr", "");
+ scopeRep.setAttributes(attrs);
String scope1Id = createClientScope(scopeRep);
// Assert created attributes
scopeRep = clientScopes().get(scope1Id).toRepresentation();
Assert.assertEquals("scope1", scopeRep.getName());
Assert.assertEquals("scope1-desc", scopeRep.getDescription());
+ Assert.assertEquals("someAttrValue", scopeRep.getAttributes().get("someAttr"));
+ Assert.assertTrue(ObjectUtil.isBlank(scopeRep.getAttributes().get("emptyAttr")));
Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, scopeRep.getProtocol());
@@ -149,6 +157,9 @@ public class ClientScopeTest extends AbstractClientTest {
scopeRep.setDescription("scope1-desc-updated");
scopeRep.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+ // Test update attribute to some non-blank value
+ scopeRep.getAttributes().put("emptyAttr", "someValue");
+
clientScopes().get(scope1Id).update(scopeRep);
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientScopeResourcePath(scope1Id), scopeRep, ResourceType.CLIENT_SCOPE);
@@ -158,6 +169,8 @@ public class ClientScopeTest extends AbstractClientTest {
Assert.assertEquals("scope1-updated", scopeRep.getName());
Assert.assertEquals("scope1-desc-updated", scopeRep.getDescription());
Assert.assertEquals(SamlProtocol.LOGIN_PROTOCOL, scopeRep.getProtocol());
+ Assert.assertEquals("someAttrValue", scopeRep.getAttributes().get("someAttr"));
+ Assert.assertEquals("someValue", scopeRep.getAttributes().get("emptyAttr"));
// Remove scope1
clientScopes().get(scope1Id).remove();