Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

OrganizationUpdaterImpl.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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.organization;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.Date;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Optional;
  27. import java.util.function.Consumer;
  28. import javax.annotation.Nullable;
  29. import org.sonar.api.config.Configuration;
  30. import org.sonar.api.utils.System2;
  31. import org.sonar.core.config.CorePropertyDefinitions;
  32. import org.sonar.core.util.UuidFactory;
  33. import org.sonar.db.DbClient;
  34. import org.sonar.db.DbSession;
  35. import org.sonar.db.organization.DefaultTemplates;
  36. import org.sonar.db.organization.OrganizationDto;
  37. import org.sonar.db.organization.OrganizationMemberDto;
  38. import org.sonar.db.permission.GroupPermissionDto;
  39. import org.sonar.db.permission.OrganizationPermission;
  40. import org.sonar.db.permission.UserPermissionDto;
  41. import org.sonar.db.permission.template.PermissionTemplateCharacteristicDto;
  42. import org.sonar.db.permission.template.PermissionTemplateDto;
  43. import org.sonar.db.qualitygate.QualityGateDto;
  44. import org.sonar.db.qualityprofile.DefaultQProfileDto;
  45. import org.sonar.db.qualityprofile.OrgQProfileDto;
  46. import org.sonar.db.user.GroupDto;
  47. import org.sonar.db.user.UserDto;
  48. import org.sonar.db.user.UserGroupDto;
  49. import org.sonar.server.permission.PermissionService;
  50. import org.sonar.server.qualityprofile.BuiltInQProfile;
  51. import org.sonar.server.qualityprofile.BuiltInQProfileRepository;
  52. import org.sonar.server.qualityprofile.QProfileName;
  53. import org.sonar.server.user.index.UserIndexer;
  54. import org.sonar.server.usergroups.DefaultGroupCreator;
  55. import static com.google.common.base.Preconditions.checkState;
  56. import static java.lang.String.format;
  57. import static java.util.Objects.requireNonNull;
  58. import static org.sonar.api.web.UserRole.ADMIN;
  59. import static org.sonar.api.web.UserRole.CODEVIEWER;
  60. import static org.sonar.api.web.UserRole.ISSUE_ADMIN;
  61. import static org.sonar.api.web.UserRole.SECURITYHOTSPOT_ADMIN;
  62. import static org.sonar.api.web.UserRole.USER;
  63. import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
  64. import static org.sonar.db.organization.OrganizationDto.Subscription.FREE;
  65. import static org.sonar.db.permission.OrganizationPermission.SCAN;
  66. import static org.sonar.server.organization.OrganizationUpdater.NewOrganization.newOrganizationBuilder;
  67. public class OrganizationUpdaterImpl implements OrganizationUpdater {
  68. private final DbClient dbClient;
  69. private final System2 system2;
  70. private final UuidFactory uuidFactory;
  71. private final OrganizationValidation organizationValidation;
  72. private final Configuration config;
  73. private final BuiltInQProfileRepository builtInQProfileRepository;
  74. private final DefaultGroupCreator defaultGroupCreator;
  75. private final UserIndexer userIndexer;
  76. private final PermissionService permissionService;
  77. public OrganizationUpdaterImpl(DbClient dbClient, System2 system2, UuidFactory uuidFactory,
  78. OrganizationValidation organizationValidation, Configuration config, UserIndexer userIndexer,
  79. BuiltInQProfileRepository builtInQProfileRepository, DefaultGroupCreator defaultGroupCreator, PermissionService permissionService) {
  80. this.dbClient = dbClient;
  81. this.system2 = system2;
  82. this.uuidFactory = uuidFactory;
  83. this.organizationValidation = organizationValidation;
  84. this.config = config;
  85. this.userIndexer = userIndexer;
  86. this.builtInQProfileRepository = builtInQProfileRepository;
  87. this.defaultGroupCreator = defaultGroupCreator;
  88. this.permissionService = permissionService;
  89. }
  90. @Override
  91. public OrganizationDto create(DbSession dbSession, UserDto userCreator, NewOrganization newOrganization, Consumer<OrganizationDto> beforeCommit) throws KeyConflictException {
  92. validate(newOrganization);
  93. String key = newOrganization.getKey();
  94. if (organizationKeyIsUsed(dbSession, key)) {
  95. throw new KeyConflictException(format("Organization key '%s' is already used", key));
  96. }
  97. QualityGateDto builtInQualityGate = dbClient.qualityGateDao().selectBuiltIn(dbSession);
  98. OrganizationDto organization = insertOrganization(dbSession, newOrganization, builtInQualityGate);
  99. beforeCommit.accept(organization);
  100. insertOrganizationMember(dbSession, organization, userCreator.getId());
  101. dbClient.qualityGateDao().associate(dbSession, uuidFactory.create(), organization, builtInQualityGate);
  102. GroupDto ownerGroup = insertOwnersGroup(dbSession, organization);
  103. GroupDto defaultGroup = defaultGroupCreator.create(dbSession, organization.getUuid());
  104. insertDefaultTemplateOnGroups(dbSession, organization, ownerGroup, defaultGroup);
  105. addCurrentUserToGroup(dbSession, ownerGroup, userCreator.getId());
  106. addCurrentUserToGroup(dbSession, defaultGroup, userCreator.getId());
  107. try (DbSession batchDbSession = dbClient.openSession(true)) {
  108. insertQualityProfiles(dbSession, batchDbSession, organization);
  109. batchDbSession.commit();
  110. // Elasticsearch is updated when DB session is committed
  111. userIndexer.commitAndIndex(dbSession, userCreator);
  112. return organization;
  113. }
  114. }
  115. @Override
  116. public Optional<OrganizationDto> createForUser(DbSession dbSession, UserDto newUser) {
  117. if (!isCreatePersonalOrgEnabled()) {
  118. return Optional.empty();
  119. }
  120. String nameOrLogin = nameOrLogin(newUser);
  121. NewOrganization newOrganization = newOrganizationBuilder()
  122. .setKey(organizationValidation.generateKeyFrom(newUser.getLogin()))
  123. .setName(toName(nameOrLogin))
  124. .setDescription(format(PERSONAL_ORGANIZATION_DESCRIPTION_PATTERN, nameOrLogin))
  125. .build();
  126. checkKey(dbSession, newOrganization.getKey());
  127. QualityGateDto builtInQualityGate = dbClient.qualityGateDao().selectBuiltIn(dbSession);
  128. OrganizationDto organization = insertOrganization(dbSession, newOrganization, builtInQualityGate,
  129. dto -> dto.setGuarded(true));
  130. dbClient.userDao().update(dbSession, newUser.setOrganizationUuid(organization.getUuid()));
  131. insertOrganizationMember(dbSession, organization, newUser.getId());
  132. GroupDto defaultGroup = defaultGroupCreator.create(dbSession, organization.getUuid());
  133. dbClient.qualityGateDao().associate(dbSession, uuidFactory.create(), organization, builtInQualityGate);
  134. permissionService.getAllOrganizationPermissions()
  135. .forEach(p -> insertUserPermissions(dbSession, newUser, organization, p));
  136. insertPersonalOrgDefaultTemplate(dbSession, organization, defaultGroup);
  137. try (DbSession batchDbSession = dbClient.openSession(true)) {
  138. insertQualityProfiles(dbSession, batchDbSession, organization);
  139. addCurrentUserToGroup(dbSession, defaultGroup, newUser.getId());
  140. batchDbSession.commit();
  141. // Elasticsearch is updated when DB session is committed
  142. userIndexer.commitAndIndex(dbSession, newUser);
  143. return Optional.of(organization);
  144. }
  145. }
  146. @Override
  147. public void updateOrganizationKey(DbSession dbSession, OrganizationDto organization, String newKey) {
  148. String sanitizedKey = organizationValidation.generateKeyFrom(newKey);
  149. if (organization.getKey().equals(sanitizedKey)) {
  150. return;
  151. }
  152. checkKey(dbSession, sanitizedKey);
  153. dbClient.organizationDao().update(dbSession, organization.setKey(sanitizedKey));
  154. }
  155. private void checkKey(DbSession dbSession, String key) {
  156. checkState(!organizationKeyIsUsed(dbSession, key),
  157. "Can't create organization with key '%s' because an organization with this key already exists", key);
  158. }
  159. private static String nameOrLogin(UserDto newUser) {
  160. String name = newUser.getName();
  161. if (name == null || name.isEmpty()) {
  162. return newUser.getLogin();
  163. }
  164. return name;
  165. }
  166. private String toName(String login) {
  167. String name = login.substring(0, Math.min(login.length(), OrganizationValidation.NAME_MAX_LENGTH));
  168. // should not happen has login can't be less than 2 chars, but we call it for safety
  169. organizationValidation.checkName(name);
  170. return name;
  171. }
  172. private boolean isCreatePersonalOrgEnabled() {
  173. return config.getBoolean(CorePropertyDefinitions.ORGANIZATIONS_CREATE_PERSONAL_ORG).orElse(false);
  174. }
  175. private void validate(NewOrganization newOrganization) {
  176. requireNonNull(newOrganization, "newOrganization can't be null");
  177. organizationValidation.checkName(newOrganization.getName());
  178. organizationValidation.checkKey(newOrganization.getKey());
  179. organizationValidation.checkDescription(newOrganization.getDescription());
  180. organizationValidation.checkUrl(newOrganization.getUrl());
  181. organizationValidation.checkAvatar(newOrganization.getAvatar());
  182. }
  183. private OrganizationDto insertOrganization(DbSession dbSession, NewOrganization newOrganization, QualityGateDto builtInQualityGate, Consumer<OrganizationDto>... extendCreation) {
  184. OrganizationDto res = new OrganizationDto()
  185. .setUuid(uuidFactory.create())
  186. .setName(newOrganization.getName())
  187. .setKey(newOrganization.getKey())
  188. .setDescription(newOrganization.getDescription())
  189. .setUrl(newOrganization.getUrl())
  190. .setDefaultQualityGateUuid(builtInQualityGate.getUuid())
  191. .setAvatarUrl(newOrganization.getAvatar())
  192. .setSubscription(FREE);
  193. Arrays.stream(extendCreation).forEach(c -> c.accept(res));
  194. dbClient.organizationDao().insert(dbSession, res, false);
  195. return res;
  196. }
  197. private boolean organizationKeyIsUsed(DbSession dbSession, String key) {
  198. return dbClient.organizationDao().selectByKey(dbSession, key).isPresent();
  199. }
  200. private void insertDefaultTemplateOnGroups(DbSession dbSession, OrganizationDto organizationDto, GroupDto ownerGroup, GroupDto defaultGroup) {
  201. Date now = new Date(system2.now());
  202. PermissionTemplateDto permissionTemplateDto = dbClient.permissionTemplateDao().insert(
  203. dbSession,
  204. new PermissionTemplateDto()
  205. .setOrganizationUuid(organizationDto.getUuid())
  206. .setUuid(uuidFactory.create())
  207. .setName(PERM_TEMPLATE_NAME)
  208. .setDescription(format(PERM_TEMPLATE_DESCRIPTION_PATTERN, organizationDto.getName()))
  209. .setCreatedAt(now)
  210. .setUpdatedAt(now));
  211. insertGroupPermission(dbSession, permissionTemplateDto, ADMIN, ownerGroup);
  212. insertGroupPermission(dbSession, permissionTemplateDto, ISSUE_ADMIN, ownerGroup);
  213. insertGroupPermission(dbSession, permissionTemplateDto, SECURITYHOTSPOT_ADMIN, ownerGroup);
  214. insertGroupPermission(dbSession, permissionTemplateDto, SCAN.getKey(), ownerGroup);
  215. insertGroupPermission(dbSession, permissionTemplateDto, USER, defaultGroup);
  216. insertGroupPermission(dbSession, permissionTemplateDto, CODEVIEWER, defaultGroup);
  217. dbClient.organizationDao().setDefaultTemplates(
  218. dbSession,
  219. organizationDto.getUuid(),
  220. new DefaultTemplates().setProjectUuid(permissionTemplateDto.getUuid()));
  221. }
  222. private void insertPersonalOrgDefaultTemplate(DbSession dbSession, OrganizationDto organizationDto, GroupDto defaultGroup) {
  223. long now = system2.now();
  224. Date dateNow = new Date(now);
  225. PermissionTemplateDto permissionTemplateDto = dbClient.permissionTemplateDao().insert(
  226. dbSession,
  227. new PermissionTemplateDto()
  228. .setOrganizationUuid(organizationDto.getUuid())
  229. .setUuid(uuidFactory.create())
  230. .setName("Default template")
  231. .setDescription(format(PERM_TEMPLATE_DESCRIPTION_PATTERN, organizationDto.getName()))
  232. .setCreatedAt(dateNow)
  233. .setUpdatedAt(dateNow));
  234. insertProjectCreatorPermission(dbSession, permissionTemplateDto, ADMIN, now);
  235. insertProjectCreatorPermission(dbSession, permissionTemplateDto, ISSUE_ADMIN, now);
  236. insertProjectCreatorPermission(dbSession, permissionTemplateDto, SECURITYHOTSPOT_ADMIN, now);
  237. insertProjectCreatorPermission(dbSession, permissionTemplateDto, SCAN.getKey(), now);
  238. insertGroupPermission(dbSession, permissionTemplateDto, USER, defaultGroup);
  239. insertGroupPermission(dbSession, permissionTemplateDto, CODEVIEWER, defaultGroup);
  240. dbClient.organizationDao().setDefaultTemplates(
  241. dbSession,
  242. organizationDto.getUuid(),
  243. new DefaultTemplates().setProjectUuid(permissionTemplateDto.getUuid()));
  244. }
  245. private void insertProjectCreatorPermission(DbSession dbSession, PermissionTemplateDto permissionTemplateDto, String permission, long now) {
  246. dbClient.permissionTemplateCharacteristicDao().insert(
  247. dbSession,
  248. new PermissionTemplateCharacteristicDto()
  249. .setTemplateId(permissionTemplateDto.getId())
  250. .setWithProjectCreator(true)
  251. .setPermission(permission)
  252. .setCreatedAt(now)
  253. .setUpdatedAt(now));
  254. }
  255. private void insertGroupPermission(DbSession dbSession, PermissionTemplateDto template, String permission, @Nullable GroupDto group) {
  256. dbClient.permissionTemplateDao().insertGroupPermission(dbSession, template.getId(), group == null ? null : group.getId(), permission);
  257. }
  258. private void insertQualityProfiles(DbSession dbSession, DbSession batchDbSession, OrganizationDto organization) {
  259. Map<QProfileName, BuiltInQProfile> builtInsPerName = builtInQProfileRepository.get().stream()
  260. .collect(uniqueIndex(BuiltInQProfile::getQProfileName));
  261. List<DefaultQProfileDto> defaults = new ArrayList<>();
  262. dbClient.qualityProfileDao().selectBuiltInRuleProfiles(dbSession).forEach(rulesProfile -> {
  263. OrgQProfileDto dto = new OrgQProfileDto()
  264. .setOrganizationUuid(organization.getUuid())
  265. .setRulesProfileUuid(rulesProfile.getKee())
  266. .setUuid(uuidFactory.create());
  267. QProfileName name = new QProfileName(rulesProfile.getLanguage(), rulesProfile.getName());
  268. BuiltInQProfile builtIn = builtInsPerName.get(name);
  269. if (builtIn == null || builtIn.isDefault()) {
  270. // If builtIn == null, the plugin has been removed
  271. // rows of table default_qprofiles must be inserted after org_qprofiles
  272. // in order to benefit from batch SQL inserts
  273. defaults.add(new DefaultQProfileDto()
  274. .setQProfileUuid(dto.getUuid())
  275. .setOrganizationUuid(organization.getUuid())
  276. .setLanguage(rulesProfile.getLanguage()));
  277. }
  278. dbClient.qualityProfileDao().insert(batchDbSession, dto);
  279. });
  280. defaults.forEach(defaultQProfileDto -> dbClient.defaultQProfileDao().insertOrUpdate(dbSession, defaultQProfileDto));
  281. }
  282. /**
  283. * Owners group has an hard coded name, a description based on the organization's name and has all global permissions.
  284. */
  285. private GroupDto insertOwnersGroup(DbSession dbSession, OrganizationDto organization) {
  286. GroupDto group = dbClient.groupDao().insert(dbSession, new GroupDto()
  287. .setOrganizationUuid(organization.getUuid())
  288. .setName(OWNERS_GROUP_NAME)
  289. .setDescription(format(OWNERS_GROUP_DESCRIPTION_PATTERN, organization.getName())));
  290. permissionService.getAllOrganizationPermissions().forEach(p -> addPermissionToGroup(dbSession, group, p));
  291. return group;
  292. }
  293. private void addPermissionToGroup(DbSession dbSession, GroupDto group, OrganizationPermission permission) {
  294. dbClient.groupPermissionDao().insert(
  295. dbSession,
  296. new GroupPermissionDto()
  297. .setOrganizationUuid(group.getOrganizationUuid())
  298. .setGroupId(group.getId())
  299. .setRole(permission.getKey()));
  300. }
  301. private void insertUserPermissions(DbSession dbSession, UserDto userDto, OrganizationDto organization, OrganizationPermission permission) {
  302. dbClient.userPermissionDao().insert(
  303. dbSession,
  304. new UserPermissionDto(organization.getUuid(), permission.getKey(), userDto.getId(), null));
  305. }
  306. private void addCurrentUserToGroup(DbSession dbSession, GroupDto group, int createUserId) {
  307. dbClient.userGroupDao().insert(
  308. dbSession,
  309. new UserGroupDto().setGroupId(group.getId()).setUserId(createUserId));
  310. }
  311. private void insertOrganizationMember(DbSession dbSession, OrganizationDto organizationDto, int userId) {
  312. dbClient.organizationMemberDao().insert(dbSession, new OrganizationMemberDto()
  313. .setOrganizationUuid(organizationDto.getUuid())
  314. .setUserId(userId));
  315. }
  316. }