diff --git a/gms-ui/src/App.vue b/gms-ui/src/App.vue index cd5b2f3ddde667fb88a475edef23a9b89e251294..aa5de323797d278443026570d34aaa8f8cce679f 100644 --- a/gms-ui/src/App.vue +++ b/gms-ui/src/App.vue @@ -2,9 +2,7 @@ <div id="app" v-if="model"> <TopMenu v-bind:user="model.user" /> <div class="container"> - <Main v-if="page === 'main'" /> - <GenericSearchResults v-if="page === 'search'" /> - <UserSearchResult v-if="page === 'userSearch'" /> + <router-view></router-view> </div> <div id="loading" v-if="loading"> <div id="spinner-wrapper"> @@ -16,21 +14,13 @@ <script> import TopMenu from './components/TopMenu.vue'; -import Main from './components/Main.vue'; -import GenericSearchResults from './components/GenericSearchResults.vue'; -import UserSearchResult from './components/UserSearchResult.vue'; -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; import client from 'api-client'; export default { name: 'app', components: { - TopMenu, - Main, - GenericSearchResults, - UserSearchResult + TopMenu }, computed: mapState({ model: state => state.model, @@ -52,10 +42,7 @@ export default { }); // retrieve the initial model - client.fetchHomePageModel(this.input) - .then(model => { - this.$store.commit('updateHomePageModel', model); - }); + this.$store.dispatch('loadHomePageModel'); setInterval(client.keepAlive, 60000); } diff --git a/gms-ui/src/api/mock/index.js b/gms-ui/src/api/mock/index.js index 33bd82d1c17582828e1b4aa1cf06973d8969462e..c1c8e00bdaba2897a574ca703169a57b00ec36b1 100644 --- a/gms-ui/src/api/mock/index.js +++ b/gms-ui/src/api/mock/index.js @@ -47,6 +47,9 @@ export default { addPermission() { return fetch(permissionsPanel); }, + updatePermission() { + return fetch({"permission": "ADMIN"}); + }, getPermission() { return fetch(permission); }, diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js index 4006f18ad703965b14e04de30cb28ff2bc36b881..f7079c8850ba39cd5adf841fddf58ca281cda1a6 100644 --- a/gms-ui/src/api/server/index.js +++ b/gms-ui/src/api/server/index.js @@ -1,35 +1,45 @@ const BASE_API_URL = process.env.VUE_APP_API_BASE_URL; -function apiRequest(url, options, showLoading = true) { +import axios from 'axios'; + +function apiRequest(options, showLoading = true, handleValidationErrors = false) { if (showLoading) { loading(true); } - return new Promise((resolve) => { - fetch(url, options) + return new Promise((resolve, reject) => { + axios(options) .then(response => { - loading(false); - if ([200, 201, 204, 400].includes(response.status)) { // valid status codes - if (response.status === 204) { - resolve({}); - } else { - resolve(response.json()); - } + if (response.status === 204) { + resolve({}); } else { - response.json().then(jsonValue => dispatchApiErrorEvent(jsonValue)); + resolve(response.data); } + loading(false); }) .catch(error => { + if(handleValidationErrors && error.response && error.response.status === 400) { + reject(error.response.data); + } else { + dispatchApiErrorEvent(error); + } loading(false); - dispatchApiErrorEvent(error); }); }); } function dispatchApiErrorEvent(error) { let event = new CustomEvent('apiError'); + let errorMessage; + if (error.response && error.response.data && error.response.data.message) { + errorMessage = error.response.data.message; + } else if (error.message) { + errorMessage = error.message; + } else { + errorMessage = 'Unknown error'; + } event.message = { title: error.error || 'Error', - body: error.message || 'Unknown error' + body: errorMessage }; document.dispatchEvent(event); } @@ -47,13 +57,14 @@ export default { 'home?groupId=' + input.selectedGroupId + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -66,13 +77,14 @@ export default { if (input.searchFilter !== null) { url += '&searchFilter=' + input.searchFilter; } - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -85,13 +97,14 @@ export default { if (input.searchFilter !== null) { url += '&searchFilter=' + input.searchFilter; } - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -100,13 +113,14 @@ export default { 'members?groupId=' + input.selectedGroupId + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -115,54 +129,57 @@ export default { 'permissions?groupId=' + input.selectedGroupId + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, addGroup(newGroupName, leaf, input) { let url = BASE_API_URL + 'group'; - return apiRequest(url, { + return apiRequest({ method: 'POST', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' }, - body: JSON.stringify({ + data: { newGroupName: newGroupName, parentGroupId: input.selectedGroupId, paginatorPageSize: input.paginatorPageSize, paginatorPage: input.paginatorPage, searchFilter: input.searchFilter, leaf: leaf - }) - }); + } + }, true, true); }, updateGroup(groupId, newGroupName, leaf, input) { let url = BASE_API_URL + 'group/' + groupId; - return apiRequest(url, { + return apiRequest({ method: 'PUT', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' }, - body: JSON.stringify({ + data: { newGroupName: newGroupName, leaf: leaf, paginatorPageSize: input.paginatorPageSize, paginatorPage: input.paginatorPage, searchFilter: input.searchFilter - }) - }); + } + }, true, true); }, removeGroup(groupId, input) { let url = BASE_API_URL + 'group/' + groupId + @@ -171,80 +188,104 @@ export default { if (input.searchFilter !== null) { url += '&searchFilter=' + input.searchFilter; } - return apiRequest(url, { + return apiRequest({ method: 'DELETE', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, searchUser(searchInput) { let url = BASE_API_URL + 'users?search=' + searchInput; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { - 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, - addPermission(userId, permission, input) { + addPermission(userId, permission, input, override) { let url = BASE_API_URL + 'permission'; - return apiRequest(url, { + return apiRequest({ method: 'POST', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' }, - body: JSON.stringify({ + data: { groupId: input.selectedGroupId, userId: userId, permission: permission, + override: override, paginatorPageSize: input.paginatorPageSize, paginatorPage: input.paginatorPage - }) + } + }); + }, + updatePermission(groupId, userId, permission) { + let url = BASE_API_URL + 'permission'; + + return apiRequest({ + method: 'PUT', + url: url, + withCredentials: true, + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Cache-Control': 'no-cache' + }, + data: { + groupId: groupId, + userId: userId, + permission: permission + } }); }, getPermission(groupId, userId) { let url = BASE_API_URL + 'permission?groupId=' + groupId + '&userId=' + userId; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, addMember(userId, permission, input) { let url = BASE_API_URL + 'member'; - return apiRequest(url, { + return apiRequest({ method: 'POST', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' }, - body: JSON.stringify({ + data: { groupId: input.selectedGroupId, userId: userId, permission: permission, paginatorPageSize: input.paginatorPageSize, paginatorPage: input.paginatorPage - }) + } }); }, removeMember(userId, removeAlsoPermission, input) { @@ -254,13 +295,14 @@ export default { '&removeAlsoPermission=' + removeAlsoPermission + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; - return apiRequest(url, { + return apiRequest({ method: 'DELETE', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -270,13 +312,14 @@ export default { '&userId=' + userId + '&paginatorPageSize=' + input.paginatorPageSize + '&paginatorPage=' + input.paginatorPage; - return apiRequest(url, { + return apiRequest({ method: 'DELETE', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, @@ -284,36 +327,41 @@ export default { let url = BASE_API_URL + 'search?query=' + input.genericSearch.filter + '&page=' + input.genericSearch.paginatorPage + '&pageSize=' + input.genericSearch.paginatorPageSize; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, openUserSearchResult(userId) { let url = BASE_API_URL + 'search/user/' + userId; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include', + url: url, + withCredentials: true, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', + 'Cache-Control': 'no-cache' } }); }, keepAlive() { let url = BASE_API_URL + 'keepAlive'; - return apiRequest(url, { + return apiRequest({ method: 'GET', - cache: 'no-cache', - credentials: 'include' + url: url, + withCredentials: true, + headers: { + 'Cache-Control': 'no-cache' + } }, false); } }; diff --git a/gms-ui/src/assets/logo.png b/gms-ui/src/assets/logo.png deleted file mode 100644 index f3d2503fc2a44b5053b0837ebea6e87a2d339a43..0000000000000000000000000000000000000000 Binary files a/gms-ui/src/assets/logo.png and /dev/null differ diff --git a/gms-ui/src/components/GenericSearchResults.vue b/gms-ui/src/components/GenericSearchResults.vue index ea3ae44138f97111ef163e58ed40e8ef705518a8..e0277a7a1abe30798c996c043255a57bec14b4d4 100644 --- a/gms-ui/src/components/GenericSearchResults.vue +++ b/gms-ui/src/components/GenericSearchResults.vue @@ -17,11 +17,8 @@ </template> <script> -import client from 'api-client'; import Paginator from './Paginator.vue'; -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'GenericSearchResults', @@ -32,22 +29,26 @@ export default { model: state => state.model, input: state => state.input }), + created () { + this.updateSearchResults(); + }, + watch: { + // call again the method if the route changes + '$route': 'updateSearchResults' + }, methods: { openSearchResult: function(result) { switch (result.type) { case 'GROUP': - this.$store.commit('openGroup', result.id); + this.$store.dispatch('openGroup', result.id); break; case 'USER': - this.$store.dispatch('openUserPage', result.id); + this.$router.push({ path: `/user/${result.id}` }, () => {}); break; } }, updateSearchResults: function() { - client.search(this.input) - .then(results => { - this.$store.commit('displaySearchResults', results); - }); + this.$store.dispatch('search', this.$route.query.q); } } } diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue index 629059b322ea306a8eedae93db2860e46332a460..283c847fe6341801e36227c398ae0808d9a32d47 100644 --- a/gms-ui/src/components/GroupsBreadcrumb.vue +++ b/gms-ui/src/components/GroupsBreadcrumb.vue @@ -1,7 +1,7 @@ <template> <nav aria-label="breadcrumb"> <ol class="breadcrumb"> - <li class="breadcrumb-item" v-for="group in groups" v-bind:class="{ active: group.active }"> + <li class="breadcrumb-item" v-for="group in groups" v-bind:class="{ active: group.active }" v-bind:key="group.groupId"> <a href="#" v-on:click="changeBreadcrumb(group.groupId)" v-if="!group.active">{{group.groupName}}</a> <span v-if="group.active">{{group.groupName}}</span> </li> @@ -10,9 +10,7 @@ </template> <script> -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; import client from 'api-client'; function buildItems(values) { @@ -49,7 +47,7 @@ export default { this.$store.commit('updateGroups', model); }); } else { - this.$store.commit('setTabIndex', 0); + this.$store.dispatch('changeTab', 0); } } } diff --git a/gms-ui/src/components/GroupsPanel.vue b/gms-ui/src/components/GroupsPanel.vue index dfdb7d72b5231478a89d01ead8316a8cdafe2c41..d7dac8124690720eeafac5f4b80de93bfe9270a4 100644 --- a/gms-ui/src/components/GroupsPanel.vue +++ b/gms-ui/src/components/GroupsPanel.vue @@ -6,7 +6,7 @@ </b-col> </b-row> <div id="groups-list" v-if="model.groupsPanel !== null"> - <b-list-group v-for="group in model.groupsPanel.items"> + <b-list-group v-for="group in model.groupsPanel.items" v-bind:key="group.groupId"> <b-list-group-item href="#" v-on:click="openGroup(group)"> <span class="float-left">{{group.groupName}}</span> <span v-if="group.permission === 'ADMIN'" class="float-right"> @@ -32,10 +32,7 @@ import EditGroupModal from './modals/EditGroupModal.vue'; import ConfirmRemoveGroupModal from './modals/ConfirmRemoveGroupModal.vue'; import Paginator from './Paginator.vue'; -import { - mapState, - mapActions -} from 'vuex'; +import { mapState } from 'vuex'; import client from 'api-client'; import debounce from 'debounce'; // for delaying the input event (search filter) @@ -52,7 +49,7 @@ export default { }), methods: { openGroup: function(group) { - this.$store.commit('openGroup', group.groupId); + this.$store.dispatch('openGroup', group.groupId); }, openEditGroupModal: function(group) { this.$refs.editGroupModal.openEditGroupModal(group); diff --git a/gms-ui/src/components/Main.vue b/gms-ui/src/components/Main.vue index 055309c5adfa9708c4bef8c90a0c85b75fd01009..7c7829f99b855ba3660f150ce8a8ab3b9d7b02bf 100644 --- a/gms-ui/src/components/Main.vue +++ b/gms-ui/src/components/Main.vue @@ -28,10 +28,7 @@ import PermissionsPanel from './PermissionsPanel.vue' import AddGroupModal from './modals/AddGroupModal.vue' import AddMemberModal from './modals/AddMemberModal.vue' import AddPermissionModal from './modals/AddPermissionModal.vue' -import { - mapState -} from 'vuex'; -import client from 'api-client'; +import { mapState } from 'vuex'; export default { name: 'Main', @@ -54,28 +51,7 @@ export default { }), methods: { tabChanged: function(tabIndex) { - // reset paginator - this.input.paginatorPage = 1; - switch (tabIndex) { - case 0: - client.fetchGroupsTab(this.input) - .then(model => { - this.$store.commit('updateGroups', model); - }); - break; - case 1: - client.fetchMembersPanel(this.input) - .then(panel => { - this.$store.commit('updateMembersPanel', panel); - }); - break; - case 2: - client.fetchPermissionsPanel(this.input) - .then(panel => { - this.$store.commit('updatePermissionsPanel', panel); - }); - break; - } + this.$store.dispatch('changeTab', tabIndex); }, openAddGroupModal: function() { this.$bvModal.show('add-group-modal'); diff --git a/gms-ui/src/components/MembersPanel.vue b/gms-ui/src/components/MembersPanel.vue index 30ea021ede92c526fde3b37728cacbd9a6a1ce3f..34af9ce145a42062404fd6dfcea7f10aeb933fce 100644 --- a/gms-ui/src/components/MembersPanel.vue +++ b/gms-ui/src/components/MembersPanel.vue @@ -1,8 +1,8 @@ <template> <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="#" @click="openUser(member)"> + <b-list-group v-for="member in model.membersPanel.items" id="members-list" v-bind:key="member.memberId"> + <b-list-group-item href="#" @click.prevent="openUser(member)"> <div class="float-left"> <User :user="member" :anchor="false" /> </div> @@ -25,9 +25,7 @@ import client from 'api-client'; import User from './User.vue'; import Paginator from './Paginator.vue'; import ConfirmRemoveMemberModal from './modals/ConfirmRemoveMemberModal.vue'; -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'MembersPanel', @@ -54,13 +52,13 @@ export default { }); }, openUser(member) { - this.$store.dispatch('openUserPage', member.id); + this.$router.push({ path: `/user/${member.id}` }, () => {}); } } } function getMemberPermission(self, member) { - return new Promise((resolve, reject) => { + return new Promise((resolve) => { if (self.model.permission === 'ADMIN') { let groupId = self.$store.state.input.selectedGroupId; client.getPermission(groupId, member.id) diff --git a/gms-ui/src/components/Paginator.vue b/gms-ui/src/components/Paginator.vue index b6ee2a0688f4cbc1bfe3436a2b9ad9525c6d131b..32c89da70eea1f298136b6dfa4aad2961432ac47 100644 --- a/gms-ui/src/components/Paginator.vue +++ b/gms-ui/src/components/Paginator.vue @@ -18,10 +18,6 @@ </template> <script> -import { - mapState -} from 'vuex'; - export default { name: 'Paginator', props: { diff --git a/gms-ui/src/components/PermissionsPanel.vue b/gms-ui/src/components/PermissionsPanel.vue index a8537deb8cc620069042a85c846d0a55483a307f..8a30a8c42b294b8032896872e8bffdc28e2be30a 100644 --- a/gms-ui/src/components/PermissionsPanel.vue +++ b/gms-ui/src/components/PermissionsPanel.vue @@ -10,11 +10,30 @@ </tr> </thead> <tbody> - <tr v-for="up in model.permissionsPanel.items"> + <tr v-for="(up, index) in model.permissionsPanel.items" v-bind:key="index"> <td> <User :user="up.user" :anchor="true" /> </td> - <td>{{up.permission}}</td> + <td v-if="!up.editable"> + {{up.permission}} + <a href="#" v-on:click.stop.prevent="openPermissionEditor(index)"> + <font-awesome-icon icon="edit"></font-awesome-icon> + </a> + </td> + <td v-if="up.editable" class="col-3"> + <b-input-group> + <b-form-select v-model="up.permission"> + <option value="ADMIN">ADMIN</option> + <option value="MANAGE_MEMBERS">MANAGE_MEMBERS</option> + <option value="VIEW_MEMBERS">VIEW_MEMBERS</option> + </b-form-select> + <b-input-group-append> + <b-button @click="savePermission(up, index)"> + <font-awesome-icon icon="save"></font-awesome-icon> + </b-button> + </b-input-group-append> + </b-input-group> + </td> <td> <a href="#" v-on:click.stop="openRemovePermissionModal(up.user)" class="text-danger" title="Remove permission"> <font-awesome-icon icon="trash"></font-awesome-icon> @@ -59,6 +78,16 @@ export default { .then(panel => { this.$store.commit('updatePermissionsPanel', panel); }); + }, + openPermissionEditor(index) { + this.$store.commit('togglePermissionEditable', index); + }, + savePermission(up, index) { + let permission = up.permission; + client.updatePermission(this.input.selectedGroupId, up.user.id, permission) + .then(() => { + this.$store.commit('togglePermissionEditable', index); + }); } } } diff --git a/gms-ui/src/components/TopMenu.vue b/gms-ui/src/components/TopMenu.vue index fc6e5bce2cc68c906952d5ee17245fecf5867987..b71a95cf269bb8ac72c8ff807a700156331802ad 100644 --- a/gms-ui/src/components/TopMenu.vue +++ b/gms-ui/src/components/TopMenu.vue @@ -10,7 +10,7 @@ <!-- Right aligned nav items --> <b-navbar-nav class="ml-auto"> <b-nav-form> - <b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model="input.genericSearch.filter" @keydown.native.enter="genericSearch"></b-form-input> + <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-nav-form> <b-nav-item-dropdown :text="user" right v-if="user"> @@ -23,10 +23,7 @@ </template> <script> -import client from 'api-client'; -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'TopMenu', @@ -38,20 +35,13 @@ export default { }), methods: { showMainPage() { - this.$store.commit('showMainPage'); + this.$router.push('/', () => {}); }, - genericSearch(event) { - // prevent the page reload if enter is pressed - if (event) { - event.preventDefault(); - } - + genericSearch() { this.input.genericSearch.page = 1; this.input.genericSearch.pageSize = 20; - client.search(this.input) - .then(results => { - this.$store.commit('displaySearchResults', results); - }); + this.$router.push({ path: '/search', query: { q: this.input.genericSearch.filter } }, () => {}); + this.$store.dispatch('search', this.input.genericSearch.filter); } } } diff --git a/gms-ui/src/components/User.vue b/gms-ui/src/components/User.vue index 2c6c16e33099570e38ebe6e5c49533fa8b2162af..7c2af04e218a81255ff4889df5a7ed562797477b 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"> - <component :is="anchor ? 'a' : 'span'" :href="anchor ? '#' : false" @click="openUser">{{user.displayName}}</component> + <component :is="anchor ? 'a' : 'span'" :href="anchor ? '#' : false" @click.prevent="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" v-bind:key="identity.typedId"> + <li v-for="(identity, index) in user.identities" v-bind:key="index"> {{identity.email}} ({{identity.type}}) </li> </ul> @@ -24,7 +24,7 @@ export default { }, methods: { openUser() { - this.$store.dispatch('openUserPage', this.user.id); + this.$router.push({ path: `/user/${this.user.id}` }, () => {}); } } } diff --git a/gms-ui/src/components/UserSearchResult.vue b/gms-ui/src/components/UserSearchResult.vue index c648affff26627b0c80ac7e54b244f7a0e1fff31..d5ccf14e50fb4b99fd6fc39b22bb18ead0bb696a 100644 --- a/gms-ui/src/components/UserSearchResult.vue +++ b/gms-ui/src/components/UserSearchResult.vue @@ -1,5 +1,5 @@ <template> -<div class="mt-sm-3" v-if="user !== null"> +<div class="mt-sm-3" v-if="user"> <b-button variant="primary" class="float-right" v-on:click="back()">Back</b-button> <h5><strong>{{user.displayName}}</strong>:</h5> @@ -29,7 +29,7 @@ <b-row> <b-col lg="10"> <b-list-group> - <b-list-group-item v-for="identity in user.identities" v-bind:key="identity.typedId"> + <b-list-group-item v-for="(identity, index) in user.identities" v-bind:key="index"> <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> @@ -64,14 +64,11 @@ </b-col> </b-row> </b-container> - </div> </template> <script> -import { - mapState -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'UserSearchResult', @@ -80,12 +77,22 @@ export default { groups: state => state.model.userSearchResults.groups, permissions: state => state.model.userSearchResults.permissions }), + created() { + this.openUserPage(); + }, + watch: { + // call again the method if the route changes + '$route': 'openUserPage' + }, methods: { back() { - this.$store.commit('backFromUserPage'); + this.$router.go(-1); + }, + openUserPage() { + this.$store.dispatch('openUserPage', this.$route.params.id); }, openGroup(groupId) { - this.$store.commit('openGroup', groupId); + this.$store.dispatch('openGroup', groupId); } } } diff --git a/gms-ui/src/components/modals/AddGroupModal.vue b/gms-ui/src/components/modals/AddGroupModal.vue index 7f3bcd161723b55c940db2fd493a3b7d57a946b2..8850297263fec4cbd4b77ea1f60a8c70153a7418 100644 --- a/gms-ui/src/components/modals/AddGroupModal.vue +++ b/gms-ui/src/components/modals/AddGroupModal.vue @@ -50,12 +50,11 @@ export default { } else { client.addGroup(this.newGroupName, this.leaf, this.$store.state.input) .then(res => { - if (res.status === 400) { - this.newGroupNameError = res.message; - } else { - this.$store.commit('updateGroupsPanel', res); - this.$bvModal.hide('add-group-modal'); - } + this.$store.commit('updateGroupsPanel', res); + this.$bvModal.hide('add-group-modal'); + }) + .catch(res => { + this.newGroupNameError = res.message; }); } } diff --git a/gms-ui/src/components/modals/AddMemberModal.vue b/gms-ui/src/components/modals/AddMemberModal.vue index b021b10428957d880ccd08aa521bdd7a10f25c16..13e91d97725be0b646fdf00ae611a10d357fc38f 100644 --- a/gms-ui/src/components/modals/AddMemberModal.vue +++ b/gms-ui/src/components/modals/AddMemberModal.vue @@ -7,9 +7,6 @@ <script> import client from 'api-client'; import SearchUser from './SearchUser.vue' -import { - mapState -} from 'vuex'; export default { name: 'AddMemberModal', diff --git a/gms-ui/src/components/modals/AddPermissionModal.vue b/gms-ui/src/components/modals/AddPermissionModal.vue index 9fdb437ba33aecd441e507cd73be8deb0d57a56c..b210f476598bebbcddf8e404659fbdabe8c48526 100644 --- a/gms-ui/src/components/modals/AddPermissionModal.vue +++ b/gms-ui/src/components/modals/AddPermissionModal.vue @@ -1,6 +1,9 @@ <template> -<b-modal id="add-permission-modal" title="Add permission" @shown="afterShow" ok-title="Add" @ok="addPermission"> +<b-modal id="add-permission-modal" title="Add permission" @show="beforeShow" @shown="afterShow" :ok-title="okTitle" @ok="addPermission" :ok-variant="okBtnVariant"> <SearchUser ref="searchUser" @searchUserEnter="addPermission" /> + <b-alert :show="!!existingPermission" variant="warning" class="mt-3"> + <strong>Warning</strong>: the user has already a permission ({{existingPermission}}). Click confirm to override it. + </b-alert> </b-modal> </template> @@ -13,7 +16,23 @@ export default { components: { SearchUser }, + computed: { + okBtnVariant: function() { + return this.existingPermission ? 'danger' : 'primary'; + }, + okTitle: function() { + return this.existingPermission ? 'Confirm' : 'Add'; + } + }, + data() { + return { + existingPermission: null + }; + }, methods: { + beforeShow() { + this.existingPermission = null; + }, afterShow: function() { this.$refs.searchUser.$refs.userInput.focus(); }, @@ -25,15 +44,23 @@ export default { let userId = this.$refs.searchUser.selectedUser; let permission = this.$refs.searchUser.permission; + let input = this.$store.state.input; if (!userId || !permission) { return; } - client.addPermission(userId, permission, this.$store.state.input) + client.getPermission(input.selectedGroupId, userId) .then(res => { - this.$store.commit('updatePermissionsPanel', res); - this.$bvModal.hide('add-permission-modal'); + if (res.permission && res.permission !== permission && !this.existingPermission) { + this.existingPermission = res.permission; + } else { + client.addPermission(userId, permission, input, !!this.existingPermission) + .then(res => { + this.$store.commit('updatePermissionsPanel', res); + this.$bvModal.hide('add-permission-modal'); + }); + } }); } } diff --git a/gms-ui/src/components/modals/ConfirmRemoveMemberModal.vue b/gms-ui/src/components/modals/ConfirmRemoveMemberModal.vue index 0d85f65a6a5003fc611e1ea53c1b03d4323becd5..5227c1edcf8c4d8c678206b2d68e5da3d44d0d73 100644 --- a/gms-ui/src/components/modals/ConfirmRemoveMemberModal.vue +++ b/gms-ui/src/components/modals/ConfirmRemoveMemberModal.vue @@ -9,10 +9,7 @@ <script> import client from 'api-client'; -import { - mapState, - mapActions -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'ConfirmRemoveGroupModal', diff --git a/gms-ui/src/components/modals/ConfirmRemovePermissionModal.vue b/gms-ui/src/components/modals/ConfirmRemovePermissionModal.vue index 978a5903f4a9b5c3e0e529fe663e677014b6291d..e3d8b9874b7b8caad209ab925f3fb982ff625331 100644 --- a/gms-ui/src/components/modals/ConfirmRemovePermissionModal.vue +++ b/gms-ui/src/components/modals/ConfirmRemovePermissionModal.vue @@ -6,10 +6,7 @@ <script> import client from 'api-client'; -import { - mapState, - mapActions -} from 'vuex'; +import { mapState } from 'vuex'; export default { name: 'ConfirmRemovePermissionModal', diff --git a/gms-ui/src/components/modals/EditGroupModal.vue b/gms-ui/src/components/modals/EditGroupModal.vue index d699c641a088f041d9b6bf66608395cae5c4ca0a..bfd117c527284cbdfa2f1ec3b6f09b2e1d1f9c70 100644 --- a/gms-ui/src/components/modals/EditGroupModal.vue +++ b/gms-ui/src/components/modals/EditGroupModal.vue @@ -58,12 +58,11 @@ export default { client.updateGroup(this.groupId, this.newGroupName, this.leaf, this.$store.state.input) .then(res => { - if (res.status === 400) { - this.newGroupNameError = res.message; - } else { - this.$store.commit('updateGroupsPanel', res); - this.$bvModal.hide('edit-group-modal'); - } + this.$store.commit('updateGroupsPanel', res); + this.$bvModal.hide('edit-group-modal'); + }) + .catch(res => { + this.newGroupNameError = res.message; }); } } diff --git a/gms-ui/src/components/modals/SearchUser.vue b/gms-ui/src/components/modals/SearchUser.vue index bb55488d77a4e53330ac8f1a9f07d5059ef7e5b8..ebb028719683e09edc212759b3ee3e14e2bd36c9 100644 --- a/gms-ui/src/components/modals/SearchUser.vue +++ b/gms-ui/src/components/modals/SearchUser.vue @@ -1,7 +1,7 @@ <template> <b-form inline> <label class="w-25" for="user-input">Search:</label> - <b-form-input v-model="searchInput" id="user-input" ref="userInput" class="w-75 mb-2" aria-describedby="user-input-feedback" v-on:input="searchUser" @keydown.native.enter="enterPressed" placeholder="User"> + <b-form-input v-model="searchInput" id="user-input" ref="userInput" class="w-75 mb-2" aria-describedby="user-input-feedback" v-on:input="searchUser" @keydown.native.enter.prevent="enterPressed" placeholder="User"> </b-form-input> <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> @@ -18,18 +18,15 @@ <script> import client from 'api-client'; -import { - mapState -} from 'vuex'; export default { name: 'SearchUser', properties: { actionOnEnter: Function }, - computed: mapState({ - model: state => state.model, - }), + computed: { + model: function() { return this.$store.state.model } + }, data: function() { return { searchInput: null, diff --git a/gms-ui/src/main.js b/gms-ui/src/main.js index 58c7221a5668405dfddb8ac485651b58915e1cba..255ab4300b156893096e7a9e038adaa2839d3361 100644 --- a/gms-ui/src/main.js +++ b/gms-ui/src/main.js @@ -5,13 +5,14 @@ import store from './store.js' import './plugins/bootstrap-vue' import App from './App.vue' import { library } from '@fortawesome/fontawesome-svg-core' -import { faTrash, faEdit, faSpinner, faFolder, faUser } from '@fortawesome/free-solid-svg-icons' +import { faTrash, faEdit, faSpinner, faFolder, faUser, faSave } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import VueRouter from 'vue-router' Vue.use(VueRouter) +import router from './router.js'; -library.add(faTrash, faEdit, faSpinner, faFolder, faUser); +library.add(faTrash, faEdit, faSpinner, faFolder, faUser, faSave); Vue.component('font-awesome-icon', FontAwesomeIcon); @@ -19,5 +20,6 @@ Vue.config.productionTip = false; new Vue({ render: h => h(App), - store + store, + router }).$mount('#app'); diff --git a/gms-ui/src/router.js b/gms-ui/src/router.js new file mode 100644 index 0000000000000000000000000000000000000000..a250a1f13d6090edf5a3f60bd2b1497f0a4b6c23 --- /dev/null +++ b/gms-ui/src/router.js @@ -0,0 +1,21 @@ +import VueRouter from 'vue-router'; + +import Main from './components/Main.vue'; +import GenericSearchResults from './components/GenericSearchResults.vue'; +import UserSearchResult from './components/UserSearchResult.vue'; + +export default new VueRouter({ + routes: [{ + path: '/', + component: Main + }, + { + path: '/search', + component: GenericSearchResults + }, + { + path: '/user/:id', + component: UserSearchResult + } + ] +}); diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js index f175ec5b8f55d8b9deb22e316ff920b9d3e64486..fe02ab560b3b5fc89ff032f2f038595e6f154de7 100644 --- a/gms-ui/src/store.js +++ b/gms-ui/src/store.js @@ -3,6 +3,7 @@ import Vue from 'vue'; import Vuex from 'vuex'; import client from 'api-client'; +import router from './router.js'; Vue.use(Vuex); @@ -17,9 +18,9 @@ export default new Vuex.Store({ permission: null, leaf: false, user: null, - genericSearchResults: [], + genericSearchResults: {}, userSearchResults: { - userLabel: null, + user: null, groups: {}, permissions: {} } @@ -37,84 +38,112 @@ export default new Vuex.Store({ paginatorPageSize: 20 } }, - loading: false, - previousPage: null, - page: 'main' + loading: false }, mutations: { updateHomePageModel(state, model) { - this.state.model.breadcrumbs = model.breadcrumbs; - this.state.model.groupsPanel = model.groupsPanel; - this.state.model.permission = model.permission; - this.state.model.user = model.user; - }, - openGroup(state, groupId) { - let input = this.state.input; - input.selectedGroupId = groupId; - input.searchFilter = ''; - client.fetchGroupsTab(input) - .then(model => { - this.commit('updateGroups', model); - if (model.leaf) { - // If there are no subgroups show the members panel - this.commit('setTabIndex', 1); - } else { - this.commit('setTabIndex', 0); - } - this.commit('showMainPage'); - }); + state.model.breadcrumbs = model.breadcrumbs; + state.model.groupsPanel = model.groupsPanel; + state.model.permission = model.permission; + state.model.user = model.user; }, updateGroups(state, model) { - this.state.model.breadcrumbs = model.breadcrumbs; - this.state.model.groupsPanel = model.groupsPanel; - this.state.model.permission = model.permission; - this.state.model.leaf = model.leaf; + state.model.breadcrumbs = model.breadcrumbs; + state.model.groupsPanel = model.groupsPanel; + state.model.permission = model.permission; + state.model.leaf = model.leaf; }, updateGroupsPanel(state, groupsPanel) { - this.state.model.groupsPanel = groupsPanel; - this.state.input.paginatorPage = groupsPanel.currentPage; + state.model.groupsPanel = groupsPanel; + state.input.paginatorPage = groupsPanel.currentPage; }, updatePermissionsPanel(state, permissionsPanel) { - this.state.model.permissionsPanel = permissionsPanel; - this.state.input.paginatorPage = permissionsPanel.currentPage; + state.model.permissionsPanel = permissionsPanel; + for(let up of permissionsPanel.items) { + Vue.set(up, 'editable', false); + } + state.input.paginatorPage = permissionsPanel.currentPage; + }, + togglePermissionEditable(state, index) { + let up = state.model.permissionsPanel.items[index]; + up.editable = !up.editable; }, updateMembersPanel(state, membersPanel) { - this.state.model.membersPanel = membersPanel; - this.state.input.paginatorPage = membersPanel.currentPage; + state.model.membersPanel = membersPanel; + state.input.paginatorPage = membersPanel.currentPage; }, setTabIndex(state, tabIndex) { // this will trigger the tabChanged() method in Main.vue - this.state.input.tabIndex = tabIndex; + state.input.tabIndex = tabIndex; }, setLoading(state, loading) { - this.state.loading = loading; - }, - showMainPage(state) { - this.state.page = 'main'; + state.loading = loading; }, displaySearchResults(state, results) { - this.state.page = 'search'; if (results) { - this.state.model.genericSearchResults = results; + state.model.genericSearchResults = results; } }, updateSearchResults(state, results) { - this.state.model.genericSearchResults = results; + state.model.genericSearchResults = results; + }, + setUserSearchModel(state, model) { + state.model.userSearchResults.user = model.user; + state.model.userSearchResults.groups = model.groups; + state.model.userSearchResults.permissions = model.permissions; }, - backFromUserPage(state) { - state.page = state.previousPage; + setGenericSearchFilter(state, filter) { + state.input.genericSearch.filter = filter; } }, actions: { - openUserPage({ state }, userId) { - state.previousPage = state.page; - client.openUserSearchResult(userId) + loadHomePageModel({ commit, state }) { + client.fetchHomePageModel(state.input) + .then(model => commit('updateHomePageModel', model)); + }, + search({ commit, state }, filter) { + commit('setGenericSearchFilter', filter); + client.search(state.input) + .then(results => commit('displaySearchResults', results)); + }, + changeTab({ commit, state }, tabIndex) { + state.input.tabIndex = tabIndex; + // reset paginator + state.input.paginatorPage = 1; + switch (tabIndex) { + case 0: + client.fetchGroupsTab(state.input) + .then(model => commit('updateGroups', model)); + break; + case 1: + client.fetchMembersPanel(state.input) + .then(panel => commit('updateMembersPanel', panel)); + break; + case 2: + client.fetchPermissionsPanel(state.input) + .then(panel => commit('updatePermissionsPanel', panel)); + break; + } + }, + openGroup({ commit, dispatch, state }, groupId) { + let input = state.input; + input.selectedGroupId = groupId; + input.searchFilter = ''; + client.fetchGroupsTab(input) .then(model => { - state.page = 'userSearch'; - state.model.userSearchResults.user = model.user; - state.model.userSearchResults.groups = model.groups; - state.model.userSearchResults.permissions = model.permissions; + commit('updateGroups', model); + if (model.leaf) { + // If there are no subgroups show the members panel + dispatch('changeTab', 1); + } else { + dispatch('changeTab', 0); + } + router.push('/', () => {}); }); + }, + openUserPage({ commit }, userId) { + client.openUserSearchResult(userId) + .then(model => commit('setUserSearchModel', model)); } }, getters: { 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 5840978ae51c9bba38628e74e546dd944d46621e..9742396d930bb9bc279952e7c10f2661a78edbc0 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 @@ -9,7 +9,9 @@ import it.inaf.ia2.gms.model.request.PaginatedModelRequest; 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.model.request.UpdatePermissionRequest; import it.inaf.ia2.gms.persistence.model.GroupEntity; +import it.inaf.ia2.gms.persistence.model.PermissionEntity; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -22,6 +24,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -63,11 +66,27 @@ public class PermissionsController { public ResponseEntity<PaginatedData<UserPermission>> addPermission(@Valid @RequestBody AddPermissionRequest request) { GroupEntity group = groupsManager.getGroupById(request.getGroupId()); - permissionsManager.addPermission(group, request.getUserId(), request.getPermission()); + if (request.isOverride()) { + permissionsManager.updatePermission(group, request.getUserId(), request.getPermission()); + } else { + permissionsManager.addPermission(group, request.getUserId(), request.getPermission()); + } return new ResponseEntity<>(getPermissionsPanel(group, request), HttpStatus.CREATED); } + @PutMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity<Map<String, String>> updatePermission(@Valid @RequestBody UpdatePermissionRequest request) { + + GroupEntity group = groupsManager.getGroupById(request.getGroupId()); + PermissionEntity updatedEntity = permissionsManager.updatePermission(group, request.getUserId(), request.getPermission()); + + Map<String, String> response = new HashMap<>(); + response.put("permission", updatedEntity.getPermission().toString()); + + return ResponseEntity.ok(response); + } + @DeleteMapping(value = "/permission", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<PaginatedData<UserPermission>> deletePermission(@Valid MemberRequest request) { 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 595b1e48d7181d184bfb5826ed7b3f06ea244d31..e5d908c265228008b552db7aa3efa6cfd0ee826e 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_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(value = "users", 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 a1503f09b8d467433ef88e219ff8bd6cb07bb3f0..238797c65ab6282fa6d11917dec90a31b298a50b 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 @@ -83,6 +83,11 @@ public class PermissionsManager extends UserAwareComponent { return permissionsService.addPermission(group, userId, permission); } + public PermissionEntity updatePermission(GroupEntity group, String userId, Permission permission) { + verifyUserCanManagePermissions(group); + return permissionsService.updatePermission(group, userId, permission); + } + public void removePermission(GroupEntity group, String userId) { verifyUserCanManagePermissions(group); permissionsService.removePermission(group, userId); diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/request/AddPermissionRequest.java b/gms/src/main/java/it/inaf/ia2/gms/model/request/AddPermissionRequest.java index c10ae5f01a90bb2482a35d596047e5265a2f4ba8..c1d4fd0f3a0adebe285b6ddb5d8dbba84c017e49 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/model/request/AddPermissionRequest.java +++ b/gms/src/main/java/it/inaf/ia2/gms/model/request/AddPermissionRequest.java @@ -13,6 +13,8 @@ public class AddPermissionRequest extends PaginatedModelRequest { @NotNull private Permission permission; + private boolean override; + public String getGroupId() { return groupId; } @@ -36,4 +38,12 @@ public class AddPermissionRequest extends PaginatedModelRequest { public void setPermission(Permission permission) { this.permission = permission; } + + public boolean isOverride() { + return override; + } + + public void setOverride(boolean override) { + this.override = override; + } } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/request/UpdatePermissionRequest.java b/gms/src/main/java/it/inaf/ia2/gms/model/request/UpdatePermissionRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..2125f0d8c70ba003140fcc5d5503112e156e8b47 --- /dev/null +++ b/gms/src/main/java/it/inaf/ia2/gms/model/request/UpdatePermissionRequest.java @@ -0,0 +1,39 @@ +package it.inaf.ia2.gms.model.request; + +import it.inaf.ia2.gms.model.Permission; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +public class UpdatePermissionRequest { + + @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/persistence/PermissionsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java index d8d417ee24f340f86bfe14111e3f7fe8f4a03a13..4ffd9b97b6aeaa7a3670a81d449690bd701e322c 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java +++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsDAO.java @@ -41,6 +41,24 @@ public class PermissionsDAO { return userPermission; } + public PermissionEntity updatePermission(PermissionEntity userPermission, Permission newPermission) { + + String sql = "UPDATE gms_permission SET permission = ? WHERE group_id = ? AND user_id = ? AND group_path = ?"; + + userPermission.setPermission(newPermission); + + jdbcTemplate.update(conn -> { + PreparedStatement ps = conn.prepareStatement(sql); + ps.setObject(1, userPermission.getPermission().toString(), Types.OTHER); + ps.setString(2, userPermission.getGroupId()); + ps.setString(3, userPermission.getUserId()); + ps.setObject(4, userPermission.getGroupPath(), Types.OTHER); + return ps; + }); + + return userPermission; + } + public List<PermissionEntity> findUserPermissions(String userId) { String sql = "SELECT group_id, permission, group_path FROM gms_permission WHERE user_id = ?"; 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 af9a942c8e0b9f715a6b78ed0170c4a95766f52e..b0525e457852a1bc9f5e8cca8efeaa32726838ac 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 @@ -61,4 +61,12 @@ public class PermissionsService { return permissionEntity; } + + public PermissionEntity updatePermission(GroupEntity group, String userId, Permission permission) { + + PermissionEntity permissionEntity = permissionsDAO.findPermissionEntity(group.getId(), userId) + .orElseThrow(() -> new IllegalArgumentException("Specified permission not found")); + + return permissionsDAO.updatePermission(permissionEntity, permission); + } }