From 98ad9b7e7c8b422447ca4ae7602d001240811b3d Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 11 Apr 2016 22:51:05 +0200 Subject: [PATCH 1/2] KEYCLOAK-2801 Redirected to login theme error page after failed social linking from account management --- .../admin/client/resource/UserResource.java | 11 + .../keycloak/services/messages/Messages.java | 2 + .../resources/IdentityBrokerService.java | 17 +- .../OIDCKeyCloakServerBrokerBasicTest.java | 82 ------- ...DCKeycloakServerBrokerWithConsentTest.java | 227 ++++++++++++++++++ .../account/messages/messages_en.properties | 1 + 6 files changed, 257 insertions(+), 83 deletions(-) create mode 100644 testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java index 51e9a459bd..b02dd0d9b2 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java @@ -17,6 +17,7 @@ package org.keycloak.admin.client.resource; +import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.GroupRepresentation; @@ -35,6 +36,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; +import java.util.Map; /** * @author rodrigo.sasaki@icarros.com.br @@ -132,4 +134,13 @@ public interface UserResource { @Path("role-mappings") public RoleMappingResource roles(); + + @GET + @Path("consents") + public List> getConsents(); + + @DELETE + @Path("consents/{client}") + public void revokeConsent(@PathParam("client") String clientId); + } diff --git a/services/src/main/java/org/keycloak/services/messages/Messages.java b/services/src/main/java/org/keycloak/services/messages/Messages.java index c585c62c13..7fae66d96b 100755 --- a/services/src/main/java/org/keycloak/services/messages/Messages.java +++ b/services/src/main/java/org/keycloak/services/messages/Messages.java @@ -156,6 +156,8 @@ public class Messages { public static final String STALE_CODE = "staleCodeMessage"; + public static final String STALE_CODE_ACCOUNT = "staleCodeAccountMessage"; + public static final String IDENTITY_PROVIDER_NOT_UNIQUE = "identityProviderNotUniqueMessage"; public static final String REALM_SUPPORTS_NO_CREDENTIALS = "realmSupportsNoCredentialsMessage"; diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index c0cdb59a5b..5218983643 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -634,7 +634,22 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal if (!clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) { logger.debugf("Authorization code is not valid. Client session ID: %s, Client session's action: %s", clientSession.getId(), clientSession.getAction()); - Response staleCodeError = redirectToErrorPage(Messages.STALE_CODE); + + Response staleCodeError; + + // Linking identityProvider from account mgmt + if (clientSession.getUserSession() != null && client.getClientId().equals(ACCOUNT_MANAGEMENT_CLIENT_ID)) { + + this.event.event(EventType.FEDERATED_IDENTITY_LINK); + UserModel user = clientSession.getUserSession().getUser(); + this.event.user(user); + this.event.detail(Details.USERNAME, user.getUsername()); + + staleCodeError = redirectToAccountErrorPage(clientSession, Messages.STALE_CODE_ACCOUNT); + } else { + staleCodeError = redirectToErrorPage(Messages.STALE_CODE); + } + return ParsedCodeContext.response(staleCodeError); } 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 4d72ea8489..446ad5a3af 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 @@ -146,88 +146,6 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractKeycloakIdentityP keycloak.realm("realm-with-broker").identityProviders().get("kc-oidc-idp").update(idp); } - - @Test - public void testConsentDeniedWithExpiredClientSession() throws Exception { - // Disable update profile - setUpdateProfileFirstLogin(IdentityProviderRepresentation.UPFLM_OFF); - - Keycloak keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); - Keycloak keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); - - // Require broker to show consent screen - RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); - List clients = brokeredRealm.clients().findByClientId("broker-app"); - Assert.assertEquals(1, clients.size()); - ClientRepresentation brokerApp = clients.get(0); - brokerApp.setConsentRequired(true); - brokeredRealm.clients().get(brokerApp.getId()).update(brokerApp); - - // Change timeouts on realm-with-broker to lower values - RealmResource realmWithBroker = keycloak1.realm("realm-with-broker"); - RealmRepresentation realmBackup = realmWithBroker.toRepresentation(); - RealmRepresentation realm = realmWithBroker.toRepresentation(); - realm.setAccessCodeLifespanLogin(30);; - realm.setAccessCodeLifespan(30); - realm.setAccessCodeLifespanUserAction(30); - realmWithBroker.update(realm); - - // Login to broker - loginIDP("test-user"); - - // Set time offset - Time.setOffset(60); - try { - // User rejected consent - grantPage.assertCurrent(); - grantPage.cancel(); - - // Assert error page with backToApplication link displayed - errorPage.assertCurrent(); - errorPage.clickBackToApplication(); - - assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth")); - - - // Login to broker again - loginIDP("test-user"); - - // Set time offset and manually remove expiredSessions TODO: Will require custom endpoint when migrate to integration-arquillian - Time.setOffset(120); - - brokerServerRule.stopSession(this.session, true); - this.session = brokerServerRule.startSession(); - - session.sessions().removeExpired(getRealm()); - - brokerServerRule.stopSession(this.session, true); - this.session = brokerServerRule.startSession(); - - // User rejected consent - grantPage.assertCurrent(); - grantPage.cancel(); - - // Assert error page without backToApplication link (clientSession expired and was removed on the server) - errorPage.assertCurrent(); - try { - errorPage.clickBackToApplication(); - fail("Not expected to have link backToApplication available"); - } catch (NoSuchElementException nsee) { - // Expected; - } - - } finally { - Time.setOffset(0); - } - - // Revert consent - brokerApp.setConsentRequired(false); - brokeredRealm.clients().get(brokerApp.getId()).update(brokerApp); - - // Revert timeouts - realmWithBroker.update(realmBackup); - } - @Test public void testSuccessfulAuthenticationWithoutUpdateProfile() { super.testSuccessfulAuthenticationWithoutUpdateProfile(); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java new file mode 100644 index 0000000000..c2606e3c0d --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java @@ -0,0 +1,227 @@ +/* + * 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 java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.admin.client.Keycloak; +import org.keycloak.admin.client.resource.ClientResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.common.util.Time; +import org.keycloak.models.IdentityProviderModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.testsuite.KeycloakServer; +import org.keycloak.testsuite.pages.AccountFederatedIdentityPage; +import org.keycloak.testsuite.rule.AbstractKeycloakRule; +import org.keycloak.testsuite.rule.WebResource; +import org.openqa.selenium.NoSuchElementException; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * @author Marek Posolda + */ +public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityProviderTest { + + private static final int PORT = 8082; + + private static Keycloak keycloak1; + private static Keycloak keycloak2; + + @ClassRule + public static AbstractKeycloakRule oidcServerRule = 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")); + + // Disable update profile + RealmModel realm = getRealm(session); + setUpdateProfileFirstLogin(realm, IdentityProviderRepresentation.UPFLM_OFF); + } + + @Override + protected String[] getTestRealms() { + return new String[] { "realm-with-oidc-identity-provider" }; + } + }; + + + @BeforeClass + public static void before() { + keycloak1 = Keycloak.getInstance("http://localhost:8081/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); + keycloak2 = Keycloak.getInstance("http://localhost:8082/auth", "master", "admin", "admin", org.keycloak.models.Constants.ADMIN_CLI_CLIENT_ID); + + // Require broker to show consent screen + RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); + List clients = brokeredRealm.clients().findByClientId("broker-app"); + Assert.assertEquals(1, clients.size()); + ClientRepresentation brokerApp = clients.get(0); + brokerApp.setConsentRequired(true); + brokeredRealm.clients().get(brokerApp.getId()).update(brokerApp); + + + // Change timeouts on realm-with-broker to lower values + RealmResource realmWithBroker = keycloak1.realm("realm-with-broker"); + RealmRepresentation realmRep = realmWithBroker.toRepresentation(); + realmRep.setAccessCodeLifespanLogin(30);; + realmRep.setAccessCodeLifespan(30); + realmRep.setAccessCodeLifespanUserAction(30); + realmWithBroker.update(realmRep); + } + + + @Override + protected String getProviderId() { + return "kc-oidc-idp"; + } + + + // KEYCLOAK-2769 + @Test + public void testConsentDeniedWithExpiredClientSession() throws Exception { + // Login to broker + loginIDP("test-user"); + + // Set time offset + Time.setOffset(60); + try { + // User rejected consent + grantPage.assertCurrent(); + grantPage.cancel(); + + // Assert error page with backToApplication link displayed + errorPage.assertCurrent(); + errorPage.clickBackToApplication(); + + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth")); + + } finally { + Time.setOffset(0); + } + } + + + // KEYCLOAK-2769 + @Test + public void testConsentDeniedWithExpiredAndClearedClientSession() throws Exception { + // Login to broker again + loginIDP("test-user"); + + // Set time offset + Time.setOffset(60); + try { + // Manually remove expiredSessions TODO: Will require custom endpoint when migrate to integration-arquillian + brokerServerRule.stopSession(this.session, true); + this.session = brokerServerRule.startSession(); + + session.sessions().removeExpired(getRealm()); + + brokerServerRule.stopSession(this.session, true); + this.session = brokerServerRule.startSession(); + + // User rejected consent + grantPage.assertCurrent(); + grantPage.cancel(); + + // Assert error page without backToApplication link (clientSession expired and was removed on the server) + errorPage.assertCurrent(); + try { + errorPage.clickBackToApplication(); + fail("Not expected to have link backToApplication available"); + } catch (NoSuchElementException nsee) { + // Expected; + } + + } finally { + Time.setOffset(0); + } + } + + + // KEYCLOAK-2801 + @Test + public void testAccountManagementLinkingAndExpiredClientSession() throws Exception { + // 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 + accountFederatedIdentityPage.clickAddProvider(getProviderId()); + + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); + this.loginPage.login("test-user", "password"); + + // Set time offset + Time.setOffset(60); + try { + // User rejected consent + grantPage.assertCurrent(); + grantPage.cancel(); + + // Assert account error page with "staleCodeAccount" error displayed + accountFederatedIdentityPage.assertCurrent(); + Assert.assertEquals("The page expired. Please try one more time", accountFederatedIdentityPage.getError()); + + + // Try to link one more time + accountFederatedIdentityPage.clickAddProvider(getProviderId()); + + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); + this.loginPage.login("test-user", "password"); + + Time.setOffset(120); + + // User granted consent + grantPage.assertCurrent(); + grantPage.accept(); + + // Assert account error page with "staleCodeAccount" error displayed + accountFederatedIdentityPage.assertCurrent(); + Assert.assertEquals("The page expired. Please try one more time", accountFederatedIdentityPage.getError()); + + } finally { + Time.setOffset(0); + + // Revoke consent + RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); + List users = brokeredRealm.users().search("test-user", 0, 1); + brokeredRealm.users().get(users.get(0).getId()).revokeConsent("broker-app"); + } + } +} diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties index 5803146529..27a4565ba9 100755 --- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -135,6 +135,7 @@ federatedIdentityRemovingLastProviderMessage=You can''t remove last federated id identityProviderRedirectErrorMessage=Failed to redirect to identity provider. identityProviderRemovedMessage=Identity provider removed successfully. identityProviderAlreadyLinkedMessage=Federated identity returned by {0} is already linked to another user. +staleCodeAccountMessage=The page expired. Please try one more time accountDisabledMessage=Account is disabled, contact admin. From e4f75409c96183c9a4855db07796059d01725e64 Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 11 Apr 2016 23:28:16 +0200 Subject: [PATCH 2/2] KEYCLOAK-2802 NPE during identity broker cancelled from account mgmt --- .../admin/client/resource/UserResource.java | 1 - .../resources/IdentityBrokerService.java | 40 +++++++---- .../OIDCKeyCloakServerBrokerBasicTest.java | 10 --- ...DCKeycloakServerBrokerWithConsentTest.java | 71 ++++++++++++++----- .../account/messages/messages_en.properties | 3 +- 5 files changed, 83 insertions(+), 42 deletions(-) diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java index b02dd0d9b2..df13c0b756 100755 --- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java +++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java @@ -17,7 +17,6 @@ package org.keycloak.admin.client.resource; -import org.jboss.resteasy.annotations.cache.NoCache; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.GroupRepresentation; diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java index 5218983643..f0073287ef 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -543,6 +543,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal } ClientSessionCode clientCode = parsedCode.clientSessionCode; + Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), Messages.CONSENT_DENIED); + if (accountManagementFailedLinking != null) { + return accountManagementFailedLinking; + } + return browserAuthentication(clientCode.getClientSession(), null); } @@ -554,6 +559,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal } ClientSessionCode clientCode = parsedCode.clientSessionCode; + Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), message); + if (accountManagementFailedLinking != null) { + return accountManagementFailedLinking; + } + return browserAuthentication(clientCode.getClientSession(), message); } @@ -635,20 +645,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal if (!clientCode.isValid(AUTHENTICATE.name(), ClientSessionCode.ActionType.LOGIN)) { logger.debugf("Authorization code is not valid. Client session ID: %s, Client session's action: %s", clientSession.getId(), clientSession.getAction()); - Response staleCodeError; + // Check if error happened during login or during linking from account management + Response accountManagementFailedLinking = checkAccountManagementFailedLinking(clientCode.getClientSession(), Messages.STALE_CODE_ACCOUNT); + Response staleCodeError = (accountManagementFailedLinking != null) ? accountManagementFailedLinking : redirectToErrorPage(Messages.STALE_CODE); - // Linking identityProvider from account mgmt - if (clientSession.getUserSession() != null && client.getClientId().equals(ACCOUNT_MANAGEMENT_CLIENT_ID)) { - - this.event.event(EventType.FEDERATED_IDENTITY_LINK); - UserModel user = clientSession.getUserSession().getUser(); - this.event.user(user); - this.event.detail(Details.USERNAME, user.getUsername()); - - staleCodeError = redirectToAccountErrorPage(clientSession, Messages.STALE_CODE_ACCOUNT); - } else { - staleCodeError = redirectToErrorPage(Messages.STALE_CODE); - } return ParsedCodeContext.response(staleCodeError); } @@ -666,6 +666,20 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal return ParsedCodeContext.response(staleCodeError); } + private Response checkAccountManagementFailedLinking(ClientSessionModel clientSession, String error, Object... parameters) { + if (clientSession.getUserSession() != null && clientSession.getClient() != null && clientSession.getClient().getClientId().equals(ACCOUNT_MANAGEMENT_CLIENT_ID)) { + + this.event.event(EventType.FEDERATED_IDENTITY_LINK); + UserModel user = clientSession.getUserSession().getUser(); + this.event.user(user); + this.event.detail(Details.USERNAME, user.getUsername()); + + return redirectToAccountErrorPage(clientSession, error, parameters); + } else { + return null; + } + } + private AuthenticationRequest createAuthenticationRequest(String providerId, ClientSessionCode clientSessionCode) { ClientSessionModel clientSession = null; String relayState = null; 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 446ad5a3af..e0f3c54e7b 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 @@ -17,39 +17,29 @@ package org.keycloak.testsuite.broker; -import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.common.util.Time; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; import org.keycloak.representations.AccessTokenResponse; -import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.Urls; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.Constants; import org.keycloak.testsuite.pages.AccountApplicationsPage; -import org.keycloak.testsuite.pages.OAuthGrantPage; import org.keycloak.testsuite.rule.AbstractKeycloakRule; -import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.KeycloakServer; import org.keycloak.util.JsonSerialization; import org.openqa.selenium.NoSuchElementException; import java.io.IOException; -import java.util.List; import javax.ws.rs.core.UriBuilder; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java index c2606e3c0d..d648cdd538 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/OIDCKeycloakServerBrokerWithConsentTest.java @@ -20,15 +20,12 @@ package org.keycloak.testsuite.broker; import java.util.List; import org.junit.Assert; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; import org.keycloak.admin.client.Keycloak; -import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.Time; -import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.representations.idm.ClientRepresentation; @@ -37,9 +34,7 @@ import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.KeycloakServer; -import org.keycloak.testsuite.pages.AccountFederatedIdentityPage; import org.keycloak.testsuite.rule.AbstractKeycloakRule; -import org.keycloak.testsuite.rule.WebResource; import org.openqa.selenium.NoSuchElementException; import static org.junit.Assert.assertTrue; @@ -175,11 +170,7 @@ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityPro @Test public void testAccountManagementLinkingAndExpiredClientSession() throws Exception { // 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()); + loginToAccountManagement("pedroigor"); // Link my "pedroigor" identity with "test-user" from brokered Keycloak accountFederatedIdentityPage.clickAddProvider(getProviderId()); @@ -196,7 +187,7 @@ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityPro // Assert account error page with "staleCodeAccount" error displayed accountFederatedIdentityPage.assertCurrent(); - Assert.assertEquals("The page expired. Please try one more time", accountFederatedIdentityPage.getError()); + Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError()); // Try to link one more time @@ -213,15 +204,61 @@ public class OIDCKeycloakServerBrokerWithConsentTest extends AbstractIdentityPro // Assert account error page with "staleCodeAccount" error displayed accountFederatedIdentityPage.assertCurrent(); - Assert.assertEquals("The page expired. Please try one more time", accountFederatedIdentityPage.getError()); + Assert.assertEquals("The page expired. Please try one more time.", accountFederatedIdentityPage.getError()); } finally { Time.setOffset(0); - - // Revoke consent - RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); - List users = brokeredRealm.users().search("test-user", 0, 1); - brokeredRealm.users().get(users.get(0).getId()).revokeConsent("broker-app"); } + + // Revoke consent + RealmResource brokeredRealm = keycloak2.realm("realm-with-oidc-identity-provider"); + List users = brokeredRealm.users().search("test-user", 0, 1); + brokeredRealm.users().get(users.get(0).getId()).revokeConsent("broker-app"); + } + + + @Test + public void testLoginCancelConsent() throws Exception { + // Try to login + loginIDP("test-user"); + + // User rejected consent + grantPage.assertCurrent(); + grantPage.cancel(); + + // Assert back on login page + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/")); + assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); + } + + + // KEYCLOAK-2802 + @Test + public void testAccountManagementLinkingCancelConsent() throws Exception { + // Login as pedroigor to account management + loginToAccountManagement("pedroigor"); + + // Link my "pedroigor" identity with "test-user" from brokered Keycloak + accountFederatedIdentityPage.clickAddProvider(getProviderId()); + + assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/")); + this.loginPage.login("test-user", "password"); + + // User rejected consent + grantPage.assertCurrent(); + grantPage.cancel(); + + // Assert account error page with "consentDenied" error displayed + accountFederatedIdentityPage.assertCurrent(); + Assert.assertEquals("Consent denied.", accountFederatedIdentityPage.getError()); + } + + + private void loginToAccountManagement(String username) { + accountFederatedIdentityPage.realm("realm-with-broker"); + accountFederatedIdentityPage.open(); + assertTrue(driver.getTitle().equals("Log in to realm-with-broker")); + loginPage.login(username, "password"); + assertTrue(accountFederatedIdentityPage.isCurrent()); } } diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties index 27a4565ba9..8c0727fd79 100755 --- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties @@ -135,7 +135,8 @@ federatedIdentityRemovingLastProviderMessage=You can''t remove last federated id identityProviderRedirectErrorMessage=Failed to redirect to identity provider. identityProviderRemovedMessage=Identity provider removed successfully. identityProviderAlreadyLinkedMessage=Federated identity returned by {0} is already linked to another user. -staleCodeAccountMessage=The page expired. Please try one more time +staleCodeAccountMessage=The page expired. Please try one more time. +consentDenied=Consent denied. accountDisabledMessage=Account is disabled, contact admin.