diff --git a/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/java/io/gravitee/am/repository/jdbc/liquibase/UsernameUniquenessMigration.java b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/java/io/gravitee/am/repository/jdbc/liquibase/UsernameUniquenessMigration.java index 98c6c200661..afae97cea55 100644 --- a/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/java/io/gravitee/am/repository/jdbc/liquibase/UsernameUniquenessMigration.java +++ b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/java/io/gravitee/am/repository/jdbc/liquibase/UsernameUniquenessMigration.java @@ -42,10 +42,8 @@ */ public class UsernameUniquenessMigration implements CustomSqlChange { public static final String TABLE_USERS = "users"; + public static final String TABLE_ORGANIZATION_USERS = "organization_users"; private final Logger logger = LoggerFactory.getLogger(UsernameUniquenessMigration.class); - - private int counter = 0; - private ObjectMapper mapper = new ObjectMapper(); @Override @@ -57,9 +55,7 @@ public SqlStatement[] generateStatements(Database database) throws CustomChangeE try { JdbcConnection connection = (JdbcConnection) database.getConnection(); - Map> duplicatesUsersGroupByUsernameAndSource = searchDuplicatesGroupedByUsernameAndSource(connection); - - logger.info("{} duplicate usernames found", counter); + Map> duplicatesUsersGroupByUsernameAndSource = searchDuplicatesGroupedByUsernameAndSource(connection, TABLE_USERS); // boolean to keep in memory if the process must fail // in order to go through all the duplicates to see all potential errors. @@ -100,6 +96,21 @@ public SqlStatement[] generateStatements(Database database) throws CustomChangeE } } + Map> duplicatesOrgUsersGroupByUsernameAndSource = searchDuplicatesGroupedByUsernameAndSource(connection, TABLE_ORGANIZATION_USERS); + for (Map.Entry> entries : duplicatesOrgUsersGroupByUsernameAndSource.entrySet()) { + List duplicates = entries.getValue(); + User referenceUser = duplicates.get(0); + + if (!referenceUser.getSource().equalsIgnoreCase("gravitee") && !referenceUser.getSource().equalsIgnoreCase("cockpit")) { + logger.error("Organization Username '{}' migration only manages gravitee & cockpit identity providers", + referenceUser.getUsername(), + referenceUser.getSource()); + needToFail = true; + } else { + statements.addAll(generateStatementToRenameDuplicatedOrgUsers(database, duplicates)); + } + } + if (needToFail) { throw new CustomChangeException("Some duplicates can't be processed automatically, liquibase will fail"); } @@ -111,8 +122,27 @@ public SqlStatement[] generateStatements(Database database) throws CustomChangeE } } + private List generateStatementToRenameDuplicatedOrgUsers(Database database, List duplicates) { + List statementsToApply = new ArrayList<>(); + for (int i = 1; i < duplicates.size(); ++i) { + final User duplicateToUpdate = duplicates.get(i); + final String updatedUsername = duplicateToUpdate.getUsername()+"_"+i+"_TO_RENAME_OR_DELETE"; + logger.info("Renaming organization username '{}' to '{}' into tables '" + TABLE_ORGANIZATION_USERS + "' (user_id: {})", + duplicateToUpdate.getUsername(), + updatedUsername, + duplicateToUpdate.getId()); + + SqlStatement updateUsersTable = new UpdateStatement(database.getDefaultCatalogName(), database.getDefaultSchemaName(), TABLE_ORGANIZATION_USERS) + .addNewColumnValue("username", updatedUsername) + .setWhereClause(String.format("id='%s'", duplicateToUpdate.getId())); + statementsToApply.add(updateUsersTable); + } + return statementsToApply; + } + private List generateStatementToRenameDuplicatedUsers(Database database, List duplicates, String idpTable, String usernameColumn, String idColumn) { List statementsToApply = new ArrayList<>(); + for (int i = 1; i < duplicates.size(); ++i) { final User duplicateToUpdate = duplicates.get(i); final String updatedUsername = duplicateToUpdate.getUsername()+"_"+i+"_TO_RENAME_OR_DELETE"; @@ -136,12 +166,13 @@ private List generateStatementToRenameDuplicatedUsers(Database dat return statementsToApply; } - private Map> searchDuplicatesGroupedByUsernameAndSource(JdbcConnection connection) throws SQLException, DatabaseException { + private Map> searchDuplicatesGroupedByUsernameAndSource(JdbcConnection connection, String table) throws SQLException, DatabaseException { + int userCounter = 0; PreparedStatement searchDuplicates = connection.prepareStatement("select u.*\n" + - "from users u,\n" + + "from "+table+" u,\n" + "(select username, source\n" + "from (select username, source, count(username) as count\n" + - "from users\n" + + "from "+table + "\n" + "group by source, username) as multiEntries\n" + "where multiEntries.count > 1) duplicateUser\n" + "where u.username = duplicateUser.username\n" + @@ -167,17 +198,20 @@ private Map> searchDuplicatesGroupedByUsernameAndSource(JdbcC } duplicatesUsersGroupByUsernameAndSource.get(groupKey).add(currentUser); - counter++; + userCounter++; } duplicateUsers.close(); searchDuplicates.close(); + + logger.info("{} duplicate usernames found into {} table", userCounter, table); + return duplicatesUsersGroupByUsernameAndSource; } @Override public String getConfirmationMessage() { - return this.counter + " usernames processed successfully to avoid duplicates"; + return "Usernames processed successfully to avoid duplicates"; } @Override diff --git a/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/changelogs/v3_19_21/3.19.21-alter-org_users_username_unique.yml b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/changelogs/v3_19_21/3.19.21-alter-org_users_username_unique.yml new file mode 100644 index 00000000000..9b26322fa76 --- /dev/null +++ b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/changelogs/v3_19_21/3.19.21-alter-org_users_username_unique.yml @@ -0,0 +1,12 @@ +databaseChangeLog: + - changeSet: + id: 3.19.21-org-users-add-username-unique-constraints + author: GraviteeSource Team + changes: + ############################# + # users Table, add unique constraints to the username field + ############################ + - addUniqueConstraint: + tableName: organization_users + columnNames: username, source + constraintName: users_username_source_unique diff --git a/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/master.yml b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/master.yml index de729720f90..b463dcf796a 100644 --- a/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/master.yml +++ b/gravitee-am-repository/gravitee-am-repository-jdbc/src/main/resources/liquibase/master.yml @@ -85,6 +85,8 @@ databaseChangeLog: - file: liquibase/changelogs/v3_19_13/3.19.13-alter-i18n_dictionary_entries-column-length.yml - include: - file: liquibase/changelogs/v3_19_21/3.19.21-migrate-users_username_unique.yml + - include: + - file: liquibase/changelogs/v3_19_21/3.19.21-alter-org_users_username_unique.yml - include: - file: liquibase/changelogs/v3_19_21/3.19.21-alter-users_username_unique.yml - include: