[KEYCLOAK-18626] - Avoid changing username when registration as email is enabled

This commit is contained in:
Pedro Igor 2021-07-01 11:26:01 -03:00
parent f15536a88c
commit b26b41332e
3 changed files with 48 additions and 10 deletions

View file

@ -95,11 +95,6 @@ public class VerifyUserProfile implements RequiredActionProvider, RequiredAction
EventBuilder event = context.getEvent(); EventBuilder event = context.getEvent();
event.event(EventType.VERIFY_PROFILE); event.event(EventType.VERIFY_PROFILE);
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters(); MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
if (!context.getRealm().isEditUsernameAllowed()) {
formData.putSingle(UserModel.USERNAME, context.getUser().getUsername());
}
UserProfileProvider provider = context.getSession().getProvider(UserProfileProvider.class); UserProfileProvider provider = context.getSession().getProvider(UserProfileProvider.class);
UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, formData, context.getUser()); UserProfile profile = provider.create(UserProfileContext.UPDATE_PROFILE, formData, context.getUser());

View file

@ -22,7 +22,9 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation; import org.keycloak.services.validation.Validation;
import org.keycloak.userprofile.AttributeContext;
import org.keycloak.userprofile.UserProfileAttributeValidationContext; import org.keycloak.userprofile.UserProfileAttributeValidationContext;
import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.validate.SimpleValidator; import org.keycloak.validate.SimpleValidator;
import org.keycloak.validate.ValidationContext; import org.keycloak.validate.ValidationContext;
import org.keycloak.validate.ValidationError; import org.keycloak.validate.ValidationError;
@ -59,10 +61,16 @@ public class UsernameMutationValidator implements SimpleValidator {
return context; return context;
} }
UserModel user = UserProfileAttributeValidationContext.from(context).getAttributeContext().getUser(); AttributeContext attributeContext = UserProfileAttributeValidationContext.from(context).getAttributeContext();
UserModel user = attributeContext.getUser();
RealmModel realm = context.getSession().getContext().getRealm(); RealmModel realm = context.getSession().getContext().getRealm();
if (!realm.isEditUsernameAllowed() && user != null && !value.equals(user.getFirstAttribute(UserModel.USERNAME))) { if (!realm.isEditUsernameAllowed() && user != null && !value.equals(user.getFirstAttribute(UserModel.USERNAME))) {
if (realm.isRegistrationEmailAsUsername() && UserProfileContext.UPDATE_PROFILE.equals(attributeContext.getContext())) {
// if username changed is because email as username is allowed so no validation should happen for update profile
// it is expected that username changes when attributes are normalized by the provider
return context;
}
context.addError(new ValidationError(ID, inputHint, Messages.READ_ONLY_USERNAME)); context.addError(new ValidationError(ID, inputHint, Messages.READ_ONLY_USERNAME));
} }
return context; return context;

View file

@ -35,6 +35,7 @@ import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
@ -85,10 +86,12 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
private static String user4Id; private static String user4Id;
private static String user5Id; private static String user5Id;
private static String user6Id;
private static ClientRepresentation client_scope_default; private static ClientRepresentation client_scope_default;
private static ClientRepresentation client_scope_optional; private static ClientRepresentation client_scope_optional;
@Override @Override
public void configureTestRealm(RealmRepresentation testRealm) { public void configureTestRealm(RealmRepresentation testRealm) {
UserRepresentation user = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test").email("login@test.com").enabled(true).password("password").build(); UserRepresentation user = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test").email("login@test.com").enabled(true).password("password").build();
@ -106,7 +109,10 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
UserRepresentation user5 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test5").email("login5@test.com").enabled(true).password("password").firstName("ExistingFirst").lastName("ExistingLast").build(); UserRepresentation user5 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test5").email("login5@test.com").enabled(true).password("password").firstName("ExistingFirst").lastName("ExistingLast").build();
user5Id = user5.getId(); user5Id = user5.getId();
RealmBuilder.edit(testRealm).user(user).user(user2).user(user3).user(user4).user(user5); UserRepresentation user6 = UserBuilder.create().id(UUID.randomUUID().toString()).username("login-test6").email("login6@test.com").enabled(true).password("password").firstName("ExistingFirst").lastName("ExistingLast").build();
user6Id = user6.getId();
RealmBuilder.edit(testRealm).user(user).user(user2).user(user3).user(user4).user(user5).user(user6);
RequiredActionProviderRepresentation action = new RequiredActionProviderRepresentation(); RequiredActionProviderRepresentation action = new RequiredActionProviderRepresentation();
action.setAlias(UserModel.RequiredAction.VERIFY_PROFILE.name()); action.setAlias(UserModel.RequiredAction.VERIFY_PROFILE.name());
@ -305,6 +311,35 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
} }
@Test
public void testDoNotValidateUsernameWhenRegistrationAsEmailEnabled() {
RealmResource realmResource = testRealm();
RealmRepresentation realm = realmResource.toRepresentation();
try {
setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT);
updateUser(user6Id, "ExistingFirst", "ExistingLast", "Department");
realm.setRegistrationEmailAsUsername(true);
realmResource.update(realm);
setUserProfileConfiguration("{\"attributes\": ["
+ "{\"name\": \"firstName\"," + PERMISSIONS_ALL + ", \"required\": {}},"
+ "{\"name\": \"lastName\"," + PERMISSIONS_ALL +","+VALIDATIONS_LENGTH + "}"
+ "]}");
loginPage.open();
loginPage.login("login6@test.com", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
} finally {
realm.setRegistrationEmailAsUsername(false);
realmResource.update(realm);
}
}
@Test @Test
public void testRequiredReadOnlyAttribute() { public void testRequiredReadOnlyAttribute() {
@ -553,7 +588,7 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
@Test @Test
public void testAttributeRequiredButNotSelectedByScopeDoesntForceVerificationScreen() { public void testAttributeRequiredButNotSelectedByScopeDoesntForceVerificationScreen() {
setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT); setUserProfileConfiguration(CONFIGURATION_FOR_USER_EDIT);
updateUser(user5Id, "ExistingFirst", "ExistingLast", null); updateUser(user5Id, "ExistingFirst", "ExistingLast", null);