Entry 999.0.0 in MIGRATION_MODEL prevents future migrations of the database

Closes #27941

Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
Marek Posolda 2024-05-23 14:00:18 +02:00 committed by GitHub
parent 4acf61dd00
commit 2efc163b89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 87 additions and 0 deletions

View file

@ -63,6 +63,7 @@ import org.keycloak.migration.migrators.Migration;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.DeploymentStateProvider; import org.keycloak.models.DeploymentStateProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.storage.MigrationManager; import org.keycloak.storage.MigrationManager;
@ -145,6 +146,14 @@ public class DefaultMigrationManager implements MigrationManager {
m.migrate(session); m.migrate(session);
} }
} }
} else if (currentVersion.lessThan(databaseVersion)) {
if (databaseVersion.equals(SNAPSHOT_VERSION)) {
throw new ModelException("Incorrect state of migration. You are trying to run server version '" + currentVersion + "' against a database which was migrated to snapshot version '"
+ databaseVersion + "'. Databases that have been migrated to a snapshot version can't be migrated to a released version of Keycloak or to a more recent snapshot version.");
} else {
logger.warnf("Possibly incorrect state of migration. You are trying to run server version '" + currentVersion + "' against database, which was already migrated to newer version '" +
databaseVersion + "'.");
}
} }
if (databaseVersion == null || databaseVersion.lessThan(currentVersion)) { if (databaseVersion == null || databaseVersion.lessThan(currentVersion)) {
@ -159,6 +168,7 @@ public class DefaultMigrationManager implements MigrationManager {
public static final ModelVersion RHSSO_VERSION_7_2_KEYCLOAK_VERSION = new ModelVersion("3.4.3"); public static final ModelVersion RHSSO_VERSION_7_2_KEYCLOAK_VERSION = new ModelVersion("3.4.3");
public static final ModelVersion RHSSO_VERSION_7_3_KEYCLOAK_VERSION = new ModelVersion("4.8.3"); public static final ModelVersion RHSSO_VERSION_7_3_KEYCLOAK_VERSION = new ModelVersion("4.8.3");
public static final ModelVersion RHSSO_VERSION_7_4_KEYCLOAK_VERSION = new ModelVersion("9.0.3"); public static final ModelVersion RHSSO_VERSION_7_4_KEYCLOAK_VERSION = new ModelVersion("9.0.3");
public static final ModelVersion SNAPSHOT_VERSION = new ModelVersion("999.0.0-SNAPSHOT");
private static final Map<Pattern, ModelVersion> PATTERN_MATCHER = new LinkedHashMap<>(); private static final Map<Pattern, ModelVersion> PATTERN_MATCHER = new LinkedHashMap<>();
static { static {
@ -180,6 +190,12 @@ public class DefaultMigrationManager implements MigrationManager {
} }
if (stored == null) { if (stored == null) {
stored = migrations[0].getVersion(); stored = migrations[0].getVersion();
} else {
ModelVersion currentVersion = new ModelVersion(Version.VERSION);
if (currentVersion.lessThan(stored)) {
logger.warnf("Possibly incorrect state of migration during realm import. You are running server version '" + currentVersion + "' when importing JSON file, which was created in the newer version '" +
stored + "'.");
}
} }
for (Migration m : migrations) { for (Migration m : migrations) {

View file

@ -0,0 +1,71 @@
/*
* Copyright 2024 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.testsuite.migration;
import java.util.List;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import org.keycloak.common.Version;
import org.keycloak.migration.MigrationModel;
import org.keycloak.models.DeploymentStateProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.storage.datastore.DefaultMigrationManager;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.arquillian.annotation.ModelTest;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MigrationDeniedTest extends AbstractKeycloakTest {
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
}
/**
* Tests migration should not be allowed when DB version is set to snapshot version like "999.0.0", but Keycloak server version is lower like "23.0.0"
*/
@Test
@ModelTest
public void testMigrationDenied(KeycloakSession session) {
MigrationModel model = session.getProvider(DeploymentStateProvider.class).getMigrationModel();
String databaseVersion = model.getStoredVersion() != null ? model.getStoredVersion() : null;
Assume.assumeTrue("Test ignored as it is working just with DB migrated in version '" + databaseVersion + "', but current DB version is " + databaseVersion,
DefaultMigrationManager.SNAPSHOT_VERSION.toString().equals(databaseVersion));
String currentVersion = Version.VERSION;
try {
// Simulate to manually set runtime version of KeycloakServer to 23. Migration should fail as the version is lower than DB version.
Version.VERSION = "23.0.0";
new DefaultMigrationManager(session).migrate();
Assert.fail("Not expected to successfully run migration. DB version was " + databaseVersion + ". Keycloak version was " + currentVersion);
} catch (ModelException expected) {
Assert.assertTrue(expected.getMessage().startsWith("Incorrect state of migration"));
} finally {
Version.VERSION = currentVersion;
}
}
}