Run import within the context of the realm being imported
Closes #12289
This commit is contained in:
parent
74b2541d10
commit
ddcf0f45f9
3 changed files with 235 additions and 82 deletions
|
@ -507,101 +507,109 @@ public class RealmManager {
|
||||||
} else {
|
} else {
|
||||||
ReservedCharValidator.validate(id);
|
ReservedCharValidator.validate(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
RealmModel realm = model.createRealm(id, rep.getRealm());
|
RealmModel realm = model.createRealm(id, rep.getRealm());
|
||||||
ReservedCharValidator.validate(rep.getRealm());
|
RealmModel currentRealm = session.getContext().getRealm();
|
||||||
realm.setName(rep.getRealm());
|
|
||||||
|
|
||||||
// setup defaults
|
try {
|
||||||
|
session.getContext().setRealm(realm);
|
||||||
|
ReservedCharValidator.validate(rep.getRealm());
|
||||||
|
realm.setName(rep.getRealm());
|
||||||
|
|
||||||
setupRealmDefaults(realm);
|
// setup defaults
|
||||||
|
|
||||||
if (rep.getDefaultRole() == null) {
|
setupRealmDefaults(realm);
|
||||||
KeycloakModelUtils.setupDefaultRole(realm, determineDefaultRoleName(rep));
|
|
||||||
} else {
|
|
||||||
realm.setDefaultRole(RepresentationToModel.createRole(realm, rep.getDefaultRole()));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean postponeMasterClientSetup = postponeMasterClientSetup(rep);
|
if (rep.getDefaultRole() == null) {
|
||||||
if (!postponeMasterClientSetup) {
|
KeycloakModelUtils.setupDefaultRole(realm, determineDefaultRoleName(rep));
|
||||||
setupMasterAdminManagement(realm);
|
} else {
|
||||||
}
|
realm.setDefaultRole(RepresentationToModel.createRole(realm, rep.getDefaultRole()));
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasRealmAdminManagementClient(rep)) setupRealmAdminManagement(realm);
|
boolean postponeMasterClientSetup = postponeMasterClientSetup(rep);
|
||||||
if (!hasAccountManagementClient(rep)) setupAccountManagement(realm);
|
if (!postponeMasterClientSetup) {
|
||||||
|
setupMasterAdminManagement(realm);
|
||||||
|
}
|
||||||
|
|
||||||
boolean postponeImpersonationSetup = hasRealmAdminManagementClient(rep);
|
if (!hasRealmAdminManagementClient(rep)) setupRealmAdminManagement(realm);
|
||||||
if (!postponeImpersonationSetup) {
|
if (!hasAccountManagementClient(rep)) setupAccountManagement(realm);
|
||||||
setupImpersonationService(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasBrokerClient(rep)) setupBrokerService(realm);
|
boolean postponeImpersonationSetup = hasRealmAdminManagementClient(rep);
|
||||||
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
|
if (!postponeImpersonationSetup) {
|
||||||
|
setupImpersonationService(realm);
|
||||||
|
}
|
||||||
|
|
||||||
boolean postponeAdminCliSetup = false;
|
if (!hasBrokerClient(rep)) setupBrokerService(realm);
|
||||||
if (!hasAdminCliClient(rep)) {
|
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
|
||||||
postponeAdminCliSetup = hasRealmAdminManagementClient(rep);
|
|
||||||
|
boolean postponeAdminCliSetup = false;
|
||||||
if(!postponeAdminCliSetup) {
|
if (!hasAdminCliClient(rep)) {
|
||||||
|
postponeAdminCliSetup = hasRealmAdminManagementClient(rep);
|
||||||
|
|
||||||
|
if(!postponeAdminCliSetup) {
|
||||||
|
setupAdminCli(realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE) || !hasClientScope(rep, Constants.OFFLINE_ACCESS_ROLE)) {
|
||||||
|
setupOfflineTokens(realm, rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (rep.getClientScopes() == null) {
|
||||||
|
createDefaultClientScopes(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
RepresentationToModel.importRealm(session, rep, realm, skipUserDependent);
|
||||||
|
|
||||||
|
setupClientServiceAccountsAndAuthorizationOnImport(rep, skipUserDependent);
|
||||||
|
|
||||||
|
setupAdminConsoleLocaleMapper(realm);
|
||||||
|
|
||||||
|
if (postponeMasterClientSetup) {
|
||||||
|
setupMasterAdminManagement(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rep.getRoles() != null || hasRealmAdminManagementClient(rep)) {
|
||||||
|
// Assert all admin roles are available once import took place. This is needed due to import from previous version where JSON file may not contain all admin roles
|
||||||
|
checkMasterAdminManagementRoles(realm);
|
||||||
|
checkRealmAdminManagementRoles(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Could happen when migrating from older version and I have exported JSON file, which contains "realm-management" client but not "impersonation" client
|
||||||
|
// I need to postpone impersonation because it needs "realm-management" client and its roles set
|
||||||
|
if (postponeImpersonationSetup) {
|
||||||
|
setupImpersonationService(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postponeAdminCliSetup) {
|
||||||
setupAdminCli(realm);
|
setupAdminCli(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupAuthenticationFlows(realm);
|
||||||
|
setupRequiredActions(realm);
|
||||||
|
|
||||||
|
if (!hasRealmRole(rep, AccountRoles.DELETE_ACCOUNT)) {
|
||||||
|
KeycloakModelUtils.setupDeleteAccount(realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh periodic sync tasks for configured storageProviders
|
||||||
|
LegacyStoreSyncEvent.fire(session, realm, false);
|
||||||
|
|
||||||
|
setupAuthorizationServices(realm);
|
||||||
|
setupClientRegistrations(realm);
|
||||||
|
|
||||||
|
if (rep.getKeycloakVersion() != null) {
|
||||||
|
LegacyStoreMigrateRepresentationEvent.fire(session, realm, rep, skipUserDependent);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.clientPolicy().updateRealmModelFromRepresentation(realm, rep);
|
||||||
|
|
||||||
|
fireRealmPostCreate(realm);
|
||||||
|
} finally {
|
||||||
|
session.getContext().setRealm(currentRealm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE) || !hasClientScope(rep, Constants.OFFLINE_ACCESS_ROLE)) {
|
|
||||||
setupOfflineTokens(realm, rep);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (rep.getClientScopes() == null) {
|
|
||||||
createDefaultClientScopes(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
RepresentationToModel.importRealm(session, rep, realm, skipUserDependent);
|
|
||||||
|
|
||||||
setupClientServiceAccountsAndAuthorizationOnImport(rep, skipUserDependent);
|
|
||||||
|
|
||||||
setupAdminConsoleLocaleMapper(realm);
|
|
||||||
|
|
||||||
if (postponeMasterClientSetup) {
|
|
||||||
setupMasterAdminManagement(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rep.getRoles() != null || hasRealmAdminManagementClient(rep)) {
|
|
||||||
// Assert all admin roles are available once import took place. This is needed due to import from previous version where JSON file may not contain all admin roles
|
|
||||||
checkMasterAdminManagementRoles(realm);
|
|
||||||
checkRealmAdminManagementRoles(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Could happen when migrating from older version and I have exported JSON file, which contains "realm-management" client but not "impersonation" client
|
|
||||||
// I need to postpone impersonation because it needs "realm-management" client and its roles set
|
|
||||||
if (postponeImpersonationSetup) {
|
|
||||||
setupImpersonationService(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postponeAdminCliSetup) {
|
|
||||||
setupAdminCli(realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupAuthenticationFlows(realm);
|
|
||||||
setupRequiredActions(realm);
|
|
||||||
|
|
||||||
if (!hasRealmRole(rep, AccountRoles.DELETE_ACCOUNT)) {
|
|
||||||
KeycloakModelUtils.setupDeleteAccount(realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh periodic sync tasks for configured storageProviders
|
|
||||||
LegacyStoreSyncEvent.fire(session, realm, false);
|
|
||||||
|
|
||||||
setupAuthorizationServices(realm);
|
|
||||||
setupClientRegistrations(realm);
|
|
||||||
|
|
||||||
if (rep.getKeycloakVersion() != null) {
|
|
||||||
LegacyStoreMigrateRepresentationEvent.fire(session, realm, rep, skipUserDependent);
|
|
||||||
}
|
|
||||||
|
|
||||||
session.clientPolicy().updateRealmModelFromRepresentation(realm, rep);
|
|
||||||
|
|
||||||
fireRealmPostCreate(realm);
|
|
||||||
|
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.junit.runners.MethodSorters;
|
||||||
import org.keycloak.authorization.AuthorizationProvider;
|
import org.keycloak.authorization.AuthorizationProvider;
|
||||||
import org.keycloak.authorization.model.ResourceServer;
|
import org.keycloak.authorization.model.ResourceServer;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.exportimport.Strategy;
|
||||||
|
import org.keycloak.exportimport.util.ImportUtils;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -35,12 +37,22 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.ProfileAssume;
|
import org.keycloak.testsuite.ProfileAssume;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
import org.keycloak.testsuite.runonserver.RunOnServerException;
|
import org.keycloak.testsuite.runonserver.RunOnServerException;
|
||||||
|
import org.keycloak.userprofile.UserProfileProvider;
|
||||||
|
import org.keycloak.userprofile.config.UPAttribute;
|
||||||
|
import org.keycloak.userprofile.config.UPAttributeSelector;
|
||||||
|
import org.keycloak.userprofile.config.UPConfig;
|
||||||
|
import org.keycloak.userprofile.config.UPConfigUtils;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||||
|
|
||||||
|
@ -138,6 +150,38 @@ public class ImportTest extends AbstractTestRealmKeycloakTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||||
|
public void importUserProfile() throws Exception {
|
||||||
|
final String realmString = IOUtils.toString(getClass().getResourceAsStream("/model/import-userprofile.json"), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmRepresentation realmRep = JsonSerialization.readValue(realmString, RealmRepresentation.class);
|
||||||
|
|
||||||
|
// make sure the import happens within the context of the realm being imported
|
||||||
|
session.getContext().setRealm(null);
|
||||||
|
ImportUtils.importRealm(session, realmRep, Strategy.OVERWRITE_EXISTING, true);
|
||||||
|
|
||||||
|
RealmModel realm = session.realms().getRealmByName(realmRep.getRealm());
|
||||||
|
|
||||||
|
session.getContext().setRealm(realm);
|
||||||
|
|
||||||
|
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||||
|
UPConfig config = UPConfigUtils.readConfig(new ByteArrayInputStream(provider.getConfiguration().getBytes()));
|
||||||
|
|
||||||
|
Assert.assertTrue(config.getAttributes().stream().map(UPAttribute::getName).anyMatch("email"::equals));
|
||||||
|
Assert.assertTrue(config.getAttributes().stream().map(UPAttribute::getName).anyMatch("test"::equals));
|
||||||
|
Assert.assertTrue(config.getAttributes().stream().map(UPAttribute::getSelector)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.map(UPAttributeSelector::getScopes)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
.contains("microprofile-jwt")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealmParm) {
|
public void configureTestRealm(RealmRepresentation testRealmParm) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"realm": "user-profile",
|
||||||
|
"enabled": true,
|
||||||
|
"accessTokenLifespan": 3000,
|
||||||
|
"accessCodeLifespan": 10,
|
||||||
|
"accessCodeLifespanUserAction": 6000,
|
||||||
|
"sslRequired": "external",
|
||||||
|
"registrationAllowed": false,
|
||||||
|
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
||||||
|
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||||
|
"requiredCredentials": [ "password" ],
|
||||||
|
"users" : [
|
||||||
|
{
|
||||||
|
"username" : "bburke@redhat.com",
|
||||||
|
"enabled": true,
|
||||||
|
"email" : "bburke@redhat.com",
|
||||||
|
"firstName": "Bill",
|
||||||
|
"lastName": "Burke",
|
||||||
|
"credentials" : [
|
||||||
|
{ "type" : "password",
|
||||||
|
"value" : "password" }
|
||||||
|
],
|
||||||
|
"realmRoles": ["user"],
|
||||||
|
"applicationRoles": {
|
||||||
|
"account": [ "manage-account" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"roles" : {
|
||||||
|
"realm" : [
|
||||||
|
{
|
||||||
|
"name": "user",
|
||||||
|
"description": "User privileges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "admin",
|
||||||
|
"description": "Administrator privileges"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"scopeMappings": [
|
||||||
|
{
|
||||||
|
"client": "third-party",
|
||||||
|
"roles": ["user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client": "customer-portal",
|
||||||
|
"roles": ["user"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"client": "product-portal",
|
||||||
|
"roles": ["user"]
|
||||||
|
}
|
||||||
|
|
||||||
|
],
|
||||||
|
"applications": [
|
||||||
|
{
|
||||||
|
"name": "customer-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8080/customer-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8080/customer-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "product-portal",
|
||||||
|
"enabled": true,
|
||||||
|
"adminUrl": "http://localhost:8080/product-portal",
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8080/product-portal/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"oauthClients": [
|
||||||
|
{
|
||||||
|
"name": "third-party",
|
||||||
|
"enabled": true,
|
||||||
|
"redirectUris": [
|
||||||
|
"http://localhost:8080/oauth-client/*",
|
||||||
|
"http://localhost:8080/oauth-client-cdi/*"
|
||||||
|
],
|
||||||
|
"secret": "password"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"components": {
|
||||||
|
"org.keycloak.userprofile.UserProfileProvider" : [ {
|
||||||
|
"providerId" : "declarative-user-profile",
|
||||||
|
"subComponents" : { },
|
||||||
|
"config" : {
|
||||||
|
"config-pieces-count" : [ "1" ],
|
||||||
|
"config-piece-0" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{}}},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}}},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]},\"required\":{}},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"permissions\":{\"view\":[\"user\",\"admin\"],\"edit\":[\"user\",\"admin\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"selector\":{\"scopes\":[]}},{\"selector\":{\"scopes\":[\"microprofile-jwt\"]},\"permissions\":{\"view\":[],\"edit\":[]},\"name\":\"test\"}]}" ]
|
||||||
|
}
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
"attributes" : {
|
||||||
|
"userProfileEnabled" : "true"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue