From d0d000e192c76953c18d9ade0595899f6738e015 Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Thu, 10 Sep 2020 14:49:25 +0200
Subject: [PATCH] Various improvements

---
 database/Dockerfile                           |  1 +
 gms-client/gms-client-lib/pom.xml             |  5 +++
 .../inaf/ia2/gms/client/call/BaseGmsCall.java | 21 +++++-----
 gms-ui/src/components/GroupsBreadcrumb.vue    |  2 +-
 gms-ui/src/components/MembersPanel.vue        |  2 +-
 .../src/components/modals/AddGroupModal.vue   |  3 +-
 .../gms/controller/HomePageController.java    | 38 +++++++++++++++++--
 .../ia2/gms/manager/GroupStatusManager.java   |  1 +
 .../persistence/InvitedRegistrationDAO.java   | 29 ++++++++++++++
 .../ia2/gms/persistence/MembershipsDAO.java   |  4 ++
 .../ia2/gms/persistence/PermissionsDAO.java   |  6 ++-
 .../inaf/ia2/gms/service/GroupsService.java   |  7 +++-
 .../NestedGroupsIntegrationTest.java          |  3 +-
 13 files changed, 101 insertions(+), 21 deletions(-)

diff --git a/database/Dockerfile b/database/Dockerfile
index d73d745..28a49e7 100644
--- a/database/Dockerfile
+++ b/database/Dockerfile
@@ -1,4 +1,5 @@
 FROM library/postgres:11
 COPY gms/src/main/resources/sql/init.sql /docker-entrypoint-initdb.d/
 COPY database/user.sql /docker-entrypoint-initdb.d/
+ENV ALLOW_IP_RANGE=0.0.0.0/0
 ENV POSTGRES_HOST_AUTH_METHOD=trust
diff --git a/gms-client/gms-client-lib/pom.xml b/gms-client/gms-client-lib/pom.xml
index b05f489..571ed7f 100644
--- a/gms-client/gms-client-lib/pom.xml
+++ b/gms-client/gms-client-lib/pom.xml
@@ -11,6 +11,11 @@
         <maven.compiler.target>12</maven.compiler.target>
     </properties>
     <dependencies>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-to-slf4j</artifactId>
+            <version>2.12.1</version>
+        </dependency>
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java
index 161de1f..f5304b7 100644
--- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java
+++ b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/call/BaseGmsCall.java
@@ -6,12 +6,12 @@ import java.net.http.HttpRequest;
 import java.net.http.HttpRequest.Builder;
 import java.net.http.HttpResponse;
 import java.util.Scanner;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public abstract class BaseGmsCall {
 
-    private static final Logger LOGGER = Logger.getLogger(BaseGmsCall.class.getName());
+    private static final Logger LOGGER = LoggerFactory.getLogger(BaseGmsCall.class);
 
     protected final HttpClientWrapper clientWrapper;
 
@@ -28,18 +28,17 @@ public abstract class BaseGmsCall {
     }
 
     protected static void logServerError(HttpRequest request, HttpResponse<String> response) {
-        LOGGER.log(Level.SEVERE, () -> "Error while reading " + request.uri()
+        LOGGER.error("Error while reading " + request.uri()
                 + "\nServer response status code is " + response.statusCode()
                 + "\nServer response text is " + response.body());
     }
 
     protected static void logServerErrorInputStream(HttpRequest request, HttpResponse<InputStream> response) {
-        LOGGER.log(Level.SEVERE, () -> {
-            Scanner s = new Scanner(response.body()).useDelimiter("\\A");
-            String responseBody = s.hasNext() ? s.next() : "";
-            return "Error while reading " + request.uri()
-                    + "\nServer response status code is " + response.statusCode()
-                    + "\nServer response text is " + responseBody;
-        });
+        Scanner s = new Scanner(response.body()).useDelimiter("\\A");
+        String responseBody = s.hasNext() ? s.next() : "";
+        String error = "Error while reading " + request.uri()
+                + "\nServer response status code is " + response.statusCode()
+                + "\nServer response text is " + responseBody;
+        LOGGER.error(error);
     }
 }
diff --git a/gms-ui/src/components/GroupsBreadcrumb.vue b/gms-ui/src/components/GroupsBreadcrumb.vue
index f1e6f6c..c638b20 100644
--- a/gms-ui/src/components/GroupsBreadcrumb.vue
+++ b/gms-ui/src/components/GroupsBreadcrumb.vue
@@ -6,7 +6,7 @@
       <span v-if="group.active">{{group.groupName}}</span>
     </li>
   </ol>
-  <a :href="'group/status/' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV">
+  <a v-if="currentGroup" :href="'group/status/' + currentGroup.groupId" :download="currentGroup.groupName + '.csv'" id="csv-status-download" title="Download CSV">
     <font-awesome-icon icon="download"></font-awesome-icon>
   </a>
 </nav>
diff --git a/gms-ui/src/components/MembersPanel.vue b/gms-ui/src/components/MembersPanel.vue
index 335d23d..eb71f91 100644
--- a/gms-ui/src/components/MembersPanel.vue
+++ b/gms-ui/src/components/MembersPanel.vue
@@ -7,7 +7,7 @@
           <User :user="member" :anchor="false" />
         </div>
         <span v-if="model.permission === 'ADMIN' || model.permission === 'MANAGE_MEMBERS'" class="float-right">
-          <a href="#" v-on:click.stop="openRemoveMemberModal(member)" class="text-danger" title="Remove member">
+          <a href="#" v-on:click.stop.prevent="openRemoveMemberModal(member)" class="text-danger" title="Remove member">
             <font-awesome-icon icon="trash"></font-awesome-icon>
           </a>
         </span>
diff --git a/gms-ui/src/components/modals/AddGroupModal.vue b/gms-ui/src/components/modals/AddGroupModal.vue
index 8850297..e48328f 100644
--- a/gms-ui/src/components/modals/AddGroupModal.vue
+++ b/gms-ui/src/components/modals/AddGroupModal.vue
@@ -27,7 +27,7 @@ export default {
     return {
       newGroupName: '',
       newGroupNameError: '',
-      leaf: false
+      leaf: true
     };
   },
   methods: {
@@ -37,6 +37,7 @@ export default {
     },
     afterShow: function() {
       this.$refs.newGroupNameInput.focus();
+      this.leaf = true;
     },
     resetError: function() {
       this.newGroupNameError = null;
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 bb5240f..ab10140 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
@@ -1,12 +1,19 @@
 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;
 import javax.servlet.http.HttpServletRequest;
@@ -41,14 +48,37 @@ public class HomePageController {
 
         response.setUser(session.getUserName());
 
-        GroupsTabResponse groupsTabResponse = groupsTabResponseBuilder.getGroupsTab(request);
-        response.setBreadcrumbs(groupsTabResponse.getBreadcrumbs());
-        response.setGroupsPanel(groupsTabResponse.getGroupsPanel());
-        response.setPermission(groupsTabResponse.getPermission());
+        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;
+            }
+        }
 
         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 {
 
diff --git a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
index 07cbb42..7e75650 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/manager/GroupStatusManager.java
@@ -55,6 +55,7 @@ public class GroupStatusManager extends UserAwareComponent {
         }
 
         List<GroupEntity> groups = groupsDAO.getAllChildren(parentGroup.getPath());
+        groups.add(parentGroup);
 
         List<String> names = groupNameService.getGroupsNames(groups);
 
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/InvitedRegistrationDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/InvitedRegistrationDAO.java
index 5fb1057..af61db9 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/InvitedRegistrationDAO.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/InvitedRegistrationDAO.java
@@ -5,8 +5,10 @@ import it.inaf.ia2.gms.persistence.model.InvitedRegistration;
 import java.sql.PreparedStatement;
 import java.sql.Types;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 import javax.sql.DataSource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -114,4 +116,31 @@ public class InvitedRegistrationDAO {
             return ps;
         });
     }
+
+    /**
+     * Called before deleting a group.
+     */
+    public void deleteAllGroupsInvitedRegistrations(List<String> groupIds) {
+
+        if (groupIds.isEmpty()) {
+            return;
+        }
+
+        String sql = "DELETE FROM invited_registration_request_group WHERE group_id = ("
+                + String.join(",", groupIds.stream().map(g -> "?").collect(Collectors.toList()))
+                + ")";
+
+        jdbcTemplate.update(conn -> {
+            PreparedStatement ps = conn.prepareStatement(sql);
+            int i = 0;
+            for (String groupId : groupIds) {
+                ps.setString(++i, groupId);
+            }
+            return ps;
+        });
+
+        // Cleanup orphan invited requests
+        jdbcTemplate.update("DELETE FROM invited_registration_request WHERE id NOT IN "
+                + "(SELECT request_id FROM invited_registration_request_group)");
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java
index 22b0811..d08faf4 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipsDAO.java
@@ -151,6 +151,10 @@ public class MembershipsDAO {
 
     public void deleteAllGroupsMembership(List<String> groupIds) {
 
+        if (groupIds.isEmpty()) {
+            return;
+        }
+
         String sql = "DELETE FROM gms_membership WHERE group_id IN ("
                 + String.join(",", groupIds.stream().map(g -> "?").collect(Collectors.toList()))
                 + ")";
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 4ffd9b9..48bc56e 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
@@ -46,7 +46,7 @@ public class PermissionsDAO {
         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);
@@ -164,6 +164,10 @@ public class PermissionsDAO {
 
     public void deleteAllGroupsPermissions(List<String> groupIds) {
 
+        if (groupIds.isEmpty()) {
+            return;
+        }
+
         String sql = "DELETE FROM gms_permission WHERE group_id IN ("
                 + String.join(",", groupIds.stream().map(g -> "?").collect(Collectors.toList()))
                 + ")";
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
index 6ae23bb..14d503e 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsService.java
@@ -6,6 +6,7 @@ import it.inaf.ia2.gms.model.GroupBreadcrumb;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import it.inaf.ia2.gms.persistence.GroupsDAO;
+import it.inaf.ia2.gms.persistence.InvitedRegistrationDAO;
 import it.inaf.ia2.gms.persistence.LoggingDAO;
 import it.inaf.ia2.gms.persistence.MembershipsDAO;
 import it.inaf.ia2.gms.persistence.PermissionsDAO;
@@ -23,14 +24,17 @@ public class GroupsService {
     private final GroupsDAO groupsDAO;
     private final PermissionsDAO permissionsDAO;
     private final MembershipsDAO membershipsDAO;
+    private final InvitedRegistrationDAO invitedRegistrationDAO;
     private final LoggingDAO loggingDAO;
 
     @Autowired
     public GroupsService(GroupsDAO groupsDAO, PermissionsDAO permissionsDAO,
-            MembershipsDAO membershipsDAO, LoggingDAO loggingDAO) {
+            MembershipsDAO membershipsDAO, InvitedRegistrationDAO invitedRegistrationDAO,
+            LoggingDAO loggingDAO) {
         this.groupsDAO = groupsDAO;
         this.permissionsDAO = permissionsDAO;
         this.membershipsDAO = membershipsDAO;
+        this.invitedRegistrationDAO = invitedRegistrationDAO;
         this.loggingDAO = loggingDAO;
         createRootIfNecessary();
     }
@@ -105,6 +109,7 @@ public class GroupsService {
         List<String> groupsToDeleteIds = groupsToDelete.stream()
                 .map(g -> g.getId()).collect(Collectors.toList());
 
+        invitedRegistrationDAO.deleteAllGroupsInvitedRegistrations(groupsToDeleteIds);
         membershipsDAO.deleteAllGroupsMembership(groupsToDeleteIds);
         permissionsDAO.deleteAllGroupsPermissions(groupsToDeleteIds);
 
diff --git a/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java b/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java
index 21d437e..baa08c1 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/persistence/NestedGroupsIntegrationTest.java
@@ -46,10 +46,11 @@ public class NestedGroupsIntegrationTest {
         GroupsDAO groupsDAO = new GroupsDAO(dataSource);
         PermissionsDAO permissionsDAO = new PermissionsDAO(dataSource);
         MembershipsDAO membershipsDAO = new MembershipsDAO(dataSource);
+        InvitedRegistrationDAO invitedRegistrationDAO = new InvitedRegistrationDAO(dataSource);
         PermissionsService permissionsService = new PermissionsService(permissionsDAO, loggingDAO);
         PermissionsManager permissionsManager = new PermissionsManager(permissionsService, rapClient, loggingDAO);
         UserAwareComponentTestUtil.setUser(permissionsManager, userId);
-        GroupsService groupsService = new GroupsService(groupsDAO, permissionsDAO, membershipsDAO, loggingDAO);
+        GroupsService groupsService = new GroupsService(groupsDAO, permissionsDAO, membershipsDAO, invitedRegistrationDAO, loggingDAO);
         GroupsManager groupsManager = new GroupsManager(groupsService, permissionsManager, loggingDAO);
         GroupsTreeBuilder groupsTreeBuilder = new GroupsTreeBuilder(groupsDAO, permissionsDAO);
 
-- 
GitLab