From 1d8607c20141f313fd7a3c1e2ec8a5b6b05cb81a Mon Sep 17 00:00:00 2001 From: Aurelien Poscia Date: Wed, 30 Aug 2023 15:41:11 +0200 Subject: [PATCH] SONAR-20312 add migration to require user consent for GH synchronization --- ...quiredIfGithubAutoProvisioningEnabled.java | 72 ++++++++++++ .../migration/version/v102/DbVersion102.java | 1 + ...edIfGithubAutoProvisioningEnabledTest.java | 106 ++++++++++++++++++ .../schema.sql | 21 ++++ 4 files changed, 200 insertions(+) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabled.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest/schema.sql diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabled.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabled.java new file mode 100644 index 00000000000..10eccb018eb --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabled.java @@ -0,0 +1,72 @@ +package org.sonar.server.platform.db.migration.version.v102; + +import com.google.common.annotations.VisibleForTesting; +import java.sql.SQLException; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DataChange; +import org.sonar.server.platform.db.migration.step.Upsert; + +public class AddUserConsentRequiredIfGithubAutoProvisioningEnabled extends DataChange { + + private static final Logger LOG = LoggerFactory.getLogger(AddUserConsentRequiredIfGithubAutoProvisioningEnabled.class); + @VisibleForTesting + static final String PROVISIONING_GITHUB_ENABLED_PROP_KEY = "provisioning.github.enabled"; + + @VisibleForTesting + static final String PROP_KEY = "sonar.auth.github.userConsentForPermissionProvisioningRequired"; + + private static final String INSERT_QUERY = """ + INSERT INTO PROPERTIES (UUID, PROP_KEY, IS_EMPTY, CREATED_AT) + VALUES (?, ?, ?, ?) + """; + + private final System2 system2; + private final UuidFactory uuidFactory; + + public AddUserConsentRequiredIfGithubAutoProvisioningEnabled(Database db, System2 system2, UuidFactory uuidFactory) { + super(db); + this.system2 = system2; + this.uuidFactory = uuidFactory; + } + + @Override + protected void execute(DataChange.Context context) throws SQLException { + if (!isGithubAutoProvisioningEnabled(context)) { + return; + } + if (isUserConsentAlreadyRequired(context)) { + return; + } + LOG.warn("Automatic synchronization was previously activated for GitHub. It requires user consent to continue working as new " + + " features were added with the synchronization. Please read the upgrade notes."); + Upsert upsert = context.prepareUpsert(INSERT_QUERY); + upsert + .setString(1, uuidFactory.create()) + .setString(2, PROP_KEY) + .setBoolean(3, true) + .setLong(4, system2.now()) + .execute() + .commit(); + } + + private static boolean isUserConsentAlreadyRequired(Context context) throws SQLException { + return Optional.ofNullable(context.prepareSelect("select count(*) from properties where prop_key = ?") + .setString(1, PROP_KEY) + .get(t -> 1 == t.getInt(1))) + .orElseThrow(); + } + + private static boolean isGithubAutoProvisioningEnabled(Context context) throws SQLException { + return Optional.ofNullable(context.prepareSelect("select count(*) from internal_properties where kee = ? and text_value = ?") + .setString(1, PROVISIONING_GITHUB_ENABLED_PROP_KEY) + .setString(2, "true") + .get(t -> 1 == t.getInt(1))) + .orElseThrow(); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java index d676fdd91c3..900b9eb7cc7 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java @@ -110,6 +110,7 @@ public class DbVersion102 implements DbVersion { .add(10_2_052, "Create index 'wd_task_uuid_created_at' in 'webhook_deliveries'", CreateIndexTaskUuidCreatedAtInWebhookDeliveries.class) .add(10_2_053, "Create index 'wd_created_at' in 'webhook_deliveries'", CreateIndexCreatedAtInWebhookDeliveries.class) + .add(10_2_054, "Insert property github.userConsentementForPermissionProvisioningRequired", AddUserConsentRequiredIfGithubAutoProvisioningEnabled.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.java new file mode 100644 index 00000000000..06ea3d24998 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.java @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.v102; + +import java.sql.SQLException; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.slf4j.event.Level; +import org.sonar.api.testfixtures.log.LogTester; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactoryFast; +import org.sonar.db.CoreDbTester; +import org.sonar.server.platform.db.migration.step.DataChange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.server.platform.db.migration.version.v102.AddUserConsentRequiredIfGithubAutoProvisioningEnabled.PROP_KEY; +import static org.sonar.server.platform.db.migration.version.v102.AddUserConsentRequiredIfGithubAutoProvisioningEnabled.PROVISIONING_GITHUB_ENABLED_PROP_KEY; + +public class AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest { + + @Rule + public LogTester logger = new LogTester(); + + @Rule + public final CoreDbTester db = CoreDbTester.createForSchema(AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.class, "schema.sql"); + + private final DataChange underTest = new AddUserConsentRequiredIfGithubAutoProvisioningEnabled(db.database(), new System2(), UuidFactoryFast.getInstance()); + + @Before + public void before() { + logger.clear(); + } + + @Test + public void migration_whenGitHubAutoProvisioningPropertyNotPresent_shouldNotRequireConsent() throws SQLException { + underTest.execute(); + + assertThat(logger.logs(Level.WARN)).isEmpty(); + assertThat(isConsentRequired()).isFalse(); + } + + @Test + public void migration_whenGitHubAutoProvisioningDisabled_shouldNotRequireConsent() throws SQLException { + disableGithubProvisioning(); + underTest.execute(); + + assertThat(logger.logs(Level.WARN)).isEmpty(); + assertThat(isConsentRequired()).isFalse(); + } + + @Test + public void migration_whenGitHubAutoProvisioningEnabled_shouldRequireConsent() throws SQLException { + enableGithubProvisioning(); + + underTest.execute(); + + assertThat(logger.logs(Level.WARN)).containsExactly("Automatic synchronization was previously activated for GitHub. It requires user consent to continue working as new" + + " features were added with the synchronization. Please read the upgrade notes."); + assertThat(isConsentRequired()).isTrue(); + } + + @Test + public void migration_is_reentrant() throws SQLException { + enableGithubProvisioning(); + + underTest.execute(); + underTest.execute(); + + assertThat(logger.logs(Level.WARN)).containsExactly("Automatic synchronization was previously activated for GitHub. It requires user consent to continue working as new" + + " features were added with the synchronization. Please read the upgrade notes."); + assertThat(isConsentRequired()).isTrue(); + } + + private void disableGithubProvisioning() { + toggleGithubProvisioning(false); + } + private void enableGithubProvisioning() { + toggleGithubProvisioning(true); + } + + private boolean isConsentRequired() { + return db.countSql("select count(*) from properties where prop_key = '" + PROP_KEY + "'") >= 1; + } + + private void toggleGithubProvisioning(boolean enabled) { + db.executeInsert("internal_properties", "kee", PROVISIONING_GITHUB_ENABLED_PROP_KEY, "text_value", String.valueOf(enabled), "is_empty", true, "created_at", 0); + } +} diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest/schema.sql new file mode 100644 index 00000000000..5e16ba34ab5 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest/schema.sql @@ -0,0 +1,21 @@ +CREATE TABLE "INTERNAL_PROPERTIES"( + "KEE" CHARACTER VARYING(40) NOT NULL, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" CHARACTER VARYING(4000), + "CLOB_VALUE" CHARACTER LARGE OBJECT, + "CREATED_AT" BIGINT NOT NULL +); +ALTER TABLE "INTERNAL_PROPERTIES" ADD CONSTRAINT "PK_INTERNAL_PROPERTIES" PRIMARY KEY("KEE"); + +CREATE TABLE "PROPERTIES"( + "UUID" CHARACTER VARYING(40) NOT NULL, + "PROP_KEY" CHARACTER VARYING(512) NOT NULL, + "IS_EMPTY" BOOLEAN NOT NULL, + "TEXT_VALUE" CHARACTER VARYING(4000), + "CLOB_VALUE" CHARACTER LARGE OBJECT, + "CREATED_AT" BIGINT NOT NULL, + "ENTITY_UUID" CHARACTER VARYING(40), + "USER_UUID" CHARACTER VARYING(255) +); +ALTER TABLE "PROPERTIES" ADD CONSTRAINT "PK_PROPERTIES" PRIMARY KEY("UUID"); +CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES"("PROP_KEY" NULLS FIRST); -- 2.39.5