Re-index CLIENT_ATTRIBUTES using name and value

Closes #26618

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc
2024-02-27 13:41:38 +01:00
committed by Alexander Schwartz
parent ca0526f54d
commit 2bd9f09e29
4 changed files with 70 additions and 11 deletions

View File

@@ -864,19 +864,24 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
Predicate attrNamePredicate = builder.equal(attributeJoin.get("name"), key);
Predicate attrValuePredicate;
if (dbProductName.equals("Oracle")) {
// SELECT * FROM client_attributes WHERE ... DBMS_LOB.COMPARE(value, '0') = 0 ...;
// Oracle is not able to compare a CLOB with a VARCHAR unless it being converted with TO_CHAR
// But for this all values in the table need to be smaller than 4K, otherwise the cast will fail with
// "ORA-22835: Buffer too small for CLOB to CHAR" (even if it is in another row).
// This leaves DBMS_LOB.COMPARE as the option to compare the CLOB with the value.
attrValuePredicate = builder.equal(builder.function("DBMS_LOB.COMPARE", Integer.class, attributeJoin.get("value"), builder.literal(value)), 0);
// Use the dbms_lob.substr index and the full comparison in oracle
Predicate attrValuePredicate1 = builder.equal(
builder.function("dbms_lob.substr", Integer.class, attributeJoin.get("value"), builder.literal(255), builder.literal(1)),
builder.function("substr", Integer.class, builder.literal(value), builder.literal(1), builder.literal(255)));
Predicate attrValuePredicate2 = builder.equal(builder.function("dbms_lob.compare", Integer.class, attributeJoin.get("value"), builder.literal(value)), 0);
predicates.add(builder.and(attrNamePredicate, attrValuePredicate1, attrValuePredicate2));
} else if (dbProductName.equals("PostgreSQL")) {
// use the substr comparison and the full comparison in postgresql
Predicate attrValuePredicate1 = builder.equal(
builder.function("substr", Integer.class, attributeJoin.get("value"), builder.literal(1), builder.literal(255)),
builder.function("substr", Integer.class, builder.literal(value), builder.literal(1), builder.literal(255)));
Predicate attrValuePredicate2 = builder.equal(attributeJoin.get("value"), value);
predicates.add(builder.and(attrNamePredicate, attrValuePredicate1, attrValuePredicate2));
} else {
attrValuePredicate = builder.equal(attributeJoin.get("value"), value);
Predicate attrValuePredicate = builder.equal(attributeJoin.get("value"), value);
predicates.add(builder.and(attrNamePredicate, attrValuePredicate));
}
predicates.add(builder.and(attrNamePredicate, attrValuePredicate));
}
Predicate finalPredicate = builder.and(predicates.toArray(new Predicate[0]));

View File

@@ -50,4 +50,40 @@
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.FederatedUserAttributeTextColumnMigration" />
</changeSet>
<changeSet author="keycloak" id="24.0.0-26618-drop-index-if-present">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<and>
<indexExists tableName="CLIENT_ATTRIBUTES" indexName="IDX_CLIENT_ATT_BY_NAME_VALUE" />
<or>
<dbms type="mysql"/>
<dbms type="mariadb"/>
<dbms type="postgresql"/>
<dbms type="oracle"/>
</or>
</and>
</preConditions>
<dropIndex tableName="CLIENT_ATTRIBUTES" indexName="IDX_CLIENT_ATT_BY_NAME_VALUE"/>
</changeSet>
<changeSet author="keycloak" id="24.0.0-26618-reindex">
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
<or>
<dbms type="mysql"/>
<dbms type="mariadb"/>
<dbms type="postgresql"/>
<dbms type="oracle"/>
</or>
</preConditions>
<createIndex tableName="CLIENT_ATTRIBUTES" indexName="IDX_CLIENT_ATT_BY_NAME_VALUE">
<column name="NAME" type="VARCHAR(255)"/>
<column name="VALUE(255)" valueComputed="VALUE(255)" />
</createIndex>
<modifySql dbms="postgresql">
<replace replace="VALUE(255)" with="substr(VALUE,1,255)" />
</modifySql>
<modifySql dbms="oracle">
<replace replace="VALUE(255)" with="dbms_lob.substr(VALUE,255,1)" />
</modifySql>
</changeSet>
</databaseChangeLog>

View File

@@ -82,6 +82,7 @@ import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -409,6 +410,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testUnmanagedAttributePolicySet(migrationRealm2, null);
testHS512KeyCreated(migrationRealm);
testHS512KeyCreated(migrationRealm2);
testClientAttributes(migrationRealm);
}
if (testLdapUseTruststoreSpiMigration) {
testLdapUseTruststoreSpiMigration(migrationRealm2);
@@ -1254,4 +1256,20 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
Assert.assertNotNull("Old HS256 key does not exist", keysMetadata.getActive().get(Algorithm.HS256));
Assert.assertNotNull("New HS256 key does not exist", keysMetadata.getActive().get(Algorithm.HS512));
}
private void testClientAttributes(RealmResource realm) {
List<ClientRepresentation> clients = realm.clients().findByClientId("migration-saml-client");
Assert.assertEquals(1, clients.size());
ClientRepresentation client = clients.get(0);
Assert.assertNotNull(client.getAttributes().get("saml.artifact.binding.identifier"));
Assert.assertNotNull(client.getAttributes().get("saml_idp_initiated_sso_url_name"));
List<String> clientIds = realm.clients().query("saml.artifact.binding.identifier:\"" + client.getAttributes().get("saml.artifact.binding.identifier") + "\"")
.stream().map(ClientRepresentation::getClientId)
.collect(Collectors.toList());
Assert.assertEquals(Collections.singletonList(client.getClientId()), clientIds);
clientIds = realm.clients().query("saml_idp_initiated_sso_url_name:\"" + client.getAttributes().get("saml_idp_initiated_sso_url_name") + "\"")
.stream().map(ClientRepresentation::getClientId)
.collect(Collectors.toList());
Assert.assertEquals(Collections.singletonList(client.getClientId()), clientIds);
}
}

View File

@@ -600,7 +600,7 @@
"saml.encrypt" : "false",
"saml_assertion_consumer_url_post" : "http://localhost:8080/sales-post/saml",
"saml.server.signature" : "true",
"saml_idp_initiated_sso_url_name" : "sales-post",
"saml_idp_initiated_sso_url_name" : "sales-post-very-long-name-greater-than-255-characters-0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901",
"exclude.session.state.from.auth.response" : "false",
"saml.artifact.binding.identifier" : "ZDisLXkadz6IlDoL8l343V44KP0=",
"saml.artifact.binding" : "false",