[KEYCLOAK-14326] Identity Provider force sync is not working

This commit is contained in:
Martin Bartos 2020-09-02 14:52:30 +02:00 committed by Marek Posolda
parent 1d8230d438
commit e34ff6cd9c
2 changed files with 120 additions and 0 deletions

View file

@ -26,6 +26,8 @@ import org.keycloak.authentication.AuthenticationFlowException;
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
import org.keycloak.broker.provider.AuthenticationRequest;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
@ -54,6 +56,7 @@ import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.IdentityProviderSyncMode;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
@ -120,6 +123,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
/**
* <p></p>
@ -1007,6 +1011,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
private void updateFederatedIdentity(BrokeredIdentityContext context, UserModel federatedUser) {
FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
if (context.getIdpConfig().getSyncMode() == IdentityProviderSyncMode.FORCE) {
setBasicUserAttributes(context, federatedUser);
}
// Skip DB write if tokens are null or equal
updateToken(context, federatedUser, federatedIdentityModel);
context.getIdp().updateBrokeredUser(session, realmModel, federatedUser, context);
@ -1021,6 +1029,19 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
private void setBasicUserAttributes(BrokeredIdentityContext context, UserModel federatedUser) {
setDiffAttrToConsumer(federatedUser.getEmail(), context.getEmail(), federatedUser::setEmail);
setDiffAttrToConsumer(federatedUser.getFirstName(), context.getFirstName(), federatedUser::setFirstName);
setDiffAttrToConsumer(federatedUser.getLastName(), context.getLastName(), federatedUser::setLastName);
}
private void setDiffAttrToConsumer(String actualValue, String newValue, Consumer<String> consumer) {
String actualValueNotNull = Optional.ofNullable(actualValue).orElse("");
if (newValue != null && !newValue.equals(actualValueNotNull)) {
consumer.accept(newValue);
}
}
private void migrateFederatedIdentityId(BrokeredIdentityContext context, UserModel federatedUser) {
FederatedIdentityModel identityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
FederatedIdentityModel migratedIdentityModel = new FederatedIdentityModel(identityModel, context.getId());

View file

@ -16,6 +16,8 @@ import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
import org.keycloak.crypto.Algorithm;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderMapperSyncMode;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.IdentityProviderSyncMode;
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.ClientRepresentation;
@ -26,6 +28,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.util.WaitUtils;
import javax.ws.rs.core.Response;
import java.util.Collections;
@ -445,6 +448,102 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
}
}
@Test
public void testIdPForceSyncUserAttributes() {
checkUpdatedUserAttributesIdP(true);
}
@Test
public void testIdPNotForceSyncUserAttributes() {
checkUpdatedUserAttributesIdP(false);
}
private void checkUpdatedUserAttributesIdP(boolean isForceSync) {
final String IDP_NAME = getBrokerConfiguration().getIDPAlias();
final String USERNAME = "demoUser";
final String FIRST_NAME = "John";
final String LAST_NAME = "Doe";
final String EMAIL = "mail@example.com";
final String NEW_FIRST_NAME = "Jack";
final String NEW_LAST_NAME = "Doee";
final String NEW_EMAIL = "mail123@example.com";
UsersResource providerUserResource = Optional.ofNullable(realmsResouce().realm(bc.providerRealmName()).users()).orElse(null);
assertThat("Cannot get User Resource from Provider realm", providerUserResource, Matchers.notNullValue());
String userID = createUser(bc.providerRealmName(), USERNAME, USERNAME, FIRST_NAME, LAST_NAME, EMAIL);
assertThat("Cannot create user : " + USERNAME, userID, Matchers.notNullValue());
try {
UserRepresentation user = Optional.ofNullable(providerUserResource.get(userID).toRepresentation()).orElse(null);
assertThat("Cannot get user from provider", user, Matchers.notNullValue());
IdentityProviderResource consumerIdentityResource = Optional.ofNullable(getIdentityProviderResource()).orElse(null);
assertThat("Cannot get Identity Provider resource", consumerIdentityResource, Matchers.notNullValue());
IdentityProviderRepresentation idProvider = Optional.ofNullable(consumerIdentityResource.toRepresentation()).orElse(null);
assertThat("Cannot get Identity Provider", idProvider, Matchers.notNullValue());
updateIdPSyncMode(idProvider, consumerIdentityResource, isForceSync ? IdentityProviderSyncMode.FORCE : IdentityProviderSyncMode.IMPORT);
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
WaitUtils.waitForPageToLoad();
assertThat(driver.getTitle(), Matchers.containsString("Log in to " + bc.consumerRealmName()));
logInWithIdp(IDP_NAME, USERNAME, USERNAME);
accountUpdateProfilePage.assertCurrent();
logoutFromRealm(getProviderRoot(), bc.providerRealmName());
logoutFromRealm(getConsumerRoot(), bc.consumerRealmName());
driver.navigate().to(getAccountUrl(getProviderRoot(), bc.providerRealmName()));
WaitUtils.waitForPageToLoad();
assertThat(driver.getTitle(), Matchers.containsString("Log in to " + bc.providerRealmName()));
loginPage.login(USERNAME, USERNAME);
WaitUtils.waitForPageToLoad();
accountUpdateProfilePage.assertCurrent();
accountUpdateProfilePage.updateProfile(NEW_FIRST_NAME, NEW_LAST_NAME, NEW_EMAIL);
logoutFromRealm(getProviderRoot(), bc.providerRealmName());
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
WaitUtils.waitForPageToLoad();
assertThat(driver.getTitle(), Matchers.containsString("Log in to " + bc.consumerRealmName()));
logInWithIdp(IDP_NAME, USERNAME, USERNAME);
accountUpdateProfilePage.assertCurrent();
assertThat(accountUpdateProfilePage.getEmail(), Matchers.equalTo(isForceSync ? NEW_EMAIL : EMAIL));
assertThat(accountUpdateProfilePage.getFirstName(), Matchers.equalTo(isForceSync ? NEW_FIRST_NAME : FIRST_NAME));
assertThat(accountUpdateProfilePage.getLastName(), Matchers.equalTo(isForceSync ? NEW_LAST_NAME : LAST_NAME));
} finally {
providerUserResource.delete(userID);
assertThat("User wasn't deleted", providerUserResource.search(USERNAME).size(), Matchers.is(0));
}
}
private void updateIdPSyncMode(IdentityProviderRepresentation idProvider, IdentityProviderResource idProviderResource, IdentityProviderSyncMode syncMode) {
assertThat(idProvider, Matchers.notNullValue());
assertThat(idProviderResource, Matchers.notNullValue());
assertThat(syncMode, Matchers.notNullValue());
if (idProvider.getConfig().get(IdentityProviderModel.SYNC_MODE).equals(syncMode.name())) {
return;
}
idProvider.getConfig().put(IdentityProviderModel.SYNC_MODE, syncMode.name());
idProviderResource.update(idProvider);
idProvider = Optional.ofNullable(idProviderResource.toRepresentation()).orElse(null);
assertThat("Cannot get Identity Provider", idProvider, Matchers.notNullValue());
assertThat("Sync mode didn't change", idProvider.getConfig().get(IdentityProviderModel.SYNC_MODE), Matchers.equalTo(syncMode.name()));
}
private UserRepresentation getFederatedIdentity() {
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());