]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20312 add migration to require user consent for GH synchronization
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Wed, 30 Aug 2023 13:41:11 +0000 (15:41 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 31 Aug 2023 20:02:57 +0000 (20:02 +0000)
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabled.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v102/DbVersion102.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v102/AddUserConsentRequiredIfGithubAutoProvisioningEnabledTest/schema.sql [new file with mode: 0644]

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 (file)
index 0000000..10eccb0
--- /dev/null
@@ -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();
+  }
+
+}
index d676fdd91c3996ec9828ad7ed634df0738bfe448..900b9eb7cc70fcbcca6586dc9fdbd7d1cfacc206 100644 (file)
@@ -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 (file)
index 0000000..06ea3d2
--- /dev/null
@@ -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 (file)
index 0000000..5e16ba3
--- /dev/null
@@ -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);