]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9019 Set organization members into members groups
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 31 Mar 2017 13:42:34 +0000 (15:42 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 13 Apr 2017 09:51:55 +0000 (11:51 +0200)
server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroup.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest/initial.sql [new file with mode: 0644]

index 0b5fbc287576db6a6bf615b382d832dda5883d9b..951d88be4b71391dfee779eb0eeedaf53ed04f7c 100644 (file)
@@ -556,6 +556,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1619');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1620');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1621');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1622');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1623');
 
 INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, IS_ROOT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', false, '1418215735482', '1418215735482');
 ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
index c4625c245ac2e8f8bd456c76b502c59f7521fb58..aa767167c2440a004f0740c9f841ee4ac44cdf9e 100644 (file)
@@ -50,6 +50,7 @@ public class DbVersion64 implements DbVersion {
       .add(1619, "Restore 'sonar-users' group", RestoreSonarUsersGroups.class)
       .add(1620, "Delete 'sonar.defaultGroup' setting", DeleteDefaultGroupSetting.class)
       .add(1621, "Set all users into 'sonar-users' group", SetAllUsersIntoSonarUsersGroup.class)
-      .add(1622, "Create 'Members' group in each organization", CreateMembersGroupsInEachOrganization.class);
+      .add(1622, "Create 'Members' group in each organization", CreateMembersGroupsInEachOrganization.class)
+      .add(1623, "Set organization members into 'Members' group", SetOrganizationMembersIntoMembersGroup.class);
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroup.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroup.java
new file mode 100644 (file)
index 0000000..4e2085b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.platform.db.migration.version.v64;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class SetOrganizationMembersIntoMembersGroup extends DataChange {
+
+  private static final String MEMBERS_NAME = "Members";
+
+  public SetOrganizationMembersIntoMembersGroup(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate().rowPluralName("users not belonging to 'members' group");
+    massUpdate.select("SELECT u.id, g.id FROM users u " +
+      "INNER JOIN organization_members om ON om.user_id=u.id " +
+      "INNER JOIN groups g ON g.organization_uuid=om.organization_uuid AND g.name=? " +
+      "WHERE u.active=? AND NOT EXISTS " +
+      "(SELECT 1 FROM groups_users gu " +
+      "INNER JOIN groups g on g.id=gu.group_id AND g.name=? " +
+      "WHERE gu.user_id=u.id)")
+      .setString(1, MEMBERS_NAME)
+      .setBoolean(2, true)
+      .setString(3, MEMBERS_NAME);
+    massUpdate.update("INSERT INTO groups_users (user_id, group_id) values (?, ?)");
+    massUpdate.execute((row, update) -> {
+      update.setLong(1, row.getLong(1));
+      update.setLong(2, row.getLong(2));
+      return true;
+    });
+  }
+
+}
index 8a813b71ba54a88fe3fadae04899f9f54d1ed3e4..9d3fe8c1d85b6c268450be83396ca7f019d1c28f 100644 (file)
@@ -35,7 +35,7 @@ public class DbVersion64Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 23);
+    verifyMigrationCount(underTest, 24);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest.java
new file mode 100644 (file)
index 0000000..4a8eba7
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info 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.platform.db.migration.version.v64;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SetOrganizationMembersIntoMembersGroupTest {
+
+  private static final String MEMBERS_NAME = "Members";
+  private static final String ORGANIZATION_1 = "ORGANIZATION_1";
+  private static final String ORGANIZATION_2 = "ORGANIZATION_2";
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(SetOrganizationMembersIntoMembersGroupTest.class, "initial.sql");
+
+  private SetOrganizationMembersIntoMembersGroup underTest = new SetOrganizationMembersIntoMembersGroup(db.database());
+
+  @Test
+  public void set_users_into_group_members() throws Exception {
+    long user1 = insertUser("user1", true);
+    long user2 = insertUser("user2", true);
+    long user3 = insertUser("user3", true);
+    long group1 = insertGroup(ORGANIZATION_1, MEMBERS_NAME);
+    long group2 = insertGroup(ORGANIZATION_2, MEMBERS_NAME);
+    insertOrganizationMember(user1, ORGANIZATION_1);
+    insertOrganizationMember(user2, ORGANIZATION_2);
+    insertOrganizationMember(user3, ORGANIZATION_1);
+
+    underTest.execute();
+
+    checkUserGroups(user1, group1);
+    checkUserGroups(user2, group2);
+    checkUserGroups(user3, group1);
+  }
+
+  @Test
+  public void set_users_into_group_members_when_some_users_already_belongs_to_group() throws Exception {
+    long user1 = insertUser("user1", true);
+    long user2 = insertUser("user2", true);
+    long user3 = insertUser("user3", true);
+    long group1 = insertGroup(ORGANIZATION_1, MEMBERS_NAME);
+    long group2 = insertGroup(ORGANIZATION_2, MEMBERS_NAME);
+    insertOrganizationMember(user1, ORGANIZATION_1);
+    insertOrganizationMember(user2, ORGANIZATION_2);
+    insertOrganizationMember(user3, ORGANIZATION_1);
+    insertUserGroups(user1, group1);
+
+    underTest.execute();
+
+    checkUserGroups(user1, group1);
+    checkUserGroups(user2, group2);
+    checkUserGroups(user3, group1);
+  }
+
+  @Test
+  public void does_nothing_if_members_group_does_not_exist() throws Exception {
+    long user1 = insertUser("user1", true);
+    insertGroup(ORGANIZATION_1, "other");
+    insertGroup(ORGANIZATION_2, "other");
+    insertOrganizationMember(user1, ORGANIZATION_1);
+
+    underTest.execute();
+
+    checkUserGroups(user1);
+  }
+
+  @Test
+  public void does_not_fail_when_users_already_belongs_to_group_members() throws Exception {
+    long user1 = insertUser("user1", true);
+    long user2 = insertUser("user2", true);
+    long group1 = insertGroup(ORGANIZATION_1, MEMBERS_NAME);
+    long group2 = insertGroup(ORGANIZATION_2, MEMBERS_NAME);
+    insertOrganizationMember(user1, ORGANIZATION_1);
+    insertOrganizationMember(user2, ORGANIZATION_2);
+    insertUserGroups(user1, group1);
+    insertUserGroups(user2, group2);
+
+    underTest.execute();
+
+    checkUserGroups(user1, group1);
+    checkUserGroups(user2, group2);
+  }
+
+  @Test
+  public void ignore_disabled_users() throws Exception {
+    long user = insertUser("user", false);
+    insertGroup(ORGANIZATION_1, MEMBERS_NAME);
+    insertOrganizationMember(user, ORGANIZATION_1);
+
+    underTest.execute();
+
+    checkUserGroups(user);
+  }
+
+  @Test
+  public void migration_is_renentrant() throws Exception {
+    long user = insertUser("user1", true);
+    long group = insertGroup(ORGANIZATION_1, MEMBERS_NAME);
+    insertOrganizationMember(user, ORGANIZATION_1);
+
+    underTest.execute();
+    checkUserGroups(user, group);
+
+    underTest.execute();
+    checkUserGroups(user, group);
+  }
+
+  private void checkUserGroups(long userId, Long... expectedGroupIds) {
+    List<Long> groups = db.select(format("select gu.group_id from groups_users gu where gu.user_id=%s", userId)).stream()
+      .map(map -> (Long) map.get("GROUP_ID"))
+      .collect(Collectors.toList());
+    assertThat(groups).containsOnly(expectedGroupIds);
+  }
+
+  private long insertUser(String login, boolean enabled) {
+    db.executeInsert(
+      "USERS",
+      "LOGIN", login,
+      "ACTIVE", Boolean.toString(enabled),
+      "IS_ROOT", "false",
+      "CREATED_AT", "1000",
+      "UPDATED_AT", "1000");
+    return (Long) db.selectFirst(format("select id from users where login='%s'", login)).get("ID");
+  }
+
+  private void insertOrganizationMember(long userId, String organizationUuid) {
+    db.executeInsert("ORGANIZATION_MEMBERS", "USER_ID", userId, "ORGANIZATION_UUID", organizationUuid);
+  }
+
+  private long insertGroup(String organization, String name) {
+    db.executeInsert(
+      "GROUPS",
+      "NAME", name,
+      "DESCRIPTION", name,
+      "ORGANIZATION_UUID", organization,
+      "CREATED_AT", new Date(),
+      "UPDATED_AT", new Date());
+    return (Long) db.selectFirst(format("select id from groups where name='%s' and organization_uuid='%s'", name, organization)).get("ID");
+  }
+
+  private void insertUserGroups(long userId, Long... groupIds) {
+    Arrays.stream(groupIds).forEach(groupId -> db.executeInsert(
+      "GROUPS_USERS",
+      "USER_ID", userId,
+      "GROUP_ID", groupId));
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/SetOrganizationMembersIntoMembersGroupTest/initial.sql
new file mode 100644 (file)
index 0000000..ffcb12a
--- /dev/null
@@ -0,0 +1,61 @@
+CREATE TABLE "USERS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "LOGIN" VARCHAR(255),
+  "NAME" VARCHAR(200),
+  "EMAIL" VARCHAR(100),
+  "CRYPTED_PASSWORD" VARCHAR(40),
+  "SALT" VARCHAR(40),
+  "ACTIVE" BOOLEAN DEFAULT TRUE,
+  "SCM_ACCOUNTS" VARCHAR(4000),
+  "EXTERNAL_IDENTITY" VARCHAR(255),
+  "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100),
+  "IS_ROOT" BOOLEAN NOT NULL,
+  "USER_LOCAL" BOOLEAN,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
+
+CREATE TABLE "GROUPS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+  "NAME" VARCHAR(500),
+  "DESCRIPTION" VARCHAR(200),
+  "IS_DEFAULT" BOOLEAN,
+  "CREATED_AT" TIMESTAMP,
+  "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "GROUPS_USERS" (
+  "USER_ID" INTEGER,
+  "GROUP_ID" INTEGER
+);
+CREATE INDEX "INDEX_GROUPS_USERS_ON_GROUP_ID" ON "GROUPS_USERS" ("GROUP_ID");
+CREATE INDEX "INDEX_GROUPS_USERS_ON_USER_ID" ON "GROUPS_USERS" ("USER_ID");
+CREATE UNIQUE INDEX "GROUPS_USERS_UNIQUE" ON "GROUPS_USERS" ("GROUP_ID", "USER_ID");
+
+CREATE TABLE "ORGANIZATIONS" (
+  "UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
+  "KEE" VARCHAR(32) NOT NULL,
+  "NAME" VARCHAR(64) NOT NULL,
+  "DESCRIPTION" VARCHAR(256),
+  "URL" VARCHAR(256),
+  "AVATAR_URL" VARCHAR(256),
+  "GUARDED" BOOLEAN NOT NULL,
+  "USER_ID" INTEGER,
+  "DEFAULT_PERM_TEMPLATE_PROJECT" VARCHAR(40),
+  "DEFAULT_PERM_TEMPLATE_VIEW" VARCHAR(40),
+  "CREATED_AT" BIGINT NOT NULL,
+  "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_ORGANIZATIONS" ON "ORGANIZATIONS" ("UUID");
+CREATE UNIQUE INDEX "ORGANIZATION_KEY" ON "ORGANIZATIONS" ("KEE");
+
+CREATE TABLE "ORGANIZATION_MEMBERS" (
+  "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+  "USER_ID" INTEGER NOT NULL
+);
+CREATE PRIMARY KEY ON "ORGANIZATION_MEMBERS" ("ORGANIZATION_UUID", "USER_ID");
+
+