/*
* 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.organization.ws;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.sonar.api.config.MapSettings;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.permission.template.PermissionTemplateDto;
import org.sonar.db.qualityprofile.QualityProfileDto;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserDto;
import org.sonar.server.component.ComponentCleanerService;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import org.sonar.server.organization.TestOrganizationFlags;
import org.sonar.server.qualityprofile.QProfileFactory;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.user.index.UserIndex;
import org.sonar.server.user.index.UserIndexDefinition;
import org.sonar.server.user.index.UserIndexer;
import org.sonar.server.user.index.UserQuery;
import org.sonar.server.ws.WsActionTester;
import static com.google.common.collect.ImmutableList.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_ORGANIZATION;
public class DeleteActionTest {
@Rule
public DbTester db = DbTester.create(System2.INSTANCE);
@Rule
public EsTester es = new EsTester(new UserIndexDefinition(new MapSettings()));
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
@Rule
public ExpectedException expectedException = ExpectedException.none();
private DbClient dbClient = db.getDbClient();
private DbSession session = db.getSession();
private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone().setEnabled(true);
private TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
private QProfileFactory qProfileFactory = new QProfileFactory(dbClient, mock(UuidFactory.class), System2.INSTANCE, mock(ActiveRuleIndexer.class));
private UserIndex userIndex = new UserIndex(es.client());
private UserIndexer userIndexer = new UserIndexer(dbClient, es.client());
private DeleteAction underTest = new DeleteAction(userSession, dbClient, defaultOrganizationProvider, componentCleanerService, organizationFlags, userIndexer, qProfileFactory);
private WsActionTester wsTester = new WsActionTester(underTest);
@Test
public void verify_define() {
WebService.Action action = wsTester.getDef();
assertThat(action.key()).isEqualTo("delete");
assertThat(action.isPost()).isTrue();
assertThat(action.description()).isEqualTo("Delete an organization.<br/>" +
"Require 'Administer System' permission on the specified organization. Organization support must be enabled.");
assertThat(action.isInternal()).isTrue();
assertThat(action.since()).isEqualTo("6.2");
assertThat(action.handler()).isEqualTo(underTest);
assertThat(action.params()).hasSize(1);
assertThat(action.responseExample()).isNull();
assertThat(action.param("organization"))
.matches(param -> param.isRequired())
.matches(param -> "foo-company".equals(param.exampleValue()))
.matches(param -> "Organization key".equals(param.description()));
}
@Test
public void request_fails_with_IllegalStateException_if_organization_support_is_disabled() {
organizationFlags.setEnabled(false);
userSession.logIn();
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Organization support is disabled");
wsTester.newRequest().execute();
}
@Test
public void request_fails_with_UnauthorizedException_if_user_is_not_logged_in() {
expectedException.expect(UnauthorizedException.class);
expectedException.expectMessage("Authentication is required");
wsTester.newRequest()
.execute();
}
@Test
public void request_fails_with_IAE_if_key_param_is_missing() {
logInAsSystemAdministrator();
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("The 'organization' parameter is missing");
wsTester.newRequest().execute();
}
@Test
public void request_fails_with_IAE_if_key_is_the_one_of_default_organization() {
logInAsSystemAdministrator();
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Default Organization can't be deleted");
sendRequest(db.getDefaultOrganization());
}
@Test
public void request_fails_with_NotFoundException_if_organization_with_specified_key_does_not_exist() {
logInAsSystemAdministrator();
expectedException.expect(NotFoundException.class);
expectedException.expectMessage("Organization with key 'foo' not found");
sendRequest("foo");
}
@Test
public void request_fails_with_ForbiddenException_when_user_is_not_administrator_of_specified_organization() {
OrganizationDto organization = db.organizations().insert();
userSession.logIn();
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
sendRequest(organization);
}
@Test
public void request_fails_with_ForbiddenException_when_user_is_system_administrator() {
OrganizationDto organization = db.organizations().insert();
userSession.logIn().setSystemAdministrator();
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
sendRequest(organization);
}
@Test
public void request_fails_with_ForbiddenException_when_user_is_administrator_of_other_organization() {
OrganizationDto organization = db.organizations().insert();
logInAsAdministrator(db.getDefaultOrganization());
expectedException.expect(ForbiddenException.class);
expectedException.expectMessage("Insufficient privileges");
sendRequest(organization);
}
@Test
public void request_deletes_specified_organization_if_exists_and_user_is_administrator_of_it() {
OrganizationDto organization = db.organizations().insert();
logInAsAdministrator(organization);
sendRequest(organization);
verifyOrganizationDoesNotExist(organization);
}
@Test
public void request_deletes_specified_organization_if_exists_and_user_is_organization_administrator() {
OrganizationDto organization = db.organizations().insert();
logInAsAdministrator(organization);
sendRequest(organization);
verifyOrganizationDoesNotExist(organization);
}
@Test
public void request_deletes_specified_guarded_organization_if_exists_and_user_is_system_administrator() {
OrganizationDto organization = db.organizations().insert(dto -> dto.setGuarded(true));
logInAsSystemAdministrator();
sendRequest(organization);
verifyOrganizationDoesNotExist(organization);
}
@Test
public void request_also_deletes_components_of_specified_organization() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPrivateProject(organization);
ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project));
ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(module, "a/b"));
ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module, directory));
ComponentDto view = db.components().insertView(organization, (dto) -> {
});
ComponentDto subview1 = db.components().insertComponent(ComponentTesting.newSubView(view, "v1", "ksv1"));
ComponentDto subview2 = db.components().insertComponent(ComponentTesting.newSubView(subview1, "v2", "ksv2"));
ComponentDto projectCopy = db.components().insertComponent(ComponentTesting.newProjectCopy("pc1", project, subview1));
logInAsAdministrator(organization);
sendRequest(organization);
verifyOrganizationDoesNotExist(organization);
ArgumentCaptor<List<ComponentDto>> arg = (ArgumentCaptor<List<ComponentDto>>) ((ArgumentCaptor) ArgumentCaptor.forClass(List.class));
verify(componentCleanerService).delete(any(DbSession.class), arg.capture());
assertThat(arg.getValue()).containsOnly(project, view);
}
@Test
public void request_also_deletes_permissions_templates_and_permissions_and_groups_of_specified_organization() {
OrganizationDto org = db.organizations().insert();
OrganizationDto otherOrg = db.organizations().insert();
UserDto user1 = db.users().insertUser();
UserDto user2 = db.users().insertUser();
GroupDto group1 = db.users().insertGroup(org);
GroupDto group2 = db.users().insertGroup(org);
GroupDto otherGroup1 = db.users().insertGroup(otherOrg);
GroupDto otherGroup2 = db.users().insertGroup(otherOrg);
ComponentDto projectDto = db.components().insertPublicProject(org);
ComponentDto otherProjectDto = db.components().insertPublicProject(otherOrg);
db.users().insertPermissionOnAnyone(org, "u1");
db.users().insertPermissionOnAnyone(otherOrg, "not deleted u1");
db.users().insertPermissionOnUser(org, user1, "u2");
db.users().insertPermissionOnUser(otherOrg, user1, "not deleted u2");
db.users().insertPermissionOnGroup(group1, "u3");
db.users().insertPermissionOnGroup(otherGroup1, "not deleted u3");
db.users().insertProjectPermissionOnAnyone("u4", projectDto);
db.users().insertProjectPermissionOnAnyone("not deleted u4", otherProjectDto);
db.users().insertProjectPermissionOnGroup(group1, "u5", projectDto);
db.users().insertProjectPermissionOnGroup(otherGroup1, "not deleted u5", otherProjectDto);
db.users().insertProjectPermissionOnUser(user1, "u6", projectDto);
db.users().insertProjectPermissionOnUser(user1, "not deleted u6", otherProjectDto);
PermissionTemplateDto templateDto = db.permissionTemplates().insertTemplate(org);
PermissionTemplateDto otherTemplateDto = db.permissionTemplates().insertTemplate(otherOrg);
logInAsAdministrator(org);
sendRequest(org);
verifyOrganizationDoesNotExist(org);
assertThat(dbClient.groupDao().selectByIds(session, of(group1.getId(), otherGroup1.getId(), group2.getId(), otherGroup2.getId())))
.extracting(GroupDto::getId)
.containsOnly(otherGroup1.getId(), otherGroup2.getId());
assertThat(dbClient.permissionTemplateDao().selectByUuid(session, templateDto.getUuid()))
.isNull();
assertThat(dbClient.permissionTemplateDao().selectByUuid(session, otherTemplateDto.getUuid()))
.isNotNull();
assertThat(db.select("select role as \"role\" from USER_ROLES"))
.extracting(row -> (String) row.get("role"))
.doesNotContain("u2", "u6")
.contains("not deleted u2", "not deleted u6");
assertThat(db.select("select role as \"role\" from GROUP_ROLES"))
.extracting(row -> (String) row.get("role"))
.doesNotContain("u1", "u3", "u4", "u5")
.contains("not deleted u1", "not deleted u3", "not deleted u4", "not deleted u5");
}
@Test
public void request_also_deletes_members_of_specified_organization() {
OrganizationDto org = db.organizations().insert();
OrganizationDto otherOrg = db.organizations().insert();
UserDto user1 = db.users().insertUser();
UserDto user2 = db.users().insertUser();
db.organizations().addMember(org, user1);
db.organizations().addMember(otherOrg, user1);
db.organizations().addMember(org, user2);
userIndexer.index(Arrays.asList(user1.getLogin(), user2.getLogin()));
logInAsAdministrator(org);
sendRequest(org);
verifyOrganizationDoesNotExist(org);
assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), org.getUuid(), user1.getId())).isNotPresent();
assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), org.getUuid(), user2.getId())).isNotPresent();
assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), otherOrg.getUuid(), user1.getId())).isPresent();
assertThat(userIndex.search(UserQuery.builder().setOrganizationUuid(org.getUuid()).build(), new SearchOptions()).getTotal()).isEqualTo(0);
assertThat(userIndex.search(UserQuery.builder().setOrganizationUuid(otherOrg.getUuid()).build(), new SearchOptions()).getTotal()).isEqualTo(1);
}
@Test
public void request_also_deletes_quality_profiles_of_specified_organization() {
OrganizationDto org = db.organizations().insert();
OrganizationDto otherOrg = db.organizations().insert();
QualityProfileDto profileInOrg = db.qualityProfiles().insert(org);
QualityProfileDto profileInOtherOrg = db.qualityProfiles().insert(otherOrg);
logInAsAdministrator(org);
sendRequest(org);
verifyOrganizationDoesNotExist(org);
assertThat(db.select("select kee as \"profileKey\" from rules_profiles"))
.extracting(row -> (String) row.get("profileKey"))
.containsOnly(profileInOtherOrg.getKey());
}
private void verifyOrganizationDoesNotExist(OrganizationDto organization) {
assertThat(db.getDbClient().organizationDao().selectByKey(session, organization.getKey()))
.isEmpty();
}
private void sendRequest(OrganizationDto organization) {
sendRequest(organization.getKey());
}
private void sendRequest(String organizationKey) {
wsTester.newRequest()
.setParam(PARAM_ORGANIZATION, organizationKey)
.execute();
}
private void logInAsSystemAdministrator() {
userSession.logIn().setSystemAdministrator();
}
private void logInAsAdministrator(OrganizationDto organization) {
userSession.logIn().addPermission(ADMINISTER, organization);
}
}