KEYCLOAK-4664
This commit is contained in:
parent
3ce8da0126
commit
dd8a64f30c
3 changed files with 58 additions and 9 deletions
|
@ -815,7 +815,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
return browserAuthentication(clientCode.getClientSession(), message);
|
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);
|
this.event.event(EventType.FEDERATED_IDENTITY_LINK);
|
||||||
|
|
||||||
UserModel authenticatedUser = clientSession.getUserSession().getUser();
|
UserModel authenticatedUser = clientSession.getUserSession().getUser();
|
||||||
|
@ -824,10 +824,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
if (authenticatedUser.getId().equals(federatedUser.getId())) {
|
if (authenticatedUser.getId().equals(federatedUser.getId())) {
|
||||||
// refresh the token
|
// refresh the token
|
||||||
if (context.getIdpConfig().isStoreToken()) {
|
if (context.getIdpConfig().isStoreToken()) {
|
||||||
federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
FederatedIdentityModel oldModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
||||||
if (!ObjectUtil.isEqualOrBothNull(context.getToken(), federatedIdentityModel.getToken())) {
|
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()) {
|
if (isDebugEnabled()) {
|
||||||
logger.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias());
|
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()) {
|
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()) {
|
if (!authenticatedUser.isEnabled()) {
|
||||||
|
@ -853,13 +853,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
return redirectToErrorPage(Messages.INSUFFICIENT_PERMISSION);
|
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);
|
context.getIdp().attachUserSession(clientSession.getUserSession(), clientSession, context);
|
||||||
|
|
||||||
this.event.user(authenticatedUser)
|
this.event.user(authenticatedUser)
|
||||||
.detail(Details.USERNAME, authenticatedUser.getUsername())
|
.detail(Details.USERNAME, authenticatedUser.getUsername())
|
||||||
.detail(Details.IDENTITY_PROVIDER, federatedIdentityModel.getIdentityProvider())
|
.detail(Details.IDENTITY_PROVIDER, newModel.getIdentityProvider())
|
||||||
.detail(Details.IDENTITY_PROVIDER_USERNAME, federatedIdentityModel.getUserName())
|
.detail(Details.IDENTITY_PROVIDER_USERNAME, newModel.getUserName())
|
||||||
.success();
|
.success();
|
||||||
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
|
return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,12 +93,12 @@ public class BrokerTestTools {
|
||||||
public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext, String alias, boolean linkOnly) {
|
public static void createKcOidcBroker(Keycloak adminClient, String childRealm, String idpRealm, SuiteContext suiteContext, String alias, boolean linkOnly) {
|
||||||
IdentityProviderRepresentation idp = createIdentityProvider(alias, IDP_OIDC_PROVIDER_ID);
|
IdentityProviderRepresentation idp = createIdentityProvider(alias, IDP_OIDC_PROVIDER_ID);
|
||||||
idp.setLinkOnly(linkOnly);
|
idp.setLinkOnly(linkOnly);
|
||||||
|
idp.setStoreToken(true);
|
||||||
|
|
||||||
Map<String, String> config = idp.getConfig();
|
Map<String, String> config = idp.getConfig();
|
||||||
|
|
||||||
config.put("clientId", childRealm);
|
config.put("clientId", childRealm);
|
||||||
config.put("clientSecret", childRealm);
|
config.put("clientSecret", childRealm);
|
||||||
config.put("prompt", "login");
|
|
||||||
config.put("authorizationUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/auth");
|
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("tokenUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/token");
|
||||||
config.put("logoutUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout");
|
config.put("logoutUrl", getAuthRoot(suiteContext) + "/auth/realms/" + idpRealm + "/protocol/openid-connect/logout");
|
||||||
|
|
|
@ -29,8 +29,10 @@ import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||||
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
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.LoginPage;
|
||||||
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
|
||||||
import org.keycloak.testsuite.util.AdapterServletDeployment;
|
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 javax.ws.rs.core.UriBuilder;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -82,6 +89,10 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
|
||||||
@Page
|
@Page
|
||||||
protected AppServerContextRoot appServerContextRootPage;
|
protected AppServerContextRoot appServerContextRootPage;
|
||||||
|
|
||||||
|
@ArquillianResource
|
||||||
|
protected OAuthClient oauth;
|
||||||
|
|
||||||
|
|
||||||
public boolean isRelative() {
|
public boolean isRelative() {
|
||||||
return testContext.isRelativeAdapterTest();
|
return testContext.isRelativeAdapterTest();
|
||||||
}
|
}
|
||||||
|
@ -122,6 +133,7 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
|
||||||
uri = appServerContextRootPage.toString() + uri;
|
uri = appServerContextRootPage.toString() + uri;
|
||||||
}
|
}
|
||||||
servlet.setAdminUrl(uri);
|
servlet.setAdminUrl(uri);
|
||||||
|
servlet.setDirectAccessGrantsEnabled(true);
|
||||||
servlet.setBaseUrl(uri);
|
servlet.setBaseUrl(uri);
|
||||||
servlet.setRedirectUris(new LinkedList<>());
|
servlet.setRedirectUris(new LinkedList<>());
|
||||||
servlet.getRedirectUris().add(uri + "/*");
|
servlet.getRedirectUris().add(uri + "/*");
|
||||||
|
@ -172,6 +184,11 @@ public class ClientInitiatedAccountLinkTest extends AbstractKeycloakTest {
|
||||||
List<RoleRepresentation> roles = new LinkedList<>();
|
List<RoleRepresentation> roles = new LinkedList<>();
|
||||||
roles.add(role);
|
roles.add(role);
|
||||||
realm.users().get(childUserId).roles().realmLevel().add(roles);
|
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.getCurrentUrl().startsWith(linkBuilder.toTemplate()));
|
||||||
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
|
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();
|
links = realm.users().get(childUserId).getFederatedIdentity();
|
||||||
Assert.assertFalse(links.isEmpty());
|
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() {
|
public void logoutAll() {
|
||||||
String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(CHILD_IDP).toString();
|
String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder()).build(CHILD_IDP).toString();
|
||||||
driver.navigate().to(logoutUri);
|
driver.navigate().to(logoutUri);
|
||||||
|
|
Loading…
Reference in a new issue