Remove unnecessary clone operations when building user representations - #51215
Remove unnecessary clone operations when building user representations#51215pedroigor wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Updates Keycloak’s user-profile representation and attribute handling to avoid unnecessary cloning/metadata computation, particularly when building UserRepresentations.
Changes:
- Adds a
toRepresentation(full, includeMetadata)overload to skip building user-profile metadata when not needed. - Propagates
UnmanagedAttributePolicyintoDefaultAttributes/ServiceAccountAttributesinstead of reading configuration from the session provider each time. - Refactors profile metadata decoration/caching to reduce cloning.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| services/src/main/java/org/keycloak/userprofile/ServiceAccountAttributes.java | Passes unmanaged-attribute policy into service-account attribute handling. |
| services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProvider.java | Computes unmanaged-attribute policy once per attributes creation; adjusts metadata decoration clone flow. |
| server-spi-private/src/main/java/org/keycloak/userprofile/UserProfile.java | Adds representation overload to control metadata inclusion. |
| server-spi-private/src/main/java/org/keycloak/userprofile/DefaultUserProfile.java | Implements metadata inclusion flag to avoid unnecessary metadata creation. |
| server-spi-private/src/main/java/org/keycloak/userprofile/DefaultAttributes.java | Uses injected unmanaged-attribute policy; reduces metadata cloning via read-only accessor. |
| server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java | Uses new representation overload to skip metadata creation; simplifies brief/full mapping. |
| default <R extends AbstractUserRepresentation> R toRepresentation(boolean full, boolean includeMetadata) { | ||
| return toRepresentation(full); | ||
| } |
| UserRepresentation rep = profile.toRepresentation(!brief, false); | ||
| RealmModel realm = session.getContext().getRealm(); | ||
|
|
||
| rep = brief ? | ||
| ModelToRepresentation.toBriefRepresentation(user, rep, false) : | ||
| ModelToRepresentation.toRepresentation(session, realm, user, rep, false); | ||
|
|
||
| rep.setUserProfileMetadata(null); | ||
| if (brief) { | ||
| ModelToRepresentation.toBriefRepresentation(user, rep, false); | ||
| } else { | ||
| ModelToRepresentation.toRepresentation(session, realm, user, rep, false); | ||
| } |
| public Map<String, Object> getAnnotations(String name) { | ||
| AttributeMetadata metadata = getMetadata(name); | ||
| AttributeMetadata metadata = getMetadataReadOnly(name); | ||
|
|
||
| if (metadata == null) { | ||
| return Collections.emptyMap(); |
Closes keycloak#51214 Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
server-spi-private/src/main/java/org/keycloak/userprofile/UserProfile.java:142
- The new
includeMetadataparameter is currently ignored in the default implementation, which makes the API contract misleading and can break callers expecting metadata suppression (e.g.,toRepresentation(full, false)). Consider making this method non-default (forcing implementations to handleincludeMetadata), or have the default implementation actively remove metadata whenincludeMetadataisfalse(e.g., create the representation viatoRepresentation(full)and then clearuserProfileMetadataon the returned representation).
default <R extends AbstractUserRepresentation> R toRepresentation(boolean full, boolean includeMetadata) {
return toRepresentation(full);
}
server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java:366
- Previously the return value of
toBriefRepresentation(...)/toRepresentation(...)was assigned back torep. This change discards the returned value, which can alter behavior if those helpers ever return a different instance (even if they currently mutate in place). To preserve prior semantics and avoid future regressions, keep assigning the returned value back torep(or refactor the helpers to be clearlyvoid/mutating-only if that’s the intended contract).
if (brief) {
ModelToRepresentation.toBriefRepresentation(user, rep, false);
} else {
ModelToRepresentation.toRepresentation(session, realm, user, rep, false);
}
| public AttributeMetadata getMetadata(String name) { | ||
| AttributeMetadata metadata = getMetadataReadOnly(name); | ||
| return metadata != null ? metadata.clone() : null; | ||
| } | ||
|
|
||
| private AttributeMetadata getMetadataReadOnly(String name) { | ||
| if (unmanagedAttributes.containsKey(name)) { | ||
| return createUnmanagedAttributeMetadata(name); | ||
| } | ||
|
|
||
| return Optional.ofNullable(metadataByAttribute.get(name)) | ||
| .map(AttributeMetadata::clone) | ||
| .orElse(null); | ||
| return metadataByAttribute.get(name); | ||
| } |
Closes #51214