Fixing how e-mail attribute permissions are set for both USER_API and ACCOUNT contexts

Closes #21751
This commit is contained in:
Pedro Igor 2023-08-08 16:16:34 -03:00 committed by Alexander Schwartz
parent 8f38e26970
commit baac060eb1
4 changed files with 69 additions and 6 deletions

View file

@ -39,6 +39,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.keycloak.Config;
import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -119,13 +120,21 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
}
private static boolean readEmailCondition(AttributeContext c) {
RealmModel realm = c.getSession().getContext().getRealm();
UserProfileContext context = c.getContext();
if (realm.isRegistrationEmailAsUsername() && !realm.isEditUsernameAllowed()) {
return REGISTRATION_PROFILE.equals(c.getContext());
if (UPDATE_PROFILE.equals(context)) {
if (Profile.isFeatureEnabled(Feature.UPDATE_EMAIL)) {
return false;
}
RealmModel realm = c.getSession().getContext().getRealm();
if (realm.isRegistrationEmailAsUsername() && !realm.isEditUsernameAllowed()) {
return false;
}
}
return !Profile.isFeatureEnabled(Profile.Feature.UPDATE_EMAIL) || c.getContext() != UPDATE_PROFILE;
return true;
}
public static Pattern getRegexPatternString(String[] builtinReadOnlyAttributes) {

View file

@ -48,7 +48,12 @@ public class LegacyAttributes extends DefaultAttributes {
@Override
protected boolean isIncludeAttributeIfNotProvided(AttributeMetadata metadata) {
// user api expects that built-in attributes are not updated if not provided when in legacy mode
return UserProfileContext.USER_API.equals(context) && !isRootAttribute(metadata.getName());
if (UserModel.LOCALE.equals(metadata.getName())) {
// locale is an internal attribute and should be updated as a regular attribute
return false;
}
// user api expects that attributes are not updated if not provided when in legacy mode
return UserProfileContext.USER_API.equals(context);
}
}

View file

@ -421,6 +421,28 @@ public class AccountRestServiceTest extends AbstractRestServiceTest {
}
@Test
public void testEmailReadableWhenEditUsernameDisabled() throws IOException {
RealmRepresentation realmRep = testRealm().toRepresentation();
Boolean emailAsUsername = realmRep.isRegistrationEmailAsUsername();
Boolean editUsernameAllowed = realmRep.isEditUsernameAllowed();
realmRep.setRegistrationEmailAsUsername(true);
realmRep.setEditUsernameAllowed(false);
testRealm().update(realmRep);
try {
UserRepresentation user = getUser();
String email = user.getEmail();
assertNotNull(email);
user = updateAndGet(user);
assertEquals(email, user.getEmail());
} finally {
realmRep.setRegistrationEmailAsUsername(emailAsUsername);
realmRep.setEditUsernameAllowed(editUsernameAllowed);
testRealm().update(realmRep);
}
}
@Test
public void testUpdateProfileCannotChangeThroughAttributes() throws IOException {
UserRepresentation user = getUser();

View file

@ -106,6 +106,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
@ -2331,6 +2332,32 @@ public class UserTest extends AbstractAdminTest {
}
}
@Test
public void testKeepRootAttributeWhenOtherAttributesAreSet() {
String random = UUID.randomUUID().toString();
String userName = String.format("username-%s", random);
String email = String.format("my@mail-%s.com", random);
UserRepresentation user = new UserRepresentation();
user.setUsername(userName);
user.setEmail(email);
String userId = createUser(user);
UserRepresentation created = realm.users().get(userId).toRepresentation();
assertThat(created.getEmail(), equalTo(email));
assertThat(created.getUsername(), equalTo(userName));
assertThat(created.getAttributes(), Matchers.nullValue());
UserRepresentation update = new UserRepresentation();
update.setId(userId);
update.setAttributes(Map.of("phoneNumber", List.of("123")));
updateUser(realm.users().get(userId), update);
UserRepresentation updated = realm.users().get(userId).toRepresentation();
assertThat(updated.getUsername(), equalTo(userName));
assertThat(updated.getAttributes(), equalTo(Map.of("phoneNumber", List.of("123"))));
assertThat(updated.getEmail(), equalTo(email));
}
@Test
public void updateUserWithNewUsernameNotPossible() {
String id = createUser();