Uploaded image for project: 'Red Hat build of Keycloak'
  1. Red Hat build of Keycloak
  2. RHBK-2645

Keycloak incorrect usage of UserPolicy and cache. [GHI#35796]

XMLWordPrintable

    • False
    • Hide

      None

      Show
      None
    • False

      Before reporting an issue

      [X] I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

      Area

      authorization-services

      Describe the bug

      We have an LDAP provider, and any request to issue an RPT token leads to a call to the LDAP server.

      Cache tuning does not resolve this issue.

      I observed some problems in the Keycloak code.

      In the class UserPolicyProviderFactory, the method toRepresentation deserializes a UserPolicy that always stores only a Set of UUIDs.

      During this process, the method calls getUser with the UUID parameter (userId), which is correct:

      ```
      representation.setUsers((Set<String>) JsonSerialization.readValue(users, Set.class).stream()

                          .filter(id -> getUser((String) id, authorization) != null)
                          .collect(Collectors.toSet()));
      

      ```

      However, inside getUser, I noticed an issue. The method first attempts to search for a user using userProvider.getUserByUsername, passing an incorrect parameter, userId:

      ```
      UserModel user = userProvider.getUserByUsername(realm, userId);

      if (user == null)

      { {code}

      user = userProvider.getUserById(realm, userId);

      
      

      }
      ```

      This behavior causes the following problems:

      The call to userProvider.getUserByUsername always ignores the cache and queries the LDAP server.
      The reason: No user provider can return a user by username = :userId since userId is a UUID. Consequently, the user is not stored in the cache.
      The subsequent call to userProvider.getUserById works correctly and caches the user. However, the cache entry is tied to the getUserById key, not getUserByUsername.
      On subsequent client requests, this process repeats:
      The cache is bypassed for getUserByUsername, resulting in repeated LDAP lookups.

      Proposed Fix

      I suggest modifying the getUser method in the UserPolicyProviderFactory to remove the unnecessary userProvider.getUserByUsername call. Since the userId is already a UUID, directly calling getUserById would avoid redundant LDAP queries and ensure proper caching behavior.

      Version

      26.0.7

      Regression

      [ ] The issue is a regression

      Expected behavior

      Keycloak goes to ldap only at first client request, then put user model to cache with correct cache key..

      Actual behavior

      The call to userProvider.getUserByUsername always ignores the cache and queries the LDAP server.
      The reason: No user provider can return a user by username = :userId since userId is a UUID. Consequently, the user is not stored in the cache.
      The subsequent call to userProvider.getUserById works correctly and caches the user. However, the cache entry is tied to the getUserById key, not getUserByUsername.
      On subsequent client requests, this process repeats:
      The cache is bypassed for getUserByUsername, resulting in repeated LDAP lookups.

      How to Reproduce?

      Configure any ldap provider, enable debug log, issue rpt token.
      See logs.

      Anything else?

      No response

              Unassigned Unassigned
              pvlha Pavel Vlha
              Keycloak Core IAM
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: