Ensure that values of attributes are unique in the database

While this is already ensured on the Java level when using a Set, database inconsistencies as occurred with Hibernate could lead to follow-up problems that are hard to analyze (as seen in #11666).

Closes #11671
This commit is contained in:
Alexander Schwartz 2022-04-27 10:19:46 +02:00 committed by Hynek Mlnařík
parent b65d76edab
commit cd20f45b8a
10 changed files with 34 additions and 5 deletions

View file

@ -18,10 +18,14 @@ package org.keycloak.models.map.storage.jpa.client.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity; import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
@Entity @Entity
@Table(name = "kc_client_attribute") @Table(name = "kc_client_attribute", uniqueConstraints = {
@UniqueConstraint(columnNames = {"fk_root", "name", "value"})
})
public class JpaClientAttributeEntity extends JpaAttributeEntity<JpaClientEntity> { public class JpaClientAttributeEntity extends JpaAttributeEntity<JpaClientEntity> {
public JpaClientAttributeEntity() { public JpaClientAttributeEntity() {

View file

@ -18,10 +18,14 @@ package org.keycloak.models.map.storage.jpa.clientscope.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity; import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
@Entity @Entity
@Table(name = "kc_client_scope_attribute") @Table(name = "kc_client_scope_attribute", uniqueConstraints = {
@UniqueConstraint(columnNames = {"fk_root", "name", "value"})
})
public class JpaClientScopeAttributeEntity extends JpaAttributeEntity<JpaClientScopeEntity> { public class JpaClientScopeAttributeEntity extends JpaAttributeEntity<JpaClientScopeEntity> {
public JpaClientScopeAttributeEntity() { public JpaClientScopeAttributeEntity() {

View file

@ -18,10 +18,14 @@ package org.keycloak.models.map.storage.jpa.group.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity; import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
@Entity @Entity
@Table(name = "kc_group_attribute") @Table(name = "kc_group_attribute", uniqueConstraints = {
@UniqueConstraint(columnNames = {"fk_root", "name", "value"})
})
public class JpaGroupAttributeEntity extends JpaAttributeEntity<JpaGroupEntity> { public class JpaGroupAttributeEntity extends JpaAttributeEntity<JpaGroupEntity> {
public JpaGroupAttributeEntity() { public JpaGroupAttributeEntity() {

View file

@ -18,6 +18,7 @@ package org.keycloak.models.map.storage.jpa.realm.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity; import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
@ -28,7 +29,9 @@ import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a> * @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/ */
@Entity @Entity
@Table(name = "kc_realm_attribute") @Table(name = "kc_realm_attribute", uniqueConstraints = {
@UniqueConstraint(columnNames = {"fk_root", "name", "value"})
})
public class JpaRealmAttributeEntity extends JpaAttributeEntity<JpaRealmEntity> { public class JpaRealmAttributeEntity extends JpaAttributeEntity<JpaRealmEntity> {
public JpaRealmAttributeEntity() { public JpaRealmAttributeEntity() {

View file

@ -18,10 +18,14 @@ package org.keycloak.models.map.storage.jpa.role.entity;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.keycloak.models.map.storage.jpa.JpaAttributeEntity; import org.keycloak.models.map.storage.jpa.JpaAttributeEntity;
@Entity @Entity
@Table(name = "kc_role_attribute") @Table(name = "kc_role_attribute", uniqueConstraints = {
@UniqueConstraint(columnNames = {"fk_root", "name", "value"})
})
public class JpaRoleAttributeEntity extends JpaAttributeEntity<JpaRoleEntity> { public class JpaRoleAttributeEntity extends JpaAttributeEntity<JpaRoleEntity> {
public JpaRoleAttributeEntity() { public JpaRoleAttributeEntity() {

View file

@ -57,6 +57,8 @@ limitations under the License.
<column name="name" type="VARCHAR(255)"/> <column name="name" type="VARCHAR(255)"/>
<column name="value" type="text"/> <column name="value" type="text"/>
</createTable> </createTable>
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
<addUniqueConstraint tableName="kc_client_scope_attribute" columnNames="fk_root, name, value" deferrable="true" initiallyDeferred="true" />
<createIndex tableName="kc_client_scope_attribute" indexName="client_scope_attr_fk_root"> <createIndex tableName="kc_client_scope_attribute" indexName="client_scope_attr_fk_root">
<column name="fk_root"/> <column name="fk_root"/>
</createIndex> </createIndex>

View file

@ -64,6 +64,8 @@ limitations under the License.
<column name="name" type="VARCHAR(255)"/> <column name="name" type="VARCHAR(255)"/>
<column name="value" type="text"/> <column name="value" type="text"/>
</createTable> </createTable>
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
<addUniqueConstraint tableName="kc_client_attribute" columnNames="fk_root, name, value" deferrable="true" initiallyDeferred="true" />
<createIndex tableName="kc_client_attribute" indexName="client_attr_fk_root"> <createIndex tableName="kc_client_attribute" indexName="client_attr_fk_root">
<column name="fk_root"/> <column name="fk_root"/>
</createIndex> </createIndex>

View file

@ -59,6 +59,8 @@ limitations under the License.
<column name="name" type="VARCHAR(255)"/> <column name="name" type="VARCHAR(255)"/>
<column name="value" type="text"/> <column name="value" type="text"/>
</createTable> </createTable>
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
<addUniqueConstraint tableName="kc_group_attribute" columnNames="fk_root, name, value" deferrable="true" initiallyDeferred="true" />
<createIndex tableName="kc_group_attribute" indexName="group_attr_fk_root"> <createIndex tableName="kc_group_attribute" indexName="group_attr_fk_root">
<column name="fk_root"/> <column name="fk_root"/>
</createIndex> </createIndex>

View file

@ -83,6 +83,8 @@ limitations under the License.
<column name="name" type="VARCHAR(255)"/> <column name="name" type="VARCHAR(255)"/>
<column name="value" type="TEXT"/> <column name="value" type="TEXT"/>
</createTable> </createTable>
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
<addUniqueConstraint tableName="kc_realm_attribute" columnNames="fk_root, name, value" deferrable="true" initiallyDeferred="true" />
<createIndex tableName="kc_realm_attribute" indexName="realm_attr_fk_root"> <createIndex tableName="kc_realm_attribute" indexName="realm_attr_fk_root">
<column name="fk_root"/> <column name="fk_root"/>
</createIndex> </createIndex>

View file

@ -66,6 +66,8 @@ limitations under the License.
<column name="name" type="VARCHAR(255)"/> <column name="name" type="VARCHAR(255)"/>
<column name="value" type="text"/> <column name="value" type="text"/>
</createTable> </createTable>
<!-- this is deferrable and initiallyDeferred as hibernate will first insert new entries and then delete the old by default -->
<addUniqueConstraint tableName="kc_role_attribute" columnNames="fk_root, name, value" deferrable="true" initiallyDeferred="true" />
<createIndex tableName="kc_role_attribute" indexName="role_attr_fk_root"> <createIndex tableName="kc_role_attribute" indexName="role_attr_fk_root">
<column name="fk_root"/> <column name="fk_root"/>
</createIndex> </createIndex>