From 7534b360dac6e9e139bd148613fe6a9930dfffa4 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 9 Oct 2014 16:35:05 +0200 Subject: [PATCH] SONAR-4950 Add a transaction to prevent multiple connection to add multiple lines in group_roles --- .../602_remove_duplication_in_group_roles.rb | 44 ++++++++++++ .../webapp/WEB-INF/lib/need_authentication.rb | 70 +++++++++++-------- .../core/persistence/DatabaseVersion.java | 2 +- .../org/sonar/core/persistence/rows-h2.sql | 1 + 4 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb diff --git a/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb new file mode 100644 index 00000000000..168fa378ebb --- /dev/null +++ b/server/sonar-web/src/main/webapp/WEB-INF/db/migrate/602_remove_duplication_in_group_roles.rb @@ -0,0 +1,44 @@ +# +# 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 4.5 +# SONAR-4950 Unique constraint cannot be created because it would be on resource_id that is nullable +# +class RemoveDuplicationInGroupRoles < ActiveRecord::Migration + + class GroupRole < ActiveRecord::Base + end + + def self.up + GroupRole.reset_column_information + + duplicated_ids = ActiveRecord::Base.connection.select_rows('select group_id,resource_id,role from group_roles group by group_id,resource_id,role having count(*) > 1') + say_with_time "Remove #{duplicated_ids.size} duplicated group roles" do + duplicated_ids.each do |fields| + rows = GroupRole.find(:all, :conditions => {:group_id => fields[0], :resource_id => fields[1], :role => fields[2]}) + # delete all rows except the last one + rows[0...-1].each do |row| + GroupRole.delete(row.id) + end + end + end + end +end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb b/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb index 400ccbad2f1..61b266fc66d 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/lib/need_authentication.rb @@ -64,6 +64,10 @@ class PluginRealm result = nil if !username.blank? && !password.blank? user = User.find_active_by_login(username) + # SONAR-4950 Use a transaction to prevent multiple insertion of same groups + User.transaction do + user.save(false) + end result = user if user && user.authenticated?(password) end result @@ -117,40 +121,46 @@ class PluginRealm def synchronize(username, password, details) username=details.getName() if username.blank? && details user = User.find_by_login(username) - if !user - # No such user in Sonar database - return nil if !Api::Utils.java_facade.getSettings().getBoolean('sonar.authenticator.createUsers') - # Automatically create a user in the sonar db if authentication has been successfully done - user = User.new(:login => username, :name => username, :email => '') - if details - user.name = details.getName() - user.email = details.getEmail() - end - default_group_name = Api::Utils.java_facade.getSettings().getString('sonar.defaultGroup') - default_group = Group.find_by_name(default_group_name) - if default_group - user.groups << default_group + + # SONAR-4950 Use a transaction to prevent multiple insertion of same groups + User.transaction do + if !user + # No such user in Sonar database + return nil if !Api::Utils.java_facade.getSettings().getBoolean('sonar.authenticator.createUsers') + # Automatically create a user in the sonar db if authentication has been successfully done + user = User.new(:login => username, :name => username, :email => '') + if details + user.name = details.getName() + user.email = details.getEmail() + end + default_group_name = Api::Utils.java_facade.getSettings().getString('sonar.defaultGroup') + default_group = Group.find_by_name(default_group_name) + if default_group + user.groups << default_group + else + Rails.logger.error("The default user group does not exist: #{default_group_name}. Please check the parameter 'Default user group' in general settings.") + end else - Rails.logger.error("The default user group does not exist: #{default_group_name}. Please check the parameter 'Default user group' in general settings.") + # Existing user + if details && Api::Utils.java_facade.getSettings().getBoolean('sonar.security.updateUserAttributes') + user.name = details.getName() + user.email = details.getEmail() + end end - else - # Existing user - if details && Api::Utils.java_facade.getSettings().getBoolean('sonar.security.updateUserAttributes') - user.name = details.getName() - user.email = details.getEmail() + if @save_password + user.password = password + user.password_confirmation = password end + + # A user that is synchronized with an external system is always set to 'active' (see SONAR-3258 for the deactivation concept) + user.active=true + # Note that validation disabled + user.save(false) + + synchronize_groups(user) + user.notify_creation_handlers + user end - if @save_password - user.password = password - user.password_confirmation = password - end - synchronize_groups(user) - # A user that is synchronized with an external system is always set to 'active' (see SONAR-3258 for the deactivation concept) - user.active=true - # Note that validation disabled - user.save(false) - user.notify_creation_handlers - user end def synchronize_groups(user) diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java index af91a6ae928..9045874e393 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DatabaseVersion.java @@ -33,7 +33,7 @@ import java.util.List; */ public class DatabaseVersion implements BatchComponent, ServerComponent { - public static final int LAST_VERSION = 601; + public static final int LAST_VERSION = 602; public static enum Status { UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 53b8dcc9d12..7987b78321e 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -256,6 +256,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('583'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('584'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('600'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('601'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('602'); INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null); ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2; -- 2.39.5