keycloak-aplcache
Changes
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js 47(+47 -0)
Details
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index c148a02..fe74ff2 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -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 : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
index 7e82f32..4e3b986 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/groups.js
@@ -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();
+
+});
+
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index c2d8c32..065f831 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -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',
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html
new file mode 100755
index 0000000..6c20930
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/group-members.html
@@ -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>
\ No newline at end of file
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
index 6c973e1..87253c1 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupResource.java
@@ -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;
+ }
+
+
}