KEYCLOAK-18069 Migration of client policies JSON from Keycloak 13
This commit is contained in:
parent
aac0b6ec5f
commit
070c68e18a
9 changed files with 2888 additions and 21 deletions
|
@ -18,8 +18,13 @@
|
|||
package org.keycloak.representations.idm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -33,6 +38,9 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmRepresentation {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(RealmRepresentation.class);
|
||||
|
||||
protected String id;
|
||||
protected String realm;
|
||||
protected String displayName;
|
||||
|
@ -144,8 +152,11 @@ public class RealmRepresentation {
|
|||
|
||||
// Client Policies/Profiles
|
||||
|
||||
protected ClientProfilesRepresentation clientProfiles;
|
||||
protected ClientPoliciesRepresentation clientPolicies;
|
||||
@JsonProperty("clientProfiles")
|
||||
protected JsonNode clientProfiles;
|
||||
|
||||
@JsonProperty("clientPolicies")
|
||||
protected JsonNode clientPolicies;
|
||||
|
||||
protected List<UserRepresentation> users;
|
||||
protected List<UserRepresentation> federatedUsers;
|
||||
|
@ -1181,20 +1192,44 @@ public class RealmRepresentation {
|
|||
|
||||
// Client Policies/Profiles
|
||||
|
||||
public ClientProfilesRepresentation getClientProfiles() {
|
||||
return clientProfiles;
|
||||
@JsonIgnore
|
||||
public ClientProfilesRepresentation getParsedClientProfiles() {
|
||||
try {
|
||||
if (clientProfiles == null) return null;
|
||||
return JsonSerialization.mapper.convertValue(clientProfiles, ClientProfilesRepresentation.class);
|
||||
} catch (IllegalArgumentException ioe) {
|
||||
logger.warnf("Failed to deserialize client profiles in the realm %s. Fallback to return empty profiles. Details: %s", realm, ioe.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setClientProfiles(ClientProfilesRepresentation clientProfiles) {
|
||||
this.clientProfiles = clientProfiles;
|
||||
@JsonIgnore
|
||||
public void setParsedClientProfiles(ClientProfilesRepresentation clientProfiles) {
|
||||
if (clientProfiles == null) {
|
||||
this.clientProfiles = null;
|
||||
return;
|
||||
}
|
||||
this.clientProfiles = JsonSerialization.mapper.convertValue(clientProfiles, JsonNode.class);
|
||||
}
|
||||
|
||||
public ClientPoliciesRepresentation getClientPolicies() {
|
||||
return clientPolicies;
|
||||
@JsonIgnore
|
||||
public ClientPoliciesRepresentation getParsedClientPolicies() {
|
||||
try {
|
||||
if (clientPolicies == null) return null;
|
||||
return JsonSerialization.mapper.convertValue(clientPolicies, ClientPoliciesRepresentation.class);
|
||||
} catch (IllegalArgumentException ioe) {
|
||||
logger.warnf("Failed to deserialize client policies in the realm %s. Fallback to return empty profiles. Details: %s", realm, ioe.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setClientPolicies(ClientPoliciesRepresentation clientPolicies) {
|
||||
this.clientPolicies = clientPolicies;
|
||||
@JsonIgnore
|
||||
public void setParsedClientPolicies(ClientPoliciesRepresentation clientPolicies) {
|
||||
if (clientPolicies == null) {
|
||||
this.clientPolicies = null;
|
||||
return;
|
||||
}
|
||||
this.clientPolicies = JsonSerialization.mapper.convertValue(clientPolicies, JsonNode.class);
|
||||
}
|
||||
|
||||
public String getBrowserFlow() {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
|
||||
<module name="org.keycloak.keycloak-common"/>
|
||||
<module name="org.bouncycastle" />
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.regex.Pattern;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.common.Version;
|
||||
import org.keycloak.migration.migrators.MigrateTo12_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo14_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_2_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_3_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo1_4_0;
|
||||
|
@ -94,7 +95,8 @@ public class MigrationModelManager {
|
|||
new MigrateTo8_0_2(),
|
||||
new MigrateTo9_0_0(),
|
||||
new MigrateTo9_0_4(),
|
||||
new MigrateTo12_0_0()
|
||||
new MigrateTo12_0_0(),
|
||||
new MigrateTo14_0_0()
|
||||
};
|
||||
|
||||
public static void migrate(KeycloakSession session) {
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2021 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.migration.migrators;
|
||||
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelException;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
|
||||
import org.keycloak.representations.idm.ClientProfilesRepresentation;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class MigrateTo14_0_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("14.0.0");
|
||||
|
||||
@Override
|
||||
public void migrate(KeycloakSession session) {
|
||||
session.realms()
|
||||
.getRealmsStream()
|
||||
.forEach(realm -> migrateRealm(session, realm));
|
||||
}
|
||||
|
||||
private void migrateRealm(KeycloakSession session, RealmModel realm) {
|
||||
try {
|
||||
session.clientPolicy().updateClientProfiles(realm, new ClientProfilesRepresentation());
|
||||
session.clientPolicy().updateClientPolicies(realm, new ClientPoliciesRepresentation());
|
||||
} catch (ClientPolicyException cpe) {
|
||||
throw new ModelException("Exception during migration client profiles or client policies", cpe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
}
|
|
@ -202,6 +202,7 @@ public class ClientPoliciesUtil {
|
|||
try {
|
||||
return JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
|
||||
} catch (IOException ioe) {
|
||||
|
||||
throw new ClientPolicyException(ioe.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -184,18 +184,19 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
|||
public void updateRealmModelFromRepresentation(RealmModel realm, RealmRepresentation rep) {
|
||||
logger.tracev("LOAD PROFILE POLICIES ON IMPORTED REALM :: realm = {0}", realm.getName());
|
||||
|
||||
if (rep.getClientProfiles() != null) {
|
||||
if (rep.getParsedClientProfiles() != null) {
|
||||
try {
|
||||
updateClientProfiles(realm, rep.getClientProfiles());
|
||||
updateClientProfiles(realm, rep.getParsedClientProfiles());
|
||||
} catch (ClientPolicyException e) {
|
||||
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
|
||||
throw new RuntimeException("Failed to update client profiles", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (rep.getClientPolicies() != null) {
|
||||
ClientPoliciesRepresentation clientPolicies = rep.getParsedClientPolicies();
|
||||
if (clientPolicies != null) {
|
||||
try {
|
||||
updateClientPolicies(realm, rep.getClientPolicies());
|
||||
updateClientPolicies(realm, clientPolicies);
|
||||
} catch (ClientPolicyException e) {
|
||||
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
|
||||
throw new RuntimeException("Failed to update client policies", e);
|
||||
|
@ -280,10 +281,10 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
|||
try {
|
||||
// client profiles that filter out global profiles..
|
||||
ClientProfilesRepresentation filteredOutProfiles = getClientProfiles(realm, false);
|
||||
rep.setClientProfiles(filteredOutProfiles);
|
||||
rep.setParsedClientProfiles(filteredOutProfiles);
|
||||
|
||||
ClientPoliciesRepresentation filteredOutPolicies = getClientPolicies(realm);
|
||||
rep.setClientPolicies(filteredOutPolicies);
|
||||
rep.setParsedClientPolicies(filteredOutPolicies);
|
||||
} catch (ClientPolicyException cpe) {
|
||||
throw new IllegalStateException("Exception during export client profiles or client policies", cpe);
|
||||
}
|
||||
|
|
|
@ -428,15 +428,15 @@ public class ClientPoliciesLoadUpdateTest extends AbstractClientPoliciesTest {
|
|||
// Get the realm and assert that expected policies and profiles are present
|
||||
RealmResource testRealm = realmsResouce().realm("test");
|
||||
RealmRepresentation realmRep = testRealm.toRepresentation();
|
||||
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getClientPolicies());
|
||||
assertExpectedProfiles(realmRep.getParsedClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getParsedClientPolicies());
|
||||
|
||||
// Update the realm
|
||||
testRealm.update(realmRep);
|
||||
|
||||
// Test the realm again
|
||||
realmRep = testRealm.toRepresentation();
|
||||
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getClientPolicies());
|
||||
assertExpectedProfiles(realmRep.getParsedClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
||||
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getParsedClientPolicies());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2021 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.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.exportimport.util.ImportUtils;
|
||||
import org.keycloak.representations.idm.ClientPoliciesRepresentation;
|
||||
import org.keycloak.representations.idm.ClientProfilesRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.keycloak.testsuite.utils.io.IOUtil;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* This is test only for migration of client policies from Keycloak 13. As the format JSON format of client policies changed between Keycloak 13 and 14
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
@AuthServerContainerExclude(value = {AuthServerContainerExclude.AuthServer.REMOTE, AuthServerContainerExclude.AuthServer.QUARKUS}, details = "It works locally for Quarkus, but failing on CI for unknown reason")
|
||||
public class JsonFileImport1301MigrationClientPoliciesTest extends AbstractJsonFileImportMigrationTest {
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
Map<String, RealmRepresentation> reps = null;
|
||||
try {
|
||||
reps = ImportUtils.getRealmsFromStream(JsonSerialization.mapper, IOUtil.class.getResourceAsStream("/migration-test/migration-realm-13.0.1-client-policies.json"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
for (RealmRepresentation rep : reps.values()) {
|
||||
testRealms.add(rep);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void migration13_0_1_Test() throws Exception {
|
||||
RealmRepresentation testRealm = adminClient.realms().realm("test").toRepresentation();
|
||||
|
||||
// Stick to null for now. No support for proper migration from Keycloak 13 as client policies was preview and JSON format was changed significantly
|
||||
Assert.assertTrue(testRealm.getParsedClientProfiles().getProfiles().isEmpty());
|
||||
Assert.assertTrue(testRealm.getParsedClientPolicies().getPolicies().isEmpty());
|
||||
|
||||
ClientProfilesRepresentation clientProfiles = adminClient.realms().realm("test").clientPoliciesProfilesResource().getProfiles(false);
|
||||
Assert.assertTrue(clientProfiles.getProfiles().isEmpty());
|
||||
ClientPoliciesRepresentation clientPolicies = adminClient.realms().realm("test").clientPoliciesPoliciesResource().getPolicies();
|
||||
Assert.assertTrue(clientPolicies.getPolicies().isEmpty());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue