From 7c292b1213ec8cb0c5d1f0ce0d6c881a70b7ab4c Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Wed, 7 Sep 2016 08:52:55 +0200 Subject: [PATCH] KEYCLOAK-3342 Add Identity Provider authenticator --- .../idm/IdentityProviderRepresentation.java | 5 + .../keycloak/migration/MigrationModel.java | 5 - .../migration/MigrationModelManager.java | 101 ++++++----------- .../org/keycloak/migration/ModelVersion.java | 15 +++ ...onTo1_2_0_CR1.java => MigrateTo1_2_0.java} | 9 +- .../migration/migrators/MigrateTo1_3_0.java | 6 +- .../migration/migrators/MigrateTo1_4_0.java | 6 +- .../migration/migrators/MigrateTo1_5_0.java | 6 +- .../migration/migrators/MigrateTo1_6_0.java | 6 +- .../migration/migrators/MigrateTo1_7_0.java | 6 +- .../migration/migrators/MigrateTo1_8_0.java | 6 +- .../migration/migrators/MigrateTo1_9_0.java | 6 +- .../migration/migrators/MigrateTo1_9_2.java | 6 +- .../migration/migrators/MigrateTo2_0_0.java | 6 +- .../migration/migrators/MigrateTo2_1_0.java | 6 +- .../migration/migrators/MigrateTo2_2_0.java | 59 ++++++++++ .../migration/migrators/Migration.java | 32 ++++++ .../models/IdentityProviderModel.java | 2 + .../utils/DefaultAuthenticationFlows.java | 40 +++++++ .../models/utils/RepresentationToModel.java | 13 +++ .../IdentityProviderAuthenticator.java | 103 ++++++++++++++++++ .../IdentityProviderAuthenticatorFactory.java | 102 +++++++++++++++++ .../protocol/AuthorizationEndpointBase.java | 21 ---- .../oidc/endpoints/AuthorizationEndpoint.java | 16 --- ...ycloak.authentication.AuthenticatorFactory | 3 +- .../AbstractAuthenticationTest.java | 2 +- .../admin/authentication/ExecutionTest.java | 4 +- .../admin/authentication/FlowTest.java | 2 +- .../authentication/InitialFlowsTest.java | 4 +- .../admin/authentication/ProvidersTest.java | 1 + .../base/src/test/resources/log4j.properties | 2 + .../broker/IdentityProviderHintTest.java | 18 ++- .../src/test/resources/log4j.properties | 5 +- .../realm-identity-provider-oidc.html | 7 -- .../realm-identity-provider-saml.html | 7 -- .../realm-identity-provider-social.html | 7 -- 36 files changed, 489 insertions(+), 156 deletions(-) rename server-spi/src/main/java/org/keycloak/migration/migrators/{MigrationTo1_2_0_CR1.java => MigrateTo1_2_0.java} (94%) create mode 100644 server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java create mode 100644 server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java create mode 100644 services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java index 524cbb39ce..2466f12e4f 100755 --- a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java @@ -123,10 +123,15 @@ public class IdentityProviderRepresentation { this.updateProfileFirstLoginMode = updateProfileFirstLoginMode; } + /** + * @deprecated Replaced by configuration option in identity provider authenticator + */ + @Deprecated public boolean isAuthenticateByDefault() { return authenticateByDefault; } + @Deprecated public void setAuthenticateByDefault(boolean authenticateByDefault) { this.authenticateByDefault = authenticateByDefault; } diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java index c2e6dab0ec..a59508b4f4 100755 --- a/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java +++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModel.java @@ -23,11 +23,6 @@ package org.keycloak.migration; * @version $Revision: 1 $ */ public interface MigrationModel { - /** - * Must have the form of major.minor.micro as the version is parsed and numbers are compared - */ - String LATEST_VERSION = "2.1.0"; - String getStoredVersion(); void setStoredVersion(String version); } diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java index 6a2f448b58..e2b55e139e 100755 --- a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -18,6 +18,7 @@ package org.keycloak.migration; import org.jboss.logging.Logger; +import org.keycloak.migration.migrators.MigrateTo1_2_0; import org.keycloak.migration.migrators.MigrateTo1_3_0; import org.keycloak.migration.migrators.MigrateTo1_4_0; import org.keycloak.migration.migrators.MigrateTo1_5_0; @@ -28,7 +29,8 @@ import org.keycloak.migration.migrators.MigrateTo1_9_0; import org.keycloak.migration.migrators.MigrateTo1_9_2; import org.keycloak.migration.migrators.MigrateTo2_0_0; import org.keycloak.migration.migrators.MigrateTo2_1_0; -import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1; +import org.keycloak.migration.migrators.MigrateTo2_2_0; +import org.keycloak.migration.migrators.Migration; import org.keycloak.models.KeycloakSession; /** @@ -38,82 +40,41 @@ import org.keycloak.models.KeycloakSession; public class MigrationModelManager { private static Logger logger = Logger.getLogger(MigrationModelManager.class); + private static final Migration[] migrations = { + new MigrateTo1_2_0(), + new MigrateTo1_3_0(), + new MigrateTo1_4_0(), + new MigrateTo1_5_0(), + new MigrateTo1_6_0(), + new MigrateTo1_7_0(), + new MigrateTo1_8_0(), + new MigrateTo1_9_0(), + new MigrateTo1_9_2(), + new MigrateTo2_0_0(), + new MigrateTo2_1_0(), + new MigrateTo2_2_0(), + }; + public static void migrate(KeycloakSession session) { + ModelVersion latest = migrations[migrations.length-1].getVersion(); MigrationModel model = session.realms().getMigrationModel(); - String storedVersion = model.getStoredVersion(); - if (MigrationModel.LATEST_VERSION.equals(storedVersion)) return; ModelVersion stored = null; - if (storedVersion != null) { - stored = new ModelVersion(storedVersion); + if (model.getStoredVersion() != null) { + stored = new ModelVersion(model.getStoredVersion()); + if (latest.equals(stored)) { + return; + } } - if (stored == null || stored.lessThan(MigrationTo1_2_0_CR1.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.2.0.CR1 updates"); + for (Migration m : migrations) { + if (stored == null || stored.lessThan(m.getVersion())) { + if (stored != null) { + logger.debugf("Migrating older model to %s", m.getVersion()); + } + m.migrate(session); } - new MigrationTo1_2_0_CR1().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_3_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.3.0 updates"); - } - new MigrateTo1_3_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_4_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.4.0 updates"); - } - new MigrateTo1_4_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_5_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.5.0 updates"); - } - new MigrateTo1_5_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_6_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.6.0 updates"); - } - new MigrateTo1_6_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_7_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.7.0 updates"); - } - new MigrateTo1_7_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_8_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.8.0 updates"); - } - new MigrateTo1_8_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_9_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.9.0 updates"); - } - new MigrateTo1_9_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo1_9_2.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 1.9.2 updates"); - } - new MigrateTo1_9_2().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo2_0_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 2.0.0 updates"); - } - new MigrateTo2_0_0().migrate(session); - } - if (stored == null || stored.lessThan(MigrateTo2_1_0.VERSION)) { - if (stored != null) { - logger.debug("Migrating older model to 2.1.0 updates"); - } - new MigrateTo2_1_0().migrate(session); } - model.setStoredVersion(MigrationModel.LATEST_VERSION); + model.setStoredVersion(latest.toString()); } } diff --git a/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java b/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java index 383edd5e80..884587917e 100755 --- a/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java +++ b/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java @@ -98,4 +98,19 @@ public class ModelVersion { if (comp < 0) return true; return false; } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ModelVersion)) { + return false; + } + + ModelVersion v = (ModelVersion) obj; + return v.getMajor() == major && v.getMinor() == minor && v.getMicro() != micro; + } + + @Override + public String toString() { + return major + "." + minor + "." + micro; + } } diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java similarity index 94% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java rename to server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java index f2a86ce466..d363a10e53 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java @@ -32,8 +32,13 @@ import java.util.Map; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrationTo1_2_0_CR1 { - public static final ModelVersion VERSION = new ModelVersion("1.2.0.CR1"); +public class MigrateTo1_2_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("1.2.0"); + + @Override + public ModelVersion getVersion() { + return VERSION; + } public void setupBrokerService(RealmModel realm) { ClientModel client = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java index 8d573c7afa..ee337147aa 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java @@ -37,9 +37,13 @@ import javax.naming.directory.SearchControls; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_3_0 { +public class MigrateTo1_3_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("1.3.0"); + public ModelVersion getVersion() { + return VERSION; + } public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java index 5fbab26194..9f28f91918 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java @@ -34,9 +34,13 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_4_0 { +public class MigrateTo1_4_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.4.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java index 1a7f83e670..6969d5c30c 100755 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java @@ -32,9 +32,13 @@ import java.util.List; * @author Bill Burke * @version $Revision: 1 $ */ -public class MigrateTo1_5_0 { +public class MigrateTo1_5_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.5.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java index 17ddf1b37f..c3d922258d 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java @@ -27,10 +27,14 @@ import org.keycloak.models.utils.KeycloakModelUtils; /** * @author Marek Posolda */ -public class MigrateTo1_6_0 { +public class MigrateTo1_6_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.6.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { MigrationProvider provider = session.getProvider(MigrationProvider.class); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java index 47abe62a37..3d4a5d5c9f 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java @@ -31,10 +31,14 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows; /** * @author Marek Posolda */ -public class MigrateTo1_7_0 { +public class MigrateTo1_7_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.7.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java index 94e0243c12..549cde9a7c 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java @@ -30,10 +30,14 @@ import org.keycloak.models.utils.KeycloakModelUtils; /** * @author Marek Posolda */ -public class MigrateTo1_8_0 { +public class MigrateTo1_8_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.8.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { List realms = session.realms().getRealms(); for (RealmModel realm : realms) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java index fb24598afa..e91f1265b1 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java @@ -33,10 +33,14 @@ import java.util.Map; /** * @author Marek Posolda */ -public class MigrateTo1_9_0 { +public class MigrateTo1_9_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.9.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { RealmModel realm = session.realms().getRealm(Config.getAdminRealm()); if (realm != null && realm.getDisplayNameHtml() != null && realm.getDisplayNameHtml().equals("Keycloak")) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java index 2473937382..3a41058c81 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java @@ -25,10 +25,14 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -public class MigrateTo1_9_2 { +public class MigrateTo1_9_2 implements Migration { public static final ModelVersion VERSION = new ModelVersion("1.9.2"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { if (realm.getBrowserSecurityHeaders() != null) { diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java index 23368f3130..d36a8583c7 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java @@ -23,10 +23,14 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; -public class MigrateTo2_0_0 { +public class MigrateTo2_0_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("2.0.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { migrateAuthorizationServices(realm); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java index 9e7b93130d..995dafb3fb 100644 --- a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java @@ -38,9 +38,13 @@ import java.util.stream.Collectors; * * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. */ -public class MigrateTo2_1_0 { +public class MigrateTo2_1_0 implements Migration { public static final ModelVersion VERSION = new ModelVersion("2.1.0"); + public ModelVersion getVersion() { + return VERSION; + } + public void migrate(KeycloakSession session) { for (RealmModel realm : session.realms().getRealms()) { migrateDefaultRequiredAction(realm); diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java new file mode 100644 index 0000000000..1dbcba4200 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java @@ -0,0 +1,59 @@ +/* + * Copyright 2016 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.jboss.logging.Logger; +import org.keycloak.migration.ModelVersion; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.AuthenticatorConfigModel; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.utils.DefaultAuthenticationFlows; + +import java.util.HashMap; +import java.util.Map; + +public class MigrateTo2_2_0 implements Migration { + public static final ModelVersion VERSION = new ModelVersion("2.2.0"); + + private static final Logger LOG = Logger.getLogger(MigrateTo2_2_0.class); + + public ModelVersion getVersion() { + return VERSION; + } + + public void migrate(KeycloakSession session) { + for (RealmModel realm : session.realms().getRealms()) { + addIdentityProviderAuthenticator(realm); + } + } + + private void addIdentityProviderAuthenticator(RealmModel realm) { + String defaultProvider = null; + for (IdentityProviderModel provider : realm.getIdentityProviders()) { + if (provider.isEnabled() && provider.isAuthenticateByDefault()) { + defaultProvider = provider.getAlias(); + break; + } + } + + DefaultAuthenticationFlows.addIdentityProviderAuthenticator(realm, defaultProvider); + } + +} diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java b/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java new file mode 100644 index 0000000000..e37dad29f9 --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 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; + +/** + * @author Stian Thorgersen + */ +public interface Migration { + + void migrate(KeycloakSession session); + + ModelVersion getVersion(); + +} diff --git a/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java b/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java index 82e373f77f..2425c7d877 100755 --- a/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java +++ b/server-spi/src/main/java/org/keycloak/models/IdentityProviderModel.java @@ -120,10 +120,12 @@ public class IdentityProviderModel implements Serializable { this.storeToken = storeToken; } + @Deprecated public boolean isAuthenticateByDefault() { return authenticateByDefault; } + @Deprecated public void setAuthenticateByDefault(boolean authenticateByDefault) { this.authenticateByDefault = authenticateByDefault; } diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java index 7b2c61ba93..a738d05af7 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java @@ -276,6 +276,7 @@ public class DefaultAuthenticationFlows { execution.setAuthenticatorFlow(false); realm.addAuthenticatorExecution(execution); + addIdentityProviderAuthenticator(realm, null); AuthenticationFlowModel forms = new AuthenticationFlowModel(); forms.setTopLevel(false); @@ -317,6 +318,45 @@ public class DefaultAuthenticationFlows { realm.addAuthenticatorExecution(execution); } + public static void addIdentityProviderAuthenticator(RealmModel realm, String defaultProvider) { + String browserFlowId = null; + for (AuthenticationFlowModel f : realm.getAuthenticationFlows()) { + if (f.getAlias().equals(DefaultAuthenticationFlows.BROWSER_FLOW)) { + browserFlowId = f.getId(); + break; + } + } + + if (browserFlowId != null) { + for (AuthenticationExecutionModel e : realm.getAuthenticationExecutions(browserFlowId)) { + if ("identity-provider-redirector".equals(e.getAuthenticator())) { + return; + } + } + + AuthenticationExecutionModel execution; + execution = new AuthenticationExecutionModel(); + execution.setParentFlow(browserFlowId); + execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); + execution.setAuthenticator("identity-provider-redirector"); + execution.setPriority(25); + execution.setAuthenticatorFlow(false); + + if (defaultProvider != null) { + AuthenticatorConfigModel configModel = new AuthenticatorConfigModel(); + + Map config = new HashMap<>(); + config.put("defaultProvider", defaultProvider); + configModel.setConfig(config); + configModel = realm.addAuthenticatorConfig(configModel); + + execution.setAuthenticatorConfig(configModel.getId()); + } + + realm.addAuthenticatorExecution(execution); + } + } + public static void clientAuthFlow(RealmModel realm) { AuthenticationFlowModel clients = new AuthenticationFlowModel(); clients.setAlias(CLIENT_AUTHENTICATION_FLOW); diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index d1d5b059d7..04e856ba2c 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -556,6 +556,19 @@ public class RepresentationToModel { if (newRealm.getFlowByAlias(DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW) == null) { DefaultAuthenticationFlows.firstBrokerLoginFlow(newRealm, true); } + + // Added in 2.2 + String defaultProvider = null; + if (rep.getIdentityProviders() != null) { + for (IdentityProviderRepresentation i : rep.getIdentityProviders()) { + if (i.isEnabled() && i.isAuthenticateByDefault()) { + defaultProvider = i.getProviderId(); + break; + } + } + } + + DefaultAuthenticationFlows.addIdentityProviderAuthenticator(newRealm, defaultProvider); } private static void convertDeprecatedSocialProviders(RealmRepresentation rep) { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java new file mode 100644 index 0000000000..795d0c2d6e --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticator.java @@ -0,0 +1,103 @@ +/* + * Copyright 2016 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.authentication.authenticators.browser; + +import org.jboss.logging.Logger; +import org.keycloak.authentication.AuthenticationFlowContext; +import org.keycloak.authentication.Authenticator; +import org.keycloak.constants.AdapterConstants; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.Urls; +import org.keycloak.services.managers.ClientSessionCode; + +import javax.ws.rs.core.Response; +import java.util.List; + +/** + * @author Stian Thorgersen + */ +public class IdentityProviderAuthenticator implements Authenticator { + + private static final Logger LOG = Logger.getLogger(IdentityProviderAuthenticator.class); + + @Override + public void authenticate(AuthenticationFlowContext context) { + if (context.getUriInfo().getQueryParameters().containsKey(AdapterConstants.KC_IDP_HINT)) { + String providerId = context.getUriInfo().getQueryParameters().getFirst(AdapterConstants.KC_IDP_HINT); + if (providerId == null || providerId.equals("")) { + LOG.tracef("Skipping: kc_idp_hint query parameter is empty"); + context.attempted(); + } else { + LOG.tracef("Redirecting: %s set to %s", AdapterConstants.KC_IDP_HINT, providerId); + redirect(context, providerId); + } + } else if (context.getAuthenticatorConfig() != null && context.getAuthenticatorConfig().getConfig().containsKey(IdentityProviderAuthenticatorFactory.DEFAULT_PROVIDER)) { + String defaultProvider = context.getAuthenticatorConfig().getConfig().get(IdentityProviderAuthenticatorFactory.DEFAULT_PROVIDER); + LOG.tracef("Redirecting: default provider set to %s", defaultProvider); + redirect(context, defaultProvider); + } else { + LOG.tracef("No default provider set or %s query parameter provided", AdapterConstants.KC_IDP_HINT); + context.attempted(); + } + } + + private void redirect(AuthenticationFlowContext context, String providerId) { + List identityProviders = context.getRealm().getIdentityProviders(); + for (IdentityProviderModel identityProvider : identityProviders) { + if (identityProvider.isEnabled() && providerId.equals(identityProvider.getAlias())) { + String accessCode = new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode(); + Response response = Response.temporaryRedirect( + Urls.identityProviderAuthnRequest(context.getUriInfo().getBaseUri(), providerId, context.getRealm().getName(), accessCode)) + .build(); + + LOG.debugf("Redirecting to %s", providerId); + context.forceChallenge(response); + return; + } + } + + LOG.warnf("Provider not found or not enabled for realm %s", providerId); + context.attempted(); + } + + @Override + public void action(AuthenticationFlowContext context) { + } + + @Override + public boolean requiresUser() { + return false; + } + + @Override + public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) { + return true; + } + + @Override + public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) { + } + + @Override + public void close() { + } + +} diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java new file mode 100644 index 0000000000..635c95e3d7 --- /dev/null +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/IdentityProviderAuthenticatorFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 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.authentication.authenticators.browser; + +import org.keycloak.Config; +import org.keycloak.authentication.Authenticator; +import org.keycloak.authentication.AuthenticatorFactory; +import org.keycloak.models.AuthenticationExecutionModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderConfigProperty; + +import java.util.Collections; +import java.util.List; + +import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; + +/** + * @author Stian Thorgersen + */ +public class IdentityProviderAuthenticatorFactory implements AuthenticatorFactory { + + protected static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { + AuthenticationExecutionModel.Requirement.ALTERNATIVE, AuthenticationExecutionModel.Requirement.DISABLED + }; + + protected static final String DEFAULT_PROVIDER = "defaultProvider"; + + @Override + public String getDisplayType() { + return "Identity Provider Redirector"; + } + + @Override + public String getReferenceCategory() { + return null; + } + + @Override + public boolean isConfigurable() { + return true; + } + + @Override + public AuthenticationExecutionModel.Requirement[] getRequirementChoices() { + return REQUIREMENT_CHOICES; + } + + @Override + public boolean isUserSetupAllowed() { + return true; + } + + @Override + public String getHelpText() { + return "Redirects to default Identity Provider or Identity Provider specified with kc_idp_hint query parameter"; + } + + @Override + public List getConfigProperties() { + ProviderConfigProperty rep = new ProviderConfigProperty(DEFAULT_PROVIDER, "Default Identity Provider", "To automatically redirect to an identity provider set to the alias of the identity provider", STRING_TYPE, null); + return Collections.singletonList(rep); + } + + @Override + public Authenticator create(KeycloakSession session) { + return new IdentityProviderAuthenticator(); + } + + @Override + public void init(Config.Scope config) { + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + } + + @Override + public void close() { + } + + @Override + public String getId() { + return "identity-provider-redirector"; + } + +} diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java index bfbf7a1468..12dba27e2c 100755 --- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java +++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java @@ -17,8 +17,6 @@ package org.keycloak.protocol; -import java.util.List; - import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -31,14 +29,11 @@ import org.keycloak.events.Details; import org.keycloak.events.EventBuilder; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.protocol.LoginProtocol.Error; import org.keycloak.services.ServicesLogger; -import org.keycloak.services.Urls; import org.keycloak.services.managers.AuthenticationManager; -import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.resources.LoginActionsService; /** @@ -95,15 +90,6 @@ public abstract class AuthorizationEndpointBase { * @return response to be returned to the browser */ protected Response handleBrowserAuthenticationRequest(ClientSessionModel clientSession, LoginProtocol protocol, boolean isPassive, boolean redirectToAuthentication) { - - List identityProviders = realm.getIdentityProviders(); - for (IdentityProviderModel identityProvider : identityProviders) { - if (identityProvider.isEnabled() && identityProvider.isAuthenticateByDefault()) { - // TODO if we are isPassive we should propagate this flag to default identity provider also if possible - return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode()); - } - } - AuthenticationFlowModel flow = getAuthenticationFlow(); String flowId = flow.getId(); AuthenticationProcessor processor = createProcessor(clientSession, flowId, LoginActionsService.AUTHENTICATE_PATH); @@ -147,11 +133,4 @@ public abstract class AuthorizationEndpointBase { return realm.getBrowserFlow(); } - protected Response buildRedirectToIdentityProvider(String providerId, String accessCode) { - logger.debug("Automatically redirect to identity provider: " + providerId); - return Response.temporaryRedirect( - Urls.identityProviderAuthnRequest(this.uriInfo.getBaseUri(), providerId, this.realm.getName(), accessCode)) - .build(); - } - } \ No newline at end of file diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java index 1b4db2951a..3f18b27c3a 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java @@ -29,11 +29,9 @@ import org.keycloak.events.Details; import org.keycloak.events.Errors; import org.keycloak.events.EventBuilder; import org.keycloak.events.EventType; -import org.keycloak.forms.login.LoginFormsProvider; import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.ClientModel; import org.keycloak.models.ClientSessionModel; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.RealmModel; import org.keycloak.protocol.AuthorizationEndpointBase; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -46,7 +44,6 @@ import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.services.ErrorPageException; import org.keycloak.services.ServicesLogger; import org.keycloak.services.Urls; -import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.messages.Messages; import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.util.CacheControlUtil; @@ -313,19 +310,6 @@ public class AuthorizationEndpoint extends AuthorizationEndpointBase { } private Response buildAuthorizationCodeAuthorizationResponse() { - String idpHint = request.getIdpHint(); - - if (idpHint != null && !"".equals(idpHint)) { - IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint); - - if (identityProviderModel == null) { - return session.getProvider(LoginFormsProvider.class) - .setError(Messages.IDENTITY_PROVIDER_NOT_FOUND, idpHint) - .createErrorPage(); - } - return buildRedirectToIdentityProvider(idpHint, new ClientSessionCode(realm, clientSession).getCode()); - } - this.event.event(EventType.LOGIN); clientSession.setNote(Details.AUTH_TYPE, CODE_AUTH_TYPE); diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory index 97c92994fb..fa7ee28eea 100755 --- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory +++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory @@ -20,6 +20,7 @@ org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory org.keycloak.authentication.authenticators.browser.SpnegoAuthenticatorFactory +org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticatorFactory org.keycloak.authentication.authenticators.directgrant.ValidateOTP org.keycloak.authentication.authenticators.directgrant.ValidatePassword org.keycloak.authentication.authenticators.directgrant.ValidateUsername @@ -33,4 +34,4 @@ org.keycloak.authentication.authenticators.broker.IdpConfirmLinkAuthenticatorFac org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory org.keycloak.authentication.authenticators.broker.IdpUsernamePasswordFormFactory org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticatorFactory -org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator +org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java index f115c49326..c32ba08618 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/AbstractAuthenticationTest.java @@ -104,7 +104,7 @@ public abstract class AbstractAuthenticationTest extends AbstractKeycloakTest { } void compareExecution(AuthenticationExecutionExportRepresentation expected, AuthenticationExecutionExportRepresentation actual) { - Assert.assertEquals("Execution flowAlias - " + actual.getAuthenticator(), expected.getFlowAlias(), actual.getFlowAlias()); + Assert.assertEquals("Execution flowAlias - " + actual.getFlowAlias(), expected.getFlowAlias(), actual.getFlowAlias()); Assert.assertEquals("Execution authenticator - " + actual.getAuthenticator(), expected.getAuthenticator(), actual.getAuthenticator()); Assert.assertEquals("Execution userSetupAllowed - " + actual.getAuthenticator(), expected.isUserSetupAllowed(), actual.isUserSetupAllowed()); Assert.assertEquals("Execution authenticatorFlow - " + actual.getAuthenticator(), expected.isAutheticatorFlow(), actual.isAutheticatorFlow()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java index 42015edce0..d7caba46d5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ExecutionTest.java @@ -94,7 +94,7 @@ public class ExecutionTest extends AbstractAuthenticationTest { // we'll need auth-cookie later AuthenticationExecutionInfoRepresentation authCookieExec = findExecutionByProvider("auth-cookie", executionReps); - compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 3, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec); + compareExecution(newExecInfo("Review Profile", "idp-review-profile", true, 0, 4, DISABLED, null, new String[]{REQUIRED, DISABLED}), exec); // remove execution authMgmtResource.removeExecution(exec.getId()); @@ -164,7 +164,7 @@ public class ExecutionTest extends AbstractAuthenticationTest { // Note: there is no checking in addExecution if requirement is one of requirementChoices // Thus we can have OPTIONAL which is neither ALTERNATIVE, nor DISABLED - compareExecution(newExecInfo("Cookie", "auth-cookie", false, 0, 2, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec); + compareExecution(newExecInfo("Cookie", "auth-cookie", false, 0, 3, OPTIONAL, null, new String[]{ALTERNATIVE, DISABLED}), exec); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java index fc50bc6916..90f88745a9 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/FlowTest.java @@ -203,7 +203,7 @@ public class FlowTest extends AbstractAuthenticationTest { // adjust expected values before comparing browser.setAlias("Copy of browser"); browser.setBuiltIn(false); - browser.getAuthenticationExecutions().get(2).setFlowAlias("Copy of browser forms"); + browser.getAuthenticationExecutions().get(3).setFlowAlias("Copy of browser forms"); compareFlows(browser, copyOfBrowser); // get new flow directly and compare diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java index 3640af5288..ee79c27213 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/InitialFlowsTest.java @@ -125,12 +125,14 @@ public class InitialFlowsTest extends AbstractAuthenticationTest { AuthenticationFlowRepresentation flow = newFlow("browser", "browser based authentication", "basic-flow", true, true); addExecExport(flow, null, false, "auth-cookie", false, null, ALTERNATIVE, 10); addExecExport(flow, null, false, "auth-spnego", false, null, DISABLED, 20); + addExecExport(flow, null, false, "identity-provider-redirector", false, null, ALTERNATIVE, 25); addExecExport(flow, "forms", false, null, true, null, ALTERNATIVE, 30); List execs = new LinkedList<>(); addExecInfo(execs, "Cookie", "auth-cookie", false, 0, 0, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}); addExecInfo(execs, "Kerberos", "auth-spnego", false, 0, 1, DISABLED, null, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); - addExecInfo(execs, "forms", null, false, 0, 2, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); + addExecInfo(execs, "Identity Provider Redirector", "identity-provider-redirector", true, 0, 2, ALTERNATIVE, null, new String[]{ALTERNATIVE, DISABLED}); + addExecInfo(execs, "forms", null, false, 0, 3, ALTERNATIVE, true, new String[]{ALTERNATIVE, REQUIRED, DISABLED}); addExecInfo(execs, "Username Password Form", "auth-username-password-form", false, 1, 0, REQUIRED, null, new String[]{REQUIRED}); addExecInfo(execs, "OTP Form", "auth-otp-form", false, 1, 1, OPTIONAL, null, new String[]{REQUIRED, OPTIONAL, DISABLED}); expected.add(new FlowExecutions(flow, execs)); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java index 858e06ef45..4e07e6fbcc 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/authentication/ProvidersTest.java @@ -146,6 +146,7 @@ public class ProvidersTest extends AbstractAuthenticationTest { addProviderInfo(result, "direct-grant-validate-username", "Username Validation", "Validates the username supplied as a 'username' form parameter in direct grant request"); addProviderInfo(result, "http-basic-authenticator", "HTTP Basic Authentication", "Validates username and password from Authorization HTTP header"); + addProviderInfo(result, "identity-provider-redirector", "Identity Provider Redirector", "Redirects to default Identity Provider or Identity Provider specified with kc_idp_hint query parameter"); addProviderInfo(result, "idp-confirm-link", "Confirm link existing account", "Show the form where user confirms if he wants " + "to link identity provider with existing account or rather edit user profile data retrieved from identity provider to avoid conflict"); addProviderInfo(result, "idp-create-user-if-unique", "Create User If Unique", "Detect if there is existing Keycloak account " + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties index 04c79fecd3..ce33acfc86 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties @@ -61,3 +61,5 @@ log4j.logger.org.hibernate=off log4j.logger.org.jboss.resteasy=warn log4j.logger.org.apache.directory.api=warn log4j.logger.org.apache.directory.server.core=warn + +log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace \ No newline at end of file diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java index 7c1f47beb0..36c291f02b 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderHintTest.java @@ -20,19 +20,19 @@ package org.keycloak.testsuite.broker; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; +import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.AuthenticationExecutionInfoRepresentation; import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.KeycloakServer; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.OAuthGrantPage; import org.keycloak.testsuite.rule.AbstractKeycloakRule; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebRule; -import org.keycloak.testsuite.KeycloakServer; -import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** @@ -94,6 +94,16 @@ public class IdentityProviderHintTest { assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth")); - assertEquals("Could not find an identity provider with the identifier.", this.driver.findElement(By.className("instruction")).getText()); + System.out.println(driver.getPageSource()); + assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); + } + + private AuthenticationExecutionInfoRepresentation findExecution(RealmResource realm) { + for (AuthenticationExecutionInfoRepresentation e : realm.flows().getExecutions("browser")) { + if (e.getProviderId().equals("identity-provider-redirector")) { + return e; + } + } + return null; } } diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties index 5d5369c20b..f0ff6ac6e3 100755 --- a/testsuite/integration/src/test/resources/log4j.properties +++ b/testsuite/integration/src/test/resources/log4j.properties @@ -74,4 +74,7 @@ log4j.logger.org.apache.directory.server.core=warn log4j.logger.org.apache.directory.server.ldap.LdapProtocolHandler=error # Enable to view HttpClient connection pool activity -#log4j.logger.org.apache.http.impl.conn=debug \ No newline at end of file +#log4j.logger.org.apache.http.impl.conn=debug + +# Enable to view details from identity provider authenticator +# log4j.logger.org.keycloak.authentication.authenticators.browser.IdentityProviderAuthenticator=trace \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html index 81690be0aa..bbbf9b9ba5 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html @@ -34,13 +34,6 @@ {{:: 'identity-provider.enabled.tooltip' | translate}} -
- -
- -
- {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html index f9b8c740e9..eaf4439f03 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html @@ -31,13 +31,6 @@
{{:: 'identity-provider.enabled.tooltip' | translate}}
-
- -
- -
- {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html index 9599d98905..6c7ceb79e6 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html @@ -70,13 +70,6 @@
{{:: 'trust-email.tooltip' | translate}}
-
- -
- -
- {{:: 'identity-provider.authenticate-by-default.tooltip' | translate}} -