package org.sonar.ce.user;
import java.util.Collection;
+import java.util.List;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.user.GroupDto;
throw notImplemented();
}
+ @Override
+ public List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ throw notImplemented();
+ }
+
private static RuntimeException notImplemented() {
throw new UnsupportedOperationException(UOE_MESSAGE);
}
*/
package org.sonar.ce.user;
-import com.google.common.base.Predicate;
import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
-import static com.google.common.collect.FluentIterable.from;
-import static java.util.Arrays.asList;
import static org.sonar.test.ExceptionCauseMatcher.hasType;
@RunWith(DataProviderRunner.class)
@DataProvider
public static Object[][] ceUserSessionPublicMethods() {
- List<Method> declaredMethods = from(asList(CeUserSession.class.getDeclaredMethods()))
- .filter(PublicMethodPredicate.INSTANCE).toList();
+ List<Method> declaredMethods = Arrays.stream(CeUserSession.class.getDeclaredMethods())
+ .filter(m -> Modifier.isPublic(m.getModifiers()))
+ .collect(Collectors.toList());
Object[][] res = new Object[declaredMethods.size()][1];
int i = 0;
for (Method declaredMethod : declaredMethods) {
expectedException.expect(InvocationTargetException.class);
expectedException.expectCause(
hasType(UnsupportedOperationException.class)
- .andMessage("UserSession must not be used from within the Compute Engine")
- );
- }
-
- private enum PublicMethodPredicate implements Predicate<Method> {
- INSTANCE;
-
- @Override
- public boolean apply(Method input) {
- return Modifier.isPublic(input.getModifiers());
- }
+ .andMessage("UserSession must not be used from within the Compute Engine"));
}
}
});
}
+ public Set<String> keepAuthorizedProjectUuids(DbSession dbSession, Collection<String> projectUuids, @Nullable Integer userId, String permission) {
+ return executeLargeInputsIntoSet(
+ projectUuids,
+ partition -> {
+ if (userId == null) {
+ return mapper(dbSession).keepAuthorizedProjectUuidsForAnonymous(permission, partition);
+ }
+ return mapper(dbSession).keepAuthorizedProjectUuidsForUser(userId, permission, partition);
+ });
+ }
+
/**
* Keep only authorized user that have the given permission on a given project.
* Please Note that if the permission is 'Anyone' is NOT taking into account by this method.
List<Integer> keepAuthorizedUsersForRoleAndProject(@Param("role") String role, @Param("componentId") long componentId, @Param("userIds") List<Integer> userIds);
+ Set<String> keepAuthorizedProjectUuidsForUser(@Param("userId") int userId, @Param("permission") String permission, @Param("projectUuids") Collection<String> projectUuids);
+
+ Set<String> keepAuthorizedProjectUuidsForAnonymous(@Param("permission") String permission, @Param("projectUuids") Collection<String> projectUuids);
+
Set<String> selectProjectPermissions(@Param("projectUuid") String projectUuid, @Param("userId") long userId);
Set<String> selectProjectPermissionsOfAnonymous(@Param("projectUuid") String projectUuid);
<sql id="sqlSelectPublicProjectsIfRole">
select
- p.id
+ p.id
from
- projects p
+ projects p
where
- <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">
- p.id=#{element,jdbcType=BIGINT}
- </foreach>
- and p.private = ${_false}
- and #{role,jdbcType=VARCHAR} in ('user','codeviewer')
+ <foreach collection="componentIds" open="(" close=")" item="element" index="index" separator=" or ">
+ p.id=#{element,jdbcType=BIGINT}
+ </foreach>
+ and p.private = ${_false}
+ and #{role,jdbcType=VARCHAR} in ('user','codeviewer')
</sql>
+ <select id="keepAuthorizedProjectUuidsForUser" parameterType="map" resultType="String">
+ select p.uuid
+ from projects p
+ inner join group_roles gr on p.id = gr.resource_id
+ where
+ gr.role = #{permission,jdbcType=VARCHAR}
+ and (gr.group_id is null or exists (
+ select 1 from groups_users gu
+ where
+ gu.user_id = #{userId, jdbcType=INTEGER}
+ and gr.group_id = gu.group_id)
+ )
+ and p.uuid in <foreach collection="projectUuids" open="(" close=")" item="projectUuid" index="index" separator=",">#{projectUuid,jdbcType=VARCHAR}</foreach>
+
+ union
+
+ select p.uuid
+ from projects p
+ inner join user_roles ur on p.id = ur.resource_id
+ where
+ ur.role=#{permission,jdbcType=VARCHAR}
+ and ur.user_id=#{userId,jdbcType=INTEGER}
+ and p.uuid in <foreach collection="projectUuids" open="(" close=")" item="projectUuid" index="index" separator=",">#{projectUuid,jdbcType=VARCHAR}</foreach>
+
+ <if test="permission == 'user' or permission == 'codeviewer'">
+ union
+
+ select p.uuid
+ from projects p
+ where
+ p.uuid in <foreach collection="projectUuids" open="(" close=")" item="projectUuid" index="index" separator=",">#{projectUuid,jdbcType=VARCHAR}</foreach>
+ and p.private = ${_false}
+ </if>
+ </select>
+
+ <select id="keepAuthorizedProjectUuidsForAnonymous" parameterType="map" resultType="String">
+ select p.uuid
+ from projects p
+ inner join group_roles gr on p.id = gr.resource_id
+ where
+ gr.role=#{permission,jdbcType=VARCHAR}
+ and gr.group_id is null
+ and p.uuid in <foreach collection="projectUuids" open="(" close=")" item="projectUuid" index="index" separator=",">#{projectUuid,jdbcType=VARCHAR}</foreach>
+
+ <if test="permission == 'user' or permission == 'codeviewer'">
+ union
+
+ select p.uuid
+ from projects p
+ where
+ p.uuid in <foreach collection="projectUuids" open="(" close=")" item="projectUuid" index="index" separator=",">#{projectUuid,jdbcType=VARCHAR}</foreach>
+ and p.private = ${_false}
+ </if>
+ </select>
+
<select id="keepAuthorizedUsersForRoleAndProject" parameterType="map" resultType="int">
select
gu.user_id
.setPath(null)
.setLanguage(null)
.setEnabled(true)
- .setPrivate(isPrivate);
+ .setPrivate(isPrivate);
}
public static ComponentDto newView(OrganizationDto organizationDto) {
assertThat(underTest.selectProjectPermissions(dbSession, project.uuid(), user.getId())).containsOnly("p1", "p2", "p3");
}
+
+ @Test
+ public void keepAuthorizedProjectUuids_filters_projects_authorized_to_logged_in_user_by_direct_permission() {
+ ComponentDto privateProject = db.components().insertPrivateProject(organization);
+ ComponentDto publicProject = db.components().insertPublicProject(organization);
+ UserDto user = db.users().insertUser();
+ db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, privateProject);
+
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(privateProject.uuid(), publicProject.uuid()), user.getId(), UserRole.ADMIN))
+ .containsOnly(privateProject.uuid());
+ // user does not have the permission "issueadmin"
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(privateProject.uuid(), publicProject.uuid()), user.getId(), UserRole.ISSUE_ADMIN))
+ .isEmpty();
+ }
+
+ @Test
+ public void keepAuthorizedProjectUuids_filters_projects_authorized_to_logged_in_user_by_group_permission() {
+ ComponentDto privateProject = db.components().insertPrivateProject(organization);
+ ComponentDto publicProject = db.components().insertPublicProject(organization);
+ UserDto user = db.users().insertUser();
+ GroupDto group = db.users().insertGroup(organization);
+ db.users().insertMember(group, user);
+ db.users().insertProjectPermissionOnGroup(group, UserRole.ADMIN, privateProject);
+
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(privateProject.uuid(), publicProject.uuid()), user.getId(), UserRole.ADMIN))
+ .containsOnly(privateProject.uuid());
+ // user does not have the permission "issueadmin"
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(privateProject.uuid(), publicProject.uuid()), user.getId(), UserRole.ISSUE_ADMIN))
+ .isEmpty();
+ }
+
+ @Test
+ public void keepAuthorizedProjectUuids_returns_empty_list_if_input_is_empty() {
+ ComponentDto publicProject = db.components().insertPublicProject(organization);
+ UserDto user = db.users().insertUser();
+
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, Collections.emptySet(), user.getId(), UserRole.USER))
+ .isEmpty();
+
+ // projects do not exist
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet("does_not_exist"), user.getId(), UserRole.USER))
+ .isEmpty();
+ }
+
+ @Test
+ public void keepAuthorizedProjectUuids_returns_empty_list_if_input_does_not_reference_existing_projects() {
+ ComponentDto publicProject = db.components().insertPublicProject(organization);
+ UserDto user = db.users().insertUser();
+
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet("does_not_exist"), user.getId(), UserRole.USER))
+ .isEmpty();
+ }
+
+ @Test
+ public void keepAuthorizedProjectUuids_returns_public_projects_if_permission_USER_or_CODEVIEWER() {
+ ComponentDto publicProject = db.components().insertPublicProject(organization);
+ UserDto user = db.users().insertUser();
+
+ // logged-in user
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), user.getId(), UserRole.CODEVIEWER))
+ .containsOnly(publicProject.uuid());
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), user.getId(), UserRole.USER))
+ .containsOnly(publicProject.uuid());
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), user.getId(), UserRole.ADMIN))
+ .isEmpty();
+
+ // anonymous
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), null, UserRole.CODEVIEWER))
+ .containsOnly(publicProject.uuid());
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), null, UserRole.USER))
+ .containsOnly(publicProject.uuid());
+ assertThat(underTest.keepAuthorizedProjectUuids(dbSession, newHashSet(publicProject.uuid()), null, UserRole.ADMIN))
+ .isEmpty();
+
+ }
}
*/
package org.sonar.server.user;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.Optional;
+import org.sonar.core.permission.ProjectPermissions;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.exceptions.ForbiddenException;
return INSUFFICIENT_PRIVILEGES_EXCEPTION;
}
+ @Override
+ public final List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ if (isRoot()) {
+ return new ArrayList<>(components);
+ }
+ return doKeepAuthorizedComponents(permission, components);
+ }
+
+ /**
+ * Naive implementation, to be overridden if needed
+ */
+ protected List<ComponentDto> doKeepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ boolean allowPublicComponent = ProjectPermissions.PUBLIC_PERMISSIONS.contains(permission);
+ return components.stream()
+ .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasProjectUuidPermission(permission, c.projectUuid()))
+ .collect(MoreCollectors.toList());
+ }
+
@Override
public final UserSession checkIsSystemAdministrator() {
if (!isSystemAdministrator()) {
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
-import org.sonar.db.user.GroupDto;
import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.user.GroupDto;
/**
* Allow code to be executed with the highest privileges possible, as if executed by a {@link OrganizationPermission#ADMINISTER} account.
protected boolean hasProjectUuidPermission(String permission, String projectUuid) {
return true;
}
-
+
@Override
public boolean isSystemAdministrator() {
return true;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
return dbClient.authorizationDao().selectProjectPermissionsOfAnonymous(dbSession, projectUuid);
}
+ @Override
+ protected List<ComponentDto> doKeepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ Set<String> projectUuids = components.stream()
+ .map(ComponentDto::projectUuid)
+ .collect(MoreCollectors.toSet(components.size()));
+ Set<String> authorizedProjectUuids = dbClient.authorizationDao().keepAuthorizedProjectUuids(dbSession, projectUuids, getUserId(), permission);
+
+ return components.stream()
+ .filter(c -> authorizedProjectUuids.contains(c.projectUuid()))
+ .collect(MoreCollectors.toList(components.size()));
+ }
+ }
+
@Override
public boolean isSystemAdministrator() {
return isSystemAdministratorSupplier.get();
package org.sonar.server.user;
import java.util.Collection;
+import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
public boolean hasPermission(OrganizationPermission permission, OrganizationDto organization) {
return get().hasPermission(permission, organization);
}
+
+ @Override
+ public List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ return get().keepAuthorizedComponents(permission, components);
+ }
}
package org.sonar.server.user;
import java.util.Collection;
+import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.user.GroupDto;
import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.user.GroupDto;
public interface UserSession {
@Deprecated
boolean hasComponentUuidPermission(String permission, String componentUuid);
+ /**
+ * Return the subset of specified components which the user has granted permission.
+ * An empty list is returned if input is empty or if no components are allowed to be
+ * accessed.
+ * If the input is ordered, then the returned components are in the same order.
+ * The duplicated components are returned duplicated too.
+ */
+ List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components);
+
/**
* Ensures that {@link #hasComponentPermission(String, ComponentDto)} is {@code true},
* otherwise throws a {@link org.sonar.server.exceptions.ForbiddenException}.
import com.google.common.base.Preconditions;
import java.util.Collection;
+import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.junit.rules.TestRule;
return currentUserSession.hasComponentUuidPermission(permission, componentUuid);
}
+ @Override
+ public List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
+ return currentUserSession.keepAuthorizedComponents(permission, components);
+ }
+
@Override
@CheckForNull
public String getLogin() {
*/
package org.sonar.server.user;
+import java.util.Arrays;
import java.util.Random;
import javax.annotation.Nullable;
import org.junit.Before;
return new Random().nextBoolean() ? underTest.hasComponentPermission(permission, component) : underTest.hasComponentUuidPermission(permission, component.uuid());
}
+ @Test
+ public void keepAuthorizedComponents_returns_empty_list_if_no_permissions_are_granted() {
+ UserSession underTest = newAnonymousSession();
+
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
+ }
+
+ @Test
+ public void keepAuthorizedComponents_filters_components_with_granted_permissions_for_logged_in_user() {
+ UserSession underTest = newUserSession(user);
+ db.users().insertProjectPermissionOnUser(user, UserRole.ADMIN, privateProject);
+
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(privateProject);
+ }
+
+ @Test
+ public void keepAuthorizedComponents_filters_components_with_granted_permissions_for_anonymous() {
+ UserSession underTest = newAnonymousSession();
+ db.users().insertProjectPermissionOnAnyone(UserRole.ISSUE_ADMIN, publicProject);
+
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject))).isEmpty();
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ISSUE_ADMIN, Arrays.asList(privateProject, publicProject))).containsExactly(publicProject);
+ }
+
+ @Test
+ public void keepAuthorizedComponents_returns_all_specified_components_if_root() {
+ user = db.users().makeRoot(user);
+ UserSession underTest = newUserSession(user);
+
+ assertThat(underTest.keepAuthorizedComponents(UserRole.ADMIN, Arrays.asList(privateProject, publicProject)))
+ .containsExactly(privateProject, publicProject);
+ }
+
@Test
public void isSystemAdministrator_returns_true_if_org_feature_is_enabled_and_user_is_root() {
organizationFlags.setEnabled(true);