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.

ProjectDataLoader.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 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.batch;
  21. import com.google.common.collect.ArrayListMultimap;
  22. import com.google.common.collect.ImmutableList;
  23. import com.google.common.collect.Maps;
  24. import com.google.common.collect.Multimap;
  25. import java.util.Collections;
  26. import java.util.Date;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import javax.annotation.Nullable;
  31. import org.sonar.api.resources.Qualifiers;
  32. import org.sonar.api.resources.Scopes;
  33. import org.sonar.api.server.ServerSide;
  34. import org.sonar.core.util.stream.MoreCollectors;
  35. import org.sonar.db.DbClient;
  36. import org.sonar.db.DbSession;
  37. import org.sonar.db.component.ComponentDto;
  38. import org.sonar.db.component.FilePathWithHashDto;
  39. import org.sonar.db.permission.OrganizationPermission;
  40. import org.sonar.db.property.PropertyDto;
  41. import org.sonar.scanner.protocol.input.FileData;
  42. import org.sonar.scanner.protocol.input.ProjectRepositories;
  43. import org.sonar.server.component.ComponentFinder;
  44. import org.sonar.server.exceptions.ForbiddenException;
  45. import org.sonar.server.user.UserSession;
  46. import static com.google.common.collect.Lists.newArrayList;
  47. import static com.google.common.collect.Maps.newHashMap;
  48. import static org.sonar.api.web.UserRole.USER;
  49. import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
  50. import static org.sonar.core.util.stream.MoreCollectors.index;
  51. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  52. import static org.sonar.server.ws.WsUtils.checkRequest;
  53. @ServerSide
  54. public class ProjectDataLoader {
  55. private final DbClient dbClient;
  56. private final UserSession userSession;
  57. private final ComponentFinder componentFinder;
  58. public ProjectDataLoader(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
  59. this.dbClient = dbClient;
  60. this.userSession = userSession;
  61. this.componentFinder = componentFinder;
  62. }
  63. public ProjectRepositories load(ProjectDataQuery query) {
  64. try (DbSession session = dbClient.openSession(false)) {
  65. ProjectRepositories data = new ProjectRepositories();
  66. String moduleKey = query.getModuleKey();
  67. String branch = query.getBranch();
  68. String pullRequest = query.getPullRequest();
  69. ComponentDto mainModule = componentFinder.getByKey(session, moduleKey);
  70. checkRequest(isProjectOrModule(mainModule), "Key '%s' belongs to a component which is not a Project", moduleKey);
  71. boolean hasScanPerm = userSession.hasComponentPermission(SCAN_EXECUTION, mainModule) ||
  72. userSession.hasPermission(OrganizationPermission.SCAN, mainModule.getOrganizationUuid());
  73. boolean hasBrowsePerm = userSession.hasComponentPermission(USER, mainModule);
  74. checkPermission(query.isIssuesMode(), hasScanPerm, hasBrowsePerm);
  75. ComponentDto branchOrMainModule = (branch == null && pullRequest == null) ? mainModule
  76. : componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, moduleKey, branch, pullRequest);
  77. ComponentDto project = getProject(branchOrMainModule, session);
  78. if (!project.getKey().equals(branchOrMainModule.getKey())) {
  79. addSettings(data, branchOrMainModule.getKey(), getSettingsFromParents(branchOrMainModule, hasScanPerm, session));
  80. }
  81. List<ComponentDto> modulesTree = dbClient.componentDao().selectEnabledDescendantModules(session, branchOrMainModule.uuid());
  82. List<PropertyDto> modulesTreeSettings = dbClient.propertiesDao().selectEnabledDescendantModuleProperties(mainModule.uuid(), session);
  83. TreeModuleSettings treeModuleSettings = new TreeModuleSettings(session, modulesTree, modulesTreeSettings);
  84. addSettingsToChildrenModules(data, moduleKey, Maps.newHashMap(), treeModuleSettings, hasScanPerm);
  85. List<FilePathWithHashDto> files = searchFilesWithHashAndRevision(session, branchOrMainModule);
  86. addFileData(data, modulesTree, files);
  87. // FIXME need real value but actually only used to know if there is a previous analysis in local issue tracking mode so any value is
  88. // ok
  89. data.setLastAnalysisDate(new Date());
  90. return data;
  91. }
  92. }
  93. private static boolean isProjectOrModule(ComponentDto module) {
  94. if (!Scopes.PROJECT.equals(module.scope())) {
  95. return false;
  96. }
  97. return Qualifiers.PROJECT.equals(module.qualifier()) || Qualifiers.MODULE.equals(module.qualifier());
  98. }
  99. private List<FilePathWithHashDto> searchFilesWithHashAndRevision(DbSession session, @Nullable ComponentDto module) {
  100. if (module == null) {
  101. return Collections.emptyList();
  102. }
  103. return module.isRootProject() ? dbClient.componentDao().selectEnabledFilesFromProject(session, module.uuid())
  104. : dbClient.componentDao().selectEnabledDescendantFiles(session, module.uuid());
  105. }
  106. private ComponentDto getProject(ComponentDto module, DbSession session) {
  107. if (!module.isRootProject()) {
  108. return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid());
  109. } else {
  110. return module;
  111. }
  112. }
  113. private Map<String, String> getSettingsFromParents(ComponentDto module, boolean hasScanPerm, DbSession session) {
  114. List<ComponentDto> parents = newArrayList();
  115. aggregateParentModules(module, parents, session);
  116. Collections.reverse(parents);
  117. Map<String, String> parentProperties = newHashMap();
  118. for (ComponentDto parent : parents) {
  119. parentProperties.putAll(getPropertiesMap(dbClient.propertiesDao().selectProjectProperties(session, parent.getKey()), hasScanPerm));
  120. }
  121. return parentProperties;
  122. }
  123. private void aggregateParentModules(ComponentDto component, List<ComponentDto> parents, DbSession session) {
  124. String moduleUuid = component.moduleUuid();
  125. if (moduleUuid != null) {
  126. ComponentDto parent = dbClient.componentDao().selectOrFailByUuid(session, moduleUuid);
  127. if (parent != null) {
  128. parents.add(parent);
  129. aggregateParentModules(parent, parents, session);
  130. }
  131. }
  132. }
  133. private static void addSettingsToChildrenModules(ProjectRepositories ref, String moduleKey, Map<String, String> parentProperties, TreeModuleSettings treeModuleSettings,
  134. boolean hasScanPerm) {
  135. Map<String, String> currentParentProperties = newHashMap();
  136. currentParentProperties.putAll(parentProperties);
  137. currentParentProperties.putAll(getPropertiesMap(treeModuleSettings.findModuleSettings(moduleKey), hasScanPerm));
  138. addSettings(ref, moduleKey, currentParentProperties);
  139. for (ComponentDto childModule : treeModuleSettings.findChildrenModule(moduleKey)) {
  140. addSettings(ref, childModule.getKey(), currentParentProperties);
  141. addSettingsToChildrenModules(ref, childModule.getKey(), currentParentProperties, treeModuleSettings, hasScanPerm);
  142. }
  143. }
  144. private static void addSettings(ProjectRepositories ref, String module, Map<String, String> properties) {
  145. if (!properties.isEmpty()) {
  146. ref.addSettings(module, properties);
  147. }
  148. }
  149. private static Map<String, String> getPropertiesMap(List<PropertyDto> propertyDtos, boolean hasScanPerm) {
  150. Map<String, String> properties = newHashMap();
  151. for (PropertyDto propertyDto : propertyDtos) {
  152. String key = propertyDto.getKey();
  153. String value = propertyDto.getValue();
  154. if (isPropertyAllowed(key, hasScanPerm)) {
  155. properties.put(key, value);
  156. }
  157. }
  158. return properties;
  159. }
  160. private static boolean isPropertyAllowed(String key, boolean hasScanPerm) {
  161. return !key.contains(".secured") || hasScanPerm;
  162. }
  163. private static void addFileData(ProjectRepositories data, List<ComponentDto> moduleChildren, List<FilePathWithHashDto> files) {
  164. Map<String, String> moduleKeysByUuid = newHashMap();
  165. for (ComponentDto module : moduleChildren) {
  166. moduleKeysByUuid.put(module.uuid(), module.getKey());
  167. }
  168. for (FilePathWithHashDto file : files) {
  169. FileData fileData = new FileData(file.getSrcHash(), file.getRevision());
  170. data.addFileData(moduleKeysByUuid.get(file.getModuleUuid()), file.getPath(), fileData);
  171. }
  172. }
  173. private static void checkPermission(boolean preview, boolean hasScanPerm, boolean hasBrowsePerm) {
  174. if (!hasBrowsePerm && !hasScanPerm) {
  175. throw new ForbiddenException(Messages.NO_PERMISSION);
  176. }
  177. if (!preview && !hasScanPerm) {
  178. throw new ForbiddenException("You're only authorized to execute a local (preview) SonarQube analysis without pushing the results to the SonarQube server. " +
  179. "Please contact your SonarQube administrator.");
  180. }
  181. if (preview && !hasBrowsePerm) {
  182. throw new ForbiddenException("You don't have the required permissions to access this project. Please contact your SonarQube administrator.");
  183. }
  184. }
  185. private class TreeModuleSettings {
  186. private Map<String, ComponentDto> modulesByKey;
  187. private Multimap<String, PropertyDto> propertiesByModuleKey;
  188. private Multimap<String, ComponentDto> moduleChildrenByModuleUuid;
  189. private TreeModuleSettings(DbSession session, List<ComponentDto> moduleChildren, List<PropertyDto> moduleChildrenSettings) {
  190. modulesByKey = moduleChildren.stream().collect(uniqueIndex(ComponentDto::getKey));
  191. moduleChildrenByModuleUuid = ArrayListMultimap.create();
  192. Set<Long> propertiesByComponentId = moduleChildrenSettings.stream().map(PropertyDto::getResourceId).collect(MoreCollectors.toSet());
  193. Map<Long, ComponentDto> componentsById = dbClient.componentDao().selectByIds(session, propertiesByComponentId).stream().collect(uniqueIndex(ComponentDto::getId));
  194. propertiesByModuleKey = moduleChildrenSettings.stream().collect(index(s -> componentsById.get(s.getResourceId()).getKey()));
  195. moduleChildrenByModuleUuid = moduleChildren.stream().filter(c -> c.moduleUuid() != null).collect(index(ComponentDto::moduleUuid));
  196. }
  197. List<PropertyDto> findModuleSettings(String moduleKey) {
  198. return ImmutableList.copyOf(propertiesByModuleKey.get(moduleKey));
  199. }
  200. List<ComponentDto> findChildrenModule(String moduleKey) {
  201. String moduleUuid = modulesByKey.get(moduleKey).uuid();
  202. return ImmutableList.copyOf(moduleChildrenByModuleUuid.get(moduleUuid));
  203. }
  204. }
  205. }