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.

CreateActionTest.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2019 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.ws;
  21. import java.util.HashSet;
  22. import java.util.Optional;
  23. import org.junit.Before;
  24. import org.junit.Rule;
  25. import org.junit.Test;
  26. import org.junit.rules.ExpectedException;
  27. import org.mockito.ArgumentCaptor;
  28. import org.sonar.api.config.internal.MapSettings;
  29. import org.sonar.api.utils.System2;
  30. import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
  31. import org.sonar.core.config.CorePropertyDefinitions;
  32. import org.sonar.db.DbSession;
  33. import org.sonar.db.DbTester;
  34. import org.sonar.db.user.GroupDto;
  35. import org.sonar.db.user.UserDto;
  36. import org.sonar.server.authentication.CredentialsLocalAuthentication;
  37. import org.sonar.server.es.EsTester;
  38. import org.sonar.server.exceptions.ForbiddenException;
  39. import org.sonar.server.organization.DefaultOrganizationProvider;
  40. import org.sonar.server.organization.OrganizationUpdater;
  41. import org.sonar.server.organization.TestDefaultOrganizationProvider;
  42. import org.sonar.server.organization.TestOrganizationFlags;
  43. import org.sonar.server.tester.UserSessionRule;
  44. import org.sonar.server.user.NewUserNotifier;
  45. import org.sonar.server.user.UserUpdater;
  46. import org.sonar.server.user.index.UserIndexDefinition;
  47. import org.sonar.server.user.index.UserIndexer;
  48. import org.sonar.server.user.ws.CreateAction.CreateRequest;
  49. import org.sonar.server.usergroups.DefaultGroupFinder;
  50. import org.sonar.server.ws.TestRequest;
  51. import org.sonar.server.ws.WsActionTester;
  52. import org.sonarqube.ws.Users.CreateWsResponse;
  53. import org.sonarqube.ws.Users.CreateWsResponse.User;
  54. import static java.lang.String.format;
  55. import static java.util.Collections.singletonList;
  56. import static java.util.Optional.ofNullable;
  57. import static org.assertj.core.api.Assertions.assertThat;
  58. import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
  59. import static org.elasticsearch.index.query.QueryBuilders.termQuery;
  60. import static org.mockito.ArgumentMatchers.any;
  61. import static org.mockito.Mockito.mock;
  62. import static org.mockito.Mockito.verify;
  63. import static org.sonar.db.user.UserTesting.newUserDto;
  64. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_EMAIL;
  65. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_LOGIN;
  66. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_NAME;
  67. import static org.sonar.server.user.index.UserIndexDefinition.FIELD_SCM_ACCOUNTS;
  68. public class CreateActionTest {
  69. private static final String DEFAULT_GROUP_NAME = "sonar-users";
  70. private MapSettings settings = new MapSettings();
  71. private System2 system2 = new AlwaysIncreasingSystem2();
  72. @Rule
  73. public DbTester db = DbTester.create(system2);
  74. @Rule
  75. public EsTester es = EsTester.create();
  76. @Rule
  77. public UserSessionRule userSessionRule = UserSessionRule.standalone();
  78. @Rule
  79. public ExpectedException expectedException = ExpectedException.none();
  80. private UserIndexer userIndexer = new UserIndexer(db.getDbClient(), es.client());
  81. private GroupDto defaultGroupInDefaultOrg;
  82. private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db);
  83. private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone();
  84. private OrganizationUpdater organizationUpdater = mock(OrganizationUpdater.class);
  85. private CredentialsLocalAuthentication localAuthentication = new CredentialsLocalAuthentication(db.getDbClient());
  86. private WsActionTester tester = new WsActionTester(new CreateAction(
  87. db.getDbClient(),
  88. new UserUpdater(system2, mock(NewUserNotifier.class), db.getDbClient(), userIndexer, organizationFlags, defaultOrganizationProvider,
  89. organizationUpdater, new DefaultGroupFinder(db.getDbClient()), settings.asConfig(), localAuthentication),
  90. userSessionRule));
  91. @Before
  92. public void setUp() {
  93. defaultGroupInDefaultOrg = db.users().insertDefaultGroup(db.getDefaultOrganization(), DEFAULT_GROUP_NAME);
  94. }
  95. @Test
  96. public void create_user() {
  97. logInAsSystemAdministrator();
  98. CreateWsResponse response = call(CreateRequest.builder()
  99. .setLogin("john")
  100. .setName("John")
  101. .setEmail("john@email.com")
  102. .setScmAccounts(singletonList("jn"))
  103. .setPassword("1234")
  104. .build());
  105. assertThat(response.getUser())
  106. .extracting(User::getLogin, User::getName, User::getEmail, User::getScmAccountsList, User::getLocal)
  107. .containsOnly("john", "John", "john@email.com", singletonList("jn"), true);
  108. // exists in index
  109. assertThat(es.client().prepareSearch(UserIndexDefinition.TYPE_USER)
  110. .setQuery(boolQuery()
  111. .must(termQuery(FIELD_LOGIN, "john"))
  112. .must(termQuery(FIELD_NAME, "John"))
  113. .must(termQuery(FIELD_EMAIL, "john@email.com"))
  114. .must(termQuery(FIELD_SCM_ACCOUNTS, "jn")))
  115. .get().getHits().getHits()).hasSize(1);
  116. // exists in db
  117. Optional<UserDto> dbUser = db.users().selectUserByLogin("john");
  118. assertThat(dbUser).isPresent();
  119. assertThat(dbUser.get().isRoot()).isFalse();
  120. // member of default group in default organization
  121. assertThat(db.users().selectGroupIdsOfUser(dbUser.get())).containsOnly(defaultGroupInDefaultOrg.getId());
  122. }
  123. @Test
  124. public void create_user_calls_create_personal_organization_if_personal_organizations_are_enabled() {
  125. logInAsSystemAdministrator();
  126. enableCreatePersonalOrg(true);
  127. assertACallToOrganizationCreationWhenUserIsCreated();
  128. }
  129. @Test
  130. public void create_user_calls_create_personal_organization_if_personal_organizations_are_disabled() {
  131. logInAsSystemAdministrator();
  132. enableCreatePersonalOrg(false);
  133. assertACallToOrganizationCreationWhenUserIsCreated();
  134. }
  135. @Test
  136. public void create_user_associates_him_to_default_organization() {
  137. logInAsSystemAdministrator();
  138. enableCreatePersonalOrg(true);
  139. call(CreateRequest.builder()
  140. .setLogin("john")
  141. .setName("John")
  142. .setPassword("1234")
  143. .build());
  144. Optional<UserDto> dbUser = db.users().selectUserByLogin("john");
  145. assertThat(dbUser).isPresent();
  146. assertThat(db.getDbClient().organizationMemberDao().select(db.getSession(), defaultOrganizationProvider.get().getUuid(), dbUser.get().getId())).isPresent();
  147. }
  148. @Test
  149. public void create_local_user() {
  150. logInAsSystemAdministrator();
  151. call(CreateRequest.builder()
  152. .setLogin("john")
  153. .setName("John")
  154. .setPassword("1234")
  155. .setLocal(true)
  156. .build());
  157. assertThat(db.users().selectUserByLogin("john").get())
  158. .extracting(UserDto::isLocal, UserDto::getExternalIdentityProvider, UserDto::getExternalLogin, UserDto::isRoot)
  159. .containsOnly(true, "sonarqube", "john", false);
  160. }
  161. @Test
  162. public void create_none_local_user() {
  163. logInAsSystemAdministrator();
  164. call(CreateRequest.builder()
  165. .setLogin("john")
  166. .setName("John")
  167. .setLocal(false)
  168. .build());
  169. assertThat(db.users().selectUserByLogin("john").get())
  170. .extracting(UserDto::isLocal, UserDto::getExternalIdentityProvider, UserDto::getExternalLogin, UserDto::isRoot)
  171. .containsOnly(false, "sonarqube", "john", false);
  172. }
  173. @Test
  174. public void create_user_with_comma_in_scm_account() {
  175. logInAsSystemAdministrator();
  176. CreateWsResponse response = call(CreateRequest.builder()
  177. .setLogin("john")
  178. .setName("John")
  179. .setEmail("john@email.com")
  180. .setScmAccounts(singletonList("j,n"))
  181. .setPassword("1234")
  182. .build());
  183. assertThat(response.getUser().getScmAccountsList()).containsOnly("j,n");
  184. }
  185. @Test
  186. public void create_user_with_empty_email() {
  187. logInAsSystemAdministrator();
  188. call(CreateRequest.builder()
  189. .setLogin("john")
  190. .setName("John")
  191. .setPassword("1234")
  192. .setEmail("")
  193. .build());
  194. assertThat(db.users().selectUserByLogin("john").get())
  195. .extracting(UserDto::getExternalLogin)
  196. .containsOnly("john");
  197. }
  198. @Test
  199. public void create_user_with_deprecated_scmAccounts_parameter() {
  200. logInAsSystemAdministrator();
  201. tester.newRequest()
  202. .setParam("login", "john")
  203. .setParam("name", "John")
  204. .setParam("password", "1234")
  205. .setParam("scmAccounts", "jn")
  206. .execute();
  207. assertThat(db.users().selectUserByLogin("john").get().getScmAccountsAsList()).containsOnly("jn");
  208. }
  209. @Test
  210. public void create_user_with_deprecated_scm_accounts_parameter() {
  211. logInAsSystemAdministrator();
  212. tester.newRequest()
  213. .setParam("login", "john")
  214. .setParam("name", "John")
  215. .setParam("password", "1234")
  216. .setParam("scm_accounts", "jn")
  217. .execute();
  218. assertThat(db.users().selectUserByLogin("john").get().getScmAccountsAsList()).containsOnly("jn");
  219. }
  220. @Test
  221. public void reactivate_user() {
  222. logInAsSystemAdministrator();
  223. db.users().insertUser(newUserDto("john", "John", "john@email.com").setActive(false));
  224. userIndexer.indexOnStartup(new HashSet<>());
  225. call(CreateRequest.builder()
  226. .setLogin("john")
  227. .setName("John")
  228. .setEmail("john@email.com")
  229. .setScmAccounts(singletonList("jn"))
  230. .setPassword("1234")
  231. .build());
  232. assertThat(db.users().selectUserByLogin("john").get().isActive()).isTrue();
  233. }
  234. @Test
  235. public void fail_to_reactivate_user_when_active_user_exists() {
  236. logInAsSystemAdministrator();
  237. UserDto user = db.users().insertUser();
  238. expectedException.expect(IllegalArgumentException.class);
  239. expectedException.expectMessage(format("An active user with login '%s' already exists", user.getLogin()));
  240. call(CreateRequest.builder()
  241. .setLogin(user.getLogin())
  242. .setName("John")
  243. .setPassword("1234")
  244. .build());
  245. }
  246. @Test
  247. public void fail_when_missing_login() {
  248. logInAsSystemAdministrator();
  249. expectedException.expect(IllegalArgumentException.class);
  250. expectedException.expectMessage("Login is mandatory and must not be empty");
  251. call(CreateRequest.builder()
  252. .setLogin(null)
  253. .setName("John")
  254. .setPassword("1234")
  255. .build());
  256. }
  257. @Test
  258. public void fail_when_login_is_too_short() {
  259. logInAsSystemAdministrator();
  260. expectedException.expect(IllegalArgumentException.class);
  261. expectedException.expectMessage("'login' length (1) is shorter than the minimum authorized (2)");
  262. call(CreateRequest.builder()
  263. .setLogin("a")
  264. .setName("John")
  265. .setPassword("1234")
  266. .build());
  267. }
  268. @Test
  269. public void fail_when_missing_name() {
  270. logInAsSystemAdministrator();
  271. expectedException.expect(IllegalArgumentException.class);
  272. expectedException.expectMessage("Name is mandatory and must not be empty");
  273. call(CreateRequest.builder()
  274. .setLogin("john")
  275. .setName(null)
  276. .setPassword("1234")
  277. .build());
  278. }
  279. @Test
  280. public void fail_when_missing_password() {
  281. logInAsSystemAdministrator();
  282. expectedException.expect(IllegalArgumentException.class);
  283. expectedException.expectMessage("Password is mandatory and must not be empty");
  284. call(CreateRequest.builder()
  285. .setLogin("john")
  286. .setName("John")
  287. .setPassword(null)
  288. .build());
  289. }
  290. @Test
  291. public void fail_when_password_is_set_on_none_local_user() {
  292. logInAsSystemAdministrator();
  293. expectedException.expect(IllegalArgumentException.class);
  294. expectedException.expectMessage("Password should only be set on local user");
  295. call(CreateRequest.builder()
  296. .setLogin("john")
  297. .setName("John")
  298. .setPassword("1234")
  299. .setLocal(false)
  300. .build());
  301. }
  302. @Test
  303. public void fail_when_email_is_invalid() {
  304. logInAsSystemAdministrator();
  305. expectedException.expect(IllegalArgumentException.class);
  306. expectedException.expectMessage("Email 'invalid-email' is not valid");
  307. call(CreateRequest.builder()
  308. .setLogin("pipo")
  309. .setName("John")
  310. .setPassword("1234")
  311. .setEmail("invalid-email")
  312. .build());
  313. }
  314. @Test
  315. public void throw_ForbiddenException_if_not_system_administrator() {
  316. userSessionRule.logIn().setNonSystemAdministrator();
  317. expectedException.expect(ForbiddenException.class);
  318. expectedException.expectMessage("");
  319. expectedException.expect(ForbiddenException.class);
  320. executeRequest("john");
  321. }
  322. private CreateWsResponse executeRequest(String login) {
  323. return call(CreateRequest.builder()
  324. .setLogin(login)
  325. .setName("name of " + login)
  326. .setEmail(login + "@email.com")
  327. .setScmAccounts(singletonList(login.substring(0, 2)))
  328. .setPassword("pwd_" + login)
  329. .build());
  330. }
  331. private void assertACallToOrganizationCreationWhenUserIsCreated() {
  332. call(CreateRequest.builder()
  333. .setLogin("john")
  334. .setName("John")
  335. .setPassword("1234")
  336. .build());
  337. Optional<UserDto> dbUser = db.users().selectUserByLogin("john");
  338. assertThat(dbUser).isPresent();
  339. ArgumentCaptor<UserDto> userCaptor = ArgumentCaptor.forClass(UserDto.class);
  340. verify(organizationUpdater).createForUser(any(DbSession.class), userCaptor.capture());
  341. assertThat(userCaptor.getValue().getId()).isEqualTo(dbUser.get().getId());
  342. }
  343. private void logInAsSystemAdministrator() {
  344. userSessionRule.logIn().setSystemAdministrator();
  345. }
  346. private CreateWsResponse call(CreateRequest createRequest) {
  347. TestRequest request = tester.newRequest();
  348. ofNullable(createRequest.getLogin()).ifPresent(e4 -> request.setParam("login", e4));
  349. ofNullable(createRequest.getName()).ifPresent(e3 -> request.setParam("name", e3));
  350. ofNullable(createRequest.getEmail()).ifPresent(e2 -> request.setParam("email", e2));
  351. ofNullable(createRequest.getPassword()).ifPresent(e1 -> request.setParam("password", e1));
  352. ofNullable(createRequest.getScmAccounts()).ifPresent(e -> request.setMultiParam("scmAccount", e));
  353. request.setParam("local", createRequest.isLocal() ? "true" : "false");
  354. return request.executeProtobuf(CreateWsResponse.class);
  355. }
  356. private void enableCreatePersonalOrg(boolean flag) {
  357. settings.setProperty(CorePropertyDefinitions.ORGANIZATIONS_CREATE_PERSONAL_ORG, flag);
  358. }
  359. }