From 0c7fdc60f3ede3ced3c2cc5307ed86af03b3ce9d Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Tue, 6 Aug 2019 10:17:54 +0200
Subject: [PATCH] Added PermissionsController and search user functionality

---
 .../it/inaf/ia2/gms/authn/SessionData.java    |  2 +-
 .../ia2/gms/controller/GroupsController.java  |  3 -
 .../gms/controller/PermissionsController.java | 36 +++++++++
 .../ia2/gms/controller/UsersController.java   | 23 ++++++
 .../java/it/inaf/ia2/gms/model/Identity.java  | 27 +++++++
 .../it/inaf/ia2/gms/model/IdentityType.java   | 15 ++++
 .../java/it/inaf/ia2/gms/model/RapUser.java   | 38 ++++++---
 .../persistence/PermissionsRepository.java    |  6 ++
 .../java/it/inaf/ia2/gms/rap/RapClient.java   | 16 ++++
 .../ia2/gms/service/PermissionsService.java   | 20 ++++-
 .../PermissionsServiceIntegrationTest.java    | 79 +++++++++++++++++++
 11 files changed, 249 insertions(+), 16 deletions(-)
 create mode 100644 gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java
 create mode 100644 gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java
 create mode 100644 gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java

diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
index 92ddbc8..b3fd20a 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
@@ -26,7 +26,7 @@ public class SessionData {
     public void init() {
         CustomAuthenticationData authn = (CustomAuthenticationData) ((OAuth2Authentication) request.getUserPrincipal()).getUserAuthentication();
         userId = (String) authn.getPrincipal();
-        accessToken = (String) authn.getAttributes().get("access_token");
+        accessToken = (String) authn.getAccessToken().getValue();
 
         if (!usersRepository.findById(userId).isPresent()) {
             User user = new User();
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
index 256cc9a..22c93cb 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
@@ -1,7 +1,6 @@
 package it.inaf.ia2.gms.controller;
 
 import it.inaf.ia2.gms.authn.SessionData;
-import it.inaf.ia2.gms.exception.BadRequestException;
 import it.inaf.ia2.gms.model.CreateGroupRequest;
 import it.inaf.ia2.gms.model.GroupNode;
 import it.inaf.ia2.gms.model.GroupsModelRequest;
@@ -14,9 +13,7 @@ import it.inaf.ia2.gms.persistence.model.User;
 import it.inaf.ia2.gms.service.GroupsModelService;
 import it.inaf.ia2.gms.service.GroupsService;
 import it.inaf.ia2.gms.service.UsersService;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import javax.validation.Valid;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
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
new file mode 100644
index 0000000..4ae3d1c
--- /dev/null
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/PermissionsController.java
@@ -0,0 +1,36 @@
+package it.inaf.ia2.gms.controller;
+
+import it.inaf.ia2.gms.authn.SessionData;
+import it.inaf.ia2.gms.model.Permission;
+import it.inaf.ia2.gms.persistence.model.User;
+import it.inaf.ia2.gms.service.PermissionsService;
+import it.inaf.ia2.gms.service.UsersService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class PermissionsController {
+
+    @Autowired
+    private SessionData session;
+
+    @Autowired
+    private UsersService usersService;
+    
+    @Autowired
+    private PermissionsService permissionsService;
+
+    @DeleteMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+    public ResponseEntity deletePermission(@RequestParam("user") String userId, @RequestParam("group") String group, @RequestParam("permission") Permission permission) {
+        User user = getUser();
+        return null;
+    }
+
+    private User getUser() {
+        return usersService.getUserById(session.getUserId());
+    }
+}
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
new file mode 100644
index 0000000..54577f6
--- /dev/null
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/UsersController.java
@@ -0,0 +1,23 @@
+package it.inaf.ia2.gms.controller;
+
+import it.inaf.ia2.gms.model.RapUser;
+import it.inaf.ia2.gms.rap.RapClient;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class UsersController {
+
+    @Autowired
+    private RapClient rapClient;
+
+    @GetMapping(value = "users", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_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/model/Identity.java b/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java
index c7031e5..12d99a2 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/model/Identity.java
@@ -4,6 +4,9 @@ public class Identity {
 
     private IdentityType type;
     private String email;
+    private String name;
+    private String surname;
+    private boolean primary;
 
     public IdentityType getType() {
         return type;
@@ -20,4 +23,28 @@ public class Identity {
     public void setEmail(String email) {
         this.email = email;
     }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSurname() {
+        return surname;
+    }
+
+    public void setSurname(String surname) {
+        this.surname = surname;
+    }
+
+    public boolean isPrimary() {
+        return primary;
+    }
+
+    public void setPrimary(boolean primary) {
+        this.primary = primary;
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java b/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
index fe0c6f6..4ae1214 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
@@ -1,5 +1,9 @@
 package it.inaf.ia2.gms.model;
 
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import java.util.Arrays;
+
 public enum IdentityType {
 
     EDU_GAIN("EduGAIN"),
@@ -19,4 +23,15 @@ public enum IdentityType {
     public String getValue() {
         return value;
     }
+
+    @JsonCreator
+    public static IdentityType forValue(String value) {
+        return Arrays.stream(IdentityType.values())
+                .filter(it -> value.equals(it.value)).findFirst().get();
+    }
+
+    @JsonValue
+    public String toValue() {
+        return value;
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java b/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java
index e58f27d..c3854ec 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/model/RapUser.java
@@ -1,11 +1,12 @@
 package it.inaf.ia2.gms.model;
 
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 public class RapUser {
 
     private String id;
-    private String displayName;
     private List<Identity> identities;
 
     public String getId() {
@@ -16,14 +17,6 @@ public class RapUser {
         this.id = id;
     }
 
-    public String getDisplayName() {
-        return displayName;
-    }
-
-    public void setDisplayName(String displayName) {
-        this.displayName = displayName;
-    }
-
     public List<Identity> getIdentities() {
         return identities;
     }
@@ -31,4 +24,31 @@ public class RapUser {
     public void setIdentities(List<Identity> identities) {
         this.identities = identities;
     }
+
+    public String getDisplayName() {
+
+        String displayName = null;
+
+        // trying to extract name and surname
+        for (Identity identity : identities) {
+            if (identity.getName() != null && identity.getSurname() != null) {
+                displayName = String.format("%s %s", identity.getName(), identity.getSurname());
+                if (identity.isPrimary()) { // prefer always primary
+                    break;
+                }
+            }
+        }
+
+        if (displayName == null) { // No name and surname --> using primary email
+            Identity primaryIdentity = identities.stream().filter(i -> i.isPrimary()).findFirst()
+                    .orElseThrow(() -> new IllegalStateException("No primary identity for user " + id));
+            displayName = primaryIdentity.getEmail();
+        }
+
+        // Adding types
+        Set<String> types = identities.stream().map(i -> i.getType().getValue()).collect(Collectors.toSet());
+        displayName += String.format(" (%s)", String.join(",", types));
+
+        return displayName;
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
index d43f293..9184992 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
@@ -1,12 +1,15 @@
 package it.inaf.ia2.gms.persistence;
 
+import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.persistence.model.Group;
 import it.inaf.ia2.gms.persistence.model.User;
 import it.inaf.ia2.gms.persistence.model.UserGroupPermission;
 import it.inaf.ia2.gms.persistence.model.UserGroupPermissionId;
 import java.util.List;
+import java.util.Optional;
 import javax.transaction.Transactional;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 
 @Repository
@@ -16,4 +19,7 @@ public interface PermissionsRepository extends JpaRepository<UserGroupPermission
     List<UserGroupPermission> findBy_user(User user);
 
     List<UserGroupPermission> findBy_group(Group group);
+
+    @Query("SELECT u FROM UserGroupPermission u WHERE u._user.id = ?1 AND u._group.id = ?2 AND u.permission = ?3")
+    Optional<UserGroupPermission> findPermission(String userId, String groupId, Permission permission);
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
index 0dba6d8..6481b8d 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
@@ -2,6 +2,7 @@ package it.inaf.ia2.gms.rap;
 
 import it.inaf.ia2.gms.authn.SessionData;
 import it.inaf.ia2.gms.model.RapUser;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -29,11 +30,26 @@ public class RapClient {
 
     public List<RapUser> getUsers(Set<String> identifiers) {
 
+        if (identifiers.isEmpty()) {
+            return new ArrayList<>();
+        }
+
         String url = rapBaseUrl + "/user?identifiers=" + String.join(",", identifiers);
         return rapRestTemplate.exchange(url, HttpMethod.GET, getEntity(), new ParameterizedTypeReference<List<RapUser>>() {
         }).getBody();
     }
 
+    public List<RapUser> searchUsers(String searchText) {
+
+        if (searchText == null || searchText.trim().isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        String url = rapBaseUrl + "/user?search=" + searchText;
+        return rapRestTemplate.exchange(url, HttpMethod.GET, getEntity(), new ParameterizedTypeReference<List<RapUser>>() {
+        }).getBody();
+    }
+
     private HttpEntity<?> getEntity() {
         return getEntity(null);
     }
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 7f7d3e3..1a8d643 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
@@ -1,5 +1,7 @@
 package it.inaf.ia2.gms.service;
 
+import it.inaf.ia2.gms.exception.BadRequestException;
+import it.inaf.ia2.gms.model.Permission;
 import it.inaf.ia2.gms.model.RapUser;
 import it.inaf.ia2.gms.model.UserPermission;
 import it.inaf.ia2.gms.persistence.PermissionsRepository;
@@ -18,11 +20,14 @@ import org.springframework.stereotype.Service;
 @Service
 public class PermissionsService {
 
-    @Autowired
-    private PermissionsRepository permissionsRepository;
+    private final PermissionsRepository permissionsRepository;
+    private final RapClient rapClient;
 
     @Autowired
-    private RapClient rapClient;
+    public PermissionsService(PermissionsRepository permissionsRepository, RapClient rapClient) {
+        this.permissionsRepository = permissionsRepository;
+        this.rapClient = rapClient;
+    }
 
     public List<UserPermission> getUserPermissions(Group group) {
 
@@ -49,4 +54,13 @@ public class PermissionsService {
 
         return result;
     }
+
+    public void deletePermission(String userId, String groupId, Permission permission) {
+
+        UserGroupPermission ugp = permissionsRepository.findPermission(userId, groupId, permission)
+                .orElseThrow(() -> new BadRequestException("Permission not found (UserId="
+                + userId + ", GroupId=" + groupId + ", Permission=" + permission + ")"));
+
+        permissionsRepository.delete(ugp);
+    }
 }
diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java
new file mode 100644
index 0000000..99e6479
--- /dev/null
+++ b/gms/src/test/java/it/inaf/ia2/gms/service/PermissionsServiceIntegrationTest.java
@@ -0,0 +1,79 @@
+package it.inaf.ia2.gms.service;
+
+import it.inaf.ia2.gms.model.Permission;
+import it.inaf.ia2.gms.model.RapUser;
+import it.inaf.ia2.gms.model.UserPermission;
+import it.inaf.ia2.gms.persistence.GroupsRepository;
+import it.inaf.ia2.gms.persistence.PermissionsRepository;
+import it.inaf.ia2.gms.persistence.UsersRepository;
+import it.inaf.ia2.gms.persistence.model.Group;
+import it.inaf.ia2.gms.persistence.model.User;
+import it.inaf.ia2.gms.persistence.model.UserGroupPermission;
+import it.inaf.ia2.gms.rap.RapClient;
+import java.util.Collections;
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@DataJpaTest
+@AutoConfigureTestDatabase
+@RunWith(SpringRunner.class)
+public class PermissionsServiceIntegrationTest {
+
+    private static final String USER_ID = "USER_ID";
+
+    @Autowired
+    private UsersRepository usersRepository;
+
+    @Autowired
+    private GroupsRepository groupsRepository;
+
+    @Autowired
+    private PermissionsRepository permissionsRepository;
+
+    @MockBean
+    private RapClient rapClient;
+
+    @Test
+    public void permissionsRetrievalTest() {
+
+        // Mock RAP client
+        RapUser rapUser = new RapUser();
+        rapUser.setId(USER_ID);
+        when(rapClient.getUsers(any())).thenReturn(Collections.singletonList(rapUser));
+
+        PermissionsService permissionsService = new PermissionsService(permissionsRepository, rapClient);
+
+        // Create user
+        User user = new User();
+        user.setId(USER_ID);
+        user = usersRepository.save(user);
+
+        Group root = new Group();
+        root.setId(GroupsService.ROOT);
+        root.setName(GroupsService.ROOT);
+        root = groupsRepository.save(root);
+
+        UserGroupPermission superAdminPermission = new UserGroupPermission();
+        superAdminPermission.setUser(user);
+        superAdminPermission.setGroup(root);
+        superAdminPermission.setPermission(Permission.ADMIN);
+        permissionsRepository.save(superAdminPermission);
+
+        List<UserPermission> permissions = permissionsService.getUserPermissions(root);
+
+        assertEquals(1, permissions.size());
+        assertEquals(Permission.ADMIN, permissions.get(0).getPermission());
+        assertEquals(USER_ID, permissions.get(0).getUser().getId());
+
+        permissionsService.deletePermission(USER_ID, GroupsService.ROOT, Permission.ADMIN);
+    }
+}
-- 
GitLab