You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ResetAction.java 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.setting.ws;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import java.util.Optional;
  24. import java.util.stream.Collectors;
  25. import javax.annotation.CheckForNull;
  26. import javax.annotation.Nullable;
  27. import org.sonar.api.config.PropertyDefinition;
  28. import org.sonar.api.config.PropertyDefinitions;
  29. import org.sonar.api.server.ws.Change;
  30. import org.sonar.api.server.ws.Request;
  31. import org.sonar.api.server.ws.Response;
  32. import org.sonar.api.server.ws.WebService;
  33. import org.sonar.api.web.UserRole;
  34. import org.sonar.db.DbClient;
  35. import org.sonar.db.DbSession;
  36. import org.sonar.db.entity.EntityDto;
  37. import org.sonar.server.exceptions.NotFoundException;
  38. import org.sonar.server.setting.ws.SettingValidations.SettingData;
  39. import org.sonar.server.user.UserSession;
  40. import static java.lang.String.format;
  41. import static java.util.Collections.emptyList;
  42. import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
  43. import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
  44. import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_KEYS;
  45. import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
  46. import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
  47. public class ResetAction implements SettingsWsAction {
  48. private final DbClient dbClient;
  49. private final SettingsUpdater settingsUpdater;
  50. private final UserSession userSession;
  51. private final PropertyDefinitions definitions;
  52. private final SettingValidations validations;
  53. public ResetAction(DbClient dbClient, SettingsUpdater settingsUpdater, UserSession userSession, PropertyDefinitions definitions, SettingValidations validations) {
  54. this.dbClient = dbClient;
  55. this.settingsUpdater = settingsUpdater;
  56. this.userSession = userSession;
  57. this.definitions = definitions;
  58. this.validations = validations;
  59. }
  60. @Override
  61. public void define(WebService.NewController context) {
  62. WebService.NewAction action = context.createAction("reset")
  63. .setDescription("Remove a setting value.<br>" +
  64. "The settings defined in conf/sonar.properties are read-only and can't be changed.<br/>" +
  65. "Requires one of the following permissions: " +
  66. "<ul>" +
  67. "<li>'Administer System'</li>" +
  68. "<li>'Administer' rights on the specified component</li>" +
  69. "</ul>")
  70. .setSince("6.1")
  71. .setChangelog(
  72. new Change("10.1", "Param 'component' now only accept keys for projects, applications, portfolios or subportfolios"),
  73. new Change("10.1", format("Internal parameters '%s' and '%s' were removed", PARAM_BRANCH, PARAM_PULL_REQUEST)),
  74. new Change("8.8", "Deprecated parameter 'componentKey' has been removed"),
  75. new Change("7.6", format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT)),
  76. new Change("7.1", "The settings defined in conf/sonar.properties are read-only and can't be changed"))
  77. .setPost(true)
  78. .setHandler(this);
  79. action.createParam(PARAM_KEYS)
  80. .setDescription("Comma-separated list of keys")
  81. .setExampleValue("sonar.links.scm,sonar.debt.hoursInDay")
  82. .setRequired(true);
  83. action.createParam(PARAM_COMPONENT)
  84. .setDescription("Component key. Only keys for projects, applications, portfolios or subportfolios are accepted.")
  85. .setExampleValue(KEY_PROJECT_EXAMPLE_001);
  86. }
  87. @Override
  88. public void handle(Request request, Response response) throws Exception {
  89. try (DbSession dbSession = dbClient.openSession(false)) {
  90. ResetRequest resetRequest = toWsRequest(request);
  91. Optional<EntityDto> entity = getEntity(dbSession, resetRequest);
  92. checkPermissions(entity);
  93. resetRequest.getKeys().forEach(key -> {
  94. SettingsWsSupport.validateKey(key);
  95. SettingData data = new SettingData(key, emptyList(), entity.orElse(null));
  96. validations.validateScope(data);
  97. validations.validateQualifier(data);
  98. });
  99. List<String> keys = getKeys(resetRequest);
  100. if (entity.isPresent()) {
  101. settingsUpdater.deleteComponentSettings(dbSession, entity.get(), keys);
  102. } else {
  103. settingsUpdater.deleteGlobalSettings(dbSession, keys);
  104. }
  105. dbSession.commit();
  106. response.noContent();
  107. }
  108. }
  109. private List<String> getKeys(ResetRequest request) {
  110. return new ArrayList<>(request.getKeys().stream()
  111. .map(key -> {
  112. PropertyDefinition definition = definitions.get(key);
  113. return definition != null ? definition.key() : key;
  114. })
  115. .collect(Collectors.toSet()));
  116. }
  117. private static ResetRequest toWsRequest(Request request) {
  118. return new ResetRequest()
  119. .setKeys(request.mandatoryParamAsStrings(PARAM_KEYS))
  120. .setEntity(request.param(PARAM_COMPONENT));
  121. }
  122. private Optional<EntityDto> getEntity(DbSession dbSession, ResetRequest request) {
  123. String componentKey = request.getEntity();
  124. if (componentKey == null) {
  125. return Optional.empty();
  126. }
  127. return Optional.of(dbClient.entityDao().selectByKey(dbSession, componentKey)
  128. .orElseThrow(() -> new NotFoundException(format("Component key '%s' not found", componentKey))));
  129. }
  130. private void checkPermissions(Optional<EntityDto> component) {
  131. if (component.isPresent()) {
  132. userSession.checkEntityPermission(UserRole.ADMIN, component.get());
  133. } else {
  134. userSession.checkIsSystemAdministrator();
  135. }
  136. }
  137. private static class ResetRequest {
  138. private String entity = null;
  139. private List<String> keys = null;
  140. public ResetRequest setEntity(@Nullable String entity) {
  141. this.entity = entity;
  142. return this;
  143. }
  144. @CheckForNull
  145. public String getEntity() {
  146. return entity;
  147. }
  148. public ResetRequest setKeys(List<String> keys) {
  149. this.keys = keys;
  150. return this;
  151. }
  152. public List<String> getKeys() {
  153. return keys;
  154. }
  155. }
  156. }