From: Eric Hartmann Date: Tue, 9 Oct 2018 05:38:37 +0000 (+0200) Subject: SONAR-11271 Add application and portfolio creator to sonar-administrators group X-Git-Tag: 7.5~337 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=924ce6095da8a1116727639e2c3771a4d5dc391f;p=sonarqube.git SONAR-11271 Add application and portfolio creator to sonar-administrators group --- diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql b/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql index 03a0ce6a1e4..643be985663 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql @@ -18,7 +18,9 @@ INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALU INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (4, 'AVdqnciQUUs7Zd3KPvFD', null, null, 'scan'); INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (5, 'AVdqnciQUUs7Zd3KPvFD', null, null, 'provisioning'); INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (6, 'AVdqnciQUUs7Zd3KPvFD', 1, null, 'provisioning'); -ALTER TABLE GROUP_ROLES ALTER COLUMN ID RESTART WITH 7; +INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (7, 'AVdqnciQUUs7Zd3KPvFD', 1, null, 'applicationcreator'); +INSERT INTO GROUP_ROLES(ID, ORGANIZATION_UUID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (8, 'AVdqnciQUUs7Zd3KPvFD', 1, null, 'portfoliocreator'); +ALTER TABLE GROUP_ROLES ALTER COLUMN ID RESTART WITH 9; INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 1); INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 2); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator.java new file mode 100644 index 00000000000..32e8512cd50 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator.java @@ -0,0 +1,82 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.platform.db.migration.version.v74; + +import java.sql.SQLException; +import org.sonar.api.config.Configuration; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.SupportsBlueGreen; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidProvider; + +@SupportsBlueGreen +public class AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator extends DataChange { + + private final Configuration configuration; + private final DefaultOrganizationUuidProvider defaultOrganizationUuidProvider; + + public AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator(Database db, Configuration configuration, + DefaultOrganizationUuidProvider defaultOrganizationUuidProvider) { + super(db); + this.configuration = configuration; + this.defaultOrganizationUuidProvider = defaultOrganizationUuidProvider; + } + + @Override + protected void execute(Context context) throws SQLException { + if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) { + // Nothing to do on SonarCloud + return; + } + + Integer sonarAdmGroupId = context.prepareSelect("select ID from GROUPS where name=?") + .setString(1, "sonar-administrators") + .get(r -> r.getInt(1)); + + if (sonarAdmGroupId == null) { + // We cannot find the default sonar-administrators groups + return; + } + + insertPermissionIfMissing(context, sonarAdmGroupId, "applicationcreator"); + insertPermissionIfMissing(context, sonarAdmGroupId, "portfoliocreator"); + } + + private void insertPermissionIfMissing(Context context, Integer sonarAdmGroupId, String role) throws SQLException { + if (isPermissionMissing(context, sonarAdmGroupId, role)) { + context.prepareUpsert("insert into GROUP_ROLES(ORGANIZATION_UUID, GROUP_ID, ROLE) values(?, ?, ?)") + .setString(1, defaultOrganizationUuidProvider.get(context)) + .setInt(2, sonarAdmGroupId) + .setString(3, role) + .execute() + .commit(); + } + } + + private static boolean isPermissionMissing(Context context, Integer sonarAdmGroupId, String role) throws SQLException { + Integer count = context.prepareSelect("select count(ID) from GROUP_ROLES where GROUP_ID=? and ROLE=?") + .setInt(1, sonarAdmGroupId) + .setString(2, role) + .get(r -> r.getInt(1)); + + return count == null || count == 0; + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java index 285adb6a2f4..b092e76abdc 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v74/DbVersion74.java @@ -47,8 +47,8 @@ public class DbVersion74 implements DbVersion { .add(2324, "Create new creator permissions for applications and portfolios", CreateApplicationsAndPortfoliosCreatorPermissions.class) .add(2325, "Add default templates for applications and portfolios", AddDefaultPermTemplateColumnsToOrganizations.class) .add(2326, "Create new creator permissions for applications and portfolios", CreateApplicationsAndPortfoliosCreatorPermissions.class) - .add(2327, "Add default templates for applications and portfolios", AddDefaultPermTemplateColumnsToOrganizations.class) - .add(2328, "Populate default template permissions on organizations", PopulateDefaultPermTemplateOnOrganizations.class) + .add(2327, "Populate default template permissions on organizations", PopulateDefaultPermTemplateOnOrganizations.class) + .add(2328, "Add portfolio and application creator permissions on sonar-administrators group", AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest.java new file mode 100644 index 00000000000..b9908b7889e --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest.java @@ -0,0 +1,149 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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.platform.db.migration.version.v74; + +import java.sql.SQLException; +import java.util.Date; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.assertj.core.groups.Tuple; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidProvider; +import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidProviderImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest { + + @Rule + public CoreDbTester db = CoreDbTester.createForSchema(AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest.class, + "group_roles_and_internal_properties.sql"); + + private UuidFactoryFast uuidFactoryFast = UuidFactoryFast.getInstance(); + private MapSettings settings = new MapSettings(); + private DefaultOrganizationUuidProvider defaultOrganizationUuidProvider = new DefaultOrganizationUuidProviderImpl(); + private AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator underTest = new AddApplicationCreatorAndPortfolioCreatorToSonarAdministrator(db.database(), settings.asConfig(), + defaultOrganizationUuidProvider); + + @Test + public void is_reentrant() throws SQLException { + String orgUuid = uuidFactoryFast.create(); + insertDefaultOrganizationUuid(orgUuid); + insertGroup(orgUuid, "sonar-administrators"); + Long adminGroupId = getGroupId("sonar-administrators"); + + underTest.execute(); + underTest.execute(); + + assertGroupRoles( + tuple(orgUuid, adminGroupId, null, "applicationcreator"), + tuple(orgUuid, adminGroupId, null, "portfoliocreator")); + } + + @Test + public void create_missing_permissions() throws SQLException { + String orgUuid = uuidFactoryFast.create(); + insertDefaultOrganizationUuid(orgUuid); + insertGroup(orgUuid, "sonar-administrators"); + Long adminGroupId = getGroupId("sonar-administrators"); + + underTest.execute(); + + assertGroupRoles( + tuple(orgUuid, adminGroupId, null, "applicationcreator"), + tuple(orgUuid, adminGroupId, null, "portfoliocreator")); + } + + @Test + public void has_no_effect_if_group_does_not_exist() throws SQLException { + String orgUuid = uuidFactoryFast.create(); + insertDefaultOrganizationUuid(orgUuid); + insertGroup(orgUuid, "sonar"); + + underTest.execute(); + + assertGroupRoles(); + } + + @Test + public void has_no_effect_if_roles_are_already_present() throws SQLException { + String orgUuid = uuidFactoryFast.create(); + insertDefaultOrganizationUuid(orgUuid); + insertGroup(orgUuid, "sonar-administrators"); + Long adminGroupId = getGroupId("sonar-administrators"); + insertGroupRole(orgUuid, adminGroupId, null, "applicationcreator"); + insertGroupRole(orgUuid, adminGroupId, null, "portfoliocreator"); + + underTest.execute(); + + assertGroupRoles( + tuple(orgUuid, adminGroupId, null, "applicationcreator"), + tuple(orgUuid, adminGroupId, null, "portfoliocreator")); + } + + @Test + public void has_no_effect_on_SonarCloud() throws SQLException { + settings.setProperty("sonar.sonarcloud.enabled", true); + underTest.execute(); + assertGroupRoles(); + } + + private void insertDefaultOrganizationUuid(String uuid) { + db.executeInsert("INTERNAL_PROPERTIES", + "KEE", "organization.default", + "IS_EMPTY", false, + "TEXT_VALUE", uuid, + "CREATED_AT", System.currentTimeMillis()); + } + + private void insertGroup(String organizationUuid, String name) { + db.executeInsert("GROUPS", + "ORGANIZATION_UUID", organizationUuid, + "NAME", name, + "CREATED_AT", new Date(), + "UPDATED_AT", new Date()); + } + + private void insertGroupRole(String organizationUuid, @Nullable Long groupId, @Nullable Integer resourceId, String role) { + db.executeInsert("GROUP_ROLES", + "ORGANIZATION_UUID", organizationUuid, + "GROUP_ID", groupId, + "RESOURCE_ID", resourceId, + "ROLE", role); + } + + private Long getGroupId(String groupName) { + return (Long) db.selectFirst("SELECT id FROM groups WHERE name = '" + groupName + "'").get("ID"); + } + + private void assertGroupRoles(Tuple... expectedTuples) { + assertThat(db.select("SELECT organization_uuid, group_id, resource_id, role FROM group_roles") + .stream() + .map(row -> new Tuple(row.get("ORGANIZATION_UUID"), row.get("GROUP_ID"), row.get("RESOURCE_ID"), row.get("ROLE"))) + .collect(Collectors.toList())) + .containsExactlyInAnyOrder(expectedTuples); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest/group_roles_and_internal_properties.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest/group_roles_and_internal_properties.sql new file mode 100644 index 00000000000..8871f93bca8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v74/AddApplicationCreatorAndPortfolioCreatorToSonarAdministratorTest/group_roles_and_internal_properties.sql @@ -0,0 +1,36 @@ +CREATE TABLE "PROPERTIES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "PROP_KEY" VARCHAR(512), + "RESOURCE_ID" INTEGER, + "TEXT_VALUE" CLOB(2147483647), + "USER_ID" INTEGER +); + +CREATE TABLE "INTERNAL_PROPERTIES" ( + "KEE" VARCHAR(50) NOT NULL PRIMARY KEY, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" VARCHAR(4000), + "CLOB_VALUE" CLOB, + "CREATED_AT" BIGINT +); + +CREATE UNIQUE INDEX "UNIQ_INTERNAL_PROPERTIES" ON "INTERNAL_PROPERTIES" ("KEE"); + +CREATE TABLE "GROUPS" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(500), + "DESCRIPTION" VARCHAR(200), + "CREATED_AT" TIMESTAMP, + "UPDATED_AT" TIMESTAMP +); + +CREATE TABLE "GROUP_ROLES" ( + "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "GROUP_ID" INTEGER, + "RESOURCE_ID" INTEGER, + "ROLE" VARCHAR(64) NOT NULL +); +CREATE INDEX "GROUP_ROLES_RESOURCE" ON "GROUP_ROLES" ("RESOURCE_ID"); +CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES" ("ORGANIZATION_UUID", "GROUP_ID", "RESOURCE_ID", "ROLE");