package org.keycloak.testsuite.broker; import org.jboss.arquillian.graphene.Graphene; import org.jboss.arquillian.graphene.page.Page; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.social.openshift.OpenshiftV3IdentityProvider; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.auth.page.login.UpdateAccount; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.social.AbstractSocialLoginPage; import org.keycloak.testsuite.pages.social.FacebookLoginPage; import org.keycloak.testsuite.pages.social.GitHubLoginPage; import org.keycloak.testsuite.pages.social.GoogleLoginPage; import org.keycloak.testsuite.pages.social.LinkedInLoginPage; import org.keycloak.testsuite.pages.social.MicrosoftLoginPage; import org.keycloak.testsuite.pages.social.StackOverflowLoginPage; import org.keycloak.testsuite.pages.social.TwitterLoginPage; import org.keycloak.testsuite.util.IdentityProviderBuilder; import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.URLUtils; import org.keycloak.testsuite.util.WaitUtils; import org.openqa.selenium.By; import org.openqa.selenium.support.ui.ExpectedConditions; import java.io.FileInputStream; import java.util.LinkedList; import java.util.List; import java.util.Properties; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.FACEBOOK; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GITHUB; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.GOOGLE; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.LINKEDIN; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.MICROSOFT; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.OPENSHIFT; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.STACKOVERFLOW; import static org.keycloak.testsuite.broker.SocialLoginTest.Provider.TWITTER; /** * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author Vaclav Muzikar <vmuzikar@redhat.com> */ public class SocialLoginTest extends AbstractKeycloakTest { public static final String SOCIAL_CONFIG = "social.config"; public static final String REALM = "social"; private static Properties config = new Properties(); @Page private LoginPage loginPage; @Page private UpdateAccount updateAccountPage; public enum Provider { GOOGLE("google", GoogleLoginPage.class), FACEBOOK("facebook", FacebookLoginPage.class), GITHUB("github", GitHubLoginPage.class), TWITTER("twitter", TwitterLoginPage.class), LINKEDIN("linkedin", LinkedInLoginPage.class), MICROSOFT("microsoft", MicrosoftLoginPage.class), STACKOVERFLOW("stackoverflow", StackOverflowLoginPage.class), OPENSHIFT("openshift-v3", null); private String id; private Class<? extends AbstractSocialLoginPage> pageObjectClazz; Provider(String id, Class<? extends AbstractSocialLoginPage> pageObjectClazz) { this.id = id; this.pageObjectClazz = pageObjectClazz; } public String id() { return id; } public Class<? extends AbstractSocialLoginPage> pageObjectClazz() { return pageObjectClazz; } } private Provider currentTestProvider; @BeforeClass public static void loadConfig() throws Exception { assumeTrue(System.getProperties().containsKey(SOCIAL_CONFIG)); config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG))); } @Before public void beforeSocialLoginTest() { accountPage.setAuthRealm(REALM); accountPage.navigateTo(); currentTestProvider = null; } @After public void removeUser() { List<UserRepresentation> users = adminClient.realm(REALM).users().search(null, null, null); for (UserRepresentation user : users) { if (user.getServiceAccountClientId() == null) { log.infof("removing test user '%s'", user.getUsername()); adminClient.realm(REALM).users().get(user.getId()).remove(); } } } @Override public void addTestRealms(List<RealmRepresentation> testRealms) { RealmRepresentation rep = RealmBuilder.create().name(REALM).build(); List<IdentityProviderRepresentation> idps = new LinkedList<>(); rep.setIdentityProviders(idps); for (Provider provider : Provider.values()) { idps.add(buildIdp(provider)); } testRealms.add(rep); } @Test @Ignore // TODO: Fix and revamp this test public void openshiftLogin() throws Exception { loginPage.clickSocial("openshift-v3"); Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("inputUsername"))); driver.findElement(By.id("inputUsername")).sendKeys(config.getProperty("openshift-v3.username", config.getProperty("common.username"))); driver.findElement(By.id("inputPassword")).sendKeys(config.getProperty("openshift-v3.password", config.getProperty("common.password"))); driver.findElement(By.cssSelector("button[type=submit]")).click(); Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input[name=approve]"))); driver.findElement(By.cssSelector("input[name=approve]")).click(); assertEquals(config.getProperty("openshift-v3.username", config.getProperty("common.profile.username")), accountPage.getUsername()); } @Test public void googleLogin() throws InterruptedException { currentTestProvider = GOOGLE; performLogin(); assertAccount(); } @Test public void facebookLogin() { currentTestProvider = FACEBOOK; performLogin(); assertAccount(); } @Test public void githubLogin() { currentTestProvider = GITHUB; performLogin(); assertAccount(); } @Test public void twitterLogin() { currentTestProvider = TWITTER; performLogin(); assertUpdateProfile(false, false, true); assertAccount(); } @Test public void linkedinLogin() { currentTestProvider = LINKEDIN; performLogin(); assertAccount(); } @Test public void microsoftLogin() { currentTestProvider = MICROSOFT; performLogin(); assertAccount(); } @Test public void stackoverflowLogin() { currentTestProvider = STACKOVERFLOW; performLogin(); assertUpdateProfile(false, false, true); assertAccount(); } private IdentityProviderRepresentation buildIdp(Provider provider) { IdentityProviderRepresentation idp = IdentityProviderBuilder.create().alias(provider.id()).providerId(provider.id()).build(); idp.setEnabled(true); idp.getConfig().put("clientId", getConfig(provider, "clientId")); idp.getConfig().put("clientSecret", getConfig(provider, "clientSecret")); if (provider == STACKOVERFLOW) { idp.getConfig().put("key", getConfig(provider, "clientKey")); } if (provider == OPENSHIFT) { idp.getConfig().put("baseUrl", config.getProperty(provider.id() + ".baseUrl", OpenshiftV3IdentityProvider.BASE_URL)); } return idp; } private String getConfig(Provider provider, String key) { return config.getProperty(provider.id() + "." + key, config.getProperty("common." + key)); } private String getConfig(String key) { return getConfig(currentTestProvider, key); } private void performLogin() { loginPage.clickSocial(currentTestProvider.id()); // Just to be sure there's no redirect in progress WaitUtils.pause(3000); WaitUtils.waitForPageToLoad(driver); // Only when there's not active session for the social provider, i.e. login is required if (URLUtils.currentUrlDoesntStartWith(driver, getAuthServerRoot().toASCIIString())) { log.infof("current URL: %s", driver.getCurrentUrl()); log.infof("performing log in to '%s' ...", currentTestProvider.id()); AbstractSocialLoginPage loginPage = Graphene.createPageFragment(currentTestProvider.pageObjectClazz(), driver.findElement(By.tagName("html"))); loginPage.login(getConfig("username"), getConfig("password")); } else { log.infof("already logged in to '%s'; skipping the login process", currentTestProvider.id()); } } private void assertAccount() { assertTrue(URLUtils.currentUrlStartWith(driver, accountPage.toString())); // Sometimes after login the URL ends with /# or similar assertEquals(getConfig("profile.firstName"), accountPage.getFirstName()); assertEquals(getConfig("profile.lastName"), accountPage.getLastName()); assertEquals(getConfig("profile.email"), accountPage.getEmail()); } private void assertUpdateProfile(boolean firstName, boolean lastName, boolean email) { assertTrue(URLUtils.currentUrlDoesntStartWith(driver, accountPage.toString())); if (firstName) { assertTrue(updateAccountPage.fields().getFirstName().isEmpty()); updateAccountPage.fields().setFirstName(getConfig("profile.firstName")); } else { assertEquals(getConfig("profile.firstName"), updateAccountPage.fields().getFirstName()); } if (lastName) { assertTrue(updateAccountPage.fields().getLastName().isEmpty()); updateAccountPage.fields().setLastName(getConfig("profile.lastName")); } else { assertEquals(getConfig("profile.lastName"), updateAccountPage.fields().getLastName()); } if (email) { assertTrue(updateAccountPage.fields().getEmail().isEmpty()); updateAccountPage.fields().setEmail(getConfig("profile.email")); } else { assertEquals(getConfig("profile.email"), updateAccountPage.fields().getEmail()); } updateAccountPage.submit(); } }