[KEYCLOAK-14326] Identity Provider force sync is not working
This commit is contained in:
parent
1d8230d438
commit
e34ff6cd9c
2 changed files with 120 additions and 0 deletions
|
@ -26,6 +26,8 @@ import org.keycloak.authentication.AuthenticationFlowException;
|
||||||
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
import org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator;
|
||||||
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
|
import org.keycloak.authentication.authenticators.broker.util.PostBrokerLoginConstants;
|
||||||
import org.keycloak.authentication.authenticators.broker.util.SerializedBrokeredIdentityContext;
|
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.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
@ -54,6 +56,7 @@ import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -120,6 +123,7 @@ import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p></p>
|
* <p></p>
|
||||||
|
@ -1007,6 +1011,10 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
private void updateFederatedIdentity(BrokeredIdentityContext context, UserModel federatedUser) {
|
private void updateFederatedIdentity(BrokeredIdentityContext context, UserModel federatedUser) {
|
||||||
FederatedIdentityModel federatedIdentityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
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
|
// Skip DB write if tokens are null or equal
|
||||||
updateToken(context, federatedUser, federatedIdentityModel);
|
updateToken(context, federatedUser, federatedIdentityModel);
|
||||||
context.getIdp().updateBrokeredUser(session, realmModel, federatedUser, context);
|
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) {
|
private void migrateFederatedIdentityId(BrokeredIdentityContext context, UserModel federatedUser) {
|
||||||
FederatedIdentityModel identityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
FederatedIdentityModel identityModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
|
||||||
FederatedIdentityModel migratedIdentityModel = new FederatedIdentityModel(identityModel, context.getId());
|
FederatedIdentityModel migratedIdentityModel = new FederatedIdentityModel(identityModel, context.getId());
|
||||||
|
|
|
@ -16,6 +16,8 @@ import org.keycloak.broker.oidc.mappers.UserAttributeMapper;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
import org.keycloak.models.IdentityProviderMapperModel;
|
import org.keycloak.models.IdentityProviderMapperModel;
|
||||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
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.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
import org.keycloak.testsuite.util.WaitUtils;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.Collections;
|
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() {
|
private UserRepresentation getFederatedIdentity() {
|
||||||
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());
|
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue