display users in roles

This commit is contained in:
Leon Graser 2019-04-26 14:32:25 +02:00 committed by Stan Silvert
parent 785eed791d
commit e1cb17586f
6 changed files with 171 additions and 1 deletions

View file

@ -21,15 +21,20 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.AdminEventPaths;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import static org.junit.Assert.assertEquals;
@ -37,7 +42,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class ClientRolesTest extends AbstractClientTest {
@ -132,4 +136,48 @@ public class ClientRolesTest extends AbstractClientTest {
assertFalse(rolesRsc.get("role-a").toRepresentation().isComposite());
assertEquals(0, rolesRsc.get("role-a").getRoleComposites().size());
}
@Test
public void usersInRole() {
String clientID = clientRsc.toRepresentation().getId();
// create test role on client
String roleName = "test-role";
RoleRepresentation role = makeRole(roleName);
rolesRsc.create(role);
assertTrue(hasRole(rolesRsc, roleName));
List<RoleRepresentation> roleToAdd = Collections.singletonList(rolesRsc.get(roleName).toRepresentation());
//create users and assign test role
Set<UserRepresentation> users = new HashSet<>();
for (int i = 0; i < 10; i++) {
String userName = "user" + i;
UserRepresentation user = new UserRepresentation();
user.setUsername(userName);
testRealmResource().users().create(user);
user = getFullUserRep(userName);
testRealmResource().users().get(user.getId()).roles().clientLevel(clientID).add(roleToAdd);
users.add(user);
}
// check if users have test role assigned
RoleResource roleResource = rolesRsc.get(roleName);
Set<UserRepresentation> usersInRole = roleResource.getRoleUserMembers();
assertEquals(users.size(), usersInRole.size());
for (UserRepresentation user : users) {
Optional<UserRepresentation> result = usersInRole.stream().filter(u -> user.getUsername().equals(u.getUsername())).findAny();
assertTrue(result.isPresent());
}
// pagination
Set<UserRepresentation> usersInRole1 = roleResource.getRoleUserMembers(0, 5);
assertEquals(5, usersInRole1.size());
Set<UserRepresentation> usersInRole2 = roleResource.getRoleUserMembers(5, 10);
assertEquals(5, usersInRole2.size());
for (UserRepresentation user : users) {
Optional<UserRepresentation> result1 = usersInRole1.stream().filter(u -> user.getUsername().equals(u.getUsername())).findAny();
Optional<UserRepresentation> result2 = usersInRole2.stream().filter(u -> user.getUsername().equals(u.getUsername())).findAny();
assertTrue((result1.isPresent() || result2.isPresent()) && !(result1.isPresent() && result2.isPresent()));
}
}
}

View file

@ -976,6 +976,21 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ClientRoleDetailCtrl'
})
.when('/realms/:realm/clients/:client/roles/:role/users', {
templateUrl : resourceUrl + '/partials/client-role-users.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
client : function(ClientLoader) {
return ClientLoader();
},
role : function(ClientRoleLoader) {
return ClientRoleLoader();
}
},
controller : 'ClientRoleMembersCtrl'
})
.when('/realms/:realm/clients/:client/mappers', {
templateUrl : resourceUrl + '/partials/client-mappers.html',
resolve : {

View file

@ -758,6 +758,51 @@ module.controller('ClientRoleDetailCtrl', function($scope, realm, client, role,
});
module.controller('ClientRoleMembersCtrl', function($scope, realm, client, role, ClientRoleMembership, Dialog, Notifications, $location) {
$scope.realm = realm;
$scope.page = 0;
$scope.role = role;
$scope.client = client;
$scope.query = {
realm: realm.realm,
role: role.name,
client: client.id,
max : 5,
first : 0
}
$scope.firstPage = function() {
$scope.query.first = 0;
$scope.searchQuery();
}
$scope.previousPage = function() {
$scope.query.first -= parseInt($scope.query.max);
if ($scope.query.first < 0) {
$scope.query.first = 0;
}
$scope.searchQuery();
}
$scope.nextPage = function() {
$scope.query.first += parseInt($scope.query.max);
$scope.searchQuery();
}
$scope.searchQuery = function() {
$scope.searchLoaded = false;
$scope.users = ClientRoleMembership.query($scope.query, function() {
console.log('search loaded');
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
});
};
$scope.searchQuery();
});
module.controller('ClientImportCtrl', function($scope, $location, $upload, realm, serverInfo, Notifications) {
$scope.realm = realm;

View file

@ -1913,6 +1913,13 @@ module.factory('RoleMembership', function($resource) {
});
});
module.factory('ClientRoleMembership', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/roles/:role/users', {
realm : '@realm',
client : '@client',
role : '@role'
});
});
module.factory('UserGroupMembership', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups', {

View file

@ -0,0 +1,52 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
<li>{{role.name}}</li>
</ol>
<kc-tabs-client-role></kc-tabs-client-role>
<table class="table table-striped table-bordered">
<caption data-ng-show="users" class="hidden">{{:: 'table-of-role-members' | translate}}</caption>
<thead>
<tr>
<tr data-ng-show="searchLoaded && users.length > 0">
<th>{{:: 'username' | translate}}</th>
<th>{{:: 'last-name' | translate}}</th>
<th>{{:: 'first-name' | translate}}</th>
<th>{{:: 'email' | translate}}</th>
<th></th>
</tr>
</tr>
</thead>
<tfoot data-ng-show="users && (users.length >= query.max || query.first > 0)">
<tr>
<td colspan="7">
<div class="table-nav">
<button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="users.length < query.max">{{:: 'next-page' | translate}}</button>
</div>
</td>
</tr>
</tfoot>
<tbody>
<tr ng-repeat="user in users">
<td><a href="#/realms/{{realm.realm}}/users/{{user.id}}">{{user.username}}</a></td>
<td>{{user.lastName}}</td>
<td>{{user.firstName}}</td>
<td>{{user.email}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/users/{{user.id}}">{{:: 'edit' | translate}}</td>
</tr>
<tr data-ng-show="!users || users.length == 0">
<td class="text-muted" data-ng-show="searchLoaded && users.length == 0 && lastSearch != null">{{:: 'no-role-members' | translate}}</td>
<td class="text-muted" data-ng-show="searchLoaded && users.length == 0 && lastSearch == null">{{:: 'no-role-members' | translate}}</td>
</tr>
</tbody>
</table>
</div>
<kc-menu></kc-menu>

View file

@ -10,5 +10,8 @@
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
<kc-tooltip>{{:: 'manage-permissions-role.tooltip' | translate}}</kc-tooltip>
</li>
<li ng-class="{active: path[6] && path[6] == 'users'}" data-ng-show="access.viewUsers">
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}/users">{{:: 'authz-users' | translate}}</a>
</li>
</ul>
</div>