Browse Source

SONAR-7254 Create task to feed user.local

tags/5.5-M11
Julien Lancelot 8 years ago
parent
commit
fc48f0fb03

+ 3
- 1
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java View File

@@ -28,6 +28,7 @@ import org.sonar.server.rule.RegisterRules;
import org.sonar.server.search.IndexSynchronizer;
import org.sonar.server.startup.ClearRulesOverloadedDebt;
import org.sonar.server.startup.DisplayLogOnDeprecatedProjects;
import org.sonar.server.startup.FeedUsersLocalStartupTask;
import org.sonar.server.startup.GeneratePluginIndex;
import org.sonar.server.startup.LogServerId;
import org.sonar.server.startup.RegisterDashboards;
@@ -65,7 +66,8 @@ public class PlatformLevelStartup extends PlatformLevel {
ServerLifecycleNotifier.class,
PurgeCeActivities.class,
DisplayLogOnDeprecatedProjects.class,
ClearRulesOverloadedDebt.class
ClearRulesOverloadedDebt.class,
FeedUsersLocalStartupTask.class
);
}


+ 115
- 0
server/sonar-server/src/main/java/org/sonar/server/startup/FeedUsersLocalStartupTask.java View File

@@ -0,0 +1,115 @@
/*
* 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.HashSet;
import java.util.Set;
import org.picocontainer.Startable;
import org.sonar.api.config.Settings;
import org.sonar.api.user.UserQuery;
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.
*
* See <a href="https://jira.sonarsource.com/browse/SONAR-7254">SONAR-7254</a>.
*
* 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;

public FeedUsersLocalStartupTask(System2 system2, DbClient dbClient, Settings settings) {
this.system2 = system2;
this.dbClient = dbClient;
this.settings = settings;
}

@Override
public void start() {
DbSession session = dbClient.openSession(false);
try {
if (hasAlreadyBeenExecuted(session)) {
return;
}
updateUsersLocal(session);
markAsExecuted(session);
session.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(session);
}
}

private void updateUsersLocal(DbSession session) {
long now = system2.now();
Set<String> localUsers = new HashSet<>(asList(settings.getStringArray(LOCAL_USERS_PROPERTY)));
boolean isRealmExist = settings.getString(CORE_AUTHENTICATOR_REALM) != null;
for (UserDto user : dbClient.userDao().selectUsers(session, UserQuery.ALL_ACTIVES)) {
if (user.getExternalIdentityProvider().equals(UserUpdater.SQ_AUTHORITY)) {
user.setLocal(!isRealmExist || localUsers.contains(user.getLogin()));
} else {
user.setLocal(false);
}
user.setUpdatedAt(now);
dbClient.userDao().update(session, user);
}
}

private boolean hasAlreadyBeenExecuted(DbSession session) {
return dbClient.loadedTemplateDao().countByTypeAndKey(ONE_SHOT_TASK_TYPE, TEMPLATE_KEY, session) > 0;
}

private void markAsExecuted(DbSession session) {
dbClient.loadedTemplateDao().insert(new LoadedTemplateDto(TEMPLATE_KEY, ONE_SHOT_TASK_TYPE), session);
}

@Override
public void stop() {
// Nothing to do
}
}

+ 1
- 1
server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java View File

@@ -55,7 +55,7 @@ import static org.sonar.api.CoreProperties.CORE_AUTHENTICATOR_LOCAL_USERS;
@ServerSide
public class UserUpdater {

private static final String SQ_AUTHORITY = "sonarqube";
public static final String SQ_AUTHORITY = "sonarqube";

private static final String LOGIN_PARAM = "Login";
private static final String PASSWORD_PARAM = "Password";

+ 205
- 0
server/sonar-server/src/test/java/org/sonar/server/startup/FeedUsersLocalStartupTaskTest.java View File

@@ -0,0 +1,205 @@
/*
* 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.sonar.api.config.Settings;
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";

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 = new FeedUsersLocalStartupTask(system2, dbTester.getDbClient(), settings);

@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 {
settings.setProperty("sonar.security.realm", "LDAP");
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 {
settings.setProperty("sonar.security.realm", "LDAP");
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 {
settings.setProperty("sonar.security.realm", "LDAP");
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 {
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_identiy_is_not_sonarqube() throws Exception {
UserDto user = addUser(USER1_LOGIN, false, "github");

underTest.start();

verifyUserIsUpdated(user.getId(), false);
verifyEmptyLog();
}

@Test
public void does_not_update_removed_user() throws Exception {
settings.setProperty("sonar.security.realm", "LDAP");

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 {
settings.setProperty("sonar.security.realm", "LDAP");
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();
}

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();
}

}

+ 2
- 0
sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java View File

@@ -468,7 +468,9 @@ public interface CoreProperties {

/**
* @since 4.2
* @deprecated no more used since 5.5
*/
@Deprecated
String CORE_AUTHENTICATOR_LOCAL_USERS = "sonar.security.localUsers";

/**

Loading…
Cancel
Save