Skip to content
......@@ -50,9 +50,9 @@ public class GetUserGroupsTest extends BaseGmsClientTest {
CompletableFuture response = CompletableFuture.completedFuture(getMockedStreamResponse(200, body));
when(httpClient.sendAsync(any(), any())).thenReturn(response);
List<String> groups = gmsClient.listGroups("LBT.");
List<String> groups = gmsClient.listGroups("LBT", false);
verify(httpClient, times(1)).sendAsync(endpointEq("GET", "ws/jwt/list/LBT."), any());
verify(httpClient, times(1)).sendAsync(endpointEq("GET", "groups?parent=LBT&recursive=false"), any());
assertEquals(2, groups.size());
assertEquals("INAF", groups.get(0));
......
......@@ -28,6 +28,6 @@ public class RemoveMemberTest extends BaseGmsClientTest {
when(httpClient.sendAsync(any(), any())).thenReturn(response);
gmsClient.removeMember("LBT.INAF", "user");
verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "ws/jwt/membership/LBT.INAF?user_id=user"), any());
verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "membership?group=LBT.INAF&user_id=user"), any());
}
}
......@@ -28,6 +28,6 @@ public class RemovePermissionTest extends BaseGmsClientTest {
when(httpClient.sendAsync(any(), any())).thenReturn(response);
gmsClient.removePermission("LBT.INAF", "user");
verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "ws/jwt/permission/LBT.INAF?user_id=user"), any());
verify(httpClient, times(1)).sendAsync(endpointEq("DELETE", "permission?group=LBT.INAF&user_id=user"), any());
}
}
......@@ -9,6 +9,12 @@
<b-spinner variant="primary" style="width: 3rem; height: 3rem;" label="Loading"></b-spinner>
</div>
</div>
<div class="mt-4">
<p class="text-center">&mdash;&nbsp; Powered by
<img src="@/assets/logo-ia2-small.jpg" alt="logo IA2" /> &nbsp;
<a href="https://www.ia2.inaf.it/" target="_blank">IA2</a> &nbsp;&mdash;
</p>
</div>
</div>
</template>
......
......@@ -54,7 +54,7 @@ function loading(value) {
export default {
fetchHomePageModel(input) {
let url = BASE_API_URL +
'home?groupId=' + input.selectedGroupId +
'ui/home?groupId=' + input.selectedGroupId +
'&paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage;
return apiRequest({
......@@ -70,7 +70,7 @@ export default {
},
fetchGroupsTab(input) {
let url = BASE_API_URL +
'groups?groupId=' + input.selectedGroupId +
'ui/groups?groupId=' + input.selectedGroupId +
'&paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage +
'&onlyPanel=false';
......@@ -90,7 +90,7 @@ export default {
},
fetchGroupsPanel(input) {
let url = BASE_API_URL +
'groups?groupId=' + input.selectedGroupId +
'ui/groups?groupId=' + input.selectedGroupId +
'&paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage +
'&onlyPanel=true';
......@@ -110,7 +110,7 @@ export default {
},
fetchMembersPanel(input) {
let url = BASE_API_URL +
'members?groupId=' + input.selectedGroupId +
'ui/members?groupId=' + input.selectedGroupId +
'&paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage;
return apiRequest({
......@@ -126,7 +126,7 @@ export default {
},
fetchPermissionsPanel(input) {
let url = BASE_API_URL +
'permissions?groupId=' + input.selectedGroupId +
'ui/permissions?groupId=' + input.selectedGroupId +
'&paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage;
return apiRequest({
......@@ -141,7 +141,7 @@ export default {
});
},
addGroup(newGroupName, leaf, input) {
let url = BASE_API_URL + 'group';
let url = BASE_API_URL + 'ui/group';
return apiRequest({
method: 'POST',
url: url,
......@@ -162,7 +162,7 @@ export default {
}, true, true);
},
updateGroup(groupId, newGroupName, leaf, input) {
let url = BASE_API_URL + 'group/' + groupId;
let url = BASE_API_URL + 'ui/group/' + groupId;
return apiRequest({
method: 'PUT',
url: url,
......@@ -182,7 +182,7 @@ export default {
}, true, true);
},
removeGroup(groupId, input) {
let url = BASE_API_URL + 'group/' + groupId +
let url = BASE_API_URL + 'ui/group/' + groupId +
'?paginatorPageSize=' + input.paginatorPageSize +
'&paginatorPage=' + input.paginatorPage;
if (input.searchFilter !== null) {
......@@ -200,7 +200,7 @@ export default {
});
},
searchUser(searchInput) {
let url = BASE_API_URL + 'users?search=' + searchInput;
let url = BASE_API_URL + 'ui/users?search=' + searchInput;
return apiRequest({
method: 'GET',
......@@ -213,7 +213,7 @@ export default {
});
},
addPermission(userId, permission, input, override) {
let url = BASE_API_URL + 'permission';
let url = BASE_API_URL + 'ui/permission';
return apiRequest({
method: 'POST',
......@@ -235,7 +235,7 @@ export default {
});
},
updatePermission(groupId, userId, permission) {
let url = BASE_API_URL + 'permission';
let url = BASE_API_URL + 'ui/permission';
return apiRequest({
method: 'PUT',
......@@ -254,7 +254,7 @@ export default {
});
},
getPermission(groupId, userId) {
let url = BASE_API_URL + 'permission?groupId=' + groupId + '&userId=' + userId;
let url = BASE_API_URL + 'ui/permission?groupId=' + groupId + '&userId=' + userId;
return apiRequest({
method: 'GET',
......@@ -268,7 +268,7 @@ export default {
});
},
addMember(userId, permission, input) {
let url = BASE_API_URL + 'member';
let url = BASE_API_URL + 'ui/member';
return apiRequest({
method: 'POST',
......@@ -289,7 +289,7 @@ export default {
});
},
removeMember(userId, removeAlsoPermission, input) {
let url = BASE_API_URL + 'member' +
let url = BASE_API_URL + 'ui/member' +
'?groupId=' + input.selectedGroupId +
'&userId=' + userId +
'&removeAlsoPermission=' + removeAlsoPermission +
......@@ -307,7 +307,7 @@ export default {
});
},
removePermission(userId, input) {
let url = BASE_API_URL + 'permission' +
let url = BASE_API_URL + 'ui/permission' +
'?groupId=' + input.selectedGroupId +
'&userId=' + userId +
'&paginatorPageSize=' + input.paginatorPageSize +
......@@ -324,8 +324,10 @@ export default {
});
},
search(input) {
let url = BASE_API_URL + 'search?query=' + input.genericSearch.filter +
'&page=' + input.genericSearch.paginatorPage + '&pageSize=' + input.genericSearch.paginatorPageSize;
let url = BASE_API_URL + 'ui/search?query=' + input.genericSearch.filter +
'&paginatorPage=' + input.genericSearch.paginatorPage +
'&paginatorPageSize=' + input.genericSearch.paginatorPageSize +
'&users=' + input.genericSearch.users + "&groups=" + input.genericSearch.groups;
return apiRequest({
method: 'GET',
......@@ -339,7 +341,7 @@ export default {
});
},
openUserSearchResult(userId) {
let url = BASE_API_URL + 'search/user/' + userId;
let url = BASE_API_URL + 'ui/search/user/' + userId;
return apiRequest({
method: 'GET',
......@@ -353,7 +355,7 @@ export default {
});
},
keepAlive() {
let url = BASE_API_URL + 'keepAlive';
let url = BASE_API_URL + 'ui/keepAlive';
return apiRequest({
method: 'GET',
......@@ -365,7 +367,7 @@ export default {
}, false);
},
deleteInvitedRegistration(requestId, groupId) {
let url = BASE_API_URL + 'registration?' +
let url = BASE_API_URL + 'ui/registration?' +
'request_id=' + requestId + '&group_id=' + groupId;
return apiRequest({
method: 'DELETE',
......
<template>
<div class="mt-sm-3">
<b-form inline>
Includes:
<b-form-checkbox class="ml-4" v-model="input.genericSearch.users" @input="updateTypeFilter">users</b-form-checkbox>
<b-form-checkbox class="ml-4" v-model="input.genericSearch.groups" @input="updateTypeFilter">groups</b-form-checkbox>
</b-form>
<div>
<div v-if="model.genericSearchResults.items && model.genericSearchResults.items.length > 0">
<p>Search results:</p>
......@@ -40,7 +45,7 @@ export default {
'$route': 'updateSearchResults'
},
methods: {
openSearchResult: function(result) {
openSearchResult(result) {
switch (result.type) {
case 'GROUP':
this.$store.dispatch('openGroup', result.id);
......@@ -50,7 +55,11 @@ export default {
break;
}
},
updateSearchResults: function() {
updateTypeFilter() {
this.input.genericSearch.paginatorPage = 1;
this.updateSearchResults();
},
updateSearchResults() {
this.$store.dispatch('search', this.$route.query.q);
}
}
......
......@@ -6,15 +6,20 @@
<span v-if="group.active">{{group.groupName}}</span>
</li>
</ol>
<a v-if="currentGroup" :href="'group/status?groupId=' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV">
<font-awesome-icon icon="download"></font-awesome-icon>
</a>
<span id="breadcrumb-buttons">
<a href="#" v-on:click.stop.prevent="openEditGroupModal(currentGroup)" title="Edit" v-if="currentGroup && isAdmin && currentGroup.groupId !== 'ROOT'">
<font-awesome-icon icon="edit"></font-awesome-icon>
</a>
&nbsp;
<a v-if="currentGroup" :href="'group/status?groupId=' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" title="Download CSV">
<font-awesome-icon icon="download"></font-awesome-icon>
</a>
</span>
</nav>
</template>
<script>
import { mapState } from 'vuex';
import client from 'api-client';
function buildItems(values) {
let groups = [];
......@@ -44,16 +49,11 @@ export default {
}),
methods: {
changeBreadcrumb: function(groupId) {
this.input.selectedGroupId = groupId;
this.input.searchFilter = null;
if (this.input.tabIndex === 0) {
client.fetchGroupsTab(this.input)
.then(model => {
this.$store.commit('updateGroups', model);
});
} else {
this.$store.dispatch('changeTab', 0);
}
this.$store.dispatch('changeBreadcrumb', groupId);
},
openEditGroupModal: function(group) {
group.leaf = this.model.leaf;
this.$parent.$refs.editGroupModal.openEditGroupModal(group, false);
}
}
}
......@@ -65,7 +65,7 @@ export default {
position: relative;
}
#csv-status-download {
#breadcrumb-buttons {
position: absolute;
right: 18px;
top: 12px;
......
......@@ -23,13 +23,11 @@
<p v-if="model.groupsPanel.items.length === 0">No groups</p>
</div>
<Paginator :paginatedPanel="model.groupsPanel" :onUpdate="updatePagination" :paginatorInput="input" />
<EditGroupModal ref="editGroupModal" />
<ConfirmRemoveGroupModal ref="confirmRemoveGroupModal" />
</b-tab>
</template>
<script>
import EditGroupModal from './modals/EditGroupModal.vue';
import ConfirmRemoveGroupModal from './modals/ConfirmRemoveGroupModal.vue';
import Paginator from './Paginator.vue';
import { mapState } from 'vuex';
......@@ -39,7 +37,6 @@ import debounce from 'debounce'; // for delaying the input event (search filter)
export default {
name: 'GroupsPanel',
components: {
EditGroupModal,
ConfirmRemoveGroupModal,
Paginator
},
......@@ -52,7 +49,7 @@ export default {
this.$store.dispatch('openGroup', group.groupId);
},
openEditGroupModal: function(group) {
this.$refs.editGroupModal.openEditGroupModal(group);
this.$parent.$parent.$refs.editGroupModal.openEditGroupModal(group, true);
},
openRemoveGroupModal: function(group) {
this.$refs.confirmRemoveGroupModal.openRemoveGroupModal(group);
......
......@@ -18,6 +18,7 @@
<AddGroupModal />
<AddMemberModal />
<AddPermissionModal />
<EditGroupModal ref="editGroupModal" />
</div>
</template>
......@@ -30,6 +31,7 @@ import InvitedRegistrationPanel from './InvitedRegistrationPanel.vue'
import AddGroupModal from './modals/AddGroupModal.vue'
import AddMemberModal from './modals/AddMemberModal.vue'
import AddPermissionModal from './modals/AddPermissionModal.vue'
import EditGroupModal from './modals/EditGroupModal.vue';
import { mapState } from 'vuex';
export default {
......@@ -42,7 +44,8 @@ export default {
InvitedRegistrationPanel,
AddGroupModal,
AddMemberModal,
AddPermissionModal
AddPermissionModal,
EditGroupModal
},
computed: mapState({
model: state => state.model,
......
......@@ -7,7 +7,7 @@
<div class="col-md-4">
<b-row>
<b-col sm="5">
<label for="page-size">Page size:</label>
<label for="page-size" class="mt-2">Page size:</label>
</b-col>
<b-col sm="6">
<b-form-select id="page-size" v-model="paginatorInput.paginatorPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select>
......
......@@ -9,10 +9,10 @@
<!-- Right aligned nav items -->
<b-navbar-nav class="ml-auto">
<b-nav-item href="help/index.html" target="blank_" class="mr-4">Help</b-nav-item>
<b-nav-item href="help/index.html" target="_blank" class="mr-4">Help</b-nav-item>
<b-nav-form>
<b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model="input.genericSearch.filter" @keydown.native.enter.prevent="genericSearch"></b-form-input>
<b-button size="sm" class="my-2 my-sm-0" type="button" v-on:click="genericSearch()">Search</b-button>
<b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model.trim="input.genericSearch.filter" @keydown.native.enter.prevent="genericSearch"></b-form-input>
<b-button size="sm" type="button" v-on:click="genericSearch()">Search</b-button>
</b-nav-form>
<b-nav-item-dropdown :text="user" right v-if="user">
<b-dropdown-item href="logout">Logout</b-dropdown-item>
......@@ -36,13 +36,16 @@ export default {
}),
methods: {
showMainPage() {
if (this.$store.state.input.selectedGroupId !== 'ROOT') {
this.$store.dispatch('changeBreadcrumb', 'ROOT');
} else {
this.$store.dispatch('changeTab', 0);
}
this.$router.push('/', () => {});
},
genericSearch() {
this.input.genericSearch.page = 1;
this.input.genericSearch.pageSize = 20;
this.$router.push({ path: '/search', query: { q: this.input.genericSearch.filter } }, () => {});
this.$store.dispatch('search', this.input.genericSearch.filter);
this.$store.commit('setGenericSearchFilter', this.input.genericSearch.filter);
}
}
}
......
......@@ -5,7 +5,7 @@
<b-form-input v-model="newGroupName" id="new-group-name-input" ref="newGroupNameInput" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError" @keydown.native.enter="addGroup">
</b-form-input>
<b-form-invalid-feedback id="new-group-name-input-feedback" class="text-right">{{newGroupNameError}}</b-form-invalid-feedback>
<b-form-checkbox class="mt-3 ml-3" v-model="leaf">is leaf</b-form-checkbox>
<b-form-checkbox class="mt-3 ml-3" v-model="allowChildGroups">allow child groups</b-form-checkbox>
</b-form>
</b-modal>
</template>
......@@ -27,13 +27,13 @@ export default {
return {
newGroupName: '',
newGroupNameError: '',
leaf: true
allowChildGroups: false
};
},
methods: {
resetModal: function() {
this.newGroupName = null;
this.leaf = true;
this.allowChildGroups = false;
this.resetError();
},
afterShow: function() {
......@@ -49,7 +49,7 @@ export default {
if (!this.newGroupName) {
this.newGroupNameError = "Group name is required";
} else {
client.addGroup(this.newGroupName, this.leaf, this.$store.state.input)
client.addGroup(this.newGroupName, !this.allowChildGroups, this.$store.state.input)
.then(res => {
this.$store.commit('updateGroupsPanel', res);
this.$bvModal.hide('add-group-modal');
......
......@@ -2,10 +2,10 @@
<b-modal id="edit-group-modal" title="Edit group" ok-title="Update" @ok="updateGroup">
<b-form inline>
<label class="w-25" for="new-group-name-input">Group name:</label>
<b-form-input v-model="newGroupName" id="new-group-name-input" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError">
<b-form-input v-model="newGroupName" id="new-group-name-input" class="w-75" aria-describedby="new-group-name-input-feedback" :state="newGroupNameState" v-on:input="resetError" @keydown.native.enter.prevent="updateGroup">
</b-form-input>
<b-form-invalid-feedback id="new-group-name-input-feedback" class="text-right">{{newGroupNameError}}</b-form-invalid-feedback>
<b-form-checkbox class="mt-3 ml-3" v-model="leaf">is leaf</b-form-checkbox>
<b-form-checkbox class="mt-3 ml-3" v-model="allowChildGroups">allow child groups</b-form-checkbox>
</b-form>
</b-modal>
</template>
......@@ -29,18 +29,20 @@ export default {
oldGroupName: '',
newGroupName: '',
newGroupNameError: '',
leaf: false
allowChildGroups: false,
reloadChildren: false
};
},
methods: {
resetError: function() {
this.newGroupNameError = null;
},
openEditGroupModal: function(group) {
openEditGroupModal: function(group, reloadChildren) {
this.newGroupName = group.groupName;
this.groupId = group.groupId;
this.leaf = group.leaf;
this.allowChildGroups = !group.leaf;
this.$bvModal.show('edit-group-modal');
this.reloadChildren = reloadChildren;
},
updateGroup: function(event) {
// Prevent modal from closing
......@@ -56,9 +58,16 @@ export default {
return;
}
client.updateGroup(this.groupId, this.newGroupName, this.leaf, this.$store.state.input)
client.updateGroup(this.groupId, this.newGroupName, !this.allowChildGroups, this.$store.state.input)
.then(res => {
this.$store.commit('updateGroupsPanel', res);
if (this.reloadChildren) {
this.$store.commit('updateGroupsPanel', res);
} else {
this.$store.dispatch('updateCurrentGroup', {
newGroupName: this.newGroupName,
leaf: !this.allowChildGroups
});
}
this.$bvModal.hide('edit-group-modal');
})
.catch(res => {
......
......@@ -32,11 +32,14 @@ export default new Vuex.Store({
paginatorPageSize: 20,
paginatorPage: 1,
tabIndex: 0,
previousTabIndex: null,
searchFilter: '',
genericSearch: {
filter: '',
paginatorPage: 1,
paginatorPageSize: 20
paginatorPageSize: 20,
users: true,
groups: true
}
},
loading: false
......@@ -97,6 +100,10 @@ export default new Vuex.Store({
},
setGenericSearchFilter(state, filter) {
state.input.genericSearch.filter = filter;
state.input.genericSearch.paginatorPage = 1;
state.input.genericSearch.paginatorPageSize = 20;
state.input.genericSearch.users = true;
state.input.genericSearch.groups = true;
},
removeInvitedRegistration(state, regId) {
let index = -1;
......@@ -117,12 +124,26 @@ export default new Vuex.Store({
.then(model => commit('updateHomePageModel', model));
},
search({ commit, state }, filter) {
commit('setGenericSearchFilter', filter);
if (state.input.genericSearch.filter !== filter) {
commit('setGenericSearchFilter', filter);
}
client.search(state.input)
.then(results => commit('displaySearchResults', results));
},
/**
* tabIndex parameter is equal to state.input.tabIndex when this method
* is called as a result of the @input event on the b-tabs component.
* For this reason the additional field state.input.previousTabIndex is used
* to check if the tab index changed and perform the AJAX calls only when
* they are really needed.
*/
changeTab({ commit, state }, tabIndex) {
let skip = tabIndex === state.input.previousTabIndex;
state.input.previousTabIndex = tabIndex;
state.input.tabIndex = tabIndex;
if (skip) {
return;
}
// reset paginator
state.input.paginatorPage = 1;
switch (tabIndex) {
......@@ -140,18 +161,39 @@ export default new Vuex.Store({
break;
}
},
changeBreadcrumb({ state, commit, dispatch }, groupId) {
state.input.selectedGroupId = groupId;
state.input.searchFilter = null;
if (state.input.tabIndex === 0) {
client.fetchGroupsTab(state.input)
.then(model => {
commit('updateGroups', model);
dispatch('changeTab', 0);
});
} else {
dispatch('changeTab', 0);
}
},
updateCurrentGroup({ dispatch, state }, data) {
state.model.breadcrumbs[state.model.breadcrumbs.length - 1].groupName = data.newGroupName;
state.model.leaf = data.leaf;
if (state.input.tabIndex === 0 && state.model.leaf) {
dispatch('changeTab', 1);
}
},
openGroup({ commit, dispatch, state }, groupId) {
let input = state.input;
input.selectedGroupId = groupId;
input.searchFilter = '';
input.paginatorPage = 1;
input.tabIndex = 0;
input.previousTabIndex = 0;
client.fetchGroupsTab(input)
.then(model => {
commit('updateGroups', model);
if (model.leaf) {
// If there are no subgroups show the members panel
dispatch('changeTab', 1);
} else {
dispatch('changeTab', 0);
}
router.push('/', () => {});
});
......
......@@ -48,12 +48,6 @@
<artifactId>opencsv</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
......
......@@ -3,6 +3,7 @@ package it.inaf.ia2.gms.authn;
import it.inaf.ia2.aa.UserManager;
import it.inaf.ia2.aa.data.User;
import it.inaf.ia2.gms.persistence.LoggingDAO;
import it.inaf.ia2.gms.persistence.model.ActionType;
import java.io.IOException;
import java.security.Principal;
import java.util.Map;
......@@ -55,13 +56,14 @@ public class JWTFilter implements Filter {
Map<String, Object> claims = userManager.parseIdTokenClaims(token);
if (claims.get("sub") == null) {
loggingDAO.logAction("Attempt to access WS with invalid token", request);
loggingDAO.logAction(ActionType.UNAUTHORIZED_ACCESS_ATTEMPT, "Attempt to access API with invalid token " + request.getRequestURI(), request);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid access token: missing sub claim");
return;
}
ServletRequestWithJWTPrincipal wrappedRequest = new ServletRequestWithJWTPrincipal(request, token, claims);
loggingDAO.logAction("WS access from " + wrappedRequest.getUserPrincipal().getName(), request);
loggingDAO.logAction(ActionType.API_CALL, request.getRequestURI() + " called by " + wrappedRequest.getUserPrincipal().getName(), request);
fc.doFilter(wrappedRequest, res);
}
......
......@@ -14,9 +14,13 @@ import it.inaf.ia2.gms.model.request.RenameGroupRequest;
import it.inaf.ia2.gms.model.request.SearchFilterRequest;
import it.inaf.ia2.gms.persistence.model.GroupEntity;
import it.inaf.ia2.gms.service.GroupNameService;
import it.inaf.ia2.gms.service.GroupsService;
import it.inaf.ia2.gms.service.GroupsTreeBuilder;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.Principal;
import java.util.List;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
......@@ -46,6 +50,9 @@ public class GroupsController {
@Autowired
private GroupsManager groupsManager;
@Autowired
private GroupsService groupsService;
@Autowired
private GroupsTreeBuilder groupsTreeBuilder;
......@@ -56,9 +63,9 @@ public class GroupsController {
private GroupStatusManager groupStatusManager;
@Autowired
private GroupNameService groupNameService;
protected GroupNameService groupNameService;
@GetMapping(value = "/groups", produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(value = "/ui/groups", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> getGroupsTab(@Valid GroupsRequest request) {
if (request.isOnlyPanel()) {
// Only groupsPanel
......@@ -70,7 +77,7 @@ public class GroupsController {
}
}
@PostMapping(value = "/group", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PostMapping(value = "/ui/group", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PaginatedData<GroupNode>> createGroup(@Valid @RequestBody AddGroupRequest request) {
GroupEntity parent = groupsManager.getGroupById(request.getParentGroupId());
......@@ -82,7 +89,7 @@ public class GroupsController {
return new ResponseEntity<>(groupsPanel, HttpStatus.CREATED);
}
@PutMapping(value = "/group/{groupId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@PutMapping(value = "/ui/group/{groupId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PaginatedData<GroupNode>> updateGroup(@PathVariable("groupId") String groupId, @Valid @RequestBody RenameGroupRequest request) {
GroupEntity updatedGroup = groupsManager.updateGroup(groupId, request.getNewGroupName(), request.isLeaf());
......@@ -94,8 +101,8 @@ public class GroupsController {
return ResponseEntity.ok(groupsPanel);
}
@DeleteMapping(value = "/group/{groupId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> deleteGroup(@PathVariable("groupId") String groupId, DeleteGroupRequest request) {
@DeleteMapping(value = "/ui/group/{groupId}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PaginatedData<GroupNode>> deleteGroup(@PathVariable("groupId") String groupId, DeleteGroupRequest request) {
GroupEntity parent = groupsManager.deleteGroup(groupId);
PaginatedData<GroupNode> groupsPanel = getGroupsPanel(parent, request);
......@@ -139,4 +146,55 @@ public class GroupsController {
private <T extends PaginatedModelRequest & SearchFilterRequest> PaginatedData<GroupNode> getGroupsPanel(GroupEntity parentGroup, T request) {
return groupsTreeBuilder.listSubGroups(parentGroup, request, servletRequest.getUserPrincipal().getName());
}
@GetMapping(value = "/groups", produces = MediaType.TEXT_PLAIN_VALUE)
public void listChildGroups(@RequestParam("parent") Optional<String> groupNames,
@RequestParam(value = "recursive", defaultValue = "false") boolean recursive,
Principal principal, HttpServletResponse response) throws IOException {
GroupEntity parentGroup = groupNameService.getGroupFromNames(groupNames);
List<GroupEntity> visibleSubgroups = groupsManager.getChildGroups(parentGroup, recursive);
try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) {
for (String groupName : groupNameService.getGroupsNames(visibleSubgroups)) {
pw.println(groupNameService.getShortGroupName(groupName, groupNames));
}
}
}
/**
* Creates a group and its ancestors if they are missing. It doesn't fail if
* the last group already exists. For CLI/API usage.
*/
@PostMapping(value = "/group", produces = MediaType.TEXT_PLAIN_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void createGroup(@RequestParam("name") String groupParam,
@RequestParam(value = "leaf", required = false, defaultValue = "false") boolean leaf,
HttpServletResponse response) throws IOException {
List<String> groupNames = groupNameService.extractGroupNames(groupParam);
GroupEntity group = groupsManager.getRoot();
for (int i = 0; i < groupNames.size(); i++) {
String name = groupNames.get(i);
Optional<GroupEntity> optGroup = groupsService.findGroupByParentAndName(group, name);
if (optGroup.isPresent()) {
group = optGroup.get();
} else {
group = groupsManager.createGroup(group, name, i == groupNames.size() - 1 ? leaf : false);
}
}
response.setStatus(HttpServletResponse.SC_CREATED);
try ( PrintWriter pw = new PrintWriter(response.getOutputStream())) {
pw.println(groupParam);
}
}
@DeleteMapping(value = "/group", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<?> deleteGroup(@RequestParam("name") String groupParam) {
GroupEntity group = groupNameService.getGroupFromNames(Optional.of(groupParam));
groupsManager.deleteGroup(group.getId());
return ResponseEntity.noContent().build();
}
}
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.manager.InvitedRegistrationManager;
import it.inaf.ia2.gms.model.GroupBreadcrumb;
import it.inaf.ia2.gms.model.GroupNode;
import it.inaf.ia2.gms.model.Permission;
import it.inaf.ia2.gms.model.request.GroupsRequest;
import it.inaf.ia2.gms.model.response.GroupsTabResponse;
import it.inaf.ia2.gms.model.response.HomePageResponse;
import it.inaf.ia2.gms.model.response.PaginatedData;
import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.servlet.ServletException;
......@@ -41,44 +35,21 @@ public class HomePageController {
private InvitedRegistrationManager invitedRegistrationManager;
@ResponseBody
@GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_VALUE)
@GetMapping(value = "/ui/home", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<HomePageResponse> getMainPage(@Valid GroupsRequest request) {
HomePageResponse response = new HomePageResponse();
response.setUser(session.getUserName());
try {
GroupsTabResponse groupsTabResponse = groupsTabResponseBuilder.getGroupsTab(request);
response.setBreadcrumbs(groupsTabResponse.getBreadcrumbs());
response.setGroupsPanel(groupsTabResponse.getGroupsPanel());
response.setPermission(groupsTabResponse.getPermission());
} catch (UnauthorizedException ex) {
if ("ROOT".equals(request.getGroupId())) {
response.setBreadcrumbs(getRootBreadcrumbs());
response.setGroupsPanel(getEmptyGroupsPanel(request));
response.setPermission(Permission.TRAVERSE);
} else {
throw ex;
}
}
GroupsTabResponse groupsTabResponse = groupsTabResponseBuilder.getGroupsTab(request);
response.setBreadcrumbs(groupsTabResponse.getBreadcrumbs());
response.setGroupsPanel(groupsTabResponse.getGroupsPanel());
response.setPermission(groupsTabResponse.getPermission());
return ResponseEntity.ok(response);
}
private List<GroupBreadcrumb> getRootBreadcrumbs() {
List<GroupBreadcrumb> breadcrumbs = new ArrayList<>();
GroupBreadcrumb breadcrumb = new GroupBreadcrumb();
breadcrumb.setGroupId("ROOT");
breadcrumb.setGroupName("ROOT");
breadcrumbs.add(breadcrumb);
return breadcrumbs;
}
private PaginatedData<GroupNode> getEmptyGroupsPanel(GroupsRequest request) {
return new PaginatedData<>(new ArrayList<>(), 1, request.getPaginatorPageSize());
}
@GetMapping(value = "/", produces = MediaType.TEXT_HTML_VALUE)
public String index(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
......
......@@ -59,7 +59,7 @@ public class InvitedRegistrationController {
}
}
@DeleteMapping(value = "/registration", produces = MediaType.APPLICATION_JSON_VALUE)
@DeleteMapping(value = "/ui/registration", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> deleteInvitedRegistration(@RequestParam("request_id") String requestId, @RequestParam("group_id") String groupId) {
invitedRegistrationManager.deleteInvitedRegistration(requestId, groupId);
return ResponseEntity.noContent().build();
......