diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js
index b73e02220319d8c4a7689bfa9cad65261831d35d..631e6bebc48cf698f43f064c1955abd991b88e5e 100644
--- a/gms-ui/src/api/server/index.js
+++ b/gms-ui/src/api/server/index.js
@@ -54,7 +54,7 @@ function loading(value) {
 export default {
   fetchHomePageModel(input) {
     let url = BASE_API_URL +
-      'home?groupId=' + input.selectedGroupId +
+      'ui/home?groupId=' + input.selectedGroupId +
       '&paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage;
     return apiRequest({
@@ -70,7 +70,7 @@ export default {
   },
   fetchGroupsTab(input) {
     let url = BASE_API_URL +
-      'groups?groupId=' + input.selectedGroupId +
+      'ui/groups?groupId=' + input.selectedGroupId +
       '&paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage +
       '&onlyPanel=false';
@@ -90,7 +90,7 @@ export default {
   },
   fetchGroupsPanel(input) {
     let url = BASE_API_URL +
-      'groups?groupId=' + input.selectedGroupId +
+      'ui/groups?groupId=' + input.selectedGroupId +
       '&paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage +
       '&onlyPanel=true';
@@ -110,7 +110,7 @@ export default {
   },
   fetchMembersPanel(input) {
     let url = BASE_API_URL +
-      'members?groupId=' + input.selectedGroupId +
+      'ui/members?groupId=' + input.selectedGroupId +
       '&paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage;
     return apiRequest({
@@ -126,7 +126,7 @@ export default {
   },
   fetchPermissionsPanel(input) {
     let url = BASE_API_URL +
-      'permissions?groupId=' + input.selectedGroupId +
+      'ui/permissions?groupId=' + input.selectedGroupId +
       '&paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage;
     return apiRequest({
@@ -141,7 +141,7 @@ export default {
     });
   },
   addGroup(newGroupName, leaf, input) {
-    let url = BASE_API_URL + 'group';
+    let url = BASE_API_URL + 'ui/group';
     return apiRequest({
       method: 'POST',
       url: url,
@@ -162,7 +162,7 @@ export default {
     }, true, true);
   },
   updateGroup(groupId, newGroupName, leaf, input) {
-    let url = BASE_API_URL + 'group/' + groupId;
+    let url = BASE_API_URL + 'ui/group/' + groupId;
     return apiRequest({
       method: 'PUT',
       url: url,
@@ -182,7 +182,7 @@ export default {
     }, true, true);
   },
   removeGroup(groupId, input) {
-    let url = BASE_API_URL + 'group/' + groupId +
+    let url = BASE_API_URL + 'ui/group/' + groupId +
       '?paginatorPageSize=' + input.paginatorPageSize +
       '&paginatorPage=' + input.paginatorPage;
     if (input.searchFilter !== null) {
@@ -200,7 +200,7 @@ export default {
     });
   },
   searchUser(searchInput) {
-    let url = BASE_API_URL + 'users?search=' + searchInput;
+    let url = BASE_API_URL + 'ui/users?search=' + searchInput;
 
     return apiRequest({
       method: 'GET',
@@ -213,7 +213,7 @@ export default {
     });
   },
   addPermission(userId, permission, input, override) {
-    let url = BASE_API_URL + 'permission';
+    let url = BASE_API_URL + 'ui/permission';
 
     return apiRequest({
       method: 'POST',
@@ -235,7 +235,7 @@ export default {
     });
   },
   updatePermission(groupId, userId, permission) {
-    let url = BASE_API_URL + 'permission';
+    let url = BASE_API_URL + 'ui/permission';
 
     return apiRequest({
       method: 'PUT',
@@ -254,7 +254,7 @@ export default {
     });
   },
   getPermission(groupId, userId) {
-    let url = BASE_API_URL + 'permission?groupId=' + groupId + '&userId=' + userId;
+    let url = BASE_API_URL + 'ui/permission?groupId=' + groupId + '&userId=' + userId;
 
     return apiRequest({
       method: 'GET',
@@ -268,7 +268,7 @@ export default {
     });
   },
   addMember(userId, permission, input) {
-    let url = BASE_API_URL + 'member';
+    let url = BASE_API_URL + 'ui/member';
 
     return apiRequest({
       method: 'POST',
@@ -289,7 +289,7 @@ export default {
     });
   },
   removeMember(userId, removeAlsoPermission, input) {
-    let url = BASE_API_URL + 'member' +
+    let url = BASE_API_URL + 'ui/member' +
       '?groupId=' + input.selectedGroupId +
       '&userId=' + userId +
       '&removeAlsoPermission=' + removeAlsoPermission +
@@ -307,7 +307,7 @@ export default {
     });
   },
   removePermission(userId, input) {
-    let url = BASE_API_URL + 'permission' +
+    let url = BASE_API_URL + 'ui/permission' +
       '?groupId=' + input.selectedGroupId +
       '&userId=' + userId +
       '&paginatorPageSize=' + input.paginatorPageSize +
@@ -324,7 +324,7 @@ export default {
     });
   },
   search(input) {
-    let url = BASE_API_URL + 'search?query=' + input.genericSearch.filter +
+    let url = BASE_API_URL + 'ui/search?query=' + input.genericSearch.filter +
       '&paginatorPage=' + input.genericSearch.paginatorPage +
       '&paginatorPageSize=' + input.genericSearch.paginatorPageSize +
       '&users=' + input.genericSearch.users + "&groups=" + input.genericSearch.groups;
@@ -341,7 +341,7 @@ export default {
     });
   },
   openUserSearchResult(userId) {
-    let url = BASE_API_URL + 'search/user/' + userId;
+    let url = BASE_API_URL + 'ui/search/user/' + userId;
 
     return apiRequest({
       method: 'GET',
@@ -355,7 +355,7 @@ export default {
     });
   },
   keepAlive() {
-    let url = BASE_API_URL + 'keepAlive';
+    let url = BASE_API_URL + 'ui/keepAlive';
 
     return apiRequest({
       method: 'GET',
@@ -367,7 +367,7 @@ export default {
     }, false);
   },
   deleteInvitedRegistration(requestId, groupId) {
-    let url = BASE_API_URL + 'registration?' +
+    let url = BASE_API_URL + 'ui/registration?' +
       'request_id=' + requestId + '&group_id=' + groupId;
     return apiRequest({
       method: 'DELETE',
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 c4ec0fb90d7602f8ee0d61290cfa4eda35265285..a5f810db342e2fa11f810aa34ab341cc73729cf4 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
@@ -65,7 +65,7 @@ public class GroupsController {
     @Autowired
     protected GroupNameService groupNameService;
 
-    @GetMapping(value = "/groups", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/groups", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<?> getGroupsTab(@Valid GroupsRequest request) {
         if (request.isOnlyPanel()) {
             // Only groupsPanel
@@ -77,7 +77,7 @@ public class GroupsController {
         }
     }
 
-    @PostMapping(value = "/group", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @PostMapping(value = "/ui/group", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<GroupNode>> createGroup(@Valid @RequestBody AddGroupRequest request) {
 
         GroupEntity parent = groupsManager.getGroupById(request.getParentGroupId());
@@ -89,7 +89,7 @@ public class GroupsController {
         return new ResponseEntity<>(groupsPanel, HttpStatus.CREATED);
     }
 
-    @PutMapping(value = "/group/{groupId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @PutMapping(value = "/ui/group/{groupId}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<GroupNode>> updateGroup(@PathVariable("groupId") String groupId, @Valid @RequestBody RenameGroupRequest request) {
 
         GroupEntity updatedGroup = groupsManager.updateGroup(groupId, request.getNewGroupName(), request.isLeaf());
@@ -101,8 +101,8 @@ public class GroupsController {
         return ResponseEntity.ok(groupsPanel);
     }
 
-    @DeleteMapping(value = "/group/{groupId}", produces = MediaType.APPLICATION_JSON_VALUE)
-    public ResponseEntity<?> deleteGroup(@PathVariable("groupId") String groupId, DeleteGroupRequest request) {
+    @DeleteMapping(value = "/ui/group/{groupId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    public ResponseEntity<PaginatedData<GroupNode>> deleteGroup(@PathVariable("groupId") String groupId, DeleteGroupRequest request) {
 
         GroupEntity parent = groupsManager.deleteGroup(groupId);
         PaginatedData<GroupNode> groupsPanel = getGroupsPanel(parent, request);
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 b005156b253fe19f18490ecc1c14571731ebd78b..d9f18f65b0b612d9c8dc5bc2b0e7a5c051bf7224 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
@@ -41,7 +41,7 @@ public class HomePageController {
     private InvitedRegistrationManager invitedRegistrationManager;
 
     @ResponseBody
-    @GetMapping(value = "/home", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/home", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<HomePageResponse> getMainPage(@Valid GroupsRequest request) {
 
         HomePageResponse response = new HomePageResponse();
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/InvitedRegistrationController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/InvitedRegistrationController.java
index e5c2a18472ab425d1726386a7a579366958973b5..627a52b7b24ad9f76818450c0825a9f8c7728674 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/InvitedRegistrationController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/InvitedRegistrationController.java
@@ -59,7 +59,7 @@ public class InvitedRegistrationController {
         }
     }
 
-    @DeleteMapping(value = "/registration", produces = MediaType.APPLICATION_JSON_VALUE)
+    @DeleteMapping(value = "/ui/registration", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<?> deleteInvitedRegistration(@RequestParam("request_id") String requestId, @RequestParam("group_id") String groupId) {
         invitedRegistrationManager.deleteInvitedRegistration(requestId, groupId);
         return ResponseEntity.noContent().build();
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java
index 5e4b15fde34b823c852ad66b1523fe4d1873fda4..5d29638009d3bbed53fcbeca655c58becb8642c3 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/KeepAliveController.java
@@ -27,7 +27,7 @@ public class KeepAliveController {
         userManager = ServiceLocator.getInstance().getUserManager();
     }
 
-    @GetMapping(value = "/keepAlive", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/keepAlive", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<?> keepAlive(HttpServletRequest request) {
         LOG.trace("Keepalive called");
         if (sessionData.getExpiresIn() < 60) {
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java
index def7eed25602a9711feb52f7eb1fcf770859580a..b29e6d9edda6bbe59d437b74a3d25c3f876b3e7c 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/MembersController.java
@@ -41,7 +41,7 @@ public class MembersController {
     @Autowired
     protected GroupNameService groupNameService;
 
-    @GetMapping(value = "/members", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/members", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUser>> getMembersTab(TabRequest request) {
 
         GroupEntity group = groupsService.getGroupById(request.getGroupId());
@@ -51,7 +51,7 @@ public class MembersController {
         return ResponseEntity.ok(membersPanel);
     }
 
-    @PostMapping(value = "/member", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @PostMapping(value = "/ui/member", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUser>> addMember(@Valid @RequestBody AddMemberRequest request) {
 
         GroupEntity group = groupsService.getGroupById(request.getGroupId());
@@ -62,7 +62,7 @@ public class MembersController {
         return new ResponseEntity<>(getMembersPanel(group, request), HttpStatus.CREATED);
     }
 
-    @DeleteMapping(value = "/member", produces = MediaType.APPLICATION_JSON_VALUE)
+    @DeleteMapping(value = "/ui/member", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUser>> removeMember(@Valid RemoveMemberRequest request) {
 
         GroupEntity group = groupsService.getGroupById(request.getGroupId());
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 80942eab71327620319b3d6f0b19790b16b132bd..68fe4899df63978708b036e86728a0540230b2d3 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
@@ -31,7 +31,6 @@ import org.springframework.http.MediaType;
 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.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -53,7 +52,7 @@ public class PermissionsController {
     @Autowired
     private SearchService searchService;
 
-    @GetMapping(value = "/permissions", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/permissions", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUserPermission>> getPermissionsTab(TabRequest request) {
 
         GroupEntity group = groupsManager.getGroupById(request.getGroupId());
@@ -62,7 +61,7 @@ public class PermissionsController {
         return ResponseEntity.ok(permissionsPanel);
     }
 
-    @GetMapping(value = "/permission", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/permission", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<Map<String, Permission>> getUserPermission(@RequestParam("groupId") String groupId, @RequestParam("userId") String userId) {
 
         GroupEntity group = groupsManager.getGroupById(groupId);
@@ -77,7 +76,7 @@ public class PermissionsController {
         }
     }
 
-    @PostMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @PostMapping(value = "/ui/permission", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUserPermission>> addPermission(@Valid @RequestBody AddPermissionRequest request) {
 
         GroupEntity group = groupsManager.getGroupById(request.getGroupId());
@@ -90,7 +89,7 @@ public class PermissionsController {
         return new ResponseEntity<>(getPermissionsPanel(group, request), HttpStatus.CREATED);
     }
 
-    @PutMapping(value = "/permission", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+    @PutMapping(value = "/ui/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());
@@ -102,7 +101,7 @@ public class PermissionsController {
         return ResponseEntity.ok(response);
     }
 
-    @DeleteMapping(value = "/permission", produces = MediaType.APPLICATION_JSON_VALUE)
+    @DeleteMapping(value = "/ui/permission", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<RapUserPermission>> deletePermission(@Valid MemberRequest request) {
 
         GroupEntity group = groupsManager.getGroupById(request.getGroupId());
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java
index 20c18fc56c5be28c617b3f937368204ede895dc2..6cf51037c8f557497b55655e10bec94e5828168a 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/SearchController.java
@@ -23,13 +23,13 @@ public class SearchController {
     @Autowired
     private SearchService searchService;
 
-    @GetMapping(value = "/search", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/search", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<PaginatedData<SearchResponseItem>> getSearchResults(@Valid GenericSearchRequest searchRequest) {
         PaginatedData<SearchResponseItem> response = searchService.search(searchRequest, servletRequest.getUserPrincipal().getName());
         return ResponseEntity.ok(response);
     }
 
-    @GetMapping(value = "/search/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/search/user/{userId}", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<UserSearchResponse> getSearchResultUser(@PathVariable("userId") String userId) {
 
         UserSearchResponse response = searchService.getUserSearchResult(servletRequest.getUserPrincipal().getName(), userId);
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 082a4f324da627f7fe1e5e7d886f16ff4feb2560..34bdc094e62ef6cc5a897e3ed07a5cbf2dae5a95 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", produces = MediaType.APPLICATION_JSON_VALUE)
+    @GetMapping(value = "/ui/users", produces = MediaType.APPLICATION_JSON_VALUE)
     public ResponseEntity<List<RapUser>> searchUsers(@RequestParam("search") String searchText) {
         return ResponseEntity.ok(rapClient.getUsers(searchText));
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ClientEntity.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ClientEntity.java
deleted file mode 100644
index eb00aa98d8c87699dae2179af96e4e7ca965f3a7..0000000000000000000000000000000000000000
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/model/ClientEntity.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package it.inaf.ia2.gms.persistence.model;
-
-import java.util.List;
-
-public class ClientEntity {
-
-    private String id;
-    private String secret;
-    private List<String> allowedActions;
-    private String ipFilter;
-
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    public String getSecret() {
-        return secret;
-    }
-
-    public void setSecret(String secret) {
-        this.secret = secret;
-    }
-
-    public List<String> getAllowedActions() {
-        return allowedActions;
-    }
-
-    public void setAllowedActions(List<String> allowedActions) {
-        this.allowedActions = allowedActions;
-    }
-
-    public String getIpFilter() {
-        return ipFilter;
-    }
-
-    public void setIpFilter(String ipFilter) {
-        this.ipFilter = ipFilter;
-    }
-}
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/ControllersMockData.java b/gms/src/test/java/it/inaf/ia2/gms/controller/ControllersMockData.java
index 020129770811edee9226d05ed467648c7e028770..7f31a881fd150d9600a41ebbaca044626e249757 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/ControllersMockData.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/ControllersMockData.java
@@ -31,6 +31,14 @@ public class ControllersMockData {
         return inaf;
     }
 
+    public static GroupEntity getInafProgramGroup() {
+        GroupEntity inafProgram = new GroupEntity();
+        inafProgram.setId("inaf_p1_id");
+        inafProgram.setName("P1");
+        inafProgram.setPath("lbt_id.inaf_id.inaf_p1_id");
+        return inafProgram;
+    }
+
     public static GroupEntity getPeopleGroup() {
         GroupEntity lbt = new GroupEntity();
         lbt.setId("people_id");
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java
index 29afb8a439be6d2ed27c7e787bf2011ff74dfc06..892367e188ae2547374e5e2611a34071b14f7275 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java
@@ -17,15 +17,20 @@ import it.inaf.ia2.gms.service.GroupNameService;
 import it.inaf.ia2.gms.service.GroupsService;
 import it.inaf.ia2.gms.service.GroupsTreeBuilder;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertEquals;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import org.mockito.InjectMocks;
@@ -86,7 +91,7 @@ public class GroupsControllerTest {
     }
 
     @Test
-    public void testGetGroups() throws Exception {
+    public void testGetGroupsPaginated() throws Exception {
 
         GroupsTabResponse response = new GroupsTabResponse();
         response.setBreadcrumbs(new ArrayList<>());
@@ -95,7 +100,7 @@ public class GroupsControllerTest {
 
         when(groupsTabResponseBuilder.getGroupsTab(any())).thenReturn(response);
 
-        mockMvc.perform(get("/groups?groupId=ROOT&paginatorPageSize=20&paginatorPage=1"))
+        mockMvc.perform(get("/ui/groups?groupId=ROOT&paginatorPageSize=20&paginatorPage=1"))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.breadcrumbs", notNullValue()))
                 .andExpect(jsonPath("$.groupsPanel", notNullValue()))
@@ -116,7 +121,7 @@ public class GroupsControllerTest {
         PaginatedData<GroupNode> paginatedData = new PaginatedData<>(nodes, 1, 10);
         when(groupsTreeBuilder.listSubGroups(any(), any(), any())).thenReturn(paginatedData);
 
-        mockMvc.perform(post("/group")
+        mockMvc.perform(post("/ui/group")
                 .content(mapper.writeValueAsString(request))
                 .accept(MediaType.APPLICATION_JSON)
                 .contentType(MediaType.APPLICATION_JSON))
@@ -126,7 +131,7 @@ public class GroupsControllerTest {
     @Test
     public void testDeleteGroupPaginated() throws Exception {
 
-        mockMvc.perform(delete("/group/id?paginatorPageSize=20&paginatorPage=1&searchFilter="))
+        mockMvc.perform(delete("/ui/group/id?paginatorPageSize=20&paginatorPage=1&searchFilter="))
                 .andDo(print())
                 .andExpect(status().isOk());
 
@@ -134,7 +139,7 @@ public class GroupsControllerTest {
     }
 
     @Test
-    public void testAddGroupWs() throws Exception {
+    public void testAddGroup() throws Exception {
 
         GroupEntity peopleGroup = getPeopleGroup();
 
@@ -155,7 +160,7 @@ public class GroupsControllerTest {
     }
 
     @Test
-    public void testDeleteGroupWs() throws Exception {
+    public void testDeleteGroup() throws Exception {
 
         GroupEntity inafGroup = getInafGroup();
 
@@ -168,6 +173,29 @@ public class GroupsControllerTest {
         verify(groupsManager, times(1)).deleteGroup(eq(inafGroup.getId()));
     }
 
+    @Test
+    public void testListChildGroups() throws Exception {
+
+        GroupEntity lbtGroup = getLbtGroup();
+        GroupEntity inafGroup = getInafGroup();
+        GroupEntity inafProgramGroup = getInafProgramGroup();
+
+        when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbtGroup));
+        when(groupsDAO.findGroupByParentAndName(lbtGroup.getPath(), "INAF")).thenReturn(Optional.of(inafGroup));
+
+        Map<String, String> namesMap = Map.of(inafProgramGroup.getId(), "LBT.INAF.P1");
+        when(groupsDAO.getGroupCompleteNamesFromId(Set.of(inafProgramGroup.getId()))).thenReturn(namesMap);
+
+        when(groupsManager.getChildGroups(eq(inafGroup), anyBoolean())).thenReturn(Arrays.asList(inafProgramGroup));
+
+        String response = mockMvc.perform(get("/groups?parent=LBT.INAF&recursive=false")
+                .accept(MediaType.TEXT_PLAIN))
+                .andExpect(status().isOk())
+                .andReturn().getResponse().getContentAsString();
+
+        assertEquals("P1\n", response);
+    }
+
     private GroupEntity argGroupIdEq(String groupId) {
         return argThat(g -> g.getId().equals(groupId));
     }
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/HomePageControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/HomePageControllerTest.java
index 2cf8b3a6a2940445682d6da03419db86d0273078..04d322e07b6833631a624a63da173c83f6e7c72a 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/HomePageControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/HomePageControllerTest.java
@@ -43,7 +43,7 @@ public class HomePageControllerTest {
 
         when(groupsTabResponseBuilder.getGroupsTab(any())).thenReturn(new GroupsTabResponse());
 
-        mockMvc.perform(get("/home?groupId=ROOT&paginatorPageSize=20&paginatorPage=1"))
+        mockMvc.perform(get("/ui/home?groupId=ROOT&paginatorPageSize=20&paginatorPage=1"))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.user", notNullValue()));
     }
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/InvitedRegistrationControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/InvitedRegistrationControllerTest.java
index c6c1c2e5a295afe822145b7f7f2c85e22a72a566..f125960682ee57eaf2197fa060296f88cc45a84f 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/InvitedRegistrationControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/InvitedRegistrationControllerTest.java
@@ -35,7 +35,7 @@ public class InvitedRegistrationControllerTest {
     @Test
     public void testDeleteInvitedRegistration() throws Exception {
 
-        mockMvc.perform(delete("/registration?request_id=req1&group_id=group1"))
+        mockMvc.perform(delete("/ui/registration?request_id=req1&group_id=group1"))
                 .andDo(print())
                 .andExpect(status().isNoContent());
 
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/MembersControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/MembersControllerTest.java
index 4d7c75fb91902f52630e5c7e0930233600c37121..ae69b825282b986fe6e59ea1251c924318d279a9 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/MembersControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/MembersControllerTest.java
@@ -70,7 +70,7 @@ public class MembersControllerTest {
     }
 
     @Test
-    public void testAddMember() throws Exception {
+    public void testAddMemberPaginated() throws Exception {
 
         AddMemberRequest request = new AddMemberRequest();
         request.setGroupId("lbt_id");
@@ -79,7 +79,7 @@ public class MembersControllerTest {
         request.setPaginatorPageSize(10);
         request.setPermission(Permission.VIEW_MEMBERS);
 
-        mockMvc.perform(post("/member")
+        mockMvc.perform(post("/ui/member")
                 .content(mapper.writeValueAsString(request))
                 .contentType(MediaType.APPLICATION_JSON_VALUE))
                 .andExpect(status().isCreated())
@@ -89,9 +89,9 @@ public class MembersControllerTest {
     }
 
     @Test
-    public void testRemoveMember() throws Exception {
+    public void testRemoveMemberPaginated() throws Exception {
 
-        mockMvc.perform(delete("/member?groupId=group_id&userId=user_id&paginatorPage=1&paginatorPageSize=10"))
+        mockMvc.perform(delete("/ui/member?groupId=group_id&userId=user_id&paginatorPage=1&paginatorPageSize=10"))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.currentPage", is(1)));
 
@@ -99,7 +99,7 @@ public class MembersControllerTest {
     }
 
     @Test
-    public void testAddMemberWs() throws Exception {
+    public void testAddMember() throws Exception {
 
         when(groupsDAO.findGroupByParentAndName("", "LBT")).thenReturn(Optional.of(lbt));
         when(groupsDAO.findGroupByParentAndName("lbt_id", "INAF")).thenReturn(Optional.of(inaf));
@@ -121,7 +121,7 @@ public class MembersControllerTest {
     }
 
     @Test
-    public void testRemoveMemberWs() throws Exception {
+    public void testRemoveMember() throws Exception {
 
         mockMvc.perform(delete("/membership?group=LBT.INAF&user_id=userId"))
                 .andExpect(status().isNoContent());
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java
index d4b8f9fdb5e89a3aba8f78b73759c3661f2ff850..8b41c2037542e8116ed75a102a982e45c4280813 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/PermissionsControllerTest.java
@@ -75,7 +75,7 @@ public class PermissionsControllerTest {
 
         when(permissionsManager.getDirectUserPermission(eq(group), eq("user_id"))).thenReturn(Permission.MANAGE_MEMBERS);
 
-        mockMvc.perform(get("/permission?groupId=group_id&userId=user_id")
+        mockMvc.perform(get("/ui/permission?groupId=group_id&userId=user_id")
                 .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.permission", is("MANAGE_MEMBERS")));
@@ -84,13 +84,13 @@ public class PermissionsControllerTest {
     @Test
     public void testGetInexistentPermission() throws Exception {
 
-        mockMvc.perform(get("/permission?groupId=group_id&userId=user_id")
+        mockMvc.perform(get("/ui/permission?groupId=group_id&userId=user_id")
                 .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().isNoContent());
     }
 
     @Test
-    public void testAddPermission() throws Exception {
+    public void testAddPermissionPaginated() throws Exception {
 
         AddPermissionRequest request = new AddPermissionRequest();
         request.setGroupId("group_id");
@@ -99,7 +99,7 @@ public class PermissionsControllerTest {
         request.setPaginatorPage(1);
         request.setPaginatorPageSize(10);
 
-        mockMvc.perform(post("/permission")
+        mockMvc.perform(post("/ui/permission")
                 .content(mapper.writeValueAsString(request))
                 .contentType(MediaType.APPLICATION_JSON))
                 .andExpect(status().isCreated())
@@ -109,9 +109,9 @@ public class PermissionsControllerTest {
     }
 
     @Test
-    public void testRemovePermission() throws Exception {
+    public void testRemovePermissionPaginated() throws Exception {
 
-        mockMvc.perform(delete("/permission?groupId=group_id&userId=user_id&paginatorPage=1&paginatorPageSize=10"))
+        mockMvc.perform(delete("/ui/permission?groupId=group_id&userId=user_id&paginatorPage=1&paginatorPageSize=10"))
                 .andExpect(status().isOk())
                 .andExpect(jsonPath("$.currentPage", is(1)));
 
@@ -138,7 +138,7 @@ public class PermissionsControllerTest {
     }
 
     @Test
-    public void testAddPermissionWs() throws Exception {
+    public void testAddPermission() throws Exception {
 
         String userId = "target_user";
         Permission permission = Permission.ADMIN;
@@ -171,7 +171,7 @@ public class PermissionsControllerTest {
     }
 
     @Test
-    public void testRemovePermissionWs() throws Exception {
+    public void testRemovePermission() throws Exception {
 
         GroupEntity lbt = getLbtGroup();
         GroupEntity inaf = getInafGroup();
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java
index 4c98a67d48a48ac6b28df19edb294eb5a055a021..025652f9919b45cfcf07204c51d2b2a8a118ff82 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/SearchControllerTest.java
@@ -58,7 +58,7 @@ public class SearchControllerTest {
 
         when(searchService.search(any(), any())).thenReturn(response);
 
-        mockMvc.perform(get("/search?query=searchText&paginatorPage=1&paginatorPageSize=10&groups=false")
+        mockMvc.perform(get("/ui/search?query=searchText&paginatorPage=1&paginatorPageSize=10&groups=false")
                 .contentType(MediaType.APPLICATION_JSON_VALUE))
                 .andExpect(status().isOk());
 
@@ -77,7 +77,7 @@ public class SearchControllerTest {
 
         when(searchService.getUserSearchResult(any(), any())).thenReturn(new UserSearchResponse());
 
-        mockMvc.perform(get("/search/user/user_id")
+        mockMvc.perform(get("/ui/search/user/user_id")
                 .contentType(MediaType.APPLICATION_JSON_VALUE))
                 .andExpect(status().isOk());
 
diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/GroupsServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/GroupsServiceTest.java
index 4cf460fe7393931b7256e0f26927ce537164953a..3594238711ada560c864602ee6bc440adcb434ed 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/service/GroupsServiceTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/service/GroupsServiceTest.java
@@ -1,27 +1,35 @@
 package it.inaf.ia2.gms.service;
 
+import static it.inaf.ia2.gms.controller.ControllersMockData.*;
+import it.inaf.ia2.gms.exception.UnauthorizedException;
 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;
 import it.inaf.ia2.gms.persistence.model.GroupEntity;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 import org.mockito.junit.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class GroupsServiceTest {
-
+    
     @Mock
     private GroupsDAO groupsDAO;
     @Mock
@@ -32,35 +40,85 @@ public class GroupsServiceTest {
     private InvitedRegistrationDAO invitedRegistrationDAO;
     @Mock
     private LoggingDAO loggingDAO;
-
+    
     @InjectMocks
     private GroupsService groupsService;
-
+    
     private final GroupEntity root = group("ROOT", "ROOT", "");
-
+    
     @Test
     public void testAddGroup() {
-
+        
         GroupEntity group1 = groupsService.addGroup(root, "group1", false, "creator_id");
-
+        
         verify(groupsDAO, times(1)).createGroup(argThat(g -> "group1".equals(g.getName())));
-
+        
         assertNotNull(group1.getId());
         assertEquals("group1", group1.getName());
         assertFalse(group1.isLeaf());
         assertEquals("creator_id", group1.getCreatedBy());
         assertNotNull(group1.getCreationTime());
-
+        
         GroupEntity group2 = groupsService.addGroup(group1, "group2", true, "creator_id");
-
+        
         verify(groupsDAO, times(1)).createGroup(argThat(g -> "group2".equals(g.getName())));
-
+        
         assertNotNull(group2.getId());
         assertEquals("group2", group2.getName());
         assertTrue(group2.isLeaf());
         assertEquals(group1.getId() + "." + group2.getId(), group2.getPath());
     }
-
+    
+    @Test
+    public void testGetChildGroups() {
+        groupsService.getChildGroups(getLbtGroup(), false);
+        verify(groupsDAO, times(1)).getDirectSubGroups(getLbtGroup().getPath());
+    }
+    
+    @Test
+    public void testGetChildGroupsRecursive() {
+        groupsService.getChildGroups(getLbtGroup(), true);
+        verify(groupsDAO, times(1)).getAllChildren(getLbtGroup().getPath());
+    }
+    
+    @Test
+    public void testDeleteRootIsDenied() {
+        boolean exception = false;
+        try {
+            groupsService.deleteGroup(root);
+        } catch (UnauthorizedException ex) {
+            exception = true;
+        }
+        assertTrue(exception);
+    }
+    
+    @Test
+    public void testDeleteLockedGroupIsDenied() {
+        GroupEntity group = getLbtGroup();
+        group.setLocked(true);
+        boolean exception = false;
+        try {
+            groupsService.deleteGroup(group);
+        } catch (UnauthorizedException ex) {
+            exception = true;
+        }
+        assertTrue(exception);
+    }
+    
+    @Test
+    public void testDeleteGroup() {
+        
+        GroupEntity lbt = getLbtGroup();
+        GroupEntity inaf = getInafGroup();
+        
+        when(groupsDAO.findGroupByPath(lbt.getPath())).thenReturn(Optional.of(lbt));
+        when(groupsDAO.getAllChildren(inaf.getPath())).thenReturn(new ArrayList<>(List.of(getInafProgramGroup())));
+        
+        groupsService.deleteGroup(inaf);
+        
+        verify(groupsDAO, times(2)).deleteGroup(any());
+    }
+    
     private GroupEntity group(String id, String name, String path) {
         GroupEntity group = new GroupEntity();
         group.setId(id);