diff --git a/dspace-api/src/main/java/org/dspace/discovery/SolrServiceAdministrativeSearchRestrictionPlugin.java b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceAdministrativeSearchRestrictionPlugin.java
new file mode 100644
index 00000000000..e17d0f37238
--- /dev/null
+++ b/dspace-api/src/main/java/org/dspace/discovery/SolrServiceAdministrativeSearchRestrictionPlugin.java
@@ -0,0 +1,90 @@
+/**
+ * The contents of this file are subject to the license and copyright
+ * detailed in the LICENSE and NOTICE files at the root of the source
+ * tree and available online at
+ *
+ * http://www.dspace.org/license/
+ */
+package org.dspace.discovery;
+
+import java.sql.SQLException;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.dspace.authorize.service.AuthorizeService;
+import org.dspace.core.Context;
+import org.dspace.core.LogHelper;
+import org.dspace.eperson.service.GroupService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Plugin that filters out non-administered items from administrative searches for collections and communities admins.
+ *
+ * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
+ */
+public class SolrServiceAdministrativeSearchRestrictionPlugin implements SolrServiceSearchPlugin {
+
+ private static final Logger log =
+ org.apache.logging.log4j.LogManager.getLogger(SolrServiceAdministrativeSearchRestrictionPlugin.class);
+ public static final String SEARCH_CONFIGURATION_PREFIX = "administrative";
+
+ @Autowired
+ protected AuthorizeService authorizeService;
+ @Autowired
+ protected GroupService groupService;
+
+ private static boolean isAdministrativeConfiguration(DiscoverQuery discoveryQuery) {
+ return discoveryQuery != null &&
+ StringUtils.isNotBlank(discoveryQuery.getDiscoveryConfigurationName()) &&
+ discoveryQuery.getDiscoveryConfigurationName().startsWith(SEARCH_CONFIGURATION_PREFIX);
+ }
+
+ @Override
+ public void additionalSearchParameters(Context context, DiscoverQuery discoveryQuery, SolrQuery solrQuery) {
+ try {
+
+ // Only apply this plugin to administrative searches
+ if (!isAdministrativeConfiguration(discoveryQuery)) {
+ return;
+ }
+
+ // Only apply this plugin to non-administrators
+ if (isAdmin(context)) {
+ return;
+ }
+
+ // Only apply this plugin to community / collection administrators
+ if (!isCommunityCollAdmin(context)) {
+ return;
+ }
+
+ // Applies filter query to restrict search results to only those that are administrate by the current user
+ solrQuery.addFilterQuery(
+ Stream.concat(
+ groupService.allMemberGroupsSet(context, context.getCurrentUser())
+ .stream()
+ .map(group -> "g" + group.getID()),
+ Stream.of(context.getCurrentUser())
+ .filter(Objects::nonNull)
+ .map(eperson -> String.valueOf(eperson.getID()))
+ )
+ .collect(Collectors.joining(" OR ", "admin:(", ")"))
+ );
+ } catch (SQLException e) {
+ log.error(LogHelper.getHeader(context, "Error while adding resource policy information to query", ""), e);
+ }
+ }
+
+ private boolean isCommunityCollAdmin(Context context) throws SQLException {
+ return this.authorizeService.isCollectionAdmin(context) || this.authorizeService.isCommunityAdmin(context);
+ }
+
+ private boolean isAdmin(Context context) throws SQLException {
+ return authorizeService.isAdmin(context);
+ }
+
+}
diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml
index 3c09125b0fd..49a1fc6e060 100644
--- a/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml
+++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/spring/api/test-discovery.xml
@@ -52,6 +52,7 @@
+
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java
index 43aecb96e3a..ae5982e9fff 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/DiscoveryRestControllerIT.java
@@ -5506,6 +5506,201 @@ public void discoverSearchObjectsTestForAdministrativeViewAdmin() throws Excepti
.andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
}
+ @Test
+ public void discoverSearchObjectsTestForAdministrativeViewCollCommAdministrators() throws Exception {
+
+ //We turn off the authorization system in order to create the structure as defined below
+ context.turnOffAuthorisationSystem();
+
+ //** GIVEN **
+
+ //1. A community-collection structure with one parent community with sub-community and two collections.
+
+ EPerson commAdmin =
+ EPersonBuilder.createEPerson(context)
+ .withEmail("community-admin@4science.com")
+ .withPassword(password)
+ .withNameInMetadata("Community", "Admin")
+ .withCanLogin(true)
+ .build();
+
+ EPerson subCommAdmin =
+ EPersonBuilder.createEPerson(context)
+ .withEmail("sub-community-admin@4science.com")
+ .withPassword(password)
+ .withNameInMetadata("SubCommunity", "Admin")
+ .withCanLogin(true)
+ .build();
+
+ EPerson collAdmin =
+ EPersonBuilder.createEPerson(context)
+ .withEmail("collection-admin@4science.com")
+ .withPassword(password)
+ .withNameInMetadata("Collection", "Admin")
+ .withCanLogin(true)
+ .build();
+
+ parentCommunity = CommunityBuilder
+ .createCommunity(context)
+ .withName("Parent Community")
+ .withAdminGroup(commAdmin)
+ .build();
+ Community child1 = CommunityBuilder
+ .createSubCommunity(context, parentCommunity)
+ .withName("Sub Community")
+ .withAdminGroup(subCommAdmin)
+ .build();
+ Collection col1 = CollectionBuilder
+ .createCollection(context, child1)
+ .withName("Collection 1")
+ .withAdminGroup(collAdmin)
+ .build();
+ Collection col2 = CollectionBuilder
+ .createCollection(context, child1)
+ .withName("Collection 2")
+ .build();
+ Collection col3 = CollectionBuilder
+ .createCollection(context, parentCommunity)
+ .withName("Collection 3")
+ .build();
+
+ //2. One public item, one private, one withdrawn.
+
+ ItemBuilder.createItem(context, col1)
+ .withTitle("COL1 Test Item")
+ .withIssueDate("2010-10-17")
+ .withAuthor("Smith, Donald")
+ .withSubject("ExtraEntry")
+ .build();
+
+ ItemBuilder.createItem(context, col2)
+ .withTitle("COL2 Test Item")
+ .withIssueDate("2024-09-16")
+ .withAuthor("Smith, Maria")
+ .withAuthor("Doe, Jane")
+ .build();
+
+ ItemBuilder.createItem(context, col2)
+ .withTitle("COL2-1 Test Item")
+ .withIssueDate("2024-09-16")
+ .withAuthor("Smith, Maria")
+ .withAuthor("Doe, Jane")
+ .build();
+
+ ItemBuilder.createItem(context, col3)
+ .withTitle("COL3 Test Item")
+ .withIssueDate("2024-09-16")
+ .withAuthor("Smith, Maria")
+ .withAuthor("Doe, Jane")
+ .build();
+
+ context.restoreAuthSystemState();
+
+ String adminToken = getAuthToken(admin.getEmail(), password);
+
+ getClient(adminToken).perform(get("/api/discover/search/objects")
+ .param("configuration", "administrativeView")
+ .param("query", "Test"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("discover")))
+ .andExpect(jsonPath("$._embedded.searchResult.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 4)
+ )))
+ .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
+ Matchers.containsInAnyOrder(
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL1 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2-1 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL3 Test Item"
+ )
+ )
+ ))
+ .andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
+
+ String commAdminToken = getAuthToken(commAdmin.getEmail(), password);
+
+ getClient(commAdminToken).perform(get("/api/discover/search/objects")
+ .param("configuration", "administrativeView")
+ .param("query", "Test"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("discover")))
+ .andExpect(jsonPath("$._embedded.searchResult.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 4)
+ )))
+ .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
+ Matchers.containsInAnyOrder(
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL1 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2-1 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL3 Test Item"
+ )
+ )
+ ))
+ .andExpect(jsonPath("$._links.self.href", containsString("/api/discover/search/objects")));
+
+ String collAdminToken = getAuthToken(collAdmin.getEmail(), password);
+
+ getClient(collAdminToken).perform(get("/api/discover/search/objects")
+ .param("configuration", "administrativeView")
+ .param("query", "Test"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("discover")))
+ .andExpect(jsonPath("$._embedded.searchResult.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 1)
+ )))
+ .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
+ Matchers.containsInAnyOrder(
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL1 Test Item"
+ )
+ )
+ ))
+ .andExpect(jsonPath("$._links.self.href",
+ containsString("/api/discover/search/objects"))
+ );
+
+ String subCommAdminToken = getAuthToken(subCommAdmin.getEmail(), password);
+
+ getClient(subCommAdminToken).perform(get("/api/discover/search/objects")
+ .param("configuration", "administrativeView")
+ .param("query", "Test"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.type", is("discover")))
+ .andExpect(jsonPath("$._embedded.searchResult.page", is(
+ PageMatcher.pageEntryWithTotalPagesAndElements(0, 20, 1, 3)
+ )))
+ .andExpect(jsonPath("$._embedded.searchResult._embedded.objects",
+ Matchers.containsInAnyOrder(
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL1 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2 Test Item"
+ ),
+ SearchResultMatcher.matchOnItemName(
+ "item", "items", "COL2-1 Test Item"
+ )
+ )
+ ))
+ .andExpect(jsonPath("$._links.self.href",
+ containsString("/api/discover/search/objects"))
+ );
+ }
+
@Test
public void discoverSearchObjectsTestForAdministrativeViewWithFilters() throws Exception {
diff --git a/dspace/config/spring/api/discovery.xml b/dspace/config/spring/api/discovery.xml
index a556ba4849c..080edb2e4ad 100644
--- a/dspace/config/spring/api/discovery.xml
+++ b/dspace/config/spring/api/discovery.xml
@@ -74,6 +74,7 @@
+
dc.contributor.author