Users in role Rest API returns empty when User federation used (#23318)

* Users in role Rest API returns empty when User federation used

Co-authored-by: Shankar Yadav <ET1024@neeyamoworks.com>
Co-authored-by: Martin Kanis <mkanis@redhat.com>
Co-authored-by: Michal Hajas <mhajas@redhat.com>
This commit is contained in:
Martin Kanis 2023-10-24 17:10:20 +02:00 committed by GitHub
parent 9627187447
commit 10a2c96c72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 1 deletions

View file

@ -475,6 +475,15 @@ public class JpaUserFederatedStorageProvider implements
return closing(paginateQuery(query, firstResult, max).getResultStream());
}
@Override
public Stream<String> getRoleMembersStream(RealmModel realm, RoleModel role, Integer firstResult, Integer max) {
TypedQuery<String> query = em.createNamedQuery("fedRoleMembership", String.class);
query.setParameter("roleId", role.getId());
query.setParameter("realmId", realm.getId());
return closing(paginateQuery(query, firstResult, max).getResultStream());
}
@Override
public Stream<String> getRequiredActionsStream(RealmModel realm, String userId) {
return this.getRequiredActionEntitiesStream(realm, userId, LockModeType.NONE).

View file

@ -40,7 +40,7 @@ import java.io.Serializable;
@NamedQuery(name="deleteFederatedUserRoleMappingsByRealmAndLink", query="delete from FederatedUserRoleMappingEntity mapping where mapping.userId IN (select u.id from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
@NamedQuery(name="deleteFederatedUserRoleMappingsByRole", query="delete from FederatedUserRoleMappingEntity m where m.roleId = :roleId"),
@NamedQuery(name="deleteFederatedUserRoleMappingsByUser", query="delete from FederatedUserRoleMappingEntity m where m.userId = :userId and m.realmId = :realmId"),
@NamedQuery(name="fedRoleMembership", query="select m.userId FROM FederatedUserRoleMappingEntity m where m.roleId = :roleId AND m.realmId = :realmId"),
})
@Table(name="FED_USER_ROLE_MAPPING")
@Entity

View file

@ -423,6 +423,10 @@ public class UserStorageManager extends AbstractStorageManager<UserStorageProvid
if (provider instanceof UserQueryMethodsProvider) {
return ((UserQueryMethodsProvider)provider).getRoleMembersStream(realm, role, firstResultInQuery, maxResultsInQuery);
}
else if (provider instanceof UserFederatedStorageProvider) {
return ((UserFederatedStorageProvider)provider).getRoleMembersStream(realm, role, firstResultInQuery, maxResultsInQuery).
map(id -> getUserById(realm, id));
}
return Stream.empty();
}, realm, firstResult, maxResults);
return importValidation(realm, results);

View file

@ -40,6 +40,17 @@ public interface UserRoleMappingsFederatedStorage {
void deleteRoleMapping(RealmModel realm, String userId, RoleModel role);
/**
* Obtains the federated users that are members of the given {@code role} in the specified {@code realm}.
*
* @param realm a reference to the realm.
* @param role a reference to the role whose federated members are being searched.
* @param firstResult first result to return. Ignored if negative or {@code null}.
* @param max maximum number of results to return. Ignored if negative or {@code null}.
* @return a non-null {@code Stream} of federated user ids that are members of the role in the realm.
*/
Stream<String> getRoleMembersStream(RealmModel realm, RoleModel role, Integer firstResult, Integer max);
/**
* @deprecated This interface is no longer necessary; collection-based methods were removed from the parent interface
* and therefore the parent interface can be used directly

View file

@ -1,6 +1,7 @@
package org.keycloak.testsuite.federation.storage;
import org.apache.commons.io.FileUtils;
import org.hamcrest.Matchers;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Assert;
@ -20,6 +21,7 @@ import org.keycloak.credential.CredentialProvider;
import org.keycloak.credential.CredentialProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.credential.OTPCredentialModel;
@ -867,6 +869,40 @@ public class UserStorageTest extends AbstractAuthTest {
adminClient.realms().create(repOrig);
}
@Test
public void testRoleMembership() {
RoleRepresentation role1 = new RoleRepresentation();
role1.setName("role1");
RoleRepresentation role2 = new RoleRepresentation();
role2.setName("role2");
testRealmResource().roles().create(role1);
testRealmResource().roles().create(role2);
UserRepresentation thor = ApiUtil.findUserByUsername(testRealmResource(), "thor");
ApiUtil.assignRealmRoles(testRealmResource(), thor.getId(), "role1", "role2");
UserRepresentation zeus = ApiUtil.findUserByUsername(testRealmResource(), "zeus");
ApiUtil.assignRealmRoles(testRealmResource(), zeus.getId(), "role1");
testingClient.server().run(session -> {
RealmModel realm = session.realms().getRealmByName("test");
RoleModel roleModel1 = session.roles().getRealmRole(realm, "role1");
RoleModel roleModel2 = session.roles().getRealmRole(realm, "role2");
List<String> users = session.users().getRoleMembersStream(realm, roleModel1).map(UserModel::getUsername).collect(Collectors.toList());
Assert.assertEquals(2, users.size());
Assert.assertThat(users, Matchers.containsInAnyOrder("thor", "zeus"));
users = session.users().getRoleMembersStream(realm, roleModel2).map(UserModel::getUsername).collect(Collectors.toList());
Assert.assertEquals(1, users.size());
Assert.assertThat(users, Matchers.containsInAnyOrder("thor"));
});
testRealmResource().roles().get("role1").remove();
testRealmResource().roles().get("role2").remove();
}
@Test
@Ignore
public void testEntityRemovalHooksCascade() {