From ace9a50d55d831ec71b7db421aa04d1198392c6c Mon Sep 17 00:00:00 2001 From: Duarte Meneses Date: Wed, 23 Oct 2019 15:33:39 -0500 Subject: SONAR-12689 Separate storage of projects/apps from their components and branches --- .../org/sonar/server/user/AbstractUserSession.java | 35 ++++++++++++++++++++++ .../org/sonar/server/user/ServerUserSession.java | 3 ++ .../sonar/server/user/ThreadLocalUserSession.java | 15 ++++++++++ .../java/org/sonar/server/user/UserSession.java | 13 ++++++-- .../server/tester/AbstractMockUserSession.java | 28 +++++++++++++++++ .../org/sonar/server/tester/UserSessionRule.java | 19 ++++++++++++ 6 files changed, 111 insertions(+), 2 deletions(-) (limited to 'server/sonar-webserver-auth') 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 5689072bc1c..8ba3e53e1fc 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 @@ -32,6 +32,7 @@ import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.project.ProjectDto; import org.sonar.db.user.UserDto; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; @@ -97,6 +98,14 @@ public abstract class AbstractUserSession implements UserSession { return hasProjectUuidPermission(permission, projectUuid); } + @Override + public final boolean hasProjectPermission(String permission, ProjectDto project) { + if (isRoot()) { + return true; + } + return hasProjectUuidPermission(permission, project.getUuid()); + } + @Override public final boolean hasComponentUuidPermission(String permission, String componentUuid) { if (isRoot()) { @@ -127,6 +136,24 @@ public abstract class AbstractUserSession implements UserSession { return doKeepAuthorizedComponents(permission, components); } + @Override + public List keepAuthorizedProjects(String permission, Collection projects) { + if (isRoot()) { + return new ArrayList<>(projects); + } + return doKeepAuthorizedProjects(permission, projects); + } + + /** + * Naive implementation, to be overridden if needed + */ + protected List doKeepAuthorizedProjects(String permission, Collection projects) { + boolean allowPublicComponent = PUBLIC_PERMISSIONS.contains(permission); + return projects.stream() + .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasProjectPermission(permission, c)) + .collect(MoreCollectors.toList()); + } + /** * Naive implementation, to be overridden if needed */ @@ -174,6 +201,14 @@ public abstract class AbstractUserSession implements UserSession { return this; } + @Override public UserSession checkProjectPermission(String projectPermission, ProjectDto project) { + if (isRoot() || hasProjectUuidPermission(projectPermission, project.getUuid())) { + return this; + } + + throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE); + } + @Override public final UserSession checkComponentUuidPermission(String permission, String componentUuid) { if (!hasComponentUuidPermission(permission, componentUuid)) { 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 1fbbe17116f..c62055870d6 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 @@ -183,6 +183,9 @@ public class ServerUserSession extends AbstractUserSession { return permissions.contains(permission); } + /** + * Also applies to views + */ private Set loadProjectPermissions(String projectUuid) { try (DbSession dbSession = dbClient.openSession(false)) { Optional component = dbClient.componentDao().selectByUuid(dbSession, projectUuid); 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 771a434cc23..d310e17dd1c 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 @@ -26,6 +26,7 @@ import javax.annotation.CheckForNull; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.project.ProjectDto; import org.sonar.db.user.GroupDto; import org.sonar.server.exceptions.UnauthorizedException; @@ -133,6 +134,12 @@ public class ThreadLocalUserSession implements UserSession { return this; } + @Override + public UserSession checkProjectPermission(String projectPermission, ProjectDto project) { + get().checkProjectPermission(projectPermission, project); + return this; + } + @Override public UserSession checkComponentUuidPermission(String permission, String componentUuid) { get().checkComponentUuidPermission(permission, componentUuid); @@ -155,6 +162,10 @@ public class ThreadLocalUserSession implements UserSession { return get().hasComponentPermission(permission, component); } + @Override public boolean hasProjectPermission(String permission, ProjectDto project) { + return get().hasProjectPermission(permission, project); + } + @Override public boolean hasComponentUuidPermission(String permission, String componentUuid) { return get().hasComponentUuidPermission(permission, componentUuid); @@ -176,6 +187,10 @@ public class ThreadLocalUserSession implements UserSession { return get().keepAuthorizedComponents(permission, components); } + @Override public List keepAuthorizedProjects(String permission, Collection projects) { + return get().keepAuthorizedProjects(permission, projects); + } + @Override public boolean hasMembership(OrganizationDto organizationDto) { return get().hasMembership(organizationDto); 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 7fb907e4b9b..6c160f26a71 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 @@ -29,6 +29,7 @@ import javax.annotation.concurrent.Immutable; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.project.ProjectDto; import org.sonar.db.user.GroupDto; import static java.util.Objects.requireNonNull; @@ -98,8 +99,7 @@ public interface UserSession { */ Optional getIdentityProvider(); - @Immutable - final class ExternalIdentity { + @Immutable final class ExternalIdentity { private final String id; private final String login; @@ -206,6 +206,8 @@ public interface UserSession { */ boolean hasComponentPermission(String permission, ComponentDto component); + boolean hasProjectPermission(String permission, ProjectDto project); + /** * Using {@link #hasComponentPermission(String, ComponentDto)} is recommended * because it does not have to load project if the referenced component @@ -225,12 +227,19 @@ public interface UserSession { */ List keepAuthorizedComponents(String permission, Collection components); + List keepAuthorizedProjects(String permission, Collection projects); /** * Ensures that {@link #hasComponentPermission(String, ComponentDto)} is {@code true}, * otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}. */ UserSession checkComponentPermission(String projectPermission, ComponentDto component); + /** + * Ensures that {@link #hasProjectPermission(String, ProjectDto)} is {@code true}, + * otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}. + */ + UserSession checkProjectPermission(String projectPermission, ProjectDto 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/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java b/server/sonar-webserver-auth/src/testFixtures/java/org/sonar/server/tester/AbstractMockUserSession.java index 22e9e3ab9c5..00968fe8362 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 @@ -31,6 +31,7 @@ import org.sonar.api.web.UserRole; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.project.ProjectDto; import org.sonar.server.user.AbstractUserSession; import static com.google.common.base.Preconditions.checkArgument; @@ -78,6 +79,20 @@ public abstract class AbstractMockUserSession return clazz.cast(this); } + public T registerProjects(ProjectDto... projects) { + Arrays.stream(projects) + .forEach(project -> { + if (!project.isPrivate()) { + this.projectUuidByPermission.put(UserRole.USER, project.getUuid()); + this.projectUuidByPermission.put(UserRole.CODEVIEWER, project.getUuid()); + this.projectPermissions.add(UserRole.USER); + this.projectPermissions.add(UserRole.CODEVIEWER); + } + this.projectUuidByComponentUuid.put(project.getUuid(), project.getUuid()); + }); + return clazz.cast(this); + } + public T addProjectPermission(String permission, ComponentDto... components) { Arrays.stream(components).forEach(component -> { checkArgument( @@ -91,6 +106,19 @@ public abstract class AbstractMockUserSession return clazz.cast(this); } + public T addProjectPermission(String permission, ProjectDto... projects) { + Arrays.stream(projects).forEach(component -> { + checkArgument( + component.isPrivate() || !PUBLIC_PERMISSIONS.contains(permission), + "public component %s can't be granted public permission %s", component.getUuid(), permission); + }); + registerProjects(projects); + this.projectPermissions.add(permission); + Arrays.stream(projects) + .forEach(component -> this.projectUuidByPermission.put(permission, component.getUuid())); + return clazz.cast(this); + } + @Override protected Optional componentUuidToProjectUuid(String componentUuid) { return Optional.ofNullable(projectUuidByComponentUuid.get(componentUuid)); 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 c72088db1ae..e243f3870d4 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 @@ -31,6 +31,7 @@ import org.junit.runners.model.Statement; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.permission.OrganizationPermission; +import org.sonar.db.project.ProjectDto; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.user.UserSession; @@ -192,6 +193,11 @@ public class UserSessionRule implements TestRule, UserSession { return this; } + public UserSessionRule addProjectPermission(String projectPermission, ProjectDto projectDto) { + ensureAbstractMockUserSession().addProjectPermission(projectPermission, projectDto); + return this; + } + public UserSessionRule addPermission(OrganizationPermission permission, String organizationUuid) { ensureAbstractMockUserSession().addPermission(permission, organizationUuid); return this; @@ -240,6 +246,10 @@ public class UserSessionRule implements TestRule, UserSession { return currentUserSession.hasComponentPermission(permission, component); } + @Override public boolean hasProjectPermission(String permission, ProjectDto project) { + return currentUserSession.hasProjectPermission(permission, project); + } + @Override public boolean hasComponentUuidPermission(String permission, String componentUuid) { return currentUserSession.hasComponentUuidPermission(permission, componentUuid); @@ -250,6 +260,10 @@ public class UserSessionRule implements TestRule, UserSession { return currentUserSession.keepAuthorizedComponents(permission, components); } + @Override public List keepAuthorizedProjects(String permission, Collection projects) { + return currentUserSession.keepAuthorizedProjects(permission, projects); + } + @Override @CheckForNull public String getLogin() { @@ -338,6 +352,11 @@ public class UserSessionRule implements TestRule, UserSession { return this; } + @Override public UserSession checkProjectPermission(String projectPermission, ProjectDto project) { + currentUserSession.checkProjectPermission(projectPermission, project); + return this; + } + @Override public UserSession checkComponentUuidPermission(String permission, String componentUuid) { currentUserSession.checkComponentUuidPermission(permission, componentUuid); -- cgit v1.2.3