From 7c5f5d5c80a14019bf974460843587df36f0aa19 Mon Sep 17 00:00:00 2001 From: Sonia Zorba <sonia.zorba@inaf.it> Date: Mon, 8 Jun 2020 17:52:04 +0200 Subject: [PATCH] Bugfix on permission removal; various small improvements --- .../src/components/GenericSearchResults.vue | 5 +-- gms-ui/src/components/Main.vue | 2 ++ gms-ui/src/components/MembersPanel.vue | 7 ++-- gms-ui/src/components/PermissionsPanel.vue | 2 +- gms-ui/src/components/User.vue | 12 +++++-- gms-ui/src/components/UserSearchResult.vue | 29 +++++++++++++--- gms-ui/src/store.js | 20 ++++++++--- .../ia2/gms/controller/MembersController.java | 22 ++++++------ .../gms/controller/PermissionsController.java | 7 ++-- .../ia2/gms/controller/SearchController.java | 5 +-- .../ia2/gms/controller/UsersController.java | 2 +- .../ia2/gms/manager/PermissionsManager.java | 4 +-- .../model/response/UserSearchResponse.java | 10 ++++++ .../java/it/inaf/ia2/gms/rap/RapClient.java | 10 ++++++ .../inaf/ia2/gms/service/SearchService.java | 2 ++ .../JWTWebServiceControllerTest.java | 34 ++++++++----------- 16 files changed, 118 insertions(+), 55 deletions(-) diff --git a/gms-ui/src/components/GenericSearchResults.vue b/gms-ui/src/components/GenericSearchResults.vue index 27d9199..ea3ae44 100644 --- a/gms-ui/src/components/GenericSearchResults.vue +++ b/gms-ui/src/components/GenericSearchResults.vue @@ -39,10 +39,7 @@ export default { this.$store.commit('openGroup', result.id); break; case 'USER': - client.openUserSearchResult(result.id) - .then(model => { - this.$store.commit('displayUserSearchResults', [result.label, model]); - }); + this.$store.dispatch('openUserPage', result.id); break; } }, diff --git a/gms-ui/src/components/Main.vue b/gms-ui/src/components/Main.vue index 115922f..055309c 100644 --- a/gms-ui/src/components/Main.vue +++ b/gms-ui/src/components/Main.vue @@ -54,6 +54,8 @@ export default { }), methods: { tabChanged: function(tabIndex) { + // reset paginator + this.input.paginatorPage = 1; switch (tabIndex) { case 0: client.fetchGroupsTab(this.input) diff --git a/gms-ui/src/components/MembersPanel.vue b/gms-ui/src/components/MembersPanel.vue index b8d2736..30ea021 100644 --- a/gms-ui/src/components/MembersPanel.vue +++ b/gms-ui/src/components/MembersPanel.vue @@ -2,9 +2,9 @@ <b-tab title="Members" v-if="model.permission === 'ADMIN' || model.permission === 'MANAGE_MEMBERS' || model.permission === 'VIEW_MEMBERS'"> <div v-if="model.membersPanel !== null"> <b-list-group v-for="member in model.membersPanel.items" id="members-list"> - <b-list-group-item href="#"> + <b-list-group-item href="#" @click="openUser(member)"> <div class="float-left"> - <User v-bind:user="member" /> + <User :user="member" :anchor="false" /> </div> <span v-if="model.permission === 'ADMIN' || model.permission === 'MANAGE_MEMBERS'" class="float-right"> <a href="#" v-on:click.stop="openRemoveMemberModal(member)" class="text-danger" title="Remove member"> @@ -52,6 +52,9 @@ export default { .then(panel => { this.$store.commit('updateMembersPanel', panel); }); + }, + openUser(member) { + this.$store.dispatch('openUserPage', member.id); } } } diff --git a/gms-ui/src/components/PermissionsPanel.vue b/gms-ui/src/components/PermissionsPanel.vue index 95f67a8..a8537de 100644 --- a/gms-ui/src/components/PermissionsPanel.vue +++ b/gms-ui/src/components/PermissionsPanel.vue @@ -12,7 +12,7 @@ <tbody> <tr v-for="up in model.permissionsPanel.items"> <td> - <User v-bind:user="up.user" /> + <User :user="up.user" :anchor="true" /> </td> <td>{{up.permission}}</td> <td> diff --git a/gms-ui/src/components/User.vue b/gms-ui/src/components/User.vue index d12e873..2c6c16e 100644 --- a/gms-ui/src/components/User.vue +++ b/gms-ui/src/components/User.vue @@ -1,12 +1,12 @@ <template> <div :id="'user-name-' + user.id"> - <span>{{user.displayName}}</span> + <component :is="anchor ? 'a' : 'span'" :href="anchor ? '#' : false" @click="openUser">{{user.displayName}}</component> <b-tooltip ref="user-tooltip" :target="'user-name-' + user.id" placement="bottom"> <div class="text-left"> <p><strong>User id</strong>: {{user.id}}</p> <p><strong>Identities</strong>:</p> <ul> - <li v-for="identity in user.identities"> + <li v-for="identity in user.identities" v-bind:key="identity.typedId"> {{identity.email}} ({{identity.type}}) </li> </ul> @@ -19,7 +19,13 @@ export default { name: 'User', props: { - user: Object + user: Object, + anchor: Boolean + }, + methods: { + openUser() { + this.$store.dispatch('openUserPage', this.user.id); + } } } </script> diff --git a/gms-ui/src/components/UserSearchResult.vue b/gms-ui/src/components/UserSearchResult.vue index a2b87c8..c648aff 100644 --- a/gms-ui/src/components/UserSearchResult.vue +++ b/gms-ui/src/components/UserSearchResult.vue @@ -1,9 +1,12 @@ <template> -<div class="mt-sm-3" v-if="userLabel !== null"> +<div class="mt-sm-3" v-if="user !== null"> <b-button variant="primary" class="float-right" v-on:click="back()">Back</b-button> - <h5>Results for <strong>{{userLabel}}</strong>:</h5> + <h5><strong>{{user.displayName}}</strong>:</h5> <b-container class="mt-sm-5"> + <b-row> + + </b-row> <b-row> <b-col class="text-left"> <h5>Is member of</h5> @@ -19,6 +22,24 @@ </li> </ul> </div> + + <h5 class="mt-5 mb-3">User info</h5> + <p><strong>User id</strong>: {{user.id}}</p> + <p><strong>Identities ({{user.identities.length}})</strong>:</p> + <b-row> + <b-col lg="10"> + <b-list-group> + <b-list-group-item v-for="identity in user.identities" v-bind:key="identity.typedId"> + <dl class="mb-0 ml-0 row"> + <dt class="col-3">Type</dt><dd class="col-9">{{identity.type}}</dd> + <dt class="col-3">Email</dt><dd class="col-9">{{identity.email}}</dd> + <dt class="col-3" v-if="identity.type === 'eduGAIN'"><abbr title="EduPerson Principal Name, an unique identifier used into federations.">EPPN</abbr></dt> + <dd class="col-9" v-if="identity.type === 'eduGAIN'">{{identity.typedId}}</dd> + </dl> + </b-list-group-item> + </b-list-group> + </b-col> + </b-row> </b-col> <b-col v-if="permissions.length > 0"> <h5>Permissions</h5> @@ -55,13 +76,13 @@ import { export default { name: 'UserSearchResult', computed: mapState({ - userLabel: state => state.model.userSearchResults.userLabel, + user: state => state.model.userSearchResults.user, groups: state => state.model.userSearchResults.groups, permissions: state => state.model.userSearchResults.permissions }), methods: { back() { - this.$store.commit('displaySearchResults'); + this.$store.commit('backFromUserPage'); }, openGroup(groupId) { this.$store.commit('openGroup', groupId); diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js index 0a44fd6..f175ec5 100644 --- a/gms-ui/src/store.js +++ b/gms-ui/src/store.js @@ -38,6 +38,7 @@ export default new Vuex.Store({ } }, loading: false, + previousPage: null, page: 'main' }, mutations: { @@ -100,11 +101,20 @@ export default new Vuex.Store({ updateSearchResults(state, results) { this.state.model.genericSearchResults = results; }, - displayUserSearchResults(state, data) { - this.state.page = 'userSearch'; - this.state.model.userSearchResults.userLabel = data[0]; - this.state.model.userSearchResults.groups = data[1].groups; - this.state.model.userSearchResults.permissions = data[1].permissions; + backFromUserPage(state) { + state.page = state.previousPage; + } + }, + actions: { + openUserPage({ state }, userId) { + state.previousPage = state.page; + client.openUserSearchResult(userId) + .then(model => { + state.page = 'userSearch'; + state.model.userSearchResults.user = model.user; + state.model.userSearchResults.groups = model.groups; + state.model.userSearchResults.permissions = model.permissions; + }); } }, getters: { diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java index 77469ed..792e2d5 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java @@ -1,17 +1,17 @@ package it.inaf.ia2.gms.controller; -import it.inaf.ia2.gms.authn.SessionData; import it.inaf.ia2.gms.manager.MembershipManager; import it.inaf.ia2.gms.manager.PermissionsManager; import it.inaf.ia2.gms.model.request.AddMemberRequest; -import it.inaf.ia2.gms.model.request.MemberRequest; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.Permission; import it.inaf.ia2.gms.model.RapUser; +import it.inaf.ia2.gms.model.request.PaginatedModelRequest; import it.inaf.ia2.gms.model.request.RemoveMemberRequest; import it.inaf.ia2.gms.model.request.TabRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; import it.inaf.ia2.gms.service.GroupsService; +import java.util.Collections; import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -36,18 +36,17 @@ public class MembersController { @Autowired private PermissionsManager permissionsManager; - @GetMapping(value = "/members", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @GetMapping(value = "/members", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<PaginatedData<RapUser>> getMembersTab(TabRequest request) { GroupEntity group = groupsService.getGroupById(request.getGroupId()); - List<RapUser> members = membershipManager.getMembers(group); - PaginatedData<RapUser> membersPanel = new PaginatedData<>(members, request.getPaginatorPage(), request.getPaginatorPageSize()); + PaginatedData<RapUser> membersPanel = getMembersPanel(group, request); return ResponseEntity.ok(membersPanel); } - @PostMapping(value = "/member", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @PostMapping(value = "/member", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<PaginatedData<RapUser>> addMember(@Valid @RequestBody AddMemberRequest request) { GroupEntity group = groupsService.getGroupById(request.getGroupId()); @@ -64,10 +63,10 @@ public class MembersController { permissionsManager.addPermission(group, request.getUserId(), request.getPermission()); } - return new ResponseEntity<>(getMembersPanel(request, group), HttpStatus.CREATED); + return new ResponseEntity<>(getMembersPanel(group, request), HttpStatus.CREATED); } - @DeleteMapping(value = "/member", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @DeleteMapping(value = "/member", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<PaginatedData<RapUser>> removeMember(@Valid RemoveMemberRequest request) { GroupEntity group = groupsService.getGroupById(request.getGroupId()); @@ -91,11 +90,14 @@ public class MembersController { permissionsManager.removePermission(group, request.getUserId()); } - return ResponseEntity.ok(getMembersPanel(request, group)); + return ResponseEntity.ok(getMembersPanel(group, request)); } - private PaginatedData<RapUser> getMembersPanel(MemberRequest request, GroupEntity group) { + private PaginatedData<RapUser> getMembersPanel(GroupEntity group, PaginatedModelRequest request) { List<RapUser> members = membershipManager.getMembers(group); + Collections.sort(members, (m1, m2) -> { + return m1.getDisplayName().compareTo(m2.getDisplayName()); + }); return new PaginatedData<>(members, request.getPaginatorPage(), request.getPaginatorPageSize()); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java index 3f7d8e8..5840978 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java @@ -10,6 +10,7 @@ import it.inaf.ia2.gms.model.Permission; import it.inaf.ia2.gms.model.UserPermission; import it.inaf.ia2.gms.model.request.TabRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,8 +39,7 @@ public class PermissionsController { public ResponseEntity<PaginatedData<UserPermission>> getPermissionsTab(TabRequest request) { GroupEntity group = groupsManager.getGroupById(request.getGroupId()); - List<UserPermission> permissions = permissionsManager.getAllPermissions(group); - PaginatedData<UserPermission> permissionsPanel = new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize()); + PaginatedData<UserPermission> permissionsPanel = getPermissionsPanel(group, request); return ResponseEntity.ok(permissionsPanel); } @@ -79,6 +79,9 @@ public class PermissionsController { private PaginatedData<UserPermission> getPermissionsPanel(GroupEntity group, PaginatedModelRequest request) { List<UserPermission> permissions = permissionsManager.getAllPermissions(group); + Collections.sort(permissions, (p1, p2) -> { + return p1.getUser().getDisplayName().compareTo(p2.getUser().getDisplayName()); + }); return new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize()); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java index 6920999..c612e9c 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java @@ -1,6 +1,7 @@ package it.inaf.ia2.gms.controller; import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.model.RapUser; import it.inaf.ia2.gms.model.response.PaginatedData; import it.inaf.ia2.gms.model.response.SearchResponseItem; import it.inaf.ia2.gms.model.response.UserSearchResponse; @@ -22,7 +23,7 @@ public class SearchController { @Autowired private SearchService searchService; - @GetMapping(value = "/search", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @GetMapping(value = "/search", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<PaginatedData<SearchResponseItem>> getSearchResults(@RequestParam("query") String query, @RequestParam("page") int page, @RequestParam("pageSize") int pageSize) { @@ -30,7 +31,7 @@ public class SearchController { return ResponseEntity.ok(response); } - @GetMapping(value = "/search/user/{userId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @GetMapping(value = "/search/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<UserSearchResponse> getSearchResultUser(@PathVariable("userId") String userId) { UserSearchResponse response = searchService.getUserSearchResult(sessionData.getUserId(), userId); diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java index 54577f6..595b1e4 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java @@ -16,7 +16,7 @@ public class UsersController { @Autowired private RapClient rapClient; - @GetMapping(value = "users", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + @GetMapping(value = "users", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<RapUser>> searchUsers(@RequestParam("search") String searchText) { return ResponseEntity.ok(rapClient.searchUsers(searchText)); } diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java index 7d08b2e..a1503f0 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java +++ b/gms/src/main/java/it/inaf/ia2/gms/manager/PermissionsManager.java @@ -63,7 +63,7 @@ public class PermissionsManager extends UserAwareComponent { public Permission getDirectUserPermission(GroupEntity group, String userId) { verifyUserCanManagePermissions(group); - List<PermissionEntity> permissions = permissionsService.findUserPermissions(group, getCurrentUserId()); + List<PermissionEntity> permissions = permissionsService.findUserPermissions(group, userId); for (PermissionEntity permission : permissions) { if (permission.getGroupId().equals(group.getId())) { return permission.getPermission(); @@ -74,7 +74,7 @@ public class PermissionsManager extends UserAwareComponent { public Permission getUserPermission(GroupEntity group, String userId) { verifyUserCanManagePermissions(group); - List<PermissionEntity> permissions = permissionsService.findUserPermissions(group, getCurrentUserId()); + List<PermissionEntity> permissions = permissionsService.findUserPermissions(group, userId); return PermissionUtils.getGroupPermission(group, permissions).orElse(null); } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java b/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java index 0ec8567..4d00c24 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java +++ b/gms/src/main/java/it/inaf/ia2/gms/model/response/UserSearchResponse.java @@ -1,12 +1,22 @@ package it.inaf.ia2.gms.model.response; +import it.inaf.ia2.gms.model.RapUser; import java.util.List; public class UserSearchResponse { + private RapUser user; private List<UserGroup> groups; private List<UserPermission> permissions; + public RapUser getUser() { + return user; + } + + public void setUser(RapUser user) { + this.user = user; + } + public List<UserGroup> getGroups() { return groups; } diff --git a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java index b13b1de..ddb51db 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java +++ b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java @@ -57,6 +57,16 @@ public class RapClient { this.refreshTokenRestTemplate = new RestTemplate(); } + public RapUser getUser(String userId) { + + String url = rapBaseUrl + "/user/" + userId; + + return httpCall(entity -> { + return rapRestTemplate.exchange(url, HttpMethod.GET, entity, new ParameterizedTypeReference<RapUser>() { + }).getBody(); + }); + } + public List<RapUser> getUsers(Set<String> identifiers) { if (identifiers.isEmpty()) { diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java index ac6b72a..9f59907 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java @@ -119,6 +119,8 @@ public class SearchService { sortByGroupCompleteName(permissions); response.setPermissions(permissions); + response.setUser(rapClient.getUser(targetUserId)); + return response; } diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java index a665c24..e47104e 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/JWTWebServiceControllerTest.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; @@ -141,17 +140,15 @@ public class JWTWebServiceControllerTest { } @Test - @Ignore public void testDeleteGroupByPath() throws Exception { - List<String> names = Arrays.asList("LBT", "INAF"); - - when(groupsService.findGroupByNames(names)).thenReturn(Optional.of(inaf)); + when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbt)); + when(groupsDAO.findGroupByParentAndName("lbt_id", "INAF")).thenReturn(Optional.of(inaf)); - mockMvc.perform(delete("/ws/jwt/group/LBT.INAF")) + mockMvc.perform(delete("/ws/jwt/LBT.INAF")) .andExpect(status().isNoContent()); - verify(groupsService, times(1)).deleteGroup(eq(inaf)); + verify(groupsDAO, times(1)).deleteGroupById(eq(inaf.getId())); } @Test @@ -176,19 +173,17 @@ public class JWTWebServiceControllerTest { } @Test - @Ignore public void testRemoveMember() throws Exception { List<String> names = Arrays.asList("LBT", "INAF"); - GroupEntity inaf = getInafGroup(); - - when(groupsService.findGroupByNames(names)).thenReturn(Optional.of(inaf)); - - mockMvc.perform(delete("/ws/jwt/membership/LBT.INAF?userId=user_id")) + when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbt)); + when(groupsDAO.findGroupByParentAndName("lbt_id", "INAF")).thenReturn(Optional.of(inaf)); + + mockMvc.perform(delete("/ws/jwt/membership/LBT.INAF?user_id=userId")) .andExpect(status().isNoContent()); - verify(membershipManager, times(1)).removeMember(eq(inaf), eq("user_id")); + verify(membershipManager, times(1)).removeMember(eq(inaf), eq("userId")); } @Test @@ -220,17 +215,18 @@ public class JWTWebServiceControllerTest { } @Test - @Ignore public void testRemovePermission() throws Exception { List<String> names = Arrays.asList("LBT", "INAF"); - GroupEntity inaf = getInafGroup(); - when(groupsService.findGroupByNames(names)).thenReturn(Optional.of(inaf)); + + //when(groupsService.findGroupByNames(names)).thenReturn(Optional.of(inaf)); + when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbt)); + when(groupsDAO.findGroupByParentAndName("lbt_id", "INAF")).thenReturn(Optional.of(inaf)); - mockMvc.perform(delete("/ws/jwt/permission?LBT.INAF?userId=user_id&permission=ADMIN")) + mockMvc.perform(delete("/ws/jwt/permission/LBT.INAF?user_id=userId&permission=ADMIN")) .andExpect(status().isNoContent()); - verify(permissionsManager, times(1)).removePermission(eq(inaf), eq("user_id")); + verify(permissionsManager, times(1)).removePermission(eq(inaf), eq("userId")); } private GroupEntity getRoot() { -- GitLab