/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.keycloak.testsuite.broker; 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 java.util.Set; 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.getInfoMessage()); 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); } }