diff --git a/gms-ui/.env.production b/gms-ui/.env.production index 64c8364dd0191c84b8fe5dffb41abd01962bd626..6010f55022316c82ff11bfeeb216720afd9614ea 100644 --- a/gms-ui/.env.production +++ b/gms-ui/.env.production @@ -1,2 +1,2 @@ VUE_APP_API_CLIENT = 'server' -VUE_APP_API_BASE_URL = '/' +VUE_APP_API_BASE_URL = '' diff --git a/gms-ui/src/components/GroupsPanel.vue b/gms-ui/src/components/GroupsPanel.vue index cde71c94d7fc34f4f107541131e35a8128c5ea05..4522e7198c387649dfbce9fb7943788c6c82b878 100644 --- a/gms-ui/src/components/GroupsPanel.vue +++ b/gms-ui/src/components/GroupsPanel.vue @@ -61,7 +61,12 @@ export default { this.$store.state.input.searchFilter = null; client.fetchGroupsTab(this.input) .then(model => { - this.$store.commit('updateGroups', model); + if (model.groupsPanel.items.length > 0) { + this.$store.commit('updateGroups', model); + } else { + // If there are no subgroups show the members panel + this.$store.commit('setTabIndex', '1'); + } }); }, openRenameGroupModal: function(group) { diff --git a/gms-ui/src/components/TopMenu.vue b/gms-ui/src/components/TopMenu.vue index 1035d588751601d8244c9253c5b100dbd3158f89..f3f91cac693ea1cc9d5e954b52b7b71b209c47b6 100644 --- a/gms-ui/src/components/TopMenu.vue +++ b/gms-ui/src/components/TopMenu.vue @@ -9,14 +9,14 @@ <!-- Right aligned nav items --> <b-navbar-nav class="ml-auto"> + <!-- <b-nav-form> <b-form-input size="sm" class="mr-sm-2" placeholder="Search"></b-form-input> <b-button size="sm" class="my-2 my-sm-0" type="submit">Search</b-button> </b-nav-form> - - <b-nav-item-dropdown right v-if="user"> - <template slot="button-content"><em>{{user}}</em></template> - <b-dropdown-item href="#">Preferences</b-dropdown-item> + --> + <b-nav-item-dropdown :text="user" right v-if="user"> + <b-dropdown-item href="logout">Logout</b-dropdown-item> </b-nav-item-dropdown> </b-navbar-nav> </b-collapse> @@ -32,3 +32,10 @@ export default { } } </script> + +<style> +.navbar-dark .nav-link { + color: rgba(255, 255, 255, .7); + font-weight: bold; +} +</style> diff --git a/gms-ui/src/components/modals/AddGroupModal.vue b/gms-ui/src/components/modals/AddGroupModal.vue index f8eb7e22dee07a4c9f46806f22f01a0d6ee21f5d..09e1f5aa0629b939d3ae835ef9cb12aa929ef02e 100644 --- a/gms-ui/src/components/modals/AddGroupModal.vue +++ b/gms-ui/src/components/modals/AddGroupModal.vue @@ -1,8 +1,8 @@ <template> -<b-modal id="add-group-modal" title="Add group" @show="resetModal" ok-title="Add" @ok="addGroup"> +<b-modal id="add-group-modal" title="Add group" @show="resetModal" @shown="afterShow" ok-title="Add" @ok="addGroup"> <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" 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">{{newGroupNameError}}</b-form-invalid-feedback> </b-form> @@ -32,6 +32,9 @@ export default { this.newGroupName = null; this.resetError(); }, + afterShow: function() { + this.$refs.newGroupNameInput.focus(); + }, resetError: function() { this.newGroupNameError = null; }, @@ -41,19 +44,17 @@ export default { if (!this.newGroupName) { this.newGroupNameError = "Group name is required"; - return; + } else { + client.addGroup(this.newGroupName, 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'); + } + }); } - - let parent = this.$store.getters.selectedGroupId; - client.addGroup(this.newGroupName, 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'); - } - }); } } } diff --git a/gms-ui/src/components/modals/AddMemberModal.vue b/gms-ui/src/components/modals/AddMemberModal.vue index 37c4feacaaa7dfa879d845c274e39fb043c05dd9..b021b10428957d880ccd08aa521bdd7a10f25c16 100644 --- a/gms-ui/src/components/modals/AddMemberModal.vue +++ b/gms-ui/src/components/modals/AddMemberModal.vue @@ -1,6 +1,6 @@ <template> -<b-modal id="add-member-modal" title="Add member" ok-title="Add" @ok="addMember"> - <SearchUser ref="searchUser" /> +<b-modal id="add-member-modal" title="Add member" ok-title="Add" @shown="afterShow" @ok="addMember"> + <SearchUser ref="searchUser" @searchUserEnter="addMember" /> </b-modal> </template> @@ -17,13 +17,22 @@ export default { SearchUser }, methods: { + afterShow: function() { + this.$refs.searchUser.$refs.userInput.focus(); + }, addMember: function(event) { // Prevent modal from closing - event.preventDefault(); + if (event) { + event.preventDefault(); + } let userId = this.$refs.searchUser.selectedUser; let permission = this.$refs.searchUser.permission; + if (!userId || !permission) { + return; + } + client.addMember(userId, permission, this.$store.state.input) .then(res => { this.$store.commit('updateMembersPanel', res); diff --git a/gms-ui/src/components/modals/AddPermissionModal.vue b/gms-ui/src/components/modals/AddPermissionModal.vue index b9ea8eac555bdf8ba3fc0fba38c94e6e1a03d62e..9fdb437ba33aecd441e507cd73be8deb0d57a56c 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 ref="searchUser" /> +<b-modal id="add-permission-modal" title="Add permission" @shown="afterShow" ok-title="Add" @ok="addPermission"> + <SearchUser ref="searchUser" @searchUserEnter="addPermission" /> </b-modal> </template> @@ -14,13 +14,22 @@ export default { SearchUser }, methods: { + afterShow: function() { + this.$refs.searchUser.$refs.userInput.focus(); + }, addPermission: function(event) { // Prevent modal from closing - event.preventDefault(); + if (event) { + event.preventDefault(); + } let userId = this.$refs.searchUser.selectedUser; let permission = this.$refs.searchUser.permission; + if (!userId || !permission) { + return; + } + client.addPermission(userId, permission, this.$store.state.input) .then(res => { this.$store.commit('updatePermissionsPanel', res); diff --git a/gms-ui/src/components/modals/SearchUser.vue b/gms-ui/src/components/modals/SearchUser.vue index 4c1272b52c93715a7d68ecab07086b20ccd3411c..bb55488d77a4e53330ac8f1a9f07d5059ef7e5b8 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" class="w-75 mb-2" aria-describedby="user-input-feedback" v-on:input="searchUser" 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="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> @@ -24,6 +24,9 @@ import { export default { name: 'SearchUser', + properties: { + actionOnEnter: Function + }, computed: mapState({ model: state => state.model, }), @@ -53,6 +56,9 @@ export default { this.selectedUser = this.users[0].value; } }); + }, + enterPressed: function() { + this.$emit('searchUserEnter'); } } }; diff --git a/gms-ui/vue.config.js b/gms-ui/vue.config.js index faddadb79b9d4c35bd8c0740575b9ffff2550f98..93ef4cbf7ba4ae11f7d8cde4b5cfd575e48eeae1 100644 --- a/gms-ui/vue.config.js +++ b/gms-ui/vue.config.js @@ -1,6 +1,7 @@ const path = require('path'); module.exports = { + publicPath: '', chainWebpack: config => { const apiClient = process.env.VUE_APP_API_CLIENT // mock or server config.resolve.alias.set( diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java b/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java index 114fa930665cbd8ff1f40b480b63c9a73d4d8110..e77eaaf786c031ec92919c4fa461e1660f3374fe 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java +++ b/gms/src/main/java/it/inaf/ia2/gms/authn/SecurityConfig.java @@ -59,7 +59,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { */ @Override public void configure(WebSecurity web) throws Exception { - web.ignoring().antMatchers("/ws/basic/**", "/ws/jwt/**", "/error"); + web.ignoring().antMatchers("/ws/basic/**", "/ws/jwt/**", "/error", "/logout"); } /** diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java index 0bfdeac494acc4c05b08bdfa69ce6105ab9cbfa7..fc3f0a4bb06da987cff3d6f85acf18cc14c89448 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java +++ b/gms/src/main/java/it/inaf/ia2/gms/controller/HomePageController.java @@ -4,6 +4,9 @@ import it.inaf.ia2.gms.authn.SessionData; 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 java.io.IOException; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; @@ -11,6 +14,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @Controller public class HomePageController { @@ -41,4 +45,11 @@ public class HomePageController { public String index() { return "index.html"; } + + @GetMapping(value = "/logout", produces = MediaType.TEXT_HTML_VALUE) + public void logout(HttpSession httpSession, HttpServletResponse response) throws IOException { + httpSession.invalidate(); + String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString(); + response.sendRedirect(baseUrl); + } } diff --git a/gms/src/main/resources/application.properties b/gms/src/main/resources/application.properties index 94200da73eda4b6759c19f6a73062489b9bd584c..1bb740b6c30e8a89172d700379d7fccaf1514431 100644 --- a/gms/src/main/resources/application.properties +++ b/gms/src/main/resources/application.properties @@ -1,4 +1,5 @@ server.port=8081 +server.servlet.context-path=/gms security.oauth2.client.client-id=gms security.oauth2.client.client-secret=gms-secret