diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-04-28 09:14:34 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2017-04-28 18:36:09 +0200 |
commit | 65a250757f5c7d60c03a8d94a78ba716729cf379 (patch) | |
tree | 815591b11668217297e187edf39935fca45bd63e | |
parent | 211c8f4574a43d68a026a1b230ca3c9a9ff52587 (diff) | |
download | sonarqube-65a250757f5c7d60c03a8d94a78ba716729cf379.tar.gz sonarqube-65a250757f5c7d60c03a8d94a78ba716729cf379.zip |
SONAR-9124 Allow preventing project to become private api/projects/update_visibility
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 -> { |