--- /dev/null
+/*
+ * SonarQube :: Database
+ * 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.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Global permission 'profileadmin' is split into 'profileadmin' and 'gateadmin' so for each permission in DB
+ * 'profileadmin', a permission 'gateadmin' must be inserted in DB to keep the same level as before this split is
+ * introduced in SQ.
+ */
+public class InsertGateAdminPermissionForEachProfileAdmin extends BaseDataChange {
+
+ public InsertGateAdminPermissionForEachProfileAdmin(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ updateGroupAnyOne(context);
+ updateOtherGroups(context);
+ updateUsers(context);
+ }
+
+ private void updateGroupAnyOne(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("Group AnyOne");
+ update.select("select gr1.id from group_roles gr1 " +
+ "where gr1.role = 'profileadmin' " +
+ "and gr1.resource_id is null " +
+ "and gr1.group_id is null " +
+ "and not exists (" +
+ " select gr2.id from group_roles gr2 " +
+ " where gr2.group_id is null " +
+ " and gr2.resource_id is null " +
+ " and gr2.role='gateadmin'" +
+ ")");
+ update.update("insert into group_roles " +
+ "(group_id,resource_id,role) " +
+ "values " +
+ "(null, null, 'gateadmin')");
+ update.execute(GroupAnyOneHandler.INSTANCE);
+ }
+
+ private void updateOtherGroups(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("Other groups");
+ update.select("select gr1.group_id from group_roles gr1 " +
+ "where gr1.role = 'profileadmin' " +
+ "and gr1.resource_id is null " +
+ "and gr1.group_id is not null " +
+ "and not exists (" +
+ " select gr2.id from group_roles gr2 " +
+ " where gr2.group_id=gr1.group_id " +
+ " and gr2.resource_id is null " +
+ " and gr2.role='gateadmin'" +
+ ")");
+ update.update("insert into group_roles " +
+ "(group_id,resource_id,role) " +
+ "values " +
+ "(?, null, 'gateadmin')");
+ update.execute(OtherGroupsHandler.INSTANCE);
+ }
+
+ private void updateUsers(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("Users");
+ update.select("select ur1.user_id from user_roles ur1 " +
+ "where ur1.role = 'profileadmin' " +
+ "and ur1.resource_id is null " +
+ "and not exists (" +
+ " select ur2.id from user_roles ur2 " +
+ " where ur2.user_id=ur1.user_id " +
+ " and ur2.resource_id is null " +
+ " and ur2.role='gateadmin'" +
+ ")");
+ update.update("insert into user_roles " +
+ "(user_id,resource_id,role) " +
+ "values " +
+ "(?,null,'gateadmin')");
+ update.execute(UserRolesHandler.INSTANCE);
+ }
+
+ private enum GroupAnyOneHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ return true;
+ }
+ }
+
+ private enum OtherGroupsHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setLong(1, row.getLong(1));
+ return true;
+ }
+ }
+
+ private enum UserRolesHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setLong(1, row.getLong(1));
+ return true;
+ }
+ }
+}
INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (1, 1, null, 'admin');
INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (2, 1, null, 'profileadmin');
-INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (3, 1, null, 'shareDashboard');
-INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (4, null, null, 'scan');
-INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (5, null, null, 'dryRunScan');
-INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (6, null, null, 'provisioning');
-INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (7, 1, null, 'provisioning');
-ALTER TABLE GROUP_ROLES ALTER COLUMN ID RESTART WITH 8;
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (3, 1, null, 'gateadmin');
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (4, 1, null, 'shareDashboard');
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (5, null, null, 'scan');
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (6, null, null, 'dryRunScan');
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (7, null, null, 'provisioning');
+INSERT INTO GROUP_ROLES(ID, GROUP_ID, RESOURCE_ID, ROLE) VALUES (8, 1, null, 'provisioning');
+ALTER TABLE GROUP_ROLES ALTER COLUMN ID RESTART WITH 9;
INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 1);
INSERT INTO GROUPS_USERS(USER_ID, GROUP_ID) VALUES (1, 2);
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1005');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1006');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1007');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1008');
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', '1418215735482', '1418215735482', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
--- /dev/null
+/*
+ * SonarQube :: Database
+ * 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 javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class InsertGateAdminPermissionForEachProfileAdminTest {
+
+ private static final String TABLE_GROUP_ROLES = "group_roles";
+ private static final String TABLE_USER_ROLES = "user_roles";
+ private static final int SOME_GROUP_ID = 964;
+ private static final int SOME_USER_ID = 112;
+ private static final Integer ANYONE_GROUP_ID = null;
+ private static final int SOME_RESOURCE_ID = 25;
+ private static final String PROFILEADMIN_ROLE = "profileadmin";
+ private static final String NOT_PROFILE_ADMIN_ROLE = "ProfileAdmin";
+ private static final String GATE_ADMIN_ROLE = "gateadmin";
+
+ @ClassRule
+ public static DbTester db = DbTester.createForSchema(System2.INSTANCE, InsertGateAdminPermissionForEachProfileAdminTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ truncate(TABLE_GROUP_ROLES);
+ truncate(TABLE_USER_ROLES);
+
+ migration = new InsertGateAdminPermissionForEachProfileAdmin(db.database());
+ }
+
+ @Test
+ public void migrate_without_error_on_empty_db() throws Exception {
+ migration.execute();
+
+ assertGroupRoleTableSize(0);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void migrate_group_AnyOne() throws Exception {
+ insertGroupRole(ANYONE_GROUP_ID, null, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, null, PROFILEADMIN_ROLE);
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, null, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(2);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_AnyOne_for_resource() throws Exception {
+ insertGroupRole(ANYONE_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertGroupRoleDoesNotContainRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(1);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_AnyOne_when_not_profileadmin() throws Exception {
+ insertGroupRole(ANYONE_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+ assertGroupRoleDoesNotContainRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(1);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_AnyOne_already_has_role_gateadmin() throws Exception {
+ insertGroupRole(ANYONE_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ insertGroupRole(ANYONE_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertGroupRoleContainsRow(ANYONE_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(2);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void migrate_group() throws Exception {
+ insertGroupRole(SOME_GROUP_ID, null, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(SOME_GROUP_ID, null, PROFILEADMIN_ROLE);
+ assertGroupRoleContainsRow(SOME_GROUP_ID, null, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(2);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_for_resource() throws Exception {
+ insertGroupRole(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertGroupRoleDoesNotContainRow(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(1);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_when_not_profileadmin() throws Exception {
+ insertGroupRole(SOME_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+ assertGroupRoleDoesNotContainRow(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(1);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_group_already_has_role_gateadmin() throws Exception {
+ insertGroupRole(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ insertGroupRole(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertGroupRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertGroupRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertGroupRoleTableSize(2);
+ assertUserRoleTableSize(0);
+ }
+
+ @Test
+ public void migrate_user() throws Exception {
+ insertUserRole(SOME_USER_ID, null, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertUserRoleContainsRow(SOME_USER_ID, null, PROFILEADMIN_ROLE);
+ assertUserRoleContainsRow(SOME_USER_ID, null, GATE_ADMIN_ROLE);
+ assertUserRoleTableSize(2);
+ assertGroupRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_user_for_resource() throws Exception {
+ insertUserRole(SOME_USER_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+
+ migration.execute();
+
+ assertUserRoleContainsRow(SOME_USER_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertUserRoleDoesNotContainRow(SOME_USER_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertUserRoleTableSize(1);
+ assertGroupRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_user_when_not_profileadmin() throws Exception {
+ insertUserRole(SOME_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertUserRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, NOT_PROFILE_ADMIN_ROLE);
+ assertUserRoleDoesNotContainRow(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertUserRoleTableSize(1);
+ assertGroupRoleTableSize(0);
+ }
+
+ @Test
+ public void do_not_migrate_user_already_has_role_gateadmin() throws Exception {
+ insertUserRole(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ insertUserRole(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+
+ migration.execute();
+
+ assertUserRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, PROFILEADMIN_ROLE);
+ assertUserRoleContainsRow(SOME_GROUP_ID, SOME_RESOURCE_ID, GATE_ADMIN_ROLE);
+ assertUserRoleTableSize(2);
+ assertGroupRoleTableSize(0);
+ }
+
+ private void assertGroupRoleTableSize(int expected) {
+ assertThat(db.countRowsOfTable(TABLE_GROUP_ROLES)).isEqualTo(expected);
+ }
+
+ private void assertUserRoleTableSize(int expected) {
+ assertThat(db.countRowsOfTable(TABLE_USER_ROLES)).isEqualTo(expected);
+ }
+
+ private void assertGroupRoleDoesNotContainRow(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ String sql = groupRoleRowSql(groupId, resourceId, role);
+ assertThat(db.countSql(sql)).isEqualTo(0);
+ }
+
+ private void assertGroupRoleContainsRow(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ String sql = groupRoleRowSql(groupId, resourceId, role);
+ assertThat(db.countSql(sql)).isEqualTo(1);
+ }
+
+ private static String groupRoleRowSql(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ return format(
+ "select count(*) from group_roles where group_id %s and resource_id %s and role = '%s'",
+ whereClauseFromInteger(groupId),
+ whereClauseFromInteger(resourceId),
+ role);
+ }
+
+ private void assertUserRoleDoesNotContainRow(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ String sql = userRoleRowSql(groupId, resourceId, role);
+ assertThat(db.countSql(sql)).isEqualTo(0);
+ }
+
+ private void assertUserRoleContainsRow(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ String sql = userRoleRowSql(groupId, resourceId, role);
+ assertThat(db.countSql(sql)).isEqualTo(1);
+ }
+
+ private static String userRoleRowSql(@Nullable Integer userId, @Nullable Integer resourceId, String role) {
+ return format(
+ "select count(*) from user_roles where user_id %s and resource_id %s and role = '%s'",
+ whereClauseFromInteger(userId),
+ whereClauseFromInteger(resourceId),
+ role);
+ }
+
+ private void insertGroupRole(@Nullable Integer groupId, @Nullable Integer resourceId, String role) {
+ db.executeUpdateSql(format("insert into group_roles (group_id,resource_id,role) values(%s,%s,'%s')", nullValueFromInteger(groupId), nullValueFromInteger(resourceId), role));
+ }
+
+ private void insertUserRole(@Nullable Integer userId, @Nullable Integer resourceId, String role) {
+ db.executeUpdateSql(format("insert into user_roles (user_id,resource_id,role) values(%s,%s,'%s')", nullValueFromInteger(userId), nullValueFromInteger(resourceId), role));
+ }
+
+ private static String whereClauseFromInteger(@Nullable Integer id) {
+ if (id == null) {
+ return "is null";
+ }
+ return "=" + id;
+ }
+
+ private String nullValueFromInteger(@Nullable Integer value) {
+ if (value == null) {
+ return "null";
+ }
+ return String.valueOf(value);
+ }
+
+ private void truncate(String tableName) {
+ db.executeUpdateSql("truncate table " + tableName);
+ }
+
+}