parent
b2e11735ed
commit
6f6b5e8e84
18 changed files with 500 additions and 69 deletions
|
@ -75,7 +75,7 @@ public class ExportUtils {
|
|||
|
||||
public static RealmRepresentation exportRealm(KeycloakSession session, RealmModel realm, ExportOptions options, boolean internal) {
|
||||
RealmRepresentation rep = ModelToRepresentation.toRepresentation(session, realm, internal);
|
||||
ModelToRepresentation.exportAuthenticationFlows(realm, rep);
|
||||
ModelToRepresentation.exportAuthenticationFlows(session, realm, rep);
|
||||
ModelToRepresentation.exportRequiredActions(realm, rep);
|
||||
|
||||
// Project/product version
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.exportimport.ExportAdapter;
|
||||
import org.keycloak.exportimport.ExportOptions;
|
||||
import org.keycloak.exportimport.util.ExportUtils;
|
||||
|
@ -307,7 +308,7 @@ public class LegacyExportImportManager implements ExportImportManager {
|
|||
|
||||
updateParSettings(rep, newRealm);
|
||||
|
||||
Map<String, String> mappedFlows = importAuthenticationFlows(newRealm, rep);
|
||||
Map<String, String> mappedFlows = importAuthenticationFlows(session, newRealm, rep);
|
||||
if (rep.getRequiredActions() != null) {
|
||||
for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
|
||||
RequiredActionProviderModel model = toModel(action);
|
||||
|
@ -1257,7 +1258,7 @@ public class LegacyExportImportManager implements ExportImportManager {
|
|||
|
||||
return webAuthnPolicy;
|
||||
}
|
||||
public static Map<String, String> importAuthenticationFlows(RealmModel newRealm, RealmRepresentation rep) {
|
||||
public static Map<String, String> importAuthenticationFlows(KeycloakSession session, RealmModel newRealm, RealmRepresentation rep) {
|
||||
Map<String, String> mappedFlows = new HashMap<>();
|
||||
if (rep.getAuthenticationFlows() == null) {
|
||||
// assume this is an old version being imported
|
||||
|
@ -1285,7 +1286,7 @@ public class LegacyExportImportManager implements ExportImportManager {
|
|||
for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) {
|
||||
AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias());
|
||||
for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) {
|
||||
AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep);
|
||||
AuthenticationExecutionModel execution = toModel(session, newRealm, model, exeRep);
|
||||
newRealm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
}
|
||||
|
@ -1371,10 +1372,10 @@ public class LegacyExportImportManager implements ExportImportManager {
|
|||
return mappedFlows;
|
||||
}
|
||||
|
||||
private static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) {
|
||||
private static AuthenticationExecutionModel toModel(KeycloakSession session, RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) {
|
||||
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
|
||||
if (rep.getAuthenticatorConfig() != null) {
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigByAlias(rep.getAuthenticatorConfig());
|
||||
AuthenticatorConfigModel config = new DeployedConfigurationsManager(session).getAuthenticatorConfigByAlias(realm, rep.getAuthenticatorConfig());
|
||||
model.setAuthenticatorConfig(config.getId());
|
||||
}
|
||||
model.setAuthenticator(rep.getAuthenticator());
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.Config;
|
|||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ClientProvider;
|
||||
import org.keycloak.models.ClientScopeProvider;
|
||||
|
@ -314,7 +315,7 @@ public class MapExportImportManager implements ExportImportManager {
|
|||
|
||||
updateParSettings(rep, newRealm);
|
||||
|
||||
Map<String, String> mappedFlows = importAuthenticationFlows(newRealm, rep);
|
||||
Map<String, String> mappedFlows = importAuthenticationFlows(session, newRealm, rep);
|
||||
if (rep.getRequiredActions() != null) {
|
||||
for (RequiredActionProviderRepresentation action : rep.getRequiredActions()) {
|
||||
RequiredActionProviderModel model = toModel(action);
|
||||
|
@ -1461,7 +1462,7 @@ public class MapExportImportManager implements ExportImportManager {
|
|||
|
||||
return webAuthnPolicy;
|
||||
}
|
||||
public static Map<String, String> importAuthenticationFlows(RealmModel newRealm, RealmRepresentation rep) {
|
||||
public static Map<String, String> importAuthenticationFlows(KeycloakSession session, RealmModel newRealm, RealmRepresentation rep) {
|
||||
Map<String, String> mappedFlows = new HashMap<>();
|
||||
if (rep.getAuthenticationFlows() == null) {
|
||||
// assume this is an old version being imported
|
||||
|
@ -1489,7 +1490,7 @@ public class MapExportImportManager implements ExportImportManager {
|
|||
for (AuthenticationFlowRepresentation flowRep : rep.getAuthenticationFlows()) {
|
||||
AuthenticationFlowModel model = newRealm.getFlowByAlias(flowRep.getAlias());
|
||||
for (AuthenticationExecutionExportRepresentation exeRep : flowRep.getAuthenticationExecutions()) {
|
||||
AuthenticationExecutionModel execution = toModel(newRealm, model, exeRep);
|
||||
AuthenticationExecutionModel execution = toModel(session, newRealm, model, exeRep);
|
||||
newRealm.addAuthenticatorExecution(execution);
|
||||
}
|
||||
}
|
||||
|
@ -1575,10 +1576,10 @@ public class MapExportImportManager implements ExportImportManager {
|
|||
return mappedFlows;
|
||||
}
|
||||
|
||||
private static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) {
|
||||
private static AuthenticationExecutionModel toModel(KeycloakSession session, RealmModel realm, AuthenticationFlowModel parentFlow, AuthenticationExecutionExportRepresentation rep) {
|
||||
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
|
||||
if (rep.getAuthenticatorConfig() != null) {
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigByAlias(rep.getAuthenticatorConfig());
|
||||
AuthenticatorConfigModel config = new DeployedConfigurationsManager(session).getAuthenticatorConfigByAlias(realm, rep.getAuthenticatorConfig());
|
||||
model.setAuthenticatorConfig(config.getId());
|
||||
}
|
||||
model.setAuthenticator(rep.getAuthenticator());
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
/**
|
||||
* Allows to CRUD for configurations (like Authenticator configs). Those are typically saved in the store (realm), but can be also
|
||||
* deployed and hence not saved in the DB
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DeployedConfigurationsManager {
|
||||
|
||||
private static final Logger log = Logger.getLogger(DeployedConfigurationsManager.class);
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
||||
public DeployedConfigurationsManager(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void registerDeployedAuthenticatorConfig(AuthenticatorConfigModel model) {
|
||||
log.debugf("Register deployed authenticator config: %s", model.getId());
|
||||
session.getProvider(DeployedConfigurationsProvider.class).registerDeployedAuthenticatorConfig(model);
|
||||
}
|
||||
|
||||
public AuthenticatorConfigModel getDeployedAuthenticatorConfig(String configId) {
|
||||
return session.getProvider(DeployedConfigurationsProvider.class).getDeployedAuthenticatorConfigs()
|
||||
.filter(config -> configId.equals(config.getId()))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public AuthenticatorConfigModel getAuthenticatorConfig(RealmModel realm, String configId) {
|
||||
AuthenticatorConfigModel cfgModel = getDeployedAuthenticatorConfig(configId);
|
||||
if (cfgModel != null) {
|
||||
log.tracef("Found deployed configuration by id: %s", configId);
|
||||
return cfgModel;
|
||||
} else {
|
||||
return realm.getAuthenticatorConfigById(configId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AuthenticatorConfigModel getAuthenticatorConfigByAlias(RealmModel realm, String alias) {
|
||||
if (alias == null) return null;
|
||||
AuthenticatorConfigModel cfgModel = session.getProvider(DeployedConfigurationsProvider.class).getDeployedAuthenticatorConfigs()
|
||||
.filter(config -> alias.equals(config.getAlias()))
|
||||
.findFirst().orElse(null);
|
||||
if (cfgModel != null) {
|
||||
log.debugf("Found deployed configuration by alias: %s", alias);
|
||||
return cfgModel;
|
||||
} else {
|
||||
return realm.getAuthenticatorConfigByAlias(alias);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
/**
|
||||
* Allows to register "deployed configurations", which are retrieved in runtime from deployed providers and hence are not saved in the DB
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface DeployedConfigurationsProvider extends Provider {
|
||||
|
||||
void registerDeployedAuthenticatorConfig(AuthenticatorConfigModel model);
|
||||
|
||||
Stream<AuthenticatorConfigModel> getDeployedAuthenticatorConfigs();
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public interface DeployedConfigurationsProviderFactory extends ProviderFactory<DeployedConfigurationsProvider> {
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DeployedConfigurationsSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "deployed-configurations";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return DeployedConfigurationsProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return DeployedConfigurationsProviderFactory.class;
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ import org.keycloak.common.util.Time;
|
|||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialMetadata;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.admin.AdminEvent;
|
||||
import org.keycloak.events.admin.AuthDetails;
|
||||
|
@ -547,7 +548,7 @@ public class ModelToRepresentation {
|
|||
rep.setSupportedLocales(realm.getSupportedLocalesStream().collect(Collectors.toSet()));
|
||||
rep.setDefaultLocale(realm.getDefaultLocale());
|
||||
if (internal) {
|
||||
exportAuthenticationFlows(realm, rep);
|
||||
exportAuthenticationFlows(session, realm, rep);
|
||||
exportRequiredActions(realm, rep);
|
||||
exportGroups(realm, rep);
|
||||
}
|
||||
|
@ -586,10 +587,10 @@ public class ModelToRepresentation {
|
|||
rep.setGroups(toGroupHierarchy(realm, true).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static void exportAuthenticationFlows(RealmModel realm, RealmRepresentation rep) {
|
||||
public static void exportAuthenticationFlows(KeycloakSession session, RealmModel realm, RealmRepresentation rep) {
|
||||
List<AuthenticationFlowRepresentation> authenticationFlows = realm.getAuthenticationFlowsStream()
|
||||
.sorted(AuthenticationFlowModel.AuthenticationFlowComparator.SINGLETON)
|
||||
.map(flow -> toRepresentation(realm, flow))
|
||||
.map(flow -> toRepresentation(session, realm, flow))
|
||||
.collect(Collectors.toList());
|
||||
rep.setAuthenticationFlows(authenticationFlows);
|
||||
|
||||
|
@ -882,7 +883,7 @@ public class ModelToRepresentation {
|
|||
return consentRep;
|
||||
}
|
||||
|
||||
public static AuthenticationFlowRepresentation toRepresentation(RealmModel realm, AuthenticationFlowModel model) {
|
||||
public static AuthenticationFlowRepresentation toRepresentation(KeycloakSession session, RealmModel realm, AuthenticationFlowModel model) {
|
||||
AuthenticationFlowRepresentation rep = new AuthenticationFlowRepresentation();
|
||||
rep.setId(model.getId());
|
||||
rep.setBuiltIn(model.isBuiltIn());
|
||||
|
@ -891,14 +892,14 @@ public class ModelToRepresentation {
|
|||
rep.setAlias(model.getAlias());
|
||||
rep.setDescription(model.getDescription());
|
||||
rep.setAuthenticationExecutions(realm.getAuthenticationExecutionsStream(model.getId())
|
||||
.map(e -> toRepresentation(realm, e)).collect(Collectors.toList()));
|
||||
.map(e -> toRepresentation(session, realm, e)).collect(Collectors.toList()));
|
||||
return rep;
|
||||
}
|
||||
|
||||
public static AuthenticationExecutionExportRepresentation toRepresentation(RealmModel realm, AuthenticationExecutionModel model) {
|
||||
public static AuthenticationExecutionExportRepresentation toRepresentation(KeycloakSession session, RealmModel realm, AuthenticationExecutionModel model) {
|
||||
AuthenticationExecutionExportRepresentation rep = new AuthenticationExecutionExportRepresentation();
|
||||
if (model.getAuthenticatorConfig() != null) {
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(model.getAuthenticatorConfig());
|
||||
AuthenticatorConfigModel config = new DeployedConfigurationsManager(session).getAuthenticatorConfig(realm, model.getAuthenticatorConfig());
|
||||
rep.setAuthenticatorConfig(config.getAlias());
|
||||
}
|
||||
rep.setAuthenticator(model.getAuthenticator());
|
||||
|
|
|
@ -57,6 +57,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
|
|||
import org.keycloak.common.util.UriUtils;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.credential.CredentialModel;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.migration.migrators.MigrationUtils;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
|
@ -926,7 +927,7 @@ public class RepresentationToModel {
|
|||
}
|
||||
|
||||
|
||||
public static AuthenticationExecutionModel toModel(RealmModel realm, AuthenticationExecutionRepresentation rep) {
|
||||
public static AuthenticationExecutionModel toModel(KeycloakSession session, RealmModel realm, AuthenticationExecutionRepresentation rep) {
|
||||
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
|
||||
model.setId(rep.getId());
|
||||
model.setFlowId(rep.getFlowId());
|
||||
|
@ -938,7 +939,7 @@ public class RepresentationToModel {
|
|||
model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement()));
|
||||
|
||||
if (rep.getAuthenticatorConfig() != null) {
|
||||
AuthenticatorConfigModel cfg = realm.getAuthenticatorConfigByAlias(rep.getAuthenticatorConfig());
|
||||
AuthenticatorConfigModel cfg = new DeployedConfigurationsManager(session).getAuthenticatorConfigByAlias(realm, rep.getAuthenticatorConfig());
|
||||
model.setAuthenticatorConfig(cfg.getId());
|
||||
}
|
||||
return model;
|
||||
|
|
|
@ -62,6 +62,7 @@ org.keycloak.authentication.otp.OTPApplicationSpi
|
|||
org.keycloak.authorization.policy.provider.PolicySpi
|
||||
org.keycloak.authorization.store.StoreFactorySpi
|
||||
org.keycloak.authorization.AuthorizationSpi
|
||||
org.keycloak.deployment.DeployedConfigurationsSpi
|
||||
org.keycloak.models.cache.authorization.CachedStoreFactorySpi
|
||||
org.keycloak.protocol.oidc.TokenExchangeSpi
|
||||
org.keycloak.protocol.oidc.TokenIntrospectionSpi
|
||||
|
|
|
@ -24,9 +24,14 @@ import org.keycloak.Config;
|
|||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.authentication.Authenticator;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.PostMigrationEvent;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.representations.provider.ScriptProviderMetadata;
|
||||
|
||||
/**
|
||||
|
@ -88,6 +93,13 @@ public final class DeployedScriptAuthenticatorFactory extends ScriptBasedAuthent
|
|||
configProperties = super.getConfigProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, session -> {
|
||||
new DeployedConfigurationsManager(session).registerDeployedAuthenticatorConfig(model);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DefaultDeployedConfigurationsProvider implements DeployedConfigurationsProvider {
|
||||
|
||||
private final Map<String, AuthenticatorConfigModel> deployedAuthenticatorConfigs;
|
||||
public DefaultDeployedConfigurationsProvider(Map<String, AuthenticatorConfigModel> deployedAuthenticatorConfigs) {
|
||||
this.deployedAuthenticatorConfigs = deployedAuthenticatorConfigs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDeployedAuthenticatorConfig(AuthenticatorConfigModel model) {
|
||||
deployedAuthenticatorConfigs.put(model.getId(), model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<AuthenticatorConfigModel> getDeployedAuthenticatorConfigs() {
|
||||
return deployedAuthenticatorConfigs.values().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2023 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.deployment;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class DefaultDeployedConfigurationsProviderFactory implements DeployedConfigurationsProviderFactory {
|
||||
|
||||
public static final String PROVIDER_ID = "default";
|
||||
private final Map<String, AuthenticatorConfigModel> deployedAuthenticatorConfigs = new ConcurrentHashMap<>();
|
||||
@Override
|
||||
public DeployedConfigurationsProvider create(KeycloakSession session) {
|
||||
return new DefaultDeployedConfigurationsProvider(deployedAuthenticatorConfigs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
}
|
|
@ -33,11 +33,13 @@ import org.keycloak.authentication.FormAction;
|
|||
import org.keycloak.authentication.FormAuthenticator;
|
||||
import org.keycloak.authentication.RequiredActionFactory;
|
||||
import org.keycloak.authentication.RequiredActionProvider;
|
||||
import org.keycloak.deployment.DeployedConfigurationsManager;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.AuthenticatorConfigModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
|
@ -205,7 +207,7 @@ public class AuthenticationManagementResource {
|
|||
|
||||
return realm.getAuthenticationFlowsStream()
|
||||
.filter(flow -> flow.isTopLevel() && !Objects.equals(flow.getAlias(), DefaultAuthenticationFlows.SAML_ECP_FLOW))
|
||||
.map(flow -> ModelToRepresentation.toRepresentation(realm, flow));
|
||||
.map(flow -> ModelToRepresentation.toRepresentation(session, realm, flow));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -264,7 +266,7 @@ public class AuthenticationManagementResource {
|
|||
if (flow == null) {
|
||||
throw new NotFoundException("Could not find flow with id");
|
||||
}
|
||||
return ModelToRepresentation.toRepresentation(realm, flow);
|
||||
return ModelToRepresentation.toRepresentation(session, realm, flow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,7 +377,7 @@ public class AuthenticationManagementResource {
|
|||
logger.debug("flow not found: " + flowAlias);
|
||||
return Response.status(NOT_FOUND).build();
|
||||
}
|
||||
AuthenticationFlowModel copy = copyFlow(realm, flow, newName);
|
||||
AuthenticationFlowModel copy = copyFlow(session, realm, flow, newName);
|
||||
|
||||
data.put("id", copy.getId());
|
||||
adminEvent.operation(OperationType.CREATE).resourcePath(session.getContext().getUri()).representation(data).success();
|
||||
|
@ -384,7 +386,7 @@ public class AuthenticationManagementResource {
|
|||
|
||||
}
|
||||
|
||||
public static AuthenticationFlowModel copyFlow(RealmModel realm, AuthenticationFlowModel flow, String newName) {
|
||||
public static AuthenticationFlowModel copyFlow(KeycloakSession session, RealmModel realm, AuthenticationFlowModel flow, String newName) {
|
||||
AuthenticationFlowModel copy = new AuthenticationFlowModel();
|
||||
copy.setAlias(newName);
|
||||
copy.setDescription(flow.getDescription());
|
||||
|
@ -392,11 +394,11 @@ public class AuthenticationManagementResource {
|
|||
copy.setBuiltIn(false);
|
||||
copy.setTopLevel(flow.isTopLevel());
|
||||
copy = realm.addAuthenticationFlow(copy);
|
||||
copy(realm, newName, flow, copy);
|
||||
copy(session, realm, newName, flow, copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static void copy(RealmModel realm, String newName, AuthenticationFlowModel from, AuthenticationFlowModel to) {
|
||||
public static void copy(KeycloakSession session, RealmModel realm, String newName, AuthenticationFlowModel from, AuthenticationFlowModel to) {
|
||||
realm.getAuthenticationExecutionsStream(from.getId()).forEachOrdered(execution -> {
|
||||
if (execution.isAuthenticatorFlow()) {
|
||||
AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
|
||||
|
@ -408,26 +410,32 @@ public class AuthenticationManagementResource {
|
|||
copy.setTopLevel(false);
|
||||
copy = realm.addAuthenticationFlow(copy);
|
||||
execution.setFlowId(copy.getId());
|
||||
copy(realm, newName, subFlow, copy);
|
||||
copy(session, realm, newName, subFlow, copy);
|
||||
}
|
||||
|
||||
if (execution.getAuthenticatorConfig() != null) {
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(execution.getAuthenticatorConfig());
|
||||
DeployedConfigurationsManager configManager = new DeployedConfigurationsManager(session);
|
||||
AuthenticatorConfigModel config = configManager.getAuthenticatorConfig(realm, execution.getAuthenticatorConfig());
|
||||
|
||||
if (config == null) {
|
||||
logger.debugf("Authentication execution with id [%s] not found", config.getId());
|
||||
throw new IllegalStateException("Authentication execution configuration not found");
|
||||
}
|
||||
|
||||
config.setId(null);
|
||||
if (configManager.getDeployedAuthenticatorConfig(execution.getAuthenticatorConfig()) != null) {
|
||||
// Shared configuration of deployed provider
|
||||
execution.setAuthenticatorConfig(config.getId());
|
||||
} else {
|
||||
config.setId(null);
|
||||
|
||||
if (config.getAlias() != null) {
|
||||
config.setAlias(newName + " " + config.getAlias());
|
||||
if (config.getAlias() != null) {
|
||||
config.setAlias(newName + " " + config.getAlias());
|
||||
}
|
||||
|
||||
AuthenticatorConfigModel newConfig = realm.addAuthenticatorConfig(config);
|
||||
|
||||
execution.setAuthenticatorConfig(newConfig.getId());
|
||||
}
|
||||
|
||||
AuthenticatorConfigModel newConfig = realm.addAuthenticatorConfig(config);
|
||||
|
||||
execution.setAuthenticatorConfig(newConfig.getId());
|
||||
}
|
||||
|
||||
execution.setId(null);
|
||||
|
@ -524,17 +532,7 @@ public class AuthenticationManagementResource {
|
|||
String provider = data.get("provider");
|
||||
|
||||
// make sure provider is one of the registered providers
|
||||
ProviderFactory f;
|
||||
if (parentFlow.getProviderId().equals(AuthenticationFlow.CLIENT_FLOW)) {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, provider);
|
||||
} else if (parentFlow.getProviderId().equals(AuthenticationFlow.FORM_FLOW)) {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, provider);
|
||||
} else {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, provider);
|
||||
}
|
||||
if (f == null) {
|
||||
throw new BadRequestException("No authentication provider found for id: " + provider);
|
||||
}
|
||||
ProviderFactory f = getProviderFactory( parentFlow, provider);
|
||||
|
||||
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
|
||||
execution.setParentFlow(parentFlow.getId());
|
||||
|
@ -551,18 +549,7 @@ public class AuthenticationManagementResource {
|
|||
|
||||
execution = realm.addAuthenticatorExecution(execution);
|
||||
|
||||
if (f instanceof ConfiguredProvider) {
|
||||
ConfiguredProvider internalProviderFactory = (ConfiguredProvider) f;
|
||||
AuthenticatorConfigModel config = internalProviderFactory.getConfig();
|
||||
|
||||
if (config != null) {
|
||||
// creates a default configuration if the factory defines one
|
||||
// useful for internal providers that already provide a built-in configuration
|
||||
AuthenticatorConfigRepresentation configRepresentation = ModelToRepresentation.toRepresentation(
|
||||
config);
|
||||
newExecutionConfig(execution.getId(), configRepresentation).close();
|
||||
}
|
||||
}
|
||||
checkConfigForDeployedProvider(f, execution);
|
||||
|
||||
data.put("id", execution.getId());
|
||||
adminEvent.operation(OperationType.CREATE).resource(ResourceType.AUTH_EXECUTION).resourcePath(session.getContext().getUri()).representation(data).success();
|
||||
|
@ -571,6 +558,38 @@ public class AuthenticationManagementResource {
|
|||
return Response.created(session.getContext().getUri().getBaseUriBuilder().path(session.getContext().getUri().getPath().replace(addExecutionPathSegment, "")).path("executions").path(execution.getId()).build()).build();
|
||||
}
|
||||
|
||||
private ProviderFactory getProviderFactory(AuthenticationFlowModel parentFlow, String provider) {
|
||||
ProviderFactory f = null;
|
||||
if (parentFlow.getProviderId().equals(AuthenticationFlow.CLIENT_FLOW)) {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(ClientAuthenticator.class, provider);
|
||||
} else if (parentFlow.getProviderId().equals(AuthenticationFlow.FORM_FLOW)) {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, provider);
|
||||
} else {
|
||||
f = session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, provider);
|
||||
}
|
||||
if (f == null) {
|
||||
throw new BadRequestException("No authentication provider found for id: " + provider);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
private void checkConfigForDeployedProvider(ProviderFactory f, AuthenticationExecutionModel execution) {
|
||||
if (f instanceof ConfiguredProvider) {
|
||||
ConfiguredProvider internalProviderFactory = (ConfiguredProvider) f;
|
||||
AuthenticatorConfigModel config = internalProviderFactory.getConfig();
|
||||
|
||||
if (config != null) {
|
||||
// use a default configuration if the factory defines one
|
||||
// Assumption is that this is registered in DeployedConfigurationsProvider
|
||||
// useful for internal providers that already provide a built-in configuration
|
||||
logger.tracef("Updating execution of provider '%s' with shared configuration.", execution.getAuthenticator());
|
||||
execution.setAuthenticatorConfig(config.getId());
|
||||
realm.updateAuthenticatorExecution(execution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authentication executions for a flow
|
||||
*
|
||||
|
@ -649,7 +668,7 @@ public class AuthenticationManagementResource {
|
|||
if (factory.isConfigurable()) {
|
||||
String authenticatorConfigId = execution.getAuthenticatorConfig();
|
||||
if(authenticatorConfigId != null) {
|
||||
AuthenticatorConfigModel authenticatorConfig = realm.getAuthenticatorConfigById(authenticatorConfigId);
|
||||
AuthenticatorConfigModel authenticatorConfig = new DeployedConfigurationsManager(session).getAuthenticatorConfig(realm, authenticatorConfigId);
|
||||
|
||||
if (authenticatorConfig != null) {
|
||||
rep.setAlias(authenticatorConfig.getAlias());
|
||||
|
@ -779,7 +798,7 @@ public class AuthenticationManagementResource {
|
|||
public Response addExecution(@Parameter(description = "JSON model describing authentication execution") AuthenticationExecutionRepresentation execution) {
|
||||
auth.realm().requireManageRealm();
|
||||
|
||||
AuthenticationExecutionModel model = RepresentationToModel.toModel(realm, execution);
|
||||
AuthenticationExecutionModel model = RepresentationToModel.toModel(session, realm, execution);
|
||||
AuthenticationFlowModel parentFlow = getParentFlow(model);
|
||||
if (parentFlow.isBuiltIn()) {
|
||||
throw new BadRequestException("It is illegal to add execution to a built in flow");
|
||||
|
@ -787,6 +806,11 @@ public class AuthenticationManagementResource {
|
|||
model.setPriority(getNextPriority(parentFlow));
|
||||
model = realm.addAuthenticatorExecution(model);
|
||||
|
||||
if (!execution.isAuthenticatorFlow()) {
|
||||
ProviderFactory f = getProviderFactory(parentFlow, execution.getAuthenticator());
|
||||
checkConfigForDeployedProvider(f, model);
|
||||
}
|
||||
|
||||
adminEvent.operation(OperationType.CREATE).resource(ResourceType.AUTH_EXECUTION).resourcePath(session.getContext().getUri(), model.getId()).representation(execution).success();
|
||||
return Response.created(session.getContext().getUri().getAbsolutePathBuilder().path(model.getId()).build()).build();
|
||||
}
|
||||
|
@ -976,7 +1000,7 @@ public class AuthenticationManagementResource {
|
|||
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@Parameter(description = "Execution id") @PathParam("executionId") String execution, @Parameter(description = "Configuration id") @PathParam("id") String id) {
|
||||
auth.realm().requireViewRealm();
|
||||
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
|
||||
AuthenticatorConfigModel config = new DeployedConfigurationsManager(session).getAuthenticatorConfig(realm, id);
|
||||
if (config == null) {
|
||||
throw new NotFoundException("Could not find authenticator config");
|
||||
|
||||
|
@ -1317,7 +1341,7 @@ public class AuthenticationManagementResource {
|
|||
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@Parameter(description = "Configuration id") @PathParam("id") String id) {
|
||||
auth.realm().requireViewRealm();
|
||||
|
||||
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
|
||||
AuthenticatorConfigModel config = new DeployedConfigurationsManager(session).getAuthenticatorConfig(realm, id);
|
||||
if (config == null) {
|
||||
throw new NotFoundException("Could not find authenticator config");
|
||||
|
||||
|
@ -1369,6 +1393,10 @@ public class AuthenticationManagementResource {
|
|||
auth.realm().requireManageRealm();
|
||||
|
||||
ReservedCharValidator.validate(rep.getAlias());
|
||||
if (new DeployedConfigurationsManager(session).getDeployedAuthenticatorConfig(id) != null) {
|
||||
throw new BadRequestException("Authenticator config is read-only");
|
||||
}
|
||||
|
||||
AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
|
||||
if (exists == null) {
|
||||
throw new NotFoundException("Could not find authenticator config");
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#
|
||||
# Copyright 2023 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.
|
||||
#
|
||||
#
|
||||
|
||||
org.keycloak.deployment.DefaultDeployedConfigurationsProviderFactory
|
|
@ -674,7 +674,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
|||
RealmModel realm = getRealmByName(realmName);
|
||||
AuthenticationFlowModel flow = realm.getClientAuthenticationFlow();
|
||||
if (flow == null) return null;
|
||||
return ModelToRepresentation.toRepresentation(realm, flow);
|
||||
return ModelToRepresentation.toRepresentation(session, realm, flow);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -684,7 +684,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
|
|||
RealmModel realm = getRealmByName(realmName);
|
||||
AuthenticationFlowModel flow = realm.getResetCredentialsFlow();
|
||||
if (flow == null) return null;
|
||||
return ModelToRepresentation.toRepresentation(realm, flow);
|
||||
return ModelToRepresentation.toRepresentation(session, realm, flow);
|
||||
}
|
||||
|
||||
@GET
|
||||
|
|
|
@ -21,7 +21,12 @@ import static org.keycloak.common.Profile.Feature.SCRIPTS;
|
|||
import static org.keycloak.testsuite.arquillian.DeploymentTargetModifier.AUTH_SERVER_CURRENT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
|
@ -34,18 +39,23 @@ import org.junit.Assert;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthenticationManagementResource;
|
||||
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.provider.ScriptProviderDescriptor;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.admin.authentication.AbstractAuthenticationTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.forms.AbstractFlowTest;
|
||||
|
@ -180,6 +190,53 @@ public class DeployedScriptAuthenticatorTest extends AbstractFlowTest {
|
|||
events.expectLogin().user(okayUser()).detail(Details.USERNAME, "user").assertEvent();
|
||||
}
|
||||
|
||||
// Issue 20005
|
||||
@Test
|
||||
public void testManyScriptAuthenticatorInstances() throws Exception {
|
||||
configureFlows();
|
||||
AuthenticationManagementResource authMgmtResource = adminClient.realm(TEST_REALM_NAME).flows();
|
||||
|
||||
// Endpoint used by admin console
|
||||
Map<String, String> scriptExecution = new HashMap<>();
|
||||
scriptExecution.put("provider", "script-authenticator-a.js");
|
||||
|
||||
// It should be possible to add another script-authenticator to the flow
|
||||
authMgmtResource.addExecution("scriptBrowser", scriptExecution);
|
||||
|
||||
List<AuthenticationExecutionInfoRepresentation> executions = authMgmtResource.getExecutions("scriptBrowser");
|
||||
List<AuthenticationExecutionInfoRepresentation> scriptExecutions = executions.stream()
|
||||
.filter(execution -> execution.getDisplayName().equals("My Authenticator"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Both executions refers to same config of deployed script provider
|
||||
Assert.assertEquals(2, scriptExecutions.size());
|
||||
for (AuthenticationExecutionInfoRepresentation execution : scriptExecutions) {
|
||||
Assert.assertEquals(execution.getAuthenticationConfig(), "script-authenticator-a.js");
|
||||
}
|
||||
|
||||
// Assert updating config should fail due it's read-only
|
||||
AuthenticatorConfigRepresentation configRep = authMgmtResource.getAuthenticatorConfig("script-authenticator-a.js");
|
||||
configRep.getConfig().put("scriptCode", "Something");
|
||||
try {
|
||||
authMgmtResource.updateAuthenticatorConfig("script-authenticator-a.js", configRep);
|
||||
Assert.fail("Update of configuration should have failed");
|
||||
} catch (BadRequestException bre) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
// Test copy flow is OK
|
||||
Map<String, String> newFlow = new HashMap<>();
|
||||
newFlow.put("newName", "Copy of script flow");
|
||||
Response resp = authMgmtResource.copy("scriptBrowser", newFlow);
|
||||
Assert.assertEquals(201, resp.getStatus());
|
||||
resp.close();
|
||||
AuthenticationFlowRepresentation copiedFlow = AbstractAuthenticationTest.findFlowByAlias("Copy of script flow", authMgmtResource.getFlows());
|
||||
|
||||
// Cleanup
|
||||
authMgmtResource.deleteFlow(copiedFlow.getId());
|
||||
authMgmtResource.removeExecution(scriptExecutions.get(1).getId());
|
||||
}
|
||||
|
||||
private UserRepresentation okayUser() {
|
||||
return adminClient.realm(TEST_REALM_NAME).users().search("user", true).get(0);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ import static org.keycloak.models.utils.DefaultAuthenticationFlows.REGISTRATION_
|
|||
import static org.keycloak.models.utils.DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW;
|
||||
|
||||
public class FlowUtil {
|
||||
private RealmModel realm;
|
||||
private final KeycloakSession session;
|
||||
private final RealmModel realm;
|
||||
private AuthenticationFlowModel currentFlow;
|
||||
private String flowAlias;
|
||||
private int maxPriority = 0;
|
||||
|
@ -42,7 +43,8 @@ public class FlowUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public FlowUtil(RealmModel realm) {
|
||||
private FlowUtil(KeycloakSession session, RealmModel realm) {
|
||||
this.session = session;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
|
@ -55,11 +57,11 @@ public class FlowUtil {
|
|||
}
|
||||
|
||||
public static FlowUtil inCurrentRealm(KeycloakSession session) {
|
||||
return new FlowUtil(session.getContext().getRealm());
|
||||
return new FlowUtil(session, session.getContext().getRealm());
|
||||
}
|
||||
|
||||
private FlowUtil newFlowUtil(AuthenticationFlowModel flowModel) {
|
||||
FlowUtil subflow = new FlowUtil(realm);
|
||||
FlowUtil subflow = new FlowUtil(session, realm);
|
||||
subflow.currentFlow = flowModel;
|
||||
return subflow;
|
||||
}
|
||||
|
@ -112,7 +114,7 @@ public class FlowUtil {
|
|||
realm.removeAuthenticationFlow(foundFlow);
|
||||
}
|
||||
|
||||
currentFlow = AuthenticationManagementResource.copyFlow(realm, existingBrowserFlow, newFlowAlias);
|
||||
currentFlow = AuthenticationManagementResource.copyFlow(session, realm, existingBrowserFlow, newFlowAlias);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue