--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.project.ws;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.permission.GroupPermissionDto;
+import org.sonar.db.permission.UserPermissionDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.permission.index.PermissionIndexer;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Collections.singletonList;
+import static org.sonar.core.permission.ProjectPermissions.PUBLIC_PERMISSIONS;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+
+public class UpdateVisibilityAction implements ProjectsWsAction {
+ private static final String ACTION = "update_visibility";
+ private static final String PARAM_VISIBILITY = "visibility";
+ private static final String PUBLIC_VISIBILITY = "public";
+ private static final String PRIVATE_VISIBILITY = "private";
+ private static final Set<String> ALLOWED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.VIEW);
+
+ private final DbClient dbClient;
+ private final ComponentFinder componentFinder;
+ private final UserSession userSession;
+ private final PermissionIndexer permissionIndexer;
+
+ public UpdateVisibilityAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession,
+ PermissionIndexer permissionIndexer) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ this.permissionIndexer = permissionIndexer;
+ }
+
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION)
+ .setDescription("Updates visibility of a project or a view.<br/>" +
+ "Requires 'Project administer' permission on the specified project or view")
+ .setSince("6.4")
+ .setPost(true)
+ .setHandler(this);
+
+ action.createParam(PARAM_PROJECT)
+ .setDescription("Project or view key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+
+ action.createParam(PARAM_VISIBILITY)
+ .setDescription("new visibility of the project or view")
+ .setPossibleValues(PUBLIC_VISIBILITY, PRIVATE_VISIBILITY)
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ boolean changeToPrivate = PRIVATE_VISIBILITY.equals(request.mandatoryParam(PARAM_VISIBILITY));
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto component = componentFinder.getByKey(dbSession, projectKey);
+ checkRequest(isRoot(component), "Component must either be a project or a view");
+ checkRequest(!changeToPrivate || !Qualifiers.VIEW.equals(component.qualifier()), "Views can't be made private");
+ userSession.checkComponentPermission(UserRole.ADMIN, component);
+ checkRequest(noPendingTask(dbSession, component), "Component visibility can't be changed as long as it has background task(s) pending or in progress");
+
+ if (changeToPrivate != component.isPrivate()) {
+ dbClient.componentDao().setPrivateForRootComponentUuid(dbSession, component.uuid(), changeToPrivate);
+ if (changeToPrivate) {
+ updatePermissionsToPrivate(dbSession, component);
+ } else {
+ updatePermissionsToPublic(dbSession, component);
+ }
+ dbSession.commit();
+ permissionIndexer.indexProjectsByUuids(dbSession, singletonList(component.uuid()));
+ }
+ }
+ }
+
+ private static boolean isRoot(ComponentDto component) {
+ return Scopes.PROJECT.equals(component.scope()) && ALLOWED_QUALIFIERS.contains(component.qualifier());
+ }
+
+ private boolean noPendingTask(DbSession dbSession, ComponentDto rootComponent) {
+ return dbClient.ceQueueDao().selectByComponentUuid(dbSession, rootComponent.uuid()).isEmpty();
+ }
+
+ private void updatePermissionsToPrivate(DbSession dbSession, ComponentDto component) {
+ // delete project permissions for group AnyOne
+ dbClient.groupPermissionDao().deleteByRootComponentIdAndGroupId(dbSession, component.getId(), null);
+ // grant UserRole.CODEVIEWER and UserRole.USER
+ PUBLIC_PERMISSIONS.forEach(permission -> {
+ dbClient.groupPermissionDao().selectGroupIdsWithPermissionOnProjectBut(dbSession, component.getId(), permission)
+ .forEach(groupId -> insertProjectPermissionOnGroup(dbSession, component, permission, groupId));
+ dbClient.userPermissionDao().selectUserIdsWithPermissionOnProjectBut(dbSession, component.getId(), permission)
+ .forEach(userId -> insertProjectPermissionOnUser(dbSession, component, permission, userId));
+ });
+ }
+
+ private void insertProjectPermissionOnUser(DbSession dbSession, ComponentDto component, String permission, Integer userId) {
+ dbClient.userPermissionDao().insert(dbSession, new UserPermissionDto(component.getOrganizationUuid(), permission, userId, component.getId()));
+ }
+
+ private void insertProjectPermissionOnGroup(DbSession dbSession, ComponentDto component, String permission, Integer groupId) {
+ dbClient.groupPermissionDao().insert(dbSession, new GroupPermissionDto()
+ .setOrganizationUuid(component.getOrganizationUuid())
+ .setResourceId(component.getId())
+ .setGroupId(groupId)
+ .setRole(permission));
+ }
+
+ private void updatePermissionsToPublic(DbSession dbSession, ComponentDto component) {
+ PUBLIC_PERMISSIONS.forEach(permission -> {
+ // delete project group permission for UserRole.CODEVIEWER and UserRole.USER
+ dbClient.groupPermissionDao().deleteByRootComponentIdAndPermission(dbSession, component.getId(), permission);
+ // delete project user permission for UserRole.CODEVIEWER and UserRole.USER
+ dbClient.userPermissionDao().deleteProjectPermissionOfAnyUser(dbSession, component.getId(), permission);
+ });
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.project.ws;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.permission.ProjectPermissions;
+import org.sonar.core.util.stream.MoreCollectors;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.ce.CeQueueDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.permission.GroupPermissionDto;
+import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.db.permission.UserPermissionDto;
+import org.sonar.db.user.GroupDto;
+import org.sonar.db.user.UserDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.permission.index.PermissionIndexer;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.lang.String.format;
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class UpdateVisibilityActionTest {
+ private static final String PARAM_VISIBILITY = "visibility";
+ private static final String PARAM_PROJECT = "project";
+ private static final String PUBLIC = "public";
+ private static final String PRIVATE = "private";
+ private static final Set<String> ORGANIZATION_PERMISSIONS_NAME_SET = stream(OrganizationPermission.values()).map(OrganizationPermission::getKey)
+ .collect(MoreCollectors.toSet(OrganizationPermission.values().length));
+ private static final Set<String> PROJECT_PERMISSIONS_BUT_USER_AND_CODEVIEWER = ProjectPermissions.ALL.stream()
+ .filter(perm -> !perm.equals(UserRole.USER) && !perm.equals(UserRole.CODEVIEWER)).collect(MoreCollectors.toSet(ProjectPermissions.ALL.size() - 2));
+
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ @Rule
+ public UserSessionRule userSessionRule = UserSessionRule.standalone()
+ .logIn();
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private DbClient dbClient = dbTester.getDbClient();
+ private DbSession dbSession = dbTester.getSession();
+ private PermissionIndexer permissionIndexer = mock(PermissionIndexer.class);
+
+ private UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, new ComponentFinder(dbClient), userSessionRule, permissionIndexer);
+ private WsActionTester actionTester = new WsActionTester(underTest);
+
+ private final Random random = new Random();
+ private final String randomVisibility = random.nextBoolean() ? PUBLIC : PRIVATE;
+ private final TestRequest request = actionTester.newRequest();
+ private final OrganizationDto organization = dbTester.organizations().insert();
+
+ @Test
+ public void execute_fails_if_user_is_not_logged_in() {
+ userSessionRule.anonymous();
+
+ expectedException.expect(UnauthorizedException.class);
+ expectedException.expectMessage("Authentication is required");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_IAE_when_project_parameter_is_not_provided() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_IAE_when_project_parameter_is_not_empty() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_IAE_when_parameter_visibility_is_not_provided() {
+ request.setParam(PARAM_PROJECT, "foo");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'visibility' parameter is missing");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_IAE_when_parameter_visibility_is_empty() {
+ request.setParam(PARAM_PROJECT, "foo")
+ .setParam(PARAM_VISIBILITY, "");
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Value of parameter '" + PARAM_VISIBILITY + "' () must be one of: [public, private]");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_IAE_when_value_of_parameter_visibility_is_not_lowercase() {
+ request.setParam(PARAM_PROJECT, "foo");
+
+ Stream.of("PUBLIC", "pUBliC", "PRIVATE", "PrIVAtE")
+ .forEach(visibility -> {
+ try {
+ request.setParam(PARAM_VISIBILITY, visibility).execute();
+ fail("An exception should have been raised");
+ } catch (IllegalArgumentException e) {
+ assertThat(e.getMessage()).isEqualTo(format("Value of parameter '%s' (%s) must be one of: %s", PARAM_VISIBILITY, visibility, "[public, private]"));
+ }
+ });
+ }
+
+ @Test
+ public void execute_fails_with_NotFoundException_when_specified_component_does_not_exist() {
+ request.setParam(PARAM_PROJECT, "foo")
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Component key 'foo' not found");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_fails_with_BadRequestException_if_specified_component_is_neither_a_project_nor_a_view() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ ComponentDto module = ComponentTesting.newModuleDto(project);
+ ComponentDto dir = ComponentTesting.newDirectory(project, "path");
+ ComponentDto file = ComponentTesting.newFileDto(project);
+ dbTester.components().insertComponents(module, dir, file);
+ ComponentDto view = dbTester.components().insertView(organization);
+ ComponentDto subView = ComponentTesting.newSubView(view);
+ ComponentDto projectCopy = ComponentTesting.newProjectCopy("foo", project, subView);
+ dbTester.components().insertComponents(subView, projectCopy);
+
+ Stream.of(module, dir, file, subView, projectCopy)
+ .forEach(nonRootComponent -> {
+ request.setParam(PARAM_PROJECT, nonRootComponent.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+
+ try {
+ request.execute();
+ fail("a BadRequestException should have been raised");
+ } catch (BadRequestException e) {
+ assertThat(e.getMessage()).isEqualTo("Component must either be a project or a view");
+ }
+ });
+ }
+
+ @Test
+ public void execute_throws_ForbiddenException_if_user_has_no_permission_on_specified_component() {
+ ComponentDto project = dbTester.components().insertPrivateProject(organization);
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+
+ expectInsufficientPrivilegeException();
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_throws_ForbiddenException_if_user_has_all_permissions_but_ADMIN_on_specified_component() {
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+ userSessionRule.addProjectPermission(UserRole.ISSUE_ADMIN, project);
+ Arrays.stream(OrganizationPermission.values())
+ .forEach(perm -> userSessionRule.addPermission(perm, organization));
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+
+ expectInsufficientPrivilegeException();
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_throws_BadRequestException_if_specified_component_has_pending_tasks() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ IntStream.range(0, 1 + Math.abs(random.nextInt(5)))
+ .forEach(i -> insertPendingTask(project));
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Component visibility can't be changed as long as it has background task(s) pending or in progress");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_throws_BadRequestException_if_specified_component_has_in_progress_tasks() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ IntStream.range(0, 1 + Math.abs(random.nextInt(5)))
+ .forEach(i -> insertInProgressTask(project));
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, randomVisibility);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Component visibility can't be changed as long as it has background task(s) pending or in progress");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_changes_private_flag_of_specified_project_and_all_children_to_specified_new_visibility() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ boolean initiallyPrivate = project.isPrivate();
+ ComponentDto module = ComponentTesting.newModuleDto(project);
+ ComponentDto dir = ComponentTesting.newDirectory(project, "path");
+ ComponentDto file = ComponentTesting.newFileDto(project);
+ dbTester.components().insertComponents(module, dir, file);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, initiallyPrivate ? PUBLIC : PRIVATE)
+ .execute();
+
+ assertThat(isPrivateInDb(project)).isEqualTo(!initiallyPrivate);
+ assertThat(isPrivateInDb(module)).isEqualTo(!initiallyPrivate);
+ assertThat(isPrivateInDb(dir)).isEqualTo(!initiallyPrivate);
+ assertThat(isPrivateInDb(file)).isEqualTo(!initiallyPrivate);
+ }
+
+ @Test
+ public void execute_has_no_effect_when_changing_a_view_to_public() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ ComponentDto view = dbTester.components().insertView(organization);
+ ComponentDto subView = ComponentTesting.newSubView(view);
+ ComponentDto projectCopy = ComponentTesting.newProjectCopy("foo", project, subView);
+ dbTester.components().insertComponents(subView, projectCopy);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, view);
+
+ request.setParam(PARAM_PROJECT, view.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+
+ assertThat(isPrivateInDb(view)).isEqualTo(false);
+ assertThat(isPrivateInDb(subView)).isEqualTo(false);
+ assertThat(isPrivateInDb(projectCopy)).isEqualTo(false);
+ }
+
+ @Test
+ public void execute_fails_with_BadRequestException_when_changing_a_view_to_private() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ ComponentDto view = dbTester.components().insertView(organization);
+ ComponentDto subView = ComponentTesting.newSubView(view);
+ ComponentDto projectCopy = ComponentTesting.newProjectCopy("foo", project, subView);
+ dbTester.components().insertComponents(subView, projectCopy);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, view);
+ TestRequest request = this.request.setParam(PARAM_PROJECT, view.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE);
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Views can't be made private");
+
+ request.execute();
+ }
+
+ @Test
+ public void execute_has_no_effect_if_specified_project_already_has_specified_visibility() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ boolean initiallyPrivate = project.isPrivate();
+ ComponentDto module = ComponentTesting.newModuleDto(project)
+ .setPrivate(initiallyPrivate);
+ ComponentDto dir = ComponentTesting.newDirectory(project, "path")
+ // child is inconsistent with root (should not occur) and won't be fixed
+ .setPrivate(!initiallyPrivate);
+ ComponentDto file = ComponentTesting.newFileDto(project)
+ .setPrivate(initiallyPrivate);
+ dbTester.components().insertComponents(module, dir, file);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, initiallyPrivate ? PRIVATE : PUBLIC)
+ .execute();
+
+ assertThat(isPrivateInDb(project)).isEqualTo(initiallyPrivate);
+ assertThat(isPrivateInDb(module)).isEqualTo(initiallyPrivate);
+ assertThat(isPrivateInDb(dir)).isEqualTo(!initiallyPrivate);
+ assertThat(isPrivateInDb(file)).isEqualTo(initiallyPrivate);
+ }
+
+ @Test
+ public void execute_deletes_all_permissions_to_Anyone_on_specified_project_when_new_visibility_is_private() {
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ UserDto user = dbTester.users().insertUser();
+ GroupDto group = dbTester.users().insertGroup(organization);
+ unsafeGiveAllPermissionsToRootComponent(project, user, group);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .execute();
+
+ verifyHasAllPermissionsButProjectPermissionsToGroupAnyOne(project, user, group);
+ }
+
+ @Test
+ public void execute_does_not_delete_all_permissions_to_AnyOne_on_specified_project_if_already_private() {
+ ComponentDto project = dbTester.components().insertPrivateProject(organization);
+ UserDto user = dbTester.users().insertUser();
+ GroupDto group = dbTester.users().insertGroup(organization);
+ unsafeGiveAllPermissionsToRootComponent(project, user, group);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .execute();
+
+ verifyStillHasAllPermissions(project, user, group);
+ }
+
+ @Test
+ public void execute_deletes_all_permissions_USER_and_BROWSE_of_specified_project_when_new_visibility_is_public() {
+ ComponentDto project = dbTester.components().insertPrivateProject(organization);
+ UserDto user = dbTester.users().insertUser();
+ GroupDto group = dbTester.users().insertGroup(organization);
+ unsafeGiveAllPermissionsToRootComponent(project, user, group);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+
+ verifyHasAllPermissionsButProjectPermissionsUserAndBrowse(project, user, group);
+ }
+
+ @Test
+ public void execute_does_not_delete_permissions_USER_and_BROWSE_of_specified_project_when_new_component_is_already_public() {
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ UserDto user = dbTester.users().insertUser();
+ GroupDto group = dbTester.users().insertGroup(organization);
+ unsafeGiveAllPermissionsToRootComponent(project, user, group);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+
+ verifyStillHasAllPermissions(project, user, group);
+ }
+
+ @Test
+ public void execute_does_not_delete_permissions_USER_and_BROWSE_of_specified_view_when_making_it_public() {
+ ComponentDto view = dbTester.components().insertView(organization);
+ UserDto user = dbTester.users().insertUser();
+ GroupDto group = dbTester.users().insertGroup(organization);
+ unsafeGiveAllPermissionsToRootComponent(view, user, group);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, view);
+
+ request.setParam(PARAM_PROJECT, view.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+
+ verifyStillHasAllPermissions(view, user, group);
+ }
+
+ @Test
+ public void execute_updates_permission_of_specified_project_in_indexes_when_changing_visibility() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ boolean initiallyPrivate = project.isPrivate();
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, initiallyPrivate ? PUBLIC : PRIVATE)
+ .execute();
+
+ verify(permissionIndexer).indexProjectsByUuids(any(DbSession.class), eq(Collections.singletonList(project.uuid())));
+ }
+
+ @Test
+ public void execute_does_not_update_permission_of_specified_project_in_indexes_if_already_has_specified_visibility() {
+ ComponentDto project = randomPublicOrPrivateProject();
+ boolean initiallyPrivate = project.isPrivate();
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, initiallyPrivate ? PRIVATE : PUBLIC)
+ .execute();
+
+ verifyZeroInteractions(permissionIndexer);
+ }
+
+ @Test
+ public void execute_does_not_update_permission_of_specified_view_in_indexes_when_making_it_public() {
+ ComponentDto view = dbTester.components().insertView(organization);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, view);
+
+ request.setParam(PARAM_PROJECT, view.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+
+ verifyZeroInteractions(permissionIndexer);
+ }
+
+ @Test
+ public void execute_grants_USER_and_CODEVIEWER_permissions_to_any_user_with_at_least_one_permission_when_making_project_private() {
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ UserDto user1 = dbTester.users().insertUser();
+ UserDto user2 = dbTester.users().insertUser();
+ UserDto user3 = dbTester.users().insertUser();
+ dbTester.users().insertProjectPermissionOnUser(user1, "p1", project);
+ dbTester.users().insertProjectPermissionOnUser(user1, "p2", project);
+ dbTester.users().insertProjectPermissionOnUser(user2, "p2", project);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .execute();
+
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user1.getId(), project.getId()))
+ .containsOnly(UserRole.USER, UserRole.CODEVIEWER, "p1", "p2");
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user2.getId(), project.getId()))
+ .containsOnly(UserRole.USER, UserRole.CODEVIEWER, "p2");
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user3.getId(), project.getId()))
+ .isEmpty();
+ }
+
+ @Test
+ public void execute_grants_USER_and_CODEVIEWER_permissions_to_any_group_with_at_least_one_permission_when_making_project_private() {
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ GroupDto group1 = dbTester.users().insertGroup(organization);
+ GroupDto group2 = dbTester.users().insertGroup(organization);
+ GroupDto group3 = dbTester.users().insertGroup(organization);
+ dbTester.users().insertProjectPermissionOnGroup(group1, "p1", project);
+ dbTester.users().insertProjectPermissionOnGroup(group1, "p2", project);
+ dbTester.users().insertProjectPermissionOnGroup(group2, "p2", project);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .execute();
+
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group1.getId(), project.getId()))
+ .containsOnly(UserRole.USER, UserRole.CODEVIEWER, "p1", "p2");
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group2.getId(), project.getId()))
+ .containsOnly(UserRole.USER, UserRole.CODEVIEWER, "p2");
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group3.getId(), project.getId()))
+ .isEmpty();
+ }
+
+ private void unsafeGiveAllPermissionsToRootComponent(ComponentDto component, UserDto user, GroupDto group) {
+ Arrays.stream(OrganizationPermission.values())
+ .forEach(organizationPermission -> {
+ dbTester.users().insertPermissionOnAnyone(organization, organizationPermission);
+ dbTester.users().insertPermissionOnGroup(group, organizationPermission);
+ dbTester.users().insertPermissionOnUser(organization, user, organizationPermission);
+ });
+ ProjectPermissions.ALL
+ .forEach(permission -> {
+ unsafeInsertProjectPermissionOnAnyone(component, permission);
+ unsafeInsertProjectPermissionOnGroup(component, group, permission);
+ unsafeInsertProjectPermissionOnUser(component, user, permission);
+ });
+ }
+
+ private void unsafeInsertProjectPermissionOnAnyone(ComponentDto component, String permission) {
+ GroupPermissionDto dto = new GroupPermissionDto()
+ .setOrganizationUuid(component.getOrganizationUuid())
+ .setGroupId(null)
+ .setRole(permission)
+ .setResourceId(component.getId());
+ dbTester.getDbClient().groupPermissionDao().insert(dbTester.getSession(), dto);
+ dbTester.commit();
+ }
+
+ private void unsafeInsertProjectPermissionOnGroup(ComponentDto component, GroupDto group, String permission) {
+ GroupPermissionDto dto = new GroupPermissionDto()
+ .setOrganizationUuid(group.getOrganizationUuid())
+ .setGroupId(group.getId())
+ .setRole(permission)
+ .setResourceId(component.getId());
+ dbTester.getDbClient().groupPermissionDao().insert(dbTester.getSession(), dto);
+ dbTester.commit();
+ }
+
+ private void unsafeInsertProjectPermissionOnUser(ComponentDto component, UserDto user, String permission) {
+ UserPermissionDto dto = new UserPermissionDto(component.getOrganizationUuid(), permission, user.getId(), component.getId());
+ dbTester.getDbClient().userPermissionDao().insert(dbTester.getSession(), dto);
+ dbTester.commit();
+ }
+
+ private void verifyHasAllPermissionsButProjectPermissionsToGroupAnyOne(ComponentDto component, UserDto user, GroupDto group) {
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), null))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), group.getId()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.userPermissionDao().selectGlobalPermissionsOfUser(dbSession, user.getId(), organization.getUuid()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), null, component.getId()))
+ .isEmpty();
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group.getId(), component.getId()))
+ .containsAll(ProjectPermissions.ALL);
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user.getId(), component.getId()))
+ .containsAll(ProjectPermissions.ALL);
+ }
+
+ private void verifyHasAllPermissionsButProjectPermissionsUserAndBrowse(ComponentDto component, UserDto user, GroupDto group) {
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), null))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), group.getId()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.userPermissionDao().selectGlobalPermissionsOfUser(dbSession, user.getId(), organization.getUuid()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), null, component.getId()))
+ .doesNotContain(UserRole.USER)
+ .doesNotContain(UserRole.CODEVIEWER)
+ .containsAll(PROJECT_PERMISSIONS_BUT_USER_AND_CODEVIEWER);
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group.getId(), component.getId()))
+ .doesNotContain(UserRole.USER)
+ .doesNotContain(UserRole.CODEVIEWER)
+ .containsAll(PROJECT_PERMISSIONS_BUT_USER_AND_CODEVIEWER);
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user.getId(), component.getId()))
+ .doesNotContain(UserRole.USER)
+ .doesNotContain(UserRole.CODEVIEWER)
+ .containsAll(PROJECT_PERMISSIONS_BUT_USER_AND_CODEVIEWER);
+ }
+
+ private void verifyStillHasAllPermissions(ComponentDto component, UserDto user, GroupDto group) {
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), null))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, organization.getUuid(), group.getId()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.userPermissionDao().selectGlobalPermissionsOfUser(dbSession, user.getId(), organization.getUuid()))
+ .containsAll(ORGANIZATION_PERMISSIONS_NAME_SET);
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), null, component.getId()))
+ .containsAll(ProjectPermissions.ALL);
+ assertThat(dbClient.groupPermissionDao().selectProjectPermissionsOfGroup(dbSession, organization.getUuid(), group.getId(), component.getId()))
+ .containsAll(ProjectPermissions.ALL);
+ assertThat(dbClient.userPermissionDao().selectProjectPermissionsOfUser(dbSession, user.getId(), component.getId()))
+ .containsAll(ProjectPermissions.ALL);
+ }
+
+ private void insertPendingTask(ComponentDto project) {
+ insertCeQueueDto(project, CeQueueDto.Status.PENDING);
+ }
+
+ private void insertInProgressTask(ComponentDto project) {
+ insertCeQueueDto(project, CeQueueDto.Status.IN_PROGRESS);
+ }
+
+ private int counter = 0;
+
+ private void insertCeQueueDto(ComponentDto project, CeQueueDto.Status status) {
+ dbClient.ceQueueDao().insert(dbTester.getSession(), new CeQueueDto()
+ .setUuid("pending" + counter++)
+ .setComponentUuid(project.uuid())
+ .setTaskType("foo")
+ .setStatus(status));
+ dbTester.commit();
+ }
+
+ private void expectInsufficientPrivilegeException() {
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+ }
+
+ private boolean isPrivateInDb(ComponentDto project) {
+ return dbClient.componentDao().selectByUuid(dbTester.getSession(), project.uuid()).get().isPrivate();
+ }
+
+ private ComponentDto randomPublicOrPrivateProject() {
+ return random.nextBoolean() ? dbTester.components().insertPublicProject(organization) : dbTester.components().insertPrivateProject(organization);
+ }
+}