MigrateTo25_0_0 does not complete within default transaction timeout

closes #29756

Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
mposolda 2024-05-23 12:01:12 +02:00 committed by Alexander Schwartz
parent f34a7c2af4
commit ea1cdc10bd
7 changed files with 84 additions and 5 deletions

View file

@ -1455,6 +1455,16 @@ public class RealmCacheSession implements CacheRealmProvider {
return assignedScopes;
}
@Override
public void addClientScopeToAllClients(RealmModel realm, ClientScopeModel clientScope, boolean defaultClientScope) {
getClientDelegate().addClientScopeToAllClients(realm, clientScope, defaultClientScope);
// This will make sure to all invalidate all clients dependent on clientScope
listInvalidations.add(realm.getId());
cache.clientScopeRemoval(realm.getId(), invalidations);
invalidationEvents.add(ClientScopeRemovedEvent.create(clientScope.getId(), realm.getId()));
}
// Don't cache ClientInitialAccessModel for now
@Override
public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {

View file

@ -1093,6 +1093,18 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
em.flush();
}
@Override
public void addClientScopeToAllClients(RealmModel realm, ClientScopeModel clientScope, boolean defaultClientScope) {
if (realm.equals(clientScope.getRealm())) {
em.createNamedQuery("addClientScopeToAllClients")
.setParameter("realmId", realm.getId())
.setParameter("clientScopeId", clientScope.getId())
.setParameter("clientProtocol", clientScope.getProtocol())
.setParameter("defaultScope", defaultClientScope)
.executeUpdate();
}
}
@Override
public Map<String, ClientScopeModel> getClientScopes(RealmModel realm, ClientModel client, boolean defaultScope) {
// Defaults to openid-connect

View file

@ -36,7 +36,8 @@ import jakarta.persistence.Table;
@NamedQuery(name="clientScopeClientMappingIdsByClient", query="select m.clientScopeId from ClientScopeClientMappingEntity m where m.clientId = :clientId and m.defaultScope = :defaultScope"),
@NamedQuery(name="deleteClientScopeClientMapping", query="delete from ClientScopeClientMappingEntity where clientId = :clientId and clientScopeId = :clientScopeId"),
@NamedQuery(name="deleteClientScopeClientMappingByClient", query="delete from ClientScopeClientMappingEntity where clientId = :clientId"),
@NamedQuery(name="deleteClientScopeClientMappingByClientScope", query="delete from ClientScopeClientMappingEntity where clientScopeId = :clientScopeId")
@NamedQuery(name="deleteClientScopeClientMappingByClientScope", query="delete from ClientScopeClientMappingEntity where clientScopeId = :clientScopeId"),
@NamedQuery(name="addClientScopeToAllClients", query="insert into ClientScopeClientMappingEntity (clientScopeId, defaultScope, clientId) select :clientScopeId, :defaultScope, client.id from ClientEntity client where client.realmId = :realmId and client.bearerOnly <> true and client.protocol = :clientProtocol")
})
@Entity
@Table(name="CLIENT_SCOPE_CLIENT")

View file

@ -56,11 +56,14 @@ public class MigrateTo25_0_0 implements Migration {
protected void migrateRealm(KeycloakSession session, RealmModel realm) {
MigrationProvider migrationProvider = session.getProvider(MigrationProvider.class);
// create 'basic' client scope in the realm.
ClientScopeModel basicScope = migrationProvider.addOIDCBasicClientScope(realm);
ClientScopeModel basicScope = KeycloakModelUtils.getClientScopeByName(realm, "basic");
if (basicScope == null) {
// create 'basic' client scope in the realm.
basicScope = migrationProvider.addOIDCBasicClientScope(realm);
//add basic scope to existing clients
realm.getClientsStream().forEach(c-> c.addClientScope(basicScope, true));
//add basic scope to all existing OIDC clients
session.clients().addClientScopeToAllClients(realm, basicScope, true);
}
// offer a migration for persistent user sessions which was added in KC25
session.sessions().migrate(VERSION.toString());

View file

@ -273,6 +273,11 @@ public class ClientStorageManager implements ClientProvider {
localStorage().removeClientScope(realm, client, clientScope);
}
@Override
public void addClientScopeToAllClients(RealmModel realm, ClientScopeModel clientScope, boolean defaultClientScope) {
localStorage().addClientScopeToAllClients(realm, clientScope, defaultClientScope);
}
@Override
public Map<ClientModel, Set<String>> getAllRedirectUrisOfEnabledClients(RealmModel realm) {
return localStorage().getAllRedirectUrisOfEnabledClients(realm);

View file

@ -124,6 +124,15 @@ public interface ClientProvider extends ClientLookupProvider, Provider {
*/
void removeClientScope(RealmModel realm, ClientModel client, ClientScopeModel clientScope);
/**
* Add specified client scope to all non bearer-only clients in the realm, which have same protocol as specified client scope
*
* @param realm Realm
* @param clientScope client scope from the specified realm, which would be added to all clients
* @param defaultClientScope If true, then it will be added as "default" client scope. If false, then it will be added as "optional" client scope
*/
void addClientScopeToAllClients(RealmModel realm, ClientScopeModel clientScope, boolean defaultClientScope);
/**
* Returns a map of (rootUrl, {validRedirectUris}) for all enabled clients.
* @param realm

View file

@ -18,6 +18,7 @@ package org.keycloak.testsuite.migration;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.hamcrest.Matchers;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
@ -47,6 +48,7 @@ import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.protocol.saml.SamlConfigAttributes;
import org.keycloak.protocol.saml.SamlProtocolFactory;
import org.keycloak.protocol.saml.util.ArtifactBindingUtils;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
@ -422,6 +424,43 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
protected void testMigrationTo25_0_0() {
// check that all expected scopes exist in the migrated realm.
testRealmDefaultClientScopes(migrationRealm);
testClientContainsExpectedClientScopes();
}
private void testClientContainsExpectedClientScopes() {
// Test OIDC client contains expected client scopes
ClientResource migrationTestOIDCClient = ApiUtil.findClientByClientId(migrationRealm, "migration-test-client");
List<String> defaultClientScopes = migrationTestOIDCClient.getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList());
List<String> optionalClientScopes = migrationTestOIDCClient.getOptionalClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList());
assertThat(defaultClientScopes, Matchers.hasItems(
OIDCLoginProtocolFactory.BASIC_SCOPE,
OAuth2Constants.SCOPE_PROFILE,
OAuth2Constants.SCOPE_EMAIL
));
assertThat(optionalClientScopes, Matchers.hasItems(
OAuth2Constants.SCOPE_ADDRESS,
OAuth2Constants.OFFLINE_ACCESS,
OAuth2Constants.SCOPE_PHONE
));
// Test SAML client
ClientResource migrationTestSAMLClient = ApiUtil.findClientByClientId(migrationRealm, "migration-saml-client");
defaultClientScopes = migrationTestSAMLClient.getDefaultClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList());
optionalClientScopes = migrationTestSAMLClient.getOptionalClientScopes().stream()
.map(ClientScopeRepresentation::getName)
.collect(Collectors.toList());
assertThat(defaultClientScopes, Matchers.hasItems(
SamlProtocolFactory.SCOPE_ROLE_LIST
));
Assert.assertTrue(optionalClientScopes.isEmpty());
}
protected void testDeleteAccount(RealmResource realm) {