Skip to content

Commit

Permalink
hotfix/NP-48375-accessLevel (#634)
Browse files Browse the repository at this point in the history
#634 NP-48375
  • Loading branch information
StigNorland authored Jan 17, 2025
1 parent 6c81bbb commit 7e70f56
Show file tree
Hide file tree
Showing 19 changed files with 335 additions and 315 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ spotless {

format 'misc', {
target '.gitignore', '.gitattributes', '.editorconfig', '**/*.gradle'
indentWithSpaces(4)
leadingTabsToSpaces(4)
trimTrailingWhitespace()
endWithNewline()
}
Expand All @@ -96,12 +96,16 @@ tasks.named('test').configure {
dependsOn 'spotlessApply'
}


pmd {
toolVersion = '7.5.0'
ruleSetConfig = rootProject.resources.text.fromFile('config/pmd/ruleset.xml')
ruleSets = []
ignoreFailures = false
pmdMain {
excludes = [
'**/generated/.*'
]
}
}

checkstyle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ public OpenSearchClient(HttpClient httpClient, CachedJwtProvider jwtProvider) {
}

public R doSearch(Q query) {
if (query.filters().hasContent()) {
logger.info(query.filters().toString());
}
queryBuilderStart = query.getStartTime();
queryParameters =
query.parameters().asMap().entrySet().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ public ParameterValidator<K, Q> fromRequestInfo(RequestInfo requestInfo) {
var contentType = extractContentTypeFromRequestInfo(requestInfo);
query.setMediaType(isNull(contentType) ? null : contentType.getMimeType());
var uri = URI.create(HTTPS + requestInfo.getDomainName() + requestInfo.getPath());
query.setAccessRights(requestInfo.getAccessRights());
if (requestInfo.getHeaders().containsKey("Authorization")) {
query.setAccessRights(requestInfo.getAccessRights());
}
query.setNvaSearchApiUri(uri);
return fromMultiValueParameters(requestInfo.getMultiValueQueryStringParameters());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public void set(QueryBuilder... filters) {
Arrays.stream(filters).forEach(this::add);
}

public boolean hasContent() {
return !filters.isEmpty();
public int size() {
return filters.size();
}

public QueryFilter add(QueryBuilder builder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,7 @@ private void handleSearchAfter(SearchSourceBuilder builder) {

private void handleSorting(SearchSourceBuilder builder) {
if (hasSortBy(RELEVANCE_KEY_NAME)) {
// Not very well documented.
// This allows sorting on relevance together with other fields.
// This allows sorting on relevance together with other fields. (Not very well documented)
builder.trackScores(true);
}
builderStreamFieldSort().forEach(builder::sort);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
package no.unit.nva.search.resource;

import static no.unit.nva.constants.Words.AUTHORIZATION;
import static no.unit.nva.constants.Words.CURATING_INSTITUTIONS;
import static no.unit.nva.constants.Words.DOT;
import static no.unit.nva.constants.Words.KEYWORD;
import static no.unit.nva.constants.Words.STATUS;
import static no.unit.nva.search.common.enums.PublicationStatus.DELETED;
import static no.unit.nva.search.common.enums.PublicationStatus.PUBLISHED;
import static no.unit.nva.search.common.enums.PublicationStatus.PUBLISHED_METADATA;
import static no.unit.nva.search.common.enums.PublicationStatus.UNPUBLISHED;
import static no.unit.nva.search.resource.Constants.CONTRIBUTOR_ORG_KEYWORD;
import static no.unit.nva.search.resource.Constants.STATUS_KEYWORD;
import static no.unit.nva.search.resource.ResourceParameter.STATISTICS;
import static nva.commons.apigateway.AccessRight.MANAGE_CUSTOMERS;
import static nva.commons.apigateway.AccessRight.MANAGE_RESOURCES_ALL;
import static nva.commons.apigateway.AccessRight.MANAGE_RESOURCES_STANDARD;

import java.net.URI;
import java.util.Arrays;
import java.util.stream.Stream;
import no.unit.nva.search.common.enums.PublicationStatus;
import no.unit.nva.search.common.records.FilterBuilder;
import nva.commons.apigateway.RequestInfo;
import nva.commons.apigateway.exceptions.UnauthorizedException;
import org.opensearch.index.query.DisMaxQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.index.query.TermsQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* ResourceAccessFilter is a class that filters tickets based on access rights.
Expand All @@ -41,6 +46,7 @@ public class ResourceAccessFilter implements FilterBuilder<ResourceSearchQuery>
private static final String EDITOR_FILTER = "EditorFilter";
private static final String CURATOR_FILTER = "CuratorFilter";

protected static final Logger logger = LoggerFactory.getLogger(ResourceAccessFilter.class);
private final ResourceSearchQuery searchQuery;

public ResourceAccessFilter(ResourceSearchQuery query) {
Expand All @@ -62,10 +68,11 @@ public ResourceSearchQuery apply() {

@Override
public ResourceSearchQuery fromRequestInfo(RequestInfo requestInfo) throws UnauthorizedException {

return customerCurationInstitutions(requestInfo)
.requiredStatus(PUBLISHED, PUBLISHED_METADATA)
.apply();
if (isAuthorized(requestInfo)) {
return customerCurationInstitutions(requestInfo).apply();
} else {
return requiredStatus(PUBLISHED).apply();
}
}

/**
Expand All @@ -82,12 +89,8 @@ public ResourceSearchQuery fromRequestInfo(RequestInfo requestInfo) throws Unaut
*/
public ResourceAccessFilter requiredStatus(PublicationStatus... publicationStatus) {
final var values =
Arrays.stream(publicationStatus)
.filter(this::isStatusAllowed)
.map(PublicationStatus::toString)
.toArray(String[]::new);
final var filter = new TermsQueryBuilder(STATUS_KEYWORD, values).queryName(STATUS);
this.searchQuery.filters().add(filter);
Arrays.stream(publicationStatus).map(PublicationStatus::toString).toArray(String[]::new);
this.searchQuery.filters().add(new TermsQueryBuilder(STATUS_KEYWORD, values).queryName(STATUS));
return this;
}

Expand All @@ -101,31 +104,50 @@ public ResourceAccessFilter requiredStatus(PublicationStatus... publicationStatu
*/
public ResourceAccessFilter customerCurationInstitutions(RequestInfo requestInfo)
throws UnauthorizedException {
if (isCurator() && isStatisticsQuery()) {
if (isAppAdmin() && isStatisticsQuery()) {
return this;
}
final var filter =
QueryBuilders.boolQuery().minimumShouldMatch(1).queryName(EDITOR_CURATOR_FILTER);

final var statuses =
Stream.of(PUBLISHED, DELETED, UNPUBLISHED)
.filter(this::isStatusAllowed)
.toArray(PublicationStatus[]::new);

requiredStatus(statuses);

var curationInstitutionId = getCurationInstitutionId(requestInfo).toString();
if (isCurator()) {
filter.should(getCuratingInstitutionAccessFilter(curationInstitutionId));
filter.should(getContributingOrganisationAccessFilter(curationInstitutionId));
this.searchQuery.filters().add(buildMatchBoth(curationInstitutionId));
} else if (isEditor()) {
filter.should(getContributingOrganisationAccessFilter(curationInstitutionId));
this.searchQuery.filters().add(filterByContributingOrg(curationInstitutionId));
}
if (!filter.hasClauses()) {
if (statusOrOrgfilterIsMissing() && !isAppAdmin()) {
throw new UnauthorizedException();
}
this.searchQuery.filters().add(filter);
return this;
}

private QueryBuilder getContributingOrganisationAccessFilter(String institutionId) {
return QueryBuilders.termQuery(CONTRIBUTOR_ORG_KEYWORD, institutionId).queryName(EDITOR_FILTER);
private boolean statusOrOrgfilterIsMissing() {
return this.searchQuery.filters().size() < 2;
}

private QueryBuilder filterByContributingOrg(String institutionId) {
return new TermsQueryBuilder(CONTRIBUTOR_ORG_KEYWORD, institutionId).queryName(EDITOR_FILTER);
}

private QueryBuilder getCuratingInstitutionAccessFilter(String institutionId) {
return QueryBuilders.termQuery(CURATING_INST_KEYWORD, institutionId).queryName(CURATOR_FILTER);
private QueryBuilder filterByCuratingOrg(String institutionId) {
return new TermsQueryBuilder(CURATING_INST_KEYWORD, institutionId).queryName(CURATOR_FILTER);
}

private DisMaxQueryBuilder buildMatchBoth(String institutionId) {
return new DisMaxQueryBuilder()
.add(filterByContributingOrg(institutionId))
.add(filterByCuratingOrg(institutionId))
.queryName(EDITOR_CURATOR_FILTER);
}

private boolean isAuthorized(RequestInfo requestInfo) {
return requestInfo.getHeaders().containsKey(AUTHORIZATION);
}

/**
Expand All @@ -135,17 +157,24 @@ private QueryBuilder getCuratingInstitutionAccessFilter(String institutionId) {
* @return true if allowed
*/
private boolean isStatusAllowed(PublicationStatus publicationStatus) {
return isEditor() || publicationStatus != UNPUBLISHED;
return isAppAdmin()
|| (isEditor() && publicationStatus == DELETED)
|| publicationStatus == PUBLISHED
|| publicationStatus == UNPUBLISHED;
}

private boolean isStatisticsQuery() {
return searchQuery.parameters().isPresent(STATISTICS);
}

private boolean isCurator() {
private boolean isAppAdmin() {
return searchQuery.hasAccessRights(MANAGE_CUSTOMERS);
}

private boolean isCurator() {
return searchQuery.hasAccessRights(MANAGE_RESOURCES_STANDARD);
}

private boolean isEditor() {
return searchQuery.hasAccessRights(MANAGE_RESOURCES_ALL);
}
Expand Down
Loading

0 comments on commit 7e70f56

Please sign in to comment.