From a477248ab4050a0c81cf9a462c3b99fe4fffddc1 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Fri, 10 Feb 2017 15:13:56 +0100 Subject: [PATCH] SONAR-8761 new WS api/organizations/enable_support --- .../organization/ws/EnableSupportAction.java | 94 ++++++++++++++ .../ws/OrganizationsWsModule.java | 1 + .../ws/OrganizationsWsSupport.java | 13 +- .../server/property/InternalProperties.java | 2 + .../organization/ws/CreateActionTest.java | 2 +- .../ws/EnableSupportActionTest.java | 118 ++++++++++++++++++ .../ws/OrganizationsWsModuleTest.java | 2 +- .../organization/ws/SearchActionTest.java | 2 +- .../organization/ws/UpdateActionTest.java | 2 +- 9 files changed, 231 insertions(+), 5 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/organization/ws/EnableSupportAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/organization/ws/EnableSupportActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/EnableSupportAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/EnableSupportAction.java new file mode 100644 index 00000000000..61759ee382f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/EnableSupportAction.java @@ -0,0 +1,94 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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 org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.organization.DefaultOrganizationProvider; +import org.sonar.server.property.InternalProperties; +import org.sonar.server.user.UserSession; + +import static java.lang.String.valueOf; +import static java.util.Objects.requireNonNull; +import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; + +public class EnableSupportAction implements OrganizationsAction { + private static final String ACTION = "enable_support"; + + private final UserSession userSession; + private final DbClient dbClient; + private final DefaultOrganizationProvider defaultOrganizationProvider; + private final OrganizationsWsSupport support; + + public EnableSupportAction(UserSession userSession, DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider, OrganizationsWsSupport support) { + this.userSession = userSession; + this.dbClient = dbClient; + this.defaultOrganizationProvider = defaultOrganizationProvider; + this.support = support; + } + + @Override + public void define(WebService.NewController context) { + context.createAction(ACTION) + .setPost(true) + .setDescription("Enable support of organizations.
" + + "'Administer System' permission is required. The logged-in user will be flagged as root and will be able to manage organizations and other root users.") + .setInternal(true) + .setPost(true) + .setSince("6.3") + .setHandler(this); + } + + @Override + public void handle(Request request, Response response) throws Exception { + verifySystemAdministrator(); + + try (DbSession dbSession = dbClient.openSession(false)) { + verifyFeatureIsDisabled(dbSession); + flagCurrentUserAsRoot(dbSession); + enableFeature(dbSession); + dbSession.commit(); + } + response.noContent(); + } + + private void verifySystemAdministrator() { + userSession.checkLoggedIn().checkOrganizationPermission(defaultOrganizationProvider.get().getUuid(), SYSTEM_ADMIN); + } + + private void verifyFeatureIsDisabled(DbSession dbSession) { + if (support.isFeatureEnabled(dbSession)) { + throw new BadRequestException("Organizations are already enabled"); + } + } + + private void flagCurrentUserAsRoot(DbSession dbSession) { + dbClient.userDao().setRoot(dbSession, requireNonNull(userSession.getLogin()), true); + } + + private void enableFeature(DbSession dbSession) { + dbClient.internalPropertiesDao().save(dbSession, InternalProperties.ORGANIZATION_ENABLED, valueOf(true)); + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java index 41c9dfcb92c..c05b428bc39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsModule.java @@ -30,6 +30,7 @@ public class OrganizationsWsModule extends Module { OrganizationsWsSupport.class, // actions CreateAction.class, + EnableSupportAction.class, SearchAction.class, UpdateAction.class, DeleteAction.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java index 42019093cd6..90c5f6e5c39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java @@ -19,11 +19,15 @@ */ package org.sonar.server.organization.ws; +import java.util.Optional; import javax.annotation.CheckForNull; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.organization.OrganizationValidation; +import org.sonar.server.property.InternalProperties; import org.sonarqube.ws.Organizations; import static org.sonar.core.util.Protobuf.setNullable; @@ -39,9 +43,11 @@ public class OrganizationsWsSupport { static final String PARAM_AVATAR_URL = "avatar"; private final OrganizationValidation organizationValidation; + private final DbClient dbClient; - public OrganizationsWsSupport(OrganizationValidation organizationValidation) { + public OrganizationsWsSupport(OrganizationValidation organizationValidation, DbClient dbClient) { this.organizationValidation = organizationValidation; + this.dbClient = dbClient; } String getAndCheckMandatoryName(Request request) { @@ -112,4 +118,9 @@ public class OrganizationsWsSupport { setNullable(dto.getAvatarUrl(), builder::setAvatar); return builder.build(); } + + boolean isFeatureEnabled(DbSession dbSession) { + Optional value = dbClient.internalPropertiesDao().selectByKey(dbSession, InternalProperties.ORGANIZATION_ENABLED); + return value.isPresent() && Boolean.parseBoolean(value.get()); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java b/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java index 8c94e5072b6..1d84ad9441a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java +++ b/server/sonar-server/src/main/java/org/sonar/server/property/InternalProperties.java @@ -32,6 +32,8 @@ public interface InternalProperties { */ String DEFAULT_ORGANIZATION = "organization.default"; + String ORGANIZATION_ENABLED = "organization.enabled"; + /** * Read the value of the specified property. * diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java index 46461d96e8f..70f69075edf 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java @@ -90,7 +90,7 @@ public class CreateActionTest { private UuidFactory uuidFactory = mock(UuidFactory.class); private OrganizationValidation organizationValidation = new OrganizationValidationImpl(); private OrganizationCreation organizationCreation = new OrganizationCreationImpl(dbClient, system2, uuidFactory, organizationValidation, settings); - private CreateAction underTest = new CreateAction(settings, userSession, dbClient, new OrganizationsWsSupport(organizationValidation), organizationValidation, organizationCreation); + private CreateAction underTest = new CreateAction(settings, userSession, dbClient, new OrganizationsWsSupport(organizationValidation, dbClient), organizationValidation, organizationCreation); private WsActionTester wsTester = new WsActionTester(underTest); @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/EnableSupportActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/EnableSupportActionTest.java new file mode 100644 index 00000000000..52e65907acd --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/EnableSupportActionTest.java @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.net.HttpURLConnection; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.db.DbTester; +import org.sonar.db.user.UserDto; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.organization.DefaultOrganizationProvider; +import org.sonar.server.organization.OrganizationValidationImpl; +import org.sonar.server.organization.TestDefaultOrganizationProvider; +import org.sonar.server.property.InternalProperties; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.TestResponse; +import org.sonar.server.ws.WsActionTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN; + +public class EnableSupportActionTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public DbTester db = DbTester.create(); + + private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); + private OrganizationsWsSupport support = new OrganizationsWsSupport(new OrganizationValidationImpl(), db.getDbClient()); + private EnableSupportAction underTest = new EnableSupportAction(userSession, db.getDbClient(), defaultOrganizationProvider, support); + private WsActionTester tester = new WsActionTester(underTest); + + @Test + public void enabling_support_saves_internal_property_and_flags_caller_as_root() { + UserDto user = db.users().insertUser(); + db.rootFlag().verify(user.getLogin(), false); + logInAsSystemAdministrator(user.getLogin()); + + call(); + + assertThat(db.getDbClient().internalPropertiesDao().selectByKey(db.getSession(), InternalProperties.ORGANIZATION_ENABLED)).hasValue("true"); + db.rootFlag().verify(user.getLogin(), true); + } + + @Test + public void throw_UnauthorizedException_if_not_logged_in() { + userSession.anonymous(); + + expectedException.expect(UnauthorizedException.class); + expectedException.expectMessage("Authentication is required"); + + call(); + } + + @Test + public void throw_ForbiddenException_if_not_system_administrator() { + userSession.logIn(); + + expectedException.expect(ForbiddenException.class); + expectedException.expectMessage("Insufficient privileges"); + + call(); + } + + @Test + public void throw_BadRequestException_if_support_is_already_enabled() { + logInAsSystemAdministrator("foo"); + + call(); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Organizations are already enabled"); + + call(); + } + + @Test + public void test_definition() { + WebService.Action def = tester.getDef(); + assertThat(def.key()).isEqualTo("enable_support"); + assertThat(def.isPost()).isTrue(); + assertThat(def.isInternal()).isTrue(); + assertThat(def.params()).isEmpty(); + } + + private void logInAsSystemAdministrator(String login) { + userSession.logIn(login).addOrganizationPermission(db.getDefaultOrganization().getUuid(), SYSTEM_ADMIN); + } + + private void call() { + TestResponse response = tester.newRequest().setMethod("POST").execute(); + assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java index 0b42c200bb2..eccaae0ecc5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/OrganizationsWsModuleTest.java @@ -33,7 +33,7 @@ public class OrganizationsWsModuleTest { ComponentContainer container = new ComponentContainer(); underTest.configure(container); assertThat(container.getPicoContainer().getComponentAdapters()) - .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 7); + .hasSize(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 8); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java index b2a6977848b..5e058845037 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java @@ -68,7 +68,7 @@ public class SearchActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - private SearchAction underTest = new SearchAction(dbTester.getDbClient(), new OrganizationsWsSupport(new OrganizationValidationImpl())); + private SearchAction underTest = new SearchAction(dbTester.getDbClient(), new OrganizationsWsSupport(new OrganizationValidationImpl(), dbTester.getDbClient())); private WsActionTester wsTester = new WsActionTester(underTest); @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/UpdateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/UpdateActionTest.java index 01c90b27fd2..4339eec14ac 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/UpdateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/UpdateActionTest.java @@ -59,7 +59,7 @@ public class UpdateActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - private UpdateAction underTest = new UpdateAction(userSession, new OrganizationsWsSupport(new OrganizationValidationImpl()), dbTester.getDbClient()); + private UpdateAction underTest = new UpdateAction(userSession, new OrganizationsWsSupport(new OrganizationValidationImpl(), dbTester.getDbClient()), dbTester.getDbClient()); private WsActionTester wsTester = new WsActionTester(underTest); @Test -- 2.39.5