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(); | |||||
} | |||||
} |
.add(10_2_052, "Create index 'wd_task_uuid_created_at' in 'webhook_deliveries'", CreateIndexTaskUuidCreatedAtInWebhookDeliveries.class) | .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_053, "Create index 'wd_created_at' in 'webhook_deliveries'", CreateIndexCreatedAtInWebhookDeliveries.class) | ||||
.add(10_2_054, "Insert property github.userConsentementForPermissionProvisioningRequired", AddUserConsentRequiredIfGithubAutoProvisioningEnabled.class) | |||||
; | ; | ||||
} | } | ||||
} | } |
/* | |||||
* 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); | |||||
} | |||||
} |
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); |