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.

PermissionIndexerTest.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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.permission.index;
  21. import java.util.Collection;
  22. import org.junit.Rule;
  23. import org.junit.Test;
  24. import org.sonar.api.utils.System2;
  25. import org.sonar.db.DbSession;
  26. import org.sonar.db.DbTester;
  27. import org.sonar.db.component.ProjectData;
  28. import org.sonar.db.entity.EntityDto;
  29. import org.sonar.db.es.EsQueueDto;
  30. import org.sonar.db.portfolio.PortfolioDto;
  31. import org.sonar.db.project.ProjectDto;
  32. import org.sonar.db.user.GroupDto;
  33. import org.sonar.db.user.UserDto;
  34. import org.sonar.server.es.EsTester;
  35. import org.sonar.server.es.IndexType;
  36. import org.sonar.server.es.IndexType.IndexMainType;
  37. import org.sonar.server.es.Indexers.EntityEvent;
  38. import org.sonar.server.es.IndexingResult;
  39. import org.sonar.server.tester.UserSessionRule;
  40. import static java.util.Arrays.asList;
  41. import static java.util.Collections.singletonList;
  42. import static org.assertj.core.api.Assertions.assertThat;
  43. import static org.sonar.api.resources.Qualifiers.PROJECT;
  44. import static org.sonar.api.web.UserRole.ADMIN;
  45. import static org.sonar.api.web.UserRole.USER;
  46. import static org.sonar.server.es.Indexers.EntityEvent.PERMISSION_CHANGE;
  47. import static org.sonar.server.permission.index.IndexAuthorizationConstants.TYPE_AUTHORIZATION;
  48. public class PermissionIndexerTest {
  49. private static final IndexMainType INDEX_TYPE_FOO_AUTH = IndexType.main(FooIndexDefinition.DESCRIPTOR, TYPE_AUTHORIZATION);
  50. @Rule
  51. public DbTester db = DbTester.create(System2.INSTANCE);
  52. @Rule
  53. public EsTester es = EsTester.createCustom(new FooIndexDefinition());
  54. @Rule
  55. public UserSessionRule userSession = UserSessionRule.standalone();
  56. private FooIndex fooIndex = new FooIndex(es.client(), new WebAuthorizationTypeSupport(userSession));
  57. private FooIndexer fooIndexer = new FooIndexer(es.client(), db.getDbClient());
  58. private PermissionIndexer underTest = new PermissionIndexer(db.getDbClient(), es.client(), fooIndexer);
  59. @Test
  60. public void indexOnStartup_grants_access_to_any_user_and_to_group_Anyone_on_public_projects() {
  61. ProjectDto project = createAndIndexPublicProject();
  62. UserDto user1 = db.users().insertUser();
  63. UserDto user2 = db.users().insertUser();
  64. indexOnStartup();
  65. verifyAnyoneAuthorized(project);
  66. verifyAuthorized(project, user1);
  67. verifyAuthorized(project, user2);
  68. }
  69. @Test
  70. public void indexAll_grants_access_to_any_user_and_to_group_Anyone_on_public_projects() {
  71. ProjectDto project = createAndIndexPublicProject();
  72. UserDto user1 = db.users().insertUser();
  73. UserDto user2 = db.users().insertUser();
  74. underTest.indexAll(underTest.getIndexTypes());
  75. verifyAnyoneAuthorized(project);
  76. verifyAuthorized(project, user1);
  77. verifyAuthorized(project, user2);
  78. }
  79. @Test
  80. public void deletion_resilience_will_deindex_projects() {
  81. ProjectDto project1 = createUnindexedPublicProject();
  82. ProjectDto project2 = createUnindexedPublicProject();
  83. // UserDto user1 = db.users().insertUser();
  84. indexOnStartup();
  85. assertThat(es.countDocuments(INDEX_TYPE_FOO_AUTH)).isEqualTo(2);
  86. // Simulate an indexing issue
  87. db.getDbClient().purgeDao().deleteProject(db.getSession(), project1.getUuid(), PROJECT, project1.getName(), project1.getKey());
  88. underTest.prepareForRecoveryOnEntityEvent(db.getSession(), asList(project1.getUuid()), EntityEvent.DELETION);
  89. assertThat(db.countRowsOfTable(db.getSession(), "es_queue")).isOne();
  90. Collection<EsQueueDto> esQueueDtos = db.getDbClient().esQueueDao().selectForRecovery(db.getSession(), Long.MAX_VALUE, 2);
  91. underTest.index(db.getSession(), esQueueDtos);
  92. assertThat(db.countRowsOfTable(db.getSession(), "es_queue")).isZero();
  93. assertThat(es.countDocuments(INDEX_TYPE_FOO_AUTH)).isOne();
  94. }
  95. @Test
  96. public void indexOnStartup_grants_access_to_user() {
  97. ProjectDto project = createAndIndexPrivateProject();
  98. UserDto user1 = db.users().insertUser();
  99. UserDto user2 = db.users().insertUser();
  100. db.users().insertProjectPermissionOnUser(user1, USER, project);
  101. db.users().insertProjectPermissionOnUser(user2, ADMIN, project);
  102. indexOnStartup();
  103. // anonymous
  104. verifyAnyoneNotAuthorized(project);
  105. // user1 has access
  106. verifyAuthorized(project, user1);
  107. // user2 has not access (only USER permission is accepted)
  108. verifyNotAuthorized(project, user2);
  109. }
  110. @Test
  111. public void indexOnStartup_grants_access_to_group_on_private_project() {
  112. ProjectDto project = createAndIndexPrivateProject();
  113. UserDto user1 = db.users().insertUser();
  114. UserDto user2 = db.users().insertUser();
  115. UserDto user3 = db.users().insertUser();
  116. GroupDto group1 = db.users().insertGroup();
  117. GroupDto group2 = db.users().insertGroup();
  118. db.users().insertEntityPermissionOnGroup(group1, USER, project);
  119. db.users().insertEntityPermissionOnGroup(group2, ADMIN, project);
  120. indexOnStartup();
  121. // anonymous
  122. verifyAnyoneNotAuthorized(project);
  123. // group1 has access
  124. verifyAuthorized(project, user1, group1);
  125. // group2 has not access (only USER permission is accepted)
  126. verifyNotAuthorized(project, user2, group2);
  127. // user3 is not in any group
  128. verifyNotAuthorized(project, user3);
  129. }
  130. @Test
  131. public void indexOnStartup_grants_access_to_user_and_group() {
  132. ProjectDto project = createAndIndexPrivateProject();
  133. UserDto user1 = db.users().insertUser();
  134. UserDto user2 = db.users().insertUser();
  135. GroupDto group = db.users().insertGroup();
  136. db.users().insertMember(group, user2);
  137. db.users().insertProjectPermissionOnUser(user1, USER, project);
  138. db.users().insertEntityPermissionOnGroup(group, USER, project);
  139. indexOnStartup();
  140. // anonymous
  141. verifyAnyoneNotAuthorized(project);
  142. // has direct access
  143. verifyAuthorized(project, user1);
  144. // has access through group
  145. verifyAuthorized(project, user1, group);
  146. // no access
  147. verifyNotAuthorized(project, user2);
  148. }
  149. @Test
  150. public void indexOnStartup_does_not_grant_access_to_anybody_on_private_project() {
  151. ProjectDto project = createAndIndexPrivateProject();
  152. UserDto user = db.users().insertUser();
  153. GroupDto group = db.users().insertGroup();
  154. indexOnStartup();
  155. verifyAnyoneNotAuthorized(project);
  156. verifyNotAuthorized(project, user);
  157. verifyNotAuthorized(project, user, group);
  158. }
  159. @Test
  160. public void indexOnStartup_grants_access_to_anybody_on_public_project() {
  161. ProjectDto project = createAndIndexPublicProject();
  162. UserDto user = db.users().insertUser();
  163. GroupDto group = db.users().insertGroup();
  164. indexOnStartup();
  165. verifyAnyoneAuthorized(project);
  166. verifyAuthorized(project, user);
  167. verifyAuthorized(project, user, group);
  168. }
  169. @Test
  170. public void indexOnStartup_grants_access_to_anybody_on_view() {
  171. PortfolioDto view = createAndIndexPortfolio();
  172. UserDto user = db.users().insertUser();
  173. GroupDto group = db.users().insertGroup();
  174. indexOnStartup();
  175. verifyAnyoneAuthorized(view);
  176. verifyAuthorized(view, user);
  177. verifyAuthorized(view, user, group);
  178. }
  179. @Test
  180. public void indexOnStartup_grants_access_on_many_projects() {
  181. UserDto user1 = db.users().insertUser();
  182. UserDto user2 = db.users().insertUser();
  183. ProjectDto project = null;
  184. for (int i = 0; i < 10; i++) {
  185. project = createAndIndexPrivateProject();
  186. db.users().insertProjectPermissionOnUser(user1, USER, project);
  187. }
  188. indexOnStartup();
  189. verifyAnyoneNotAuthorized(project);
  190. verifyAuthorized(project, user1);
  191. verifyNotAuthorized(project, user2);
  192. }
  193. @Test
  194. public void public_projects_are_visible_to_anybody() {
  195. ProjectDto projectOnOrg1 = createAndIndexPublicProject();
  196. UserDto user = db.users().insertUser();
  197. indexOnStartup();
  198. verifyAnyoneAuthorized(projectOnOrg1);
  199. verifyAuthorized(projectOnOrg1, user);
  200. }
  201. @Test
  202. public void permissions_are_not_updated_on_project_tags_update() {
  203. ProjectDto project = createAndIndexPublicProject();
  204. indexPermissions(project, EntityEvent.PROJECT_TAGS_UPDATE);
  205. assertThatAuthIndexHasSize(0);
  206. verifyAnyoneNotAuthorized(project);
  207. }
  208. @Test
  209. public void permissions_are_not_updated_on_project_key_update() {
  210. ProjectDto project = createAndIndexPublicProject();
  211. indexPermissions(project, EntityEvent.PROJECT_TAGS_UPDATE);
  212. assertThatAuthIndexHasSize(0);
  213. verifyAnyoneNotAuthorized(project);
  214. }
  215. @Test
  216. public void index_permissions_on_project_creation() {
  217. ProjectDto project = createAndIndexPrivateProject();
  218. UserDto user = db.users().insertUser();
  219. db.users().insertProjectPermissionOnUser(user, USER, project);
  220. indexPermissions(project, EntityEvent.CREATION);
  221. assertThatAuthIndexHasSize(1);
  222. verifyAuthorized(project, user);
  223. }
  224. @Test
  225. public void index_permissions_on_permission_change() {
  226. ProjectDto project = createAndIndexPrivateProject();
  227. UserDto user1 = db.users().insertUser();
  228. UserDto user2 = db.users().insertUser();
  229. db.users().insertProjectPermissionOnUser(user1, USER, project);
  230. indexPermissions(project, EntityEvent.CREATION);
  231. verifyAuthorized(project, user1);
  232. verifyNotAuthorized(project, user2);
  233. db.users().insertProjectPermissionOnUser(user2, USER, project);
  234. indexPermissions(project, PERMISSION_CHANGE);
  235. verifyAuthorized(project, user1);
  236. verifyAuthorized(project, user1);
  237. }
  238. @Test
  239. public void delete_permissions_on_project_deletion() {
  240. ProjectDto project = createAndIndexPrivateProject();
  241. UserDto user = db.users().insertUser();
  242. db.users().insertProjectPermissionOnUser(user, USER, project);
  243. indexPermissions(project, EntityEvent.CREATION);
  244. verifyAuthorized(project, user);
  245. db.getDbClient().purgeDao().deleteProject(db.getSession(), project.getUuid(), PROJECT, project.getUuid(), project.getKey());
  246. indexPermissions(project, EntityEvent.DELETION);
  247. verifyNotAuthorized(project, user);
  248. assertThatAuthIndexHasSize(0);
  249. }
  250. @Test
  251. public void errors_during_indexing_are_recovered() {
  252. ProjectDto project = createAndIndexPublicProject();
  253. es.lockWrites(INDEX_TYPE_FOO_AUTH);
  254. IndexingResult result = indexPermissions(project, PERMISSION_CHANGE);
  255. assertThat(result.getTotal()).isOne();
  256. assertThat(result.getFailures()).isOne();
  257. // index is still read-only, fail to recover
  258. result = recover();
  259. assertThat(result.getTotal()).isOne();
  260. assertThat(result.getFailures()).isOne();
  261. assertThatAuthIndexHasSize(0);
  262. assertThatEsQueueTableHasSize(1);
  263. es.unlockWrites(INDEX_TYPE_FOO_AUTH);
  264. result = recover();
  265. assertThat(result.getTotal()).isOne();
  266. assertThat(result.getFailures()).isZero();
  267. verifyAnyoneAuthorized(project);
  268. assertThatEsQueueTableHasSize(0);
  269. }
  270. private void assertThatAuthIndexHasSize(int expectedSize) {
  271. assertThat(es.countDocuments(FooIndexDefinition.TYPE_AUTHORIZATION)).isEqualTo(expectedSize);
  272. }
  273. private void indexOnStartup() {
  274. underTest.indexOnStartup(underTest.getIndexTypes());
  275. }
  276. private void verifyAuthorized(EntityDto entity, UserDto user) {
  277. logIn(user);
  278. verifyAuthorized(entity, true);
  279. }
  280. private void verifyAuthorized(EntityDto entity, UserDto user, GroupDto group) {
  281. logIn(user).setGroups(group);
  282. verifyAuthorized(entity, true);
  283. }
  284. private void verifyNotAuthorized(EntityDto entity, UserDto user) {
  285. logIn(user);
  286. verifyAuthorized(entity, false);
  287. }
  288. private void verifyNotAuthorized(EntityDto entity, UserDto user, GroupDto group) {
  289. logIn(user).setGroups(group);
  290. verifyAuthorized(entity, false);
  291. }
  292. private void verifyAnyoneAuthorized(EntityDto entity) {
  293. userSession.anonymous();
  294. verifyAuthorized(entity, true);
  295. }
  296. private void verifyAnyoneNotAuthorized(EntityDto entity) {
  297. userSession.anonymous();
  298. verifyAuthorized(entity, false);
  299. }
  300. private void verifyAuthorized(EntityDto entity, boolean expectedAccess) {
  301. assertThat(fooIndex.hasAccessToProject(entity.getUuid())).isEqualTo(expectedAccess);
  302. }
  303. private UserSessionRule logIn(UserDto u) {
  304. userSession.logIn(u);
  305. return userSession;
  306. }
  307. private IndexingResult indexPermissions(EntityDto entity, EntityEvent cause) {
  308. DbSession dbSession = db.getSession();
  309. Collection<EsQueueDto> items = underTest.prepareForRecoveryOnEntityEvent(dbSession, singletonList(entity.getUuid()), cause);
  310. dbSession.commit();
  311. return underTest.index(dbSession, items);
  312. }
  313. private ProjectDto createUnindexedPublicProject() {
  314. return db.components().insertPublicProject().getProjectDto();
  315. }
  316. private ProjectDto createAndIndexPrivateProject() {
  317. ProjectData project = db.components().insertPrivateProject();
  318. fooIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
  319. return project.getProjectDto();
  320. }
  321. private ProjectDto createAndIndexPublicProject() {
  322. ProjectData project = db.components().insertPublicProject();
  323. fooIndexer.indexOnAnalysis(project.getMainBranchDto().getUuid());
  324. return project.getProjectDto();
  325. }
  326. private PortfolioDto createAndIndexPortfolio() {
  327. PortfolioDto view = db.components().insertPublicPortfolioDto();
  328. fooIndexer.indexOnAnalysis(view.getUuid());
  329. return view;
  330. }
  331. private IndexingResult recover() {
  332. Collection<EsQueueDto> items = db.getDbClient().esQueueDao().selectForRecovery(db.getSession(), System.currentTimeMillis() + 1_000L, 10);
  333. return underTest.index(db.getSession(), items);
  334. }
  335. private void assertThatEsQueueTableHasSize(int expectedSize) {
  336. assertThat(db.countRowsOfTable("es_queue")).isEqualTo(expectedSize);
  337. }
  338. }