From 3a9d42f4075e23448349b2f96c332fe6eae7a55b Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Tue, 19 Jan 2016 11:48:05 +0100 Subject: [PATCH] SONAR-6226 Add ext identity columns in USERS table --- .../java/org/sonar/server/user/NewUser.java | 35 ++++++- .../org/sonar/server/user/UserUpdater.java | 51 +++++++---- .../org/sonar/server/user/NewUserTest.java | 90 ++++++++++++++++++ .../sonar/server/user/UserUpdaterTest.java | 29 ++++++ .../server/user/ws/CreateActionTest.java | 12 +-- ..._default_groups_when_reactivating_user.xml | 4 +- ...iate_default_groups_when_updating_user.xml | 4 +- ..._user_when_scm_account_is_already_used.xml | 4 +- ...m_account_is_already_used_by_many_user.xml | 9 +- ...ail_to_reactivate_user_if_not_disabled.xml | 4 +- ..._user_when_scm_account_is_already_used.xml | 8 +- ...when_updating_user_if_already_existing.xml | 4 +- .../user/UserUpdaterTest/reactivate_user.xml | 4 +- .../UserUpdaterTest/update_technical_user.xml | 1 + .../user/UserUpdaterTest/update_user.xml | 4 +- .../1011_add_users_identity_columns.rb | 31 +++++++ .../db/migrate/1012_migrate_users_identity.rb | 31 +++++++ ..._unique_index_on_users_identity_columns.rb | 31 +++++++ .../main/java/org/sonar/db/user/UserDao.java | 15 +++ .../main/java/org/sonar/db/user/UserDto.java | 20 ++++ .../java/org/sonar/db/user/UserMapper.java | 3 + .../org/sonar/db/version/DatabaseVersion.java | 2 +- .../sonar/db/version/MigrationStepModule.java | 7 +- .../version/v54/AddUsersIdentityColumns.java | 55 +++++++++++ .../db/version/v54/MigrateUsersIdentity.java | 67 ++++++++++++++ .../org/sonar/db/user/UserMapper.xml | 15 ++- .../org/sonar/db/version/rows-h2.sql | 3 + .../org/sonar/db/version/schema-h2.ddl | 4 + .../java/org/sonar/db/user/UserDaoTest.java | 28 ++++++ .../java/org/sonar/db/user/UserTesting.java | 18 ++-- .../db/version/MigrationStepModuleTest.java | 2 +- .../v54/AddUsersIdentityColumnsTest.java | 51 +++++++++++ .../version/v54/MigrateUsersIdentityTest.java | 91 +++++++++++++++++++ .../select_users_by_ext_identity.xml | 12 +++ .../AddUsersIdentityColumnsTest/schema.sql | 14 +++ .../migrate-result.xml | 13 +++ .../v54/MigrateUsersIdentityTest/migrate.xml | 13 +++ .../v54/MigrateUsersIdentityTest/schema.sql | 16 ++++ 38 files changed, 753 insertions(+), 52 deletions(-) create mode 100644 server/sonar-server/src/test/java/org/sonar/server/user/NewUserTest.java create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1011_add_users_identity_columns.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1012_migrate_users_identity.rb create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1013_add_unique_index_on_users_identity_columns.rb create mode 100644 sonar-db/src/main/java/org/sonar/db/version/v54/AddUsersIdentityColumns.java create mode 100644 sonar-db/src/main/java/org/sonar/db/version/v54/MigrateUsersIdentity.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/v54/AddUsersIdentityColumnsTest.java create mode 100644 sonar-db/src/test/java/org/sonar/db/version/v54/MigrateUsersIdentityTest.java create mode 100644 sonar-db/src/test/resources/org/sonar/db/user/UserDaoTest/select_users_by_ext_identity.xml create mode 100644 sonar-db/src/test/resources/org/sonar/db/version/v54/AddUsersIdentityColumnsTest/schema.sql create mode 100644 sonar-db/src/test/resources/org/sonar/db/version/v54/MigrateUsersIdentityTest/migrate-result.xml create mode 100644 sonar-db/src/test/resources/org/sonar/db/version/v54/MigrateUsersIdentityTest/migrate.xml create mode 100644 sonar-db/src/test/resources/org/sonar/db/version/v54/MigrateUsersIdentityTest/schema.sql diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/NewUser.java b/server/sonar-server/src/main/java/org/sonar/server/user/NewUser.java index ea509021993..01d49b93f9d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/NewUser.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/NewUser.java @@ -23,15 +23,16 @@ import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import static java.util.Objects.requireNonNull; + public class NewUser { private String login; private String name; private String email; private List scmAccounts; - private String password; - private String passwordConfirmation; + private ExternalIdentity externalIdentity; private NewUser() { // No direct call to this constructor @@ -82,12 +83,40 @@ public class NewUser { return password; } - public NewUser setPassword(String password) { + public NewUser setPassword(@Nullable String password) { this.password = password; return this; } + @Nullable + public ExternalIdentity externalIdentity() { + return externalIdentity; + } + + public NewUser setExternalIdentity(@Nullable ExternalIdentity externalIdentity) { + this.externalIdentity = externalIdentity; + return this; + } + public static NewUser create() { return new NewUser(); } + + public static class ExternalIdentity { + private String provider; + private String id; + + public ExternalIdentity(String provider, String id) { + this.provider = requireNonNull(provider, "Identity provider cannot be null"); + this.id = requireNonNull(id, "Identity id cannot be null"); + } + + public String getProvider() { + return provider; + } + + public String getId() { + return id; + } + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java index b61fe46b76e..2003643daee 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/user/UserUpdater.java @@ -20,6 +20,7 @@ package org.sonar.server.user; import com.google.common.base.Joiner; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Iterables; @@ -55,6 +56,8 @@ import static org.sonar.api.CoreProperties.CORE_AUTHENTICATOR_LOCAL_USERS; @ServerSide public class UserUpdater { + private static final String SQ_AUTHORITY = "sonarqube"; + private static final String LOGIN_PARAM = "Login"; private static final String PASSWORD_PARAM = "Password"; private static final String NAME_PARAM = "Name"; @@ -95,27 +98,16 @@ public class UserUpdater { } } - public boolean create(DbSession dbSession, NewUser newUser){ + public boolean create(DbSession dbSession, NewUser newUser) { boolean isUserReactivated = false; UserDto userDto = createNewUserDto(dbSession, newUser); String login = userDto.getLogin(); - UserDto existingUser = dbClient.userDao().selectByLogin(dbSession, login); - if (existingUser == null) { + Optional existingUser = dbClient.userDao().selectByExternalIdentity(dbSession, userDto.getExternalIdentity(), userDto.getExternalIdentityProvider()); + if (!existingUser.isPresent()) { saveUser(dbSession, userDto); addDefaultGroup(dbSession, userDto); } else { - if (existingUser.isActive()) { - throw new IllegalArgumentException(String.format("An active user with login '%s' already exists", login)); - } - UpdateUser updateUser = UpdateUser.create(login) - .setName(newUser.name()) - .setEmail(newUser.email()) - .setScmAccounts(newUser.scmAccounts()) - .setPassword(newUser.password()); - updateUserDto(dbSession, updateUser, existingUser); - updateUser(dbSession, existingUser); - addDefaultGroup(dbSession, existingUser); - isUserReactivated = true; + isUserReactivated = updateExistingUser(dbSession, existingUser.get(), login, newUser); } dbSession.commit(); notifyNewUser(userDto.getLogin(), userDto.getName(), newUser.email()); @@ -123,6 +115,21 @@ public class UserUpdater { return isUserReactivated; } + private boolean updateExistingUser(DbSession dbSession, UserDto existingUser, String login, NewUser newUser) { + if (existingUser.isActive()) { + throw new IllegalArgumentException(String.format("An active user with login '%s' already exists", login)); + } + UpdateUser updateUser = UpdateUser.create(login) + .setName(newUser.name()) + .setEmail(newUser.email()) + .setScmAccounts(newUser.scmAccounts()) + .setPassword(newUser.password()); + updateUserDto(dbSession, updateUser, existingUser); + updateUser(dbSession, existingUser); + addDefaultGroup(dbSession, existingUser); + return true; + } + public void update(UpdateUser updateUser) { DbSession dbSession = dbClient.openSession(false); try { @@ -194,6 +201,15 @@ public class UserUpdater { userDto.setScmAccounts(scmAccounts); } + NewUser.ExternalIdentity externalIdentity = newUser.externalIdentity(); + if (externalIdentity == null) { + userDto.setExternalIdentity(login); + userDto.setExternalIdentityProvider(SQ_AUTHORITY); + } else { + userDto.setExternalIdentity(externalIdentity.getId()); + userDto.setExternalIdentityProvider(externalIdentity.getProvider()); + } + if (!messages.isEmpty()) { throw new BadRequestException(messages); } @@ -243,7 +259,7 @@ public class UserUpdater { } } - private static void validateLoginFormat(@Nullable String login, List messages) { + private static String validateLoginFormat(@Nullable String login, List messages) { checkNotEmptyParam(login, LOGIN_PARAM, messages); if (!Strings.isNullOrEmpty(login)) { if (login.length() < LOGIN_MIN_LENGTH) { @@ -254,6 +270,7 @@ public class UserUpdater { messages.add(Message.of("user.bad_login")); } } + return login; } private static void validateNameFormat(@Nullable String name, List messages) { @@ -280,7 +297,7 @@ public class UserUpdater { } private void validateScmAccounts(DbSession dbSession, List scmAccounts, @Nullable String login, @Nullable String email, @Nullable UserDto existingUser, - List messages) { + List messages) { for (String scmAccount : scmAccounts) { if (scmAccount.equals(login) || scmAccount.equals(email)) { messages.add(Message.of("user.login_or_email_used_as_scm_account")); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/NewUserTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/NewUserTest.java new file mode 100644 index 00000000000..4fa85b502b3 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/user/NewUserTest.java @@ -0,0 +1,90 @@ +/* + * 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.user; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +public class NewUserTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void create_new_user() throws Exception { + NewUser newUser = NewUser.create() + .setLogin("login") + .setName("name") + .setEmail("email") + .setPassword("password") + .setScmAccounts(asList("login1", "login2")); + + assertThat(newUser.login()).isEqualTo("login"); + assertThat(newUser.name()).isEqualTo("name"); + assertThat(newUser.email()).isEqualTo("email"); + assertThat(newUser.password()).isEqualTo("password"); + assertThat(newUser.scmAccounts()).contains("login1", "login2"); + assertThat(newUser.externalIdentity()).isNull(); + } + + @Test + public void create_new_user_with_minimal_fields() throws Exception { + NewUser newUser = NewUser.create(); + + assertThat(newUser.login()).isNull(); + assertThat(newUser.name()).isNull(); + assertThat(newUser.email()).isNull(); + assertThat(newUser.password()).isNull(); + assertThat(newUser.scmAccounts()).isNull(); + } + + @Test + public void create_new_user_with_authority() throws Exception { + NewUser newUser = NewUser.create() + .setLogin("login") + .setName("name") + .setEmail("email") + .setPassword("password") + .setExternalIdentity(new NewUser.ExternalIdentity("github", "github_login")); + + assertThat(newUser.externalIdentity().getProvider()).isEqualTo("github"); + assertThat(newUser.externalIdentity().getId()).isEqualTo("github_login"); + } + + @Test + public void fail_with_NPE_when_identity_provider_is_null() throws Exception { + thrown.expect(NullPointerException.class); + thrown.expectMessage("Identity provider cannot be null"); + + new NewUser.ExternalIdentity(null, "github_login"); + } + + @Test + public void fail_with_NPE_when_identity_id_is_null() throws Exception { + thrown.expect(NullPointerException.class); + thrown.expectMessage("Identity id cannot be null"); + + new NewUser.ExternalIdentity("github", null); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java index c7da098ca41..b3f2efc35c0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/UserUpdaterTest.java @@ -140,6 +140,35 @@ public class UserUpdaterTest { entry("email", "user@mail.com")); } + @Test + public void create_user_with_sq_authority_when_no_authority_set() throws Exception { + createDefaultGroup(); + + userUpdater.create(NewUser.create() + .setLogin("user") + .setName("User") + .setPassword("password")); + + UserDto dto = userDao.selectByLogin(session, "user"); + assertThat(dto.getExternalIdentity()).isEqualTo("user"); + assertThat(dto.getExternalIdentityProvider()).isEqualTo("sonarqube"); + } + + @Test + public void create_user_with_authority() { + createDefaultGroup(); + + userUpdater.create(NewUser.create() + .setLogin("ABCD") + .setName("User") + .setPassword("password") + .setExternalIdentity(new NewUser.ExternalIdentity("github", "user"))); + + UserDto dto = userDao.selectByLogin(session, "ABCD"); + assertThat(dto.getExternalIdentity()).isEqualTo("user"); + assertThat(dto.getExternalIdentityProvider()).isEqualTo("github"); + } + @Test public void create_user_with_minimum_fields() { when(system2.now()).thenReturn(1418215735482L); diff --git a/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java index 1634b6dbfd9..6d808226f15 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/user/ws/CreateActionTest.java @@ -33,9 +33,11 @@ import org.sonar.api.utils.System2; import org.sonar.core.permission.GlobalPermissions; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.user.GroupDao; import org.sonar.db.user.GroupDto; -import org.sonar.db.user.UserDto; +import org.sonar.db.user.UserDao; import org.sonar.db.user.UserGroupDao; +import org.sonar.db.user.UserTesting; import org.sonar.server.db.DbClient; import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.ForbiddenException; @@ -43,8 +45,6 @@ import org.sonar.server.tester.UserSessionRule; import org.sonar.server.user.NewUserNotifier; import org.sonar.server.user.SecurityRealmFactory; import org.sonar.server.user.UserUpdater; -import org.sonar.db.user.GroupDao; -import org.sonar.db.user.UserDao; import org.sonar.server.user.index.UserDoc; import org.sonar.server.user.index.UserIndex; import org.sonar.server.user.index.UserIndexDefinition; @@ -154,11 +154,7 @@ public class CreateActionTest { public void reactivate_user() throws Exception { userSessionRule.login("admin").setLocale(Locale.FRENCH).setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); - dbClient.userDao().insert(session, new UserDto() - .setEmail("john@email.com") - .setLogin("john") - .setName("John") - .setActive(true)); + dbClient.userDao().insert(session, UserTesting.newUserDto("john", "John", "john@email.com")); session.commit(); dbClient.userDao().deactivateUserByLogin(session, "john"); userIndexer.index(); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml index d0acdb2a121..40485114bc3 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_reactivating_user.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml index 926b75324ad..146d6a2893c 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/associate_default_groups_when_updating_user.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml index 33dac1a0693..c7ed6aa3f75 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml index 1f32fad9c4d..e44742a1dfb 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_create_user_when_scm_account_is_already_used_by_many_user.xml @@ -1,10 +1,13 @@ - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_reactivate_user_if_not_disabled.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_reactivate_user_if_not_disabled.xml index 926b75324ad..146d6a2893c 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_reactivate_user_if_not_disabled.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_reactivate_user_if_not_disabled.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml index 6c8d4d3a854..9704668e7ab 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/fail_to_update_user_when_scm_account_is_already_used.xml @@ -1,8 +1,12 @@ - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml index 8daf3302314..7b3b2790fee 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/not_associate_default_group_when_updating_user_if_already_existing.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml index eef35eda879..372d5dbc4e9 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/reactivate_user.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_technical_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_technical_user.xml index 6b824d7cf25..520f8830560 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_technical_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_technical_user.xml @@ -1,6 +1,7 @@ diff --git a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml index 926b75324ad..146d6a2893c 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/user/UserUpdaterTest/update_user.xml @@ -1,6 +1,8 @@ - diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1011_add_users_identity_columns.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1011_add_users_identity_columns.rb new file mode 100644 index 00000000000..fb0452813c3 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1011_add_users_identity_columns.rb @@ -0,0 +1,31 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube 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. +# +# SonarQube 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. +# + +# +# SonarQube 5.4 +# SONAR-6226 +# +class AddUsersIdentityColumns < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.db.version.v54.AddUsersIdentityColumns') + end + +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1012_migrate_users_identity.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1012_migrate_users_identity.rb new file mode 100644 index 00000000000..9ac30e8d9d6 --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1012_migrate_users_identity.rb @@ -0,0 +1,31 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube 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. +# +# SonarQube 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. +# + +# +# SonarQube 5.4 +# SONAR-6226 +# +class MigrateUsersIdentity < ActiveRecord::Migration + + def self.up + execute_java_migration('org.sonar.db.version.v54.MigrateUsersIdentity') + end + +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1013_add_unique_index_on_users_identity_columns.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1013_add_unique_index_on_users_identity_columns.rb new file mode 100644 index 00000000000..51fa402984a --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/1013_add_unique_index_on_users_identity_columns.rb @@ -0,0 +1,31 @@ +# +# SonarQube, open source software quality management tool. +# Copyright (C) 2008-2014 SonarSource +# mailto:contact AT sonarsource DOT com +# +# SonarQube 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. +# +# SonarQube 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. +# + +# +# SonarQube 5.4 +# SONAR-6226 +# +class AddUniqueIndexOnUsersIdentityColumns < ActiveRecord::Migration + + def self.up + add_index :users, [:external_identity, :external_identity_provider], :name => 'uniq_users_identity' + end + +end diff --git a/sonar-db/src/main/java/org/sonar/db/user/UserDao.java b/sonar-db/src/main/java/org/sonar/db/user/UserDao.java index 8172f465b3d..8799754185a 100644 --- a/sonar-db/src/main/java/org/sonar/db/user/UserDao.java +++ b/sonar-db/src/main/java/org/sonar/db/user/UserDao.java @@ -20,6 +20,7 @@ package org.sonar.db.user; import com.google.common.base.Function; +import com.google.common.base.Optional; import java.util.Collection; import java.util.List; import javax.annotation.CheckForNull; @@ -33,6 +34,8 @@ import org.sonar.db.DbSession; import org.sonar.db.MyBatis; import org.sonar.db.RowNotFoundException; +import static com.google.common.base.Optional.fromNullable; + public class UserDao implements Dao { private final MyBatis mybatis; @@ -172,6 +175,18 @@ public class UserDao implements Dao { return mapper(session).selectNullableByScmAccountOrLoginOrEmail(scmAccountOrLoginOrEmail, like); } + public Optional selectByExternalIdentity(DbSession session, String extIdentity, String extIdentityProvider){ + return fromNullable(mapper(session).selectByIdentity(extIdentity, extIdentityProvider)); + } + + public UserDto selectOrFailByExternalIdentity(DbSession session, String extIdentity, String extIdentityProvider) { + Optional user = selectByExternalIdentity(session, extIdentity, extIdentityProvider); + if (user.isPresent()) { + return user.get(); + } + throw new RowNotFoundException(String.format("User with identity provider '%s' and id '%s' has not been found", extIdentityProvider, extIdentity)); + } + protected UserMapper mapper(DbSession session) { return session.getMapper(UserMapper.class); } diff --git a/sonar-db/src/main/java/org/sonar/db/user/UserDto.java b/sonar-db/src/main/java/org/sonar/db/user/UserDto.java index 8c2b067dd72..a6983bf1638 100644 --- a/sonar-db/src/main/java/org/sonar/db/user/UserDto.java +++ b/sonar-db/src/main/java/org/sonar/db/user/UserDto.java @@ -40,6 +40,8 @@ public class UserDto { private String email; private boolean active = true; private String scmAccounts; + private String externalIdentity; + private String externalIdentityProvider; private String cryptedPassword; private String salt; private Long createdAt; @@ -129,6 +131,24 @@ public class UserDto { } } + public String getExternalIdentity() { + return externalIdentity; + } + + public UserDto setExternalIdentity(String authorithy) { + this.externalIdentity = authorithy; + return this; + } + + public String getExternalIdentityProvider() { + return externalIdentityProvider; + } + + public UserDto setExternalIdentityProvider(String externalIdentityProvider) { + this.externalIdentityProvider = externalIdentityProvider; + return this; + } + public String getCryptedPassword() { return cryptedPassword; } diff --git a/sonar-db/src/main/java/org/sonar/db/user/UserMapper.java b/sonar-db/src/main/java/org/sonar/db/user/UserMapper.java index 7835c44bcec..605998588db 100644 --- a/sonar-db/src/main/java/org/sonar/db/user/UserMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/user/UserMapper.java @@ -51,6 +51,9 @@ public interface UserMapper { List selectByLogins(List logins); + @CheckForNull + UserDto selectByIdentity(@Param("extIdentity") String authorityId, @Param("extIdentityProvider") String authorityProvider); + @CheckForNull GroupDto selectGroupByName(String name); diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java index b5f895b29ab..43be9647d12 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java +++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java @@ -29,7 +29,7 @@ import org.sonar.db.MyBatis; public class DatabaseVersion { - public static final int LAST_VERSION = 1010; + public static final int LAST_VERSION = 1013; /** * The minimum supported version which can be upgraded. Lower diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index d4f25e25adc..4591717227e 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -61,7 +61,9 @@ import org.sonar.db.version.v52.RemoveRuleMeasuresOnIssues; import org.sonar.db.version.v52.RemoveSnapshotLibraries; import org.sonar.db.version.v53.FixMsSqlCollation; import org.sonar.db.version.v53.UpdateCustomDashboardInLoadedTemplates; +import org.sonar.db.version.v54.AddUsersIdentityColumns; import org.sonar.db.version.v54.InsertGateAdminPermissionForEachProfileAdmin; +import org.sonar.db.version.v54.MigrateUsersIdentity; import org.sonar.db.version.v54.RemoveComponentPageProperties; public class MigrationStepModule extends Module { @@ -121,6 +123,9 @@ public class MigrationStepModule extends Module { // 5.4 InsertGateAdminPermissionForEachProfileAdmin.class, - RemoveComponentPageProperties.class); + RemoveComponentPageProperties.class, + AddUsersIdentityColumns.class, + MigrateUsersIdentity.class + ); } } diff --git a/sonar-db/src/main/java/org/sonar/db/version/v54/AddUsersIdentityColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v54/AddUsersIdentityColumns.java new file mode 100644 index 00000000000..989ae5db81c --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v54/AddUsersIdentityColumns.java @@ -0,0 +1,55 @@ +/* + * 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.db.version.v54; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.AddColumnsBuilder; +import org.sonar.db.version.DdlChange; + +import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder; + +/** + * Add the following columns to the USERS table : + * - external_identity + * - external_identity_provider + */ +public class AddUsersIdentityColumns extends DdlChange { + + private final Database db; + + public AddUsersIdentityColumns(Database db) { + super(db); + this.db = db; + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + context.execute(generateSql()); + } + + private String generateSql() { + return new AddColumnsBuilder(db.getDialect(), "users") + .addColumn(newVarcharColumnDefBuilder().setColumnName("external_identity").setLimit(4000).setIsNullable(true).build()) + .addColumn(newVarcharColumnDefBuilder().setColumnName("external_identity_provider").setLimit(100).setIsNullable(true).build()) + .build(); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v54/MigrateUsersIdentity.java b/sonar-db/src/main/java/org/sonar/db/version/v54/MigrateUsersIdentity.java new file mode 100644 index 00000000000..476153feb5c --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v54/MigrateUsersIdentity.java @@ -0,0 +1,67 @@ +/* + * 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.db.version.v54; + +import java.sql.SQLException; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.Select; +import org.sonar.db.version.SqlStatement; + +/** + * Update all users to feed external_identity_provider with 'sonarqube' and external_identity with the login + */ +public class MigrateUsersIdentity extends BaseDataChange { + + private final System2 system2; + + public MigrateUsersIdentity(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("users"); + update.select("SELECT u.id, u.login FROM users u"); + update.update("UPDATE users SET external_identity_provider=?, external_identity=?, updated_at=? WHERE id=? " + + "AND external_identity_provider IS NULL AND external_identity IS NULL"); + update.execute(new MigrationHandler(system2.now())); + } + + private static class MigrationHandler implements MassUpdate.Handler { + private final long now; + + public MigrationHandler(long now) { + this.now = now; + } + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setString(1, "sonarqube"); + update.setString(2, row.getString(2)); + update.setLong(3, now); + update.setLong(4, row.getLong(1)); + return true; + } + } +} diff --git a/sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml b/sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml index 14e57dcd5df..a4d3b636448 100644 --- a/sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/user/UserMapper.xml @@ -12,6 +12,8 @@ u.scm_accounts as "scmAccounts", u.salt as "salt", u.crypted_password as "cryptedPassword", + u.external_identity as "externalIdentity", + u.external_identity_provider as "externalIdentityProvider", u.created_at as "createdAt", u.updated_at as "updatedAt" @@ -87,6 +89,15 @@ ORDER BY u.name + +