dependency('com.googlecode.json-simple:json-simple:1.1.1') {
exclude 'junit:junit'
}
+ dependency 'io.prometheus:simpleclient:0.12.0'
+ dependency 'io.prometheus:simpleclient_servlet:0.12.0'
dependency 'com.google.code.findbugs:jsr305:3.0.2'
dependency 'com.google.code.gson:gson:2.8.9'
dependency('com.google.guava:guava:28.2-jre') {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.azure;
+
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.api.config.internal.Settings;
+
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class AzureDevOpsValidator {
+
+ private final AzureDevOpsHttpClient azureDevOpsHttpClient;
+ private final Settings settings;
+
+ public AzureDevOpsValidator(AzureDevOpsHttpClient azureDevOpsHttpClient, Settings settings) {
+ this.azureDevOpsHttpClient = azureDevOpsHttpClient;
+ this.settings = settings;
+ }
+
+ public void validate(AlmSettingDto dto) {
+ try {
+ azureDevOpsHttpClient.checkPAT(requireNonNull(dto.getUrl()),
+ requireNonNull(dto.getDecryptedPersonalAccessToken(settings.getEncryption())));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid Azure URL or Personal Access Token", e);
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucket.bitbucketcloud;
+
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static java.util.Objects.requireNonNull;
+
+@ServerSide
+public class BitbucketCloudValidator {
+
+ private final BitbucketCloudRestClient bitbucketCloudRestClient;
+ private final Settings settings;
+
+ public BitbucketCloudValidator(BitbucketCloudRestClient bitbucketCloudRestClient, Settings settings) {
+ this.bitbucketCloudRestClient = bitbucketCloudRestClient;
+ this.settings = settings;
+ }
+
+ public void validate(AlmSettingDto dto) {
+ String clientId = requireNonNull(dto.getClientId());
+ String appId = requireNonNull(dto.getAppId());
+ String decryptedClientSecret = requireNonNull(dto.getDecryptedClientSecret(settings.getEncryption()));
+ bitbucketCloudRestClient.validate(clientId, decryptedClientSecret, appId);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucketserver;
+
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+@ServerSide
+public class BitbucketServerSettingsValidator {
+ private final BitbucketServerRestClient bitbucketServerRestClient;
+ private final Encryption encryption;
+
+ public BitbucketServerSettingsValidator(BitbucketServerRestClient bitbucketServerRestClient, Settings settings) {
+ this.bitbucketServerRestClient = bitbucketServerRestClient;
+ this.encryption = settings.getEncryption();
+ }
+
+ public void validate(AlmSettingDto almSettingDto) {
+ String bitbucketUrl = almSettingDto.getUrl();
+ String bitbucketToken = almSettingDto.getDecryptedPersonalAccessToken(encryption);
+ if (bitbucketUrl == null || bitbucketToken == null) {
+ throw new IllegalArgumentException("Your global Bitbucket Server configuration is incomplete.");
+ }
+
+ bitbucketServerRestClient.validateUrl(bitbucketUrl);
+ bitbucketServerRestClient.validateToken(bitbucketUrl, bitbucketToken);
+ bitbucketServerRestClient.validateReadPermission(bitbucketUrl, bitbucketToken);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.github;
+
+import java.util.Optional;
+import org.sonar.alm.client.github.config.GithubAppConfiguration;
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.apache.commons.lang.StringUtils.isBlank;
+
+@ServerSide
+public class GithubGlobalSettingsValidator {
+
+ private final Encryption encryption;
+ private final GithubApplicationClient githubApplicationClient;
+
+ public GithubGlobalSettingsValidator(GithubApplicationClientImpl githubApplicationClient, Settings settings) {
+ this.encryption = settings.getEncryption();
+ this.githubApplicationClient = githubApplicationClient;
+ }
+
+ public GithubAppConfiguration validate(AlmSettingDto settings) {
+ long appId;
+ try {
+ appId = Long.parseLong(Optional.ofNullable(settings.getAppId()).orElseThrow(() -> new IllegalArgumentException("Missing appId")));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid appId; " + e.getMessage());
+ }
+ if (isBlank(settings.getClientId())) {
+ throw new IllegalArgumentException("Missing Client Id");
+ }
+ if (isBlank(settings.getDecryptedClientSecret(encryption))) {
+ throw new IllegalArgumentException("Missing Client Secret");
+ }
+ GithubAppConfiguration configuration = new GithubAppConfiguration(appId, settings.getDecryptedPrivateKey(encryption),
+ settings.getUrl());
+
+ githubApplicationClient.checkApiEndpoint(configuration);
+ githubApplicationClient.checkAppPermissions(configuration);
+
+ return configuration;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.gitlab;
+
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+@ServerSide
+public class GitlabGlobalSettingsValidator {
+
+ private final Encryption encryption;
+ private final GitlabHttpClient gitlabHttpClient;
+
+ public GitlabGlobalSettingsValidator(GitlabHttpClient gitlabHttpClient, Settings settings) {
+ this.encryption = settings.getEncryption();
+ this.gitlabHttpClient = gitlabHttpClient;
+ }
+
+ public void validate(AlmSettingDto almSettingDto) {
+ String gitlabUrl = almSettingDto.getUrl();
+ String accessToken = almSettingDto.getDecryptedPersonalAccessToken(encryption);
+
+ if (gitlabUrl == null || accessToken == null) {
+ throw new IllegalArgumentException("Your Gitlab global configuration is incomplete.");
+ }
+
+ gitlabHttpClient.checkUrl(gitlabUrl);
+ gitlabHttpClient.checkToken(gitlabUrl, accessToken);
+ gitlabHttpClient.checkReadPermission(gitlabUrl, accessToken);
+ gitlabHttpClient.checkWritePermission(gitlabUrl, accessToken);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.azure;
+
+import org.junit.Test;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonarqube.ws.AlmSettings;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AzureDevOpsValidatorTest {
+
+ private final AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class);
+ private final Settings settings = mock(Settings.class);
+ private final AzureDevOpsValidator underTest = new AzureDevOpsValidator(azureDevOpsHttpClient, settings);
+
+ @Test
+ public void validate_givenHttpClientThrowingException_throwException() {
+ AlmSettingDto dto = createMockDto();
+
+ doThrow(new IllegalArgumentException()).when(azureDevOpsHttpClient).checkPAT(any(), any());
+
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> underTest.validate(dto))
+ .withMessage("Invalid Azure URL or Personal Access Token");
+
+ }
+
+ @Test(expected = Test.None.class /* no exception expected */)
+ public void validate_givenHttpClientNotThrowingException_doesNotThrowException() {
+ AlmSettingDto dto = createMockDto();
+
+ underTest.validate(dto);
+ }
+
+ private AlmSettingDto createMockDto() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getUrl()).thenReturn("http://azure-devops-url.url");
+ when(dto.getDecryptedPersonalAccessToken(any())).thenReturn("decrypted-token");
+ return dto;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucket.bitbucketcloud;
+
+import org.junit.Test;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BitbucketCloudValidatorTest {
+
+ private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
+ private final Settings settings = mock(Settings.class);
+
+ private final BitbucketCloudValidator underTest = new BitbucketCloudValidator(bitbucketCloudRestClient, settings);
+
+ private static final String EXAMPLE_APP_ID = "123";
+
+ @Test
+ public void validate_forwardsExceptionFromRestClient() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getAppId()).thenReturn(EXAMPLE_APP_ID);
+ when(dto.getClientId()).thenReturn("clientId");
+ when(dto.getDecryptedClientSecret(any())).thenReturn("secret");
+
+ doThrow(new IllegalArgumentException("Exception from bitbucket cloud rest client"))
+ .when(bitbucketCloudRestClient).validate(any(), any(), any());
+
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> underTest.validate(dto))
+ .withMessage("Exception from bitbucket cloud rest client");
+ }
+
+ @Test
+ public void validate_callsValidate() {
+ AlmSettingDto dto = mock(AlmSettingDto.class);
+ when(dto.getAppId()).thenReturn(EXAMPLE_APP_ID);
+ when(dto.getClientId()).thenReturn("clientId");
+ when(dto.getDecryptedClientSecret(any())).thenReturn("secret");
+
+ underTest.validate(dto);
+
+ verify(bitbucketCloudRestClient, times(1)).validate("clientId", "secret", EXAMPLE_APP_ID);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.bitbucketserver;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BitbucketServerSettingsValidatorTest {
+ private static final Encryption encryption = mock(Encryption.class);
+ private static final Settings settings = mock(Settings.class);
+
+ private final BitbucketServerRestClient bitbucketServerRestClient = mock(BitbucketServerRestClient.class);
+ private final BitbucketServerSettingsValidator underTest = new BitbucketServerSettingsValidator(bitbucketServerRestClient, settings);
+
+ @BeforeClass
+ public static void setUp() {
+ when(settings.getEncryption()).thenReturn(encryption);
+ }
+
+ @Test
+ public void validate_success() {
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", "abc");
+ when(encryption.isEncrypted(any())).thenReturn(false);
+
+ underTest.validate(almSettingDto);
+
+ verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com");
+ verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", "abc");
+ verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", "abc");
+ }
+
+ @Test
+ public void validate_success_with_encrypted_token() {
+ String encryptedToken = "abc";
+ String decryptedToken = "decrypted-token";
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", encryptedToken);
+ when(encryption.isEncrypted(encryptedToken)).thenReturn(true);
+ when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken);
+
+ underTest.validate(almSettingDto);
+
+ verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com");
+ verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", decryptedToken);
+ verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", decryptedToken);
+ }
+
+ @Test
+ public void validate_failure_on_incomplete_configuration() {
+ AlmSettingDto almSettingDto = createNewBitbucketDto(null, "abc");
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ public void validate_failure_on_bitbucket_server_api_error() {
+ doThrow(new IllegalArgumentException("error")).when(bitbucketServerRestClient).validateUrl(anyString());
+ AlmSettingDto almSettingDto = createNewBitbucketDto("http://abc.com", "abc");
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ private AlmSettingDto createNewBitbucketDto(String url, String pat) {
+ AlmSettingDto dto = new AlmSettingDto();
+ dto.setAlm(ALM.BITBUCKET);
+ dto.setUrl(url);
+ dto.setPersonalAccessToken(pat);
+ return dto;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.github;
+
+import javax.annotation.Nullable;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.alm.client.github.config.GithubAppConfiguration;
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class GithubGlobalSettingsValidatorTest {
+ private static final Encryption encryption = mock(Encryption.class);
+ private static final Settings settings = mock(Settings.class);
+
+ private static final String EXAMPLE_APP_ID = "123";
+ private static final String EXAMPLE_PRIVATE_KEY = "private_key";
+
+ private final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class);
+ private final GithubGlobalSettingsValidator underTest = new GithubGlobalSettingsValidator(appClient, settings);
+
+ @BeforeClass
+ public static void setUp() {
+ when(settings.getEncryption()).thenReturn(encryption);
+ }
+
+ @Test
+ public void github_global_settings_validation() {
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, EXAMPLE_PRIVATE_KEY);
+
+ when(encryption.isEncrypted(any())).thenReturn(false);
+
+ GithubAppConfiguration configuration = underTest.validate(almSettingDto);
+
+ ArgumentCaptor<GithubAppConfiguration> configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class);
+ verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture());
+ verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture());
+ assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId());
+ assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId());
+ }
+
+ @Test
+ public void github_global_settings_validation_with_encrypted_key() {
+ String encryptedKey = "encrypted-key";
+ String decryptedKey = "decrypted-key";
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", EXAMPLE_APP_ID, encryptedKey);
+
+ when(encryption.isEncrypted(encryptedKey)).thenReturn(true);
+ when(encryption.decrypt(encryptedKey)).thenReturn(decryptedKey);
+
+ GithubAppConfiguration configuration = underTest.validate(almSettingDto);
+
+ ArgumentCaptor<GithubAppConfiguration> configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class);
+ verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture());
+ verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture());
+ assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId());
+ assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getPrivateKey());
+ assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId());
+ assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getPrivateKey());
+ }
+
+ @Test
+ public void github_validation_checks_invalid_appId() {
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", "clientSecret", "abc", null);
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Invalid appId; For input string: \"abc\"");
+ }
+
+ @Test
+ public void github_validation_checks_missing_appId() {
+ AlmSettingDto almSettingDto = new AlmSettingDto();
+ almSettingDto.setAppId(null);
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Missing appId");
+ }
+
+ @Test
+ public void github_validation_checks_missing_clientId() {
+ AlmSettingDto almSettingDto = createNewGithubDto(null, null, EXAMPLE_APP_ID, null);
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Missing Client Id");
+ }
+
+ @Test
+ public void github_validation_checks_missing_clientSecret() {
+ AlmSettingDto almSettingDto = createNewGithubDto("clientId", null, EXAMPLE_APP_ID, null);
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Missing Client Secret");
+
+ }
+
+ private AlmSettingDto createNewGithubDto(@Nullable String clientId, @Nullable String clientSecret,
+ @Nullable String appId, @Nullable String privateKey) {
+ AlmSettingDto dto = new AlmSettingDto();
+ dto.setAlm(ALM.GITHUB);
+ dto.setUrl("http://github-example-url.com");
+ dto.setClientId(clientId);
+ dto.setClientSecret(clientSecret);
+ dto.setAppId(appId);
+ dto.setPrivateKey(privateKey);
+ return dto;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.alm.client.gitlab;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabHttpClient;
+import org.sonar.api.config.internal.Encryption;
+import org.sonar.api.config.internal.Settings;
+import org.sonar.db.alm.setting.AlmSettingDto;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class GitlabGlobalSettingsValidatorTest {
+ private static final Encryption encryption = mock(Encryption.class);
+ private static final Settings settings = mock(Settings.class);
+
+ private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class);
+
+ private final GitlabGlobalSettingsValidator underTest = new GitlabGlobalSettingsValidator(gitlabHttpClient, settings);
+
+ @BeforeClass
+ public static void setUp() {
+ when(settings.getEncryption()).thenReturn(encryption);
+ }
+
+ @Test
+ public void validate_success() {
+ String token = "personal-access-token";
+ AlmSettingDto almSettingDto = new AlmSettingDto()
+ .setUrl("https://gitlab.com/api")
+ .setPersonalAccessToken("personal-access-token");
+ when(encryption.isEncrypted(token)).thenReturn(false);
+
+ underTest.validate(almSettingDto);
+ verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl());
+ verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
+ verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
+ verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
+ }
+
+ @Test
+ public void validate_success_with_encrypted_token() {
+ String encryptedToken = "personal-access-token";
+ String decryptedToken = "decrypted-token";
+ AlmSettingDto almSettingDto = new AlmSettingDto()
+ .setUrl("https://gitlab.com/api")
+ .setPersonalAccessToken(encryptedToken);
+ when(encryption.isEncrypted(encryptedToken)).thenReturn(true);
+ when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken);
+
+ underTest.validate(almSettingDto);
+
+ verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl());
+ verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), decryptedToken);
+ verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), decryptedToken);
+ verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), decryptedToken);
+ }
+
+ @Test
+ public void validate_fail_url_not_set() {
+ AlmSettingDto almSettingDto = new AlmSettingDto()
+ .setUrl(null)
+ .setPersonalAccessToken("personal-access-token");
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Your Gitlab global configuration is incomplete.");
+ }
+
+ @Test
+ public void validate_fail_pat_not_set() {
+ AlmSettingDto almSettingDto = new AlmSettingDto()
+ .setUrl("https://gitlab.com/api")
+ .setPersonalAccessToken(null);
+
+ assertThatThrownBy(() -> underTest.validate(almSettingDto))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Your Gitlab global configuration is incomplete.");
+ }
+
+}
--- /dev/null
+mock-maker-inline
--- /dev/null
+description = 'SonarQube :: Monitoring'
+
+dependencies {
+ compile project(path: ':sonar-plugin-api', configuration: 'shadow')
+ compile project(':server:sonar-webserver-api')
+ compile project(':server:sonar-alm-client')
+ compile 'io.prometheus:simpleclient'
+
+ testCompile 'junit:junit'
+ testCompile 'org.assertj:assertj-core'
+ testCompile 'org.mockito:mockito-core'
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring;
+
+import io.prometheus.client.Gauge;
+import org.sonar.api.server.ServerSide;
+
+@ServerSide
+public class ServerMonitoringMetrics {
+
+ private final Gauge githubConfigOk;
+ private final Gauge gitlabConfigOk;
+ private final Gauge bitbucketConfigOk;
+ private final Gauge azureConfigOk;
+
+ public ServerMonitoringMetrics() {
+ githubConfigOk = Gauge.build()
+ .name("github_config_ok")
+ .help("Tells whether SonarQube instance has configured GitHub integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ gitlabConfigOk = Gauge.build()
+ .name("gitlab_config_ok")
+ .help("Tells whether SonarQube instance has configured GitLab integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ bitbucketConfigOk = Gauge.build()
+ .name("bitbucket_config_ok")
+ .help("Tells whether SonarQube instance has configured BitBucket integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+
+ azureConfigOk = Gauge.build()
+ .name("azure_config_ok")
+ .help("Tells whether SonarQube instance has configured Azure integration and its status is green. 0 for green, 1 otherwise .")
+ .register();
+ }
+
+ public void setGithubStatusToGreen() {
+ githubConfigOk.set(0);
+ }
+
+ public void setGithubStatusToRed() {
+ githubConfigOk.set(1);
+ }
+
+ public void setGitlabStatusToGreen() {
+ gitlabConfigOk.set(0);
+ }
+
+ public void setGitlabStatusToRed() {
+ gitlabConfigOk.set(1);
+ }
+
+ public void setAzureStatusToGreen() {
+ azureConfigOk.set(0);
+ }
+
+ public void setAzureStatusToRed() {
+ azureConfigOk.set(1);
+ }
+
+ public void setBitbucketStatusToGreen() {
+ bitbucketConfigOk.set(0);
+ }
+
+ public void setBitbucketStatusToRed() {
+ bitbucketConfigOk.set(1);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring.devops;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
+import org.picocontainer.Startable;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
+import org.sonar.api.config.Configuration;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+@ServerSide
+public class DevOpsPlatformsMetricsCollector implements Startable {
+
+ private static final String DELAY_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.initial.delay";
+ private static final String PERIOD_IN_MILISECONDS_PROPERTY = "sonar.server.monitoring.devops.period";
+
+ private final Configuration config;
+
+ private final BitbucketServerSettingsValidator bitbucketServerValidator;
+ private final GithubGlobalSettingsValidator githubValidator;
+ private final GitlabGlobalSettingsValidator gitlabValidator;
+ private final BitbucketCloudValidator bitbucketCloudValidator;
+ private final AzureDevOpsValidator azureDevOpsValidator;
+
+ private final DbClient dbClient;
+ private final ServerMonitoringMetrics metrics;
+
+ private ScheduledExecutorService scheduledExecutorService;
+
+ public DevOpsPlatformsMetricsCollector(ServerMonitoringMetrics metrics, DbClient dbClient,
+ BitbucketServerSettingsValidator bitbucketServerValidator, GithubGlobalSettingsValidator githubValidator,
+ GitlabGlobalSettingsValidator gitlabValidator, BitbucketCloudValidator bitbucketCloudValidator,
+ AzureDevOpsValidator azureDevOpsValidator, Configuration config) {
+ this.bitbucketCloudValidator = bitbucketCloudValidator;
+ this.bitbucketServerValidator = bitbucketServerValidator;
+ this.githubValidator = githubValidator;
+ this.azureDevOpsValidator = azureDevOpsValidator;
+ this.gitlabValidator = gitlabValidator;
+ this.metrics = metrics;
+ this.dbClient = dbClient;
+ this.config = config;
+ }
+
+ @Override
+ public void start() {
+ this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
+ new ThreadFactoryBuilder()
+ .setDaemon(true)
+ .setNameFormat(getClass().getCanonicalName() + "-thread-%d")
+ .build());
+ long delayInMilliseconds = config.getLong(DELAY_IN_MILISECONDS_PROPERTY).orElse(10_000L);
+ long periodInMilliseconds = config.getLong(PERIOD_IN_MILISECONDS_PROPERTY).orElse(300_000L);
+ scheduledExecutorService.scheduleWithFixedDelay(createTask(), delayInMilliseconds, periodInMilliseconds, MILLISECONDS);
+ }
+
+ @Override
+ public void stop() {
+ scheduledExecutorService.shutdown();
+ }
+
+ @VisibleForTesting
+ Runnable createTask() {
+ return () -> {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ List<AlmSettingDto> almSettingDtos = dbClient.almSettingDao().selectAll(dbSession);
+ validateBitbucket(getALMsDTOs(almSettingDtos, ALM.BITBUCKET));
+ validateBitbucketCloud(getALMsDTOs(almSettingDtos, ALM.BITBUCKET_CLOUD));
+ validateGithub(getALMsDTOs(almSettingDtos, ALM.GITHUB));
+ validateGitlab(getALMsDTOs(almSettingDtos, ALM.GITLAB));
+ validateAzure(getALMsDTOs(almSettingDtos, ALM.AZURE_DEVOPS));
+ }
+ };
+ }
+
+ private static List<AlmSettingDto> getALMsDTOs(List<AlmSettingDto> almSettingDtos, ALM alm) {
+ return almSettingDtos.stream().filter(dto -> dto.getAlm() == alm).collect(Collectors.toList());
+ }
+
+ private void validateGithub(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ githubValidator.validate(dto);
+ }
+ metrics.setGithubStatusToGreen();
+ } catch (RuntimeException e) {
+ metrics.setGithubStatusToRed();
+ }
+ }
+
+ private void validateBitbucket(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ bitbucketServerValidator.validate(dto);
+ }
+ metrics.setBitbucketStatusToGreen();
+ } catch (Exception e) {
+ metrics.setBitbucketStatusToRed();
+ }
+ }
+
+ private void validateBitbucketCloud(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ bitbucketCloudValidator.validate(dto);
+ }
+ metrics.setBitbucketStatusToGreen();
+ } catch (Exception e) {
+ metrics.setBitbucketStatusToRed();
+ }
+ }
+
+ private void validateGitlab(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ gitlabValidator.validate(dto);
+ }
+ metrics.setGitlabStatusToGreen();
+ } catch (Exception e) {
+ metrics.setGitlabStatusToRed();
+ }
+ }
+
+ private void validateAzure(List<AlmSettingDto> almSettingDtos) {
+ try {
+ for (AlmSettingDto dto : almSettingDtos) {
+ azureDevOpsValidator.validate(dto);
+ }
+ metrics.setAzureStatusToGreen();
+ } catch (Exception e) {
+ metrics.setAzureStatusToRed();
+ }
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
+import java.util.Collections;
+import java.util.Enumeration;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+//@Execution(SAME_THREAD) for JUnit5
+public class ServerMonitoringMetricsTest {
+
+ @Before
+ public void before() {
+ CollectorRegistry.defaultRegistry.clear();
+ }
+
+ @Test
+ public void creatingClassShouldAddMetricsToRegistry() {
+ assertThat(sizeOfDefaultRegistry()).isNotPositive();
+
+ new ServerMonitoringMetrics();
+
+ assertThat(sizeOfDefaultRegistry()).isPositive();
+ }
+
+ @Test
+ public void setters_setGreenStatusForMetricsInTheMetricsRegistry() {
+ ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
+
+ metrics.setGithubStatusToGreen();
+ metrics.setGitlabStatusToGreen();
+ metrics.setAzureStatusToGreen();
+ metrics.setBitbucketStatusToGreen();
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("github_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("gitlab_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("bitbucket_config_ok")).isZero();
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("azure_config_ok")).isZero();
+ }
+
+ @Test
+ public void setters_setRedStatusForMetricsInTheMetricsRegistry() {
+ ServerMonitoringMetrics metrics = new ServerMonitoringMetrics();
+
+ metrics.setGithubStatusToRed();
+ metrics.setGitlabStatusToRed();
+ metrics.setAzureStatusToRed();
+ metrics.setBitbucketStatusToRed();
+
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("github_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("gitlab_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("bitbucket_config_ok")).isEqualTo(1);
+ assertThat(CollectorRegistry.defaultRegistry.getSampleValue("azure_config_ok")).isEqualTo(1);
+ }
+
+ private int sizeOfDefaultRegistry() {
+ Enumeration<Collector.MetricFamilySamples> metrics = CollectorRegistry.defaultRegistry.metricFamilySamples();
+ return Collections.list(metrics).size();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.monitoring.devops;
+
+import org.sonar.api.config.Configuration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
+import org.sonar.db.DbClient;
+import org.sonar.db.alm.setting.ALM;
+import org.sonar.db.alm.setting.AlmSettingDao;
+import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+public class DevOpsPlatformsMetricsCollectorTest {
+
+ private final ServerMonitoringMetrics serverMonitoringMetrics = mock(ServerMonitoringMetrics.class);
+ private final DbClient dbClient = mock(DbClient.class);
+ private final BitbucketServerSettingsValidator bitbucketServerValidator = mock(BitbucketServerSettingsValidator.class);
+ private final GithubGlobalSettingsValidator githubValidator = mock(GithubGlobalSettingsValidator.class);
+ private final GitlabGlobalSettingsValidator gitlabValidator = mock(GitlabGlobalSettingsValidator.class);
+ private final BitbucketCloudValidator bitbucketCloudValidator = mock(BitbucketCloudValidator.class);
+ private final AzureDevOpsValidator azureDevOpsValidator = mock(AzureDevOpsValidator.class);
+ private final Configuration config = mock(Configuration.class);
+
+ private DevOpsPlatformsMetricsCollector collector;
+
+ @Before
+ public void before() {
+ collector = new DevOpsPlatformsMetricsCollector(serverMonitoringMetrics,
+ dbClient, bitbucketServerValidator, githubValidator, gitlabValidator, bitbucketCloudValidator,
+ azureDevOpsValidator, config);
+ }
+
+ @Test
+ public void start_startsNewDeamonThread() {
+ collector.start();
+
+ Optional<Thread> newDeamonThread = findNewDeamonThread();
+
+ assertThat(newDeamonThread).isPresent();
+ assertThat(newDeamonThread.get().isDaemon()).isTrue();
+ }
+
+ @Test
+ public void createTask_givenOneConfigForEachALM_allValidatorsAreCalled() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verify(bitbucketCloudValidator, times(1)).validate(findDto(ALM.BITBUCKET_CLOUD, almSettingDtos));
+ verify(bitbucketServerValidator, times(1)).validate(findDto(ALM.BITBUCKET, almSettingDtos));
+ verify(azureDevOpsValidator, times(1)).validate(findDto(ALM.AZURE_DEVOPS, almSettingDtos));
+ verify(gitlabValidator, times(1)).validate(findDto(ALM.GITLAB, almSettingDtos));
+ verify(githubValidator, times(1)).validate(findDto(ALM.GITHUB, almSettingDtos));
+ }
+
+ @Test
+ public void createTask_givenOnlyGitHubConfigured_validateOnlyGithub() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ AlmSettingDto githubDto = new AlmSettingDto();
+ githubDto.setAlm(ALM.GITHUB);
+ when(dao.selectAll(any())).thenReturn(List.of(githubDto));
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verifyNoInteractions(bitbucketCloudValidator);
+ verifyNoInteractions(bitbucketServerValidator);
+ verifyNoInteractions(azureDevOpsValidator);
+ verifyNoInteractions(gitlabValidator);
+
+ verify(githubValidator, times(1)).validate(githubDto);
+ }
+
+ @Test
+ public void createTask_givenAllValidationsFailing_setAllMetricsStatusesToFalse() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ doThrow(new RuntimeException()).when(bitbucketCloudValidator).validate(any());
+ doThrow(new RuntimeException()).when(bitbucketServerValidator).validate(any());
+ doThrow(new RuntimeException()).when(azureDevOpsValidator).validate(any());
+ doThrow(new RuntimeException()).when(gitlabValidator).validate(any());
+ doThrow(new RuntimeException()).when(githubValidator).validate(any());
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToRed(); //2 validators for Bitbucket
+ verify(serverMonitoringMetrics, times(1)).setAzureStatusToRed();
+ verify(serverMonitoringMetrics, times(1)).setGitlabStatusToRed();
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed();
+ }
+
+ @Test
+ public void createTask_givenAllValidationsArePassing_setAllMetricsStatusesToTrue() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(2)).setBitbucketStatusToGreen(); //2 validators for Bitbucket
+ verify(serverMonitoringMetrics, times(1)).setAzureStatusToGreen();
+ verify(serverMonitoringMetrics, times(1)).setGitlabStatusToGreen();
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToGreen();
+ }
+
+ @Test
+ public void createTask_givenFirstGithubValidationNotPassingAndSecondPassing_setGitHubValidationToTrue() {
+ AlmSettingDao dao = mock(AlmSettingDao.class);
+ List<AlmSettingDto> almSettingDtos = createAlmSettingDtos();
+ when(dao.selectAll(any())).thenReturn(almSettingDtos);
+ when(dbClient.almSettingDao()).thenReturn(dao);
+
+ when(githubValidator.validate(any()))
+ .thenThrow(new RuntimeException())
+ .thenReturn(null);
+
+ collector.createTask().run();
+
+ verify(serverMonitoringMetrics, times(1)).setGithubStatusToRed();
+ verify(serverMonitoringMetrics, times(0)).setGithubStatusToGreen();
+ }
+
+ private AlmSettingDto findDto(ALM alm, List<AlmSettingDto> almSettingDtos) {
+ return almSettingDtos.stream().filter(d -> d.getAlm() == alm).findFirst().get();
+ }
+
+ private List<AlmSettingDto> createAlmSettingDtos() {
+ List<AlmSettingDto> dtos = new ArrayList<>();
+ for(ALM alm : ALM.values()) {
+ AlmSettingDto almSettingDto = new AlmSettingDto();
+ almSettingDto.setAlm(alm);
+ dtos.add(almSettingDto);
+ }
+ return dtos;
+ }
+
+ private Optional<Thread> findNewDeamonThread() {
+ Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
+ String threadPartialName = DevOpsPlatformsMetricsCollector.class.getName();
+ return threadSet.stream().filter(t -> t.getName().contains(threadPartialName)).findFirst();
+ }
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.api.server.ServerSide;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-@ServerSide
-public class BitbucketServerSettingsValidator {
- private final BitbucketServerRestClient bitbucketServerRestClient;
- private final Encryption encryption;
-
- public BitbucketServerSettingsValidator(BitbucketServerRestClient bitbucketServerRestClient, Settings settings) {
- this.bitbucketServerRestClient = bitbucketServerRestClient;
- this.encryption = settings.getEncryption();
- }
-
- public void validate(AlmSettingDto almSettingDto) {
- String bitbucketUrl = almSettingDto.getUrl();
- String bitbucketToken = almSettingDto.getDecryptedPersonalAccessToken(encryption);
- if (bitbucketUrl == null || bitbucketToken == null) {
- throw new IllegalArgumentException("Your global Bitbucket Server configuration is incomplete.");
- }
-
- bitbucketServerRestClient.validateUrl(bitbucketUrl);
- bitbucketServerRestClient.validateToken(bitbucketUrl, bitbucketToken);
- bitbucketServerRestClient.validateReadPermission(bitbucketUrl, bitbucketToken);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import java.util.Optional;
-import org.sonar.alm.client.github.GithubApplicationClient;
-import org.sonar.alm.client.github.GithubApplicationClientImpl;
-import org.sonar.alm.client.github.config.GithubAppConfiguration;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.api.server.ServerSide;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-import static org.apache.commons.lang.StringUtils.isBlank;
-
-@ServerSide
-public class GithubGlobalSettingsValidator {
-
- private final Encryption encryption;
- private final GithubApplicationClient githubApplicationClient;
-
- public GithubGlobalSettingsValidator(GithubApplicationClientImpl githubApplicationClient, Settings settings) {
- this.encryption = settings.getEncryption();
- this.githubApplicationClient = githubApplicationClient;
- }
-
- public GithubAppConfiguration validate(AlmSettingDto settings) {
- long appId;
- try {
- appId = Long.parseLong(Optional.ofNullable(settings.getAppId()).orElseThrow(() -> new IllegalArgumentException("Missing appId")));
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException("Invalid appId; " + e.getMessage());
- }
- if (isBlank(settings.getClientId())) {
- throw new IllegalArgumentException("Missing Client Id");
- }
- if (isBlank(settings.getDecryptedClientSecret(encryption))) {
- throw new IllegalArgumentException("Missing Client Secret");
- }
- GithubAppConfiguration configuration = new GithubAppConfiguration(appId, settings.getDecryptedPrivateKey(encryption),
- settings.getUrl());
-
- githubApplicationClient.checkApiEndpoint(configuration);
- githubApplicationClient.checkAppPermissions(configuration);
-
- return configuration;
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import org.sonar.alm.client.gitlab.GitlabHttpClient;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.api.server.ServerSide;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-@ServerSide
-public class GitlabGlobalSettingsValidator {
-
- private final Encryption encryption;
- private final GitlabHttpClient gitlabHttpClient;
-
- public GitlabGlobalSettingsValidator(GitlabHttpClient gitlabHttpClient, Settings settings) {
- this.encryption = settings.getEncryption();
- this.gitlabHttpClient = gitlabHttpClient;
- }
-
- public void validate(AlmSettingDto almSettingDto) {
- String gitlabUrl = almSettingDto.getUrl();
- String accessToken = almSettingDto.getDecryptedPersonalAccessToken(encryption);
-
- if (gitlabUrl == null || accessToken == null) {
- throw new IllegalArgumentException("Your Gitlab global configuration is incomplete.");
- }
-
- gitlabHttpClient.checkUrl(gitlabUrl);
- gitlabHttpClient.checkToken(gitlabUrl, accessToken);
- gitlabHttpClient.checkReadPermission(gitlabUrl, accessToken);
- gitlabHttpClient.checkWritePermission(gitlabUrl, accessToken);
- }
-
-}
package org.sonar.server.almintegration.ws;
import org.sonar.core.platform.Module;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
import org.sonar.server.almintegration.ws.azure.ImportAzureProjectAction;
import org.sonar.server.almintegration.ws.azure.ListAzureProjectsAction;
import org.sonar.server.almintegration.ws.azure.SearchAzureReposAction;
SearchBitbucketServerReposAction.class,
SearchBitbucketCloudReposAction.class,
GetGithubClientIdAction.class,
- GithubGlobalSettingsValidator.class,
- BitbucketServerSettingsValidator.class,
ImportGithubProjectAction.class,
ListGithubOrganizationsAction.class,
ListGithubRepositoriesAction.class,
ImportGitLabProjectAction.class,
SearchGitlabReposAction.class,
- GitlabGlobalSettingsValidator.class,
ImportAzureProjectAction.class,
ListAzureProjectsAction.class,
SearchAzureReposAction.class,
package org.sonar.server.almsettings.ws;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.server.ws.Request;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.alm.setting.AlmSettingDto;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.server.user.UserSession;
public class ValidateAction implements AlmSettingsWsAction {
private final GitlabGlobalSettingsValidator gitlabSettingsValidator;
private final GithubGlobalSettingsValidator githubGlobalSettingsValidator;
private final BitbucketServerSettingsValidator bitbucketServerSettingsValidator;
- private final BitbucketCloudRestClient bitbucketCloudRestClient;
+ private final BitbucketCloudValidator bitbucketCloudValidator;
+ private final AzureDevOpsValidator azureDevOpsValidator;
public ValidateAction(DbClient dbClient,
Settings settings,
GithubGlobalSettingsValidator githubGlobalSettingsValidator,
GitlabGlobalSettingsValidator gitlabSettingsValidator,
BitbucketServerSettingsValidator bitbucketServerSettingsValidator,
- BitbucketCloudRestClient bitbucketCloudRestClient) {
+ BitbucketCloudValidator bitbucketCloudValidator,
+ AzureDevOpsValidator azureDevOpsValidator) {
this.dbClient = dbClient;
this.encryption = settings.getEncryption();
this.userSession = userSession;
this.githubGlobalSettingsValidator = githubGlobalSettingsValidator;
this.gitlabSettingsValidator = gitlabSettingsValidator;
this.bitbucketServerSettingsValidator = bitbucketServerSettingsValidator;
- this.bitbucketCloudRestClient = bitbucketCloudRestClient;
+ this.bitbucketCloudValidator = bitbucketCloudValidator;
+ this.azureDevOpsValidator = azureDevOpsValidator;
}
@Override
bitbucketServerSettingsValidator.validate(almSettingDto);
break;
case BITBUCKET_CLOUD:
- validateBitbucketCloud(almSettingDto);
+ bitbucketCloudValidator.validate(almSettingDto);
break;
case AZURE_DEVOPS:
- validateAzure(almSettingDto);
+ azureDevOpsValidator.validate(almSettingDto);
break;
}
}
}
-
- private void validateAzure(AlmSettingDto almSettingDto) {
- try {
- azureDevOpsHttpClient.checkPAT(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Invalid Azure URL or Personal Access Token", e);
- }
- }
-
- private void validateBitbucketCloud(AlmSettingDto almSettingDto) {
- bitbucketCloudRestClient.validate(almSettingDto.getClientId(), almSettingDto.getDecryptedClientSecret(encryption), almSettingDto.getAppId());
- }
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.almsettings.AlmSettingsTesting.newBitbucketAlmSettingDto;
-
-public class BitbucketServerSettingsValidatorTest {
- private static final Encryption encryption = mock(Encryption.class);
- private static final Settings settings = mock(Settings.class);
-
- private final BitbucketServerRestClient bitbucketServerRestClient = mock(BitbucketServerRestClient.class);
- private final BitbucketServerSettingsValidator underTest = new BitbucketServerSettingsValidator(bitbucketServerRestClient, settings);
-
- @BeforeClass
- public static void setUp() {
- when(settings.getEncryption()).thenReturn(encryption);
- }
-
- @Test
- public void validate_success() {
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken("abc");
- when(encryption.isEncrypted(any())).thenReturn(false);
-
- underTest.validate(almSettingDto);
-
- verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com");
- verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", "abc");
- verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", "abc");
- }
-
- @Test
- public void validate_success_with_encrypted_token() {
- String encryptedToken = "abc";
- String decryptedToken = "decrypted-token";
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken(encryptedToken);
- when(encryption.isEncrypted(encryptedToken)).thenReturn(true);
- when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken);
-
- underTest.validate(almSettingDto);
-
- verify(bitbucketServerRestClient, times(1)).validateUrl("http://abc.com");
- verify(bitbucketServerRestClient, times(1)).validateToken("http://abc.com", decryptedToken);
- verify(bitbucketServerRestClient, times(1)).validateReadPermission("http://abc.com", decryptedToken);
- }
-
- @Test
- public void validate_failure_on_incomplete_configuration() {
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl(null)
- .setPersonalAccessToken("abc");
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class);
- }
-
- @Test
- public void validate_failure_on_bitbucket_server_api_error() {
- doThrow(new IllegalArgumentException("error")).when(bitbucketServerRestClient).validateUrl(anyString());
- AlmSettingDto almSettingDto = newBitbucketAlmSettingDto()
- .setUrl("http://abc.com")
- .setPersonalAccessToken("abc");
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class);
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.sonar.alm.client.github.GithubApplicationClientImpl;
-import org.sonar.alm.client.github.config.GithubAppConfiguration;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.almsettings.AlmSettingsTesting.newGithubAlmSettingDto;
-
-public class GithubGlobalSettingsValidatorTest {
- private static final Encryption encryption = mock(Encryption.class);
- private static final Settings settings = mock(Settings.class);
-
- private final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class);
- private final GithubGlobalSettingsValidator underTest = new GithubGlobalSettingsValidator(appClient, settings);
-
- @BeforeClass
- public static void setUp() {
- when(settings.getEncryption()).thenReturn(encryption);
- }
-
- @Test
- public void github_global_settings_validation() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setClientId("clientId")
- .setClientSecret("clientSecret");
- when(encryption.isEncrypted(any())).thenReturn(false);
-
- GithubAppConfiguration configuration = underTest.validate(almSettingDto);
-
- ArgumentCaptor<GithubAppConfiguration> configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class);
- verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture());
- verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture());
- assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId());
- assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId());
- }
-
- @Test
- public void github_global_settings_validation_with_encrypted_key() {
- String encryptedKey = "encrypted-key";
- String decryptedKey = "decrypted-key";
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setClientId("clientId")
- .setPrivateKey(encryptedKey)
- .setClientSecret("clientSecret");
- when(encryption.isEncrypted(encryptedKey)).thenReturn(true);
- when(encryption.decrypt(encryptedKey)).thenReturn(decryptedKey);
-
- GithubAppConfiguration configuration = underTest.validate(almSettingDto);
-
- ArgumentCaptor<GithubAppConfiguration> configurationArgumentCaptor = ArgumentCaptor.forClass(GithubAppConfiguration.class);
- verify(appClient).checkApiEndpoint(configurationArgumentCaptor.capture());
- verify(appClient).checkAppPermissions(configurationArgumentCaptor.capture());
- assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getId());
- assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(0).getPrivateKey());
- assertThat(configuration.getId()).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getId());
- assertThat(decryptedKey).isEqualTo(configurationArgumentCaptor.getAllValues().get(1).getPrivateKey());
- }
-
- @Test
- public void github_validation_checks_invalid_appId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto()
- .setAppId("abc")
- .setClientId("clientId")
- .setClientSecret("clientSecret");
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Invalid appId; For input string: \"abc\"");
- }
-
- @Test
- public void github_validation_checks_missing_appId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setAppId(null);
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Missing appId");
- }
-
- @Test
- public void github_validation_checks_missing_clientId() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientId(null);
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Missing Client Id");
- }
-
- @Test
- public void github_validation_checks_missing_clientSecret() {
- AlmSettingDto almSettingDto = newGithubAlmSettingDto().setClientSecret(null);
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Missing Client Secret");
-
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2021 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.almintegration.validator;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.sonar.alm.client.gitlab.GitlabHttpClient;
-import org.sonar.api.config.internal.Encryption;
-import org.sonar.api.config.internal.Settings;
-import org.sonar.db.alm.setting.AlmSettingDto;
-
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class GitlabGlobalSettingsValidatorTest {
- private static final Encryption encryption = mock(Encryption.class);
- private static final Settings settings = mock(Settings.class);
-
- private final GitlabHttpClient gitlabHttpClient = mock(GitlabHttpClient.class);
-
- private final GitlabGlobalSettingsValidator underTest = new GitlabGlobalSettingsValidator(gitlabHttpClient, settings);
-
- @BeforeClass
- public static void setUp() {
- when(settings.getEncryption()).thenReturn(encryption);
- }
-
- @Test
- public void validate_success() {
- String token = "personal-access-token";
- AlmSettingDto almSettingDto = new AlmSettingDto()
- .setUrl("https://gitlab.com/api")
- .setPersonalAccessToken("personal-access-token");
- when(encryption.isEncrypted(token)).thenReturn(false);
-
- underTest.validate(almSettingDto);
- verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl());
- verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
- verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
- verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), almSettingDto.getDecryptedPersonalAccessToken(encryption));
- }
-
- @Test
- public void validate_success_with_encrypted_token() {
- String encryptedToken = "personal-access-token";
- String decryptedToken = "decrypted-token";
- AlmSettingDto almSettingDto = new AlmSettingDto()
- .setUrl("https://gitlab.com/api")
- .setPersonalAccessToken(encryptedToken);
- when(encryption.isEncrypted(encryptedToken)).thenReturn(true);
- when(encryption.decrypt(encryptedToken)).thenReturn(decryptedToken);
-
- underTest.validate(almSettingDto);
-
- verify(gitlabHttpClient, times(1)).checkUrl(almSettingDto.getUrl());
- verify(gitlabHttpClient, times(1)).checkToken(almSettingDto.getUrl(), decryptedToken);
- verify(gitlabHttpClient, times(1)).checkReadPermission(almSettingDto.getUrl(), decryptedToken);
- verify(gitlabHttpClient, times(1)).checkWritePermission(almSettingDto.getUrl(), decryptedToken);
- }
-
- @Test
- public void validate_fail_url_not_set() {
- AlmSettingDto almSettingDto = new AlmSettingDto()
- .setUrl(null)
- .setPersonalAccessToken("personal-access-token");
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Your Gitlab global configuration is incomplete.");
- }
-
- @Test
- public void validate_fail_pat_not_set() {
- AlmSettingDto almSettingDto = new AlmSettingDto()
- .setUrl("https://gitlab.com/api")
- .setPersonalAccessToken(null);
-
- assertThatThrownBy(() -> underTest.validate(almSettingDto))
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Your Gitlab global configuration is incomplete.");
- }
-
-}
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.api.config.internal.Encryption;
import org.sonar.api.config.internal.Settings;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
import org.sonar.db.user.UserDto;
-import org.sonar.server.almintegration.validator.BitbucketServerSettingsValidator;
-import org.sonar.server.almintegration.validator.GithubGlobalSettingsValidator;
-import org.sonar.server.almintegration.validator.GitlabGlobalSettingsValidator;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.server.almsettings.MultipleAlmFeatureProvider;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.exceptions.ForbiddenException;
private final ComponentFinder componentFinder = new ComponentFinder(db.getDbClient(), mock(ResourceTypes.class));
private final AlmSettingsSupport almSettingsSupport = new AlmSettingsSupport(db.getDbClient(), userSession, componentFinder, multipleAlmFeatureProvider);
private final AzureDevOpsHttpClient azureDevOpsHttpClient = mock(AzureDevOpsHttpClient.class);
+ private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
private final GitlabGlobalSettingsValidator gitlabSettingsValidator = mock(GitlabGlobalSettingsValidator.class);
private final GithubGlobalSettingsValidator githubGlobalSettingsValidator = mock(GithubGlobalSettingsValidator.class);
private final BitbucketServerSettingsValidator bitbucketServerSettingsValidator = mock(BitbucketServerSettingsValidator.class);
- private final BitbucketCloudRestClient bitbucketCloudRestClient = mock(BitbucketCloudRestClient.class);
+ private final BitbucketCloudValidator bitbucketCloudValidator = new BitbucketCloudValidator(bitbucketCloudRestClient, settings);
+ private final AzureDevOpsValidator azureDevOpsValidator = new AzureDevOpsValidator(azureDevOpsHttpClient, settings);
private final WsActionTester ws = new WsActionTester(
new ValidateAction(db.getDbClient(), settings, userSession, almSettingsSupport, azureDevOpsHttpClient, githubGlobalSettingsValidator,
- gitlabSettingsValidator, bitbucketServerSettingsValidator, bitbucketCloudRestClient));
+ gitlabSettingsValidator, bitbucketServerSettingsValidator, bitbucketCloudValidator, azureDevOpsValidator));
@BeforeClass
public static void setUp() {
compile project(':server:sonar-process')
compile project(':server:sonar-webserver-core')
compile project(':server:sonar-webserver-webapi')
+ compile project(':server:sonar-webserver-monitoring')
compileOnly 'com.google.code.findbugs:jsr305'
import java.util.List;
import org.sonar.alm.client.TimeoutConfigurationImpl;
import org.sonar.alm.client.azure.AzureDevOpsHttpClient;
+import org.sonar.alm.client.azure.AzureDevOpsValidator;
import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudRestClient;
+import org.sonar.alm.client.bitbucket.bitbucketcloud.BitbucketCloudValidator;
import org.sonar.alm.client.bitbucketserver.BitbucketServerRestClient;
+import org.sonar.alm.client.bitbucketserver.BitbucketServerSettingsValidator;
import org.sonar.alm.client.github.GithubApplicationClientImpl;
import org.sonar.alm.client.github.GithubApplicationHttpClientImpl;
+import org.sonar.alm.client.github.GithubGlobalSettingsValidator;
import org.sonar.alm.client.github.security.GithubAppSecurityImpl;
+import org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator;
import org.sonar.alm.client.gitlab.GitlabHttpClient;
import org.sonar.api.profiles.XMLProfileParser;
import org.sonar.api.profiles.XMLProfileSerializer;
import org.sonar.server.metric.UnanalyzedLanguageMetrics;
import org.sonar.server.metric.ws.MetricsWsModule;
import org.sonar.server.monitoring.MonitoringWsModule;
+import org.sonar.server.monitoring.devops.DevOpsPlatformsMetricsCollector;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule;
import org.sonar.server.notification.NotificationModule;
import org.sonar.server.notification.ws.NotificationWsModule;
GitlabHttpClient.class,
AzureDevOpsHttpClient.class,
AlmIntegrationsWSModule.class,
+ BitbucketCloudValidator.class,
+ BitbucketServerSettingsValidator.class,
+ GithubGlobalSettingsValidator.class,
+ GitlabGlobalSettingsValidator.class,
+ AzureDevOpsValidator.class,
// ALM settings
AlmSettingsWsModule.class,
TelemetryDaemon.class,
TelemetryClient.class,
+ // monitoring
+ ServerMonitoringMetrics.class,
+ DevOpsPlatformsMetricsCollector.class,
+
PluginsRiskConsentFilter.class
);
package org.sonar.server.platform.platformlevel;
import org.sonar.server.authentication.SafeModeUserSession;
+import org.sonar.server.monitoring.ServerMonitoringMetrics;
import org.sonar.server.platform.ServerImpl;
import org.sonar.server.platform.db.migration.AutoDbMigration;
import org.sonar.server.platform.db.migration.DatabaseMigrationImpl;
// WS engine
SafeModeUserSession.class,
WebServiceEngine.class,
- WebServiceFilter.class);
+ WebServiceFilter.class,
+
+ // Monitoring
+ ServerMonitoringMetrics.class);
addIfStartupLeader(
DatabaseMigrationImpl.class,
MigrationEngineModule.class,
include 'server:sonar-webserver-webapi'
include 'server:sonar-webserver-ws'
include 'server:sonar-alm-client'
+include 'server:sonar-webserver-monitoring'
include 'sonar-application'
include 'sonar-check-api'