verify(managedInstanceService, times(0)).queueSynchronisationTask();
}
+ @Test
+ public void updateConfiguration_whenURLChangesWithoutSecret_shouldFail() {
+ gitlabConfigurationService.createConfiguration(buildGitlabConfiguration(JIT));
+
+ UpdateGitlabConfigurationRequest updateUrlRequest = builder()
+ .gitlabConfigurationId(UNIQUE_GITLAB_CONFIGURATION_ID)
+ .url(withValueOrThrow("http://malicious.url"))
+ .build();
+
+ assertThatThrownBy(() -> gitlabConfigurationService.updateConfiguration(updateUrlRequest))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("For security reasons, the URL can't be updated without providing the secret.");
+ }
+
+ @Test
+ public void updateConfiguration_whenURLChangesWithAllSecrets_shouldUpdate() {
+ gitlabConfigurationService.createConfiguration(buildGitlabConfiguration(JIT));
+
+ UpdateGitlabConfigurationRequest updateUrlRequest = builder()
+ .gitlabConfigurationId(UNIQUE_GITLAB_CONFIGURATION_ID)
+ .url(withValueOrThrow("http://new.url"))
+ .secret(withValueOrThrow("new_secret"))
+ .build();
+
+ gitlabConfigurationService.updateConfiguration(updateUrlRequest);
+
+ verifySettingWasSet(GITLAB_AUTH_URL, "http://new.url");
+ verifySettingWasSet(GITLAB_AUTH_SECRET, "new_secret");
+ }
+
private static void assertConfigurationFields(GitlabConfiguration configuration) {
assertThat(configuration).isNotNull();
assertThat(configuration.id()).isEqualTo("gitlab-configuration");
*/
package org.sonar.server.common;
-import java.util.StringJoiner;
-import javax.annotation.Nullable;
import java.util.Objects;
+import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Function;
+import javax.annotation.Nullable;
public class UpdatedValue<T> {
final T value;
return false;
}
+ public boolean isDefined() {
+ return isDefined;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator.ValidationMode.AUTH_ONLY;
import static org.sonar.alm.client.gitlab.GitlabGlobalSettingsValidator.ValidationMode.COMPLETE;
+import static org.sonar.api.utils.Preconditions.checkArgument;
import static org.sonar.api.utils.Preconditions.checkState;
import static org.sonar.auth.gitlab.GitLabSettings.GITLAB_AUTH_ALLOWED_GROUPS;
import static org.sonar.auth.gitlab.GitLabSettings.GITLAB_AUTH_ALLOW_USERS_TO_SIGNUP;
public GitlabConfiguration updateConfiguration(UpdateGitlabConfigurationRequest updateRequest) {
UpdatedValue<Boolean> provisioningEnabled = updateRequest.provisioningType().map(GitlabConfigurationService::shouldEnableAutoProvisioning);
+ throwIfUrlIsUpdatedWithoutSecrets(updateRequest);
try (DbSession dbSession = dbClient.openSession(true)) {
throwIfConfigurationDoesntExist(dbSession);
GitlabConfiguration currentConfiguration = getConfiguration(updateRequest.gitlabConfigurationId(), dbSession);
}
}
+ private static void throwIfUrlIsUpdatedWithoutSecrets(UpdateGitlabConfigurationRequest request) {
+ if (request.url().isDefined()) {
+ checkArgument(request.secret().isDefined(), "For security reasons, the URL can't be updated without providing the secret.");
+ }
+ }
+
private void setIfDefined(DbSession dbSession, String propertyName, UpdatedValue<String> value) {
value
.map(definedValue -> new PropertyDto().setKey(propertyName).setValue(definedValue))
.hasMessage(format("Setting '%s' can only be used in sonar.properties", settingKey));
}
+ @Test
+ public void fail_when_setting_key_is_forbidden() {
+ TestRequest testRequest = ws.newRequest()
+ .setParam("key", "sonar.auth.gitlab.url")
+ .setParam("value", "http://malicious.url");
+ assertThatThrownBy(testRequest::execute)
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("For security reasons, the key 'sonar.auth.gitlab.url' cannot be updated using this webservice. Please use the API v2");
+ }
+
@Test
public void definition() {
WebService.Action definition = ws.getDef();
private static final Collector<CharSequence, ?, String> COMMA_JOINER = Collectors.joining(",");
private static final String MSG_NO_EMPTY_VALUE = "A non empty value must be provided";
private static final int VALUE_MAXIMUM_LENGTH = 4000;
- private static final TypeToken<Map<String, String>> MAP_TYPE_TOKEN = new TypeToken<>() {
- };
+ private static final TypeToken<Map<String, String>> MAP_TYPE_TOKEN = new TypeToken<>() {};
+ private static final Set<String> FORBIDDEN_KEYS = Set.of("sonar.auth.gitlab.url");
+
private final PropertyDefinitions propertyDefinitions;
private final DbClient dbClient;
public void handle(Request request, Response response) throws Exception {
try (DbSession dbSession = dbClient.openSession(false)) {
SetRequest wsRequest = toWsRequest(request);
+ throwIfForbiddenKey(wsRequest.getKey());
SettingsWsSupport.validateKey(wsRequest.getKey());
doHandle(dbSession, wsRequest);
}
response.noContent();
}
+ private static void throwIfForbiddenKey(String key) {
+ if (FORBIDDEN_KEYS.contains(key)) {
+ throw new IllegalArgumentException(format("For security reasons, the key '%s' cannot be updated using this webservice. Please use the API v2", key));
+ }
+ }
+
private void doHandle(DbSession dbSession, SetRequest request) {
Optional<EntityDto> component = searchEntity(dbSession, request);
String projectKey = component.map(EntityDto::getKey).orElse(null);