display users in roles
This commit is contained in:
parent
785eed791d
commit
e1cb17586f
6 changed files with 171 additions and 1 deletions
|
@ -21,15 +21,20 @@ import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.ClientResource;
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.RoleResource;
|
||||||
import org.keycloak.admin.client.resource.RolesResource;
|
import org.keycloak.admin.client.resource.RolesResource;
|
||||||
import org.keycloak.events.admin.OperationType;
|
import org.keycloak.events.admin.OperationType;
|
||||||
import org.keycloak.events.admin.ResourceType;
|
import org.keycloak.events.admin.ResourceType;
|
||||||
import org.keycloak.representations.idm.RoleRepresentation;
|
import org.keycloak.representations.idm.RoleRepresentation;
|
||||||
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
import org.keycloak.testsuite.util.AdminEventPaths;
|
import org.keycloak.testsuite.util.AdminEventPaths;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -37,7 +42,6 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public class ClientRolesTest extends AbstractClientTest {
|
public class ClientRolesTest extends AbstractClientTest {
|
||||||
|
@ -132,4 +136,48 @@ public class ClientRolesTest extends AbstractClientTest {
|
||||||
assertFalse(rolesRsc.get("role-a").toRepresentation().isComposite());
|
assertFalse(rolesRsc.get("role-a").toRepresentation().isComposite());
|
||||||
assertEquals(0, rolesRsc.get("role-a").getRoleComposites().size());
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -976,6 +976,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'ClientRoleDetailCtrl'
|
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', {
|
.when('/realms/:realm/clients/:client/mappers', {
|
||||||
templateUrl : resourceUrl + '/partials/client-mappers.html',
|
templateUrl : resourceUrl + '/partials/client-mappers.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
|
|
|
@ -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) {
|
module.controller('ClientImportCtrl', function($scope, $location, $upload, realm, serverInfo, Notifications) {
|
||||||
|
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
|
|
|
@ -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) {
|
module.factory('UserGroupMembership', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups', {
|
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups', {
|
||||||
|
|
|
@ -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>
|
|
@ -10,5 +10,8 @@
|
||||||
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}/permissions">{{:: 'authz-permissions' | translate}}</a>
|
<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>
|
<kc-tooltip>{{:: 'manage-permissions-role.tooltip' | translate}}</kc-tooltip>
|
||||||
</li>
|
</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>
|
</ul>
|
||||||
</div>
|
</div>
|
Loading…
Reference in a new issue