keycloak-uncached
Changes
services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountFederatedIdentityBean.java 20(+3 -17)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java 6(+3 -3)
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java 73(+61 -12)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java 151(+151 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java 2(+1 -1)
Details
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 96f4257..193de28 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<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
Set<FederatedIdentityModel> 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 6ba04fe..9ea898b 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/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 0235120..f9eb1e9 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -376,6 +376,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 cc8abfb..bcc99b6 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<String, String> 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();
@@ -203,32 +209,6 @@ public abstract class AbstractSecuredLocalService {
return oauth.redirect(uriInfo, accountUri.toString());
}
- protected Response authenticateBrowser() {
- AppAuthManager authManager = new AppAuthManager();
- AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm);
- if (authResult != null) {
- auth = new Auth(realm, authResult.getToken(), authResult.getUser(), client, authResult.getSession(), true);
- } else {
- return login(null);
- }
- // don't allow cors requests
- // This is to prevent CSRF attacks.
- String requestOrigin = UriUtils.getOrigin(uriInfo.getBaseUri());
- String origin = headers.getRequestHeaders().getFirst("Origin");
- if (origin != null && !requestOrigin.equals(origin)) {
- throw new ForbiddenException();
- }
-
- if (!request.getHttpMethod().equals("GET")) {
- String referrer = headers.getRequestHeaders().getFirst("Referer");
- if (referrer != null && !requestOrigin.equals(UriUtils.getOrigin(referrer))) {
- throw new ForbiddenException();
- }
- }
- updateCsrfChecks();
- return null;
- }
-
static class OAuthRedirect extends AbstractOAuthClient {
/**
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 9b26b18..3a3a680 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -475,15 +475,15 @@ public class AccountService extends AbstractSecuredLocalService {
}
@Path("totp-remove")
- @GET
- public Response processTotpRemove(@QueryParam("stateChecker") String stateChecker) {
+ @POST
+ public Response processTotpRemove(final MultivaluedMap<String, String> formData) {
if (auth == null) {
return login("totp");
}
require(AccountRoles.MANAGE_ACCOUNT);
- csrfCheck(stateChecker);
+ csrfCheck(formData);
UserModel user = auth.getUser();
session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
@@ -496,14 +496,14 @@ public class AccountService extends AbstractSecuredLocalService {
@Path("sessions-logout")
- @GET
- public Response processSessionsLogout(@QueryParam("stateChecker") String stateChecker) {
+ @POST
+ public Response processSessionsLogout(final MultivaluedMap<String, String> formData) {
if (auth == null) {
return login("sessions");
}
require(AccountRoles.MANAGE_ACCOUNT);
- csrfCheck(stateChecker);
+ csrfCheck(formData);
UserModel user = auth.getUser();
@@ -720,19 +720,21 @@ public class AccountService 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<String, String> formData) {
if (auth == null) {
return login("identity");
}
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/services/src/main/java/org/keycloak/services/Urls.java b/services/src/main/java/org/keycloak/services/Urls.java
index 51f505e..1d73cf6 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(AccountService.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(AccountService.class, "processTotpRemove")
- .queryParam("stateChecker", stateChecker)
.build(realmName);
}
@@ -140,9 +139,8 @@ public class Urls {
return accountBase(baseUri).path(AccountService.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(AccountService.class, "processSessionsLogout")
- .queryParam("stateChecker", stateChecker)
.build(realmName);
}
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
index 781714a..fe8d78b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java
@@ -422,7 +422,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();
@@ -436,11 +436,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/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
index ac897a8..5b4898b 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountFederatedIdentityPage.java
+++ b/testsuite/integration/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/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 9ca2372..7992ccf 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 <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@@ -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 List<FederatedIdentity> getIdentities() {
+ List<FederatedIdentity> 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 WebElement findRemoveProviderButton(String alias) {
- return driver.findElement(By.id("remove-" + alias));
+
+ public WebElement findAddProvider(String providerId) {
+ return driver.findElement(By.id("add-link-" + providerId));
}
- public void clickAddProvider(String alias) {
- WebElement addButton = findAddProviderButton(alias);
- addButton.click();
+ public void clickAddProvider(String providerId) {
+ findAddProvider(providerId).click();
}
- public void clickRemoveProvider(String alias) {
- WebElement addButton = findRemoveProviderButton(alias);
- addButton.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 0000000..7403073
--- /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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ * @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<ClientRepresentation> 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<AccountFederatedIdentityPage.FederatedIdentity> 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/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index c69f489..de2fb90 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -983,7 +983,7 @@ public class AccountTest 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 f08b4bf..7c756fa 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/themes/src/main/resources/theme/base/account/federatedIdentity.ftl b/themes/src/main/resources/theme/base/account/federatedIdentity.ftl
index 9a90173..a9c6d6c 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 @@
</div>
</div>
- <form action="${url.passwordUrl}" class="form-horizontal" method="post">
- <#list federatedIdentity.identities as identity>
- <div class="form-group">
- <div class="col-sm-2 col-md-2">
- <label for="${identity.providerId!}" class="control-label">${identity.displayName!}</label>
- </div>
- <div class="col-sm-5 col-md-5">
- <input disabled="true" class="form-control" value="${identity.userName!}">
- </div>
- <div class="col-sm-5 col-md-5">
- <#if identity.connected>
- <#if federatedIdentity.removeLinkPossible>
- <a href="${identity.actionUrl}" type="submit" id="remove-${identity.providerId!}" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}">${msg("doRemove")}</a>
- </#if>
- <#else>
- <a href="${identity.actionUrl}" type="submit" id="add-${identity.providerId!}" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}">${msg("doAdd")}</a>
+ <div id="federated-identities">
+ <#list federatedIdentity.identities as identity>
+ <div class="row margin-bottom">
+ <div class="col-sm-2 col-md-2">
+ <label for="${identity.providerId!}" class="control-label">${identity.displayName!}</label>
+ </div>
+ <div class="col-sm-5 col-md-5">
+ <input disabled="true" class="form-control" value="${identity.userName!}">
+ </div>
+ <div class="col-sm-5 col-md-5">
+ <#if identity.connected>
+ <#if federatedIdentity.removeLinkPossible>
+ <form action="${url.socialUrl}" method="post" class="form-inline">
+ <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
+ <input type="hidden" id="action" name="action" value="remove">
+ <input type="hidden" id="providerId" name="providerId" value="${identity.providerId!}">
+ <button id="remove-link-${identity.providerId!}" class="btn btn-default">${msg("doRemove")}</button>
+ </form>
</#if>
- </div>
+ <#else>
+ <form action="${url.socialUrl}" method="post" class="form-inline">
+ <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
+ <input type="hidden" id="action" name="action" value="add">
+ <input type="hidden" id="providerId" name="providerId" value="${identity.providerId!}">
+ <button id="add-link-${identity.providerId!}" class="btn btn-default">${msg("doAdd")}</button>
+ </form>
+ </#if>
</div>
- </#list>
- </form>
+ </div>
+ </#list>
+ </div>
-</@layout.mainLayout>
\ No newline at end of file
+</@layout.mainLayout>
diff --git a/themes/src/main/resources/theme/base/account/sessions.ftl b/themes/src/main/resources/theme/base/account/sessions.ftl
index 1c0ef1b..5e4441d 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 @@
</table>
- <a id="logout-all-sessions" href="${url.sessionsLogoutUrl}">${msg("doLogOutAllSessions")}</a>
+ <form action="${url.sessionsLogoutUrl}" method="post">
+ <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
+ <button id="logout-all-sessions" class="btn btn-default">${msg("doLogOutAllSessions")}</button>
+ </form>
</@layout.mainLayout>
diff --git a/themes/src/main/resources/theme/base/account/totp.ftl b/themes/src/main/resources/theme/base/account/totp.ftl
index 4b96dd8..f02ef2c 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 @@
<tr>
<td class="provider">${msg("mobile")}</td>
<td class="action">
- <a id="remove-mobile" href="${url.totpRemoveUrl}"><i class="pficon pficon-delete"></i></a>
+ <form action="${url.totpRemoveUrl}" method="post" class="form-inline">
+ <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker?html}">
+ <button id="remove-mobile" class="btn btn-default"><i class="pficon pficon-delete"></i></button>
+ </form>
</td>
</tr>
</tbody>
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 22edb49..3014bca 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
+}