keycloak-aplcache
Changes
forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java 13(+7 -6)
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 5(+3 -2)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java 397(+397 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java 653(+66 -587)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java 519(+519 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java 145(+145 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java 40(+40 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java 2(+1 -1)
Details
diff --git a/common/src/main/java/org/keycloak/common/util/ObjectUtil.java b/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
index 1ade852..bec8acf 100644
--- a/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
+++ b/common/src/main/java/org/keycloak/common/util/ObjectUtil.java
@@ -24,4 +24,8 @@ public class ObjectUtil {
return str1.equals(str2);
}
+
+ public static String capitalize(String str) {
+ return str.substring(0, 1).toUpperCase() + str.substring(1);
+ }
}
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
index 02923d9..f77eb38 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-confirm.ftl
@@ -12,8 +12,8 @@
<form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<div id="kc-form-buttons" class="${properties.kcFormGroupClass!}">
- <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
- <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="linkAccount">${msg("confirmLinkIdpContinue", idpAlias)}</button>
+ <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="updateProfile" value="updateProfile">${msg("confirmLinkIdpReviewProfile")}</button>
+ <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" id="linkAccount" value="linkAccount">${msg("confirmLinkIdpContinue", idpAlias)}</button>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
index ab3e83e..0ba0686 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-idp-link-email.ftl
@@ -5,10 +5,10 @@
<#elseif section = "header">
${msg("emailLinkIdpTitle", idpAlias)}
<#elseif section = "form">
- <p class="instruction">
+ <p id="instruction1" class="instruction">
${msg("emailLinkIdp1", idpAlias, brokerContext.username, realm.name)}
</p>
- <p class="instruction">
+ <p id="instruction2" class="instruction">
${msg("emailLinkIdp2")} <a href="${url.firstBrokerLoginUrl}">${msg("doClickHere")}</a> ${msg("emailLinkIdp3")}
</p>
</#if>
diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
index 3a04fbf..25b0166 100755
--- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
+++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
@@ -21,6 +21,7 @@ import javax.mail.internet.MimeMultipart;
import org.jboss.logging.Logger;
import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.common.util.ObjectUtil;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.email.freemarker.beans.EventBean;
@@ -89,7 +90,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
attributes.put("link", link);
attributes.put("linkExpiration", expirationInMinutes);
- String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+ String realmName = ObjectUtil.capitalize(realm.getName());
attributes.put("realmName", realmName);
send("passwordResetSubject", "password-reset.ftl", attributes);
@@ -102,12 +103,12 @@ public class FreeMarkerEmailProvider implements EmailProvider {
attributes.put("link", link);
attributes.put("linkExpiration", expirationInMinutes);
- String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+ String realmName = ObjectUtil.capitalize(realm.getName());
attributes.put("realmName", realmName);
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
String idpAlias = brokerContext.getIdpConfig().getAlias();
- idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+ idpAlias = ObjectUtil.capitalize(idpAlias);
attributes.put("identityProviderContext", brokerContext);
attributes.put("identityProviderAlias", idpAlias);
@@ -123,7 +124,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
attributes.put("link", link);
attributes.put("linkExpiration", expirationInMinutes);
- String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+ String realmName = ObjectUtil.capitalize(realm.getName());
attributes.put("realmName", realmName);
send("executeActionsSubject", "executeActions.ftl", attributes);
@@ -137,7 +138,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
attributes.put("link", link);
attributes.put("linkExpiration", expirationInMinutes);
- String realmName = realm.getName().substring(0, 1).toUpperCase() + realm.getName().substring(1);
+ String realmName = ObjectUtil.capitalize(realm.getName());
attributes.put("realmName", realmName);
send("emailVerificationSubject", "email-verification.ftl", attributes);
@@ -253,7 +254,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
private String toCamelCase(EventType event){
StringBuilder sb = new StringBuilder("event");
for(String s : event.name().toString().toLowerCase().split("_")){
- sb.append(s.substring(0,1).toUpperCase()).append(s.substring(1));
+ sb.append(ObjectUtil.capitalize(s));
}
return sb.toString();
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index ef8cc74..d1b4df9 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -22,6 +22,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
import org.keycloak.broker.provider.BrokeredIdentityContext;
+import org.keycloak.common.util.ObjectUtil;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.freemarker.BrowserSecurityHeaderSetup;
@@ -288,7 +289,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
case LOGIN_IDP_LINK_EMAIL:
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
String idpAlias = brokerContext.getIdpConfig().getAlias();
- idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+ idpAlias = ObjectUtil.capitalize(idpAlias);
attributes.put("brokerContext", brokerContext);
attributes.put("idpAlias", idpAlias);
@@ -470,7 +471,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
public Response createIdpLinkEmailPage() {
BrokeredIdentityContext brokerContext = (BrokeredIdentityContext) this.attributes.get(IDENTITY_PROVIDER_BROKER_CONTEXT);
String idpAlias = brokerContext.getIdpConfig().getAlias();
- idpAlias = idpAlias.substring(0, 1).toUpperCase() + idpAlias.substring(1);
+ idpAlias = ObjectUtil.capitalize(idpAlias);;
setMessage(MessageType.WARNING, Messages.LINK_IDP, idpAlias);
return createResponse(LoginFormsPages.LOGIN_IDP_LINK_EMAIL);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index a874f5e..98e960c 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -25,8 +25,10 @@ public class DefaultAuthenticationFlows {
public static final String CLIENT_AUTHENTICATION_FLOW = "clients";
public static final String FIRST_BROKER_LOGIN_FLOW = "first broker login";
+ public static final String FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW = "Handle Existing Account";
public static final String IDP_REVIEW_PROFILE_CONFIG_ALIAS = "review profile config";
+ public static final String IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS = "create unique user config";
public static void addFlows(RealmModel realm) {
if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm);
@@ -347,7 +349,7 @@ public class DefaultAuthenticationFlows {
AuthenticatorConfigModel createUserIfUniqueConfig = new AuthenticatorConfigModel();
- createUserIfUniqueConfig.setAlias("create unique user config");
+ createUserIfUniqueConfig.setAlias(IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
config = new HashMap<>();
config.put("require.password.update.after.registration", "false");
createUserIfUniqueConfig.setConfig(config);
@@ -366,7 +368,7 @@ public class DefaultAuthenticationFlows {
AuthenticationFlowModel linkExistingAccountFlow = new AuthenticationFlowModel();
linkExistingAccountFlow.setTopLevel(false);
linkExistingAccountFlow.setBuiltIn(true);
- linkExistingAccountFlow.setAlias("Handle Existing Account");
+ linkExistingAccountFlow.setAlias(FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW);
linkExistingAccountFlow.setDescription("Handle what to do if there is existing account with same email/username like authenticated identity provider");
linkExistingAccountFlow.setProviderId("basic-flow");
linkExistingAccountFlow = realm.addAuthenticationFlow(linkExistingAccountFlow);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java
new file mode 100644
index 0000000..7d780cd
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractFirstBrokerLoginTest.java
@@ -0,0 +1,397 @@
+package org.keycloak.testsuite.broker;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.mail.internet.MimeMessage;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticator;
+import org.keycloak.authentication.authenticators.broker.IdpCreateUserIfUniqueAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory;
+import org.keycloak.authentication.authenticators.broker.IdpReviewProfileAuthenticatorFactory;
+import org.keycloak.common.util.ObjectUtil;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticationFlowModel;
+import org.keycloak.models.AuthenticatorConfigModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
+import org.keycloak.testsuite.pages.IdpLinkEmailPage;
+import org.keycloak.testsuite.pages.LoginPasswordResetPage;
+import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
+import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+import org.openqa.selenium.WebElement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractFirstBrokerLoginTest extends AbstractIdentityProviderTest {
+
+ protected static final String APP_REALM_ID = "realm-with-broker";
+
+ @WebResource
+ protected LoginUpdateProfileEditUsernameAllowedPage updateProfileWithUsernamePage;
+
+ @WebResource
+ protected IdpConfirmLinkPage idpConfirmLinkPage;
+
+ @WebResource
+ protected IdpLinkEmailPage idpLinkEmailPage;
+
+ @WebResource
+ protected LoginPasswordUpdatePage passwordUpdatePage;
+
+
+
+ /**
+ * Tests that if updateProfile is off and CreateUserIfUnique authenticator mandatory, error page will be shown if user with same email already exists
+ */
+ @Test
+ public void testErrorPageWhenDuplicationNotAllowed_updateProfileOff() {
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.REQUIRED);
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+ }
+
+ }, APP_REALM_ID);
+
+ loginIDP("pedroigor");
+
+ WebElement element = this.driver.findElement(By.className("instruction"));
+
+ assertNotNull(element);
+
+ assertEquals("User with email psilva@redhat.com already exists. Please login to account management to link the account.", element.getText());
+
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+ }
+
+ }, APP_REALM_ID);
+ }
+
+
+ /**
+ * Tests that if updateProfile is on and CreateUserIfUnique authenticator mandatory, error page will be shown if user with same email already exists
+ */
+ @Test
+ public void testErrorPageWhenDuplicationNotAllowed_updateProfileOn() {
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.REQUIRED);
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_ON);
+ }
+
+ }, APP_REALM_ID);
+
+ loginIDP("test-user");
+
+ this.updateProfileWithUsernamePage.assertCurrent();
+ this.updateProfileWithUsernamePage.update("Test", "User", "test-user@redhat.com", "pedroigor");
+
+ WebElement element = this.driver.findElement(By.className("instruction"));
+
+ assertNotNull(element);
+
+ assertEquals("User with username pedroigor already exists. Please login to account management to link the account.", element.getText());
+
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_FLOW, IdpCreateUserIfUniqueAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+ }
+
+ }, APP_REALM_ID);
+ }
+
+
+ /**
+ * Test user registers with IdentityProvider and needs to update password when it's required by IdpCreateUserIfUniqueAuthenticator
+ */
+ @Test
+ public void testRegistrationWithPasswordUpdateRequired() {
+ // Require updatePassword after user registered with broker
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ AuthenticatorConfigModel authenticatorConfig = realmWithBroker.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
+ authenticatorConfig.getConfig().put(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "true");
+ realmWithBroker.updateAuthenticatorConfig(authenticatorConfig);
+
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_MISSING);
+ }
+
+ }, APP_REALM_ID);
+
+ loginIDP("pedroigor");
+ this.updateProfileWithUsernamePage.assertCurrent();
+ this.updateProfileWithUsernamePage.update("Test", "User", "some-user@redhat.com", "some-user");
+
+ // Need to update password now
+ this.passwordUpdatePage.assertCurrent();
+ this.passwordUpdatePage.changePassword("password1", "password1");
+
+
+ // assert authenticated
+ assertFederatedUser("some-user", "some-user@redhat.com", "pedroigor");
+
+
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ AuthenticatorConfigModel authenticatorConfig = realmWithBroker.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_CREATE_UNIQUE_USER_CONFIG_ALIAS);
+ authenticatorConfig.getConfig().put(IdpCreateUserIfUniqueAuthenticatorFactory.REQUIRE_PASSWORD_UPDATE_AFTER_REGISTRATION, "false");
+ realmWithBroker.updateAuthenticatorConfig(authenticatorConfig);
+ }
+
+ }, APP_REALM_ID);
+ }
+
+
+ /**
+ * Tests that duplication is detected, the confirmation page is displayed, user clicks on "Review profile" and goes back to updateProfile page and resolves duplication
+ * by create new user
+ */
+ @Test
+ public void testFixDuplicationsByReviewProfile() {
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ loginIDP("pedroigor");
+
+ // There is user with same email. Update profile to use different email
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickReviewProfile();
+
+ this.updateProfileWithUsernamePage.assertCurrent();
+ this.updateProfileWithUsernamePage.update("Test", "User", "testing-user@redhat.com", "pedroigor");
+
+ // There is user with same username. Update profile to use different username
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with username pedroigor already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickReviewProfile();
+
+ this.updateProfileWithUsernamePage.assertCurrent();
+ this.updateProfileWithUsernamePage.update("Test", "User", "testing-user@redhat.com", "testing-user");
+
+ assertFederatedUser("testing-user", "testing-user@redhat.com", "pedroigor");
+ }
+
+ /**
+ * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by email
+ */
+ @Test
+ public void testLinkAccountByEmailVerification() throws Exception {
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ loginIDP("pedroigor");
+
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickLinkAccount();
+
+ // Confirm linking account by email
+ this.idpLinkEmailPage.assertCurrent();
+ Assert.assertEquals("An email with instructions to link " + ObjectUtil.capitalize(getProviderId()) + " account pedroigor with your " + APP_REALM_ID + " account has been sent to you.", this.idpLinkEmailPage.getMessage());
+
+ Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+ String linkFromMail = getVerificationEmailLink(message);
+
+ driver.navigate().to(linkFromMail.trim());
+
+ // authenticated and redirected to app. User is linked with identity provider
+ assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+ }
+
+
+ /**
+ * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication (confirm password on login screen)
+ */
+ @Test
+ public void testLinkAccountByReauthenticationWithPassword() throws Exception {
+ // Remove smtp config. The reauthentication by username+password screen will be automatically used then
+ final Map<String, String> smtpConfig = new HashMap<>();
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+ smtpConfig.putAll(realmWithBroker.getSmtpConfig());
+ realmWithBroker.setSmtpConfig(Collections.<String, String>emptyMap());
+ }
+
+ }, APP_REALM_ID);
+
+
+ loginIDP("pedroigor");
+
+
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickLinkAccount();
+
+ // Login screen shown. Username is prefilled and disabled. Registration link and social buttons are not shown
+ Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+ Assert.assertEquals("pedroigor", this.loginPage.getUsername());
+ Assert.assertFalse(this.loginPage.isUsernameInputEnabled());
+
+ Assert.assertEquals("Authenticate as pedroigor to link your account with " + getProviderId(), this.loginPage.getSuccessMessage());
+
+ try {
+ this.loginPage.findSocialButton(getProviderId());
+ Assert.fail("Not expected to see social button with " + getProviderId());
+ } catch (NoSuchElementException expected) {
+ }
+
+ try {
+ this.loginPage.clickRegister();
+ Assert.fail("Not expected to see register link");
+ } catch (NoSuchElementException expected) {
+ }
+
+ // Use bad password first
+ this.loginPage.login("password1");
+ Assert.assertEquals("Invalid username or password.", this.loginPage.getError());
+
+ // Use correct password now
+ this.loginPage.login("password");
+
+ // authenticated and redirected to app. User is linked with identity provider
+ assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+
+
+ // Restore smtp config
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ realmWithBroker.setSmtpConfig(smtpConfig);
+ }
+
+ }, APP_REALM_ID);
+ }
+
+
+ /**
+ * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication (confirm password on login screen)
+ * and additionally he goes through "forget password"
+ */
+ @Test
+ public void testLinkAccountByReauthentication_forgetPassword() throws Exception {
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+ IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.DISABLED);
+
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+ }
+
+ }, APP_REALM_ID);
+
+ loginIDP("pedroigor");
+
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickLinkAccount();
+
+ // Click "Forget password" on login page. Email sent directly because username is known
+ Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+ this.loginPage.resetPassword();
+
+ Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+ Assert.assertEquals("You should receive an email shortly with further instructions.", this.loginPage.getSuccessMessage());
+
+ // Click on link from email
+ Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+ String linkFromMail = getVerificationEmailLink(message);
+
+ driver.navigate().to(linkFromMail.trim());
+
+ // Need to update password now
+ this.passwordUpdatePage.assertCurrent();
+ this.passwordUpdatePage.changePassword("password", "password");
+
+ // authenticated and redirected to app. User is linked with identity provider
+ assertFederatedUser("pedroigor", "psilva@redhat.com", "pedroigor");
+
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+ IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+
+ }
+
+ }, APP_REALM_ID);
+ }
+
+
+ protected void assertFederatedUser(String expectedUsername, String expectedEmail, String expectedFederatedUsername) {
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+ UserModel federatedUser = getFederatedUser();
+
+ assertNotNull(federatedUser);
+ assertEquals(expectedUsername, federatedUser.getUsername());
+ assertEquals(expectedEmail, federatedUser.getEmail());
+
+ RealmModel realmWithBroker = getRealm();
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realmWithBroker);
+ assertEquals(1, federatedIdentities.size());
+
+ FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+ assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+ assertEquals(expectedFederatedUsername, federatedIdentityModel.getUserName());
+ }
+
+
+ protected void setExecutionRequirement(RealmModel realmWithBroker, String flowAlias, String authenticatorProvider, AuthenticationExecutionModel.Requirement requirement) {
+ AuthenticationFlowModel flowModel = realmWithBroker.getFlowByAlias(flowAlias);
+ List<AuthenticationExecutionModel> authExecutions = realmWithBroker.getAuthenticationExecutions(flowModel.getId());
+ for (AuthenticationExecutionModel execution : authExecutions) {
+ if (execution.getAuthenticator().equals(authenticatorProvider)) {
+ execution.setRequirement(requirement);
+ realmWithBroker.updateAuthenticatorExecution(execution);
+ return;
+ }
+ }
+
+ throw new IllegalStateException("Execution not found for flow " + flowAlias + " and authenticator " + authenticatorProvider);
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index 8af43b2..5248c38 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -42,6 +42,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.Urls;
import org.keycloak.testsuite.MailUtil;
import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
import org.keycloak.testsuite.pages.AccountPasswordPage;
@@ -87,7 +88,7 @@ import static org.junit.Assert.fail;
*/
public abstract class AbstractIdentityProviderTest {
- private static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
+ protected static final URI BASE_URI = UriBuilder.fromUri("http://localhost:8081/auth").build();
@ClassRule
public static BrokerKeyCloakRule brokerServerRule = new BrokerKeyCloakRule();
@@ -99,10 +100,10 @@ public abstract class AbstractIdentityProviderTest {
protected WebDriver driver;
@WebResource
- private LoginPage loginPage;
+ protected LoginPage loginPage;
@WebResource
- private LoginUpdateProfilePage updateProfilePage;
+ protected LoginUpdateProfilePage updateProfilePage;
@WebResource
@@ -123,7 +124,7 @@ public abstract class AbstractIdentityProviderTest {
@WebResource
protected AccountFederatedIdentityPage accountFederatedIdentityPage;
- private KeycloakSession session;
+ protected KeycloakSession session;
@Before
public void onBefore() {
@@ -140,87 +141,19 @@ public abstract class AbstractIdentityProviderTest {
brokerServerRule.stopSession(this.session, true);
}
- @Test
- public void testSuccessfulAuthentication() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
-
- UserModel user = assertSuccessfulAuthentication(identityProviderModel, "test-user", "new@email.com", true);
- Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
- }
-
- @Test
- public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
-
- assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
- }
-
- @Test
- public void testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
-
- assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", "new@email.com", true);
- }
-
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
- assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
- }
-
- /**
- * Test that verify email action is performed if email is provided and email trust is not enabled for the provider
- *
- * @throws MessagingException
- * @throws IOException
- */
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled() throws IOException, MessagingException {
- getRealm().setVerifyEmail(true);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- try {
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
- identityProviderModel.setTrustEmail(false);
-
- UserModel federatedUser = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "test-user@localhost", false);
-
- // email is verified now
- assertFalse(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
- } finally {
- getRealm().setVerifyEmail(false);
- }
- }
-
- private UserModel assertSuccessfulAuthenticationWithEmailVerification(IdentityProviderModel identityProviderModel, String username, String expectedEmail,
- boolean isProfileUpdateExpected)
- throws IOException, MessagingException {
+ protected UserModel assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel, String username, String expectedEmail, boolean isProfileUpdateExpected) {
authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
- // verify email is sent
- Assert.assertTrue(verifyEmailPage.isCurrent());
-
- // read email to take verification link from
- Assert.assertEquals(1, greenMail.getReceivedMessages().length);
- MimeMessage message = greenMail.getReceivedMessages()[0];
- String verificationUrl = getVerificationEmailLink(message);
-
- driver.navigate().to(verificationUrl.trim());
-
// authenticated and redirected to app
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+ assertTrue("Bad current URL " + this.driver.getCurrentUrl() + " and page source: " + this.driver.getPageSource(),
+ this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
UserModel federatedUser = getFederatedUser();
assertNotNull(federatedUser);
+ assertNotNull(federatedUser.getCreatedTimestamp());
+ // test that timestamp is current with 10s tollerance
+ Assert.assertTrue((System.currentTimeMillis() - federatedUser.getCreatedTimestamp()) < 10000);
doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
@@ -245,166 +178,7 @@ public abstract class AbstractIdentityProviderTest {
return federatedUser;
}
- /**
- * Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later
- */
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
- getRealm().setVerifyEmail(true);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- try {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
- UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", null, false);
-
- assertTrue(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
- } finally {
- getRealm().setVerifyEmail(false);
- }
- }
-
- /**
- * Test for KEYCLOAK-1372 - verify email action is not performed if email is provided but email trust is enabled for the provider
- */
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled() {
- getRealm().setVerifyEmail(true);
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- try {
- identityProviderModel.setTrustEmail(true);
-
- UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
-
- assertFalse(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
-
- } finally {
- identityProviderModel.setTrustEmail(false);
- getRealm().setVerifyEmail(false);
- }
- }
-
- /**
- * Test for KEYCLOAK-1372 - verify email action is performed if email is provided and email trust is enabled for the provider, but email is changed on First login update profile page
- *
- * @throws MessagingException
- * @throws IOException
- */
- @Test
- public void testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin() throws IOException, MessagingException {
- getRealm().setVerifyEmail(true);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- try {
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
- identityProviderModel.setTrustEmail(true);
-
- UserModel user = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "new@email.com", true);
- Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
- } finally {
- identityProviderModel.setTrustEmail(false);
- getRealm().setVerifyEmail(false);
- }
- }
-
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
-
- getRealm().setRegistrationEmailAsUsername(true);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- try {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
- authenticateWithIdentityProvider(identityProviderModel, "test-user", false);
-
- // authenticated and redirected to app
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- // check correct user is created with email as username and bound to correct federated identity
- RealmModel realm = getRealm();
-
- UserModel federatedUser = session.users().getUserByUsername("test-user@localhost", realm);
-
- assertNotNull(federatedUser);
-
- assertEquals("test-user@localhost", federatedUser.getUsername());
-
- doAssertFederatedUser(federatedUser, identityProviderModel, "test-user@localhost", false);
-
- Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
- assertEquals(1, federatedIdentities.size());
-
- FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
- assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
-
- driver.navigate().to("http://localhost:8081/test-app/logout");
- driver.navigate().to("http://localhost:8081/test-app");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- } finally {
- getRealm().setRegistrationEmailAsUsername(false);
- }
- }
-
- @Test
- public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided() {
-
- getRealm().setRegistrationEmailAsUsername(true);
- brokerServerRule.stopSession(this.session, true);
- this.session = brokerServerRule.startSession();
-
- try {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
- authenticateWithIdentityProvider(identityProviderModel, "test-user-noemail", false);
-
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
- // check correct user is created with username from provider as email is not available
- RealmModel realm = getRealm();
- UserModel federatedUser = getFederatedUser();
- assertNotNull(federatedUser);
-
- doAssertFederatedUserNoEmail(federatedUser);
-
- Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
- assertEquals(1, federatedIdentities.size());
-
- FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
- assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
- revokeGrant();
-
- driver.navigate().to("http://localhost:8081/test-app/logout");
- driver.navigate().to("http://localhost:8081/test-app");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- } finally {
- getRealm().setRegistrationEmailAsUsername(false);
- }
- }
protected void doAssertFederatedUserNoEmail(UserModel federatedUser) {
assertEquals("kc-oidc-idp.test-user-noemail", federatedUser.getUsername());
@@ -413,316 +187,7 @@ public abstract class AbstractIdentityProviderTest {
assertEquals("User", federatedUser.getLastName());
}
- @Test
- public void testDisabled() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
- identityProviderModel.setEnabled(false);
-
- this.driver.navigate().to("http://localhost:8081/test-app/");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- try {
- this.driver.findElement(By.className(getProviderId()));
- fail("Provider [" + getProviderId() + "] not disabled.");
- } catch (NoSuchElementException nsee) {
-
- }
- }
-
- @Test
- public void testProviderOnLoginPage() {
- // Provider button is available on login page
- this.driver.navigate().to("http://localhost:8081/test-app/");
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
- loginPage.findSocialButton(getProviderId());
- }
-
- // TODO: Reenable and adjust to KEYCLOAK-1750 changed behaviour
- // @Test
- public void testUserAlreadyExistsWhenUpdatingProfile() {
- this.driver.navigate().to("http://localhost:8081/test-app/");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- // choose the identity provider
- this.loginPage.clickSocial(getProviderId());
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
-
- // log in to identity provider
- this.loginPage.login("test-user", "password");
-
- doAfterProviderAuthentication();
-
- this.updateProfilePage.assertCurrent();
- this.updateProfilePage.update("Test", "User", "psilva@redhat.com");
-
- WebElement element = this.driver.findElement(By.className("kc-feedback-text"));
-
- assertNotNull(element);
-
- assertEquals("Email already exists.", element.getText());
-
- this.updateProfilePage.assertCurrent();
- this.updateProfilePage.update("Test", "User", "test-user@redhat.com");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
- UserModel federatedUser = getFederatedUser();
-
- assertNotNull(federatedUser);
- }
-
- // TODO: Reenable and adjust to KEYCLOAK-1750 changed behaviour
- // @Test
- public void testUserAlreadyExistsWhenNotUpdatingProfile() {
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
-
- this.driver.navigate().to("http://localhost:8081/test-app/");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- // choose the identity provider
- this.loginPage.clickSocial(getProviderId());
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
-
- // log in to identity provider
- this.loginPage.login("pedroigor", "password");
-
- doAfterProviderAuthentication();
-
- WebElement element = this.driver.findElement(By.className("kc-feedback-text"));
-
- assertNotNull(element);
-
- assertEquals("User with email already exists. Please login to account management to link the account.", element.getText());
- }
-
- @Test
- public void testAccountManagementLinkIdentity() {
- // Login as pedroigor to account management
- accountFederatedIdentityPage.realm("realm-with-broker");
- accountFederatedIdentityPage.open();
- assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
- loginPage.login("pedroigor", "password");
- assertTrue(accountFederatedIdentityPage.isCurrent());
-
- // Link my "pedroigor" identity with "test-user" from brokered Keycloak
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
- accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getAlias());
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
- this.loginPage.login("test-user", "password");
- doAfterProviderAuthentication();
-
- // Assert identity linked in account management
- assertTrue(accountFederatedIdentityPage.isCurrent());
- assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
-
- // Revoke grant in account mgmt
- revokeGrant();
-
- // Logout from account management
- accountFederatedIdentityPage.logout();
- assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
-
- // Assert I am logged immediately to account management due to previously linked "test-user" identity
- loginPage.clickSocial(identityProviderModel.getAlias());
- doAfterProviderAuthentication();
- assertTrue(accountFederatedIdentityPage.isCurrent());
- assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
-
- // Unlink my "test-user"
- accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
- assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
-
- // Revoke grant in account mgmt
- revokeGrant();
-
- // Logout from account management
- System.out.println("*** logout from account management");
- accountFederatedIdentityPage.logout();
- assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- // Try to login. Previous link is not valid anymore, so now it should try to register new user
- this.loginPage.clickSocial(identityProviderModel.getAlias());
- this.loginPage.login("test-user", "password");
- doAfterProviderAuthentication();
- this.updateProfilePage.assertCurrent();
- }
-
- @Test(expected = NoSuchElementException.class)
- public void testIdentityProviderNotAllowed() {
- this.driver.navigate().to("http://localhost:8081/test-app/");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- driver.findElement(By.className("model-oidc-idp"));
- }
-
- protected void configureClientRetrieveToken(String clientId) {
- RealmModel realm = getRealm();
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
- ClientModel client = realm.getClientByClientId(clientId);
- if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
-
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- }
-
- protected void configureUserRetrieveToken(String username) {
- RealmModel realm = getRealm();
- UserModel user = session.users().getUserByUsername(username, realm);
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
- if (user != null && !user.hasRole(readTokenRole)) {
- user.grantRole(readTokenRole);
- }
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- }
-
- protected void unconfigureClientRetrieveToken(String clientId) {
- RealmModel realm = getRealm();
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
- ClientModel client = realm.getClientByClientId(clientId);
- if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
-
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- }
-
- protected void unconfigureUserRetrieveToken(String username) {
- RealmModel realm = getRealm();
- UserModel user = session.users().getUserByUsername(username, realm);
- RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
- if (user != null && user.hasRole(readTokenRole)) {
- user.deleteRoleMapping(readTokenRole);
- }
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- }
-
- @Test
- public void testTokenStorageAndRetrievalByApplication() {
- setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
- IdentityProviderModel identityProviderModel = getIdentityProviderModel();
-
- identityProviderModel.setStoreToken(true);
-
- authenticateWithIdentityProvider(identityProviderModel, "test-user", true);
-
- UserModel federatedUser = getFederatedUser();
- RealmModel realm = getRealm();
- Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
- assertFalse(federatedIdentities.isEmpty());
- assertEquals(1, federatedIdentities.size());
-
- FederatedIdentityModel identityModel = federatedIdentities.iterator().next();
-
- assertNotNull(identityModel.getToken());
-
- UserSessionStatus userSessionStatus = retrieveSessionStatus();
- String accessToken = userSessionStatus.getAccessTokenString();
- URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
- final String authHeader = "Bearer " + accessToken;
- ClientRequestFilter authFilter = new ClientRequestFilter() {
- @Override
- public void filter(ClientRequestContext requestContext) throws IOException {
- requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
- }
- };
- Client client = ClientBuilder.newBuilder().register(authFilter).build();
- WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
- Response response = tokenEndpoint.request().get();
- assertEquals(Status.OK.getStatusCode(), response.getStatus());
- assertNotNull(response.readEntity(String.class));
- revokeGrant();
-
-
- driver.navigate().to("http://localhost:8081/test-app/logout");
- String currentUrl = this.driver.getCurrentUrl();
- System.out.println("after logout currentUrl: " + currentUrl);
- assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
-
- unconfigureUserRetrieveToken(getProviderId() + ".test-user");
- loginIDP("test-user");
- //authenticateWithIdentityProvider(identityProviderModel, "test-user");
- assertEquals("http://localhost:8081/test-app", driver.getCurrentUrl());
-
- userSessionStatus = retrieveSessionStatus();
- accessToken = userSessionStatus.getAccessTokenString();
- final String authHeader2 = "Bearer " + accessToken;
- ClientRequestFilter authFilter2 = new ClientRequestFilter() {
- @Override
- public void filter(ClientRequestContext requestContext) throws IOException {
- requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader2);
- }
- };
- client = ClientBuilder.newBuilder().register(authFilter2).build();
- tokenEndpoint = client.target(tokenEndpointUrl);
- response = tokenEndpoint.request().get();
-
- assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
-
- revokeGrant();
- driver.navigate().to("http://localhost:8081/test-app/logout");
- driver.navigate().to("http://localhost:8081/test-app");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
- }
-
- protected abstract void doAssertTokenRetrieval(String pageSource);
-
- private UserModel assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel, String username, String expectedEmail, boolean isProfileUpdateExpected) {
- authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
-
- // authenticated and redirected to app
- assertTrue("Bad current URL " + this.driver.getCurrentUrl() + " and page source: " + this.driver.getPageSource(),
- this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
-
- UserModel federatedUser = getFederatedUser();
-
- assertNotNull(federatedUser);
- assertNotNull(federatedUser.getCreatedTimestamp());
- // test that timestamp is current with 10s tollerance
- Assert.assertTrue((System.currentTimeMillis() - federatedUser.getCreatedTimestamp()) < 10000);
-
- doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
-
- brokerServerRule.stopSession(session, true);
- session = brokerServerRule.startSession();
-
- RealmModel realm = getRealm();
-
- Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
-
- assertEquals(1, federatedIdentities.size());
-
- FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
-
- assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
- assertEquals(federatedUser.getUsername(), federatedIdentityModel.getIdentityProvider() + "." + federatedIdentityModel.getUserName());
-
- driver.navigate().to("http://localhost:8081/test-app/logout");
- driver.navigate().to("http://localhost:8081/test-app");
-
- assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
- return federatedUser;
- }
-
- private void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel, String username, boolean isProfileUpdateExpected) {
+ protected void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel, String username, boolean isProfileUpdateExpected) {
loginIDP(username);
@@ -738,7 +203,7 @@ public abstract class AbstractIdentityProviderTest {
}
- private void loginIDP(String username) {
+ protected void loginIDP(String username) {
driver.navigate().to("http://localhost:8081/test-app");
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
@@ -776,6 +241,7 @@ public abstract class AbstractIdentityProviderTest {
protected abstract String getProviderId();
+
protected IdentityProviderModel getIdentityProviderModel() {
IdentityProviderModel identityProviderModel = getRealm().getIdentityProviderByAlias(getProviderId());
@@ -786,10 +252,16 @@ public abstract class AbstractIdentityProviderTest {
return identityProviderModel;
}
- private RealmModel getRealm() {
- return this.session.realms().getRealm("realm-with-broker");
+
+ protected RealmModel getRealm() {
+ return getRealm(this.session);
+ }
+
+ protected RealmModel getRealm(KeycloakSession session) {
+ return session.realms().getRealm("realm-with-broker");
}
+
protected void doAssertFederatedUser(UserModel federatedUser, IdentityProviderModel identityProviderModel, String expectedEmail, boolean isProfileUpdateExpected) {
if (isProfileUpdateExpected) {
String userFirstName = "New first";
@@ -805,19 +277,6 @@ public abstract class AbstractIdentityProviderTest {
}
}
- private UserSessionStatus retrieveSessionStatus() {
- UserSessionStatus sessionStatus = null;
-
- try {
- String pageSource = this.driver.getPageSource();
-
- sessionStatus = JsonSerialization.readValue(pageSource.getBytes(), UserSessionStatus.class);
- } catch (IOException ignore) {
- ignore.printStackTrace();
- }
-
- return sessionStatus;
- }
private void removeTestUsers() {
RealmModel realm = getRealm();
@@ -835,40 +294,60 @@ public abstract class AbstractIdentityProviderTest {
}
}
}
-
- private String getVerificationEmailLink(MimeMessage message) throws IOException, MessagingException {
- Multipart multipart = (Multipart) message.getContent();
-
+
+
+ protected void setUpdateProfileFirstLogin(final String updateProfileFirstLogin) {
+ KeycloakModelUtils.runJobInTransaction(this.session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
+
+ @Override
+ public void run(KeycloakSession session) {
+ RealmModel realm = getRealm(session);
+ setUpdateProfileFirstLogin(realm, updateProfileFirstLogin);
+ }
+
+ });
+ }
+
+ protected void setUpdateProfileFirstLogin(RealmModel realm, String updateProfileFirstLogin) {
+ AuthenticatorConfigModel reviewProfileConfig = realm.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS);
+ reviewProfileConfig.getConfig().put(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN, updateProfileFirstLogin);
+ realm.updateAuthenticatorConfig(reviewProfileConfig);
+ }
+
+
+ protected UserSessionStatusServlet.UserSessionStatus retrieveSessionStatus() {
+ UserSessionStatusServlet.UserSessionStatus sessionStatus = null;
+
+ try {
+ String pageSource = this.driver.getPageSource();
+
+ sessionStatus = JsonSerialization.readValue(pageSource.getBytes(), UserSessionStatusServlet.UserSessionStatus.class);
+ } catch (IOException ignore) {
+ ignore.printStackTrace();
+ }
+
+ return sessionStatus;
+ }
+
+ protected String getVerificationEmailLink(MimeMessage message) throws IOException, MessagingException {
+ Multipart multipart = (Multipart) message.getContent();
+
final String textContentType = multipart.getBodyPart(0).getContentType();
-
+
assertEquals("text/plain; charset=UTF-8", textContentType);
-
+
final String textBody = (String) multipart.getBodyPart(0).getContent();
final String textVerificationUrl = MailUtil.getLink(textBody);
-
+
final String htmlContentType = multipart.getBodyPart(1).getContentType();
-
+
assertEquals("text/html; charset=UTF-8", htmlContentType);
-
+
final String htmlBody = (String) multipart.getBodyPart(1).getContent();
final String htmlVerificationUrl = MailUtil.getLink(htmlBody);
-
+
assertEquals(htmlVerificationUrl, textVerificationUrl);
return htmlVerificationUrl;
}
-
- private void setUpdateProfileFirstLogin(final String updateProfileFirstLogin) {
- KeycloakModelUtils.runJobInTransaction(this.session.getKeycloakSessionFactory(), new KeycloakSessionTask() {
-
- @Override
- public void run(KeycloakSession session) {
- RealmModel realm = session.realms().getRealm("realm-with-broker");
- AuthenticatorConfigModel reviewProfileConfig = realm.getAuthenticatorConfigByAlias(DefaultAuthenticationFlows.IDP_REVIEW_PROFILE_CONFIG_ALIAS);
- reviewProfileConfig.getConfig().put(IdpReviewProfileAuthenticatorFactory.UPDATE_PROFILE_ON_FIRST_LOGIN, updateProfileFirstLogin);
- realm.updateAuthenticatorConfig(reviewProfileConfig);
- }
-
- });
- }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
new file mode 100644
index 0000000..904caf6
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
@@ -0,0 +1,519 @@
+package org.keycloak.testsuite.broker;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Set;
+
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.internet.MimeMessage;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.Urls;
+import org.keycloak.testsuite.MailUtil;
+import org.keycloak.testsuite.broker.util.UserSessionStatusServlet;
+import org.openqa.selenium.By;
+import org.openqa.selenium.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author pedroigor
+ */
+public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdentityProviderTest {
+
+ @Test
+ public void testSuccessfulAuthentication() {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+
+ UserModel user = assertSuccessfulAuthentication(identityProviderModel, "test-user", "new@email.com", true);
+ Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
+ }
+
+ @Test
+ public void testSuccessfulAuthenticationUpdateProfileOnMissing_nothingMissing() {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
+
+ assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+ }
+
+ @Test
+ public void testSuccessfulAuthenticationUpdateProfileOnMissing_missingEmail() {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_MISSING);
+
+ assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", "new@email.com", true);
+ }
+
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile() {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+ }
+
+ /**
+ * Test that verify email action is performed if email is provided and email trust is not enabled for the provider
+ *
+ * @throws MessagingException
+ * @throws IOException
+ */
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled() throws IOException, MessagingException {
+ getRealm().setVerifyEmail(true);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ try {
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+ identityProviderModel.setTrustEmail(false);
+
+ UserModel federatedUser = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "test-user@localhost", false);
+
+ // email is verified now
+ assertFalse(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+ } finally {
+ getRealm().setVerifyEmail(false);
+ }
+ }
+
+ private UserModel assertSuccessfulAuthenticationWithEmailVerification(IdentityProviderModel identityProviderModel, String username, String expectedEmail,
+ boolean isProfileUpdateExpected)
+ throws IOException, MessagingException {
+ authenticateWithIdentityProvider(identityProviderModel, username, isProfileUpdateExpected);
+
+ // verify email is sent
+ Assert.assertTrue(verifyEmailPage.isCurrent());
+
+ // read email to take verification link from
+ Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+ String verificationUrl = getVerificationEmailLink(message);
+
+ driver.navigate().to(verificationUrl.trim());
+
+ // authenticated and redirected to app
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+
+ UserModel federatedUser = getFederatedUser();
+
+ assertNotNull(federatedUser);
+
+ doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
+
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ RealmModel realm = getRealm();
+
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+ assertEquals(1, federatedIdentities.size());
+
+ FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+ assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+ assertEquals(federatedUser.getUsername(), federatedIdentityModel.getIdentityProvider() + "." + federatedIdentityModel.getUserName());
+
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+ driver.navigate().to("http://localhost:8081/test-app");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+ return federatedUser;
+ }
+
+ /**
+ * Test for KEYCLOAK-1053 - verify email action is not performed if email is not provided, login is normal, but action stays in set to be performed later
+ */
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailNotProvided_emailVerifyEnabled() {
+ getRealm().setVerifyEmail(true);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ try {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", null, false);
+
+ assertTrue(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+ } finally {
+ getRealm().setVerifyEmail(false);
+ }
+ }
+
+ /**
+ * Test for KEYCLOAK-1372 - verify email action is not performed if email is provided but email trust is enabled for the provider
+ */
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_emailProvided_emailVerifyEnabled_emailTrustEnabled() {
+ getRealm().setVerifyEmail(true);
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ try {
+ identityProviderModel.setTrustEmail(true);
+
+ UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user", "test-user@localhost", false);
+
+ assertFalse(federatedUser.getRequiredActions().contains(UserModel.RequiredAction.VERIFY_EMAIL.name()));
+
+ } finally {
+ identityProviderModel.setTrustEmail(false);
+ getRealm().setVerifyEmail(false);
+ }
+ }
+
+ /**
+ * Test for KEYCLOAK-1372 - verify email action is performed if email is provided and email trust is enabled for the provider, but email is changed on First login update profile page
+ *
+ * @throws MessagingException
+ * @throws IOException
+ */
+ @Test
+ public void testSuccessfulAuthentication_emailTrustEnabled_emailVerifyEnabled_emailUpdatedOnFirstLogin() throws IOException, MessagingException {
+ getRealm().setVerifyEmail(true);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ try {
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+ identityProviderModel.setTrustEmail(true);
+
+ UserModel user = assertSuccessfulAuthenticationWithEmailVerification(identityProviderModel, "test-user", "new@email.com", true);
+ Assert.assertEquals("617-666-7777", user.getFirstAttribute("mobile"));
+ } finally {
+ identityProviderModel.setTrustEmail(false);
+ getRealm().setVerifyEmail(false);
+ }
+ }
+
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
+
+ getRealm().setRegistrationEmailAsUsername(true);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ try {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ authenticateWithIdentityProvider(identityProviderModel, "test-user", false);
+
+ // authenticated and redirected to app
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ // check correct user is created with email as username and bound to correct federated identity
+ RealmModel realm = getRealm();
+
+ UserModel federatedUser = session.users().getUserByUsername("test-user@localhost", realm);
+
+ assertNotNull(federatedUser);
+
+ assertEquals("test-user@localhost", federatedUser.getUsername());
+
+ doAssertFederatedUser(federatedUser, identityProviderModel, "test-user@localhost", false);
+
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+ assertEquals(1, federatedIdentities.size());
+
+ FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+ assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+ driver.navigate().to("http://localhost:8081/test-app");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ } finally {
+ getRealm().setRegistrationEmailAsUsername(false);
+ }
+ }
+
+ @Test
+ public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided() {
+
+ getRealm().setRegistrationEmailAsUsername(true);
+ brokerServerRule.stopSession(this.session, true);
+ this.session = brokerServerRule.startSession();
+
+ try {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF);
+
+ authenticateWithIdentityProvider(identityProviderModel, "test-user-noemail", false);
+
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ // check correct user is created with username from provider as email is not available
+ RealmModel realm = getRealm();
+ UserModel federatedUser = getFederatedUser();
+ assertNotNull(federatedUser);
+
+ doAssertFederatedUserNoEmail(federatedUser);
+
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+ assertEquals(1, federatedIdentities.size());
+
+ FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
+
+ assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
+ revokeGrant();
+
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+ driver.navigate().to("http://localhost:8081/test-app");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ } finally {
+ getRealm().setRegistrationEmailAsUsername(false);
+ }
+ }
+
+ @Test
+ public void testDisabled() {
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+
+ identityProviderModel.setEnabled(false);
+
+ this.driver.navigate().to("http://localhost:8081/test-app/");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ try {
+ this.driver.findElement(By.className(getProviderId()));
+ fail("Provider [" + getProviderId() + "] not disabled.");
+ } catch (NoSuchElementException nsee) {
+
+ }
+ }
+
+ @Test
+ public void testProviderOnLoginPage() {
+ // Provider button is available on login page
+ this.driver.navigate().to("http://localhost:8081/test-app/");
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+ loginPage.findSocialButton(getProviderId());
+ }
+
+ @Test
+ public void testAccountManagementLinkIdentity() {
+ // Login as pedroigor to account management
+ accountFederatedIdentityPage.realm("realm-with-broker");
+ accountFederatedIdentityPage.open();
+ assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+ loginPage.login("pedroigor", "password");
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+
+ // Link my "pedroigor" identity with "test-user" from brokered Keycloak
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+ accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getAlias());
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+ this.loginPage.login("test-user", "password");
+ doAfterProviderAuthentication();
+
+ // Assert identity linked in account management
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+ assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
+
+ // Revoke grant in account mgmt
+ revokeGrant();
+
+ // Logout from account management
+ accountFederatedIdentityPage.logout();
+ assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+
+ // Assert I am logged immediately to account management due to previously linked "test-user" identity
+ loginPage.clickSocial(identityProviderModel.getAlias());
+ doAfterProviderAuthentication();
+ assertTrue(accountFederatedIdentityPage.isCurrent());
+ assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
+
+ // Unlink my "test-user"
+ accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
+ assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
+
+ // Revoke grant in account mgmt
+ revokeGrant();
+
+ // Logout from account management
+ System.out.println("*** logout from account management");
+ accountFederatedIdentityPage.logout();
+ assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ // Try to login. Previous link is not valid anymore, so now it should try to register new user
+ this.loginPage.clickSocial(identityProviderModel.getAlias());
+ this.loginPage.login("test-user", "password");
+ doAfterProviderAuthentication();
+ this.updateProfilePage.assertCurrent();
+ }
+
+ @Test(expected = NoSuchElementException.class)
+ public void testIdentityProviderNotAllowed() {
+ this.driver.navigate().to("http://localhost:8081/test-app/");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ driver.findElement(By.className("model-oidc-idp"));
+ }
+
+ protected void configureClientRetrieveToken(String clientId) {
+ RealmModel realm = getRealm();
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+ ClientModel client = realm.getClientByClientId(clientId);
+ if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
+
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ }
+
+ protected void configureUserRetrieveToken(String username) {
+ RealmModel realm = getRealm();
+ UserModel user = session.users().getUserByUsername(username, realm);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+ if (user != null && !user.hasRole(readTokenRole)) {
+ user.grantRole(readTokenRole);
+ }
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ }
+
+ protected void unconfigureClientRetrieveToken(String clientId) {
+ RealmModel realm = getRealm();
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+ ClientModel client = realm.getClientByClientId(clientId);
+ if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
+
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ }
+
+ protected void unconfigureUserRetrieveToken(String username) {
+ RealmModel realm = getRealm();
+ UserModel user = session.users().getUserByUsername(username, realm);
+ RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(Constants.READ_TOKEN_ROLE);
+ if (user != null && user.hasRole(readTokenRole)) {
+ user.deleteRoleMapping(readTokenRole);
+ }
+ brokerServerRule.stopSession(session, true);
+ session = brokerServerRule.startSession();
+
+ }
+
+ @Test
+ public void testTokenStorageAndRetrievalByApplication() {
+ setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_ON);
+ IdentityProviderModel identityProviderModel = getIdentityProviderModel();
+
+ identityProviderModel.setStoreToken(true);
+
+ authenticateWithIdentityProvider(identityProviderModel, "test-user", true);
+
+ UserModel federatedUser = getFederatedUser();
+ RealmModel realm = getRealm();
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realm);
+
+ assertFalse(federatedIdentities.isEmpty());
+ assertEquals(1, federatedIdentities.size());
+
+ FederatedIdentityModel identityModel = federatedIdentities.iterator().next();
+
+ assertNotNull(identityModel.getToken());
+
+ UserSessionStatusServlet.UserSessionStatus userSessionStatus = retrieveSessionStatus();
+ String accessToken = userSessionStatus.getAccessTokenString();
+ URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
+ final String authHeader = "Bearer " + accessToken;
+ ClientRequestFilter authFilter = new ClientRequestFilter() {
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
+ }
+ };
+ Client client = ClientBuilder.newBuilder().register(authFilter).build();
+ WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
+ Response response = tokenEndpoint.request().get();
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertNotNull(response.readEntity(String.class));
+ revokeGrant();
+
+
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+ String currentUrl = this.driver.getCurrentUrl();
+ System.out.println("after logout currentUrl: " + currentUrl);
+ assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+
+ unconfigureUserRetrieveToken(getProviderId() + ".test-user");
+ loginIDP("test-user");
+ //authenticateWithIdentityProvider(identityProviderModel, "test-user");
+ assertEquals("http://localhost:8081/test-app", driver.getCurrentUrl());
+
+ userSessionStatus = retrieveSessionStatus();
+ accessToken = userSessionStatus.getAccessTokenString();
+ final String authHeader2 = "Bearer " + accessToken;
+ ClientRequestFilter authFilter2 = new ClientRequestFilter() {
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader2);
+ }
+ };
+ client = ClientBuilder.newBuilder().register(authFilter2).build();
+ tokenEndpoint = client.target(tokenEndpointUrl);
+ response = tokenEndpoint.request().get();
+
+ assertEquals(Response.Status.FORBIDDEN.getStatusCode(), response.getStatus());
+
+ revokeGrant();
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+ driver.navigate().to("http://localhost:8081/test-app");
+
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
+ }
+
+ protected abstract void doAssertTokenRetrieval(String pageSource);
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java
new file mode 100644
index 0000000..d689363
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCFirstBrokerLoginTest.java
@@ -0,0 +1,145 @@
+package org.keycloak.testsuite.broker;
+
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.keycloak.authentication.authenticators.broker.IdpEmailVerificationAuthenticatorFactory;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.openqa.selenium.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
+
+ private static final int PORT = 8082;
+
+ @ClassRule
+ public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+ @Override
+ protected void configureServer(KeycloakServer server) {
+ server.getConfig().setPort(PORT);
+ }
+
+ @Override
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+ server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-kc-oidc.json"));
+ server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml.json"));
+ }
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-oidc-identity-provider", "realm-with-saml-idp-basic" };
+ }
+ };
+
+ @Override
+ protected String getProviderId() {
+ return "kc-oidc-idp";
+ }
+
+
+ /**
+ * Tests that duplication is detected and user wants to link federatedIdentity with existing account. He will confirm link by reauthentication
+ * with different broker already linked to his account
+ */
+ @Test
+ public void testLinkAccountByReauthenticationWithDifferentBroker() throws Exception {
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+ IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.DISABLED);
+
+ setUpdateProfileFirstLogin(realmWithBroker, IdentityProviderRepresentation.UPFLM_OFF);
+ }
+
+ }, APP_REALM_ID);
+
+ // First link "pedroigor" user with SAML broker and logout
+ driver.navigate().to("http://localhost:8081/test-app");
+ this.loginPage.clickSocial("kc-saml-idp-basic");
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
+ Assert.assertEquals("Log in to realm-with-saml-idp-basic", this.driver.getTitle());
+ this.loginPage.login("pedroigor", "password");
+
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickLinkAccount();
+
+ this.loginPage.login("password");
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+ driver.navigate().to("http://localhost:8081/test-app/logout");
+
+
+ // login through OIDC broker now
+ loginIDP("pedroigor");
+
+ this.idpConfirmLinkPage.assertCurrent();
+ Assert.assertEquals("User with email psilva@redhat.com already exists. How do you want to continue?", this.idpConfirmLinkPage.getMessage());
+ this.idpConfirmLinkPage.clickLinkAccount();
+
+ // assert reauthentication with login page. On login page is link to kc-saml-idp-basic as user has it linked already
+ Assert.assertEquals("Log in to " + APP_REALM_ID, this.driver.getTitle());
+ Assert.assertEquals("Authenticate as pedroigor to link your account with " + getProviderId(), this.loginPage.getSuccessMessage());
+
+ try {
+ this.loginPage.findSocialButton(getProviderId());
+ Assert.fail("Not expected to see social button with " + getProviderId());
+ } catch (NoSuchElementException expected) {
+ }
+
+ // reauthenticate with SAML broker
+ this.loginPage.clickSocial("kc-saml-idp-basic");
+ Assert.assertEquals("Log in to realm-with-saml-idp-basic", this.driver.getTitle());
+ this.loginPage.login("pedroigor", "password");
+
+
+ // authenticated and redirected to app. User is linked with identity provider
+ assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/test-app"));
+ UserModel federatedUser = getFederatedUser();
+
+ assertNotNull(federatedUser);
+ assertEquals("pedroigor", federatedUser.getUsername());
+ assertEquals("psilva@redhat.com", federatedUser.getEmail());
+
+ RealmModel realmWithBroker = getRealm();
+ Set<FederatedIdentityModel> federatedIdentities = this.session.users().getFederatedIdentities(federatedUser, realmWithBroker);
+ assertEquals(2, federatedIdentities.size());
+
+ for (FederatedIdentityModel link : federatedIdentities) {
+ Assert.assertEquals("pedroigor", link.getUserName());
+ Assert.assertTrue(link.getIdentityProvider().equals(getProviderId()) || link.getIdentityProvider().equals("kc-saml-idp-basic"));
+ }
+
+ brokerServerRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel realmWithBroker) {
+ setExecutionRequirement(realmWithBroker, DefaultAuthenticationFlows.FIRST_BROKER_LOGIN_HANDLE_EXISTING_SUBFLOW,
+ IdpEmailVerificationAuthenticatorFactory.PROVIDER_ID, AuthenticationExecutionModel.Requirement.ALTERNATIVE);
+
+ }
+
+ }, APP_REALM_ID);
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
index 0a7ee16..5bfdc1d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeyCloakServerBrokerBasicTest.java
@@ -26,7 +26,7 @@ import static org.junit.Assert.fail;
/**
* @author pedroigor
*/
-public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderTest {
+public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityProviderTest {
private static final int PORT = 8082;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java
new file mode 100644
index 0000000..66692d4
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLFirstBrokerLoginTest.java
@@ -0,0 +1,40 @@
+package org.keycloak.testsuite.broker;
+
+import org.junit.ClassRule;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SAMLFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
+
+ private static final int PORT = 8082;
+
+ @ClassRule
+ public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
+
+ @Override
+ protected void configureServer(KeycloakServer server) {
+ server.getConfig().setPort(PORT);
+ }
+
+ @Override
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+ server.importRealm(getClass().getResourceAsStream("/broker-test/test-broker-realm-with-saml.json"));
+ }
+
+ @Override
+ protected String[] getTestRealms() {
+ return new String[] { "realm-with-saml-idp-basic" };
+ }
+ };
+
+ @Override
+ protected String getProviderId() {
+ return "kc-saml-idp-basic";
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
index 7bbaa20..8be9dcc 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerBasicTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.fail;
/**
* @author pedroigor
*/
-public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderTest {
+public class SAMLKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityProviderTest {
@ClassRule
public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
index 249f2e0..197749b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/SAMLKeyCloakServerBrokerWithSignatureTest.java
@@ -24,7 +24,7 @@ import static org.junit.Assert.fail;
/**
* @author pedroigor
*/
-public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityProviderTest {
+public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractKeycloakIdentityProviderTest {
@ClassRule
public static AbstractKeycloakRule samlServerRule = new AbstractKeycloakRule() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
new file mode 100644
index 0000000..83596a0
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpConfirmLinkPage.java
@@ -0,0 +1,41 @@
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpConfirmLinkPage extends AbstractPage {
+
+ @FindBy(id = "updateProfile")
+ private WebElement updateProfileButton;
+
+ @FindBy(id = "linkAccount")
+ private WebElement linkAccountButton;
+
+ @FindBy(className = "instruction")
+ private WebElement message;
+
+ @Override
+ public boolean isCurrent() {
+ return driver.getTitle().equals("Account already exists");
+ }
+
+ public String getMessage() {
+ return message.getText();
+ }
+
+ public void clickReviewProfile() {
+ updateProfileButton.click();
+ }
+
+ public void clickLinkAccount() {
+ linkAccountButton.click();
+ }
+
+ @Override
+ public void open() throws Exception {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
new file mode 100644
index 0000000..91387b5
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/IdpLinkEmailPage.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class IdpLinkEmailPage extends AbstractPage {
+
+ @FindBy(id = "instruction1")
+ private WebElement message;
+
+ @Override
+ public boolean isCurrent() {
+ return driver.getTitle().startsWith("Link ");
+ }
+
+ @Override
+ public void open() throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getMessage() {
+ return message.getText();
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index f329533..04e5ddd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -112,6 +112,10 @@ public class LoginPage extends AbstractPage {
return usernameInput.getAttribute("value");
}
+ public boolean isUsernameInputEnabled() {
+ return usernameInput.isEnabled();
+ }
+
public String getPassword() {
return passwordInput.getAttribute("value");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
index d67862c..c9038a4 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java
@@ -21,6 +21,8 @@
*/
package org.keycloak.testsuite.pages;
+import org.junit.Assert;
+import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;