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.

AbstractUserSession.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.user;
  21. import java.util.Collection;
  22. import java.util.List;
  23. import java.util.Optional;
  24. import java.util.Set;
  25. import javax.annotation.CheckForNull;
  26. import javax.annotation.Nullable;
  27. import org.sonar.api.web.UserRole;
  28. import org.sonar.db.component.ComponentDto;
  29. import org.sonar.db.entity.EntityDto;
  30. import org.sonar.db.permission.GlobalPermission;
  31. import org.sonar.db.user.UserDto;
  32. import org.sonar.server.exceptions.ForbiddenException;
  33. import org.sonar.server.exceptions.UnauthorizedException;
  34. import static org.sonar.api.resources.Qualifiers.APP;
  35. import static org.sonar.server.user.UserSession.IdentityProvider.SONARQUBE;
  36. public abstract class AbstractUserSession implements UserSession {
  37. private static final Set<String> PUBLIC_PERMISSIONS = Set.of(UserRole.USER, UserRole.CODEVIEWER);
  38. private static final String INSUFFICIENT_PRIVILEGES_MESSAGE = "Insufficient privileges";
  39. private static final String AUTHENTICATION_IS_REQUIRED_MESSAGE = "Authentication is required";
  40. protected static Identity computeIdentity(UserDto userDto) {
  41. IdentityProvider identityProvider = IdentityProvider.getFromKey(userDto.getExternalIdentityProvider());
  42. ExternalIdentity externalIdentity = identityProvider == SONARQUBE ? null : externalIdentityOf(userDto);
  43. return new Identity(identityProvider, externalIdentity);
  44. }
  45. private static ExternalIdentity externalIdentityOf(UserDto userDto) {
  46. String externalId = userDto.getExternalId();
  47. String externalLogin = userDto.getExternalLogin();
  48. return new ExternalIdentity(externalId, externalLogin);
  49. }
  50. protected static final class Identity {
  51. private final IdentityProvider identityProvider;
  52. private final ExternalIdentity externalIdentity;
  53. private Identity(IdentityProvider identityProvider, @Nullable ExternalIdentity externalIdentity) {
  54. this.identityProvider = identityProvider;
  55. this.externalIdentity = externalIdentity;
  56. }
  57. public IdentityProvider getIdentityProvider() {
  58. return identityProvider;
  59. }
  60. @CheckForNull
  61. public ExternalIdentity getExternalIdentity() {
  62. return externalIdentity;
  63. }
  64. }
  65. @Override
  66. @CheckForNull
  67. public Long getLastSonarlintConnectionDate() {
  68. return null;
  69. }
  70. @Override
  71. public final boolean hasPermission(GlobalPermission permission) {
  72. return hasPermissionImpl(permission);
  73. }
  74. protected abstract boolean hasPermissionImpl(GlobalPermission permission);
  75. @Override
  76. public boolean hasComponentPermission(String permission, ComponentDto component) {
  77. Optional<String> projectUuid1 = componentUuidToEntityUuid(component.uuid());
  78. return projectUuid1
  79. .map(projectUuid -> hasEntityUuidPermission(permission, projectUuid))
  80. .orElse(false);
  81. }
  82. @Override
  83. public final boolean hasEntityPermission(String permission, EntityDto entity) {
  84. return hasEntityUuidPermission(permission, entity.getAuthUuid());
  85. }
  86. @Override
  87. public final boolean hasEntityPermission(String permission, String entityUuid) {
  88. return hasEntityUuidPermission(permission, entityUuid);
  89. }
  90. @Override
  91. public final boolean hasChildProjectsPermission(String permission, ComponentDto component) {
  92. return componentUuidToEntityUuid(component.uuid())
  93. .map(applicationUuid -> hasChildProjectsPermission(permission, applicationUuid)).orElse(false);
  94. }
  95. @Override
  96. public final boolean hasChildProjectsPermission(String permission, EntityDto application) {
  97. return hasChildProjectsPermission(permission, application.getUuid());
  98. }
  99. @Override
  100. public final boolean hasPortfolioChildProjectsPermission(String permission, ComponentDto portfolio) {
  101. return hasPortfolioChildProjectsPermission(permission, portfolio.uuid());
  102. }
  103. @Override
  104. public boolean hasComponentUuidPermission(String permission, String componentUuid) {
  105. Optional<String> entityUuid = componentUuidToEntityUuid(componentUuid);
  106. return entityUuid
  107. .map(s -> hasEntityUuidPermission(permission, s))
  108. .orElse(false);
  109. }
  110. protected abstract Optional<String> componentUuidToEntityUuid(String componentUuid);
  111. protected abstract boolean hasEntityUuidPermission(String permission, String entityUuid);
  112. protected abstract boolean hasChildProjectsPermission(String permission, String applicationUuid);
  113. protected abstract boolean hasPortfolioChildProjectsPermission(String permission, String portfolioUuid);
  114. @Override
  115. public final List<ComponentDto> keepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
  116. return doKeepAuthorizedComponents(permission, components);
  117. }
  118. @Override
  119. public <T extends EntityDto> List<T> keepAuthorizedEntities(String permission, Collection<T> projects) {
  120. return doKeepAuthorizedEntities(permission, projects);
  121. }
  122. /**
  123. * Naive implementation, to be overridden if needed
  124. */
  125. protected <T extends EntityDto> List<T> doKeepAuthorizedEntities(String permission, Collection<T> entities) {
  126. boolean allowPublicComponent = PUBLIC_PERMISSIONS.contains(permission);
  127. return entities.stream()
  128. .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasEntityPermission(permission, c.getUuid()))
  129. .toList();
  130. }
  131. /**
  132. * Naive implementation, to be overridden if needed
  133. */
  134. protected List<ComponentDto> doKeepAuthorizedComponents(String permission, Collection<ComponentDto> components) {
  135. boolean allowPublicComponent = PUBLIC_PERMISSIONS.contains(permission);
  136. return components.stream()
  137. .filter(c -> (allowPublicComponent && !c.isPrivate()) || hasComponentPermission(permission, c))
  138. .toList();
  139. }
  140. @Override
  141. public final UserSession checkLoggedIn() {
  142. if (!isLoggedIn()) {
  143. throw new UnauthorizedException(AUTHENTICATION_IS_REQUIRED_MESSAGE);
  144. }
  145. return this;
  146. }
  147. @Override
  148. public final UserSession checkPermission(GlobalPermission permission) {
  149. if (!hasPermission(permission)) {
  150. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  151. }
  152. return this;
  153. }
  154. @Override
  155. public final UserSession checkComponentPermission(String projectPermission, ComponentDto component) {
  156. if (!hasComponentPermission(projectPermission, component)) {
  157. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  158. }
  159. return this;
  160. }
  161. @Override
  162. public UserSession checkEntityPermission(String projectPermission, EntityDto entity) {
  163. if (hasEntityPermission(projectPermission, entity)) {
  164. return this;
  165. }
  166. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  167. }
  168. @Override
  169. public UserSession checkChildProjectsPermission(String projectPermission, ComponentDto component) {
  170. if (!APP.equals(component.qualifier()) || hasChildProjectsPermission(projectPermission, component)) {
  171. return this;
  172. }
  173. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  174. }
  175. @Override
  176. public UserSession checkChildProjectsPermission(String projectPermission, EntityDto application) {
  177. if (!APP.equals(application.getQualifier()) || hasChildProjectsPermission(projectPermission, application)) {
  178. return this;
  179. }
  180. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  181. }
  182. @Override
  183. public final UserSession checkComponentUuidPermission(String permission, String componentUuid) {
  184. if (!hasComponentUuidPermission(permission, componentUuid)) {
  185. throw new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  186. }
  187. return this;
  188. }
  189. public static ForbiddenException insufficientPrivilegesException() {
  190. return new ForbiddenException(INSUFFICIENT_PRIVILEGES_MESSAGE);
  191. }
  192. @Override
  193. public final UserSession checkIsSystemAdministrator() {
  194. if (!isSystemAdministrator()) {
  195. throw insufficientPrivilegesException();
  196. }
  197. return this;
  198. }
  199. }