aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-auth/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-webserver-auth/src')
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java5
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java26
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java5
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java38
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java11
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java8
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java1
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java2
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java223
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java7
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java27
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java16
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java5
13 files changed, 320 insertions, 54 deletions
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java
index beacb12c6fd..7bc12dd1ef4 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/SafeModeUserSession.java
@@ -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() {
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java
index 9bf08d2ec8e..3d36461a0e5 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/AbstractUserSession.java
@@ -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";
@@ -106,6 +105,14 @@ public abstract class AbstractUserSession implements UserSession {
}
@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()) {
return true;
@@ -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;
}
@@ -197,6 +207,16 @@ public abstract class AbstractUserSession implements UserSession {
}
@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)) {
throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java
index c1427d07e23..ed2770c08c0 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/DoPrivileged.java
@@ -128,6 +128,11 @@ public final class DoPrivileged {
}
@Override
+ protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
+ return true;
+ }
+
+ @Override
public boolean isSystemAdministrator() {
return true;
}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java
index c0909a7c686..0131f2ab492 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ServerUserSession.java
@@ -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)) {
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
index d9b06b04c3f..8102efd755f 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java
@@ -145,6 +145,12 @@ public class ThreadLocalUserSession implements UserSession {
}
@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);
return this;
@@ -172,6 +178,11 @@ public class ThreadLocalUserSession implements UserSession {
}
@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);
}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java
index 4bd33d3c0a5..fd6e8f71556 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java
@@ -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
@@ -229,6 +231,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}.
*
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java
index e98bdee7a9a..23ea9457b94 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/SafeModeUserSessionTest.java
@@ -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();
}
}
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java
index 3ce36ea935e..17dcd97dddb 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/DoPrivilegedTest.java
@@ -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);
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
index 0d21463a0d5..ddebf91b08d 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java
@@ -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();
@@ -265,13 +319,90 @@ public class ServerUserSessionTest {
}
@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);
}
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java
index 1183f5b0054..c9ccb3c984d 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java
@@ -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);
}
diff --git a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java
index b46b99ffe27..9e040fb3a7c 100644
--- a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java
+++ b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java
@@ -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);
diff --git a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
index b0bdbce1cf2..4b33d0ff77c 100644
--- a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
+++ b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java
@@ -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;
@@ -252,6 +257,11 @@ public class UserSessionRule implements TestRule, UserSession {
}
@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);
}
@@ -355,6 +365,12 @@ public class UserSessionRule implements TestRule, UserSession {
}
@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);
return this;
diff --git a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java
index 97d3fe28b01..cf3a2336dff 100644
--- a/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java
+++ b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java
@@ -122,6 +122,11 @@ public class TestUserSessionFactory implements UserSessionFactory {
}
@Override
+ protected boolean hasChildProjectsPermission(String permission, String applicationUuid) {
+ throw notImplemented();
+ }
+
+ @Override
public boolean isSystemAdministrator() {
throw notImplemented();
}