From 1b2c3fc0a3630b2e2ed0449243d0e195f556375d Mon Sep 17 00:00:00 2001
From: Sebastian-Ion TINCU <Sebastian-Ion.TINCU@ext.ec.europa.eu>
Date: Thu, 29 Feb 2024 12:12:54 +0100
Subject: [PATCH] EDELIVERY-11816 Resources Search Filter Does Not Support
 Colon Caracter

Add annotation to decode URI-encoded filter values.
---
 smp-webapp/pom.xml                            | 12 +++
 .../edelivery/smp/config/SMPWebAppConfig.java | 12 ++-
 .../ec/edelivery/smp/filter/Filter.java       | 34 +++++++++
 .../edelivery/smp/filter/FilterHandler.java   | 74 +++++++++++++++++++
 .../smp/ui/edit/DomainEditController.java     |  3 +-
 .../smp/ui/edit/GroupEditController.java      |  3 +-
 .../smp/ui/edit/ResourceEditController.java   |  5 +-
 .../smp/ui/external/UserController.java       |  3 +-
 .../smp/ui/internal/UserAdminController.java  |  3 +-
 .../smp/ui/edit/ResourceEditControllerIT.java | 38 +++++++++-
 10 files changed, 178 insertions(+), 9 deletions(-)
 create mode 100644 smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/Filter.java
 create mode 100644 smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java

diff --git a/smp-webapp/pom.xml b/smp-webapp/pom.xml
index 976fefb17..a89c39ba0 100644
--- a/smp-webapp/pom.xml
+++ b/smp-webapp/pom.xml
@@ -125,6 +125,18 @@
             <artifactId>mysql-connector-j</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SMPWebAppConfig.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SMPWebAppConfig.java
index c5bbaedab..4dd672e7f 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SMPWebAppConfig.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SMPWebAppConfig.java
@@ -23,12 +23,14 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.databind.json.JsonMapper;
+import eu.europa.ec.edelivery.smp.filter.FilterHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.FilterType;
 import org.springframework.context.annotation.PropertySource;
+import org.springframework.format.FormatterRegistry;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.web.servlet.config.annotation.*;
@@ -92,6 +94,7 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
                 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SMPWebAppConfig.class)})
 @PropertySource(value = "classpath:/application.properties", ignoreResourceNotFound = true)
 public class SMPWebAppConfig implements WebMvcConfigurer {
+
     private static final Logger LOG = LoggerFactory.getLogger(SMPWebAppConfig.class);
 
     @Override
@@ -103,7 +106,6 @@ public class SMPWebAppConfig implements WebMvcConfigurer {
 
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
-
         registry.setOrder(HIGHEST_PRECEDENCE)
                 .addResourceHandler("/index.html", "/favicon.png", "/favicon.ico").addResourceLocations("/html/");
 
@@ -127,7 +129,7 @@ public class SMPWebAppConfig implements WebMvcConfigurer {
     @Override
     public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
         // Configure Object Mapper with format date as: "2021-12-01T14:52:00Z"
-        LOG.debug("Register MappingJackson2HttpMessageConverter.");
+        LOG.debug("Register MappingJackson2HttpMessageConverter");
         MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
         ObjectMapper objectMapper = JsonMapper.builder()
                 .findAndAddModules()
@@ -143,4 +145,10 @@ public class SMPWebAppConfig implements WebMvcConfigurer {
 
         converters.add(0, converter);
     }
+
+    @Override
+    public void addFormatters(FormatterRegistry registry) {
+        LOG.debug("Register FilterHandler");
+        registry.addFormatterForFieldAnnotation(new FilterHandler());
+    }
 }
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/Filter.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/Filter.java
new file mode 100644
index 000000000..f3c71839e
--- /dev/null
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/Filter.java
@@ -0,0 +1,34 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2023 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.filter;
+
+import java.lang.annotation.*;
+
+/**
+ * Annotation used to mark a filter provided as an HTTP request parameters which needs its value to be URI-decoded.
+ *
+ * @author Sebastian-Ion TINCU
+ * @since 5.1
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Filter {
+
+}
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java
new file mode 100644
index 000000000..71a167a5f
--- /dev/null
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/filter/FilterHandler.java
@@ -0,0 +1,74 @@
+/*-
+ * #START_LICENSE#
+ * smp-webapp
+ * %%
+ * Copyright (C) 2017 - 2023 European Commission | eDelivery | DomiSMP
+ * %%
+ * Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent
+ * versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the Licence is
+ * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and limitations under the Licence.
+ * #END_LICENSE#
+ */
+package eu.europa.ec.edelivery.smp.filter;
+
+import org.springframework.format.AnnotationFormatterFactory;
+import org.springframework.format.Formatter;
+import org.springframework.format.Parser;
+import org.springframework.format.Printer;
+
+import java.net.URLDecoder;
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * A handler that parses filter values by decoding their tokens that have been URI-encoded in the front end.
+ *
+ * @author Sebastian-Ion TINCU
+ * @since 5.1
+ */
+public class FilterHandler implements AnnotationFormatterFactory<Filter> {
+
+    private static final Set<Class<?>> FIELD_TYPES;
+
+    static {
+        Set<Class<?>> fieldTypes = new HashSet<>();
+        fieldTypes.add(String.class);
+        FIELD_TYPES = Collections.unmodifiableSet(fieldTypes);
+    }
+
+    @Override
+    public Set<Class<?>> getFieldTypes() {
+        return FIELD_TYPES;
+    }
+
+    @Override
+    public Printer<?> getPrinter(Filter annotation, Class<?> fieldType) {
+        return getFormatter(annotation);
+    }
+
+    @Override
+    public Parser<?> getParser(Filter annotation, Class<?> fieldType) {
+        return getFormatter(annotation);
+    }
+
+    private Formatter<String> getFormatter(Filter annotation) {
+        return new Formatter<String>() {
+            @Override
+            public String print(String object, Locale locale) {
+                return object;
+            }
+
+            @Override
+            public String parse(String encoded, Locale locale) throws ParseException {
+                return URLDecoder.decode(encoded);
+            }
+        };
+    }
+}
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DomainEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DomainEditController.java
index 2d4962269..45583d315 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DomainEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/DomainEditController.java
@@ -26,6 +26,7 @@ import eu.europa.ec.edelivery.smp.data.ui.ResourceDefinitionRO;
 import eu.europa.ec.edelivery.smp.data.ui.ServiceResult;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
+import eu.europa.ec.edelivery.smp.filter.Filter;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UIDomainPublicService;
@@ -93,7 +94,7 @@ public class DomainEditController {
             @PathVariable(PATH_PARAM_ENC_DOMAIN_ID) String domainEncId,
             @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
             @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
-            @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+            @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
         logAdminAccess("getDomainMemberList");
         LOG.info("Search for domain members with filter  [{}], paging: [{}/{}], user: {}", filter, page, pageSize, userEncId);
         Long domainId = SessionSecurityUtils.decryptEntityId(domainEncId);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
index 83f8c3375..8dd9011a3 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/GroupEditController.java
@@ -25,6 +25,7 @@ import eu.europa.ec.edelivery.smp.data.ui.MemberRO;
 import eu.europa.ec.edelivery.smp.data.ui.ServiceResult;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
+import eu.europa.ec.edelivery.smp.filter.Filter;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UIGroupPublicService;
@@ -145,7 +146,7 @@ public class GroupEditController {
                                                       @PathVariable(PATH_PARAM_ENC_GROUP_ID) String groupEncId,
                                                       @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
                                                       @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
-                                                      @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+                                                      @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
 
         LOG.info("Search for group members with filter  [{}], paging: [{}/{}], user: {}", filter, page, pageSize, userEncId);
         Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
index e16d71fcf..4b4e23c47 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditController.java
@@ -25,6 +25,7 @@ import eu.europa.ec.edelivery.smp.data.ui.ResourceRO;
 import eu.europa.ec.edelivery.smp.data.ui.ServiceResult;
 import eu.europa.ec.edelivery.smp.exceptions.ErrorCode;
 import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException;
+import eu.europa.ec.edelivery.smp.filter.Filter;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UIResourceService;
@@ -76,7 +77,7 @@ public class ResourceEditController {
                                                           @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
                                                           @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
                                                           @RequestParam(value = PARAM_NAME_TYPE, defaultValue = "", required = false) String forRole,
-                                                          @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+                                                          @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
         logAdminAccess("getResourcesForGroup and type: " + forRole);
         Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId);
         Long userId = SessionSecurityUtils.decryptEntityId(userEncId);
@@ -146,7 +147,7 @@ public class ResourceEditController {
                                                       @PathVariable(PATH_PARAM_ENC_RESOURCE_ID) String resourceEncId,
                                                       @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
                                                       @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
-                                                      @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+                                                      @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
 
         LOG.info("Search for group members with filter  [{}], paging: [{}/{}], user: {}", filter, page, pageSize, userEncId);
         Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId);
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
index b794ca42a..9213164a1 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java
@@ -25,6 +25,7 @@ import eu.europa.ec.edelivery.smp.data.enums.CredentialTargetType;
 import eu.europa.ec.edelivery.smp.data.enums.CredentialType;
 import eu.europa.ec.edelivery.smp.data.model.user.DBUser;
 import eu.europa.ec.edelivery.smp.data.ui.*;
+import eu.europa.ec.edelivery.smp.filter.Filter;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UIAlertService;
@@ -78,7 +79,7 @@ public class UserController {
     @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)")
     @GetMapping(path = "/{user-id}/search", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
     public List<SearchUserRO> lookupUsers(@PathVariable(PATH_PARAM_ENC_USER_ID) String userId,
-                                          @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+                                          @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
         Long entityId = decryptEntityId(userId);
         LOG.info("Validating the password of the currently logged in user:[{}] with id:[{}] ", userId, entityId);
 
diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java
index eead0c772..6699cf9e9 100644
--- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java
+++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/UserAdminController.java
@@ -23,6 +23,7 @@ import eu.europa.ec.edelivery.smp.auth.SMPUserDetails;
 import eu.europa.ec.edelivery.smp.data.model.user.DBUser;
 import eu.europa.ec.edelivery.smp.data.ui.*;
 import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority;
+import eu.europa.ec.edelivery.smp.filter.Filter;
 import eu.europa.ec.edelivery.smp.logging.SMPLogger;
 import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
 import eu.europa.ec.edelivery.smp.services.ui.UITruststoreService;
@@ -84,7 +85,7 @@ public class UserAdminController {
             @PathVariable(PATH_PARAM_ENC_USER_ID) String userEncId,
             @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page,
             @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize,
-            @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) String filter) {
+            @RequestParam(value = PARAM_PAGINATION_FILTER, defaultValue = "", required = false) @Filter String filter) {
 
         LOG.info("Search user with filter  [{}], paging: [{}/{}], user: {}",filter,  page, pageSize, userEncId);
         return uiUserService.searchUsers(page, pageSize,  filter);
diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditControllerIT.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditControllerIT.java
index bdb26e338..0f614c20d 100644
--- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditControllerIT.java
+++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/edit/ResourceEditControllerIT.java
@@ -21,24 +21,30 @@ package eu.europa.ec.edelivery.smp.ui.edit;
 import eu.europa.ec.edelivery.smp.data.enums.VisibilityType;
 import eu.europa.ec.edelivery.smp.data.ui.*;
 import eu.europa.ec.edelivery.smp.services.ui.UIResourceSearchService;
+import eu.europa.ec.edelivery.smp.services.ui.UIResourceService;
 import eu.europa.ec.edelivery.smp.services.ui.filters.ResourceFilter;
 import eu.europa.ec.edelivery.smp.ui.AbstractControllerTest;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.CsvSource;
+import org.mockito.Mockito;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.SpyBean;
 import org.springframework.http.MediaType;
 import org.springframework.mock.web.MockHttpSession;
+import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.test.web.servlet.MvcResult;
 
 import java.io.IOException;
+import java.net.URLEncoder;
 import java.util.List;
 import java.util.UUID;
 
 import static eu.europa.ec.edelivery.smp.test.testutils.MockMvcUtils.*;
 import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -52,10 +58,16 @@ public class ResourceEditControllerIT extends AbstractControllerTest {
 
     @Autowired
     protected UIResourceSearchService uiResourceSearchService;
+    @SpyBean
+    private UIResourceService uiResourceService;
+    @Autowired
+    private ResourceEditController resourceEditController;
 
     @BeforeEach
     public void setup() throws IOException {
         super.setup();
+
+        ReflectionTestUtils.setField(resourceEditController, "uiResourceService", uiResourceService);
     }
 
     // test must match the webapp_integration_test_data.sql file!
@@ -290,8 +302,32 @@ public class ResourceEditControllerIT extends AbstractControllerTest {
         assertEquals(member.getRoleType(), response.getRoleType());
     }
 
-
     public int getResourceCount() {
         return uiResourceSearchService.getTableList(-1, -1, null, null, new ResourceFilter()).getCount().intValue();
     }
+
+    @Test
+    public void testFilter() throws Exception {
+        // given when
+        final String filter = ":%#^&$-_=asd.<>/\\";
+        String filterParam = URLEncoder.encode(filter);
+        MockHttpSession session = loginWithSystemAdmin(mvc);
+        UserRO userRO = getLoggedUserData(mvc, session);
+        List<DomainRO> domainsForUser = geUserDomainsForRole(mvc, session, userRO, null);
+        assertEquals(1, domainsForUser.size());
+        DomainRO domainRO = domainsForUser.get(0);
+        GroupRO groupRO = addGroupToDomain(session, domainRO, userRO);
+
+        // when
+        mvc.perform(get(PATH, userRO.getUserId(),
+                        domainRO.getDomainId(),
+                        groupRO.getGroupId())
+                        .session(session)
+                        .param(PARAM_PAGINATION_FILTER, filterParam)
+                        .with(csrf()))
+                .andExpect(status().isOk()).andReturn();
+
+        //then
+        Mockito.verify(uiResourceService).getGroupResources(anyLong(), anyInt(), anyInt(), eq(filter));
+    }
 }
-- 
GitLab