Update user profile to allow returning a brief user representation

Closes #42225

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2025-10-21 07:52:31 -03:00 committed by GitHub
parent 49305d1567
commit c5b560e2d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 7 deletions

View File

@ -1,6 +1,14 @@
// ------------------------ Breaking changes ------------------------ //
== Breaking changes
=== Method `UserProfile#toRepresentation(boolean)` added
The `UserProfile` interface has a new method `toRepresentation(boolean)`. This method allows clients to specify whether to include
only the basic attributes in representations or all of them.
The `UserProfile` interface is a private API and should not be implemented by custom code. However, if you have extensions that
implement this interface, you will need to update your code to accommodate this new method.
// ------------------------ Notable changes ------------------------ //
== Notable changes

View File

@ -173,7 +173,7 @@ public class BruteForceUsersResource {
return userModels.map(user -> {
UserProfile profile = provider.create(UserProfileContext.USER_API, user);
UserRepresentation rep = profile.toRepresentation();
UserRepresentation rep = profile.toRepresentation(!briefRepresentationB);
UserRepresentation userRep = briefRepresentationB ?
ModelToRepresentation.toBriefRepresentation(user, rep, false) :
ModelToRepresentation.toRepresentation(session, realm, user, rep, false);

View File

@ -220,12 +220,12 @@ public final class DefaultUserProfile implements UserProfile {
}
@Override
public <R extends AbstractUserRepresentation> R toRepresentation() {
public <R extends AbstractUserRepresentation> R toRepresentation(boolean full) {
if (user == null) {
throw new IllegalStateException("Can not create the representation because the user is not yet created");
}
R rep = createUserRepresentation();
R rep = createUserRepresentation(full);
Map<String, List<String>> readable = attributes.getReadable();
Map<String, List<String>> attributesRep = new HashMap<>(readable);
@ -290,13 +290,18 @@ public final class DefaultUserProfile implements UserProfile {
}
@SuppressWarnings("unchecked")
private <R extends AbstractUserRepresentation> R createUserRepresentation() {
private <R extends AbstractUserRepresentation> R createUserRepresentation(boolean full) {
UserProfileContext context = metadata.getContext();
R rep;
if (context.isAdminContext()) {
RealmModel realm = session.getContext().getRealm();
rep = (R) ModelToRepresentation.toRepresentation(session, realm, user);
if (full) {
rep = (R) ModelToRepresentation.toRepresentation(session, realm, user);
} else {
rep = (R) new org.keycloak.representations.idm.UserRepresentation();
}
} else {
// by default, we build the simplest representation without exposing much information about users
rep = (R) new org.keycloak.representations.account.UserRepresentation();

View File

@ -101,5 +101,20 @@ public interface UserProfile {
*/
Attributes getAttributes();
<R extends AbstractUserRepresentation> R toRepresentation();
/**
* Returns the full user representation
*
* @return the user representation
*/
default <R extends AbstractUserRepresentation> R toRepresentation() {
return toRepresentation(true);
}
/**
* Returns the user representation
*
* @param full if the full representation should be returned
* @return the user representation
*/
<R extends AbstractUserRepresentation> R toRepresentation(boolean full);
}

View File

@ -544,7 +544,7 @@ public class UsersResource {
return userModels
.map(user -> {
UserProfile profile = provider.create(UserProfileContext.USER_API, user);
UserRepresentation rep = profile.toRepresentation();
UserRepresentation rep = profile.toRepresentation(!briefRepresentationB);
UserRepresentation userRep = briefRepresentationB ?
ModelToRepresentation.toBriefRepresentation(user, rep, false) :
ModelToRepresentation.toRepresentation(session, realm, user, rep, false);

View File

@ -38,6 +38,7 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
@KeycloakIntegrationTest
public class UsersTest {
@ -62,6 +63,20 @@ public class UsersTest {
assertThat(realm.admin().users().search("Us*e", null, null), hasSize(1));
}
@Test
public void testFullRepresentationOnSearches() {
createUser("user", "firstName", "lastName", "user@example.com");
List<UserRepresentation> users = realm.admin().users().search("user", null, null, false);
UserRepresentation user = users.get(0);
assertThat(user.getRequiredActions(), empty());
users = realm.admin().users().search("user", null, null, true);
user = users.get(0);
assertThat(user.getRequiredActions(), nullValue());
assertThat(user.isTotp(), nullValue());
}
@Test
public void searchUserDefaultSettings() throws Exception {
createUser("User", "firstName", "lastName", "user@example.com");