Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-index CLIENT_ATTRIBUTES using name and value #27312

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -864,19 +864,24 @@ public Stream<ClientModel> searchClientsByAttributes(RealmModel realm, Map<Strin

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]));
Expand Down
36 changes: 36 additions & 0 deletions model/jpa/src/main/resources/META-INF/jpa-changelog-24.0.0.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
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;
Expand Down Expand Up @@ -409,6 +410,7 @@ protected void testMigrationTo24_0_0(boolean testUserProfileMigration, boolean t
testUnmanagedAttributePolicySet(migrationRealm2, null);
testHS512KeyCreated(migrationRealm);
testHS512KeyCreated(migrationRealm2);
testClientAttributes(migrationRealm);
}
if (testLdapUseTruststoreSpiMigration) {
testLdapUseTruststoreSpiMigration(migrationRealm2);
Expand Down Expand Up @@ -1254,4 +1256,20 @@ private void testHS512KeyCreated(RealmResource realm) {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading