From dd8a64f30ce20f73b10378608dd5c8d3d9b448a6 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Sat, 25 Mar 2017 11:21:11 -0400 Subject: [PATCH] KEYCLOAK-4664 --- .../resources/IdentityBrokerService.java | 16 +++--- .../testsuite/broker/BrokerTestTools.java | 2 +- .../ClientInitiatedAccountLinkTest.java | 49 +++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) 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 bf3bc9f9f4..fe82c97907 100755 --- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java +++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java @@ -815,7 +815,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal return browserAuthentication(clientCode.getClientSession(), message); } - private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel federatedIdentityModel, UserModel federatedUser) { + private Response performAccountLinking(ClientSessionModel clientSession, BrokeredIdentityContext context, FederatedIdentityModel newModel, UserModel federatedUser) { this.event.event(EventType.FEDERATED_IDENTITY_LINK); UserModel authenticatedUser = clientSession.getUserSession().getUser(); @@ -824,10 +824,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal if (authenticatedUser.getId().equals(federatedUser.getId())) { // refresh the token if (context.getIdpConfig().isStoreToken()) { - federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel); - if (!ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) { + FederatedIdentityModel oldModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel); + if (!ObjectUtil.isEqualOrBothNull(context.getToken(), oldModel.getToken())) { - this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel); + this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, newModel); if (isDebugEnabled()) { logger.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias()); @@ -842,7 +842,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal if (isDebugEnabled()) { - logger.debugf("Linking account [%s] from identity provider [%s] to user [%s].", federatedIdentityModel, context.getIdpConfig().getAlias(), authenticatedUser); + logger.debugf("Linking account [%s] from identity provider [%s] to user [%s].", newModel, context.getIdpConfig().getAlias(), authenticatedUser); } if (!authenticatedUser.isEnabled()) { @@ -853,13 +853,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal return redirectToErrorPage(Messages.INSUFFICIENT_PERMISSION); } - this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, federatedIdentityModel); + this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, newModel); context.getIdp().attachUserSession(clientSession.getUserSession(), clientSession, context); this.event.user(authenticatedUser) .detail(Details.USERNAME, authenticatedUser.getUsername()) - .detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider()) - .detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentityModel.getUserName()) + .detail(Details.IDENTITY_PROVIDER, newModel.getIdentityProvider()) + .detail(Details.IDENTITY_PROVIDER_USERNAME, newModel.getUserName()) .success(); return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build(); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java index 40c1f704fa..a43d842c84 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/BrokerTestTools.java @@ -93,12 +93,12 @@ public class BrokerTestTools { public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext, String alias, boolean linkOnly) { IdentityProviderRepresentation idp = createIdentityProvider(alias, IDP_OIDC_PROVIDER_ID); idp.setLinkOnly(linkOnly); + idp.setStoreToken(true); Map config = idp.getConfig(); config.put("clientId", childRealm); config.put("clientSecret", childRealm); - config.put("prompt", "login"); config.put("authorizationUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/auth"); config.put("tokenUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/token"); config.put("logoutUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ClientInitiatedAccountLinkTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ClientInitiatedAccountLinkTest.java index e63912791f..487e8784fa 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ClientInitiatedAccountLinkTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/ClientInitiatedAccountLinkTest.java @@ -29,8 +29,10 @@ import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.common.util.Base64Url; +import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocolService; +import org.keycloak.representations.AccessTokenResponse; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.FederatedIdentityRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; @@ -48,7 +50,12 @@ import org.keycloak.testsuite.pages.AccountFederatedIdentityPage; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.UpdateAccountInformationPage; import org.keycloak.testsuite.util.AdapterServletDeployment; +import org.keycloak.testsuite.util.OAuthClient; +import org.keycloak.util.JsonSerialization; +import javax.ws.rs.client.Client; +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import java.net.URL; import java.util.LinkedList; @@ -82,6 +89,10 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest { @Page protected AppServerContextRoot appServerContextRootPage; + @ArquillianResource + protected OAuthClient oauth; + + public boolean isRelative() { return testContext.isRelativeAdapterTest(); } @@ -122,6 +133,7 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest { uri = appServerContextRootPage.toString() + uri; } servlet.setAdminUrl(uri); + servlet.setDirectAccessGrantsEnabled(true); servlet.setBaseUrl(uri); servlet.setRedirectUris(new LinkedList<>()); servlet.getRedirectUris().add(uri + "/*"); @@ -172,6 +184,11 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest { List roles = new LinkedList<>(); roles.add(role); realm.users().get(childUserId).roles().realmLevel().add(roles); + ClientRepresentation brokerService = realm.clients().findByClientId(Constants.BROKER_SERVICE_CLIENT_ID).get(0); + role = realm.clients().get(brokerService.getId()).roles().get(Constants.READ_TOKEN_ROLE).toRepresentation(); + roles.clear(); + roles.add(role); + realm.users().get(childUserId).roles().clientLevel(brokerService.getId()).add(roles); } @@ -391,6 +408,25 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest { Assert.assertTrue(driver.getCurrentUrl().startsWith(linkBuilder.toTemplate())); Assert.assertTrue(driver.getPageSource().contains("Account Linked")); + OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(CHILD_IDP, "child", "password", null, "client-linking", "password"); + Assert.assertNotNull(response.getAccessToken()); + Assert.assertNull(response.getError()); + Client httpClient = ClientBuilder.newClient(); + String firstToken = getToken(response, httpClient); + Assert.assertNotNull(firstToken); + + + driver.navigate().to(linkUrl); + Assert.assertTrue(driver.getPageSource().contains("Account Linked")); + String nextToken = getToken(response, httpClient); + Assert.assertNotNull(nextToken); + Assert.assertNotEquals(firstToken, nextToken); + + + + + + links = realm.users().get(childUserId).getFederatedIdentity(); Assert.assertFalse(links.isEmpty()); @@ -403,6 +439,19 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest { } + private String getToken(OAuthClient.AccessTokenResponse response, Client httpClient) throws Exception { + String idpToken = httpClient.target(oauth.AUTH_SERVER_ROOT) + .path("realms") + .path("child/broker") + .path(PARENT_IDP) + .path("token") + .request() + .header("Authorization", "Bearer " + response.getAccessToken()) + .get(String.class); + AccessTokenResponse res = JsonSerialization.readValue(idpToken, AccessTokenResponse.class); + return res.getToken(); + } + public void logoutAll() { String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(CHILD_IDP).toString(); driver.navigate().to(logoutUri);