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.

OrganizationTest.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2017 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 it.organization;
  21. import com.sonar.orchestrator.Orchestrator;
  22. import com.sonar.orchestrator.build.BuildFailureException;
  23. import it.Category6Suite;
  24. import java.util.List;
  25. import java.util.function.Consumer;
  26. import java.util.function.Function;
  27. import org.junit.After;
  28. import org.junit.Before;
  29. import org.junit.BeforeClass;
  30. import org.junit.ClassRule;
  31. import org.junit.Rule;
  32. import org.junit.Test;
  33. import org.junit.rules.ExpectedException;
  34. import org.sonarqube.ws.Organizations;
  35. import org.sonarqube.ws.WsComponents;
  36. import org.sonarqube.ws.WsUsers;
  37. import org.sonarqube.ws.client.HttpException;
  38. import org.sonarqube.ws.client.PostRequest;
  39. import org.sonarqube.ws.client.WsClient;
  40. import org.sonarqube.ws.client.component.ComponentsService;
  41. import org.sonarqube.ws.client.organization.CreateWsRequest;
  42. import org.sonarqube.ws.client.organization.OrganizationService;
  43. import org.sonarqube.ws.client.organization.SearchWsRequest;
  44. import org.sonarqube.ws.client.organization.UpdateWsRequest;
  45. import org.sonarqube.ws.client.permission.AddUserWsRequest;
  46. import org.sonarqube.ws.client.permission.PermissionsService;
  47. import org.sonarqube.ws.client.user.GroupsRequest;
  48. import util.ItUtils;
  49. import util.user.GroupManagement;
  50. import util.user.Groups;
  51. import util.user.UserRule;
  52. import static it.Category6Suite.enableOrganizationsSupport;
  53. import static java.util.Collections.singletonList;
  54. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  55. import static org.assertj.core.api.Assertions.assertThat;
  56. import static org.junit.Assert.fail;
  57. import static util.ItUtils.deleteOrganizationsIfExists;
  58. import static util.ItUtils.newAdminWsClient;
  59. import static util.ItUtils.resetSettings;
  60. public class OrganizationTest {
  61. private static final String DEFAULT_ORGANIZATION_KEY = "default-organization";
  62. private static final String NAME = "Foo Company";
  63. private static final String KEY = "foo-company";
  64. private static final String DESCRIPTION = "the description of Foo company";
  65. private static final String URL = "https://www.foo.fr";
  66. private static final String AVATAR_URL = "https://www.foo.fr/corporate_logo.png";
  67. private static final String SETTING_ANYONE_CAN_CREATE_ORGANIZATIONS = "sonar.organizations.anyoneCanCreate";
  68. @ClassRule
  69. public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
  70. @ClassRule
  71. public static UserRule userRule = UserRule.from(orchestrator);
  72. @Rule
  73. public ExpectedException expectedException = ExpectedException.none();
  74. private WsClient adminClient = newAdminWsClient(orchestrator);
  75. private OrganizationService anonymousOrganizationService = ItUtils.newWsClient(orchestrator).organizations();
  76. private OrganizationService adminOrganizationService = adminClient.organizations();
  77. @BeforeClass
  78. public static void enableOrganizations() throws Exception {
  79. enableOrganizationsSupport();
  80. }
  81. @Before
  82. public void setUp() throws Exception {
  83. resetSettings(orchestrator, null, SETTING_ANYONE_CAN_CREATE_ORGANIZATIONS);
  84. deleteOrganizationsIfExists(orchestrator, KEY, "an-org");
  85. }
  86. @After
  87. public void tearDown() throws Exception {
  88. deleteOrganizationsIfExists(orchestrator, KEY, "an-org");
  89. }
  90. @Test
  91. public void create_update_delete_organizations_and_check_security() {
  92. verifyOrganizationDoesNotExit(KEY);
  93. Organizations.Organization createdOrganization = adminOrganizationService.create(new CreateWsRequest.Builder()
  94. .setName(NAME)
  95. .setKey(KEY)
  96. .setDescription(DESCRIPTION)
  97. .setUrl(URL)
  98. .setAvatar(AVATAR_URL)
  99. .build())
  100. .getOrganization();
  101. assertThat(createdOrganization.getName()).isEqualTo(NAME);
  102. assertThat(createdOrganization.getKey()).isEqualTo(KEY);
  103. assertThat(createdOrganization.getDescription()).isEqualTo(DESCRIPTION);
  104. assertThat(createdOrganization.getUrl()).isEqualTo(URL);
  105. assertThat(createdOrganization.getAvatar()).isEqualTo(AVATAR_URL);
  106. verifySingleSearchResult(createdOrganization, NAME, DESCRIPTION, URL, AVATAR_URL);
  107. // update by id
  108. adminOrganizationService.update(new UpdateWsRequest.Builder()
  109. .setKey(createdOrganization.getKey())
  110. .setName("new name")
  111. .setDescription("new description")
  112. .setUrl("new url")
  113. .setAvatar("new avatar url")
  114. .build());
  115. verifySingleSearchResult(createdOrganization, "new name", "new description", "new url", "new avatar url");
  116. // update by key
  117. adminOrganizationService.update(new UpdateWsRequest.Builder()
  118. .setKey(createdOrganization.getKey())
  119. .setName("new name 2")
  120. .setDescription("new description 2")
  121. .setUrl("new url 2")
  122. .setAvatar("new avatar url 2")
  123. .build());
  124. verifySingleSearchResult(createdOrganization, "new name 2", "new description 2", "new url 2", "new avatar url 2");
  125. // remove optional fields
  126. adminOrganizationService.update(new UpdateWsRequest.Builder()
  127. .setKey(createdOrganization.getKey())
  128. .setName("new name 3")
  129. .setDescription("")
  130. .setUrl("")
  131. .setAvatar("")
  132. .build());
  133. verifySingleSearchResult(createdOrganization, "new name 3", null, null, null);
  134. // delete organization
  135. adminOrganizationService.delete(createdOrganization.getKey());
  136. verifyOrganizationDoesNotExit(KEY);
  137. adminOrganizationService.create(new CreateWsRequest.Builder()
  138. .setName(NAME)
  139. .setKey(KEY)
  140. .build())
  141. .getOrganization();
  142. verifySingleSearchResult(createdOrganization, NAME, null, null, null);
  143. // verify anonymous can't create update nor delete an organization by default
  144. verifyAnonymousNotAuthorized(service -> service.create(new CreateWsRequest.Builder().setName("An org").build()));
  145. verifyUserNotAuthenticated(service -> service.update(new UpdateWsRequest.Builder().setKey(KEY).setName("new name").build()));
  146. verifyUserNotAuthenticated(service -> service.delete(KEY));
  147. // verify logged in user without any permission can't create update nor delete an organization by default
  148. userRule.createUser("john", "doh");
  149. verifyUserNotAuthorized("john", "doh", service -> service.create(new CreateWsRequest.Builder().setName("An org").build()));
  150. verifyUserNotAuthorized("john", "doh", service -> service.update(new UpdateWsRequest.Builder().setKey(KEY).setName("new name").build()));
  151. verifyUserNotAuthorized("john", "doh", service -> service.delete(KEY));
  152. ItUtils.setServerProperty(orchestrator, SETTING_ANYONE_CAN_CREATE_ORGANIZATIONS, "true");
  153. // verify anonymous still can't create update nor delete an organization if property is true
  154. verifyUserNotAuthenticated(service -> service.create(new CreateWsRequest.Builder().setName("An org").build()));
  155. verifyUserNotAuthenticated(service -> service.update(new UpdateWsRequest.Builder().setKey(KEY).setName("new name").build()));
  156. verifyUserNotAuthenticated(service -> service.delete(KEY));
  157. // verify logged in user without any permission can't create nor update nor delete an organization if property is true
  158. verifyUserNotAuthorized("john", "doh", service -> service.update(new UpdateWsRequest.Builder().setKey(KEY).setName("new name").build()));
  159. verifyUserNotAuthorized("john", "doh", service -> service.delete(KEY));
  160. // clean-up
  161. adminOrganizationService.delete(KEY);
  162. verifySingleSearchResult(
  163. verifyUserAuthorized("john", "doh", service -> service.create(new CreateWsRequest.Builder().setName("An org").build())).getOrganization(),
  164. "An org", null, null, null);
  165. }
  166. private void verifyAnonymousNotAuthorized(Consumer<OrganizationService> consumer) {
  167. try {
  168. consumer.accept(anonymousOrganizationService);
  169. fail("An HttpException should have been raised");
  170. } catch (HttpException e) {
  171. assertThat(e.code()).isEqualTo(403);
  172. }
  173. }
  174. private void verifyUserNotAuthenticated(Consumer<OrganizationService> consumer) {
  175. try {
  176. consumer.accept(anonymousOrganizationService);
  177. fail("An HttpException should have been raised");
  178. } catch (HttpException e) {
  179. assertThat(e.code()).isEqualTo(401);
  180. }
  181. }
  182. private void verifyUserNotAuthorized(String login, String password, Consumer<OrganizationService> consumer) {
  183. try {
  184. OrganizationService organizationService = ItUtils.newUserWsClient(orchestrator, login, password).organizations();
  185. consumer.accept(organizationService);
  186. fail("An HttpException should have been raised");
  187. } catch (HttpException e) {
  188. assertThat(e.code()).isEqualTo(403);
  189. }
  190. }
  191. private <T> T verifyUserAuthorized(String login, String password, Function<OrganizationService, T> consumer) {
  192. OrganizationService organizationService = ItUtils.newUserWsClient(orchestrator, login, password).organizations();
  193. return consumer.apply(organizationService);
  194. }
  195. @Test
  196. public void create_generates_key_from_name() {
  197. // create organization without key
  198. String name = "Foo Company to keyize";
  199. String expectedKey = "foo-company-to-keyize";
  200. Organizations.Organization createdOrganization = adminOrganizationService.create(new CreateWsRequest.Builder()
  201. .setName(name)
  202. .build())
  203. .getOrganization();
  204. assertThat(createdOrganization.getKey()).isEqualTo(expectedKey);
  205. verifySingleSearchResult(createdOrganization, name, null, null, null);
  206. // clean-up
  207. adminOrganizationService.delete(expectedKey);
  208. }
  209. @Test
  210. public void default_organization_can_not_be_deleted() {
  211. try {
  212. adminOrganizationService.delete(DEFAULT_ORGANIZATION_KEY);
  213. fail("a HttpException should have been raised");
  214. } catch (HttpException e) {
  215. assertThat(e.code()).isEqualTo(400);
  216. }
  217. }
  218. @Test
  219. public void create_fails_if_user_is_not_root() {
  220. userRule.createUser("foo", "bar");
  221. CreateWsRequest createWsRequest = new CreateWsRequest.Builder()
  222. .setName("bla bla")
  223. .build();
  224. OrganizationService fooUserOrganizationService = ItUtils.newUserWsClient(orchestrator, "foo", "bar").organizations();
  225. expect403HttpError(() -> fooUserOrganizationService.create(createWsRequest));
  226. userRule.setRoot("foo");
  227. assertThat(fooUserOrganizationService.create(createWsRequest).getOrganization().getKey()).isEqualTo("bla-bla");
  228. // delete org, attempt recreate when no root anymore and ensure it can't anymore
  229. fooUserOrganizationService.delete("bla-bla");
  230. userRule.unsetRoot("foo");
  231. expect403HttpError(() -> fooUserOrganizationService.create(createWsRequest));
  232. }
  233. @Test
  234. public void an_organization_member_can_analyze_project() {
  235. verifyOrganizationDoesNotExit(KEY);
  236. Organizations.Organization createdOrganization = adminOrganizationService.create(new CreateWsRequest.Builder()
  237. .setName(KEY)
  238. .setKey(KEY)
  239. .build())
  240. .getOrganization();
  241. verifySingleSearchResult(createdOrganization, KEY, null, null, null);
  242. userRule.createUser("bob", "bob");
  243. userRule.removeGroups("sonar-users");
  244. adminOrganizationService.addMember(KEY, "bob");
  245. addPermissionsToUser(KEY, "bob", "provisioning", "scan");
  246. ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample",
  247. "sonar.organization", KEY, "sonar.login", "bob", "sonar.password", "bob");
  248. ComponentsService componentsService = ItUtils.newAdminWsClient(orchestrator).components();
  249. assertThat(searchSampleProject(KEY, componentsService).getComponentsList()).hasSize(1);
  250. }
  251. @Test
  252. public void by_default_anonymous_cannot_analyse_project_on_organization() {
  253. verifyOrganizationDoesNotExit(KEY);
  254. Organizations.Organization createdOrganization = adminOrganizationService.create(new CreateWsRequest.Builder()
  255. .setName(KEY)
  256. .setKey(KEY)
  257. .build())
  258. .getOrganization();
  259. verifySingleSearchResult(createdOrganization, KEY, null, null, null);
  260. try {
  261. ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample",
  262. "sonar.organization", KEY);
  263. fail();
  264. } catch (BuildFailureException e) {
  265. assertThat(e.getResult().getLogs()).contains("Insufficient privileges");
  266. }
  267. ComponentsService componentsService = ItUtils.newAdminWsClient(orchestrator).components();
  268. assertThat(searchSampleProject(KEY, componentsService).getComponentsCount()).isEqualTo(0);
  269. }
  270. private void addPermissionsToUser(String orgKeyAndName, String login, String permission, String... otherPermissions) {
  271. PermissionsService permissionsService = ItUtils.newAdminWsClient(orchestrator).permissions();
  272. permissionsService.addUser(new AddUserWsRequest().setLogin(login).setOrganization(orgKeyAndName).setPermission(permission));
  273. for (String otherPermission : otherPermissions) {
  274. permissionsService.addUser(new AddUserWsRequest().setLogin(login).setOrganization(orgKeyAndName).setPermission(otherPermission));
  275. }
  276. }
  277. @Test
  278. public void deleting_an_organization_also_deletes_projects_and_check_security() {
  279. verifyOrganizationDoesNotExit(KEY);
  280. Organizations.Organization createdOrganization = adminOrganizationService.create(new CreateWsRequest.Builder()
  281. .setName(KEY)
  282. .setKey(KEY)
  283. .build())
  284. .getOrganization();
  285. verifySingleSearchResult(createdOrganization, KEY, null, null, null);
  286. GroupManagement groupManagement = userRule.forOrganization(KEY);
  287. userRule.createUser("bob", "bob");
  288. adminOrganizationService.addMember(KEY, "bob");
  289. groupManagement.createGroup("grp1");
  290. groupManagement.createGroup("grp2");
  291. groupManagement.associateGroupsToUser("bob", "grp1", "grp2");
  292. assertThat(groupManagement.getUserGroups("bob").getGroups())
  293. .extracting(Groups.Group::getName)
  294. .contains("grp1", "grp2");
  295. addPermissionsToUser(KEY, "bob", "provisioning", "scan");
  296. ItUtils.runProjectAnalysis(orchestrator, "shared/xoo-sample",
  297. "sonar.organization", KEY, "sonar.login", "bob", "sonar.password", "bob");
  298. ComponentsService componentsService = ItUtils.newAdminWsClient(orchestrator).components();
  299. assertThat(searchSampleProject(KEY, componentsService).getComponentsList()).hasSize(1);
  300. adminOrganizationService.delete(KEY);
  301. expect404HttpError(() -> searchSampleProject(KEY, componentsService));
  302. verifyOrganizationDoesNotExit(KEY);
  303. }
  304. @Test
  305. public void return_groups_belonging_to_a_user_on_an_organization() throws Exception {
  306. String userLogin = randomAlphabetic(10);
  307. String groupName = randomAlphabetic(10);
  308. adminClient.organizations().create(new CreateWsRequest.Builder().setKey(KEY).setName(KEY).build()).getOrganization();
  309. userRule.createUser(userLogin, userLogin);
  310. adminOrganizationService.addMember(KEY, userLogin);
  311. adminClient.wsConnector().call(new PostRequest("api/user_groups/create")
  312. .setParam("name", groupName)
  313. .setParam("description", groupName)
  314. .setParam("organization", KEY)).failIfNotSuccessful();
  315. adminClient.wsConnector().call(new PostRequest("api/user_groups/add_user")
  316. .setParam("login", userLogin)
  317. .setParam("name", groupName)
  318. .setParam("organization", KEY)).failIfNotSuccessful();
  319. List<WsUsers.GroupsWsResponse.Group> result = adminClient.users().groups(
  320. GroupsRequest.builder().setLogin(userLogin).setOrganization(KEY).build()).getGroupsList();
  321. assertThat(result).extracting(WsUsers.GroupsWsResponse.Group::getName).containsOnly(groupName);
  322. }
  323. private WsComponents.SearchWsResponse searchSampleProject(String organizationKey, ComponentsService componentsService) {
  324. return componentsService
  325. .search(new org.sonarqube.ws.client.component.SearchWsRequest()
  326. .setOrganization(organizationKey)
  327. .setQualifiers(singletonList("TRK"))
  328. .setQuery("sample"));
  329. }
  330. private void expect403HttpError(Runnable runnable) {
  331. try {
  332. runnable.run();
  333. fail("Ws call should have failed");
  334. } catch (HttpException e) {
  335. assertThat(e.code()).isEqualTo(403);
  336. }
  337. }
  338. private void expect404HttpError(Runnable runnable) {
  339. try {
  340. runnable.run();
  341. fail("Ws call should have failed");
  342. } catch (HttpException e) {
  343. assertThat(e.code()).isEqualTo(404);
  344. }
  345. }
  346. private void verifyOrganizationDoesNotExit(String organizationKey) {
  347. Organizations.SearchWsResponse searchWsResponse = anonymousOrganizationService.search(new SearchWsRequest.Builder().setOrganizations(organizationKey).build());
  348. assertThat(searchWsResponse.getOrganizationsList()).isEmpty();
  349. }
  350. private void verifySingleSearchResult(Organizations.Organization createdOrganization, String name, String description, String url,
  351. String avatarUrl) {
  352. List<Organizations.Organization> organizations = anonymousOrganizationService.search(new SearchWsRequest.Builder().setOrganizations(createdOrganization.getKey())
  353. .build()).getOrganizationsList();
  354. assertThat(organizations).hasSize(1);
  355. Organizations.Organization searchedOrganization = organizations.get(0);
  356. assertThat(searchedOrganization.getKey()).isEqualTo(createdOrganization.getKey());
  357. assertThat(searchedOrganization.getName()).isEqualTo(name);
  358. if (description == null) {
  359. assertThat(searchedOrganization.hasDescription()).isFalse();
  360. } else {
  361. assertThat(searchedOrganization.getDescription()).isEqualTo(description);
  362. }
  363. if (url == null) {
  364. assertThat(searchedOrganization.hasUrl()).isFalse();
  365. } else {
  366. assertThat(searchedOrganization.getUrl()).isEqualTo(url);
  367. }
  368. if (avatarUrl == null) {
  369. assertThat(searchedOrganization.hasAvatar()).isFalse();
  370. } else {
  371. assertThat(searchedOrganization.getAvatar()).isEqualTo(avatarUrl);
  372. }
  373. }
  374. }