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;
|
package org.keycloak.representations.idm;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
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.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -33,6 +38,9 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class RealmRepresentation {
|
public class RealmRepresentation {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(RealmRepresentation.class);
|
||||||
|
|
||||||
protected String id;
|
protected String id;
|
||||||
protected String realm;
|
protected String realm;
|
||||||
protected String displayName;
|
protected String displayName;
|
||||||
|
@ -144,8 +152,11 @@ public class RealmRepresentation {
|
||||||
|
|
||||||
// Client Policies/Profiles
|
// Client Policies/Profiles
|
||||||
|
|
||||||
protected ClientProfilesRepresentation clientProfiles;
|
@JsonProperty("clientProfiles")
|
||||||
protected ClientPoliciesRepresentation clientPolicies;
|
protected JsonNode clientProfiles;
|
||||||
|
|
||||||
|
@JsonProperty("clientPolicies")
|
||||||
|
protected JsonNode clientPolicies;
|
||||||
|
|
||||||
protected List<UserRepresentation> users;
|
protected List<UserRepresentation> users;
|
||||||
protected List<UserRepresentation> federatedUsers;
|
protected List<UserRepresentation> federatedUsers;
|
||||||
|
@ -1181,20 +1192,44 @@ public class RealmRepresentation {
|
||||||
|
|
||||||
// Client Policies/Profiles
|
// Client Policies/Profiles
|
||||||
|
|
||||||
public ClientProfilesRepresentation getClientProfiles() {
|
@JsonIgnore
|
||||||
return clientProfiles;
|
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) {
|
@JsonIgnore
|
||||||
this.clientProfiles = clientProfiles;
|
public void setParsedClientProfiles(ClientProfilesRepresentation clientProfiles) {
|
||||||
|
if (clientProfiles == null) {
|
||||||
|
this.clientProfiles = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.clientProfiles = JsonSerialization.mapper.convertValue(clientProfiles, JsonNode.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientPoliciesRepresentation getClientPolicies() {
|
@JsonIgnore
|
||||||
return clientPolicies;
|
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) {
|
@JsonIgnore
|
||||||
this.clientPolicies = clientPolicies;
|
public void setParsedClientPolicies(ClientPoliciesRepresentation clientPolicies) {
|
||||||
|
if (clientPolicies == null) {
|
||||||
|
this.clientPolicies = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.clientPolicies = JsonSerialization.mapper.convertValue(clientPolicies, JsonNode.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBrowserFlow() {
|
public String getBrowserFlow() {
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
|
<module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
|
||||||
<module name="org.keycloak.keycloak-common"/>
|
<module name="org.keycloak.keycloak-common"/>
|
||||||
<module name="org.bouncycastle" />
|
<module name="org.bouncycastle" />
|
||||||
|
<module name="org.jboss.logging"/>
|
||||||
<module name="javax.api"/>
|
<module name="javax.api"/>
|
||||||
<module name="javax.activation.api"/>
|
<module name="javax.activation.api"/>
|
||||||
<module name="sun.jdk" optional="true" />
|
<module name="sun.jdk" optional="true" />
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.regex.Pattern;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.Version;
|
import org.keycloak.common.Version;
|
||||||
import org.keycloak.migration.migrators.MigrateTo12_0_0;
|
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_2_0;
|
||||||
import org.keycloak.migration.migrators.MigrateTo1_3_0;
|
import org.keycloak.migration.migrators.MigrateTo1_3_0;
|
||||||
import org.keycloak.migration.migrators.MigrateTo1_4_0;
|
import org.keycloak.migration.migrators.MigrateTo1_4_0;
|
||||||
|
@ -94,7 +95,8 @@ public class MigrationModelManager {
|
||||||
new MigrateTo8_0_2(),
|
new MigrateTo8_0_2(),
|
||||||
new MigrateTo9_0_0(),
|
new MigrateTo9_0_0(),
|
||||||
new MigrateTo9_0_4(),
|
new MigrateTo9_0_4(),
|
||||||
new MigrateTo12_0_0()
|
new MigrateTo12_0_0(),
|
||||||
|
new MigrateTo14_0_0()
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void migrate(KeycloakSession session) {
|
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 {
|
try {
|
||||||
return JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
|
return JsonSerialization.readValue(json, ClientProfilesRepresentation.class);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
|
||||||
throw new ClientPolicyException(ioe.getMessage());
|
throw new ClientPolicyException(ioe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,18 +184,19 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
||||||
public void updateRealmModelFromRepresentation(RealmModel realm, RealmRepresentation rep) {
|
public void updateRealmModelFromRepresentation(RealmModel realm, RealmRepresentation rep) {
|
||||||
logger.tracev("LOAD PROFILE POLICIES ON IMPORTED REALM :: realm = {0}", realm.getName());
|
logger.tracev("LOAD PROFILE POLICIES ON IMPORTED REALM :: realm = {0}", realm.getName());
|
||||||
|
|
||||||
if (rep.getClientProfiles() != null) {
|
if (rep.getParsedClientProfiles() != null) {
|
||||||
try {
|
try {
|
||||||
updateClientProfiles(realm, rep.getClientProfiles());
|
updateClientProfiles(realm, rep.getParsedClientProfiles());
|
||||||
} catch (ClientPolicyException e) {
|
} catch (ClientPolicyException e) {
|
||||||
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM PROFILES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
|
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);
|
throw new RuntimeException("Failed to update client profiles", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rep.getClientPolicies() != null) {
|
ClientPoliciesRepresentation clientPolicies = rep.getParsedClientPolicies();
|
||||||
|
if (clientPolicies != null) {
|
||||||
try {
|
try {
|
||||||
updateClientPolicies(realm, rep.getClientPolicies());
|
updateClientPolicies(realm, clientPolicies);
|
||||||
} catch (ClientPolicyException e) {
|
} catch (ClientPolicyException e) {
|
||||||
logger.warnv("VALIDATE SERIALIZE IMPORTED REALM POLICIES FAILED :: error = {0}, error detail = {1}", e.getError(), e.getErrorDetail());
|
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);
|
throw new RuntimeException("Failed to update client policies", e);
|
||||||
|
@ -280,10 +281,10 @@ public class DefaultClientPolicyManager implements ClientPolicyManager {
|
||||||
try {
|
try {
|
||||||
// client profiles that filter out global profiles..
|
// client profiles that filter out global profiles..
|
||||||
ClientProfilesRepresentation filteredOutProfiles = getClientProfiles(realm, false);
|
ClientProfilesRepresentation filteredOutProfiles = getClientProfiles(realm, false);
|
||||||
rep.setClientProfiles(filteredOutProfiles);
|
rep.setParsedClientProfiles(filteredOutProfiles);
|
||||||
|
|
||||||
ClientPoliciesRepresentation filteredOutPolicies = getClientPolicies(realm);
|
ClientPoliciesRepresentation filteredOutPolicies = getClientPolicies(realm);
|
||||||
rep.setClientPolicies(filteredOutPolicies);
|
rep.setParsedClientPolicies(filteredOutPolicies);
|
||||||
} catch (ClientPolicyException cpe) {
|
} catch (ClientPolicyException cpe) {
|
||||||
throw new IllegalStateException("Exception during export client profiles or client policies", 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
|
// Get the realm and assert that expected policies and profiles are present
|
||||||
RealmResource testRealm = realmsResouce().realm("test");
|
RealmResource testRealm = realmsResouce().realm("test");
|
||||||
RealmRepresentation realmRep = testRealm.toRepresentation();
|
RealmRepresentation realmRep = testRealm.toRepresentation();
|
||||||
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
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.getClientPolicies());
|
assertExpectedPolicies(Arrays.asList("new-policy", "lack-of-builtin-field-test-policy"), realmRep.getParsedClientPolicies());
|
||||||
|
|
||||||
// Update the realm
|
// Update the realm
|
||||||
testRealm.update(realmRep);
|
testRealm.update(realmRep);
|
||||||
|
|
||||||
// Test the realm again
|
// Test the realm again
|
||||||
realmRep = testRealm.toRepresentation();
|
realmRep = testRealm.toRepresentation();
|
||||||
assertExpectedProfiles(realmRep.getClientProfiles(), null, Arrays.asList("ordinal-test-profile", "lack-of-builtin-field-test-profile"));
|
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.getClientPolicies());
|
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