import it.serverSystem.ServerSystemRestartingOrchestrator;
import it.settings.SettingsTestRestartingOrchestrator;
import it.updateCenter.UpdateCenterTest;
-import it.user.RailsExternalAuthenticationTest;
+import it.user.RealmAuthenticationTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
SettingsTestRestartingOrchestrator.class,
// update center
UpdateCenterTest.class,
- RailsExternalAuthenticationTest.class
+ RealmAuthenticationTest.class
})
public class Category5Suite {
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package it.user;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.selenium.Selenese;
-import java.util.Map;
-import java.util.Objects;
-import org.apache.commons.lang.RandomStringUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.wsclient.Host;
-import org.sonar.wsclient.Sonar;
-import org.sonar.wsclient.base.HttpException;
-import org.sonar.wsclient.connectors.ConnectionException;
-import org.sonar.wsclient.connectors.HttpClient4Connector;
-import org.sonar.wsclient.services.AuthenticationQuery;
-import org.sonar.wsclient.services.UserPropertyCreateQuery;
-import org.sonar.wsclient.services.UserPropertyQuery;
-import org.sonar.wsclient.user.UserParameters;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.HttpConnector;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.WsClientFactories;
-import org.sonarqube.ws.client.WsResponse;
-import util.selenium.SeleneseTest;
-
-import static java.net.HttpURLConnection.HTTP_OK;
-import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.Assert.fail;
-import static util.ItUtils.pluginArtifact;
-import static util.ItUtils.setServerProperty;
-
-/**
- * Test deprecated authentication done by Rails. It's kept has every features has not bee migrated to java yet.
- *
- * It starts its own server as it's using a different authentication system
- */
-public class RailsExternalAuthenticationTest {
-
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
- static final String TECH_USER = "techUser";
- static final String USER_LOGIN = "tester";
-
- /**
- * Property from security-plugin for user management.
- */
- private static final String USERS_PROPERTY = "sonar.fakeauthenticator.users";
- private static String AUTHORIZED = "authorized";
- private static String NOT_AUTHORIZED = "not authorized";
-
- @ClassRule
- public static final Orchestrator orchestrator = Orchestrator.builderEnv()
- .addPlugin(pluginArtifact("security-plugin"))
- .setServerProperty("sonar.security.realm", "FakeRealm")
- .build();
-
- @Before
- @After
- public void resetData() throws Exception {
- setServerProperty(orchestrator, USERS_PROPERTY, null);
- setServerProperty(orchestrator, "sonar.security.updateUserAttributes", null);
- setServerProperty(orchestrator, "sonar.authenticator.createUsers", null);
- resetUsers(USER_LOGIN, TECH_USER);
- }
-
- private void resetUsers(String... logins) {
- for (String login : logins) {
- String result = orchestrator.getServer().adminWsClient().get("/api/users/search?q=" + login);
- if (result.contains(login)) {
- orchestrator.getServer().adminWsClient().userClient().deactivate(login);
- }
- }
- }
-
- /**
- * SONAR-3137, SONAR-2292
- * Restriction on password length (minimum 4 characters) should be disabled, when external system enabled.
- */
- @Test
- public void shouldSynchronizeDetailsAndGroups() {
- // Given clean Sonar installation and no users in external system
- String username = USER_LOGIN;
- String password = "123";
- Map<String, String> users = Maps.newHashMap();
-
- // When user created in external system
- users.put(username + ".password", password);
- users.put(username + ".name", "Tester Testerovich");
- users.put(username + ".email", "tester@example.org");
- users.put(username + ".groups", "sonar-user");
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
- // with external details and groups
- new SeleneseTest(
- Selenese.builder().setHtmlTestsInClasspath("external-user-details",
- "/user/ExternalAuthenticationTest/external-user-details.html")
- .build()).runOn(orchestrator);
-
- // SONAR-4462
- new SeleneseTest(
- Selenese.builder().setHtmlTestsInClasspath("system-info",
- "/user/ExternalAuthenticationTest/system-info.html")
- .build()).runOn(orchestrator);
- }
-
- /**
- * SONAR-4034
- */
- @Test
- public void shouldUpdateDetailsByDefault() {
- // Given clean Sonar installation and no users in external system
- String username = USER_LOGIN;
- String password = "123";
- Map<String, String> users = Maps.newHashMap();
-
- // When user created in external system
- users.put(username + ".password", password);
- users.put(username + ".name", "Tester Testerovich");
- users.put(username + ".email", "tester@example.org");
- users.put(username + ".groups", "sonar-user");
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
- // with external details and groups
- // TODO replace by WS ? Or with new Selenese utils
- new SeleneseTest(
- Selenese.builder().setHtmlTestsInClasspath("external-user-details",
- "/user/ExternalAuthenticationTest/external-user-details.html")
- .build()).runOn(orchestrator);
-
- // Now update user details
- users.put(username + ".name", "Tester2 Testerovich");
- users.put(username + ".email", "tester2@example.org");
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
- // with external details and groups updated
- new SeleneseTest(
- Selenese.builder().setHtmlTestsInClasspath("external-user-details2",
- "/user/ExternalAuthenticationTest/external-user-details2.html").build()).runOn(orchestrator);
- }
-
- /**
- * SONAR-3138
- */
- @Test
- public void shouldNotFallback() {
- // Given clean Sonar installation and no users in external system
- String login = USER_LOGIN;
- String password = "1234567";
- Map<String, String> users = Maps.newHashMap();
-
- // When user created in external system
- users.put(login + ".password", password);
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(login, password)).isEqualTo(AUTHORIZED);
-
- // When external system does not work
- users.remove(login + ".password");
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(login, password)).isEqualTo(NOT_AUTHORIZED);
- }
-
- /**
- * SONAR-4543
- */
- @Test
- public void adminIsLocalAccountByDefault() {
- // Given clean Sonar installation and no users in external system
- String login = "admin";
- String localPassword = "admin";
- String remotePassword = "nimda";
- Map<String, String> users = Maps.newHashMap();
-
- // When admin created in external system with a different password
- users.put(login + ".password", remotePassword);
- updateUsersInExtAuth(users);
-
- // Then this is local DB that should be used
- assertThat(loginAttempt(login, remotePassword)).isEqualTo(NOT_AUTHORIZED);
- assertThat(loginAttempt(login, localPassword)).isEqualTo(AUTHORIZED);
- }
-
- /**
- * SONAR-1334, SONAR-3185 (createUsers=true is default)
- */
- @Test
- public void shouldCreateNewUsers() {
- // Given clean Sonar installation and no users in external system
- String username = USER_LOGIN;
- String password = "1234567";
- Map<String, String> users = Maps.newHashMap();
-
- // When user not exists in external system
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
-
- // When user created in external system
- users.put(username + ".password", password);
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
- assertThat(loginAttempt(username, "wrong")).isEqualTo(NOT_AUTHORIZED);
- }
-
- /**
- * SONAR-1334 (createUsers=false)
- */
- @Test
- public void shouldNotCreateNewUsers() {
- // Given clean Sonar installation and no users in external system
- setServerProperty(orchestrator, "sonar.authenticator.createUsers", "false");
- // Use a random user name because if we use existing disabled user then it doesn't work because rails doesn't handle this case
- // (it's using User.find_by_login to know if user exists or not
- String username = RandomStringUtils.randomAlphanumeric(20);
- String password = "1234567";
- Map<String, String> users = Maps.newHashMap();
-
- // When user not exists in external system
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
-
- // When user created in external system
- users.put(username + ".password", password);
- updateUsersInExtAuth(users);
- // Then
- assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
- }
-
- // SONAR-3258
- @Test
- public void shouldAutomaticallyReactivateDeletedUser() throws Exception {
- // Given clean Sonar installation and no users in external system
-
- // Let's create and delete the user "tester" in Sonar DB
- new SeleneseTest(
- Selenese.builder().setHtmlTestsInClasspath("external-user-create-and-delete-user",
- "/user/ExternalAuthenticationTest/create-and-delete-user.html").build()).runOn(orchestrator);
-
- // And now update the security with the user that was deleted
- String login = USER_LOGIN;
- String password = "1234567";
- Map<String, String> users = Maps.newHashMap();
- users.put(login + ".password", password);
- updateUsersInExtAuth(users);
- // check that the deleted/deactivated user "tester" has been reactivated and can now log in
- assertThat(loginAttempt(login, password)).isEqualTo(AUTHORIZED);
- }
-
- /**
- * SONAR-7036
- */
- @Test
- public void update_password_of_technical_user() throws Exception {
- // Create user in external authentication
- updateUsersInExtAuth(ImmutableMap.of(USER_LOGIN + ".password", USER_LOGIN));
- assertThat(loginAttempt(USER_LOGIN, USER_LOGIN)).isEqualTo(AUTHORIZED);
-
- // Create technical user in db
- createUserInDb(TECH_USER, "old_password");
- assertThat(checkAuthenticationThroughWebService(TECH_USER, "old_password")).isTrue();
-
- // Updating password of technical user is allowed
- updateUserPasswordInDb(TECH_USER, "new_password");
- assertThat(checkAuthenticationThroughWebService(TECH_USER, "new_password")).isTrue();
-
- // But updating password of none local user is not allowed
- try {
- updateUserPasswordInDb(USER_LOGIN, "new_password");
- fail();
- } catch (HttpException e) {
- verifyHttpException(e, 400);
- }
- }
-
- /**
- * SONAR-7640
- */
- @Test
- public void authentication_with_ws() throws Exception {
- // Given clean Sonar installation and no users in external system
- String login = USER_LOGIN;
- String password = "1234567";
- Map<String, String> users = Maps.newHashMap();
-
- // When user created in external system
- users.put(login + ".password", password);
- updateUsersInExtAuth(users);
-
- assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_OK);
- assertThat(checkAuthenticationWithWebService("wrong", password).code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(login, "wrong").code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(login, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(null, null).code()).isEqualTo(HTTP_OK);
-
- setServerProperty(orchestrator, "sonar.forceAuthentication", "true");
-
- assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_OK);
- assertThat(checkAuthenticationWithWebService("wrong", password).code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(login, "wrong").code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(login, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
- assertThat(checkAuthenticationWithWebService(null, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
- }
-
- protected void verifyHttpException(Exception e, int expectedCode) {
- assertThat(e).isInstanceOf(HttpException.class);
- HttpException exception = (HttpException) e;
- assertThat(exception.status()).isEqualTo(expectedCode);
- }
-
- private boolean checkAuthenticationThroughWebService(String login, String password) {
- return createWsClient(login, password).find(new AuthenticationQuery()).isValid();
- }
-
- /**
- * Utility method to check that user can be authorized.
- *
- * @throws IllegalStateException
- */
- private String loginAttempt(String username, String password) {
- String expectedValue = Long.toString(System.currentTimeMillis());
- Sonar wsClient = createWsClient(username, password);
- try {
- wsClient.create(new UserPropertyCreateQuery("auth", expectedValue));
- } catch (ConnectionException e) {
- return NOT_AUTHORIZED;
- }
- try {
- String value = wsClient.find(new UserPropertyQuery("auth")).getValue();
- if (!Objects.equals(value, expectedValue)) {
- // exceptional case - update+retrieval were successful, but value doesn't match
- throw new IllegalStateException("Expected " + expectedValue + " , but got " + value);
- }
- } catch (ConnectionException e) {
- // exceptional case - update was successful, but not retrieval
- throw new IllegalStateException(e);
- }
- return AUTHORIZED;
- }
-
- /**
- * Updates information about users in security-plugin.
- */
- private static void updateUsersInExtAuth(Map<String, String> users) {
- setServerProperty(orchestrator, USERS_PROPERTY, format(users));
- }
-
- private void createUserInDb(String login, String password) {
- orchestrator.getServer().adminWsClient().userClient().create(UserParameters.create().login(login).name(login)
- .password(password).passwordConfirmation(password));
- }
-
- private void updateUserPasswordInDb(String login, String newPassword) {
- orchestrator.getServer().adminWsClient().post("/api/users/change_password", "login", login, "password", newPassword);
- }
-
- /**
- * Utility method to create {@link Sonar} with specified {@code username} and {@code password}.
- * Orchestrator does not provide such method.
- */
- private Sonar createWsClient(String username, String password) {
- return new Sonar(new HttpClient4Connector(new Host(orchestrator.getServer().getUrl(), username, password)));
- }
-
- private static String format(Map<String, String> map) {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<String, String> entry : map.entrySet()) {
- sb.append(entry.getKey()).append('=').append(entry.getValue()).append('\n');
- }
- return sb.toString();
- }
-
- private WsResponse checkAuthenticationWithWebService(String login, String password) {
- WsClient wsClient = WsClientFactories.getDefault().newClient(HttpConnector.newBuilder().url(orchestrator.getServer().getUrl()).credentials(login, password).build());
- // Call any WS
- return wsClient.wsConnector().call(new GetRequest("api/rules/search"));
- }
-
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package it.user;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.selenium.Selenese;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.commons.lang.RandomStringUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.wsclient.Host;
+import org.sonar.wsclient.Sonar;
+import org.sonar.wsclient.base.HttpException;
+import org.sonar.wsclient.connectors.ConnectionException;
+import org.sonar.wsclient.connectors.HttpClient4Connector;
+import org.sonar.wsclient.services.AuthenticationQuery;
+import org.sonar.wsclient.services.UserPropertyCreateQuery;
+import org.sonar.wsclient.services.UserPropertyQuery;
+import org.sonar.wsclient.user.UserParameters;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsClientFactories;
+import org.sonarqube.ws.client.WsResponse;
+import util.selenium.SeleneseTest;
+
+import static java.net.HttpURLConnection.HTTP_OK;
+import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+import static util.ItUtils.pluginArtifact;
+import static util.ItUtils.setServerProperty;
+
+/**
+ * Test REALM authentication.
+ *
+ * It starts its own server as it's using a different authentication system
+ */
+public class RealmAuthenticationTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ static final String TECH_USER = "techUser";
+ static final String USER_LOGIN = "tester";
+
+ /**
+ * Property from security-plugin for user management.
+ */
+ private static final String USERS_PROPERTY = "sonar.fakeauthenticator.users";
+ private static String AUTHORIZED = "authorized";
+ private static String NOT_AUTHORIZED = "not authorized";
+
+ @ClassRule
+ public static final Orchestrator orchestrator = Orchestrator.builderEnv()
+ .addPlugin(pluginArtifact("security-plugin"))
+ .setServerProperty("sonar.security.realm", "FakeRealm")
+ .build();
+
+ @Before
+ @After
+ public void resetData() throws Exception {
+ setServerProperty(orchestrator, USERS_PROPERTY, null);
+ setServerProperty(orchestrator, "sonar.security.updateUserAttributes", null);
+ setServerProperty(orchestrator, "sonar.authenticator.createUsers", null);
+ resetUsers(USER_LOGIN, TECH_USER);
+ }
+
+ private void resetUsers(String... logins) {
+ for (String login : logins) {
+ String result = orchestrator.getServer().adminWsClient().get("/api/users/search?q=" + login);
+ if (result.contains(login)) {
+ orchestrator.getServer().adminWsClient().userClient().deactivate(login);
+ }
+ }
+ }
+
+ /**
+ * SONAR-3137, SONAR-2292
+ * Restriction on password length (minimum 4 characters) should be disabled, when external system enabled.
+ */
+ @Test
+ public void shouldSynchronizeDetailsAndGroups() {
+ // Given clean Sonar installation and no users in external system
+ String username = USER_LOGIN;
+ String password = "123";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user created in external system
+ users.put(username + ".password", password);
+ users.put(username + ".name", "Tester Testerovich");
+ users.put(username + ".email", "tester@example.org");
+ users.put(username + ".groups", "sonar-user");
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
+ // with external details and groups
+ new SeleneseTest(
+ Selenese.builder().setHtmlTestsInClasspath("external-user-details",
+ "/user/ExternalAuthenticationTest/external-user-details.html")
+ .build()).runOn(orchestrator);
+
+ // SONAR-4462
+ new SeleneseTest(
+ Selenese.builder().setHtmlTestsInClasspath("system-info",
+ "/user/ExternalAuthenticationTest/system-info.html")
+ .build()).runOn(orchestrator);
+ }
+
+ /**
+ * SONAR-4034
+ */
+ @Test
+ public void shouldUpdateDetailsByDefault() {
+ // Given clean Sonar installation and no users in external system
+ String username = USER_LOGIN;
+ String password = "123";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user created in external system
+ users.put(username + ".password", password);
+ users.put(username + ".name", "Tester Testerovich");
+ users.put(username + ".email", "tester@example.org");
+ users.put(username + ".groups", "sonar-user");
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
+ // with external details and groups
+ // TODO replace by WS ? Or with new Selenese utils
+ new SeleneseTest(
+ Selenese.builder().setHtmlTestsInClasspath("external-user-details",
+ "/user/ExternalAuthenticationTest/external-user-details.html")
+ .build()).runOn(orchestrator);
+
+ // Now update user details
+ users.put(username + ".name", "Tester2 Testerovich");
+ users.put(username + ".email", "tester2@example.org");
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
+ // with external details and groups updated
+ new SeleneseTest(
+ Selenese.builder().setHtmlTestsInClasspath("external-user-details2",
+ "/user/ExternalAuthenticationTest/external-user-details2.html").build()).runOn(orchestrator);
+ }
+
+ /**
+ * SONAR-3138
+ */
+ @Test
+ public void shouldNotFallback() {
+ // Given clean Sonar installation and no users in external system
+ String login = USER_LOGIN;
+ String password = "1234567";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user created in external system
+ users.put(login + ".password", password);
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(login, password)).isEqualTo(AUTHORIZED);
+
+ // When external system does not work
+ users.remove(login + ".password");
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(login, password)).isEqualTo(NOT_AUTHORIZED);
+ }
+
+ /**
+ * SONAR-4543
+ */
+ @Test
+ public void adminIsLocalAccountByDefault() {
+ // Given clean Sonar installation and no users in external system
+ String login = "admin";
+ String localPassword = "admin";
+ String remotePassword = "nimda";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When admin created in external system with a different password
+ users.put(login + ".password", remotePassword);
+ updateUsersInExtAuth(users);
+
+ // Then this is local DB that should be used
+ assertThat(loginAttempt(login, remotePassword)).isEqualTo(NOT_AUTHORIZED);
+ assertThat(loginAttempt(login, localPassword)).isEqualTo(AUTHORIZED);
+ }
+
+ /**
+ * SONAR-1334, SONAR-3185 (createUsers=true is default)
+ */
+ @Test
+ public void shouldCreateNewUsers() {
+ // Given clean Sonar installation and no users in external system
+ String username = USER_LOGIN;
+ String password = "1234567";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user not exists in external system
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
+
+ // When user created in external system
+ users.put(username + ".password", password);
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(AUTHORIZED);
+ assertThat(loginAttempt(username, "wrong")).isEqualTo(NOT_AUTHORIZED);
+ }
+
+ /**
+ * SONAR-1334 (createUsers=false)
+ */
+ @Test
+ public void shouldNotCreateNewUsers() {
+ // Given clean Sonar installation and no users in external system
+ setServerProperty(orchestrator, "sonar.authenticator.createUsers", "false");
+ // Use a random user name because if we use existing disabled user then it doesn't work because rails doesn't handle this case
+ // (it's using User.find_by_login to know if user exists or not
+ String username = RandomStringUtils.randomAlphanumeric(20);
+ String password = "1234567";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user not exists in external system
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
+
+ // When user created in external system
+ users.put(username + ".password", password);
+ updateUsersInExtAuth(users);
+ // Then
+ assertThat(loginAttempt(username, password)).isEqualTo(NOT_AUTHORIZED);
+ }
+
+ // SONAR-3258
+ @Test
+ public void shouldAutomaticallyReactivateDeletedUser() throws Exception {
+ // Given clean Sonar installation and no users in external system
+
+ // Let's create and delete the user "tester" in Sonar DB
+ new SeleneseTest(
+ Selenese.builder().setHtmlTestsInClasspath("external-user-create-and-delete-user",
+ "/user/ExternalAuthenticationTest/create-and-delete-user.html").build()).runOn(orchestrator);
+
+ // And now update the security with the user that was deleted
+ String login = USER_LOGIN;
+ String password = "1234567";
+ Map<String, String> users = Maps.newHashMap();
+ users.put(login + ".password", password);
+ updateUsersInExtAuth(users);
+ // check that the deleted/deactivated user "tester" has been reactivated and can now log in
+ assertThat(loginAttempt(login, password)).isEqualTo(AUTHORIZED);
+ }
+
+ /**
+ * SONAR-7036
+ */
+ @Test
+ public void update_password_of_technical_user() throws Exception {
+ // Create user in external authentication
+ updateUsersInExtAuth(ImmutableMap.of(USER_LOGIN + ".password", USER_LOGIN));
+ assertThat(loginAttempt(USER_LOGIN, USER_LOGIN)).isEqualTo(AUTHORIZED);
+
+ // Create technical user in db
+ createUserInDb(TECH_USER, "old_password");
+ assertThat(checkAuthenticationThroughWebService(TECH_USER, "old_password")).isTrue();
+
+ // Updating password of technical user is allowed
+ updateUserPasswordInDb(TECH_USER, "new_password");
+ assertThat(checkAuthenticationThroughWebService(TECH_USER, "new_password")).isTrue();
+
+ // But updating password of none local user is not allowed
+ try {
+ updateUserPasswordInDb(USER_LOGIN, "new_password");
+ fail();
+ } catch (HttpException e) {
+ verifyHttpException(e, 400);
+ }
+ }
+
+ /**
+ * SONAR-7640
+ */
+ @Test
+ public void authentication_with_ws() throws Exception {
+ // Given clean Sonar installation and no users in external system
+ String login = USER_LOGIN;
+ String password = "1234567";
+ Map<String, String> users = Maps.newHashMap();
+
+ // When user created in external system
+ users.put(login + ".password", password);
+ updateUsersInExtAuth(users);
+
+ assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_OK);
+ assertThat(checkAuthenticationWithWebService("wrong", password).code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(login, "wrong").code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(login, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(null, null).code()).isEqualTo(HTTP_OK);
+
+ setServerProperty(orchestrator, "sonar.forceAuthentication", "true");
+
+ assertThat(checkAuthenticationWithWebService(login, password).code()).isEqualTo(HTTP_OK);
+ assertThat(checkAuthenticationWithWebService("wrong", password).code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(login, "wrong").code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(login, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
+ assertThat(checkAuthenticationWithWebService(null, null).code()).isEqualTo(HTTP_UNAUTHORIZED);
+ }
+
+ protected void verifyHttpException(Exception e, int expectedCode) {
+ assertThat(e).isInstanceOf(HttpException.class);
+ HttpException exception = (HttpException) e;
+ assertThat(exception.status()).isEqualTo(expectedCode);
+ }
+
+ private boolean checkAuthenticationThroughWebService(String login, String password) {
+ return createWsClient(login, password).find(new AuthenticationQuery()).isValid();
+ }
+
+ /**
+ * Utility method to check that user can be authorized.
+ *
+ * @throws IllegalStateException
+ */
+ private String loginAttempt(String username, String password) {
+ String expectedValue = Long.toString(System.currentTimeMillis());
+ Sonar wsClient = createWsClient(username, password);
+ try {
+ wsClient.create(new UserPropertyCreateQuery("auth", expectedValue));
+ } catch (ConnectionException e) {
+ return NOT_AUTHORIZED;
+ }
+ try {
+ String value = wsClient.find(new UserPropertyQuery("auth")).getValue();
+ if (!Objects.equals(value, expectedValue)) {
+ // exceptional case - update+retrieval were successful, but value doesn't match
+ throw new IllegalStateException("Expected " + expectedValue + " , but got " + value);
+ }
+ } catch (ConnectionException e) {
+ // exceptional case - update was successful, but not retrieval
+ throw new IllegalStateException(e);
+ }
+ return AUTHORIZED;
+ }
+
+ /**
+ * Updates information about users in security-plugin.
+ */
+ private static void updateUsersInExtAuth(Map<String, String> users) {
+ setServerProperty(orchestrator, USERS_PROPERTY, format(users));
+ }
+
+ private void createUserInDb(String login, String password) {
+ orchestrator.getServer().adminWsClient().userClient().create(UserParameters.create().login(login).name(login)
+ .password(password).passwordConfirmation(password));
+ }
+
+ private void updateUserPasswordInDb(String login, String newPassword) {
+ orchestrator.getServer().adminWsClient().post("/api/users/change_password", "login", login, "password", newPassword);
+ }
+
+ /**
+ * Utility method to create {@link Sonar} with specified {@code username} and {@code password}.
+ * Orchestrator does not provide such method.
+ */
+ private Sonar createWsClient(String username, String password) {
+ return new Sonar(new HttpClient4Connector(new Host(orchestrator.getServer().getUrl(), username, password)));
+ }
+
+ private static String format(Map<String, String> map) {
+ StringBuilder sb = new StringBuilder();
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ sb.append(entry.getKey()).append('=').append(entry.getValue()).append('\n');
+ }
+ return sb.toString();
+ }
+
+ private WsResponse checkAuthenticationWithWebService(String login, String password) {
+ WsClient wsClient = WsClientFactories.getDefault().newClient(HttpConnector.newBuilder().url(orchestrator.getServer().getUrl()).credentials(login, password).build());
+ // Call any WS
+ return wsClient.wsConnector().call(new GetRequest("api/rules/search"));
+ }
+
+}
+ 26 // level 1
+ 47 // content of DaoModule
+ 2 // content of EsSearchModule
- + 55 // content of CorePropertyDefinitions
+ + 54 // content of CorePropertyDefinitions
+ 1 // content of CePropertyDefinitions
);
assertThat(picoContainer.getParent().getParent().getParent().getParent()).isNull();
import org.sonar.server.startup.ClearRulesOverloadedDebt;
import org.sonar.server.startup.DeleteOldAnalysisReportsFromFs;
import org.sonar.server.startup.DisplayLogOnDeprecatedProjects;
-import org.sonar.server.startup.FeedUsersLocalStartupTask;
import org.sonar.server.startup.GeneratePluginIndex;
import org.sonar.server.startup.RegisterDashboards;
import org.sonar.server.startup.RegisterMetrics;
add(GeneratePluginIndex.class,
RegisterServletFilters.class,
ServerLifecycleNotifier.class);
-
+
addIfStartupLeader(
CheckDatabaseCollationDuringMigration.class,
IndexerStartupTask.class,
RenameIssueWidgets.class,
DisplayLogOnDeprecatedProjects.class,
ClearRulesOverloadedDebt.class,
- DeleteOldAnalysisReportsFromFs.class,
- FeedUsersLocalStartupTask.class);
-
+ DeleteOldAnalysisReportsFromFs.class);
}
@Override
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.picocontainer.Startable;
-import org.sonar.api.config.Settings;
-import org.sonar.api.security.SecurityRealm;
-import org.sonar.api.user.UserQuery;
-import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.loadedtemplate.LoadedTemplateDto;
-import org.sonar.db.user.UserDto;
-import org.sonar.server.user.UserUpdater;
-
-import static java.util.Arrays.asList;
-import static org.sonar.api.CoreProperties.CORE_AUTHENTICATOR_REALM;
-import static org.sonar.db.loadedtemplate.LoadedTemplateDto.ONE_SHOT_TASK_TYPE;
-
-/**
- * Feed users local property.
- * If a realm is defined, then users are set as local only if their login are found in the property "sonar.security.localUsers",
- * otherwise user are all set as local.
- * <p>
- * See <a href="https://jira.sonarsource.com/browse/SONAR-7254">SONAR-7254</a>.
- * <p>
- * Should be removed after LTS 5.X
- *
- * @since 5.5
- */
-public class FeedUsersLocalStartupTask implements Startable {
-
- private static final Logger LOG = Loggers.get(FeedUsersLocalStartupTask.class);
-
- private static final String TEMPLATE_KEY = "UpdateUsersLocal";
- private static final String LOCAL_USERS_PROPERTY = "sonar.security.localUsers";
-
- private final System2 system2;
-
- private final DbClient dbClient;
- private final Settings settings;
- private final SecurityRealm[] realms;
-
- /**
- * Used when no realm plugin
- */
- public FeedUsersLocalStartupTask(System2 system2, DbClient dbClient, Settings settings) {
- this.system2 = system2;
- this.dbClient = dbClient;
- this.settings = settings;
- this.realms = new SecurityRealm[]{};
- }
-
- public FeedUsersLocalStartupTask(System2 system2, DbClient dbClient, Settings settings, SecurityRealm[] realms) {
- this.system2 = system2;
- this.dbClient = dbClient;
- this.settings = settings;
- this.realms = realms;
- }
-
- @Override
- public void start() {
- DbSession dbSession = dbClient.openSession(false);
- try {
- if (hasAlreadyBeenExecuted(dbSession)) {
- return;
- }
- updateUsersLocal(dbSession);
- markAsExecuted(dbSession);
- dbSession.commit();
-
- if (settings.hasKey(LOCAL_USERS_PROPERTY)) {
- LOG.info("NOTE : The property '{}' is now no more needed, you can safely remove it.", LOCAL_USERS_PROPERTY);
- }
- } finally {
- dbClient.closeSession(dbSession);
- }
- }
-
- private void updateUsersLocal(DbSession dbSession) {
- boolean realmConfExists = settings.getString(CORE_AUTHENTICATOR_REALM) != null;
- checkConfigurationIfRealmExists(realmConfExists);
- long now = system2.now();
- Set<String> localUsers = new HashSet<>(asList(settings.getStringArray(LOCAL_USERS_PROPERTY)));
- for (UserDto user : dbClient.userDao().selectUsers(dbSession, UserQuery.ALL_ACTIVES)) {
- if (user.getExternalIdentityProvider().equals(UserUpdater.SQ_AUTHORITY)) {
- user.setLocal(!realmConfExists || localUsers.contains(user.getLogin()));
- } else {
- user.setLocal(false);
- }
- user.setUpdatedAt(now);
- dbClient.userDao().update(dbSession, user);
- }
- }
-
- private void checkConfigurationIfRealmExists(boolean realmConfExists) {
- List<String> realmNames = Arrays.stream(realms).map(SecurityRealm::getName).collect(Collectors.toList());
- if (!realmNames.isEmpty() && !realmConfExists) {
- throw MessageException.of(String.format("External authentication plugin %s has been found, but no related configuration has been set. " +
- "Either update your configuration or remove the plugin", realmNames));
- }
- }
-
- private boolean hasAlreadyBeenExecuted(DbSession dbSession) {
- return dbClient.loadedTemplateDao().countByTypeAndKey(ONE_SHOT_TASK_TYPE, TEMPLATE_KEY, dbSession) > 0;
- }
-
- private void markAsExecuted(DbSession dbSession) {
- dbClient.loadedTemplateDao().insert(new LoadedTemplateDto(TEMPLATE_KEY, ONE_SHOT_TASK_TYPE), dbSession);
- }
-
- @Override
- public void stop() {
- // Nothing to do
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.startup;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.Settings;
-import org.sonar.api.security.SecurityRealm;
-import org.sonar.api.utils.MessageException;
-import org.sonar.api.utils.System2;
-import org.sonar.api.utils.log.LogTester;
-import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-import org.sonar.db.user.UserDao;
-import org.sonar.db.user.UserDto;
-import org.sonar.db.user.UserTesting;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.sonar.db.loadedtemplate.LoadedTemplateDto.ONE_SHOT_TASK_TYPE;
-
-public class FeedUsersLocalStartupTaskTest {
-
- static final long NOW = 20000000L;
- static final long PAST = 10000000L;
-
- static final String USER1_LOGIN = "USER1";
- static final String USER2_LOGIN = "USER2";
-
- static final String REALM_NAME = "LDAP";
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- System2 system2 = mock(System2.class);
-
- @Rule
- public LogTester logTester = new LogTester();
-
- @Rule
- public DbTester dbTester = DbTester.create(system2);
-
- DbClient dbClient = dbTester.getDbClient();
-
- DbSession dbSession = dbTester.getSession();
-
- UserDao userDao = dbClient.userDao();
-
- Settings settings = new Settings();
-
- FeedUsersLocalStartupTask underTest;
-
- @Before
- public void setUp() throws Exception {
- when(system2.now()).thenReturn(NOW);
- }
-
- @Test
- public void set_user_local_when_id_provider_is_sonarqube_and_realm_exists_and_local_users_property_contains_user_login() throws Exception {
- initTaskWithRealm();
- settings.setProperty("sonar.security.realm", REALM_NAME);
- settings.setProperty("sonar.security.localUsers", USER1_LOGIN);
- UserDto user = addUser(USER1_LOGIN, false, "sonarqube");
-
- underTest.start();
-
- verifyUserIsUpdated(user.getId(), true);
- verifyLogAboutRemovalOfLocalUsersProperty();
- }
-
- @Test
- public void set_user_as_not_local_when_id_provider_is_sonarqube_and_ream_exists_and_no_local_users_property() throws Exception {
- initTaskWithRealm();
- settings.setProperty("sonar.security.realm", REALM_NAME);
- UserDto user = addUser(USER1_LOGIN, false, "sonarqube");
-
- underTest.start();
-
- verifyUserIsUpdated(user.getId(), false);
- verifyEmptyLog();
- }
-
- @Test
- public void set_user_as_not_local_when_id_provider_is_sonarqube_and_ream_exists_and_local_users_property_does_not_contain_user_login() throws Exception {
- initTaskWithRealm();
- settings.setProperty("sonar.security.realm", REALM_NAME);
- settings.setProperty("sonar.security.localUsers", USER2_LOGIN);
- UserDto user = addUser(USER1_LOGIN, true, "sonarqube");
-
- underTest.start();
-
- verifyUserIsUpdated(user.getId(), false);
- verifyLogAboutRemovalOfLocalUsersProperty();
- }
-
- @Test
- public void set_user_as_local_when_id_provider_is_sonarqube_and_no_realm() throws Exception {
- initTaskWithNoRealm();
- settings.setProperty("sonar.security.realm", (String) null);
- UserDto user = addUser(USER1_LOGIN, false, "sonarqube");
-
- underTest.start();
-
- verifyUserIsUpdated(user.getId(), true);
- verifyEmptyLog();
- }
-
- @Test
- public void set_user_as_not_local_when_external_identity_is_not_sonarqube() throws Exception {
- initTaskWithNoRealm();
- UserDto user = addUser(USER1_LOGIN, false, "github");
-
- underTest.start();
-
- verifyUserIsUpdated(user.getId(), false);
- verifyEmptyLog();
- }
-
- @Test
- public void does_not_update_removed_user() throws Exception {
- initTaskWithRealm();
- settings.setProperty("sonar.security.realm", REALM_NAME);
-
- UserDto user = UserTesting.newUserDto()
- .setLogin(USER1_LOGIN)
- .setActive(false)
- .setLocal(false)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST);
- userDao.insert(dbSession, user);
- dbSession.commit();
-
- underTest.start();
-
- UserDto userReloaded = userDao.selectUserById(dbSession, user.getId());
- assertThat(userReloaded.isLocal()).isFalse();
- assertThat(userReloaded.getUpdatedAt()).isEqualTo(PAST);
- verifyTaskIsRegistered();
- }
-
- @Test
- public void does_nothing_when_task_has_already_been_executed() throws Exception {
- initTaskWithRealm();
- settings.setProperty("sonar.security.realm", REALM_NAME);
- settings.setProperty("sonar.security.localUsers", USER1_LOGIN);
- UserDto user = addUser(USER1_LOGIN, false, "github");
-
- underTest.start();
- verifyLogAboutRemovalOfLocalUsersProperty();
-
- logTester.clear();
- UserDto userReloaded = userDao.selectUserById(dbSession, user.getId());
- assertThat(userReloaded.getUpdatedAt()).isEqualTo(NOW);
- verifyTaskIsRegistered();
-
- when(system2.now()).thenReturn(NOW + 1000L);
-
- underTest.start();
- userReloaded = userDao.selectUserById(dbSession, user.getId());
- assertThat(userReloaded.getUpdatedAt()).isEqualTo(NOW);
- verifyEmptyLog();
- }
-
- @Test
- public void fail_when_realm_found_but_no_configuration() throws Exception {
- initTask(createRealm("REALM1"), createRealm("REALM2"));
-
- expectedException.expect(MessageException.class);
- expectedException.expectMessage("External authentication plugin [REALM1, REALM2] has been found, but no related configuration has been set. " +
- "Either update your configuration or remove the plugin");
- underTest.start();
- }
-
- private UserDto addUser(String login, boolean local, String externalIdentityProvider) {
- UserDto user = UserTesting.newUserDto()
- .setLogin(login)
- .setActive(true)
- .setLocal(local)
- .setExternalIdentityProvider(externalIdentityProvider)
- .setExternalIdentity(login)
- .setCreatedAt(PAST)
- .setUpdatedAt(PAST);
- userDao.insert(dbSession, user);
- dbSession.commit();
- return user;
- }
-
- private void verifyUserIsUpdated(long userId, boolean expectedLocal) {
- UserDto userReloaded = userDao.selectUserById(dbSession, userId);
- assertThat(userReloaded.isLocal()).isEqualTo(expectedLocal);
- assertThat(userReloaded.getUpdatedAt()).isEqualTo(NOW);
- verifyTaskIsRegistered();
- }
-
- private void verifyTaskIsRegistered() {
- assertThat(dbClient.loadedTemplateDao().countByTypeAndKey(ONE_SHOT_TASK_TYPE, "UpdateUsersLocal")).isEqualTo(1);
- }
-
- private void verifyLogAboutRemovalOfLocalUsersProperty() {
- assertThat(logTester.logs(LoggerLevel.INFO)).containsOnly(
- "NOTE : The property 'sonar.security.localUsers' is now no more needed, you can safely remove it.");
- }
-
- private void verifyEmptyLog() {
- assertThat(logTester.logs(LoggerLevel.INFO)).isEmpty();
- }
-
- private void initTask(SecurityRealm... realms) {
- if (realms.length == 0) {
- underTest = new FeedUsersLocalStartupTask(system2, dbTester.getDbClient(), settings);
- } else {
- underTest = new FeedUsersLocalStartupTask(system2, dbTester.getDbClient(), settings, realms);
- }
- }
-
- private void initTaskWithRealm() {
- initTask(createRealm(REALM_NAME));
- }
-
- private void initTaskWithNoRealm() {
- initTask();
- }
-
- private static SecurityRealm createRealm(String name) {
- SecurityRealm realm = mock(SecurityRealm.class);
- when(realm.getName()).thenReturn(name);
- return realm;
- }
-}
.type(PropertyType.BOOLEAN)
.defaultValue(String.valueOf(false))
.build(),
- PropertyDefinition.builder(CoreProperties.CORE_AUTHENTICATOR_LOCAL_USERS)
- .name("Local/technical users")
- .description("Comma separated list of user logins that will always be authenticated using SonarQube database. "
- + "When using the LDAP plugin, for these accounts, the user attributes (name, email, ...) are not re-synchronized")
- .type(PropertyType.STRING)
- .multiValues(true)
- .defaultValue("admin")
- .build(),
PropertyDefinition.builder(CoreProperties.SCM_DISABLED_KEY)
.name("Disable the SCM Sensor")
.description("Disable the retrieval of blame information from Source Control Manager")