diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js index b5f321aa30b71e6524f267dc4c80e438d76aede8..b84d0034088859964d613f2eef04ff552f59d777 100644 --- a/gms-ui/src/api/server/index.js +++ b/gms-ui/src/api/server/index.js @@ -108,5 +108,25 @@ export default { 'Accept': 'application/json', } }); + }, + addPermission (userId, permission, input) { + let url = BASE_API_URL + 'permission'; + + return apiRequest(url, { + method: 'POST', + cache: 'no-cache', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify({ + groupId: input.selectedGroupId, + userId: userId, + permission: permission, + paginatorPageSize: input.paginatorPageSize, + paginatorPage: input.paginatorPage + }) + }); } }; diff --git a/gms-ui/src/components/Main.vue b/gms-ui/src/components/Main.vue index 36a15b26068d6884bf0074068c4f0c6b03a5b897..0fc06ebe5f89b1fdb41c0bd944a1439c5ae66c4f 100644 --- a/gms-ui/src/components/Main.vue +++ b/gms-ui/src/components/Main.vue @@ -25,6 +25,7 @@ import GroupsPanel from './GroupsPanel.vue' import MembersPanel from './MembersPanel.vue' import PermissionsPanel from './PermissionsPanel.vue' import NewGroupModal from './modals/NewGroupModal.vue' +import AddMemberModal from './modals/AddMemberModal.vue' import AddPermissionModal from './modals/AddPermissionModal.vue' import { mapState } from 'vuex'; @@ -36,6 +37,7 @@ export default { MembersPanel, PermissionsPanel, NewGroupModal, + AddMemberModal, AddPermissionModal }, computed: mapState({ diff --git a/gms-ui/src/components/modals/AddMemberModal.vue b/gms-ui/src/components/modals/AddMemberModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..23d47959172fb7f90f70188bad2bda21e25663a3 --- /dev/null +++ b/gms-ui/src/components/modals/AddMemberModal.vue @@ -0,0 +1,27 @@ +<template> + <b-modal id="add-member-modal" title="Add member" ok-title="Add" @ok="addMember"> + <SearchUser /> + </b-modal> +</template> + +<script> +import client from 'api-client'; +import SearchUser from './SearchUser.vue' + +export default { + name: 'AddMemberModal', + components: { + SearchUser + }, + data: function() { + return { + permission: 'VIEW_MEMBERS' + } + }, + methods: { + addMember: function() { + + } + } +}; +</script> diff --git a/gms-ui/src/components/modals/AddPermissionModal.vue b/gms-ui/src/components/modals/AddPermissionModal.vue index 7fccb7ec88f2ec50298e1c4be4adb62336ab3ac1..7b792e9fb52dae185fecc7ecc5cb4b97d677b923 100644 --- a/gms-ui/src/components/modals/AddPermissionModal.vue +++ b/gms-ui/src/components/modals/AddPermissionModal.vue @@ -1,6 +1,6 @@ <template> <b-modal id="add-permission-modal" title="Add permission" ok-title="Add" @ok="addPermission"> - <SearchUser /> + <SearchUser ref="searchUser" /> </b-modal> </template> @@ -15,12 +15,21 @@ export default { }, data: function() { return { - } }, methods: { - addPermission: function() { + addPermission: function(event) { + // Prevent modal from closing + event.preventDefault(); + + let userId = this.$refs.searchUser.selectedUser; + let permission = this.$refs.searchUser.permission; + client.addPermission(userId, permission, this.$store.state.input) + .then(res => { + this.$store.commit('updatePermissionsPanel', res); + this.$bvModal.hide('add-permission-modal'); + }); } } }; diff --git a/gms-ui/src/components/modals/SearchUser.vue b/gms-ui/src/components/modals/SearchUser.vue index 391a54bb383f0f4a84902c5a7fc166df7e1ea08a..20ecfa1b2b74d94a71d78817e4fa1f110c1ea7ce 100644 --- a/gms-ui/src/components/modals/SearchUser.vue +++ b/gms-ui/src/components/modals/SearchUser.vue @@ -11,12 +11,13 @@ <label class="w-25" for="user-input" v-if="users.length > 0">Selected user:</label> <b-form-select v-model="selectedUser" :options="users" class="w-75" v-if="users.length > 0"></b-form-select> <div class="w-25"></div> - <b-form-group label="Permissions:" v-if="users.length > 0" class="w-75 mt-3"> + <b-form-group label="Permission:" v-if="users.length > 0 && model.permission === 'ADMIN'" class="w-75 mt-3"> <b-form-radio-group - id="permissions-radio-group" + id="permission-radio-group" v-model="permission"> <b-form-radio value="ADMIN">Admin</b-form-radio> <b-form-radio value="MANAGE_MEMBERS">Manage members</b-form-radio> + <b-form-radio value="VIEW_MEMBERS">View members</b-form-radio> </b-form-radio-group> </b-form-group> </b-form> @@ -24,15 +25,19 @@ <script> import client from 'api-client'; +import { mapState } from 'vuex'; export default { name: 'SearchUser', + computed: mapState({ + model: state => state.model, + }), data: function() { return { searchInput: null, users: [], selectedUser: null, - permission: null + permission: 'VIEW_MEMBERS' } }, methods: { diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js index c6e95938d4ec09717f28576cf62fcf007f60a5f2..44ce164d27bc34d376eab01eb6b16c4d4f467ace 100644 --- a/gms-ui/src/store.js +++ b/gms-ui/src/store.js @@ -28,6 +28,9 @@ export default new Vuex.Store({ }, updateGroupsPanel(state, groupsPanel) { this.state.model.groupsPanel = groupsPanel; + }, + updatePermissionsPanel(state, permissionsPanel) { + this.state.model.permissionsPanel = permissionsPanel; } }, getters: { diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java index 4b91c0e98009cfb6de8eac0cb903df4ee6c6c3c8..dc12bfb49506810ff3e7b8c150400f2a42d2362e 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java @@ -56,7 +56,7 @@ public class GroupsController { GroupEntity parent = groupsService.getGroupById(request.getParentGroupId()); - if (permissionsService.getGroupPermission(parent, session.getUserId()) != Permission.ADMIN) { + if (permissionsService.getUserPermissionForGroup(parent, session.getUserId()) != Permission.ADMIN) { throw new UnauthorizedException("Missing admin privileges"); } @@ -84,7 +84,7 @@ public class GroupsController { GroupEntity group = groupsService.getGroupById(groupId); - if (permissionsService.getGroupPermission(group, session.getUserId()) != Permission.ADMIN) { + if (permissionsService.getUserPermissionForGroup(group, session.getUserId()) != Permission.ADMIN) { throw new UnauthorizedException("Missing admin privileges"); } 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 9c204e7165ce5fe2eddea59b1c491fd710d6722b..fbf9879277ea6080b49a103e8017e72965100a51 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 @@ -1,13 +1,23 @@ package it.inaf.ia2.gms.controller; import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.exception.UnauthorizedException; +import it.inaf.ia2.gms.model.PermissionRequest; +import it.inaf.ia2.gms.model.PaginatedData; import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.model.UserPermission; +import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.service.GroupsService; import it.inaf.ia2.gms.service.PermissionsService; +import java.util.List; +import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController @@ -16,11 +26,43 @@ public class PermissionsController { @Autowired private SessionData session; + @Autowired + private GroupsService groupsService; + @Autowired private PermissionsService permissionsService; - @DeleteMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public ResponseEntity deletePermission(@RequestParam("user") String userId, @RequestParam("group") String group, @RequestParam("permission") Permission permission) { - return null; + @PostMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public ResponseEntity<PaginatedData<UserPermission>> addPermission(@Valid @RequestBody PermissionRequest request) { + + GroupEntity group = groupsService.getGroupById(request.getGroupId()); + verifyAdminSession(group); + + permissionsService.addPermission(group, request.getUserId(), request.getPermission()); + + return new ResponseEntity<>(getPermissionsPanel(group, request), HttpStatus.CREATED); + } + + @DeleteMapping(value = "/permission", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public ResponseEntity<PaginatedData<UserPermission>> deletePermission(@Valid PermissionRequest request) { + + GroupEntity group = groupsService.getGroupById(request.getGroupId()); + verifyAdminSession(group); + + permissionsService.deletePermission(group, request.getUserId(), request.getPermission()); + + return ResponseEntity.ok(getPermissionsPanel(group, request)); + } + + private void verifyAdminSession(GroupEntity group) { + Permission currentNodePermissions = permissionsService.getUserPermissionForGroup(group, session.getUserId()); + if (currentNodePermissions != Permission.ADMIN) { + throw new UnauthorizedException("Only admin users can handle permissions"); + } + } + + private PaginatedData<UserPermission> getPermissionsPanel(GroupEntity group, PermissionRequest request) { + List<UserPermission> permissions = permissionsService.getAllPermissions(group); + return new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize()); } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/PermissionRequest.java b/gms/src/main/java/it/inaf/ia2/gms/model/PermissionRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..17764538ba035424c5efe042ecd0dfa820b84aeb --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/model/PermissionRequest.java @@ -0,0 +1,38 @@ +package it.inaf.ia2.gms.model; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +public class PermissionRequest extends PaginatedModelRequest { + + @NotEmpty + private String groupId; + @NotEmpty + private String userId; + @NotNull + private Permission permission; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public Permission getPermission() { + return permission; + } + + public void setPermission(Permission permission) { + this.permission = permission; + } +} diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelBuilder.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelBuilder.java index f47426daf030357e14cdf6ddd3bd355f80d38817..a0577470119ad1e1571e531be8713011ea04ce76 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelBuilder.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelBuilder.java @@ -36,7 +36,7 @@ public class GroupsModelBuilder { response.setBreadcrumbs(groupsService.getBreadcrumbs(group.getPath())); - Permission currentNodePermissions = permissionsService.getGroupPermission(group, userId); + Permission currentNodePermissions = permissionsService.getUserPermissionForGroup(group, userId); response.setPermission(currentNodePermissions); switch (request.getTab()) { @@ -51,7 +51,7 @@ public class GroupsModelBuilder { break; case "permissions": if (currentNodePermissions == Permission.ADMIN) { - List<UserPermission> permissions = permissionsService.getUserPermissions(group); + List<UserPermission> permissions = permissionsService.getAllPermissions(group); response.setPermissionsPanel(new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize())); } break; diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java index 16476167058884682ecd9d2bdd20533948b3ee23..1960fd236eed5892a211064ec465e38e20d8e2bb 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java @@ -67,7 +67,7 @@ public class GroupsService { GroupEntity group = getGroupById(groupId); - if (permissionsService.getGroupPermission(group, userId) != Permission.ADMIN) { + if (permissionsService.getUserPermissionForGroup(group, userId) != Permission.ADMIN) { throw new UnauthorizedException("Missing admin privileges"); } diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java index 96170b11d7c72209c72164e02814367184758df3..ea9761d257384e8b3e216bb83178c270eed8a269 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java @@ -29,7 +29,7 @@ public class PermissionsService { this.rapClient = rapClient; } - public List<UserPermission> getUserPermissions(GroupEntity group) { + public List<UserPermission> getAllPermissions(GroupEntity group) { List<PermissionEntity> permissions = permissionsDAO.getGroupsPermissions(group.getId()); @@ -55,7 +55,7 @@ public class PermissionsService { return result; } - public Permission getGroupPermission(GroupEntity group, String userId) { + public Permission getUserPermissionForGroup(GroupEntity group, String userId) { List<PermissionEntity> permissions = permissionsDAO.findUserPermissions(userId, group.getPath()); return PermissionUtils.getGroupPermission(group, permissions).orElse(null); } @@ -78,7 +78,7 @@ public class PermissionsService { public Optional<PermissionEntity> findPermissionEntity(String groupId, String userId, Permission permission) { return permissionsDAO.findPermissionEntity(groupId, userId, permission); } - + public void movePermissions(String fromUserId, String toUserId) { permissionsDAO.movePermissions(fromUserId, toUserId); } diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f36cb3a1f4f9b90f260994f6b9e591d9347915ad --- /dev/null +++ b/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java @@ -0,0 +1,103 @@ +package it.inaf.ia2.gms.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import it.inaf.ia2.gms.authn.SessionData; +import it.inaf.ia2.gms.model.Permission; +import it.inaf.ia2.gms.model.PermissionRequest; +import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.service.GroupsService; +import it.inaf.ia2.gms.service.PermissionsService; +import static org.hamcrest.CoreMatchers.is; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.eq; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +@RunWith(MockitoJUnitRunner.class) +public class PermissionsControllerTest { + + @Mock + private SessionData session; + + @Mock + private GroupsService groupsService; + + @Mock + private PermissionsService permissionsService; + + @InjectMocks + private PermissionsController controller; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper mapper = new ObjectMapper(); + + @Before + public void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).build(); + } + + @Test + public void testAddPermission() throws Exception { + + when(session.getUserId()).thenReturn("admin_id"); + + GroupEntity group = new GroupEntity(); + group.setId("group_id"); + group.setName("GroupName"); + group.setPath("parent_id.group_id"); + + when(groupsService.getGroupById(eq("group_id"))).thenReturn(group); + when(permissionsService.getUserPermissionForGroup(eq(group), eq("admin_id"))).thenReturn(Permission.ADMIN); + + PermissionRequest request = new PermissionRequest(); + request.setGroupId(group.getId()); + request.setUserId("user_id"); + request.setPermission(Permission.ADMIN); + request.setPaginatorPage(1); + request.setPaginatorPageSize(10); + + mockMvc.perform(post("/permission") + .content(mapper.writeValueAsString(request)) + .contentType(MediaType.APPLICATION_JSON_UTF8)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.currentPage", is(1))); + + verify(permissionsService, times(1)).addPermission(eq(group), eq("user_id"), eq(Permission.ADMIN)); + } + + @Test + public void testDeletePermission() throws Exception { + + when(session.getUserId()).thenReturn("admin_id"); + + GroupEntity group = new GroupEntity(); + group.setId("group_id"); + group.setName("GroupName"); + group.setPath("parent_id.group_id"); + + when(groupsService.getGroupById(eq("group_id"))).thenReturn(group); + when(permissionsService.getUserPermissionForGroup(eq(group), eq("admin_id"))).thenReturn(Permission.ADMIN); + + mockMvc.perform(delete("/permission?groupId=group_id&userId=user_id&permission=ADMIN&paginatorPage=1&paginatorPageSize=10")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.currentPage", is(1))); + + verify(permissionsService, times(1)).deletePermission(eq(group), eq("user_id"), eq(Permission.ADMIN)); + } +} diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java index 07be29526e48ba0743dd7bb6e8d8a9c89a0e910d..3d5c94493ce32cb7f45109d5ded8c977fd62ea1d 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java @@ -61,7 +61,7 @@ public class PermissionsServiceIntegrationTest { superAdminPermission.setGroupPath(root.getPath()); permissionsDAO.createPermission(superAdminPermission); - List<UserPermission> permissions = permissionsService.getUserPermissions(root); + List<UserPermission> permissions = permissionsService.getAllPermissions(root); assertEquals(1, permissions.size()); assertEquals(Permission.ADMIN, permissions.get(0).getPermission());