From 18d45bcaead9a34e6dac7261821f73fdc833cd47 Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Tue, 17 Sep 2019 17:09:31 +0200
Subject: [PATCH] UI improvements; Added logout

---
 gms-ui/.env.production                        |  2 +-
 gms-ui/src/components/GroupsPanel.vue         |  7 ++++-
 gms-ui/src/components/TopMenu.vue             | 15 +++++++---
 .../src/components/modals/AddGroupModal.vue   | 29 ++++++++++---------
 .../src/components/modals/AddMemberModal.vue  | 15 ++++++++--
 .../components/modals/AddPermissionModal.vue  | 15 ++++++++--
 gms-ui/src/components/modals/SearchUser.vue   |  8 ++++-
 gms-ui/vue.config.js                          |  1 +
 .../it/inaf/ia2/gms/authn/SecurityConfig.java |  2 +-
 .../gms/controller/HomePageController.java    | 11 +++++++
 gms/src/main/resources/application.properties |  1 +
 11 files changed, 78 insertions(+), 28 deletions(-)

diff --git a/gms-ui/.env.production b/gms-ui/.env.production
index 64c8364..6010f55 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 cde71c9..4522e71 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 1035d58..f3f91ca 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 f8eb7e2..09e1f5a 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 37c4fea..b021b10 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 b9ea8ea..9fdb437 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 4c1272b..bb55488 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 faddadb..93ef4cb 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 114fa93..e77eaaf 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 0bfdeac..fc3f0a4 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 94200da..1bb740b 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
-- 
GitLab