group membership ui

This commit is contained in:
Bill Burke 2015-11-12 13:16:30 -05:00
parent 0d20e3c7ff
commit d7ea66ad44
5 changed files with 158 additions and 1 deletions

View file

@ -643,9 +643,21 @@ module.config([ '$routeProvider', function($routeProvider) {
group : function(GroupLoader) {
return GroupLoader();
}
},
},
controller : 'GroupDetailCtrl'
})
.when('/realms/:realm/groups/:group/members', {
templateUrl : resourceUrl + '/partials/group-members.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
group : function(GroupLoader) {
return GroupLoader();
}
},
controller : 'GroupMembersCtrl'
})
.when('/realms/:realm/groups/:group/role-mappings', {
templateUrl : resourceUrl + '/partials/group-role-mappings.html',
resolve : {

View file

@ -319,3 +319,50 @@ module.controller('GroupRoleMappingCtrl', function($scope, $http, realm, group,
});
module.controller('GroupMembersCtrl', function($scope, realm, group, GroupMembership) {
$scope.realm = realm;
$scope.page = 0;
$scope.query = {
realm: realm.realm,
groupId: group.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() {
console.log("query.search: " + $scope.query.search);
$scope.searchLoaded = false;
$scope.users = GroupMembership.query($scope.query, function() {
console.log('search loaded');
$scope.searchLoaded = true;
$scope.lastSearch = $scope.query.search;
});
};
$scope.searchQuery();
});

View file

@ -1512,6 +1512,14 @@ module.factory('GroupCompositeClientRoleMapping', function($resource) {
});
});
module.factory('GroupMembership', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/groups/:groupId/members', {
realm : '@realm',
groupId : '@groupId'
});
});
module.factory('UserGroupMembership', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups', {
realm : '@realm',

View file

@ -0,0 +1,50 @@
<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}}/groups">Groups</a></li>
<li>{{group.name}}</li>
</ol>
<kc-tabs-group></kc-tabs-group>
<table class="table table-striped table-bordered">
<caption data-ng-show="users" class="hidden">Table of group members</caption>
<thead>
<tr>
<tr data-ng-show="searchLoaded && users.length > 0">
<th>Username</th>
<th>Last Name</th>
<th>First Name</th>
<th>Email</th>
<th>Actions</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</button>
<button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
<button data-ng-click="nextPage()" class="next" ng-disabled="users.length < query.max">Next page</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">
<button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/users/{{user.id}}">Edit</button>
</td>
</tr>
<tr data-ng-show="!users || users.length == 0">
<td class="text-muted" data-ng-show="searchLoaded && users.length == 0 && lastSearch != null">No group members</td>
<td class="text-muted" data-ng-show="searchLoaded && users.length == 0 && lastSearch == null">No group members</td>
</tr>
</tbody>
</table>
</div>
<kc-menu></kc-menu>

View file

@ -10,6 +10,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@ -19,12 +20,15 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -256,6 +260,42 @@ public class GroupResource {
}
/**
* Get users
*
* Returns a list of users, filtered according to query parameters
*
* @param firstResult Pagination offset
* @param maxResults Pagination size
* @return
*/
@GET
@NoCache
@Path("{id}/members")
@Produces(MediaType.APPLICATION_JSON)
public List<UserRepresentation> getMembers(@PathParam("id") String id,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults) {
auth.requireView();
GroupModel group = session.realms().getGroupById(id, realm);
if (group == null) {
throw new NotFoundException("Group not found");
}
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : -1;
List<UserRepresentation> results = new ArrayList<UserRepresentation>();
List<UserModel> userModels = session.users().getGroupMembers(realm, group, firstResult, maxResults);
for (UserModel user : userModels) {
results.add(ModelToRepresentation.toRepresentation(user));
}
return results;
}
}