forked from DSpace/DSpace
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merged in task/dspace-cris-2023_02_x/DSC-1904 (pull request DSpace#2751)
Task/dspace cris 2023 02 x/DSC-1904 Approved-by: Giuseppe Digilio
- Loading branch information
Showing
4 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
.../src/main/java/org/dspace/discovery/SolrServiceAdministrativeSearchRestrictionPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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("[email protected]") | ||
.withPassword(password) | ||
.withNameInMetadata("Community", "Admin") | ||
.withCanLogin(true) | ||
.build(); | ||
|
||
EPerson subCommAdmin = | ||
EPersonBuilder.createEPerson(context) | ||
.withEmail("[email protected]") | ||
.withPassword(password) | ||
.withNameInMetadata("SubCommunity", "Admin") | ||
.withCanLogin(true) | ||
.build(); | ||
|
||
EPerson collAdmin = | ||
EPersonBuilder.createEPerson(context) | ||
.withEmail("[email protected]") | ||
.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 { | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters