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.

UserPermissionChanger.java 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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 org.jetbrains.annotations.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.UserPermissionDto;
  31. import static org.sonar.server.common.permission.Operation.ADD;
  32. import static org.sonar.server.common.permission.Operation.REMOVE;
  33. import static org.sonar.server.exceptions.BadRequestException.checkRequest;
  34. /**
  35. * Adds and removes user permissions. Both global and project scopes are supported.
  36. */
  37. public class UserPermissionChanger implements GranteeTypeSpecificPermissionUpdater<UserPermissionChange> {
  38. private final DbClient dbClient;
  39. private final UuidFactory uuidFactory;
  40. public UserPermissionChanger(DbClient dbClient, UuidFactory uuidFactory) {
  41. this.dbClient = dbClient;
  42. this.uuidFactory = uuidFactory;
  43. }
  44. @Override
  45. public Class<UserPermissionChange> getHandledClass() {
  46. return UserPermissionChange.class;
  47. }
  48. @Override
  49. public Set<String> loadExistingEntityPermissions(DbSession dbSession, String uuidOfGrantee, @Nullable String entityUuid) {
  50. if (entityUuid != null) {
  51. return new HashSet<>(dbClient.userPermissionDao().selectEntityPermissionsOfUser(dbSession, uuidOfGrantee, entityUuid));
  52. }
  53. return new HashSet<>(dbClient.userPermissionDao().selectGlobalPermissionsOfUser(dbSession, uuidOfGrantee));
  54. }
  55. @Override
  56. public boolean apply(DbSession dbSession, Set<String> existingPermissions, UserPermissionChange change) {
  57. ensureConsistencyWithVisibility(change);
  58. if (isImplicitlyAlreadyDone(change)) {
  59. return false;
  60. }
  61. switch (change.getOperation()) {
  62. case ADD:
  63. return addPermission(dbSession, existingPermissions, change);
  64. case REMOVE:
  65. return removePermission(dbSession, existingPermissions, change);
  66. default:
  67. throw new UnsupportedOperationException("Unsupported permission change: " + change.getOperation());
  68. }
  69. }
  70. private static boolean isImplicitlyAlreadyDone(UserPermissionChange change) {
  71. EntityDto project = change.getEntity();
  72. if (project != null) {
  73. return isImplicitlyAlreadyDone(project, change);
  74. }
  75. return false;
  76. }
  77. private static boolean isImplicitlyAlreadyDone(EntityDto project, UserPermissionChange change) {
  78. return isAttemptToAddPublicPermissionToPublicComponent(change, project);
  79. }
  80. private static boolean isAttemptToAddPublicPermissionToPublicComponent(UserPermissionChange change, EntityDto project) {
  81. return !project.isPrivate()
  82. && change.getOperation() == ADD
  83. && UserRole.PUBLIC_PERMISSIONS.contains(change.getPermission());
  84. }
  85. private static void ensureConsistencyWithVisibility(UserPermissionChange change) {
  86. EntityDto project = change.getEntity();
  87. if (project != null) {
  88. checkRequest(!isAttemptToRemovePublicPermissionFromPublicComponent(change, project),
  89. "Permission %s can't be removed from a public component", change.getPermission());
  90. }
  91. }
  92. private static boolean isAttemptToRemovePublicPermissionFromPublicComponent(UserPermissionChange change, EntityDto entity) {
  93. return !entity.isPrivate()
  94. && change.getOperation() == REMOVE
  95. && UserRole.PUBLIC_PERMISSIONS.contains(change.getPermission());
  96. }
  97. private boolean addPermission(DbSession dbSession, Set<String> existingPermissions, UserPermissionChange change) {
  98. if (existingPermissions.contains(change.getPermission())) {
  99. return false;
  100. }
  101. UserPermissionDto dto = new UserPermissionDto(uuidFactory.create(), change.getPermission(), change.getUserId().getUuid(),
  102. change.getProjectUuid());
  103. dbClient.userPermissionDao().insert(dbSession, dto, change.getEntity(), change.getUserId(), null);
  104. return true;
  105. }
  106. private boolean removePermission(DbSession dbSession, Set<String> existingPermissions, UserPermissionChange change) {
  107. if (!existingPermissions.contains(change.getPermission())) {
  108. return false;
  109. }
  110. checkOtherAdminsExist(dbSession, change);
  111. EntityDto entity = change.getEntity();
  112. if (entity != null) {
  113. dbClient.userPermissionDao().deleteEntityPermission(dbSession, change.getUserId(), change.getPermission(), entity);
  114. } else {
  115. dbClient.userPermissionDao().deleteGlobalPermission(dbSession, change.getUserId(), change.getPermission());
  116. }
  117. return true;
  118. }
  119. private void checkOtherAdminsExist(DbSession dbSession, UserPermissionChange change) {
  120. if (GlobalPermission.ADMINISTER.getKey().equals(change.getPermission()) && change.getProjectUuid() == null) {
  121. int remaining = dbClient.authorizationDao().countUsersWithGlobalPermissionExcludingUserPermission(dbSession, change.getPermission(), change.getUserId().getUuid());
  122. checkRequest(remaining > 0, "Last user with permission '%s'. Permission cannot be removed.", GlobalPermission.ADMINISTER.getKey());
  123. }
  124. }
  125. }