Running mappers during account linking
Closes #11195 Co-authored-by: mposolda <mposolda@gmail.com> Co-authored-by: toddkazakov
This commit is contained in:
parent
41e253c054
commit
aff6cc1cbd
2 changed files with 91 additions and 33 deletions
|
@ -950,7 +950,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, newModel);
|
this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, newModel);
|
||||||
|
federatedUser = authenticatedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFederatedIdentity(context, federatedUser);
|
||||||
|
|
||||||
context.getIdp().authenticationFinished(authSession, context);
|
context.getIdp().authenticationFinished(authSession, context);
|
||||||
|
|
||||||
AuthenticationManager.setClientScopesInSession(authSession);
|
AuthenticationManager.setClientScopesInSession(authSession);
|
||||||
|
|
|
@ -25,24 +25,27 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.broker.provider.HardcodedAttributeMapper;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
|
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
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.IdentityProviderMapperRepresentation;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
import org.keycloak.services.resources.LoginActionsService;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
|
||||||
import org.keycloak.testsuite.ActionURIUtils;
|
import org.keycloak.testsuite.ActionURIUtils;
|
||||||
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.DisableFeature;
|
|
||||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||||
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
import org.keycloak.testsuite.utils.arquillian.ContainerConstants;
|
||||||
import org.keycloak.testsuite.broker.BrokerTestTools;
|
import org.keycloak.testsuite.broker.BrokerTestTools;
|
||||||
|
@ -61,7 +64,9 @@ import java.net.URL;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
|
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
|
||||||
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
|
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT_LINKS;
|
||||||
|
@ -82,6 +87,9 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
|
||||||
public static final String CHILD_IDP = "child";
|
public static final String CHILD_IDP = "child";
|
||||||
public static final String PARENT_IDP = "parent-idp";
|
public static final String PARENT_IDP = "parent-idp";
|
||||||
public static final String PARENT_USERNAME = "parent";
|
public static final String PARENT_USERNAME = "parent";
|
||||||
|
private static final String HARDCODED_ATTRIBUTE_MAPPER_NAME = "my_hardcoded_mapper";
|
||||||
|
private static final String USER_ATTRIBUTE = "hardcoded_attribute";
|
||||||
|
private static final String USER_ATTRIBUTE_VALUE = "hardcoded_value";
|
||||||
|
|
||||||
@Page
|
@Page
|
||||||
protected LoginUpdateProfilePage loginUpdateProfilePage;
|
protected LoginUpdateProfilePage loginUpdateProfilePage;
|
||||||
|
@ -383,26 +391,7 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAccountLink() throws Exception {
|
public void testAccountLink() throws Exception {
|
||||||
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
|
linkAccount();
|
||||||
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
|
|
||||||
Assert.assertTrue(links.isEmpty());
|
|
||||||
|
|
||||||
UriBuilder linkBuilder = UriBuilder.fromUri(appPage.getInjectedUrl().toString())
|
|
||||||
.path("link");
|
|
||||||
String linkUrl = linkBuilder.clone()
|
|
||||||
.queryParam("realm", CHILD_IDP)
|
|
||||||
.queryParam("provider", PARENT_IDP).build().toString();
|
|
||||||
System.out.println("linkUrl: " + linkUrl);
|
|
||||||
navigateTo(linkUrl);
|
|
||||||
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
|
|
||||||
Assert.assertTrue(driver.getPageSource().contains(PARENT_IDP));
|
|
||||||
loginPage.login("child", "password");
|
|
||||||
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
|
|
||||||
loginPage.login(PARENT_USERNAME, "password");
|
|
||||||
System.out.println("After linking: " + driver.getCurrentUrl());
|
|
||||||
System.out.println(driver.getPageSource());
|
|
||||||
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");
|
OAuthClient.AccessTokenResponse response = oauth.doGrantAccessTokenRequest(CHILD_IDP, "child", "password", null, "client-linking", "password");
|
||||||
Assert.assertNotNull(response.getAccessToken());
|
Assert.assertNotNull(response.getAccessToken());
|
||||||
|
@ -411,19 +400,14 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
|
||||||
String firstToken = getToken(response, httpClient);
|
String firstToken = getToken(response, httpClient);
|
||||||
Assert.assertNotNull(firstToken);
|
Assert.assertNotNull(firstToken);
|
||||||
|
|
||||||
|
navigateToAccountLinkPage();
|
||||||
navigateTo(linkUrl);
|
|
||||||
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
|
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
|
||||||
String nextToken = getToken(response, httpClient);
|
String nextToken = getToken(response, httpClient);
|
||||||
Assert.assertNotNull(nextToken);
|
Assert.assertNotNull(nextToken);
|
||||||
Assert.assertNotEquals(firstToken, nextToken);
|
Assert.assertNotEquals(firstToken, nextToken);
|
||||||
|
|
||||||
|
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
|
||||||
|
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
links = realm.users().get(childUserId).getFederatedIdentity();
|
|
||||||
Assert.assertFalse(links.isEmpty());
|
Assert.assertFalse(links.isEmpty());
|
||||||
|
|
||||||
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
|
realm.users().get(childUserId).removeFederatedIdentity(PARENT_IDP);
|
||||||
|
@ -431,8 +415,6 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
|
||||||
Assert.assertTrue(links.isEmpty());
|
Assert.assertTrue(links.isEmpty());
|
||||||
|
|
||||||
logoutAll();
|
logoutAll();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove this once DYNAMIC_SCOPES feature is enabled by default
|
// TODO remove this once DYNAMIC_SCOPES feature is enabled by default
|
||||||
|
@ -590,6 +572,78 @@ public class ClientInitiatedAccountLinkTest extends AbstractServletsAdapterTest
|
||||||
WaitUtils.waitForPageToLoad();
|
WaitUtils.waitForPageToLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountLinkWithHardcodedMapper() throws Exception {
|
||||||
|
driver.manage().timeouts().pageLoadTimeout(1, TimeUnit.DAYS);
|
||||||
|
addHardcodedAttributeMapper();
|
||||||
|
linkAccount();
|
||||||
|
assertHardcodedAttributeHasBeenAssigned();
|
||||||
|
removeHardcodedAttribute();
|
||||||
|
removeHardcodedAttributeMapper();
|
||||||
|
logoutAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHardcodedAttributeMapper() {
|
||||||
|
IdentityProviderResource provider = adminClient.realms().realm(CHILD_IDP).identityProviders().get(PARENT_IDP);
|
||||||
|
|
||||||
|
IdentityProviderMapperRepresentation mapper = new IdentityProviderMapperRepresentation();
|
||||||
|
mapper.setIdentityProviderAlias(PARENT_IDP);
|
||||||
|
mapper.setName(HARDCODED_ATTRIBUTE_MAPPER_NAME);
|
||||||
|
mapper.setIdentityProviderMapper("hardcoded-attribute-idp-mapper");
|
||||||
|
mapper.setConfig(Map.of(
|
||||||
|
IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.FORCE.toString(),
|
||||||
|
HardcodedAttributeMapper.ATTRIBUTE, USER_ATTRIBUTE,
|
||||||
|
HardcodedAttributeMapper.ATTRIBUTE_VALUE, USER_ATTRIBUTE_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
mapper.setIdentityProviderAlias(PARENT_IDP);
|
||||||
|
provider.addMapper(mapper).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeHardcodedAttributeMapper() {
|
||||||
|
IdentityProviderResource provider = adminClient.realms().realm(CHILD_IDP).identityProviders().get(PARENT_IDP);
|
||||||
|
Optional<IdentityProviderMapperRepresentation> mapper = provider.getMappers().stream()
|
||||||
|
.filter(m -> m.getName().equals(HARDCODED_ATTRIBUTE_MAPPER_NAME)).findFirst();
|
||||||
|
|
||||||
|
Assert.assertFalse(mapper.isEmpty());
|
||||||
|
|
||||||
|
provider.delete(mapper.get().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHardcodedAttributeHasBeenAssigned() {
|
||||||
|
UserRepresentation user = adminClient.realm(CHILD_IDP).users().get(childUserId).toRepresentation();
|
||||||
|
Assert.assertEquals(USER_ATTRIBUTE_VALUE, user.firstAttribute(USER_ATTRIBUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeHardcodedAttribute() {
|
||||||
|
UserRepresentation user = adminClient.realm(CHILD_IDP).users().get(childUserId).toRepresentation();
|
||||||
|
user.getAttributes().remove(USER_ATTRIBUTE);
|
||||||
|
adminClient.realm(CHILD_IDP).users().get(user.getId()).update(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void linkAccount() {
|
||||||
|
RealmResource realm = adminClient.realms().realm(CHILD_IDP);
|
||||||
|
List<FederatedIdentityRepresentation> links = realm.users().get(childUserId).getFederatedIdentity();
|
||||||
|
Assert.assertTrue(links.isEmpty());
|
||||||
|
|
||||||
|
navigateToAccountLinkPage();
|
||||||
|
Assert.assertTrue(loginPage.isCurrent(CHILD_IDP));
|
||||||
|
Assert.assertTrue(driver.getPageSource().contains(PARENT_IDP));
|
||||||
|
loginPage.login("child", "password");
|
||||||
|
Assert.assertTrue(loginPage.isCurrent(PARENT_IDP));
|
||||||
|
loginPage.login(PARENT_USERNAME, "password");
|
||||||
|
System.out.println("After linking: " + driver.getCurrentUrl());
|
||||||
|
System.out.println(driver.getPageSource());
|
||||||
|
Assert.assertTrue(driver.getPageSource().contains("Account Linked"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void navigateToAccountLinkPage() {
|
||||||
|
UriBuilder linkBuilder = UriBuilder.fromUri(appPage.getInjectedUrl().toString())
|
||||||
|
.path("link");
|
||||||
|
String linkUrl = linkBuilder.clone()
|
||||||
|
.queryParam("realm", CHILD_IDP)
|
||||||
|
.queryParam("provider", PARENT_IDP).build().toString();
|
||||||
|
System.out.println("linkUrl: " + linkUrl);
|
||||||
|
navigateTo(linkUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue