From da5db5a8133787b1ad655bb8f998e39b61d38da8 Mon Sep 17 00:00:00 2001 From: Andrea Peruffo Date: Tue, 29 Mar 2022 20:48:37 +0100 Subject: [PATCH] Fix NPEs during realm import (#10962) Closes #10961 --- .../models/utils/RepresentationToModel.java | 61 +++++++++++-------- .../exportimport/ExportImportTest.java | 6 ++ .../testrealm-authenticator-config-null.json | 25 ++++++++ 3 files changed, 68 insertions(+), 24 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/resources/import/testrealm-authenticator-config-null.json diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index e7859971f6..6106460773 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -729,44 +729,57 @@ public class RepresentationToModel { // assume this is an old version being imported DefaultAuthenticationFlows.migrateFlows(newRealm); } else { - for (AuthenticatorConfigRepresentation configRep : rep.getAuthenticatorConfig()) { - if (configRep.getAlias() == null) { - // this can happen only during import json files from keycloak 3.4.0 and older - throw new IllegalStateException("Provided realm contains authenticator config with null alias. " - + "It should be resolved by adding alias to the authenticator config before exporting the realm."); + if (rep.getAuthenticatorConfig() != null) { + for (AuthenticatorConfigRepresentation configRep : rep.getAuthenticatorConfig()) { + if (configRep.getAlias() == null) { + // this can happen only during import json files from keycloak 3.4.0 and older + throw new IllegalStateException("Provided realm contains authenticator config with null alias. " + + "It should be resolved by adding alias to the authenticator config before exporting the realm."); + } + AuthenticatorConfigModel model = toModel(configRep); + newRealm.addAuthenticatorConfig(model); } - AuthenticatorConfigModel model = toModel(configRep); - newRealm.addAuthenticatorConfig(model); } - for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { - AuthenticationFlowModel model = toModel(flowRep); - // make sure new id is generated for new AuthenticationFlowModel instance - String previousId = model.getId(); - model.setId(null); - model = newRealm.addAuthenticationFlow(model); - // store the mapped ids so that clients can reference the correct flow when importing the authenticationFlowBindingOverrides - mappedFlows.put(previousId, model.getId()); - } - for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { - AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias()); - for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) { - AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep); - newRealm.addAuthenticatorExecution(execution); + if (rep.getAuthenticationFlows() != null) { + for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { + AuthenticationFlowModel model = toModel(flowRep); + // make sure new id is generated for new AuthenticationFlowModel instance + String previousId = model.getId(); + model.setId(null); + model = newRealm.addAuthenticationFlow(model); + // store the mapped ids so that clients can reference the correct flow when importing the authenticationFlowBindingOverrides + mappedFlows.put(previousId, model.getId()); + } + for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) { + AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias()); + for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) { + AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep); + newRealm.addAuthenticatorExecution(execution); + } } } } if (rep.getBrowserFlow() == null) { - newRealm.setBrowserFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW)); + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW); + if (defaultFlow != null) { + newRealm.setBrowserFlow(defaultFlow); + } } else { newRealm.setBrowserFlow(newRealm.getFlowByAlias(rep.getBrowserFlow())); } if (rep.getRegistrationFlow() == null) { - newRealm.setRegistrationFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW)); + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.REGISTRATION_FLOW); + if (defaultFlow != null) { + newRealm.setRegistrationFlow(defaultFlow); + } } else { newRealm.setRegistrationFlow(newRealm.getFlowByAlias(rep.getRegistrationFlow())); } if (rep.getDirectGrantFlow() == null) { - newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW)); + AuthenticationFlowModel defaultFlow = newRealm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW); + if (defaultFlow != null) { + newRealm.setDirectGrantFlow(defaultFlow); + } } else { newRealm.setDirectGrantFlow(newRealm.getFlowByAlias(rep.getDirectGrantFlow())); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java index 91cee01d55..7a80edeb83 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java @@ -238,6 +238,12 @@ public class ExportImportTest extends AbstractKeycloakTest { addTestRealmToTestRealmReps("import-without-clients"); } + @Test + public void testImportWithNullAuthenticatorConfigAndNoDefaultBrowserFlow() { + importRealmFromFile("/import/testrealm-authenticator-config-null.json"); + Assert.assertTrue("Imported realm hasn't been found!", isRealmPresent("cez")); + } + @Test public void testImportIgnoreExistingMissingClientId() { TestingExportImportResource resource = testingClient.testing().exportImport(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/import/testrealm-authenticator-config-null.json b/testsuite/integration-arquillian/tests/base/src/test/resources/import/testrealm-authenticator-config-null.json new file mode 100644 index 0000000000..916ac2a228 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/import/testrealm-authenticator-config-null.json @@ -0,0 +1,25 @@ +{ + "authenticationFlows": [ + { + "alias": "browser", + "authenticationExecutions": [ + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "priority": 20, + "requirement": "ALTERNATIVE", + "userSetupAllowed": false + } + ], + "builtIn": true, + "description": "browser based authentification2", + "id": "3e6ccf87-5473-4eb0-8cbb-28f6b9e6f4d6", + "providerId": "basic-flow", + "topLevel": true + } + ], + "displayName": "CEZ", + "enabled": true, + "id": "cez", + "realm": "cez" +}