aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2017-04-28 09:14:34 +0200
committerJulien Lancelot <julien.lancelot@sonarsource.com>2017-04-28 18:36:09 +0200
commit65a250757f5c7d60c03a8d94a78ba716729cf379 (patch)
tree815591b11668217297e187edf39935fca45bd63e
parent211c8f4574a43d68a026a1b230ca3c9a9ff52587 (diff)
downloadsonarqube-65a250757f5c7d60c03a8d94a78ba716729cf379.tar.gz
sonarqube-65a250757f5c7d60c03a8d94a78ba716729cf379.zip
SONAR-9124 Allow preventing project to become private api/projects/update_visibility
-rw-r--r--it/it-tests/src/test/java/it/organization/BillingTest.java29
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java19
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java53
3 files changed, 98 insertions, 3 deletions
diff --git a/it/it-tests/src/test/java/it/organization/BillingTest.java b/it/it-tests/src/test/java/it/organization/BillingTest.java
index 869f7601943..ef90a697432 100644
--- a/it/it-tests/src/test/java/it/organization/BillingTest.java
+++ b/it/it-tests/src/test/java/it/organization/BillingTest.java
@@ -37,6 +37,7 @@ import org.sonarqube.ws.client.WsResponse;
import org.sonarqube.ws.client.organization.CreateWsRequest;
import org.sonarqube.ws.client.organization.UpdateProjectVisibilityWsRequest;
import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.UpdateVisibilityRequest;
import util.ItUtils;
import util.user.UserRule;
@@ -148,7 +149,7 @@ public class BillingTest {
}
@Test
- public void fail_to_update_default_projects_visibility_to_private() {
+ public void fail_to_update_organization_default_visibility_to_private() {
String organizationKey = createOrganization();
setServerProperty(orchestrator, "sonar.billing.preventUpdatingProjectsVisibilityToPrivate", "true");
@@ -161,6 +162,32 @@ public class BillingTest {
}
}
+ @Test
+ public void does_not_fail_to_update_project_visibility_to_private() {
+ String organizationKey = createOrganization();
+ String projectKey = createPublicProject(organizationKey);
+ setServerProperty(orchestrator, "sonar.billing.preventUpdatingProjectsVisibilityToPrivate", "false");
+
+ adminClient.projects().updateVisibility(UpdateVisibilityRequest.builder().setProject(projectKey).setVisibility("private").build());
+
+ assertWsResponseAsAdmin(new GetRequest("api/navigation/component").setParam("componentKey", projectKey), "\"visibility\":\"private\"");
+ }
+
+ @Test
+ public void fail_to_update_project_visibility_to_private() {
+ String organizationKey = createOrganization();
+ String projectKey = createPublicProject(organizationKey);
+ setServerProperty(orchestrator, "sonar.billing.preventUpdatingProjectsVisibilityToPrivate", "true");
+
+ try {
+ adminClient.projects().updateVisibility(UpdateVisibilityRequest.builder().setProject(projectKey).setVisibility("private").build());
+ fail();
+ } catch (HttpException ex) {
+ assertThat(ex.code()).isEqualTo(400);
+ assertThat(ex.content()).contains(format("Organization %s cannot use private project", organizationKey));
+ }
+ }
+
private static String createOrganization() {
String key = newOrganizationKey();
adminClient.organizations().create(new CreateWsRequest.Builder().setKey(key).setName(key).build()).getOrganization();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
index 7b59665c5db..c1f0fd0a259 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateVisibilityAction.java
@@ -30,14 +30,18 @@ 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.organization.OrganizationDto;
import org.sonar.db.permission.GroupPermissionDto;
import org.sonar.db.permission.UserPermissionDto;
import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.permission.index.PermissionIndexer;
import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.client.project.ProjectsWsParameters;
+import static java.lang.String.format;
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;
@@ -52,13 +56,15 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
private final ComponentFinder componentFinder;
private final UserSession userSession;
private final PermissionIndexer permissionIndexer;
+ private final BillingValidationsProxy billingValidations;
public UpdateVisibilityAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession,
- PermissionIndexer permissionIndexer) {
+ PermissionIndexer permissionIndexer, BillingValidationsProxy billingValidations) {
this.dbClient = dbClient;
this.componentFinder = componentFinder;
this.userSession = userSession;
this.permissionIndexer = permissionIndexer;
+ this.billingValidations = billingValidations;
}
public void define(WebService.NewController context) {
@@ -95,6 +101,9 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
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()) {
+ OrganizationDto organization = dbClient.organizationDao().selectByUuid(dbSession, component.getOrganizationUuid())
+ .orElseThrow(() -> new IllegalStateException(format("Could not find organization with uuid '%s' of project '%s'", component.getOrganizationUuid(), projectKey)));
+ checkCanUpdateProjectsVisibility(organization, changeToPrivate);
dbClient.componentDao().setPrivateForRootComponentUuid(dbSession, component.uuid(), changeToPrivate);
if (changeToPrivate) {
updatePermissionsToPrivate(dbSession, component);
@@ -148,4 +157,12 @@ public class UpdateVisibilityAction implements ProjectsWsAction {
});
}
+ private void checkCanUpdateProjectsVisibility(OrganizationDto organization, boolean newProjectsPrivate) {
+ try {
+ billingValidations.checkCanUpdateProjectsVisibility(new BillingValidations.Organization(organization.getKey(), organization.getUuid()), newProjectsPrivate);
+ } catch (BillingValidations.BillingValidationsException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
index c93850de003..5deb6b149e5 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateVisibilityActionTest.java
@@ -49,6 +49,8 @@ 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.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.permission.index.PermissionIndexer;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
@@ -60,9 +62,11 @@ 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.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
public class UpdateVisibilityActionTest {
private static final String PARAM_VISIBILITY = "visibility";
@@ -85,8 +89,9 @@ public class UpdateVisibilityActionTest {
private DbClient dbClient = dbTester.getDbClient();
private DbSession dbSession = dbTester.getSession();
private PermissionIndexer permissionIndexer = mock(PermissionIndexer.class);
+ private BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class);
- private UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, new ComponentFinder(dbClient), userSessionRule, permissionIndexer);
+ private UpdateVisibilityAction underTest = new UpdateVisibilityAction(dbClient, new ComponentFinder(dbClient), userSessionRule, permissionIndexer, billingValidations);
private WsActionTester actionTester = new WsActionTester(underTest);
private final Random random = new Random();
@@ -253,6 +258,21 @@ public class UpdateVisibilityActionTest {
}
@Test
+ public void execute_throws_ISE_when_project_organization_uuid_does_not_match_existing_organization() {
+ // Organization is not persisted
+ OrganizationDto organization = newOrganizationDto();
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage(format("Could not find organization with uuid '%s' of project '%s'", organization.getUuid(), project.key()));
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .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();
@@ -500,6 +520,37 @@ public class UpdateVisibilityActionTest {
.isEmpty();
}
+ @Test
+ public void fail_to_update_visibility_to_private_when_organization_is_not_allowed_to_use_private_projects() {
+ OrganizationDto organization = dbTester.organizations().insert();
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ dbTester.organizations().setNewProjectPrivate(organization, true);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+ doThrow(new BillingValidations.BillingValidationsException("This organization cannot use project private")).when(billingValidations)
+ .checkCanUpdateProjectVisibility(any(BillingValidations.Organization.class), eq(true));
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("This organization cannot use project private");
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PRIVATE)
+ .execute();
+ }
+
+ @Test
+ public void does_not_fail_to_update_visibility_to_public_when_organization_is_not_allowed_to_use_private_projects() {
+ OrganizationDto organization = dbTester.organizations().insert();
+ ComponentDto project = dbTester.components().insertPublicProject(organization);
+ dbTester.organizations().setNewProjectPrivate(organization, true);
+ userSessionRule.addProjectPermission(UserRole.ADMIN, project);
+ doThrow(new BillingValidations.BillingValidationsException("This organization cannot use project private")).when(billingValidations)
+ .checkCanUpdateProjectVisibility(any(BillingValidations.Organization.class), eq(true));
+
+ request.setParam(PARAM_PROJECT, project.key())
+ .setParam(PARAM_VISIBILITY, PUBLIC)
+ .execute();
+ }
+
private void unsafeGiveAllPermissionsToRootComponent(ComponentDto component, UserDto user, GroupDto group, OrganizationDto organization) {
Arrays.stream(OrganizationPermission.values())
.forEach(organizationPermission -> {