user group membership
This commit is contained in:
parent
33ac048c8c
commit
21119604c6
9 changed files with 262 additions and 2 deletions
|
@ -14,6 +14,7 @@ import java.util.Map;
|
|||
public class GroupRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String path;
|
||||
protected Map<String, List<String>> attributes;
|
||||
protected List<String> realmRoles;
|
||||
protected Map<String, List<String>> clientRoles;
|
||||
|
@ -35,6 +36,14 @@ public class GroupRepresentation {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public List<String> getRealmRoles() {
|
||||
return realmRoles;
|
||||
}
|
||||
|
|
|
@ -447,6 +447,21 @@ module.config([ '$routeProvider', function($routeProvider) {
|
|||
},
|
||||
controller : 'UserRoleMappingCtrl'
|
||||
})
|
||||
.when('/realms/:realm/users/:user/groups', {
|
||||
templateUrl : resourceUrl + '/partials/user-group-membership.html',
|
||||
resolve : {
|
||||
realm : function(RealmLoader) {
|
||||
return RealmLoader();
|
||||
},
|
||||
user : function(UserLoader) {
|
||||
return UserLoader();
|
||||
},
|
||||
groups : function(GroupListLoader) {
|
||||
return GroupListLoader();
|
||||
}
|
||||
},
|
||||
controller : 'UserGroupMembershipCtrl'
|
||||
})
|
||||
.when('/realms/:realm/users/:user/sessions', {
|
||||
templateUrl : resourceUrl + '/partials/user-sessions.html',
|
||||
resolve : {
|
||||
|
|
|
@ -1090,3 +1090,64 @@ module.controller('UserFederationMapperCreateCtrl', function($scope, realm, prov
|
|||
|
||||
});
|
||||
|
||||
module.controller('UserGroupMembershipCtrl', function($scope, $route, realm, groups, user, UserGroupMembership, UserGroupMapping, Notifications, $location, Dialog) {
|
||||
$scope.realm = realm;
|
||||
$scope.user = user;
|
||||
$scope.groupList = groups;
|
||||
$scope.selectedGroup = null;
|
||||
$scope.tree = [];
|
||||
|
||||
UserGroupMembership.query({realm: realm.realm, userId: user.id}, function(data) {
|
||||
$scope.groupMemberships = data;
|
||||
|
||||
});
|
||||
|
||||
$scope.joinGroup = function() {
|
||||
if (!$scope.tree.currentNode) {
|
||||
Notifications.error('Please select a group to add');
|
||||
return;
|
||||
};
|
||||
UserGroupMapping.update({realm: realm.realm, userId: user.id, groupId: $scope.tree.currentNode.id}, function() {
|
||||
Notifications.success('Added group membership');
|
||||
$route.reload();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.leaveGroup = function() {
|
||||
UserGroupMapping.remove({realm: realm.realm, userId: user.id, groupId: $scope.selectedGroup.id}, function() {
|
||||
Notifications.success('Removed group membership');
|
||||
$route.reload();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var isLeaf = function(node) {
|
||||
return node.id != "realm" && (!node.subGroups || node.subGroups.length == 0);
|
||||
};
|
||||
|
||||
$scope.getGroupClass = function(node) {
|
||||
if (node.id == "realm") {
|
||||
return 'pficon pficon-users';
|
||||
}
|
||||
if (isLeaf(node)) {
|
||||
return 'normal';
|
||||
}
|
||||
if (node.subGroups.length && node.collapsed) return 'collapsed';
|
||||
if (node.subGroups.length && !node.collapsed) return 'expanded';
|
||||
return 'collapsed';
|
||||
|
||||
}
|
||||
|
||||
$scope.getSelectedClass = function(node) {
|
||||
if (node.selected) {
|
||||
return 'selected';
|
||||
} else if ($scope.cutNode && $scope.cutNode.id == node.id) {
|
||||
return 'cut';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -1512,6 +1512,26 @@ module.factory('GroupCompositeClientRoleMapping', function($resource) {
|
|||
});
|
||||
});
|
||||
|
||||
module.factory('UserGroupMembership', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups', {
|
||||
realm : '@realm',
|
||||
userId : '@userId'
|
||||
});
|
||||
});
|
||||
|
||||
module.factory('UserGroupMapping', function($resource) {
|
||||
return $resource(authUrl + '/admin/realms/:realm/users/:userId/groups/:groupId', {
|
||||
realm : '@realm',
|
||||
userId : '@userId',
|
||||
groupId : '@groupId'
|
||||
}, {
|
||||
update : {
|
||||
method : 'PUT'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -94,7 +94,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
<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}}/users">Users</a></li>
|
||||
<li>{{user.username}}</li>
|
||||
</ol>
|
||||
|
||||
<kc-tabs-user></kc-tabs-user>
|
||||
|
||||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<div class="form-group" kc-read-only="!access.manageUsers">
|
||||
<label class="col-md-1 control-label" class="control-label"></label>
|
||||
|
||||
<div class="col-md-8" >
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="5">
|
||||
<div class="form-inline">
|
||||
<label class="control-label">Group Membership</label>
|
||||
<kc-tooltip>Groups user is a member of. Select a listed group and click the Leave button to leave the group.</kc-tooltip>
|
||||
|
||||
<div class="pull-right" data-ng-show="access.manageUsers">
|
||||
<button id="leaveGroups" class="btn btn-default" ng-click="leaveGroup()">Leave</button>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<select id="groupMembership" class="form-control" size=5
|
||||
ng-model="selectedGroup"
|
||||
ng-options="r.path for r in groupMemberships">
|
||||
<option style="display:none" value="">select a type</option>
|
||||
</select>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="kc-table-actions" colspan="5">
|
||||
|
||||
<div class="form-inline">
|
||||
<label class="control-label">Available Groups</label>
|
||||
<kc-tooltip>Groups a user can join. Select a group and click the join button.</kc-tooltip>
|
||||
|
||||
<div class="pull-right" data-ng-show="access.manageUsers">
|
||||
<button id="joinGroup" class="btn btn-default" ng-click="joinGroup()">Join</button>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <div
|
||||
tree-id="tree"
|
||||
angular-treeview="true"
|
||||
tree-model="groupList"
|
||||
node-id="id"
|
||||
node-label="name"
|
||||
node-children="subGroups" >
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<kc-menu></kc-menu>
|
1
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
Normal file → Executable file
1
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-user.html
Normal file → Executable file
|
@ -10,6 +10,7 @@
|
|||
<li ng-class="{active: path[4] == 'user-attributes'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-attributes">Attributes</a></li>
|
||||
<li ng-class="{active: path[4] == 'user-credentials'}" data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/user-credentials">Credentials</a></li>
|
||||
<li ng-class="{active: path[4] == 'role-mappings'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/role-mappings">Role Mappings</a></li>
|
||||
<li ng-class="{active: path[4] == 'groups'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/groups">Groups</a></li>
|
||||
<li ng-class="{active: path[4] == 'consents'}"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/consents">Consents</a></li>
|
||||
<li ng-class="{active: path[4] == 'sessions'}" ><a href="#/realms/{{realm.realm}}/users/{{user.id}}/sessions">Sessions</a></li>
|
||||
<li ng-class="{active: path[4] == 'federated-identity' || path[1] == 'federated-identity'}" data-ng-show="user.federatedIdentities != null"><a href="#/realms/{{realm.realm}}/users/{{user.id}}/federated-identity">Identity Provider Links</a></li>
|
||||
|
|
|
@ -60,10 +60,25 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ModelToRepresentation {
|
||||
public static void buildGroupPath(StringBuilder sb, GroupModel group) {
|
||||
if (group.getParent() != null) {
|
||||
buildGroupPath(sb, group.getParent());
|
||||
}
|
||||
sb.append('/').append(group.getName());
|
||||
}
|
||||
|
||||
public static String buildGroupPath(GroupModel group) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
buildGroupPath(sb, group);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static GroupRepresentation toRepresentation(GroupModel group, boolean full) {
|
||||
GroupRepresentation rep = new GroupRepresentation();
|
||||
rep.setId(group.getId());
|
||||
rep.setName(group.getName());
|
||||
rep.setPath(buildGroupPath(group));
|
||||
if (!full) return rep;
|
||||
// Role mappings
|
||||
Set<RoleModel> roles = group.getRoleMappings();
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ModelDuplicateException;
|
||||
|
@ -36,6 +37,7 @@ import org.keycloak.provider.ProviderFactory;
|
|||
import org.keycloak.representations.idm.ClientMappingsRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
|
||||
import org.keycloak.representations.idm.GroupRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserConsentRepresentation;
|
||||
|
@ -911,4 +913,57 @@ public class UsersResource {
|
|||
return clientSession;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}/groups")
|
||||
@NoCache
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public List<GroupRepresentation> groupMembership(@PathParam("id") String id) {
|
||||
auth.requireView();
|
||||
|
||||
UserModel user = session.users().getUserById(id, realm);
|
||||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
List<GroupRepresentation> memberships = new LinkedList<>();
|
||||
for (GroupModel group : user.getGroups()) {
|
||||
memberships.add(ModelToRepresentation.toRepresentation(group, false));
|
||||
}
|
||||
return memberships;
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}/groups/{groupId}")
|
||||
@NoCache
|
||||
public void removeMembership(@PathParam("id") String id, @PathParam("groupId") String groupId) {
|
||||
auth.requireManage();
|
||||
|
||||
UserModel user = session.users().getUserById(id, realm);
|
||||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
GroupModel group = session.realms().getGroupById(groupId, realm);
|
||||
if (group == null) {
|
||||
throw new NotFoundException("Group not found");
|
||||
}
|
||||
if (user.isMemberOf(group)) user.leaveGroup(group);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}/groups/{groupId}")
|
||||
@NoCache
|
||||
public void joinGroup(@PathParam("id") String id, @PathParam("groupId") String groupId) {
|
||||
auth.requireManage();
|
||||
|
||||
UserModel user = session.users().getUserById(id, realm);
|
||||
if (user == null) {
|
||||
throw new NotFoundException("User not found");
|
||||
}
|
||||
GroupModel group = session.realms().getGroupById(groupId, realm);
|
||||
if (group == null) {
|
||||
throw new NotFoundException("Group not found");
|
||||
}
|
||||
if (!user.isMemberOf(group)) user.joinGroup(group);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue