Check email with ignorecase when setting basic attributes in IdP

Closes #31848

Signed-off-by: rmartinc <rmartinc@redhat.com>
This commit is contained in:
rmartinc 2024-10-08 18:04:05 +02:00 committed by Alexander Schwartz
parent 3930356c21
commit a74e60f4d7
2 changed files with 104 additions and 5 deletions

View file

@ -1060,14 +1060,14 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
} }
private void setBasicUserAttributes(BrokeredIdentityContext context, UserModel federatedUser) { private void setBasicUserAttributes(BrokeredIdentityContext context, UserModel federatedUser) {
setDiffAttrToConsumer(federatedUser.getEmail(), context.getEmail(), email -> setEmail(context, federatedUser, email)); setDiffAttrToConsumer(federatedUser.getEmail(), context.getEmail(), email -> setEmail(context, federatedUser, email), true);
setDiffAttrToConsumer(federatedUser.getFirstName(), context.getFirstName(), federatedUser::setFirstName); setDiffAttrToConsumer(federatedUser.getFirstName(), context.getFirstName(), federatedUser::setFirstName, false);
setDiffAttrToConsumer(federatedUser.getLastName(), context.getLastName(), federatedUser::setLastName); setDiffAttrToConsumer(federatedUser.getLastName(), context.getLastName(), federatedUser::setLastName, false);
} }
private void setDiffAttrToConsumer(String actualValue, String newValue, Consumer<String> consumer) { private void setDiffAttrToConsumer(String actualValue, String newValue, Consumer<String> consumer, boolean ignoreCase) {
String actualValueNotNull = Optional.ofNullable(actualValue).orElse(""); String actualValueNotNull = Optional.ofNullable(actualValue).orElse("");
if (newValue != null && !newValue.equals(actualValueNotNull)) { if (newValue != null && !(ignoreCase? newValue.equalsIgnoreCase(actualValueNotNull) : newValue.equals(actualValueNotNull))) {
consumer.accept(newValue); consumer.accept(newValue);
} }
} }

View file

@ -10,6 +10,7 @@ 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.ClientsResource; import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.IdentityProviderResource; import org.keycloak.admin.client.resource.IdentityProviderResource;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource; import org.keycloak.admin.client.resource.UserResource;
@ -27,6 +28,7 @@ import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCConfigAttributes;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
@ -39,6 +41,7 @@ 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.testsuite.Assert; import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.broker.util.SimpleHttpDefault; import org.keycloak.testsuite.broker.util.SimpleHttpDefault;
import org.keycloak.testsuite.updaters.RealmAttributeUpdater; import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
import org.keycloak.testsuite.util.AccountHelper; import org.keycloak.testsuite.util.AccountHelper;
@ -707,6 +710,102 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
} }
} }
@Test
public void checkUpdatedEmailAttributeIdPSameValueDifferentCase() throws Exception {
final String IDP_NAME = getBrokerConfiguration().getIDPAlias();
final String USERNAME = "demo-user";
final String PASSWORD = "demo-pwd";
final String FIRST_NAME = "John";
final String LAST_NAME = "Doe";
final String EMAIL = "mail@example.com";
RealmResource providerRealmResource = realmsResouce().realm(bc.providerRealmName());
allowUserEdit(providerRealmResource);
UsersResource providerUsersResource = providerRealmResource.users();
String providerUserID = createUser(bc.providerRealmName(), USERNAME, PASSWORD, FIRST_NAME, LAST_NAME, EMAIL,
user -> user.setEmailVerified(true));
try {
IdentityProviderResource consumerIdentityResource = getIdentityProviderResource();
IdentityProviderRepresentation idProvider = consumerIdentityResource.toRepresentation();
updateIdPSyncMode(idProvider, consumerIdentityResource, IdentityProviderSyncMode.FORCE, false);
// login to create the user in the consumer realm
oauth.clientId("broker-app");
loginPage.open(bc.consumerRealmName());
WaitUtils.waitForPageToLoad();
assertThat(driver.getTitle(), Matchers.containsString("Sign in to " + bc.consumerRealmName()));
logInWithIdp(IDP_NAME, USERNAME, PASSWORD);
UserRepresentation userRepresentation = AccountHelper.getUserRepresentation(adminClient.realm(bc.providerRealmName()), USERNAME);
assertThat(userRepresentation.getUsername(), Matchers.equalTo(USERNAME));
assertThat(userRepresentation.getEmail(), Matchers.equalTo(EMAIL));
assertThat(userRepresentation.getFirstName(), Matchers.equalTo(FIRST_NAME));
assertThat(userRepresentation.getLastName(), Matchers.equalTo(LAST_NAME));
RealmResource consumerRealmResource = realmsResouce().realm(bc.consumerRealmName());
List<UserRepresentation> foundUsers = consumerRealmResource.users().searchByUsername(USERNAME, true);
assertThat(foundUsers, Matchers.hasSize(1));
UserRepresentation consumerUser = foundUsers.get(0);
assertThat(consumerUser, Matchers.notNullValue());
String consumerUserID = consumerUser.getId();
UserResource consumerUserResource = consumerRealmResource.users().get(consumerUserID);
checkFederatedIdentityLink(consumerUserResource, providerUserID, USERNAME);
Assert.assertFalse(consumerUserResource.toRepresentation().isEmailVerified());
AccountHelper.logout(adminClient.realm(bc.consumerRealmName()), USERNAME);
AccountHelper.logout(adminClient.realm(bc.providerRealmName()), USERNAME);
// set email verified to true on the consumer resource
consumerUser = consumerUserResource.toRepresentation();
consumerUser.setEmailVerified(true);
consumerUserResource.update(consumerUser);
Assert.assertTrue(consumerUserResource.toRepresentation().isEmailVerified());
// Change the client scope for email to set the hardcoded email in capitals
ProtocolMapperRepresentation hardcodedEmail = new ProtocolMapperRepresentation();
hardcodedEmail.setName("email");
hardcodedEmail.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
hardcodedEmail.setProtocolMapper(HardcodedClaim.PROVIDER_ID);
hardcodedEmail.getConfig().put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "email");
hardcodedEmail.getConfig().put(OIDCAttributeMapperHelper.JSON_TYPE, "String");
hardcodedEmail.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
hardcodedEmail.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
hardcodedEmail.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
hardcodedEmail.getConfig().put(HardcodedClaim.CLAIM_VALUE, EMAIL.toUpperCase());
ClientScopeResource emailClientScope = ApiUtil.findClientScopeByName(providerRealmResource, "email");
ProtocolMapperRepresentation emailMapper = ApiUtil.findProtocolMapperByName(emailClientScope, "email");
emailClientScope.getProtocolMappers().delete(emailMapper.getId());
emailClientScope.getProtocolMappers().createMapper(hardcodedEmail);
// login again to force sync
oauth.clientId("broker-app");
loginPage.open(bc.consumerRealmName());
WaitUtils.waitForPageToLoad();
assertThat(driver.getTitle(), Matchers.containsString("Sign in to " + bc.consumerRealmName()));
logInWithIdp(IDP_NAME, USERNAME, PASSWORD);
assertThat(driver.getCurrentUrl(), Matchers.containsString("/app/auth?"));
consumerUserResource = consumerRealmResource.users().get(consumerUserID);
checkFederatedIdentityLink(consumerUserResource, providerUserID, USERNAME);
// the email should be verified as it's just a different case
Assert.assertTrue(consumerUserResource.toRepresentation().isEmailVerified());
} finally {
providerUsersResource.delete(providerUserID);
}
}
private void allowUserEdit(RealmResource realmResource) { private void allowUserEdit(RealmResource realmResource) {
RealmRepresentation realm = realmResource.toRepresentation(); RealmRepresentation realm = realmResource.toRepresentation();
realm.setEditUsernameAllowed(true); realm.setEditUsernameAllowed(true);