diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountFederatedIdentityBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountFederatedIdentityBean.java index 54dd7bff9a..ad3c4915a6 100755 --- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountFederatedIdentityBean.java +++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountFederatedIdentityBean.java @@ -46,7 +46,6 @@ public class AccountFederatedIdentityBean { public AccountFederatedIdentityBean(KeycloakSession session, RealmModel realm, UserModel user, URI baseUri, String stateChecker) { this.session = session; - URI accountIdentityUpdateUri = Urls.accountFederatedIdentityUpdate(baseUri, realm.getName()); List identityProviders = realm.getIdentityProviders(); Set identities = session.users().getFederatedIdentities(user, realm); @@ -63,15 +62,8 @@ public class AccountFederatedIdentityBean { availableIdentities++; } - String action = identity != null ? "remove" : "add"; - String actionUrl = UriBuilder.fromUri(accountIdentityUpdateUri) - .queryParam("action", action) - .queryParam("provider_id", providerId) - .queryParam("stateChecker", stateChecker) - .build().toString(); - String displayName = KeycloakModelUtils.getIdentityProviderDisplayName(session, provider); - FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, displayName, provider.getAlias(), provider.getAlias(), actionUrl, + FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, displayName, provider.getAlias(), provider.getAlias(), provider.getConfig() != null ? provider.getConfig().get("guiOrder") : null); orderedSet.add(entry); } @@ -105,17 +97,15 @@ public class AccountFederatedIdentityBean { private FederatedIdentityModel federatedIdentityModel; private final String providerId; private final String providerName; - private final String actionUrl; private final String guiOrder; private final String displayName; public FederatedIdentityEntry(FederatedIdentityModel federatedIdentityModel, String displayName, String providerId, - String providerName, String actionUrl, String guiOrder) { + String providerName, String guiOrder) { this.federatedIdentityModel = federatedIdentityModel; this.displayName = displayName; this.providerId = providerId; this.providerName = providerName; - this.actionUrl = actionUrl; this.guiOrder = guiOrder; } @@ -139,10 +129,6 @@ public class AccountFederatedIdentityBean { return federatedIdentityModel != null; } - public String getActionUrl() { - return actionUrl; - } - public String getGuiOrder() { return guiOrder; } @@ -186,4 +172,4 @@ public class AccountFederatedIdentityBean { return 10000; } } -} \ No newline at end of file +} diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java index 6ba04fe09a..9ea898ba1f 100755 --- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java +++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/UrlBean.java @@ -33,7 +33,6 @@ public class UrlBean { private URI baseURI; private URI baseQueryURI; private URI currentURI; - private String stateChecker; public UrlBean(RealmModel realm, Theme theme, URI baseURI, URI baseQueryURI, URI currentURI, String stateChecker) { this.realm = realm.getName(); @@ -41,7 +40,6 @@ public class UrlBean { this.baseURI = baseURI; this.baseQueryURI = baseQueryURI; this.currentURI = currentURI; - this.stateChecker = stateChecker; } public String getApplicationsUrl() { @@ -73,7 +71,7 @@ public class UrlBean { } public String getSessionsLogoutUrl() { - return Urls.accountSessionsLogoutPage(baseQueryURI, realm, stateChecker).toString(); + return Urls.accountSessionsLogoutPage(baseQueryURI, realm).toString(); } public String getRevokeClientUrl() { @@ -81,7 +79,7 @@ public class UrlBean { } public String getTotpRemoveUrl() { - return Urls.accountTotpRemove(baseQueryURI, realm, stateChecker).toString(); + return Urls.accountTotpRemove(baseQueryURI, realm).toString(); } public String getLogoutUrl() { diff --git a/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java index cb023fa2f3..6e954f9baf 100755 --- a/services/src/main/java/org/keycloak/services/Urls.java +++ b/services/src/main/java/org/keycloak/services/Urls.java @@ -126,9 +126,8 @@ public class Urls { return accountBase(baseUri).path(AccountFormService.class, "totpPage").build(realmName); } - public static URI accountTotpRemove(URI baseUri, String realmName, String stateChecker) { + public static URI accountTotpRemove(URI baseUri, String realmName) { return accountBase(baseUri).path(AccountFormService.class, "processTotpRemove") - .queryParam("stateChecker", stateChecker) .build(realmName); } @@ -140,9 +139,8 @@ public class Urls { return accountBase(baseUri).path(AccountFormService.class, "sessionsPage").build(realmName); } - public static URI accountSessionsLogoutPage(URI baseUri, String realmName, String stateChecker) { + public static URI accountSessionsLogoutPage(URI baseUri, String realmName) { return accountBase(baseUri).path(AccountFormService.class, "processSessionsLogout") - .queryParam("stateChecker", stateChecker) .build(realmName); } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 981a47f9c9..7c779557a4 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -572,6 +572,11 @@ public class AuthenticationManager { return uri.getRawPath(); } + public static String getAccountCookiePath(RealmModel realm, UriInfo uriInfo) { + URI uri = RealmsResource.accountUrl(uriInfo.getBaseUriBuilder()).build(realm.getName()); + return uri.getRawPath(); + } + public static void expireCookie(RealmModel realm, String cookieName, String path, boolean httpOnly, ClientConnection connection) { logger.debugv("Expiring cookie: {0} path: {1}", cookieName, path); boolean secureOnly = realm.getSslRequired().isRequired(connection);; diff --git a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java index 7b85795509..bcc99b6a14 100755 --- a/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java +++ b/services/src/main/java/org/keycloak/services/resources/AbstractSecuredLocalService.java @@ -24,14 +24,12 @@ import org.keycloak.OAuth2Constants; import org.keycloak.common.ClientConnection; import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.KeycloakUriBuilder; -import org.keycloak.common.util.UriUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.services.ForbiddenException; -import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.Auth; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.util.CookieHelper; @@ -130,14 +128,20 @@ public abstract class AbstractSecuredLocalService { } protected void updateCsrfChecks() { - Cookie cookie = headers.getCookies().get(KEYCLOAK_STATE_CHECKER); - if (cookie != null) { - stateChecker = cookie.getValue(); - } else { + stateChecker = getStateChecker(); + if (stateChecker == null) { stateChecker = Base64Url.encode(KeycloakModelUtils.generateSecret()); - String cookiePath = AuthenticationManager.getRealmCookiePath(realm, uriInfo); + + StringBuilder sb = new StringBuilder(); + sb.append(auth.getSession().getId()); + sb.append("/"); + sb.append(stateChecker); + + String sessionCookieValue = sb.toString(); + + String cookiePath = AuthenticationManager.getAccountCookiePath(realm, uriInfo); boolean secureOnly = realm.getSslRequired().isRequired(clientConnection); - CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, stateChecker, cookiePath, null, null, -1, secureOnly, true); + CookieHelper.addCookie(KEYCLOAK_STATE_CHECKER, sessionCookieValue, cookiePath, null, null, -1, secureOnly, true); } } @@ -149,25 +153,27 @@ public abstract class AbstractSecuredLocalService { * @param formData */ protected void csrfCheck(final MultivaluedMap formData) { - if (!auth.isCookieAuthenticated()) return; String stateChecker = formData.getFirst("stateChecker"); - if (!this.stateChecker.equals(stateChecker)) { + if (stateChecker == null || !stateChecker.equals(getStateChecker())) { throw new ForbiddenException(); } - } - /** - * Check to see if form post has sessionId hidden field and match it against the session id. - * - */ - protected void csrfCheck(String stateChecker) { - if (!auth.isCookieAuthenticated()) return; - if (auth.getSession() == null) return; - if (!this.stateChecker.equals(stateChecker)) { - throw new ForbiddenException(); - } + protected String getStateChecker() { + Cookie cookie = headers.getCookies().get(KEYCLOAK_STATE_CHECKER); + if (cookie != null) { + stateChecker = cookie.getValue(); + String[] s = stateChecker.split("/"); + if (s.length == 2) { + String sessionId = s[0]; + String stateChecker = s[1]; + if (auth.getSession().getId().equals(sessionId)) { + return stateChecker; + } + } + } + return null; } protected abstract URI getBaseRedirectUri(); diff --git a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java index 04c6f04f64..1f73bc643f 100755 --- a/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java +++ b/services/src/main/java/org/keycloak/services/resources/account/AccountFormService.java @@ -44,6 +44,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.CredentialValidation; import org.keycloak.models.utils.FormMessage; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.protocol.oidc.utils.RedirectUtils; import org.keycloak.services.ForbiddenException; import org.keycloak.services.ServicesLogger; @@ -343,15 +344,15 @@ public class AccountFormService extends AbstractSecuredLocalService { } @Path("totp-remove") - @GET - public Response processTotpRemove(@QueryParam("stateChecker") String stateChecker) { + @POST + public Response processTotpRemove(final MultivaluedMap formData) { if (auth == null) { return login("totp"); } auth.require(AccountRoles.MANAGE_ACCOUNT); - csrfCheck(stateChecker); + csrfCheck(formData); UserModel user = auth.getUser(); session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP); @@ -364,14 +365,14 @@ public class AccountFormService extends AbstractSecuredLocalService { @Path("sessions-logout") - @GET - public Response processSessionsLogout(@QueryParam("stateChecker") String stateChecker) { + @POST + public Response processSessionsLogout(final MultivaluedMap formData) { if (auth == null) { return login("sessions"); } auth.require(AccountRoles.MANAGE_ACCOUNT); - csrfCheck(stateChecker); + csrfCheck(formData); UserModel user = auth.getUser(); @@ -588,19 +589,21 @@ public class AccountFormService extends AbstractSecuredLocalService { return account.setPasswordSet(true).setSuccess(Messages.ACCOUNT_PASSWORD_UPDATED).createResponse(AccountPages.PASSWORD); } - @Path("federated-identity-update") - @GET - public Response processFederatedIdentityUpdate(@QueryParam("action") String action, - @QueryParam("provider_id") String providerId, - @QueryParam("stateChecker") String stateChecker) { + @Path("identity") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public Response processFederatedIdentityUpdate(final MultivaluedMap formData) { if (auth == null) { return login("identity"); } auth.require(AccountRoles.MANAGE_ACCOUNT); - csrfCheck(stateChecker); + csrfCheck(formData); UserModel user = auth.getUser(); + String action = formData.getFirst("action"); + String providerId = formData.getFirst("providerId"); + if (Validation.isEmpty(providerId)) { setReferrerOnPage(); return account.setError(Messages.MISSING_IDENTITY_PROVIDER).createResponse(AccountPages.FEDERATED_IDENTITY); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java index 9ca2372dbb..7992ccf1b8 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java @@ -22,6 +22,9 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; +import java.util.LinkedList; +import java.util.List; + /** * @author Marek Posolda */ @@ -50,26 +53,72 @@ public class AccountFederatedIdentityPage extends AbstractAccountPage { public boolean isCurrent() { return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Federated Identities"); } - - public WebElement findAddProviderButton(String alias) { - return driver.findElement(By.id("add-" + alias)); - } - - public WebElement findRemoveProviderButton(String alias) { - return driver.findElement(By.id("remove-" + alias)); + + public List getIdentities() { + List identities = new LinkedList<>(); + WebElement identitiesElement = driver.findElement(By.id("federated-identities")); + for (WebElement i : identitiesElement.findElements(By.className("row"))) { + + String providerId = i.findElement(By.tagName("label")).getText(); + String subject = i.findElement(By.tagName("input")).getAttribute("value"); + WebElement button = i.findElement(By.tagName("button")); + + identities.add(new FederatedIdentity(providerId, subject, button)); + } + return identities; } - public void clickAddProvider(String alias) { - WebElement addButton = findAddProviderButton(alias); - addButton.click(); + public WebElement findAddProvider(String providerId) { + return driver.findElement(By.id("add-link-" + providerId)); } - public void clickRemoveProvider(String alias) { - WebElement addButton = findRemoveProviderButton(alias); - addButton.click(); + public void clickAddProvider(String providerId) { + findAddProvider(providerId).click(); + } + + public void clickRemoveProvider(String providerId) { + driver.findElement(By.id("remove-link-" + providerId)).click(); } public String getError() { return errorMessage.getText(); } + + public static class FederatedIdentity { + + private String providerId; + private String subject; + private WebElement action; + + public FederatedIdentity(String providerId, String subject, WebElement action) { + this.providerId = providerId; + this.subject = subject; + this.action = action; + } + + public String getProvider() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public WebElement getAction() { + return action; + } + + public void setAction(WebElement action) { + this.action = action; + } + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java new file mode 100755 index 0000000000..7403073f8d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java @@ -0,0 +1,151 @@ +/* + * 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.account; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.admin.ApiUtil; +import org.keycloak.testsuite.broker.AbstractBaseBrokerTest; +import org.keycloak.testsuite.broker.BrokerConfiguration; +import org.keycloak.testsuite.broker.KcOidcBrokerConfiguration; +import org.keycloak.testsuite.pages.AccountFederatedIdentityPage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.util.UserBuilder; +import org.openqa.selenium.WebElement; + +import javax.ws.rs.core.Response; +import java.util.List; + +import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient; +import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword; + +/** + * @author Stian Thorgersen + * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc. + */ +public class AccountBrokerTest extends AbstractBaseBrokerTest { + + @Page + protected LoginPage loginPage; + + @Page + protected AccountFederatedIdentityPage identityPage; + + @Override + protected BrokerConfiguration getBrokerConfiguration() { + return KcOidcBrokerConfiguration.INSTANCE; + } + + @Before + public void createUser() { + log.debug("creating user for realm " + bc.providerRealmName()); + + UserRepresentation user = new UserRepresentation(); + user.setUsername(bc.getUserLogin()); + user.setEmail(bc.getUserEmail()); + user.setEmailVerified(true); + user.setEnabled(true); + + RealmResource realmResource = adminClient.realm(bc.providerRealmName()); + userId = createUserWithAdminClient(realmResource, user); + + resetUserPassword(realmResource.users().get(userId), bc.getUserPassword(), false); + } + + @Before + public void addIdentityProviderToProviderRealm() { + log.debug("adding identity provider to realm " + bc.consumerRealmName()); + + RealmResource realm = adminClient.realm(bc.consumerRealmName()); + realm.identityProviders().create(bc.setUpIdentityProvider(suiteContext)).close(); + realm.identityProviders().get(bc.getIDPAlias()); + } + + @Before + public void addClients() { + List clients = bc.createProviderClients(suiteContext); + if (clients != null) { + RealmResource providerRealm = adminClient.realm(bc.providerRealmName()); + for (ClientRepresentation client : clients) { + log.debug("adding client " + client.getName() + " to realm " + bc.providerRealmName()); + + providerRealm.clients().create(client).close(); + } + } + + clients = bc.createConsumerClients(suiteContext); + if (clients != null) { + RealmResource consumerRealm = adminClient.realm(bc.consumerRealmName()); + for (ClientRepresentation client : clients) { + log.debug("adding client " + client.getName() + " to realm " + bc.consumerRealmName()); + + consumerRealm.clients().create(client).close(); + } + } + } + + @Before + public void before() { + Response response = adminClient.realm(KcOidcBrokerConfiguration.INSTANCE.consumerRealmName()).users().create(UserBuilder.create().username("accountbrokertest").build()); + String userId = ApiUtil.getCreatedId(response); + ApiUtil.resetUserPassword(adminClient.realm(KcOidcBrokerConfiguration.INSTANCE.consumerRealmName()).users().get(userId), "password", false); + } + + @Test + public void add() { + identityPage.realm(KcOidcBrokerConfiguration.INSTANCE.consumerRealmName()); + identityPage.open(); + loginPage.login("accountbrokertest", "password"); + Assert.assertTrue(identityPage.isCurrent()); + + List identities = identityPage.getIdentities(); + Assert.assertEquals(1, identities.size()); + + Assert.assertEquals("kc-oidc-idp", identities.get(0).getProvider()); + Assert.assertEquals("", identities.get(0).getSubject()); + Assert.assertEquals("add-link-kc-oidc-idp", identities.get(0).getAction().getAttribute("id")); + + identities.get(0).getAction().click(); + + loginPage.login(bc.getUserLogin(), bc.getUserPassword()); + + Assert.assertTrue(identityPage.isCurrent()); + + identities = identityPage.getIdentities(); + Assert.assertEquals(1, identities.size()); + + Assert.assertEquals("kc-oidc-idp", identities.get(0).getProvider()); + Assert.assertEquals("user@localhost.com", identities.get(0).getSubject()); + Assert.assertEquals("remove-link-kc-oidc-idp", identities.get(0).getAction().getAttribute("id")); + + identities.get(0).getAction().click(); + + Assert.assertTrue(identityPage.isCurrent()); + + identities = identityPage.getIdentities(); + + Assert.assertEquals("kc-oidc-idp", identities.get(0).getProvider()); + Assert.assertEquals("", identities.get(0).getSubject()); + Assert.assertEquals("add-link-kc-oidc-idp", identities.get(0).getAction().getAttribute("id")); + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java index 7fa6a3db6a..bf5f68a555 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java @@ -993,7 +993,7 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest { public void testIdentityProviderHiddenOnLoginPageIsVisbleInAccount(){ federatedIdentityPage.open(); loginPage.login("test-user@localhost", "password"); - Assert.assertNotNull(federatedIdentityPage.findAddProviderButton("myhiddenoidc")); + Assert.assertNotNull(federatedIdentityPage.findAddProvider("myhiddenoidc")); } @Test diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java index f08b4bf2c6..7c756fae9f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AccountLinkTest.java @@ -148,7 +148,7 @@ public class AccountLinkTest extends AbstractKeycloakTest { // Assert identity linked in account management assertTrue(accountFederatedIdentityPage.isCurrent()); - assertTrue(driver.getPageSource().contains("id=\"remove-" + PARENT_IDP + "\"")); + assertTrue(driver.getPageSource().contains("id=\"remove-link-" + PARENT_IDP + "\"")); // Logout from account management accountFederatedIdentityPage.logout(); @@ -161,11 +161,11 @@ public class AccountLinkTest extends AbstractKeycloakTest { System.out.println("--------------------------------"); System.out.println(driver.getPageSource()); assertTrue(accountFederatedIdentityPage.isCurrent()); - assertTrue(driver.getPageSource().contains("id=\"remove-" + PARENT_IDP + "\"")); + assertTrue(driver.getPageSource().contains("id=\"remove-link-" + PARENT_IDP + "\"")); // Unlink my "test-user" accountFederatedIdentityPage.clickRemoveProvider(PARENT_IDP); - assertTrue(driver.getPageSource().contains("id=\"add-" + PARENT_IDP + "\"")); + assertTrue(driver.getPageSource().contains("id=\"add-link-" + PARENT_IDP + "\"")); // Logout from account management diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java index 0831053911..05cd035055 100755 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java @@ -420,7 +420,7 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent // Assert identity linked in account management assertTrue(accountFederatedIdentityPage.isCurrent()); - assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\"")); + assertTrue(driver.getPageSource().contains("id=\"remove-link-" + identityProviderModel.getAlias() + "\"")); // Revoke grant in account mgmt revokeGrant(); @@ -434,11 +434,11 @@ public abstract class AbstractKeycloakIdentityProviderTest extends AbstractIdent this.loginPage.login("test-user", "password"); doAfterProviderAuthentication(); assertTrue(accountFederatedIdentityPage.isCurrent()); - assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\"")); + assertTrue(driver.getPageSource().contains("id=\"remove-link-" + identityProviderModel.getAlias() + "\"")); // Unlink my "test-user" accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias()); - assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\"")); + assertTrue(driver.getPageSource().contains("id=\"add-link-" + identityProviderModel.getAlias() + "\"")); // Revoke grant in account mgmt revokeGrant(); diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java index ac897a8e53..5b4898bb8c 100644 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java @@ -55,11 +55,11 @@ public class AccountFederatedIdentityPage extends AbstractAccountPage { } public void clickAddProvider(String providerId) { - driver.findElement(By.id("add-" + providerId)).click(); + driver.findElement(By.id("add-link-" + providerId)).click(); } public void clickRemoveProvider(String providerId) { - driver.findElement(By.id("remove-" + providerId)).click(); + driver.findElement(By.id("remove-link-" + providerId)).click(); } public String getError() { diff --git a/themes/src/main/resources/theme/base/account/federatedIdentity.ftl b/themes/src/main/resources/theme/base/account/federatedIdentity.ftl index 9a901733ce..a9c6d6c6af 100755 --- a/themes/src/main/resources/theme/base/account/federatedIdentity.ftl +++ b/themes/src/main/resources/theme/base/account/federatedIdentity.ftl @@ -7,26 +7,36 @@ -
- <#list federatedIdentity.identities as identity> -
-
- -
-
- -
-
- <#if identity.connected> - <#if federatedIdentity.removeLinkPossible> - ${msg("doRemove")} - - <#else> - ${msg("doAdd")} - -
+
+ <#list federatedIdentity.identities as identity> +
+
+
- - +
+ +
+
+ <#if identity.connected> + <#if federatedIdentity.removeLinkPossible> +
+ + + + +
+ + <#else> +
+ + + + +
+ +
+
+ +
- \ No newline at end of file + diff --git a/themes/src/main/resources/theme/base/account/sessions.ftl b/themes/src/main/resources/theme/base/account/sessions.ftl index 1c0ef1b0e5..5e4441d183 100755 --- a/themes/src/main/resources/theme/base/account/sessions.ftl +++ b/themes/src/main/resources/theme/base/account/sessions.ftl @@ -36,6 +36,9 @@ - ${msg("doLogOutAllSessions")} +
+ + +
diff --git a/themes/src/main/resources/theme/base/account/totp.ftl b/themes/src/main/resources/theme/base/account/totp.ftl index 4b96dd8b34..f02ef2c0dd 100755 --- a/themes/src/main/resources/theme/base/account/totp.ftl +++ b/themes/src/main/resources/theme/base/account/totp.ftl @@ -14,7 +14,10 @@ ${msg("mobile")} - +
+ + +
diff --git a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css index 22edb493b8..3014bca5aa 100644 --- a/themes/src/main/resources/theme/keycloak/account/resources/css/account.css +++ b/themes/src/main/resources/theme/keycloak/account/resources/css/account.css @@ -53,6 +53,10 @@ header .navbar { padding: 0 30px; } +.margin-bottom { + margin-bottom: 10px; +} + /* Sidebar */ .bs-sidebar { @@ -262,4 +266,4 @@ hr + .form-horizontal { } .kc-dropdown:hover ul{ display:block; -} \ No newline at end of file +}