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.

GroupPermissionChanger.java 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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.common.permission;
  21. import java.util.HashSet;
  22. import java.util.Set;
  23. import javax.annotation.Nullable;
  24. import org.sonar.api.web.UserRole;
  25. import org.sonar.core.util.UuidFactory;
  26. import org.sonar.db.DbClient;
  27. import org.sonar.db.DbSession;
  28. import org.sonar.db.entity.EntityDto;
  29. import org.sonar.db.permission.GlobalPermission;
  30. import org.sonar.db.permission.GroupPermissionDto;
  31. import org.sonar.server.exceptions.BadRequestException;
  32. import org.sonar.server.permission.GroupUuidOrAnyone;
  33. import static com.google.common.base.Preconditions.checkNotNull;
  34. import static java.lang.String.format;
  35. import static org.sonar.server.common.permission.Operation.ADD;
  36. import static org.sonar.server.common.permission.Operation.REMOVE;
  37. import static org.sonar.server.exceptions.BadRequestException.checkRequest;
  38. public class GroupPermissionChanger implements GranteeTypeSpecificPermissionUpdater<GroupPermissionChange> {
  39. private final DbClient dbClient;
  40. private final UuidFactory uuidFactory;
  41. public GroupPermissionChanger(DbClient dbClient, UuidFactory uuidFactory) {
  42. this.dbClient = dbClient;
  43. this.uuidFactory = uuidFactory;
  44. }
  45. @Override
  46. public Class<GroupPermissionChange> getHandledClass() {
  47. return GroupPermissionChange.class;
  48. }
  49. @Override
  50. public Set<String> loadExistingEntityPermissions(DbSession dbSession, String uuidOfGrantee, @Nullable String entityUuid) {
  51. if (entityUuid != null) {
  52. return new HashSet<>(dbClient.groupPermissionDao().selectEntityPermissionsOfGroup(dbSession, uuidOfGrantee, entityUuid));
  53. }
  54. return new HashSet<>(dbClient.groupPermissionDao().selectGlobalPermissionsOfGroup(dbSession, uuidOfGrantee));
  55. }
  56. @Override
  57. public boolean apply(DbSession dbSession, Set<String> existingPermissions, GroupPermissionChange change) {
  58. ensureConsistencyWithVisibility(change);
  59. if (isImplicitlyAlreadyDone(change)) {
  60. return false;
  61. }
  62. switch (change.getOperation()) {
  63. case ADD:
  64. if (existingPermissions.contains(change.getPermission())) {
  65. return false;
  66. }
  67. return addPermission(dbSession, change);
  68. case REMOVE:
  69. if (!existingPermissions.contains(change.getPermission())) {
  70. return false;
  71. }
  72. return removePermission(dbSession, change);
  73. default:
  74. throw new UnsupportedOperationException("Unsupported permission change: " + change.getOperation());
  75. }
  76. }
  77. private static boolean isImplicitlyAlreadyDone(GroupPermissionChange change) {
  78. EntityDto project = change.getEntity();
  79. if (project != null) {
  80. return isImplicitlyAlreadyDone(project, change);
  81. }
  82. return false;
  83. }
  84. private static boolean isImplicitlyAlreadyDone(EntityDto project, GroupPermissionChange change) {
  85. return isAttemptToAddPublicPermissionToPublicComponent(change, project)
  86. || isAttemptToRemovePermissionFromAnyoneOnPrivateComponent(change, project);
  87. }
  88. private static boolean isAttemptToAddPublicPermissionToPublicComponent(GroupPermissionChange change, EntityDto project) {
  89. return !project.isPrivate()
  90. && change.getOperation() == ADD
  91. && UserRole.PUBLIC_PERMISSIONS.contains(change.getPermission());
  92. }
  93. private static boolean isAttemptToRemovePermissionFromAnyoneOnPrivateComponent(GroupPermissionChange change, EntityDto project) {
  94. return project.isPrivate()
  95. && change.getOperation() == REMOVE
  96. && change.getGroupUuidOrAnyone().isAnyone();
  97. }
  98. private static void ensureConsistencyWithVisibility(GroupPermissionChange change) {
  99. EntityDto project = change.getEntity();
  100. if (project != null) {
  101. checkRequest(
  102. !isAttemptToAddPermissionToAnyoneOnPrivateComponent(change, project),
  103. "No permission can be granted to Anyone on a private component");
  104. BadRequestException.checkRequest(
  105. !isAttemptToRemovePublicPermissionFromPublicComponent(change, project),
  106. "Permission %s can't be removed from a public component", change.getPermission());
  107. }
  108. }
  109. private static boolean isAttemptToAddPermissionToAnyoneOnPrivateComponent(GroupPermissionChange change, EntityDto project) {
  110. return project.isPrivate()
  111. && change.getOperation() == ADD
  112. && change.getGroupUuidOrAnyone().isAnyone();
  113. }
  114. private static boolean isAttemptToRemovePublicPermissionFromPublicComponent(GroupPermissionChange change, EntityDto project) {
  115. return !project.isPrivate()
  116. && change.getOperation() == REMOVE
  117. && UserRole.PUBLIC_PERMISSIONS.contains(change.getPermission());
  118. }
  119. private boolean addPermission(DbSession dbSession, GroupPermissionChange change) {
  120. validateNotAnyoneAndAdminPermission(change.getPermission(), change.getGroupUuidOrAnyone());
  121. String groupUuid = change.getGroupUuidOrAnyone().getUuid();
  122. String groupName = change.getGroupName().orElse(null);
  123. GroupPermissionDto addedDto = new GroupPermissionDto()
  124. .setUuid(uuidFactory.create())
  125. .setRole(change.getPermission())
  126. .setGroupUuid(groupUuid)
  127. .setEntityName(change.getProjectName())
  128. .setEntityUuid(change.getProjectUuid())
  129. .setGroupName(groupName);
  130. dbClient.groupPermissionDao().insert(dbSession, addedDto, change.getEntity(), null);
  131. return true;
  132. }
  133. private static void validateNotAnyoneAndAdminPermission(String permission, GroupUuidOrAnyone group) {
  134. checkRequest(!GlobalPermission.ADMINISTER.getKey().equals(permission) || !group.isAnyone(),
  135. format("It is not possible to add the '%s' permission to group 'Anyone'.", permission));
  136. }
  137. private boolean removePermission(DbSession dbSession, GroupPermissionChange change) {
  138. checkIfRemainingGlobalAdministrators(dbSession, change);
  139. String groupUuid = change.getGroupUuidOrAnyone().getUuid();
  140. String groupName = change.getGroupName().orElse(null);
  141. dbClient.groupPermissionDao().delete(dbSession,
  142. change.getPermission(),
  143. groupUuid,
  144. groupName,
  145. change.getEntity());
  146. return true;
  147. }
  148. private void checkIfRemainingGlobalAdministrators(DbSession dbSession, GroupPermissionChange change) {
  149. GroupUuidOrAnyone groupUuidOrAnyone = change.getGroupUuidOrAnyone();
  150. if (GlobalPermission.ADMINISTER.getKey().equals(change.getPermission()) &&
  151. !groupUuidOrAnyone.isAnyone() &&
  152. change.getProjectUuid() == null) {
  153. String groupUuid = checkNotNull(groupUuidOrAnyone.getUuid());
  154. // removing global admin permission from group
  155. int remaining = dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingGroup(dbSession, GlobalPermission.ADMINISTER.getKey(), groupUuid);
  156. checkRequest(remaining > 0, "Last group with permission '%s'. Permission cannot be removed.", GlobalPermission.ADMINISTER.getKey());
  157. }
  158. }
  159. }