Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.b2international.snowowl.core.identity;

import static com.b2international.snowowl.core.identity.Permission.ALL;
import static com.b2international.snowowl.core.identity.Permission.OPERATION_BROWSE;
import static com.google.common.base.Preconditions.checkArgument;

import java.io.Serializable;
Expand Down Expand Up @@ -111,6 +113,17 @@ public boolean isAdministrator() {
.isPresent();
}

/**
* @return <code>true</code> if this user has a permission to browse all resources, <code>false</code> otherwise.
*/
@JsonIgnore
public boolean canBrowseAll() {
return isAdministrator() || getPermissions().stream()
.filter(p -> OPERATION_BROWSE.equals(p.getOperation()) && ALL.equals(p.getResource()))
.findFirst()
.isPresent();
}

/**
* Returns <code>true</code> if the user has the necessary permission to allow performing an operation on the given resource.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@
*/
package com.b2international.snowowl.core.request.version;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import com.b2international.index.Hits;
import com.b2international.index.query.Expression;
import com.b2international.index.query.Expressions;
import com.b2international.index.query.Expressions.ExpressionBuilder;
import com.b2international.snowowl.core.ResourceURI;
import com.b2international.snowowl.core.authorization.AuthorizationService;
import com.b2international.snowowl.core.branch.Branch;
import com.b2international.snowowl.core.domain.RepositoryContext;
import com.b2international.snowowl.core.identity.User;
import com.b2international.snowowl.core.request.SearchIndexResourceRequest;
import com.b2international.snowowl.core.version.VersionDocument;
import com.b2international.snowowl.core.version.Versions;
Expand Down Expand Up @@ -96,11 +103,7 @@

addIdFilter(query, VersionDocument.Expressions::ids);
addFilter(query, OptionKey.RESOURCE_TYPE, String.class, VersionDocument.Expressions::resourceTypes);
// TODO add a security filter to return commits from resources that can be accessed by the current user
addFilter(query, OptionKey.RESOURCE, String.class, resources -> Expressions.bool()
.should(VersionDocument.Expressions.resources(resources))
.should(VersionDocument.Expressions.resourceIds(resources))
.build());
addResourcesFilter(query, context);
addFilter(query, OptionKey.VERSION, String.class, VersionDocument.Expressions::versions);
addFilter(query, OptionKey.RESOURCE_BRANCHPATH, String.class, VersionDocument.Expressions::resourceBranchPaths);
addFilter(query, OptionKey.AUTHOR, String.class, VersionDocument.Expressions::authors);
Expand All @@ -121,7 +124,45 @@

return query.build();
}


protected final void addResourcesFilter(ExpressionBuilder queryBuilder, RepositoryContext context) {
User user = context.service(User.class);

if (containsKey(OptionKey.RESOURCE)) {
Collection<String> resources = getCollection(OptionKey.RESOURCE, String.class);
Collection<String> resourceIds = new ArrayList<>();
resources.stream()
.map(resource -> resource.contains(Branch.SEPARATOR) ? new ResourceURI(resource).getResourceId() : resource)
.forEach(id -> resourceIds.add(id));

if (user.canBrowseAll()) {
queryBuilder.filter(VersionDocument.Expressions.resourceIds(resourceIds));
} else {
final AuthorizationService authz = context.optionalService(AuthorizationService.class).orElse(AuthorizationService.DEFAULT);
Set<String> accessibleResources = authz.getAccessibleResources(context, context.service(User.class));

resourceIds.removeIf(resourceId -> !accessibleResources.contains(resourceId));
if (resourceIds.isEmpty()) {
throw new NoResultException();
}
queryBuilder.filter(VersionDocument.Expressions.resourceIds(resourceIds));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If resourceIds becomes empty after authorization check, the search request can return quickly by throwing a NoResultException.

}

return;
}

if (!user.canBrowseAll()) {
final AuthorizationService authz = context.optionalService(AuthorizationService.class).orElse(AuthorizationService.DEFAULT);
Set<String> accessibleResources = authz.getAccessibleResources(context, context.service(User.class));

Check warning on line 156 in core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java

View check run for this annotation

Codecov / codecov/patch

core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java#L155-L156

Added lines #L155 - L156 were not covered by tests

if (accessibleResources.isEmpty()) {
throw new NoResultException();

Check warning on line 159 in core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java

View check run for this annotation

Codecov / codecov/patch

core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java#L159

Added line #L159 was not covered by tests
}

queryBuilder.filter(VersionDocument.Expressions.resourceIds(accessibleResources));

Check warning on line 162 in core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java

View check run for this annotation

Codecov / codecov/patch

core/com.b2international.snowowl.core/src/com/b2international/snowowl/core/request/version/VersionSearchRequest.java#L162

Added line #L162 was not covered by tests
}
}

@Override
protected Class<VersionDocument> getDocumentType() {
return VersionDocument.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,28 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.time.LocalDate;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import org.elasticsearch.core.List;
import org.junit.Test;

import com.b2international.snowowl.core.ResourceURI;
import com.b2international.snowowl.core.authorization.AuthorizedEventBus;
import com.b2international.snowowl.core.branch.Branch;
import com.b2international.snowowl.core.branch.Branches;
import com.b2international.snowowl.core.commit.CommitInfos;
import com.b2international.snowowl.core.identity.Permission;
import com.b2international.snowowl.core.identity.User;
import com.b2international.snowowl.core.identity.request.UserRequests;
import com.b2international.snowowl.core.repository.RepositoryRequests;
import com.b2international.snowowl.core.request.ResourceRequests;
import com.b2international.snowowl.core.rest.AbstractRestService;
import com.b2international.snowowl.core.version.Version;
import com.b2international.snowowl.snomed.cis.domain.SctId;
import com.b2international.snowowl.snomed.common.SnomedConstants.Concepts;
import com.b2international.snowowl.snomed.common.SnomedTerminologyComponentConstants;
Expand All @@ -47,6 +56,7 @@
import com.b2international.snowowl.snomed.datastore.request.SnomedRequests;
import com.b2international.snowowl.test.commons.Services;
import com.b2international.snowowl.test.commons.SnomedContentRule;
import com.b2international.snowowl.test.commons.rest.RestExtensions;
import com.google.common.collect.ImmutableMap;

import io.restassured.http.ContentType;
Expand Down Expand Up @@ -86,6 +96,101 @@ public void createRegularVersion() {
assertGetVersion(INT_CODESYSTEM, "regular-version").statusCode(200);
}

@Test
public void searchForVersion() {
final String versionId = "exclusive-version";

createVersion(INT_CODESYSTEM, versionId, getNextAvailableEffectiveDate(INT_CODESYSTEM)).statusCode(201);
assertGetVersion(INT_CODESYSTEM, versionId).statusCode(200);

// Scenario 1: User can browse all
final User user1 = UserRequests.prepareGenerateApiKey()
.setUsername(RestExtensions.USER)
.setPassword(RestExtensions.PASS)
.setPermissions(List.of(String.format("%s:%s", Permission.OPERATION_BROWSE, Permission.ALL)))
.buildAsync()
.execute(getBus())
.getSync(10L, TimeUnit.SECONDS);

AuthorizedEventBus bus1 = new AuthorizedEventBus(getBus(), ImmutableMap.of("Authorization", user1.getAccessToken()));

Optional<Version> versionFoundByUser1 = ResourceRequests.prepareSearchVersion()
.filterByResource(INT_CODESYSTEM)
.filterByVersionId(versionId)
.buildAsync()
.execute(bus1)
.getSync()
.first();

assertTrue(versionFoundByUser1.isPresent());
assertEquals(versionId, versionFoundByUser1.get().getVersion());

// Scenario 2: User does not have permission to browse the given resource version
final User user2 = UserRequests.prepareGenerateApiKey()
.setUsername(RestExtensions.USER)
.setPassword(RestExtensions.PASS)
.setPermissions(List.of(String.format("%s:%s", Permission.OPERATION_BROWSE, "OtherResource")))
.buildAsync()
.execute(getBus())
.getSync(10L, TimeUnit.SECONDS);

AuthorizedEventBus bus2 = new AuthorizedEventBus(getBus(), ImmutableMap.of("Authorization", user2.getAccessToken()));

Optional<Version> versionFoundByUser2 = ResourceRequests.prepareSearchVersion()
.filterByResource(INT_CODESYSTEM)
.filterByVersionId(versionId)
.buildAsync()
.execute(bus2)
.getSync()
.first();

assertTrue(versionFoundByUser2.isEmpty());

// Scenario 3: User is admin
final User user3 = UserRequests.prepareGenerateApiKey()
.setUsername(RestExtensions.USER)
.setPassword(RestExtensions.PASS)
.setPermissions(List.of(String.format("%s:%s", Permission.ALL, Permission.ALL)))
.buildAsync()
.execute(getBus())
.getSync(10L, TimeUnit.SECONDS);

AuthorizedEventBus bus3 = new AuthorizedEventBus(getBus(), ImmutableMap.of("Authorization", user3.getAccessToken()));

Optional<Version> versionFoundByUser3 = ResourceRequests.prepareSearchVersion()
.filterByResource(INT_CODESYSTEM)
.filterByVersionId(versionId)
.buildAsync()
.execute(bus3)
.getSync()
.first();

assertTrue(versionFoundByUser3.isPresent());
assertEquals(versionId, versionFoundByUser3.get().getVersion());

// Scenario 4: User has explicit permission to browse resource
final User user4 = UserRequests.prepareGenerateApiKey()
.setUsername(RestExtensions.USER)
.setPassword(RestExtensions.PASS)
.setPermissions(List.of(String.format("%s:%s", Permission.OPERATION_BROWSE, INT_CODESYSTEM)))
.buildAsync()
.execute(getBus())
.getSync(10L, TimeUnit.SECONDS);

AuthorizedEventBus bus4 = new AuthorizedEventBus(getBus(), ImmutableMap.of("Authorization", user4.getAccessToken()));

Optional<Version> versionFoundByUser4 = ResourceRequests.prepareSearchVersion()
.filterByResource(INT_CODESYSTEM)
.filterByVersionId(versionId)
.buildAsync()
.execute(bus4)
.getSync()
.first();

assertTrue(versionFoundByUser4.isPresent());
assertEquals(versionId, versionFoundByUser4.get().getVersion());
}

@Test
public void createRegularVersionWithAuthor() {
String author = "info@b2international.com";
Expand Down