Browse Source

SONAR-15741 - Centralized check for child projects permission

tags/9.3.0.51899
Belen Pruvost 2 years ago
parent
commit
e018265818

+ 5
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java View File

@@ -46,6 +46,11 @@ public class SafeModeUserSession extends AbstractUserSession {
return false;
}

@Override
protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
return false;
}

@CheckForNull
@Override
public String getLogin() {

+ 23
- 3
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.user;

import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -40,7 +39,7 @@ import static org.apache.commons.lang.StringUtils.defaultString;
import static org.sonar.server.user.UserSession.IdentityProvider.SONARQUBE;

public abstract class AbstractUserSession implements UserSession {
private static final Set<String> PUBLIC_PERMISSIONS = ImmutableSet.of(UserRole.USER, UserRole.CODEVIEWER);
private static final Set<String> PUBLIC_PERMISSIONS = Set.of(UserRole.USER, UserRole.CODEVIEWER);
private static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges";
private static final String AUTHENTICATION_IS_REQUIRED_MESSAGE = "Authentication is required";

@@ -105,6 +104,14 @@ public abstract class AbstractUserSession implements UserSession {
return hasProjectUuidPermission(permission, project.getUuid());
}

@Override
public final boolean hasChildProjectsPermission(String permission, ComponentDto component) {
if (isRoot()) {
return true;
}
return hasChildProjectsPermission(permission, component.uuid());
}

@Override
public final boolean hasComponentUuidPermission(String permission, String componentUuid) {
if (isRoot()) {
@@ -120,6 +127,8 @@ public abstract class AbstractUserSession implements UserSession {

protected abstract boolean hasProjectUuidPermission(String permission, String projectUuid);

protected abstract boolean hasChildProjectsPermission(String permission, String applicationUuid);

@Override
public final List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
if (isRoot()) {
@@ -188,7 +197,8 @@ public abstract class AbstractUserSession implements UserSession {
return this;
}

@Override public UserSession checkProjectPermission(String projectPermission, ProjectDto project) {
@Override
public UserSession checkProjectPermission(String projectPermission, ProjectDto project) {
if (isRoot() || hasProjectUuidPermission(projectPermission, project.getUuid())) {
return this;
}
@@ -196,6 +206,16 @@ public abstract class AbstractUserSession implements UserSession {
throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
}

@Override
public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
if (isRoot() || !component.qualifier().equals(Qualifiers.APP) || hasChildProjectsPermission(projectPermission, component.uuid())) {
return this;
}

throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);

}

@Override
public final UserSession checkComponentUuidPermission(String permission, String componentUuid) {
if (!hasComponentUuidPermission(permission, componentUuid)) {

+ 5
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java View File

@@ -127,6 +127,11 @@ public final class DoPrivileged {
return true;
}

@Override
protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
return true;
}

@Override
public boolean isSystemAdministrator() {
return true;

+ 38
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java View File

@@ -29,16 +29,22 @@ import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTreeQuery;
import org.sonar.db.component.ComponentTreeQuery.Strategy;
import org.sonar.db.permission.GlobalPermission;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;

import static java.util.Collections.singleton;
import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toSet;
import static org.apache.commons.lang.StringUtils.defaultIfEmpty;
import static org.sonar.api.web.UserRole.PUBLIC_PERMISSIONS;

@@ -158,6 +164,24 @@ public class ServerUserSession extends AbstractUserSession {
if (permissionsByProjectUuid == null) {
permissionsByProjectUuid = new HashMap<>();
}
return hasPermission(permission, projectUuid);
}

@Override
protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
if (permissionsByProjectUuid == null) {
permissionsByProjectUuid = new HashMap<>();
}

Set<String> childProjectUuids = loadChildProjectUuids(applicationUuid);

return childProjectUuids
.stream()
.map(uuid -> hasPermission(permission, uuid))
.allMatch(Boolean::valueOf);
}

private boolean hasPermission(String permission, String projectUuid) {
Set<String> projectPermissions = permissionsByProjectUuid.computeIfAbsent(projectUuid, this::loadProjectPermissions);
return projectPermissions.contains(permission);
}
@@ -181,6 +205,20 @@ public class ServerUserSession extends AbstractUserSession {
}
}

private Set<String> loadChildProjectUuids(String applicationUuid) {
try (DbSession dbSession = dbClient.openSession(false)) {
return dbClient.componentDao()
.selectDescendants(dbSession, ComponentTreeQuery.builder()
.setBaseUuid(applicationUuid)
.setQualifiers(singleton(Qualifiers.PROJECT))
.setScopes(singleton(Scopes.FILE))
.setStrategy(Strategy.CHILDREN).build())
.stream()
.map(ComponentDto::getCopyComponentUuid)
.collect(toSet());
}
}

private Set<GlobalPermission> loadGlobalPermissions() {
Set<String> permissionKeys;
try (DbSession dbSession = dbClient.openSession(false)) {

+ 11
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java View File

@@ -144,6 +144,12 @@ public class ThreadLocalUserSession implements UserSession {
return this;
}

@Override
public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
get().checkChildProjectsPermission(projectPermission, component);
return this;
}

@Override
public UserSession checkComponentUuidPermission(String permission, String componentUuid) {
get().checkComponentUuidPermission(permission, componentUuid);
@@ -171,6 +177,11 @@ public class ThreadLocalUserSession implements UserSession {
return get().hasProjectPermission(permission, project);
}

@Override
public boolean hasChildProjectsPermission(String permission, ComponentDto component) {
return get().hasChildProjectsPermission(permission, component);
}

@Override
public boolean hasComponentUuidPermission(String permission, String componentUuid) {
return get().hasComponentUuidPermission(permission, componentUuid);

+ 8
- 0
server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java View File

@@ -195,6 +195,8 @@ public interface UserSession {

boolean hasProjectPermission(String permission, ProjectDto project);

boolean hasChildProjectsPermission(String permission, ComponentDto component);

/**
* Using {@link #hasComponentPermission(String, ComponentDto)} is recommended
* because it does not have to load project if the referenced component
@@ -228,6 +230,12 @@ public interface UserSession {
*/
UserSession checkProjectPermission(String projectPermission, ProjectDto project);

/**
* Ensures that {@link #hasChildProjectsPermission(String, ComponentDto)} is {@code true}
* otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.
*/
UserSession checkChildProjectsPermission(String projectPermission, ComponentDto project);

/**
* Ensures that {@link #hasComponentUuidPermission(String, String)} is {@code true},
* otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.

+ 1
- 0
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java View File

@@ -46,5 +46,6 @@ public class SafeModeUserSessionTest {
assertThat(underTest.isSystemAdministrator()).isFalse();
assertThat(underTest.hasPermissionImpl(GlobalPermission.ADMINISTER)).isFalse();
assertThat(underTest.hasProjectUuidPermission(UserRole.USER, "foo")).isFalse();
assertThat(underTest.hasChildProjectsPermission(UserRole.USER, "foo")).isFalse();
}
}

+ 2
- 0
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java View File

@@ -26,6 +26,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.server.tester.MockUserSession;

import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.web.UserRole.USER;

public class DoPrivilegedTest {

@@ -50,6 +51,7 @@ public class DoPrivilegedTest {
assertThat(catcher.userSession.hasComponentPermission("any permission", new ComponentDto())).isTrue();
assertThat(catcher.userSession.isSystemAdministrator()).isTrue();
assertThat(catcher.userSession.shouldResetPassword()).isFalse();
assertThat(catcher.userSession.hasChildProjectsPermission(USER, new ComponentDto())).isTrue();

// verify session in place after task is done
assertThat(threadLocalUserSession.get()).isSameAs(session);

+ 177
- 46
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java View File

@@ -25,7 +25,6 @@ import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
@@ -37,10 +36,15 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.api.web.UserRole.CODEVIEWER;
import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.core.permission.GlobalPermissions.PROVISIONING;
import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
import static org.sonar.db.component.ComponentTesting.newChildComponent;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER;
import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS;
import static org.sonar.db.permission.GlobalPermission.SCAN;
@@ -48,8 +52,8 @@ import static org.sonar.db.permission.GlobalPermission.SCAN;
public class ServerUserSessionTest {

@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
private DbClient dbClient = db.getDbClient();
public final DbTester db = DbTester.create(System2.INSTANCE);
private final DbClient dbClient = db.getDbClient();

@Test
public void anonymous_is_not_logged_in_and_does_not_have_login() {
@@ -157,9 +161,9 @@ public class ServerUserSessionTest {

UserSession underTest = newUserSession(root);

assertThat(underTest.hasComponentUuidPermission(UserRole.USER, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission(UserRole.CODEVIEWER, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission(UserRole.ADMIN, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission(USER, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission(CODEVIEWER, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission(ADMIN, file.uuid())).isTrue();
assertThat(underTest.hasComponentUuidPermission("whatever", "who cares?")).isTrue();
}

@@ -172,7 +176,7 @@ public class ServerUserSessionTest {

UserSession underTest = newUserSession(root);

assertThat(underTest.checkComponentUuidPermission(UserRole.USER, file.uuid())).isSameAs(underTest);
assertThat(underTest.checkComponentUuidPermission(USER, file.uuid())).isSameAs(underTest);
assertThat(underTest.checkComponentUuidPermission("whatever", "who cares?")).isSameAs(underTest);
}

@@ -180,10 +184,60 @@ public class ServerUserSessionTest {
public void checkComponentUuidPermission_fails_with_FE_when_user_has_not_permission_for_specified_uuid_in_db() {
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnUser(user, UserRole.USER, project);
db.users().insertProjectPermissionOnUser(user, USER, project);
UserSession session = newUserSession(user);

assertThatForbiddenExceptionIsThrown(() -> session.checkComponentUuidPermission(UserRole.USER, "another-uuid"));
assertThatForbiddenExceptionIsThrown(() -> session.checkComponentUuidPermission(USER, "another-uuid"));
}

@Test
public void checkChildProjectsPermission_succeeds_if_user_is_root() {
UserDto root = db.users().insertUser();
root = db.users().makeRoot(root);
ComponentDto project = db.components().insertPrivateProject();
ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project);

UserSession underTest = newUserSession(root);

assertThat(underTest.checkChildProjectsPermission(USER, application)).isSameAs(underTest);
}

@Test
public void checkChildProjectsPermission_succeeds_if_user_has_permissions_on_all_application_child_projects() {
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnUser(user, USER, project);
ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project);

UserSession underTest = newUserSession(user);

assertThat(underTest.checkChildProjectsPermission(USER, application)).isSameAs(underTest);
}

@Test
public void checkChildProjectsPermission_succeeds_if_component_is_not_an_application() {
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();

UserSession underTest = newUserSession(user);

assertThat(underTest.checkChildProjectsPermission(USER, project)).isSameAs(underTest);
}

@Test
public void checkChildProjectsPermission_fails_with_FE_when_user_has_not_permission_for_specified_uuid_in_db() {
UserDto user = db.users().insertUser();
ComponentDto project = db.components().insertPrivateProject();
ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project);
//add computed project
db.components().insertComponent(newProjectCopy(project, application));

UserSession underTest = newUserSession(user);

assertThatForbiddenExceptionIsThrown(() -> underTest.checkChildProjectsPermission(USER, application));
}

@Test
@@ -215,7 +269,7 @@ public class ServerUserSessionTest {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
db.users().insertPermissionOnUser(user, PROVISION_PROJECTS);
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, project);
db.users().insertProjectPermissionOnUser(user, ADMIN, project);

UserSession session = newUserSession(user);
assertThat(session.hasPermission(PROVISION_PROJECTS)).isTrue();
@@ -264,14 +318,91 @@ public class ServerUserSessionTest {
assertThat(session.hasPermission(SCAN)).isFalse();
}

@Test
public void test_hasChildProjectsPermission_for_logged_in_user() {
ComponentDto project1 = db.components().insertPrivateProject();
ComponentDto project2 = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
db.users().insertProjectPermissionOnUser(user, USER, project1);

ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project1);
// add computed project
db.components().insertComponent(newProjectCopy(project1, application));

UserSession session = newUserSession(user);
assertThat(session.hasChildProjectsPermission(USER, application)).isTrue();

db.components().addApplicationProject(application, project2);
db.components().insertComponent(newProjectCopy(project2, application));

assertThat(session.hasChildProjectsPermission(USER, application)).isFalse();
}

@Test
public void test_hasChildProjectsPermission_for_anonymous_user() {
ComponentDto project = db.components().insertPrivateProject();
db.users().insertPermissionOnAnyone(USER);
ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project);
// add computed project
db.components().insertComponent(newProjectCopy(project, application));

UserSession session = newAnonymousSession();
assertThat(session.hasChildProjectsPermission(USER, application)).isFalse();
}

@Test
public void hasChildProjectsPermission_keeps_cache_of_permissions_of_logged_in_user() {
ComponentDto project = db.components().insertPrivateProject();
UserDto user = db.users().insertUser();
db.users().insertProjectPermissionOnUser(user, USER, project);

ComponentDto application = db.components().insertPrivateApplication();
db.components().addApplicationProject(application, project);
// add computed project
db.components().insertComponent(newProjectCopy(project, application));

UserSession session = newUserSession(user);

// feed the cache
assertThat(session.hasChildProjectsPermission(USER, application)).isTrue();

// change permissions without updating the cache
db.users().deletePermissionFromUser(project, user, USER);
assertThat(session.hasChildProjectsPermission(USER, application)).isTrue();

// cache is refreshed when user logs in again
session = newUserSession(user);
assertThat(session.hasChildProjectsPermission(USER, application)).isFalse();
}

@Test
public void hasChildProjectsPermission_keeps_cache_of_permissions_of_anonymous_user() {
db.users().insertPermissionOnAnyone(USER);

ComponentDto project = db.components().insertPublicProject();
ComponentDto application = db.components().insertPublicApplication();
db.components().addApplicationProject(application, project);

UserSession session = newAnonymousSession();

// feed the cache
assertThat(session.hasChildProjectsPermission(USER, application)).isTrue();

// change privacy of the project without updating the cache
db.getDbClient().componentDao().setPrivateForRootComponentUuidWithoutAudit(db.getSession(), project.uuid(), true);
assertThat(session.hasChildProjectsPermission(USER, application)).isTrue();
}

@Test
public void hasComponentPermissionByDtoOrUuid_returns_true_for_anonymous_user_for_permissions_USER_and_CODEVIEWER_on_public_projects_without_permissions() {
ComponentDto publicProject = db.components().insertPublicProject();

ServerUserSession underTest = newAnonymousSession();

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, publicProject)).isTrue();
}

@Test
@@ -281,8 +412,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newAnonymousSession();

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, publicProject)).isTrue();
}

@Test
@@ -292,8 +423,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newAnonymousSession();

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, publicProject)).isTrue();
}

@Test
@@ -303,8 +434,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newAnonymousSession();

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, publicProject)).isTrue();
}

@Test
@@ -314,8 +445,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newUserSession(user);

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, privateProject)).isFalse();
}

@Test
@@ -326,8 +457,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newUserSession(user);

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, privateProject)).isFalse();
}

@Test
@@ -338,8 +469,8 @@ public class ServerUserSessionTest {

ServerUserSession underTest = newUserSession(user);

assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.CODEVIEWER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, USER, privateProject)).isFalse();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, CODEVIEWER, privateProject)).isFalse();
}

@Test
@@ -411,35 +542,35 @@ public class ServerUserSessionTest {
public void hasComponentPermissionByDtoOrUuid_keeps_cache_of_permissions_of_logged_in_user() {
UserDto user = db.users().insertUser();
ComponentDto publicProject = db.components().insertPublicProject();
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, publicProject);
db.users().insertProjectPermissionOnUser(user, ADMIN, publicProject);

UserSession underTest = newUserSession(user);

// feed the cache
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ADMIN, publicProject)).isTrue();

// change permissions without updating the cache
db.users().deletePermissionFromUser(publicProject, user, UserRole.ADMIN);
db.users().insertProjectPermissionOnUser(user, UserRole.ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ISSUE_ADMIN, publicProject)).isFalse();
db.users().deletePermissionFromUser(publicProject, user, ADMIN);
db.users().insertProjectPermissionOnUser(user, ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ISSUE_ADMIN, publicProject)).isFalse();
}

@Test
public void hasComponentPermissionByDtoOrUuid_keeps_cache_of_permissions_of_anonymous_user() {
ComponentDto publicProject = db.components().insertPublicProject();
db.users().insertProjectPermissionOnAnyone(UserRole.ADMIN, publicProject);
db.users().insertProjectPermissionOnAnyone(ADMIN, publicProject);

UserSession underTest = newAnonymousSession();

// feed the cache
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ADMIN, publicProject)).isTrue();

// change permissions without updating the cache
db.users().deleteProjectPermissionFromAnyone(publicProject, UserRole.ADMIN);
db.users().insertProjectPermissionOnAnyone(UserRole.ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, UserRole.ISSUE_ADMIN, publicProject)).isFalse();
db.users().deleteProjectPermissionFromAnyone(publicProject, ADMIN);
db.users().insertProjectPermissionOnAnyone(ISSUE_ADMIN, publicProject);
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ADMIN, publicProject)).isTrue();
assertThat(hasComponentPermissionByDtoOrUuid(underTest, ISSUE_ADMIN, publicProject)).isFalse();
}

private boolean hasComponentPermissionByDtoOrUuid(UserSession underTest, String permission, ComponentDto component) {
@@ -456,7 +587,7 @@ public class ServerUserSessionTest {

UserSession underTest = newAnonymousSession();

assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
}

@Test
@@ -464,24 +595,24 @@ public class ServerUserSessionTest {
UserDto user = db.users().insertUser();
ComponentDto publicProject = db.components().insertPublicProject();
ComponentDto privateProject = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, privateProject);
db.users().insertProjectPermissionOnUser(user, ADMIN, privateProject);

UserSession underTest = newUserSession(user);

assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(privateProject);
assertThat(underTest.keepAuthorizedComponents(ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(privateProject);
}

@Test
public void keepAuthorizedComponents_filters_components_with_granted_permissions_for_anonymous() {
ComponentDto publicProject = db.components().insertPublicProject();
ComponentDto privateProject = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnAnyone(UserRole.ISSUE_ADMIN, publicProject);
db.users().insertProjectPermissionOnAnyone(ISSUE_ADMIN, publicProject);

UserSession underTest = newAnonymousSession();

assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(publicProject);
assertThat(underTest.keepAuthorizedComponents(ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
assertThat(underTest.keepAuthorizedComponents(ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(publicProject);
}

@Test
@@ -493,7 +624,7 @@ public class ServerUserSessionTest {

UserSession underTest = newUserSession(root);

assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject)))
assertThat(underTest.keepAuthorizedComponents(ADMIN, Arrays.asList(privateProject, publicProject)))
.containsExactly(privateProject, publicProject);
}

@@ -501,12 +632,12 @@ public class ServerUserSessionTest {
public void keepAuthorizedComponents_on_branches() {
UserDto user = db.users().insertUser();
ComponentDto privateProject = db.components().insertPrivateProject();
db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, privateProject);
db.users().insertProjectPermissionOnUser(user, ADMIN, privateProject);
ComponentDto privateBranchProject = db.components().insertProjectBranch(privateProject);

UserSession underTest = newUserSession(user);

assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, asList(privateProject, privateBranchProject)))
assertThat(underTest.keepAuthorizedComponents(ADMIN, asList(privateProject, privateBranchProject)))
.containsExactlyInAnyOrder(privateProject, privateBranchProject);
}


+ 5
- 2
server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java View File

@@ -22,6 +22,7 @@ package org.sonar.server.user;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.GroupTesting;
import org.sonar.server.exceptions.UnauthorizedException;
@@ -30,10 +31,11 @@ import org.sonar.server.tester.MockUserSession;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.sonar.api.web.UserRole.USER;

public class ThreadLocalUserSessionTest {

private ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();
private final ThreadLocalUserSession threadLocalUserSession = new ThreadLocalUserSession();

@Before
public void setUp() {
@@ -65,6 +67,7 @@ public class ThreadLocalUserSessionTest {
assertThat(threadLocalUserSession.isLoggedIn()).isTrue();
assertThat(threadLocalUserSession.shouldResetPassword()).isTrue();
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getUuid).containsOnly(group.getUuid());
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ComponentDto())).isFalse();
}

@Test
@@ -82,7 +85,7 @@ public class ThreadLocalUserSessionTest {

@Test
public void throw_UnauthorizedException_when_no_session() {
assertThatThrownBy(() -> threadLocalUserSession.get())
assertThatThrownBy(threadLocalUserSession::get)
.isInstanceOf(UnauthorizedException.class);
}


+ 24
- 3
server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java View File

@@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.sonar.api.web.UserRole;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.permission.GlobalPermission;
@@ -40,10 +41,11 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
private static final Set<String> PUBLIC_PERMISSIONS = ImmutableSet.of(UserRole.USER, UserRole.CODEVIEWER); // FIXME to check with Simon

private final Class<T> clazz;
private HashMultimap<String, String> projectUuidByPermission = HashMultimap.create();
private final HashMultimap<String, String> projectUuidByPermission = HashMultimap.create();
private final Set<GlobalPermission> permissions = new HashSet<>();
private Map<String, String> projectUuidByComponentUuid = new HashMap<>();
private Set<String> projectPermissions = new HashSet<>();
private final Map<String, String> projectUuidByComponentUuid = new HashMap<>();
private final Map<String, Set<String>> applicationProjects = new HashMap<>();
private final Set<String> projectPermissions = new HashSet<>();
private boolean systemAdministrator = false;
private boolean resetPassword = false;

@@ -93,6 +95,18 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
return clazz.cast(this);
}

public T registerApplication(ProjectDto application, ProjectDto... appProjects) {
registerProjects(application);
registerProjects(appProjects);

var appProjectsUuid = Arrays.stream(appProjects)
.map(ProjectDto::getUuid)
.collect(Collectors.toSet());
this.applicationProjects.put(application.getUuid(), appProjectsUuid);

return clazz.cast(this);
}

public T registerPortfolios(PortfolioDto... portfolios) {
Arrays.stream(portfolios)
.forEach(portfolio -> {
@@ -156,6 +170,13 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
return projectPermissions.contains(permission) && projectUuidByPermission.get(permission).contains(projectUuid);
}

@Override
protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
return applicationProjects.containsKey(applicationUuid) && applicationProjects.get(applicationUuid)
.stream()
.allMatch(projectUuid -> projectPermissions.contains(permission) && projectUuidByPermission.get(permission).contains(projectUuid));
}

public T setSystemAdministrator(boolean b) {
this.systemAdministrator = b;
return clazz.cast(this);

+ 16
- 0
server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java View File

@@ -193,6 +193,11 @@ public class UserSessionRule implements TestRule, UserSession {
return this;
}

public UserSessionRule registerApplication(ProjectDto application, ProjectDto... appProjects) {
ensureAbstractMockUserSession().registerApplication(application, appProjects);
return this;
}

public UserSessionRule addProjectPermission(String projectPermission, ComponentDto... components) {
ensureAbstractMockUserSession().addProjectPermission(projectPermission, components);
return this;
@@ -251,6 +256,11 @@ public class UserSessionRule implements TestRule, UserSession {
return currentUserSession.hasProjectPermission(permission, project);
}

@Override
public boolean hasChildProjectsPermission(String permission, ComponentDto component) {
return currentUserSession.hasChildProjectsPermission(permission, component);
}

@Override
public boolean hasComponentUuidPermission(String permission, String componentUuid) {
return currentUserSession.hasComponentUuidPermission(permission, componentUuid);
@@ -354,6 +364,12 @@ public class UserSessionRule implements TestRule, UserSession {
return this;
}

@Override
public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
currentUserSession.checkChildProjectsPermission(projectPermission, component);
return this;
}

@Override
public UserSession checkComponentUuidPermission(String permission, String componentUuid) {
currentUserSession.checkComponentUuidPermission(permission, componentUuid);

+ 5
- 0
server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java View File

@@ -121,6 +121,11 @@ public class TestUserSessionFactory implements UserSessionFactory {
throw notImplemented();
}

@Override
protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
throw notImplemented();
}

@Override
public boolean isSystemAdministrator() {
throw notImplemented();

Loading…
Cancel
Save