aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-auth/src
diff options
context:
space:
mode:
authorklaudio-sinani-sonarsource <92299827+klaudio-sinani-sonarsource@users.noreply.github.com>2022-01-11 12:49:37 +0100
committersonartech <sonartech@sonarsource.com>2022-01-20 20:02:43 +0000
commit110208dd8037b2cbf63df1ae17fb523c057d2046 (patch)
treeb1503539eb1e3cbdf51cf729f29dabcc77be7163 /server/sonar-webserver-auth/src
parent5dd324c60877c4445460bde30ab3e3cc25fc7a7f (diff)
downloadsonarqube-110208dd8037b2cbf63df1ae17fb523c057d2046.tar.gz
sonarqube-110208dd8037b2cbf63df1ae17fb523c057d2046.zip
SONAR-15877 Flag portfolios that contain inaccessible components (#5239)
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.java11
-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.java53
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/ThreadLocalUserSession.java5
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/user/UserSession.java2
-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.java1
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ServerUserSessionTest.java102
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/user/ThreadLocalUserSessionTest.java3
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java21
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/UserSessionRule.java5
-rw-r--r--server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/user/TestUserSessionFactory.java3
13 files changed, 217 insertions, 0 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 7bc12dd1ef4..c14babcfdc4 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
@@ -51,6 +51,11 @@ public class SafeModeUserSession extends AbstractUserSession {
return false;
}
+ @Override
+ protected boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid) {
+ 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 9fec50125fc..29914eb211f 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
@@ -131,6 +131,15 @@ public abstract class AbstractUserSession implements UserSession {
}
@Override
+ public final boolean hasPortfolioChildProjectsPermission(String permission, ComponentDto portfolio) {
+ if (isRoot()) {
+ return true;
+ }
+
+ return hasPortfolioChildProjectsPermission(permission, portfolio.uuid());
+ }
+
+ @Override
public final boolean hasComponentUuidPermission(String permission, String componentUuid) {
if (isRoot()) {
return true;
@@ -147,6 +156,8 @@ public abstract class AbstractUserSession implements UserSession {
protected abstract boolean hasChildProjectsPermission(String permission, String applicationUuid);
+ protected abstract boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid);
+
@Override
public final List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
if (isRoot()) {
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 ed2770c08c0..c4a504d7a7b 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
@@ -133,6 +133,11 @@ public final class DoPrivileged {
}
@Override
+ protected boolean hasPortfolioChildProjectsPermission(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 0131f2ab492..7acb465d77d 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
@@ -19,6 +19,7 @@
*/
package org.sonar.server.user;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -27,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
@@ -181,6 +183,22 @@ public class ServerUserSession extends AbstractUserSession {
.allMatch(Boolean::valueOf);
}
+ @Override
+ protected boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid) {
+ if (permissionsByProjectUuid == null) {
+ permissionsByProjectUuid = new HashMap<>();
+ }
+
+ Set<ComponentDto> portfolioHierarchyComponents = resolvePortfolioHierarchyComponents(portfolioUuid);
+
+ Set<String> portfolioHierarchyComponentUuids = portfolioHierarchyComponents.stream().map(ComponentDto::getCopyComponentUuid).collect(Collectors.toSet());
+
+ return portfolioHierarchyComponentUuids
+ .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);
@@ -219,6 +237,41 @@ public class ServerUserSession extends AbstractUserSession {
}
}
+ private List<ComponentDto> getDirectChildComponents(String portfolioUuid) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ return dbClient.componentDao().selectDescendants(dbSession, ComponentTreeQuery.builder()
+ .setBaseUuid(portfolioUuid)
+ .setQualifiers(Arrays.asList(Qualifiers.PROJECT, Qualifiers.SUBVIEW))
+ .setStrategy(Strategy.CHILDREN).build());
+ }
+ }
+
+ private Set<ComponentDto> resolvePortfolioHierarchyComponents(String parentComponentUuid) {
+ Set<ComponentDto> portfolioHierarchyProjects = new HashSet<>();
+
+ resolvePortfolioHierarchyComponents(parentComponentUuid, portfolioHierarchyProjects);
+
+ return portfolioHierarchyProjects;
+ }
+
+ private void resolvePortfolioHierarchyComponents(String parentComponentUuid, Set<ComponentDto> hierarchyChildComponents) {
+ List<ComponentDto> childComponents = getDirectChildComponents(parentComponentUuid);
+
+ if (childComponents.isEmpty()) {
+ return;
+ }
+
+ childComponents.forEach(c -> {
+ if (c.getCopyComponentUuid() != null) {
+ hierarchyChildComponents.add(c);
+ }
+
+ if (Qualifiers.SUBVIEW.equals(c.qualifier())) {
+ resolvePortfolioHierarchyComponents(c.uuid(), hierarchyChildComponents);
+ }
+ });
+ }
+
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 96096ccefe1..3dec0032d96 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
@@ -199,6 +199,11 @@ public class ThreadLocalUserSession implements UserSession {
}
@Override
+ public boolean hasPortfolioChildProjectsPermission(String permission, ComponentDto portfolio) {
+ return get().hasPortfolioChildProjectsPermission(permission, portfolio);
+ }
+
+ @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 61992db77cc..5350e578539 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
@@ -201,6 +201,8 @@ public interface UserSession {
boolean hasChildProjectsPermission(String permission, ProjectDto component);
+ boolean hasPortfolioChildProjectsPermission(String permission, ComponentDto component);
+
/**
* Using {@link #hasComponentPermission(String, ComponentDto)} is recommended
* because it does not have to load project if the referenced component
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 23ea9457b94..80b2055f1d9 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
@@ -47,5 +47,6 @@ public class SafeModeUserSessionTest {
assertThat(underTest.hasPermissionImpl(GlobalPermission.ADMINISTER)).isFalse();
assertThat(underTest.hasProjectUuidPermission(UserRole.USER, "foo")).isFalse();
assertThat(underTest.hasChildProjectsPermission(UserRole.USER, "foo")).isFalse();
+ assertThat(underTest.hasPortfolioChildProjectsPermission(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 17dcd97dddb..6faaecd5fb1 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
@@ -52,6 +52,7 @@ public class DoPrivilegedTest {
assertThat(catcher.userSession.isSystemAdministrator()).isTrue();
assertThat(catcher.userSession.shouldResetPassword()).isFalse();
assertThat(catcher.userSession.hasChildProjectsPermission(USER, new ComponentDto())).isTrue();
+ assertThat(catcher.userSession.hasPortfolioChildProjectsPermission(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 ddebf91b08d..8fdd4ed8d83 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
@@ -45,6 +45,7 @@ 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.component.ComponentTesting.newSubPortfolio;
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;
@@ -396,6 +397,107 @@ public class ServerUserSessionTest {
}
@Test
+ public void test_hasPortfolioChildProjectsPermission_for_logged_in_user() {
+ ComponentDto project1 = db.components().insertPublicProject();
+ ComponentDto project2 = db.components().insertPrivateProject();
+ ComponentDto project3 = db.components().insertPrivateProject();
+ ComponentDto project4 = db.components().insertPrivateProject();
+
+ UserDto user = db.users().insertUser();
+ UserSession session = newUserSession(user);
+
+ ComponentDto portfolio = db.components().insertPrivatePortfolio();
+ ComponentDto subPortfolio = db.components().insertComponent(newSubPortfolio(portfolio));
+
+ // Add public project1 to private portfolio
+ db.components().addPortfolioProject(portfolio, project1);
+ db.components().insertComponent(newProjectCopy(project1, portfolio));
+
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // Add private project2 with USER permissions to private portfolio
+ db.users().insertProjectPermissionOnUser(user, USER, project2);
+ db.components().addPortfolioProject(portfolio, project2);
+ db.components().insertComponent(newProjectCopy(project2, portfolio));
+
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // Add private project4 with USER permissions to sub-portfolio
+ db.users().insertProjectPermissionOnUser(user, USER, project4);
+ db.components().addPortfolioProject(subPortfolio, project4);
+ db.components().insertComponent(newProjectCopy(project4, subPortfolio));
+ db.components().addPortfolioReference(portfolio, subPortfolio.uuid());
+
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // Add private project3 without permissions to private portfolio
+ db.components().addPortfolioProject(portfolio, project3);
+ db.components().insertComponent(newProjectCopy(project3, portfolio));
+
+ assertThat(session.hasChildProjectsPermission(USER, portfolio)).isFalse();
+ }
+
+ @Test
+ public void test_hasPortfolioChildProjectsPermission_for_anonymous_user() {
+ ComponentDto project = db.components().insertPrivateProject();
+
+ db.users().insertPermissionOnAnyone(USER);
+
+ ComponentDto portfolio = db.components().insertPrivatePortfolio();
+
+ db.components().addPortfolioProject(portfolio, project);
+ // add computed project
+ db.components().insertComponent(newProjectCopy(project, portfolio));
+
+ UserSession session = newAnonymousSession();
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isFalse();
+ }
+
+ @Test
+ public void hasPortfolioChildProjectsPermission_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 portfolio = db.components().insertPrivatePortfolio();
+ db.components().addPortfolioProject(portfolio, project);
+ // add computed project
+ db.components().insertComponent(newProjectCopy(project, portfolio));
+
+ UserSession session = newUserSession(user);
+
+ // feed the cache
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // change permissions without updating the cache
+ db.users().deletePermissionFromUser(project, user, USER);
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // cache is refreshed when user logs in again
+ session = newUserSession(user);
+ assertThat(session.hasPortfolioChildProjectsPermission(USER, portfolio)).isFalse();
+ }
+
+ @Test
+ public void hasPortfolioChildProjectsPermission_keeps_cache_of_permissions_of_anonymous_user() {
+ db.users().insertPermissionOnAnyone(USER);
+
+ ComponentDto project = db.components().insertPublicProject();
+ ComponentDto portfolio = db.components().insertPublicPortfolio();
+ db.components().addPortfolioProject(portfolio, project);
+
+ UserSession session = newAnonymousSession();
+
+ // feed the cache
+ assertThat(session.hasChildProjectsPermission(USER, portfolio)).isTrue();
+
+ // change privacy of the project without updating the cache
+ db.getDbClient().componentDao().setPrivateForRootComponentUuidWithoutAudit(db.getSession(), project.uuid(), true);
+ assertThat(session.hasChildProjectsPermission(USER, portfolio)).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();
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 22dc9cc8745..1ef29694dd6 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
@@ -24,6 +24,7 @@ import org.junit.Before;
import org.junit.Test;
import org.sonar.api.resources.Qualifiers;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.portfolio.PortfolioDto;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.GroupTesting;
@@ -72,6 +73,7 @@ public class ThreadLocalUserSessionTest {
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getUuid).containsOnly(group.getUuid());
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ComponentDto())).isFalse();
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ProjectDto())).isFalse();
+ assertThat(threadLocalUserSession.hasPortfolioChildProjectsPermission(USER, new ComponentDto())).isFalse();
assertThat(threadLocalUserSession.hasProjectPermission(USER, new ProjectDto().getUuid())).isFalse();
}
@@ -96,6 +98,7 @@ public class ThreadLocalUserSessionTest {
assertThat(threadLocalUserSession.getGroups()).extracting(GroupDto::getUuid).containsOnly(group.getUuid());
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ComponentDto())).isTrue();
assertThat(threadLocalUserSession.hasChildProjectsPermission(USER, new ProjectDto())).isTrue();
+ assertThat(threadLocalUserSession.hasPortfolioChildProjectsPermission(USER, new ComponentDto())).isTrue();
assertThat(threadLocalUserSession.hasProjectPermission(USER, new ProjectDto().getUuid())).isTrue();
}
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 b563be3bd23..a224c1a11d3 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
@@ -45,6 +45,7 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
private final Set<GlobalPermission> permissions = new HashSet<>();
private final Map<String, String> projectUuidByComponentUuid = new HashMap<>();
private final Map<String, Set<String>> applicationProjects = new HashMap<>();
+ private final Map<String, Set<String>> portfolioProjects = new HashMap<>();
private final Set<String> projectPermissions = new HashSet<>();
private boolean systemAdministrator = false;
private boolean resetPassword = false;
@@ -133,6 +134,19 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
return clazz.cast(this);
}
+ public T registerPortfolioProjects(PortfolioDto portfolio, ProjectDto... portfolioProjects) {
+ registerPortfolios(portfolio);
+ registerProjects(portfolioProjects);
+
+ Set<String> portfolioProjectsUuid = Arrays.stream(portfolioProjects)
+ .map(ProjectDto::getUuid)
+ .collect(Collectors.toSet());
+
+ this.portfolioProjects.put(portfolio.getUuid(), portfolioProjectsUuid);
+
+ return clazz.cast(this);
+ }
+
public T addProjectPermission(String permission, ComponentDto... components) {
Arrays.stream(components).forEach(component -> {
checkArgument(
@@ -189,6 +203,13 @@ public abstract class AbstractMockUserSession<T extends AbstractMockUserSession>
.allMatch(projectUuid -> projectPermissions.contains(permission) && projectUuidByPermission.get(permission).contains(projectUuid));
}
+ @Override
+ protected boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid) {
+ return portfolioProjects.containsKey(portfolioUuid) && portfolioProjects.get(portfolioUuid)
+ .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 7141fea1111..c762d013731 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
@@ -277,6 +277,11 @@ public class UserSessionRule implements TestRule, UserSession {
}
@Override
+ public boolean hasPortfolioChildProjectsPermission(String permission, ComponentDto component) {
+ return currentUserSession.hasPortfolioChildProjectsPermission(permission, component);
+ }
+
+ @Override
public boolean hasComponentUuidPermission(String permission, String componentUuid) {
return currentUserSession.hasComponentUuidPermission(permission, componentUuid);
}
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 cf3a2336dff..67b1e5ba512 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
@@ -127,6 +127,9 @@ public class TestUserSessionFactory implements UserSessionFactory {
}
@Override
+ protected boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid) { throw notImplemented(); }
+
+ @Override
public boolean isSystemAdministrator() {
throw notImplemented();
}