From cdcf2945e6f922a68842f2b0f9098f27a38e11ac Mon Sep 17 00:00:00 2001 From: Sabin Antohe Date: Wed, 3 Jan 2024 18:12:43 +0200 Subject: [PATCH] Initial configuration for Flyway (#178) * Initial configuration for Flyway * Use flyway migration at runtime and also calculate baseline dynamically * Remove Flyway plugin configuration * Replace dummy migration with migrations from CHANGELOG * Replace dummy baseline script * Removed all migration written in sql and replace them with Java migration * Removed all migration written in sql and replace them with Java migration 2.0 * Made migration work without compilation errors + added initial testing * cleanup the build.gradle of previous config * moved init database as a migration script * breakdown migration into smaller subunit for V3 and started the preparation for V1 * V1 will create only necessary tables and columns without indexes * updated migrations * some missing indexes for V3 * clean up --------- Co-authored-by: Sabin Antohe --- build.gradle | 6 + implementationDependencies.json | 5 + .../storage/postgresql/ConnectionPool.java | 10 + .../storage/postgresql/FlywayMigration.java | 66 + .../postgresql/MigrationContextManager.java | 36 + .../storage/postgresql/ProcessState.java | 2 +- .../postgresql/QueryExecutorTemplate.java | 1 - .../supertokens/storage/postgresql/Start.java | 25 +- .../postgresql/config/PostgreSQLConfig.java | 12 + .../migrations/V1__init_database.java | 37 + .../migrations/V2__plugin_version_3_0_0.java | 52 + .../migrations/V3__plugin_version_4_0_0.java | 1508 +++++++++++++++++ .../migrations/V4__plugin_version_5_0_0.java | 130 ++ .../migrations/V5__plugin_version_5_0_4.java | 40 + .../migrations/V6__core_version_7_0_12.java | 96 ++ .../queries/ActiveUsersQueries.java | 16 +- .../queries/BaselineMigrationQueries.java | 105 ++ .../postgresql/queries/DashboardQueries.java | 43 +- .../queries/EmailPasswordQueries.java | 70 +- .../queries/EmailVerificationQueries.java | 43 +- .../postgresql/queries/GeneralQueries.java | 391 +---- .../postgresql/queries/JWTSigningQueries.java | 21 +- .../queries/MultitenancyQueries.java | 81 - .../queries/PasswordlessQueries.java | 129 +- .../postgresql/queries/SessionQueries.java | 41 +- .../postgresql/queries/TOTPQueries.java | 82 +- .../postgresql/queries/ThirdPartyQueries.java | 49 +- .../queries/UserIdMappingQueries.java | 32 +- .../queries/UserMetadataQueries.java | 13 +- .../postgresql/queries/UserRolesQueries.java | 70 +- .../postgresql/test/FlywayMigrationTest.java | 102 ++ 31 files changed, 2383 insertions(+), 931 deletions(-) create mode 100644 src/main/java/io/supertokens/storage/postgresql/FlywayMigration.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/MigrationContextManager.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V1__init_database.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V2__plugin_version_3_0_0.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V3__plugin_version_4_0_0.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V4__plugin_version_5_0_0.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V5__plugin_version_5_0_4.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/migrations/V6__core_version_7_0_12.java create mode 100644 src/main/java/io/supertokens/storage/postgresql/queries/BaselineMigrationQueries.java create mode 100644 src/test/java/io/supertokens/storage/postgresql/test/FlywayMigrationTest.java diff --git a/build.gradle b/build.gradle index 754f70d7..685add16 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,12 @@ dependencies { // https://mvnrepository.com/artifact/org.postgresql/postgresql implementation group: 'org.postgresql', name: 'postgresql', version: '42.2.10' + // https://mvnrepository.com/artifact/org.flywaydb/flyway-core + implementation group: 'org.flywaydb', name: 'flyway-core', version: '7.15.0' + + // https://mvnrepository.com/artifact/com.googlecode.libphonenumber/libphonenumber + compileOnly group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.25' + // https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml compileOnly group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.14.0' diff --git a/implementationDependencies.json b/implementationDependencies.json index 6c885fc4..0875269e 100644 --- a/implementationDependencies.json +++ b/implementationDependencies.json @@ -15,6 +15,11 @@ "jar": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar", "name": "SLF4j API 1.7.25", "src": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25-sources.jar" + }, + { + "jar": "https://repo1.maven.org/maven2/org/flywaydb/flyway-core/7.15.0/flyway-core-7.15.0.jar", + "name": "Flyway Core 7.15.0", + "src": "https://repo1.maven.org/maven2/org/flywaydb/flyway-core/7.15.0/flyway-core-7.15.0-sources.jar" } ] } diff --git a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java index f7dbc287..43e63352 100644 --- a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java +++ b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java @@ -198,6 +198,16 @@ public static Connection getConnection(Start start) throws SQLException { return getInstance(start).hikariDataSource.getConnection(); } + public static HikariDataSource getHikariDataSource(Start start) throws SQLException { + if (getInstance(start) == null || getInstance(start).hikariDataSource == null ) { + throw new IllegalStateException("Please call initPool before getHikariDataSource"); + } + if (!start.enabled) { + throw new SQLException("Storage layer disabled"); + } + return getInstance(start).hikariDataSource; + } + static void close(Start start) { if (getInstance(start) == null) { return; diff --git a/src/main/java/io/supertokens/storage/postgresql/FlywayMigration.java b/src/main/java/io/supertokens/storage/postgresql/FlywayMigration.java new file mode 100644 index 00000000..f669033d --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/FlywayMigration.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql; + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.storage.postgresql.config.Config; +import io.supertokens.storage.postgresql.output.Logging; +import io.supertokens.storage.postgresql.queries.BaselineMigrationQueries; +import org.flywaydb.core.Flyway; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +public final class FlywayMigration { + + private static final String LOCATION = "classpath:/io/supertokens/storage/postgresql/migrations"; + private FlywayMigration() {} + + public static void startMigration(Start start) throws SQLException, StorageQueryException { + String baseline = BaselineMigrationQueries.getBaselineMigrationVersion(start); + if (Integer.parseInt(baseline) >= BaselineMigrationQueries.LAST_MIGRATION) { + return; + } + + Logging.info(start, "Starting migration.", true); + MigrationContextManager.putContext(start.getProcessId(), start); + int maxRetries = 5; + + try { + Flyway flyway = Flyway.configure() + .dataSource(ConnectionPool.getHikariDataSource(start)) + .baselineOnMigrate(true) + .baselineVersion(baseline) + .table(Config.getConfig(start).getFlywaySchemaHistory()) + .connectRetries(maxRetries) + .lockRetryCount(maxRetries) + .locations(LOCATION) + .placeholders(getPlaceholders(start)) + .load(); + flyway.migrate(); + } finally { + MigrationContextManager.removeContext(start.getProcessId()); + } + } + + private static Map getPlaceholders(Start start) { + Map ph = new HashMap<>(); + ph.put("process_id", start.getProcessId()); + ph.put("access_token_signing_key_dynamic", String.valueOf( Config.getConfig(start).getAccessTokenSigningKeyDynamic())); + return ph; + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/MigrationContextManager.java b/src/main/java/io/supertokens/storage/postgresql/MigrationContextManager.java new file mode 100644 index 00000000..b68baa31 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/MigrationContextManager.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class MigrationContextManager { + private static Map contextMap = new ConcurrentHashMap<>(); + + public static void putContext(String key, Start start) { + contextMap.put(key, start); + } + + public static Start getContext(String key) { + return contextMap.get(key); + } + + public static void removeContext(String key) { + contextMap.remove(key); + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/ProcessState.java b/src/main/java/io/supertokens/storage/postgresql/ProcessState.java index b66e7cdc..788a6c4a 100644 --- a/src/main/java/io/supertokens/storage/postgresql/ProcessState.java +++ b/src/main/java/io/supertokens/storage/postgresql/ProcessState.java @@ -61,7 +61,7 @@ public synchronized void clear() { * DEADLOCK_FOUND: In case of a deadlock situation, we put this event */ public enum PROCESS_STATE { - CREATING_NEW_TABLE, DEADLOCK_FOUND, DEADLOCK_NOT_RESOLVED + CREATING_NEW_TABLE, STARTING_MIGRATION, DEADLOCK_FOUND, DEADLOCK_NOT_RESOLVED } public static class EventAndException { diff --git a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java index db0c9785..5bf068bd 100644 --- a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java +++ b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java @@ -58,5 +58,4 @@ static int update(Connection con, String QUERY, PreparedStatementValueSetter set return pst.executeUpdate(); } } - } diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java index 91940ee0..a57142b6 100644 --- a/src/main/java/io/supertokens/storage/postgresql/Start.java +++ b/src/main/java/io/supertokens/storage/postgresql/Start.java @@ -193,11 +193,16 @@ public void initFileLogging(String infoLogPath, String errorLogPath) { * nothing will be handling logging and hikari's logs would not be outputed * anywhere. */ - final Logger infoLog = (Logger) LoggerFactory.getLogger("com.zaxxer.hikari"); + final Logger hikariLog = (Logger) LoggerFactory.getLogger("com.zaxxer.hikari"); + final Logger flywayLog = (Logger) LoggerFactory.getLogger("org.flywaydb.core"); appender = new HikariLoggingAppender(this); - if (infoLog.getAppender(HikariLoggingAppender.NAME) == null) { - infoLog.setAdditive(false); - infoLog.addAppender(appender); + if (hikariLog.getAppender(HikariLoggingAppender.NAME) == null) { + hikariLog.setAdditive(false); + hikariLog.addAppender(appender); + } + if (flywayLog.getAppender(HikariLoggingAppender.NAME) == null) { + flywayLog.setAdditive(false); + flywayLog.addAppender(appender); } } } @@ -208,9 +213,13 @@ public void stopLogging() { synchronized (appenderLock) { Logging.stopLogging(this); - final Logger infoLog = (Logger) LoggerFactory.getLogger("com.zaxxer.hikari"); - if (infoLog.getAppender(HikariLoggingAppender.NAME) != null) { - infoLog.detachAppender(HikariLoggingAppender.NAME); + final Logger hikariLog = (Logger) LoggerFactory.getLogger("com.zaxxer.hikari"); + final Logger flywayLog = (Logger) LoggerFactory.getLogger("org.flywaydb.core"); + if (hikariLog.getAppender(HikariLoggingAppender.NAME) != null) { + hikariLog.detachAppender(HikariLoggingAppender.NAME); + } + if (flywayLog.getAppender(HikariLoggingAppender.NAME) != null) { + flywayLog.detachAppender(HikariLoggingAppender.NAME); } } } @@ -228,7 +237,7 @@ public void initStorage(boolean shouldWait) throws DbInitException { } try { ConnectionPool.initPool(this, shouldWait); - GeneralQueries.createTablesIfNotExists(this); + FlywayMigration.startMigration(this); } catch (Exception e) { throw new DbInitException(e); } diff --git a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java index e8bc81d6..11ab7245 100644 --- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java +++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java @@ -115,6 +115,10 @@ public class PostgreSQLConfig { @IgnoreForAnnotationCheck boolean isValidAndNormalised = false; + @JsonProperty + @IgnoreForAnnotationCheck + private boolean access_token_signing_key_dynamic = true; + public static Set getValidFields() { PostgreSQLConfig config = new PostgreSQLConfig(); JsonObject configObj = new GsonBuilder().serializeNulls().create().toJsonTree(config).getAsJsonObject(); @@ -234,6 +238,10 @@ public String getThirdPartyUsersTable() { return postgresql_thirdparty_users_table_name; } + public boolean getAccessTokenSigningKeyDynamic() { + return access_token_signing_key_dynamic; + } + public String getThirdPartyUserToTenantTable() { return addSchemaAndPrefixToTableName("thirdparty_user_to_tenant"); } @@ -302,6 +310,10 @@ public String getTotpUsedCodesTable() { return addSchemaAndPrefixToTableName("totp_used_codes"); } + public String getFlywaySchemaHistory() { + return addSchemaAndPrefixToTableName("flyway_schema_history"); + } + private String addSchemaAndPrefixToTableName(String tableName) { return addSchemaToTableName(postgresql_table_names_prefix + tableName); } diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V1__init_database.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V1__init_database.java new file mode 100644 index 00000000..8c83c649 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V1__init_database.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.queries.GeneralQueries; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; + +import java.util.Map; + + +public class V1__init_database extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + GeneralQueries.createTablesIfNotExists(start); + } + +} diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V2__plugin_version_3_0_0.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V2__plugin_version_3_0_0.java new file mode 100644 index 00000000..cc486e73 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V2__plugin_version_3_0_0.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import java.sql.Statement; +import java.util.Map; + +public class V2__plugin_version_3_0_0 extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + String sessionInfoTable = Config.getConfig(start).getSessionInfoTable(); + String JWTSigningKeysTable = Config.getConfig(start).getJWTSigningKeysTable(); + String accessTokenSigningKeysTable = Config.getConfig(start).getAccessTokenSigningKeysTable(); + + try (Statement statement = context.getConnection().createStatement()) { + // Add a new column with a default value + statement.execute("ALTER TABLE " + sessionInfoTable + " ADD COLUMN IF NOT EXISTS use_static_key BOOLEAN NOT NULL DEFAULT" + + "(" + !Boolean.parseBoolean(ph.get("access_token_signing_key_dynamic")) + ")"); + // Alter the column to drop the default value + statement.execute("ALTER TABLE " + sessionInfoTable + " ALTER COLUMN " + + "use_static_key DROP DEFAULT"); + + // Insert data into jwt_signing_keys from session_access_token_signing_keys + statement.execute("INSERT INTO " + JWTSigningKeysTable + " (key_id, key_string, algorithm, " + + "created_at) " + + "SELECT CONCAT('s-', created_at_time) as key_id, value as key_string, 'RS256' as algorithm, created_at_time as created_at " + + "FROM " + accessTokenSigningKeysTable); + } + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V3__plugin_version_4_0_0.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V3__plugin_version_4_0_0.java new file mode 100644 index 00000000..060fcf5b --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V3__plugin_version_4_0_0.java @@ -0,0 +1,1508 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import io.supertokens.storage.postgresql.utils.Utils; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; + +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; + +public class V3__plugin_version_4_0_0 extends BaseJavaMigration { + + + @Override + public void migrate(Context context) throws Exception { + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + + // Migrate apps table + migrateAppsTable(context,start); + + // Migrate tenants table + migrateTenantsTable(context,start); + + // Migrate key_value table + migrateKeyValueTable(context,start); + + // Migrate app_id_to_user_id table + migrateAppIdToUserIdTable(context,start); + + // Migrate all_auth_recipe_users table + migrateAllUserTable(context,start); + + // Migrate tenant_configs table + migrateTenantConfigTable(context,start); + + // Migrate tenant_thirdparty_providers table + migrateTenantThirdPartyProvidersTable(context,start); + + // Migrate tenant_thirdparty_provider_clients table + migrateTenantThirdPartyProviderClientsTable(context,start); + + // Migrate session_info table + migrateSessionInfoTable(context, start); + + // Migrate session_access_token_signing_keys table + migrateSessionAccessTokenSigningKeys(context, start); + + // Migrate jwt_signing_keys table + migrateJwtSigningKeysTable(context,start); + + // Migrate emailverification_verified_emails table + migrateEmailVerificationTable(context,start); + + // Migrate emailverification_tokens table + migrateEmailVerificationTokensTable(context,start); + + // Migrate emailpassword_users table + migrateEmailPasswordUsersTable(context,start); + + // Migrate emailpassword_user_to_tenant table + migrateEmailPasswordUserToTenantTable(context,start); + + // Migrate emailpassword_pswd_reset_tokens table + migratePasswordResetTokensTable(context,start); + + // Migrate passwordless_users table + migratePasswordlessUsersTable(context,start); + + // Migrate passwordless_user_to_tenant table + migratePasswordlessUserToTenantTable(context,start); + + // Migrate passwordless_devices table + migratePasswordlessDevicesTable(context,start); + + // Migrate passwordless_codes table + migratePasswordlessCodesTable(context,start); + + // Migrate thirdparty_users table + migrateThirdPartyUsersTable(context,start); + + // Migrate thirdparty_user_to_tenant table + migrateThirdPartyUserToTenantTable(context,start); + + // Migrate userid_mapping table + migrateUserIdMappingTable(context,start); + + // Migrate roles table + migrateRolesTable(context,start); + + // Migrate role_permissions table + migrateUserRolesPermissionsTable(context,start); + + // Migrate user_roles table + migrateUserRolesTable(context,start); + + // Migrate user_metadata table + migrateUserMetadataTable(context,start); + + // Migrate dashboard_users table + migrateDashboardUsersTable(context,start); + + // Migrate dashboard_user_sessions table + migrateDashboardSessionsTable(context,start); + + // Migrate totp_users table + migrateTotpUsersTable(context,start); + + // Migrate totp_user_devices table + migrateTotpUserDevicesTable(context,start); + + // Migrate totp_used_codes table + migrateTotpUsedCodesTable(context,start); + + // Migrate user_last_active table + migrateUserLastActiveTable(context,start); + } + + + /* + - adding columns: app_id + - primary key created_at_time => (app_id,created_at_time) + fk to app_table + - index(app_id) + */ + private void migrateSessionAccessTokenSigningKeys(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String accessTokenSigningKeysTable = Config.getConfig(start).getAccessTokenSigningKeysTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + accessTokenSigningKeysTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + accessTokenSigningKeysTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + accessTokenSigningKeysTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, null, "pkey") + + " PRIMARY KEY (app_id, created_at_time);"); + + statement.execute("ALTER TABLE " + accessTokenSigningKeysTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, + "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + accessTokenSigningKeysTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, + "app_id", "fkey") + + " FOREIGN KEY (app_id) REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS access_token_signing_keys_app_id_index ON " + + accessTokenSigningKeysTable + " (app_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id,tenant_id,session_handle) + FK to tenant table in V3 + - index(app_id,tenant_id) + index(session_expiry_index) + */ + private void migrateSessionInfoTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String sessionInfoTable = Config.getConfig(start).getSessionInfoTable(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + sessionInfoTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + sessionInfoTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, null, + "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + sessionInfoTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, null, + "pkey") + + " PRIMARY KEY (app_id, tenant_id, session_handle);"); + + statement.execute("ALTER TABLE " + sessionInfoTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, sessionInfoTable, "tenant_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + sessionInfoTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS session_expiry_index ON " + sessionInfoTable + + " (expires_at);"); + + statement.execute("CREATE INDEX IF NOT EXISTS session_info_tenant_id_index ON " + sessionInfoTable + + " (app_id, tenant_id);"); + } + } + + /* + Created with V3 migration + */ + private void migrateAppsTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + appsTable + " ( " + + " app_id VARCHAR(64) NOT NULL DEFAULT 'public', " + + " created_at_time BIGINT, " + + " CONSTRAINT " + Utils.getConstraintName(schema, appsTable, null, + "pkey") + " PRIMARY KEY(app_id) " + + ");"); + + statement.execute("INSERT INTO " + appsTable + + " (app_id, created_at_time) VALUES ('public', 0) ON CONFLICT DO NOTHING;"); + } + } + + /* + Created with V3 migration + */ + private void migrateTenantsTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + tenantsTable + " ( " + + " app_id VARCHAR(64) NOT NULL DEFAULT 'public', " + + " tenant_id VARCHAR(64) NOT NULL DEFAULT 'public', " + + " created_at_time BIGINT, " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantsTable, null, + "pkey") + " PRIMARY KEY (app_id, tenant_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantsTable, "app_id", + "fkey") + " FOREIGN KEY(app_id) " + + " REFERENCES " + appsTable + " (app_id) ON DELETE CASCADE " + + ");"); + + statement.execute("INSERT INTO " + tenantsTable + + " (app_id, tenant_id, created_at_time) VALUES ('public', 'public', 0) ON CONFLICT DO NOTHING;"); + + statement.execute("CREATE INDEX IF NOT EXISTS tenants_app_id_index ON " + + tenantsTable + " (app_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id,tenant_id,name) + FK to tenant table + - index(app_id,tenant_id) + */ + private void migrateKeyValueTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + String keyValueTable = Config.getConfig(start).getKeyValueTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + keyValueTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + keyValueTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, null, + "pkey")); + + statement.execute("ALTER TABLE " + keyValueTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, null, + "pkey") + + " PRIMARY KEY (app_id, tenant_id, name);"); + + statement.execute("ALTER TABLE " + keyValueTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, keyValueTable, "tenant_id", "fkey")); + + statement.execute("ALTER TABLE " + keyValueTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS key_value_tenant_id_index" + + " ON " + keyValueTable + " (app_id, tenant_id);"); + } + } + + /* + Created with V3 migration + */ + private void migrateAppIdToUserIdTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String appsTable = Config.getConfig(start).getAppsTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + String allUserTable = Config.getConfig(start).getUsersTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + appIdToUserIdTable + " ( " + + " app_id VARCHAR(64) NOT NULL DEFAULT 'public', " + + " user_id CHAR(36) NOT NULL, " + + " recipe_id VARCHAR(128) NOT NULL, " + + " CONSTRAINT " + Utils.getConstraintName(schema, appIdToUserIdTable, null, + "pkey") + " PRIMARY KEY (app_id, user_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, appIdToUserIdTable, "app_id", + "fkey") + " FOREIGN KEY(app_id) " + + " REFERENCES " + appsTable + " (app_id) ON DELETE CASCADE " + + ");"); + + statement.execute("INSERT INTO " + appIdToUserIdTable + + " (user_id, recipe_id) " + + "SELECT user_id, recipe_id FROM " + allUserTable + " ON CONFLICT DO NOTHING;"); + + statement.execute("CREATE INDEX IF NOT EXISTS " + appIdToUserIdTable + + "_app_id_index ON " + appIdToUserIdTable + " (app_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id,tenant_id,name) + FK to tenant table + - index(app_id,tenant_id),(time_joined DESC, user_id DESC, tenant_id DESC, app_id),(app_id, user_id) + */ + private void migrateAllUserTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + String allUserTable = Config.getConfig(start).getUsersTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + allUserTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + allUserTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, allUserTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + allUserTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, allUserTable, null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id);"); + + statement.execute("ALTER TABLE " + allUserTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, allUserTable, "tenant_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + allUserTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, allUserTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + allUserTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, allUserTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + allUserTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, allUserTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS all_auth_recipe_users_pagination_index;"); + + statement.execute("CREATE INDEX all_auth_recipe_users_pagination_index ON " + allUserTable + + " (time_joined DESC, user_id DESC, tenant_id DESC, app_id DESC);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON " + allUserTable + + " (app_id, user_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON " + allUserTable + + " (app_id, tenant_id);"); + } + } + + /* + Created with V3 migration + */ + private void migrateTenantConfigTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantConfigTable = Config.getConfig(start).getTenantConfigsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + tenantConfigTable + " ( " + + " connection_uri_domain VARCHAR(256) DEFAULT '', " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " core_config TEXT, " + + " email_password_enabled BOOLEAN, " + + " passwordless_enabled BOOLEAN, " + + " third_party_enabled BOOLEAN, " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigTable, null, + "pkey") + + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id) " + + ");"); + } + } + + /* + Created with V3 migration + */ + private void migrateTenantThirdPartyProvidersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantThirdPartyProvidersTable = Config.getConfig(start).getTenantThirdPartyProvidersTable(); + String tenantConfigTable = Config.getConfig(start).getTenantConfigsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + tenantThirdPartyProvidersTable+ " ( " + + " connection_uri_domain VARCHAR(256) DEFAULT '', " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " third_party_id VARCHAR(28) NOT NULL, " + + " name VARCHAR(64), " + + " authorization_endpoint TEXT, " + + " authorization_endpoint_query_params TEXT, " + + " token_endpoint TEXT, " + + " token_endpoint_body_params TEXT, " + + " user_info_endpoint TEXT, " + + " user_info_endpoint_query_params TEXT, " + + " user_info_endpoint_headers TEXT, " + + " jwks_uri TEXT, " + + " oidc_discovery_endpoint TEXT, " + + " require_email BOOLEAN, " + + " user_info_map_from_id_token_payload_user_id VARCHAR(64), " + + " user_info_map_from_id_token_payload_email VARCHAR(64), " + + " user_info_map_from_id_token_payload_email_verified VARCHAR(64), " + + " user_info_map_from_user_info_endpoint_user_id VARCHAR(64), " + + " user_info_map_from_user_info_endpoint_email VARCHAR(64), " + + " user_info_map_from_user_info_endpoint_email_verified VARCHAR(64), " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") + + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "tenant_id", + "fkey") + + " FOREIGN KEY(connection_uri_domain, app_id, tenant_id) " + + " REFERENCES " + tenantConfigTable + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE " + + ");"); + + statement.execute("CREATE INDEX IF NOT EXISTS tenant_thirdparty_providers_tenant_id_index ON " + tenantThirdPartyProvidersTable+ + " (connection_uri_domain, app_id, tenant_id);"); + } + } + + /* + Created with V3 migration + */ + private void migrateTenantThirdPartyProviderClientsTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String tenantThirdPartyProvidersTable = Config.getConfig(start).getTenantThirdPartyProvidersTable(); + String tenantThirdPartyProviderClientsTable = Config.getConfig(start).getTenantThirdPartyProviderClientsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + tenantThirdPartyProviderClientsTable + " ( " + + " connection_uri_domain VARCHAR(256) DEFAULT '', " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " third_party_id VARCHAR(28) NOT NULL, " + + " client_type VARCHAR(64) NOT NULL DEFAULT '', " + + " client_id VARCHAR(256) NOT NULL, " + + " client_secret TEXT, " + + " scope VARCHAR(128)[], " + + " force_pkce BOOLEAN, " + + " additional_config TEXT, " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProviderClientsTable, null, "pkey") + + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type), " + + " CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProviderClientsTable, "third_party_id", "fkey") + + " FOREIGN KEY (connection_uri_domain, app_id, tenant_id, third_party_id) " + + " REFERENCES " + tenantThirdPartyProvidersTable+ " (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE " + + ");"); + + statement.execute("CREATE INDEX IF NOT EXISTS tenant_thirdparty_provider_clients_third_party_id_index ON " + tenantThirdPartyProviderClientsTable + + " (connection_uri_domain, app_id, tenant_id, third_party_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id,key_id) + FK to apps table + - index(app_id) + */ + private void migrateJwtSigningKeysTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String jwtSigningKeysTable = Config.getConfig(start).getJWTSigningKeysTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + jwtSigningKeysTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + jwtSigningKeysTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, + null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + jwtSigningKeysTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, + null, "pkey") + + " PRIMARY KEY (app_id, key_id);"); + + statement.execute("ALTER TABLE " + jwtSigningKeysTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, jwtSigningKeysTable, "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + jwtSigningKeysTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS jwt_signing_keys_app_id_index ON " + + jwtSigningKeysTable + " (app_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id,user_id,email) + FK to apps table + - index(app_id) + */ + private void migrateEmailVerificationTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String emailVerificationTable = Config.getConfig(start).getEmailVerificationTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + emailVerificationTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + emailVerificationTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + emailVerificationTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id, email);"); + + statement.execute("ALTER TABLE " + emailVerificationTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, emailVerificationTable, "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + emailVerificationTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS emailverification_verified_emails_app_id_index ON " + + emailVerificationTable + " (app_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id, tenant_id, user_id, email, token) + FK to apps table + - index((app_id, tenant_id) + */ + private void migrateEmailVerificationTokensTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String emailVerificationTokensTable = Config.getConfig(start).getEmailVerificationTokensTable(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + emailVerificationTokensTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + emailVerificationTokensTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + emailVerificationTokensTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id, email, token);"); + + statement.execute("ALTER TABLE " + emailVerificationTokensTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, emailVerificationTokensTable, "tenant_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + emailVerificationTokensTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS emailverification_tokens_tenant_id_index ON " + + emailVerificationTokensTable + " (app_id, tenant_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS emailverification_tokens_index ON " + + emailVerificationTokensTable + " (token_expiry);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id)) + FK to appIdToUserIdTable + */ + private void migrateEmailPasswordUsersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String emailPasswordUsersTable = Config.getConfig(start).getEmailPasswordUsersTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, emailPasswordUsersTable, "email", + "key") + " CASCADE;"); + + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + emailPasswordUsersTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, emailPasswordUsersTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + emailPasswordUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + } + } + + /* + Created with V3 + */ + private void migrateEmailPasswordUserToTenantTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String emailPasswordUsersTable = Config.getConfig(start).getEmailPasswordUsersTable(); + String emailPasswordUserToTenantTable = Config.getConfig(start).getEmailPasswordUserToTenantTable(); + String allUserTable = Config.getConfig(start).getUsersTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + emailPasswordUserToTenantTable + " ( " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " user_id CHAR(36) NOT NULL, " + + " email VARCHAR(256) NOT NULL, " + + " CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, + "email", "key") + + " UNIQUE (app_id, tenant_id, email), " + + " CONSTRAINT " + Utils.getConstraintName(schema, + emailPasswordUserToTenantTable, null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "user_id", "pkey") + + " FOREIGN KEY (app_id, tenant_id, user_id) " + + " REFERENCES " + allUserTable + + " (app_id, tenant_id, user_id) ON DELETE CASCADE " + + ");"); + + statement.execute("ALTER TABLE " + emailPasswordUserToTenantTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + emailPasswordUserToTenantTable, "email", "key") + ";"); + + statement.execute("ALTER TABLE " + emailPasswordUserToTenantTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "email", "key") + + " UNIQUE (app_id, tenant_id, email);"); + + statement.execute("ALTER TABLE " + emailPasswordUserToTenantTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + emailPasswordUserToTenantTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id, user_id) " + + " REFERENCES " + allUserTable + + " (app_id, tenant_id, user_id) ON DELETE CASCADE;"); + + + statement.execute("INSERT INTO " + emailPasswordUserToTenantTable + + " (user_id, email) " + + " SELECT user_id, email FROM " + emailPasswordUsersTable + + " ON CONFLICT DO NOTHING;"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id, token) + FK to emailPasswordUsersTable + */ + private void migratePasswordResetTokensTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String emailPasswordUsersTable = Config.getConfig(start).getEmailPasswordUsersTable(); + String passwordResetTokensTable = Config.getConfig(start).getPasswordResetTokensTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + passwordResetTokensTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id, token);"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, passwordResetTokensTable, + "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + emailPasswordUsersTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS emailpassword_pswd_reset_tokens_user_id_index ON " + + passwordResetTokensTable + " (app_id, user_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS emailpassword_password_reset_token_expiry_index ON " + + passwordResetTokensTable + " (token_expiry);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id, token) + FK to appIdToUserIdTable + */ + private void migratePasswordlessUsersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String passwordlessUsersTable = Config.getConfig(start).getPasswordlessUsersTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUsersTable, + null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUsersTable, + null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordlessUsersTable, "email", "key") + ";"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, passwordlessUsersTable, "phone_number", "key") + ";"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, passwordlessUsersTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordlessUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUsersTable, + "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + } + } + + /* + Created with V3 + */ + private void migratePasswordlessUserToTenantTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String passwordlessUsersTable = Config.getConfig(start).getPasswordlessUsersTable(); + String passwordlessUserToTenantTable = Config.getConfig(start).getPasswordlessUserToTenantTable(); + String allUserTable = Config.getConfig(start).getUsersTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + passwordlessUserToTenantTable+ " ( " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " user_id CHAR(36) NOT NULL, " + + " email VARCHAR(256), " + + " phone_number VARCHAR(256), " + + " CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, + "email", "key") + + " UNIQUE (app_id, tenant_id, email), " + + " CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, + "phone_number", "key") + + " UNIQUE (app_id, tenant_id, phone_number), " + + " CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, + null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, + "user_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id, user_id) " + + " REFERENCES " + allUserTable + + " (app_id, tenant_id, user_id) ON DELETE CASCADE " + + ");"); + + statement.execute("ALTER TABLE " + passwordlessUserToTenantTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordlessUserToTenantTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordlessUserToTenantTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id, user_id) " + + " REFERENCES " + allUserTable + + " (app_id, tenant_id, user_id) ON DELETE CASCADE;"); + + statement.execute("INSERT INTO " + passwordlessUserToTenantTable+ + " (user_id, email, phone_number) " + + " SELECT user_id, email, phone_number FROM " + + passwordlessUsersTable + + " ON CONFLICT DO NOTHING;"); + } + } + + /* + - adding columns: app_id,tenant_id + - primary key session_handle => (app_id, tenant_id, device_id_hash) + FK to tenantTable + */ + private void migratePasswordlessDevicesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String passwordlessDevicesTable = Config.getConfig(start).getPasswordlessDevicesTable(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + passwordlessDevicesTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + passwordlessDevicesTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, passwordlessDevicesTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + passwordlessDevicesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessDevicesTable, + null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, device_id_hash);"); + + statement.execute("ALTER TABLE " + passwordlessDevicesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordlessDevicesTable, "tenant_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordlessDevicesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessDevicesTable, + "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) " + + " REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS passwordless_devices_email_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS passwordless_devices_email_index ON " + + passwordlessDevicesTable + " (app_id, tenant_id, email);"); + + statement.execute("DROP INDEX IF EXISTS passwordless_devices_phone_number_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS passwordless_devices_phone_number_index ON " + + passwordlessDevicesTable + " (app_id, tenant_id, phone_number);"); + + statement.execute("CREATE INDEX IF NOT EXISTS passwordless_devices_tenant_id_index ON " + + passwordlessDevicesTable + " (app_id, tenant_id);"); + } + } + + /* + - adding columns: app_id,tenant_id + - primary key session_handle => (app_id, tenant_id, code_id) + FK to passwordlessDevicesTable + */ + private void migratePasswordlessCodesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String passwordlessDevicesTable = Config.getConfig(start).getPasswordlessDevicesTable(); + String passwordlessCodesTable = Config.getConfig(start).getPasswordlessCodesTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " DROP CONSTRAINT " + Utils.getConstraintName(schema, passwordlessCodesTable, + null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessCodesTable, + null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, code_id);"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordlessCodesTable, "device_id_hash", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessCodesTable, + "device_id_hash", "fkey") + + " FOREIGN KEY (app_id, tenant_id, device_id_hash) " + + " REFERENCES " + passwordlessDevicesTable + + " (app_id, tenant_id, device_id_hash) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " DROP CONSTRAINT " + Utils.getConstraintName(schema, passwordlessCodesTable, + "link_code_hash", "key") + ";"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordlessCodesTable, "link_code_hash", "key") + ";"); + + statement.execute("ALTER TABLE " + passwordlessCodesTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, passwordlessCodesTable, + "link_code_hash", "key") + + " UNIQUE (app_id, tenant_id, link_code_hash);"); + + statement.execute("DROP INDEX IF EXISTS passwordless_codes_created_at_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS passwordless_codes_created_at_index ON " + + passwordlessCodesTable+ " (app_id, tenant_id, created_at);"); + + statement.execute("DROP INDEX IF EXISTS passwordless_codes_device_id_hash_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS passwordless_codes_device_id_hash_index ON " + + passwordlessCodesTable+ " (app_id, tenant_id, device_id_hash);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id) + FK to appIdToUserIdTable + */ + private void migrateThirdPartyUsersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String thirdPartyUsersTable = Config.getConfig(start).getThirdPartyUsersTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, + null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, thirdPartyUsersTable, "user_id", "key") + " CASCADE;"); + + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, + null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, thirdPartyUsersTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + thirdPartyUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, + "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS thirdparty_users_thirdparty_user_id_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS thirdparty_users_thirdparty_user_id_index ON " + + thirdPartyUsersTable + + " (app_id, third_party_id, third_party_user_id);"); + + statement.execute("DROP INDEX IF EXISTS thirdparty_users_email_index;"); + statement.execute("CREATE INDEX IF NOT EXISTS thirdparty_users_email_index ON " + + thirdPartyUsersTable + " (app_id, email);"); + } + } + + /* + Created with V3 + */ + private void migrateThirdPartyUserToTenantTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String thirdPartyUsersTable = Config.getConfig(start).getThirdPartyUsersTable(); + String thirdPartyUserToTenantTable = Config.getConfig(start).getThirdPartyUserToTenantTable(); + String allUserTable = Config.getConfig(start).getUsersTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE TABLE IF NOT EXISTS " + thirdPartyUserToTenantTable + + " ( " + + " app_id VARCHAR(64) DEFAULT 'public', " + + " tenant_id VARCHAR(64) DEFAULT 'public', " + + " user_id CHAR(36) NOT NULL, " + + " third_party_id VARCHAR(28) NOT NULL, " + + " third_party_user_id VARCHAR(256) NOT NULL, " + + " CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, + "third_party_user_id", "key") + + " UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, + null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id), " + + " CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, + "user_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id, user_id) " + + " REFERENCES " + allUserTable + + " (app_id, tenant_id, user_id) ON DELETE CASCADE " + + ");"); + + statement.execute("ALTER TABLE " + thirdPartyUserToTenantTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + thirdPartyUserToTenantTable, "third_party_user_id", "key") + ";"); + + statement.execute("ALTER TABLE " + thirdPartyUserToTenantTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + thirdPartyUserToTenantTable, "third_party_user_id", "key") + + " UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id);"); + + statement.execute("ALTER TABLE " + thirdPartyUserToTenantTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + thirdPartyUserToTenantTable, null, "fkey") + ";"); + + statement.execute("INSERT INTO " + thirdPartyUserToTenantTable + + " (user_id, third_party_id, third_party_user_id) " + + " SELECT user_id, third_party_id, third_party_user_id FROM " + + thirdPartyUsersTable + " ON CONFLICT DO NOTHING;"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, supertokens_user_id, external_user_id) + FK to appIdToUserIdTable + */ + private void migrateUserIdMappingTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String userIdMappingTable = Config.getConfig(start).getUserIdMappingTable(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + userIdMappingTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userIdMappingTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userIdMappingTable, null, "pkey") + + " PRIMARY KEY (app_id, supertokens_user_id, external_user_id);"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userIdMappingTable, "supertokens_user_id", "key") + ";"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userIdMappingTable, "supertokens_user_id", "key") + + " UNIQUE (app_id, supertokens_user_id);"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userIdMappingTable, "external_user_id", "key") + ";"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userIdMappingTable, "external_user_id", "key") + + " UNIQUE (app_id, external_user_id);"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userIdMappingTable, "supertokens_user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + userIdMappingTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userIdMappingTable, "supertokens_user_id", "fkey") + + " FOREIGN KEY (app_id, supertokens_user_id) " + + " REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS userid_mapping_supertokens_user_id_index ON " + + userIdMappingTable + " (app_id, supertokens_user_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, role) + FK to appsTable + */ + private void migrateRolesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String rolesTable = Config.getConfig(start).getRolesTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + rolesTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + rolesTable + + " DROP CONSTRAINT " + Utils.getConstraintName(schema, + rolesTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + rolesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + rolesTable, null, "pkey") + + " PRIMARY KEY (app_id, role);"); + + statement.execute("ALTER TABLE " + rolesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + rolesTable, "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + rolesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + rolesTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) " + + " REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS roles_app_id_index ON " + + rolesTable + " (app_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, role, permission) + FK to rolesTable + */ + private void migrateUserRolesPermissionsTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String rolesTable = Config.getConfig(start).getRolesTable(); + String userRolesPermissionsTable = Config.getConfig(start).getUserRolesPermissionsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + userRolesPermissionsTable+ + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + userRolesPermissionsTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userRolesPermissionsTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + userRolesPermissionsTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userRolesPermissionsTable, null, "pkey") + + " PRIMARY KEY (app_id, role, permission);"); + + statement.execute("ALTER TABLE " + userRolesPermissionsTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userRolesPermissionsTable, "role", "fkey") + ";"); + + statement.execute("ALTER TABLE " + userRolesPermissionsTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userRolesPermissionsTable, "role", "fkey") + + " FOREIGN KEY (app_id, role) " + + " REFERENCES " + rolesTable + + " (app_id, role) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS role_permissions_permission_index;"); + + statement.execute("CREATE INDEX IF NOT EXISTS role_permissions_permission_index ON " + + userRolesPermissionsTable+ " (app_id, permission);"); + + statement.execute("CREATE INDEX IF NOT EXISTS role_permissions_role_index ON " + + userRolesPermissionsTable+ " (app_id, role);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id, tenant_id, user_id, role) + FK to tenantsTable, rolesTable + */ + private void migrateUserRolesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String rolesTable = Config.getConfig(start).getRolesTable(); + String UserRolesTable = Config.getConfig(start).getUserRolesTable(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + UserRolesTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + "ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + UserRolesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + UserRolesTable, null, "pkey") + " CASCADE;"); + + + statement.execute("ALTER TABLE " + UserRolesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + UserRolesTable, null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id, role);"); + + + statement.execute("ALTER TABLE " + UserRolesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + UserRolesTable, "tenant_id", "fkey") + ";"); + + + statement.execute("ALTER TABLE " + UserRolesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + UserRolesTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) " + + " REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + UserRolesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + UserRolesTable, "role", "fkey") + ";"); + + statement.execute("ALTER TABLE " + UserRolesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + UserRolesTable, "role", "fkey") + + " FOREIGN KEY (app_id, role) " + + " REFERENCES " + rolesTable + + " (app_id, role) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS user_roles_role_index;"); + + statement.execute("CREATE INDEX IF NOT EXISTS user_roles_role_index ON " + + UserRolesTable + " (app_id, tenant_id, role);"); + + statement.execute("CREATE INDEX IF NOT EXISTS user_roles_tenant_id_index ON " + + UserRolesTable + " (app_id, tenant_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS user_roles_app_id_role_index ON " + + UserRolesTable + " (app_id, role);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id)) + FK to appsTable + */ + private void migrateUserMetadataTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String userMetadataTable = Config.getConfig(start).getUserMetadataTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + userMetadataTable+ + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + //todo + statement.execute("ALTER TABLE " + userMetadataTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userMetadataTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + userMetadataTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userMetadataTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + userMetadataTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userMetadataTable, "app_id", "fkey") + ";"); + + + statement.execute("ALTER TABLE " + userMetadataTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userMetadataTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) " + + " REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS user_metadata_app_id_index ON " + + userMetadataTable+ " (app_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id)) + FK to appsTable + */ + private void migrateDashboardUsersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String dashboardUsersTable = Config.getConfig(start).getDashboardUsersTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + dashboardUsersTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + dashboardUsersTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + dashboardUsersTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + dashboardUsersTable, "email", "key") + ";"); + + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + dashboardUsersTable, "email", "key") + + " UNIQUE (app_id, email);"); + + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + dashboardUsersTable, "app_id", "fkey") + ";"); + + + statement.execute("ALTER TABLE " + dashboardUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + dashboardUsersTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) " + + " REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS dashboard_users_app_id_index ON " + + dashboardUsersTable + " (app_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, session_id) + FK to dashboardUsersTable + */ + private void migrateDashboardSessionsTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String dashboardUsersTable = Config.getConfig(start).getDashboardUsersTable(); + String dashboardSessionsTable = Config.getConfig(start).getDashboardSessionsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + dashboardSessionsTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + dashboardSessionsTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + dashboardSessionsTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + dashboardSessionsTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + dashboardSessionsTable, null, "pkey") + + " PRIMARY KEY (app_id, session_id);"); + + statement.execute("ALTER TABLE " + dashboardSessionsTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + dashboardSessionsTable, "user_id", "fkey") + ";"); + + + statement.execute("ALTER TABLE " + dashboardSessionsTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + dashboardSessionsTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + dashboardUsersTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + + statement.execute("CREATE INDEX IF NOT EXISTS dashboard_user_sessions_user_id_index ON " + + dashboardSessionsTable + " (app_id, user_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS dashboard_user_sessions_expiry_index ON " + + dashboardSessionsTable + " (expiry);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id) + FK to appsTable + */ + private void migrateTotpUsersTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String totpUsersTable = Config.getConfig(start).getTotpUsersTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + totpUsersTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + totpUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUsersTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + totpUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUsersTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + totpUsersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUsersTable, "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + totpUsersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUsersTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) " + + " REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS totp_users_app_id_index ON " + + totpUsersTable + " (app_id);"); + } + } + + /* + - adding columns: app_id + - primary key session_handle => (app_id, user_id, device_name) + FK to totpUsersTable + */ + private void migrateTotpUserDevicesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String totpUsersTable = Config.getConfig(start).getTotpUsersTable(); + String totpUserDevicesTable = Config.getConfig(start).getTotpUserDevicesTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + totpUserDevicesTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + totpUserDevicesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUserDevicesTable, null, "pkey") + ";"); + + + statement.execute("ALTER TABLE " + totpUserDevicesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUserDevicesTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id, device_name);"); + + + statement.execute("ALTER TABLE " + totpUserDevicesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUserDevicesTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + totpUserDevicesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUserDevicesTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + totpUsersTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("CREATE INDEX IF NOT EXISTS totp_user_devices_user_id_index ON " + + totpUserDevicesTable + " (app_id, user_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id, tenant_id, user_id, created_time_ms) + FK to totpUsersTable,tenantsTable + */ + private void migrateTotpUsedCodesTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String totpUsersTable = Config.getConfig(start).getTotpUsersTable(); + String totpUsedCodesTable = Config.getConfig(start).getTotpUsedCodesTable(); + String tenantsTable = Config.getConfig(start).getTenantsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public', " + + " ADD COLUMN IF NOT EXISTS tenant_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUsedCodesTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUsedCodesTable, null, "pkey") + + " PRIMARY KEY (app_id, tenant_id, user_id, created_time_ms);"); + + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUsedCodesTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + totpUsersTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + totpUsedCodesTable, "tenant_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + totpUsedCodesTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + totpUsedCodesTable, "tenant_id", "fkey") + + " FOREIGN KEY (app_id, tenant_id) " + + " REFERENCES " + tenantsTable + + " (app_id, tenant_id) ON DELETE CASCADE;"); + + statement.execute("DROP INDEX IF EXISTS totp_used_codes_expiry_time_ms_index;"); + + statement.execute("CREATE INDEX IF NOT EXISTS totp_used_codes_expiry_time_ms_index ON " + + totpUsedCodesTable + " (app_id, tenant_id, expiry_time_ms);"); + + statement.execute("CREATE INDEX IF NOT EXISTS totp_used_codes_user_id_index ON " + + totpUsedCodesTable + " (app_id, user_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS totp_used_codes_tenant_id_index ON " + + totpUsedCodesTable + " (app_id, tenant_id);"); + } + } + + /* + - adding columns: app_id, tenant_id + - primary key session_handle => (app_id, user_id) + FK to appsTable + */ + private void migrateUserLastActiveTable(Context context, Start start) throws SQLException { + String schema = Config.getConfig(start).getTableSchema(); + String userLastActiveTable = Config.getConfig(start).getUserLastActiveTable(); + String appsTable = Config.getConfig(start).getAppsTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + userLastActiveTable + + " ADD COLUMN IF NOT EXISTS app_id VARCHAR(64) DEFAULT 'public';"); + + statement.execute("ALTER TABLE " + userLastActiveTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userLastActiveTable, null, "pkey") + " CASCADE;"); + + statement.execute("ALTER TABLE " + userLastActiveTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userLastActiveTable, null, "pkey") + + " PRIMARY KEY (app_id, user_id);"); + + statement.execute("ALTER TABLE " + userLastActiveTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + userLastActiveTable, "app_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + userLastActiveTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + userLastActiveTable, "app_id", "fkey") + + " FOREIGN KEY (app_id) " + + " REFERENCES " + appsTable + + " (app_id) ON DELETE CASCADE;"); + + + statement.execute("CREATE INDEX IF NOT EXISTS user_last_active_app_id_index ON " + + userLastActiveTable + " (app_id);"); + } + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V4__plugin_version_5_0_0.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V4__plugin_version_5_0_0.java new file mode 100644 index 00000000..fd265269 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V4__plugin_version_5_0_0.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import io.supertokens.storage.postgresql.utils.Utils; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; + +import java.sql.Statement; +import java.util.Map; + +public class V4__plugin_version_5_0_0 extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + String usersTable = Config.getConfig(start).getUsersTable(); + String schema = Config.getConfig(start).getTableSchema(); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + String passwordResetTokensTable = Config.getConfig(start).getPasswordResetTokensTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("ALTER TABLE " + usersTable + + " ADD COLUMN IF NOT EXISTS primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT '0';"); + + statement.execute("ALTER TABLE " + usersTable + + " ADD COLUMN IF NOT EXISTS is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE;"); + + statement.execute("ALTER TABLE " + usersTable + + " ADD COLUMN IF NOT EXISTS primary_or_recipe_user_time_joined BIGINT NOT NULL DEFAULT 0;"); + + statement.execute("UPDATE " + usersTable + + " SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0';"); + + statement.execute("UPDATE " + usersTable + + " SET primary_or_recipe_user_time_joined = time_joined WHERE primary_or_recipe_user_time_joined = 0;"); + + statement.execute("ALTER TABLE " + usersTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + usersTable, "primary_or_recipe_user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + usersTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + usersTable, "primary_or_recipe_user_id", "fkey") + " FOREIGN KEY " + + "(app_id,primary_or_recipe_user_id) " + + "REFERENCES " + appIdToUserIdTable + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + usersTable + + " ALTER primary_or_recipe_user_id DROP DEFAULT;"); + + statement.execute("ALTER TABLE " + appIdToUserIdTable + + " ADD COLUMN IF NOT EXISTS primary_or_recipe_user_id CHAR(36) NOT NULL DEFAULT '0';"); + + statement.execute("ALTER TABLE " + appIdToUserIdTable + + " ADD COLUMN IF NOT EXISTS is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE;"); + + statement.execute("UPDATE " + appIdToUserIdTable + + " SET primary_or_recipe_user_id = user_id WHERE primary_or_recipe_user_id = '0';"); + + statement.execute("ALTER TABLE " + appIdToUserIdTable + + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + appIdToUserIdTable , "primary_or_recipe_user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + appIdToUserIdTable + + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + appIdToUserIdTable, "primary_or_recipe_user_id", "fkey") + " FOREIGN KEY " + + "(app_id,primary_or_recipe_user_id) " + + "REFERENCES " + appIdToUserIdTable + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + appIdToUserIdTable + + " ALTER primary_or_recipe_user_id DROP DEFAULT;"); + + statement.execute("DROP INDEX IF EXISTS all_auth_recipe_users_pagination_index;"); + + statement.execute( + "CREATE INDEX IF NOT EXISTS all_auth_recipe_users_pagination_index1 ON " + usersTable + + " (app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_users_pagination_index2 ON " + usersTable + + " (app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_users_pagination_index3 ON " + usersTable + + " (recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_users_pagination_index4 ON " + usersTable + + " (recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_users_primary_user_id_index ON " + usersTable + + " (primary_or_recipe_user_id, app_id);"); + + statement.execute("CREATE INDEX IF NOT EXISTS all_auth_recipe_users_recipe_id_index ON " + usersTable + + " (app_id, recipe_id, tenant_id);"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable+ + " DROP CONSTRAINT IF EXISTS " + Utils.getConstraintName(schema, + passwordResetTokensTable, "user_id", "fkey") + ";"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable+ + " ADD CONSTRAINT " + Utils.getConstraintName(schema, + passwordResetTokensTable, "user_id", "fkey") + + " FOREIGN KEY (app_id, user_id) " + + " REFERENCES " + appIdToUserIdTable + + " (app_id, user_id) ON DELETE CASCADE;"); + + statement.execute("ALTER TABLE " + passwordResetTokensTable+ + " ADD COLUMN IF NOT EXISTS email VARCHAR(256);"); + + + + } + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V5__plugin_version_5_0_4.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V5__plugin_version_5_0_4.java new file mode 100644 index 00000000..129c2827 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V5__plugin_version_5_0_4.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import java.sql.Statement; +import java.util.Map; + +public class V5__plugin_version_5_0_4 extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + String appIdToUserIdTable = Config.getConfig(start).getAppIdToUserIdTable(); + + try (Statement statement = context.getConnection().createStatement()) { + statement.execute("CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON " + + appIdToUserIdTable + "(primary_or_recipe_user_id, app_id);"); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/storage/postgresql/migrations/V6__core_version_7_0_12.java b/src/main/java/io/supertokens/storage/postgresql/migrations/V6__core_version_7_0_12.java new file mode 100644 index 00000000..fd4f5e36 --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/migrations/V6__core_version_7_0_12.java @@ -0,0 +1,96 @@ +package io.supertokens.storage.postgresql.migrations; + +import io.supertokens.storage.postgresql.MigrationContextManager; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import org.flywaydb.core.api.migration.BaseJavaMigration; +import org.flywaydb.core.api.migration.Context; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; + +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; + +public class V6__core_version_7_0_12 extends BaseJavaMigration { + + @Override + public void migrate(Context context) throws Exception { + try { + Connection connection = context.getConnection(); + Map ph = context.getConfiguration().getPlaceholders(); + Start start = MigrationContextManager.getContext(ph.get("process_id")); + String passwordlessUsersTable = Config.getConfig(start).getPasswordlessUsersTable(); + String passwordlessDevicesTable = Config.getConfig(start).getPasswordlessDevicesTable(); + + updatePhoneNumbers(connection, passwordlessUsersTable); + updatePhoneNumbers(connection, passwordlessDevicesTable); + } catch (SQLException e) { + throw e; + } + } + + private void updatePhoneNumbers(Connection connection, String table) throws SQLException { + final int batchSize = 1000; + int offset = 0; + + while (true) { + // Create a PreparedStatement for fetching rows + PreparedStatement selectStatement = connection.prepareStatement( + String.format("SELECT * FROM %s WHERE phone_number IS NOT NULL LIMIT ? OFFSET ?", table)); + selectStatement.setInt(1, batchSize); + selectStatement.setInt(2, offset); + + // Execute the query to get rows + ResultSet resultSet = selectStatement.executeQuery(); + + while (resultSet.next()) { + // Get the current phone number from the result set + String currentPhoneNumber = resultSet.getString("phone_number"); + + // Normalize the phone number + String normalizedPhoneNumber = getNormalizedPhoneNumber(currentPhoneNumber); + + // Check if normalization is successful and if there's a change in the phone number + if (normalizedPhoneNumber != null && !normalizedPhoneNumber.equals(currentPhoneNumber)) { + // Create a PreparedStatement for updating rows + PreparedStatement updateStatement = connection.prepareStatement( + String.format("UPDATE %s SET phone_number = ? WHERE app_id = ? AND tenant_id = ? AND device_id_hash = ?", table)); + + // Set parameters for the update statement + updateStatement.setString(1, normalizedPhoneNumber); + updateStatement.setString(2, resultSet.getString("app_id")); + updateStatement.setString(3, resultSet.getString("tenant_id")); + updateStatement.setString(4, resultSet.getString("device_id_hash")); + + // Execute the update query + updateStatement.executeUpdate(); + } + } + + // Increment the offset for the next batch + offset += batchSize; + + // Break the loop if we have processed all rows + if (!resultSet.isLast()) { + break; + } + } + } + + private String getNormalizedPhoneNumber(String phoneNumber) { + try { + PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + Phonenumber.PhoneNumber parsedPhoneNumber = phoneNumberUtil.parse(phoneNumber, "US"); + if (phoneNumberUtil.isValidNumber(parsedPhoneNumber)) { + return phoneNumberUtil.format(parsedPhoneNumber, PhoneNumberUtil.PhoneNumberFormat.E164); + } + } catch (NumberParseException e) { + //Not sure if this should fail silently for migration to continue, or should halt the migration. + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java index d40c08f1..7dae5c98 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java @@ -14,23 +14,9 @@ public class ActiveUsersQueries { static String getQueryToCreateUserLastActiveTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - return "CREATE TABLE IF NOT EXISTS " + Config.getConfig(start).getUserLastActiveTable() + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128)," - + "last_active_time BIGINT," - + "PRIMARY KEY(app_id, user_id)," - + "CONSTRAINT " + - Utils.getConstraintName(schema, Config.getConfig(start).getUserLastActiveTable(), "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; - } - - static String getQueryToCreateAppIdIndexForUserLastActiveTable(Start start) { - return "CREATE INDEX IF NOT EXISTS user_last_active_app_id_index ON " - + Config.getConfig(start).getUserLastActiveTable() + "(app_id);"; + + "last_active_time BIGINT," + "PRIMARY KEY(user_id)" + " );"; } public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime) diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/BaselineMigrationQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/BaselineMigrationQueries.java new file mode 100644 index 00000000..7de1fb6d --- /dev/null +++ b/src/main/java/io/supertokens/storage/postgresql/queries/BaselineMigrationQueries.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.queries; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storage.postgresql.config.Config; +import org.postgresql.util.PSQLException; +import java.sql.SQLException; +import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute; +import static io.supertokens.storage.postgresql.config.Config.getConfig; + +public class BaselineMigrationQueries { + /* To verify if a migration was done, we are verifying for creation of INDEXES, COLUMNS and TABLES. We do this by + counting. If the result is "1" => the migration is done. "0" => migration not done + */ + private static final String MIGRATION_EXISTS = "1"; + // This means Flyway is free to migrate starting with first migration. + private static final String NO_BASELINE_FOUND = "0"; + public static final int LAST_MIGRATION = 5; + private static final int FIRST_MIGRATION = 1; + /* + We are dealing with existing users who have already performed manual database migration, so it's important to + handle the situation carefully to avoid issues and data inconsistencies. To accomplish this, we need to ascertain + the migration version that the user has manually executed. This will serve as the baseline for our subsequent Flyway migration. + */ + public static String getBaselineMigrationVersion(Start start) + throws SQLException, StorageQueryException { + for (int migrationVersion = LAST_MIGRATION; migrationVersion >= FIRST_MIGRATION; migrationVersion--) { + try { + if (checkMigration(start, migrationVersion).equals(MIGRATION_EXISTS)) { + return String.valueOf(migrationVersion); + } + } catch (PSQLException e) { + // If the database is empty some checkMigration script will throw an exception because if table does not + // exist, the column can't exist either. + } + } + return NO_BASELINE_FOUND; + } + + private static String checkMigration(Start start, int migrationVersion) throws SQLException, StorageQueryException { + return execute(start, getQueryToCheckForMigration(start, migrationVersion), null, result -> { + result.next(); + return result.getString("MIGRATION_EXISTS"); + }); + } + + private static String getQueryToCheckForMigration(Start start, int migrationVersion) { + switch (migrationVersion) { + case 5: + return getQueryToCheckForMigrationV5(start); + case 4: + return getQueryToCheckForMigrationV4(start); + case 3: + return getQueryToCheckForMigrationV3(start); + case 2: + return getQueryToCheckForMigrationV2(start); + case 1: + return getQueryToCheckForMigrationV1(start); + default: + throw new IllegalArgumentException("Unknown migration version: " + migrationVersion); + } + } + + private static String getQueryToCheckForMigrationV1(Start start) { + return "SELECT COUNT(*) AS MIGRATION_EXISTS FROM information_schema.tables" + + " WHERE table_name =" + getConfig(start).getKeyValueTable(); + } + + private static String getQueryToCheckForMigrationV2(Start start) { + return "SELECT COUNT(*) AS MIGRATION_EXISTS FROM information_schema.columns" + + " WHERE table_name = " + Config.getConfig(start).getSessionInfoTable() + + " AND column_name = 'use_static_key';"; + } + + private static String getQueryToCheckForMigrationV3(Start start) { + return "SELECT COUNT(*) AS MIGRATION_EXISTS FROM information_schema.tables" + + " WHERE table_name =" +Config.getConfig(start).getTenantsTable(); + } + + private static String getQueryToCheckForMigrationV4(Start start) { + return "SELECT COUNT(*) AS MIGRATION_EXISTS FROM information_schema.columns" + + " WHERE table_name =" + Config.getConfig(start).getPasswordResetTokensTable() + + " AND column_name = 'email';"; + } + + private static String getQueryToCheckForMigrationV5(Start start) { + return "SELECT COUNT(*) AS MIGRATION_EXISTS FROM pg_indexes WHERE indexname = " + + "'app_id_to_user_id_primary_user_id_index'"; + } +} diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java index 135d2f7a..14c79b3e 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java @@ -40,56 +40,31 @@ public static String getQueryToCreateDashboardUsersTable(Start start) { String dashboardUsersTable = Config.getConfig(start).getDashboardUsersTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + dashboardUsersTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," - + "email VARCHAR(256) NOT NULL," - + "password_hash VARCHAR(256) NOT NULL," - + "time_joined BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, dashboardUsersTable, "email", "key") - + " UNIQUE (app_id, email)," - + "CONSTRAINT " + Utils.getConstraintName(schema, dashboardUsersTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, dashboardUsersTable, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; + + "email VARCHAR(256) NOT NULL CONSTRAINT " + + Utils.getConstraintName(schema, dashboardUsersTable, "email", "key") + " UNIQUE," + + "password_hash VARCHAR(256) NOT NULL," + "time_joined BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, dashboardUsersTable, null, "pkey") + + " PRIMARY KEY (user_id));"; // @formatter:on } - public static String getQueryToCreateAppIdIndexForDashboardUsersTable(Start start) { - return "CREATE INDEX dashboard_users_app_id_index ON " - + Config.getConfig(start).getDashboardUsersTable() + "(app_id);"; - } - public static String getQueryToCreateDashboardUserSessionsTable(Start start) { String schema = Config.getConfig(start).getTableSchema(); String tableName = Config.getConfig(start).getDashboardSessionsTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "session_id CHAR(36) NOT NULL," + "user_id CHAR(36) NOT NULL," + "time_created BIGINT NOT NULL," + "expiry BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY(app_id, session_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "user_id", "fkey") - + " FOREIGN KEY (app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getDashboardUsersTable() + "(app_id, user_id)" - + " ON DELETE CASCADE ON UPDATE CASCADE);"; + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(session_id)," + + ("CONSTRAINT " + Utils.getConstraintName(schema, tableName, "user_id", "fkey") + " FOREIGN KEY (user_id)" + + " REFERENCES " + Config.getConfig(start).getDashboardUsersTable() + "(user_id)" + + " ON DELETE CASCADE ON UPDATE CASCADE);"); // @formatter:on } - static String getQueryToCreateDashboardUserSessionsExpiryIndex(Start start) { - return "CREATE INDEX dashboard_user_sessions_expiry_index ON " - + Config.getConfig(start).getDashboardSessionsTable() + "(expiry);"; - } - - public static String getQueryToCreateUserIdIndexForDashboardUserSessionsTable(Start start) { - return "CREATE INDEX dashboard_user_sessions_user_id_index ON " - + Config.getConfig(start).getDashboardSessionsTable() + "(app_id, user_id);"; - } - public static void createDashboardUser(Start start, AppIdentifier appIdentifier, String userId, String email, String passwordHash, long timeJoined) throws SQLException, StorageQueryException { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java index 55bb51c4..e8ee9213 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java @@ -45,78 +45,32 @@ public class EmailPasswordQueries { static String getQueryToCreateUsersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String emailPasswordUsersTable = Config.getConfig(start).getEmailPasswordUsersTable(); + String schema = getConfig(start).getTableSchema(); + String emailPasswordUsersTable = getConfig(start).getEmailPasswordUsersTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + emailPasswordUsersTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," - + "email VARCHAR(256) NOT NULL," - + "password_hash VARCHAR(256) NOT NULL," - + "time_joined BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, "user_id", "fkey") - + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + - " (app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id)" - + ");"; - // @formatter:on - } - - static String getQueryToCreateEmailPasswordUserToTenantTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String emailPasswordUserToTenantTable = Config.getConfig(start).getEmailPasswordUserToTenantTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + emailPasswordUserToTenantTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "user_id CHAR(36) NOT NULL," - + "email VARCHAR(256) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "email", "key") - + " UNIQUE (app_id, tenant_id, email)," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUserToTenantTable, "user_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + - "(app_id, tenant_id, user_id) ON DELETE CASCADE" - + ");"; + + "email VARCHAR(256) NOT NULL CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, "email", "key") + " UNIQUE," + + "password_hash VARCHAR(128) NOT NULL," + "time_joined BIGINT NOT NULL," + + "CONSTRAINT " + Utils.getConstraintName(schema, emailPasswordUsersTable, null, "pkey") + " PRIMARY KEY (user_id));"; // @formatter:on } static String getQueryToCreatePasswordResetTokensTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String passwordResetTokensTable = Config.getConfig(start).getPasswordResetTokensTable(); + String schema = getConfig(start).getTableSchema(); + String passwordResetTokensTable = getConfig(start).getPasswordResetTokensTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + passwordResetTokensTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," - + "token VARCHAR(128) NOT NULL" - + " CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "token", "key") + - " UNIQUE," - + "email VARCHAR(256)," // nullable cause of backwards compatibility. + + "token VARCHAR(128) NOT NULL CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "token", "key") + " UNIQUE," + "token_expiry BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id, token)," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "user_id", "fkey") - + " FOREIGN KEY (app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" - + " ON DELETE CASCADE ON UPDATE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, null, "pkey") + " PRIMARY KEY (user_id, token)," + + ("CONSTRAINT " + Utils.getConstraintName(schema, passwordResetTokensTable, "user_id", "fkey") + " FOREIGN KEY (user_id)" + + " REFERENCES " + getConfig(start).getEmailPasswordUsersTable() + "(user_id)" + + " ON DELETE CASCADE ON UPDATE CASCADE);"); // @formatter:on } - public static String getQueryToCreateUserIdIndexForPasswordResetTokensTable(Start start) { - return "CREATE INDEX emailpassword_pswd_reset_tokens_user_id_index ON " - + Config.getConfig(start).getPasswordResetTokensTable() + "(app_id, user_id);"; - } - - static String getQueryToCreatePasswordResetTokenExpiryIndex(Start start) { - return "CREATE INDEX emailpassword_password_reset_token_expiry_index ON " - + Config.getConfig(start).getPasswordResetTokensTable() + "(token_expiry);"; - } - public static void deleteExpiredPasswordResetTokens(Start start) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getPasswordResetTokensTable() + " WHERE token_expiry < ?"; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java index ff9fc950..003acb01 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java @@ -40,58 +40,29 @@ public class EmailVerificationQueries { static String getQueryToCreateEmailVerificationTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String emailVerificationTable = Config.getConfig(start).getEmailVerificationTable(); + String schema = getConfig(start).getTableSchema(); + String emailVerificationTable = getConfig(start).getEmailVerificationTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + emailVerificationTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128) NOT NULL," + "email VARCHAR(256) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id, email)," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTable, null, "pkey") + " PRIMARY KEY (user_id, email));"; // @formatter:on } - public static String getQueryToCreateAppIdIndexForEmailVerificationTable(Start start) { - return "CREATE INDEX emailverification_verified_emails_app_id_index ON " - + Config.getConfig(start).getEmailVerificationTable() + "(app_id);"; - } - static String getQueryToCreateEmailVerificationTokensTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String emailVerificationTokensTable = Config.getConfig(start).getEmailVerificationTokensTable(); + String schema = getConfig(start).getTableSchema(); + String emailVerificationTokensTable = getConfig(start).getEmailVerificationTokensTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + emailVerificationTokensTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128) NOT NULL," + "email VARCHAR(256) NOT NULL," - + "token VARCHAR(128) NOT NULL CONSTRAINT " + - Utils.getConstraintName(schema, emailVerificationTokensTable, "token", "key") + " UNIQUE," + + "token VARCHAR(128) NOT NULL CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, "token", "key") + " UNIQUE," + "token_expiry BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id, email, token), " - + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, "tenant_id", "fkey") - + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE" - + ")"; + + "CONSTRAINT " + Utils.getConstraintName(schema, emailVerificationTokensTable, null, "pkey") + " PRIMARY KEY (user_id, email, token))"; // @formatter:on } - public static String getQueryToCreateTenantIdIndexForEmailVerificationTokensTable(Start start) { - return "CREATE INDEX emailverification_tokens_tenant_id_index ON " - + Config.getConfig(start).getEmailVerificationTokensTable() + "(app_id, tenant_id);"; - } - - static String getQueryToCreateEmailVerificationTokenExpiryIndex(Start start) { - return "CREATE INDEX emailverification_tokens_index ON " - + Config.getConfig(start).getEmailVerificationTokensTable() + "(token_expiry);"; - } - public static void deleteExpiredEmailVerificationTokens(Start start) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getEmailVerificationTokensTable() + " WHERE token_expiry < ?"; diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java index 729b8a51..387299c6 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java @@ -47,16 +47,14 @@ import static io.supertokens.storage.postgresql.config.Config.getConfig; import static io.supertokens.storage.postgresql.queries.EmailPasswordQueries.*; import static io.supertokens.storage.postgresql.queries.EmailVerificationQueries.*; -import static io.supertokens.storage.postgresql.queries.JWTSigningQueries.getQueryToCreateAppIdIndexForJWTSigningTable; import static io.supertokens.storage.postgresql.queries.JWTSigningQueries.getQueryToCreateJWTSigningTable; import static io.supertokens.storage.postgresql.queries.PasswordlessQueries.*; import static io.supertokens.storage.postgresql.queries.SessionQueries.*; -import static io.supertokens.storage.postgresql.queries.UserMetadataQueries.getQueryToCreateAppIdIndexForUserMetadataTable; import static io.supertokens.storage.postgresql.queries.UserMetadataQueries.getQueryToCreateUserMetadataTable; public class GeneralQueries { - private static boolean doesTableExists(Start start, String tableName) { + public static boolean doesTableExists(Start start, String tableName) { try { String QUERY = "SELECT 1 FROM " + tableName + " LIMIT 1"; execute(start, QUERY, NO_OP_SETTER, result -> null); @@ -67,459 +65,154 @@ private static boolean doesTableExists(Start start, String tableName) { } static String getQueryToCreateUsersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String usersTable = Config.getConfig(start).getUsersTable(); + String schema = getConfig(start).getTableSchema(); + String usersTable = getConfig(start).getUsersTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + usersTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + "user_id CHAR(36) NOT NULL," - + "primary_or_recipe_user_id CHAR(36) NOT NULL," - + "is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE," + "recipe_id VARCHAR(128) NOT NULL," + "time_joined BIGINT NOT NULL," - + "primary_or_recipe_user_time_joined BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "tenant_id", "fkey") - + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "primary_or_recipe_user_id", "fkey") - + " FOREIGN KEY(app_id, primary_or_recipe_user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "user_id", "fkey") - + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + - " (app_id, user_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, null, "pkey") + " PRIMARY KEY (user_id));"; // @formatter:on } - public static String getQueryToCreateUserIdIndexForUsersTable(Start start) { - return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON " - + Config.getConfig(start).getUsersTable() + "(app_id, user_id);"; - } - - public static String getQueryToCreateTenantIdIndexForUsersTable(Start start) { - return "CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON " - + Config.getConfig(start).getUsersTable() + "(app_id, tenant_id);"; - } - - static String getQueryToCreateUserPaginationIndex1(Start start) { - return "CREATE INDEX all_auth_recipe_users_pagination_index1 ON " + Config.getConfig(start).getUsersTable() - + "(app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"; - } - - static String getQueryToCreateUserPaginationIndex2(Start start) { - return "CREATE INDEX all_auth_recipe_users_pagination_index2 ON " + Config.getConfig(start).getUsersTable() - + "(app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"; - } - - static String getQueryToCreateUserPaginationIndex3(Start start) { - return "CREATE INDEX all_auth_recipe_users_pagination_index3 ON " + Config.getConfig(start).getUsersTable() - + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);"; - } - - static String getQueryToCreateUserPaginationIndex4(Start start) { - return "CREATE INDEX all_auth_recipe_users_pagination_index4 ON " + Config.getConfig(start).getUsersTable() - + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);"; - } - - static String getQueryToCreatePrimaryUserId(Start start) { - /* - * Used in: - * - does user exist - * */ - return "CREATE INDEX all_auth_recipe_users_primary_user_id_index ON " + - Config.getConfig(start).getUsersTable() - + "(primary_or_recipe_user_id, app_id);"; - } - - static String getQueryToCreateRecipeIdIndex(Start start) { - /* - * Used in: - * - user count query - * */ - return "CREATE INDEX all_auth_recipe_users_recipe_id_index ON " + - Config.getConfig(start).getUsersTable() - + "(app_id, recipe_id, tenant_id);"; - } - - private static String getQueryToCreateAppsTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String appsTable = Config.getConfig(start).getAppsTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + appsTable + " (" - + "app_id VARCHAR(64) NOT NULL DEFAULT 'public'," - + "created_at_time BIGINT ," - + "CONSTRAINT " + Utils.getConstraintName(schema, appsTable, null, "pkey") - + " PRIMARY KEY(app_id)" + - " );"; - // @formatter:on - } - - private static String getQueryToCreateTenantsTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tenantsTable = Config.getConfig(start).getTenantsTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tenantsTable + " (" - + "app_id VARCHAR(64) NOT NULL DEFAULT 'public'," - + "tenant_id VARCHAR(64) NOT NULL DEFAULT 'public'," - + "created_at_time BIGINT ," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantsTable, null, "pkey") + - " PRIMARY KEY(app_id, tenant_id) ," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantsTable, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - static String getQueryToCreateAppIdIndexForTenantsTable(Start start) { - return "CREATE INDEX IF NOT EXISTS tenants_app_id_index ON " - + Config.getConfig(start).getTenantsTable() + "(app_id);"; - } - private static String getQueryToCreateKeyValueTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String keyValueTable = Config.getConfig(start).getKeyValueTable(); + String schema = getConfig(start).getTableSchema(); + String keyValueTable = getConfig(start).getKeyValueTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + keyValueTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + "name VARCHAR(128)," + "value TEXT," + "created_at_time BIGINT ," - + "CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, null, "pkey") - + " PRIMARY KEY(app_id, tenant_id, name)," - + "CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, "tenant_id", "fkey") - + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, keyValueTable, null, "pkey") + " PRIMARY KEY(name)" + " );"; // @formatter:on } - static String getQueryToCreateTenantIdIndexForKeyValueTable(Start start) { - return "CREATE INDEX IF NOT EXISTS key_value_tenant_id_index ON " - + Config.getConfig(start).getKeyValueTable() + "(app_id, tenant_id);"; - } - - private static String getQueryToCreateAppIdToUserIdTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String appToUserTable = Config.getConfig(start).getAppIdToUserIdTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + appToUserTable + " (" - + "app_id VARCHAR(64) NOT NULL DEFAULT 'public'," - + "user_id CHAR(36) NOT NULL," - + "recipe_id VARCHAR(128) NOT NULL," - + "primary_or_recipe_user_id CHAR(36) NOT NULL," - + "is_linked_or_is_a_primary_user BOOLEAN NOT NULL DEFAULT FALSE," - + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id), " - + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "primary_or_recipe_user_id", "fkey") - + " FOREIGN KEY(app_id, primary_or_recipe_user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "app_id", "fkey") - + " FOREIGN KEY(app_id) REFERENCES " + Config.getConfig(start).getAppsTable() - + " (app_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - static String getQueryToCreateAppIdIndexForAppIdToUserIdTable(Start start) { - return "CREATE INDEX IF NOT EXISTS app_id_to_user_id_app_id_index ON " - + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id);"; - } - - static String getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(Start start) { - return "CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON " - + Config.getConfig(start).getAppIdToUserIdTable() + "(primary_or_recipe_user_id, app_id);"; - } - public static void createTablesIfNotExists(Start start) throws SQLException, StorageQueryException { int numberOfRetries = 0; boolean retry = true; while (retry) { retry = false; try { - if (!doesTableExists(start, Config.getConfig(start).getAppsTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, getQueryToCreateAppsTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getTenantsTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, getQueryToCreateTenantsTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateAppIdIndexForTenantsTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getKeyValueTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateKeyValueTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateTenantIdIndexForKeyValueTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getAppIdToUserIdTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, getQueryToCreateAppIdToUserIdTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateAppIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER); - update(start, getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getUsersTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateUsersTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateUserPaginationIndex1(start), NO_OP_SETTER); - update(start, getQueryToCreateUserPaginationIndex2(start), NO_OP_SETTER); - update(start, getQueryToCreateUserPaginationIndex3(start), NO_OP_SETTER); - update(start, getQueryToCreateUserPaginationIndex4(start), NO_OP_SETTER); - update(start, getQueryToCreatePrimaryUserId(start), NO_OP_SETTER); - update(start, getQueryToCreateRecipeIdIndex(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getUserLastActiveTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, ActiveUsersQueries.getQueryToCreateUserLastActiveTable(start), NO_OP_SETTER); - - // Index - update(start, ActiveUsersQueries.getQueryToCreateAppIdIndexForUserLastActiveTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getAccessTokenSigningKeysTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateAccessTokenSigningKeysTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateAppIdIndexForAccessTokenSigningKeysTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getSessionInfoTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateSessionInfoTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateSessionExpiryIndex(start), NO_OP_SETTER); - update(start, getQueryToCreateTenantIdIndexForSessionInfoTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getTenantConfigsTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, MultitenancyQueries.getQueryToCreateTenantConfigsTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getTenantThirdPartyProvidersTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, MultitenancyQueries.getQueryToCreateTenantThirdPartyProvidersTable(start), - NO_OP_SETTER); - - // index - update(start, - MultitenancyQueries.getQueryToCreateTenantIdIndexForTenantThirdPartyProvidersTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getTenantThirdPartyProviderClientsTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, MultitenancyQueries.getQueryToCreateTenantThirdPartyProviderClientsTable(start), - NO_OP_SETTER); - - // index - update(start, - MultitenancyQueries.getQueryToCreateThirdPartyIdIndexForTenantThirdPartyProviderClientsTable( - start), - NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getEmailPasswordUsersTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, EmailPasswordQueries.getQueryToCreateUsersTable(start), NO_OP_SETTER); } + - if (!doesTableExists(start, Config.getConfig(start).getEmailPasswordUserToTenantTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, EmailPasswordQueries.getQueryToCreateEmailPasswordUserToTenantTable(start), - NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getPasswordResetTokensTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreatePasswordResetTokensTable(start), NO_OP_SETTER); - // index - update(start, getQueryToCreatePasswordResetTokenExpiryIndex(start), NO_OP_SETTER); - update(start, getQueryToCreateUserIdIndexForPasswordResetTokensTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getEmailVerificationTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateEmailVerificationTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateAppIdIndexForEmailVerificationTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getEmailVerificationTokensTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateEmailVerificationTokensTable(start), NO_OP_SETTER); - // index - update(start, getQueryToCreateEmailVerificationTokenExpiryIndex(start), NO_OP_SETTER); - update(start, getQueryToCreateTenantIdIndexForEmailVerificationTokensTable(start), NO_OP_SETTER); } - - if (!doesTableExists(start, Config.getConfig(start).getThirdPartyUsersTable())) { + + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, ThirdPartyQueries.getQueryToCreateUsersTable(start), NO_OP_SETTER); - // index - update(start, ThirdPartyQueries.getQueryToThirdPartyUserEmailIndex(start), NO_OP_SETTER); - update(start, ThirdPartyQueries.getQueryToThirdPartyUserIdIndex(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getThirdPartyUserToTenantTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, ThirdPartyQueries.getQueryToCreateThirdPartyUserToTenantTable(start), NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getJWTSigningKeysTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateJWTSigningTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateAppIdIndexForJWTSigningTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getPasswordlessUsersTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, PasswordlessQueries.getQueryToCreateUsersTable(start), NO_OP_SETTER); - - // index - update(start, getQueryToCreateUserIdIndexForUsersTable(start), NO_OP_SETTER); - update(start, getQueryToCreateTenantIdIndexForUsersTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getPasswordlessUserToTenantTable())) { - getInstance(start).addState(CREATING_NEW_TABLE, null); - update(start, PasswordlessQueries.getQueryToCreatePasswordlessUserToTenantTable(start), - NO_OP_SETTER); - } - - if (!doesTableExists(start, Config.getConfig(start).getPasswordlessDevicesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateDevicesTable(start), NO_OP_SETTER); - // index - update(start, getQueryToCreateDeviceEmailIndex(start), NO_OP_SETTER); - update(start, getQueryToCreateDevicePhoneNumberIndex(start), NO_OP_SETTER); - update(start, getQueryToCreateTenantIdIndexForDevicesTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getPasswordlessCodesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateCodesTable(start), NO_OP_SETTER); - // index - update(start, getQueryToCreateCodeCreatedAtIndex(start), NO_OP_SETTER); - } - // This PostgreSQL specific, because it's created automatically in MySQL and it - // doesn't support "create - // index if not exists" - // We missed creating this earlier for the codes table, so it may be missing - // even if the table exists - update(start, getQueryToCreateCodeDeviceIdHashIndex(start), NO_OP_SETTER); - - if (!doesTableExists(start, Config.getConfig(start).getUserMetadataTable())) { + + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, getQueryToCreateUserMetadataTable(start), NO_OP_SETTER); - - // Index - update(start, getQueryToCreateAppIdIndexForUserMetadataTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getRolesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, UserRolesQueries.getQueryToCreateRolesTable(start), NO_OP_SETTER); - - // Index - update(start, UserRolesQueries.getQueryToCreateAppIdIndexForRolesTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getUserRolesPermissionsTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, UserRolesQueries.getQueryToCreateRolePermissionsTable(start), NO_OP_SETTER); - // index - update(start, UserRolesQueries.getQueryToCreateRolePermissionsPermissionIndex(start), NO_OP_SETTER); - update(start, UserRolesQueries.getQueryToCreateRoleIndexForRolePermissionsTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getUserRolesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, UserRolesQueries.getQueryToCreateUserRolesTable(start), NO_OP_SETTER); - - // index - update(start, UserRolesQueries.getQueryToCreateUserRolesRoleIndex(start), NO_OP_SETTER); - update(start, UserRolesQueries.getQueryToCreateTenantIdIndexForUserRolesTable(start), NO_OP_SETTER); - update(start, UserRolesQueries.getQueryToCreateRoleIndexForUserRolesTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getUserIdMappingTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, UserIdMappingQueries.getQueryToCreateUserIdMappingTable(start), NO_OP_SETTER); - - // index - update(start, - UserIdMappingQueries.getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getDashboardUsersTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, DashboardQueries.getQueryToCreateDashboardUsersTable(start), NO_OP_SETTER); - - // Index - update(start, DashboardQueries.getQueryToCreateAppIdIndexForDashboardUsersTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getDashboardSessionsTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, DashboardQueries.getQueryToCreateDashboardUserSessionsTable(start), NO_OP_SETTER); - // index - update(start, DashboardQueries.getQueryToCreateDashboardUserSessionsExpiryIndex(start), - NO_OP_SETTER); - update(start, DashboardQueries.getQueryToCreateUserIdIndexForDashboardUserSessionsTable(start), - NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getTotpUsersTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, TOTPQueries.getQueryToCreateUsersTable(start), NO_OP_SETTER); - - // index - update(start, TOTPQueries.getQueryToCreateAppIdIndexForUsersTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getTotpUserDevicesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, TOTPQueries.getQueryToCreateUserDevicesTable(start), NO_OP_SETTER); - - // index - update(start, TOTPQueries.getQueryToCreateUserIdIndexForUserDevicesTable(start), NO_OP_SETTER); } - if (!doesTableExists(start, Config.getConfig(start).getTotpUsedCodesTable())) { + { getInstance(start).addState(CREATING_NEW_TABLE, null); update(start, TOTPQueries.getQueryToCreateUsedCodesTable(start), NO_OP_SETTER); - // index: - update(start, TOTPQueries.getQueryToCreateUsedCodesExpiryTimeIndex(start), NO_OP_SETTER); - update(start, TOTPQueries.getQueryToCreateUserIdIndexForUsedCodesTable(start), NO_OP_SETTER); - update(start, TOTPQueries.getQueryToCreateTenantIdIndexForUsedCodesTable(start), NO_OP_SETTER); } } catch (Exception e) { @@ -591,7 +284,8 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer + getConfig(start).getDashboardUsersTable() + "," + getConfig(start).getDashboardSessionsTable() + "," + getConfig(start).getTotpUsedCodesTable() + "," + getConfig(start).getTotpUserDevicesTable() + "," - + getConfig(start).getTotpUsersTable(); + + getConfig(start).getTotpUsersTable() + "," + + getConfig(start).getFlywaySchemaHistory(); update(start, DROP_QUERY, NO_OP_SETTER); } } @@ -1642,6 +1336,19 @@ public static Map> getTenantIdsForUserIds(Start start, return new HashMap<>(); } + @TestOnly + public static Set getAllMigratedScripts(Start start) throws SQLException, StorageQueryException { + String QUERY = "SELECT script FROM " + getConfig(start).getFlywaySchemaHistory() + ";"; + return execute(start, QUERY, NO_OP_SETTER, result -> { + Set scripts = new HashSet<>(); + + while (result.next()) { + scripts.add(result.getString("script")); + } + return scripts; + }); + } + @TestOnly public static String[] getAllTablesInTheDatabase(Start start) throws StorageQueryException, SQLException { if (!Start.isTesting) { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java index f57c4402..90e84efa 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java @@ -46,29 +46,18 @@ static String getQueryToCreateJWTSigningTable(Start start) { * defined * keys in the future. */ - String schema = Config.getConfig(start).getTableSchema(); - String jwtSigningKeysTable = Config.getConfig(start).getJWTSigningKeysTable(); + String schema = getConfig(start).getTableSchema(); + String jwtSigningKeysTable = getConfig(start).getJWTSigningKeysTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + jwtSigningKeysTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "key_id VARCHAR(255) NOT NULL," - + "key_string TEXT NOT NULL," + + "key_string TEXT NOT NULL," + "algorithm VARCHAR(10) NOT NULL," - + "created_at BIGINT," - + "CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, null, "pkey") - + " PRIMARY KEY(app_id, key_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; + + "created_at BIGINT," + + "CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, null, "pkey") + " PRIMARY KEY(key_id));"; // @formatter:on } - public static String getQueryToCreateAppIdIndexForJWTSigningTable(Start start) { - return "CREATE INDEX IF NOT EXISTS jwt_signing_keys_app_id_index ON " - + getConfig(start).getJWTSigningKeysTable() + " (app_id);"; - } - public static List getJWTSigningKeys_Transaction(Start start, Connection con, AppIdentifier appIdentifier) throws SQLException, StorageQueryException { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java index 592cdbd0..cc89eb87 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java @@ -37,87 +37,6 @@ import static io.supertokens.storage.postgresql.config.Config.getConfig; public class MultitenancyQueries { - static String getQueryToCreateTenantConfigsTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tenantConfigsTable = Config.getConfig(start).getTenantConfigsTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tenantConfigsTable + " (" - + "connection_uri_domain VARCHAR(256) DEFAULT ''," - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "core_config TEXT," - + "email_password_enabled BOOLEAN," - + "passwordless_enabled BOOLEAN," - + "third_party_enabled BOOLEAN," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)" - + ");"; - // @formatter:on - } - - static String getQueryToCreateTenantThirdPartyProvidersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tenantThirdPartyProvidersTable = Config.getConfig(start).getTenantThirdPartyProvidersTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tenantThirdPartyProvidersTable + " (" - + "connection_uri_domain VARCHAR(256) DEFAULT ''," - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "third_party_id VARCHAR(28) NOT NULL," - + "name VARCHAR(64)," - + "authorization_endpoint TEXT," - + "authorization_endpoint_query_params TEXT," - + "token_endpoint TEXT," - + "token_endpoint_body_params TEXT," - + "user_info_endpoint TEXT," - + "user_info_endpoint_query_params TEXT," - + "user_info_endpoint_headers TEXT," - + "jwks_uri TEXT," - + "oidc_discovery_endpoint TEXT," - + "require_email BOOLEAN," - + "user_info_map_from_id_token_payload_user_id VARCHAR(64)," - + "user_info_map_from_id_token_payload_email VARCHAR(64)," - + "user_info_map_from_id_token_payload_email_verified VARCHAR(64)," - + "user_info_map_from_user_info_endpoint_user_id VARCHAR(64)," - + "user_info_map_from_user_info_endpoint_email VARCHAR(64)," - + "user_info_map_from_user_info_endpoint_email_verified VARCHAR(64)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "tenant_id", "fkey") - + " FOREIGN KEY(connection_uri_domain, app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateTenantIdIndexForTenantThirdPartyProvidersTable(Start start) { - return "CREATE INDEX IF NOT EXISTS tenant_thirdparty_providers_tenant_id_index ON " - + getConfig(start).getTenantThirdPartyProvidersTable() + " (connection_uri_domain, app_id, tenant_id);"; - } - - static String getQueryToCreateTenantThirdPartyProviderClientsTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tenantThirdPartyProvidersTable = Config.getConfig(start).getTenantThirdPartyProviderClientsTable(); - return "CREATE TABLE IF NOT EXISTS " + tenantThirdPartyProvidersTable + " (" - + "connection_uri_domain VARCHAR(256) DEFAULT ''," - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "third_party_id VARCHAR(28) NOT NULL," - + "client_type VARCHAR(64) NOT NULL DEFAULT ''," - + "client_id VARCHAR(256) NOT NULL," - + "client_secret TEXT," - + "scope VARCHAR(128)[]," - + "force_pkce BOOLEAN," - + "additional_config TEXT," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "third_party_id", "fkey") - + " FOREIGN KEY(connection_uri_domain, app_id, tenant_id, third_party_id)" - + " REFERENCES " + Config.getConfig(start).getTenantThirdPartyProvidersTable() + " (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE" - + ");"; - } - - public static String getQueryToCreateThirdPartyIdIndexForTenantThirdPartyProviderClientsTable(Start start) { - return "CREATE INDEX IF NOT EXISTS tenant_thirdparty_provider_clients_third_party_id_index ON " - + getConfig(start).getTenantThirdPartyProviderClientsTable() + " (connection_uri_domain, app_id, tenant_id, third_party_id);"; - } private static void executeCreateTenantQueries(Start start, Connection sqlCon, TenantConfig tenantConfig) throws SQLException, StorageQueryException { diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java index 8f8df3d6..b17c1e0c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java @@ -47,115 +47,40 @@ public class PasswordlessQueries { public static String getQueryToCreateUsersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String usersTable = Config.getConfig(start).getPasswordlessUsersTable(); - - return "CREATE TABLE IF NOT EXISTS " + usersTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "user_id CHAR(36) NOT NULL," - + "email VARCHAR(256)," - + "phone_number VARCHAR(256)," - + "time_joined BIGINT NOT NULL, " - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "user_id", "fkey") - + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + - " (app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id)" - + ");"; - } - - static String getQueryToCreatePasswordlessUserToTenantTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String passwordlessUserToTenantTable = Config.getConfig(start).getPasswordlessUserToTenantTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + passwordlessUserToTenantTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "user_id CHAR(36) NOT NULL," - + "email VARCHAR(256)," - + "phone_number VARCHAR(256)," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, "email", "key") - + " UNIQUE (app_id, tenant_id, email)," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, "phone_number", "key") - + " UNIQUE (app_id, tenant_id, phone_number)," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, passwordlessUserToTenantTable, "user_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + - "(app_id, tenant_id, user_id) ON DELETE CASCADE" - + ");"; - // @formatter:on + String schema = getConfig(start).getTableSchema(); + String usersTable = getConfig(start).getPasswordlessUsersTable(); + + return "CREATE TABLE IF NOT EXISTS " + usersTable + " (" + "user_id CHAR(36) NOT NULL," + + "email VARCHAR(256) CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "email", "key") + + " UNIQUE," + "phone_number VARCHAR(256) CONSTRAINT " + + Utils.getConstraintName(schema, usersTable, "phone_number", "key") + " UNIQUE," + + "time_joined BIGINT NOT NULL, " + "CONSTRAINT " + + Utils.getConstraintName(schema, usersTable, null, "pkey") + " PRIMARY KEY (user_id)" + ");"; } public static String getQueryToCreateDevicesTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String devicesTable = Config.getConfig(start).getPasswordlessDevicesTable(); - - return "CREATE TABLE IF NOT EXISTS " + devicesTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "device_id_hash CHAR(44) NOT NULL," - + "email VARCHAR(256), " - + "phone_number VARCHAR(256)," - + "link_code_salt CHAR(44) NOT NULL," - + "failed_attempts INT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, devicesTable, "tenant_id", "fkey") - + " FOREIGN KEY(app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, devicesTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, device_id_hash)" - + ");"; - } + String schema = getConfig(start).getTableSchema(); + String devicesTable = getConfig(start).getPasswordlessDevicesTable(); - public static String getQueryToCreateTenantIdIndexForDevicesTable(Start start) { - return "CREATE INDEX passwordless_devices_tenant_id_index ON " - + Config.getConfig(start).getPasswordlessDevicesTable() + "(app_id, tenant_id);"; + return "CREATE TABLE IF NOT EXISTS " + devicesTable + " (" + "device_id_hash CHAR(44) NOT NULL," + + "email VARCHAR(256), " + "phone_number VARCHAR(256)," + "link_code_salt CHAR(44) NOT NULL," + + "failed_attempts INT NOT NULL," + "CONSTRAINT " + + Utils.getConstraintName(schema, devicesTable, null, "pkey") + " PRIMARY KEY (device_id_hash));"; } public static String getQueryToCreateCodesTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String codesTable = Config.getConfig(start).getPasswordlessCodesTable(); - - return "CREATE TABLE IF NOT EXISTS " + codesTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "code_id CHAR(36) NOT NULL," - + "device_id_hash CHAR(44) NOT NULL," - + "link_code_hash CHAR(44) NOT NULL," - + "created_at BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, codesTable, "link_code_hash", "key") - + " UNIQUE (app_id, tenant_id, link_code_hash)," - + "CONSTRAINT " + Utils.getConstraintName(schema, codesTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, code_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, codesTable, "device_id_hash", "fkey") - + " FOREIGN KEY (app_id, tenant_id, device_id_hash)" - + " REFERENCES " + Config.getConfig(start).getPasswordlessDevicesTable() + - "(app_id, tenant_id, device_id_hash)" - + " ON DELETE CASCADE ON UPDATE CASCADE" - + ");"; - } - - public static String getQueryToCreateDeviceEmailIndex(Start start) { - return "CREATE INDEX passwordless_devices_email_index ON " - + Config.getConfig(start).getPasswordlessDevicesTable() + " (app_id, tenant_id, email);"; // USING hash - } - - public static String getQueryToCreateDevicePhoneNumberIndex(Start start) { - return "CREATE INDEX passwordless_devices_phone_number_index ON " - + Config.getConfig(start).getPasswordlessDevicesTable() + - " (app_id, tenant_id, phone_number);"; // USING hash - } - - public static String getQueryToCreateCodeDeviceIdHashIndex(Start start) { - return "CREATE INDEX IF NOT EXISTS passwordless_codes_device_id_hash_index ON " - + Config.getConfig(start).getPasswordlessCodesTable() + "(app_id, tenant_id, device_id_hash);"; - } - - public static String getQueryToCreateCodeCreatedAtIndex(Start start) { - return "CREATE INDEX passwordless_codes_created_at_index ON " - + Config.getConfig(start).getPasswordlessCodesTable() + "(app_id, tenant_id, created_at);"; + String schema = getConfig(start).getTableSchema(); + String codesTable = getConfig(start).getPasswordlessCodesTable(); + + return "CREATE TABLE IF NOT EXISTS " + codesTable + " (" + "code_id CHAR(36) NOT NULL," + + "device_id_hash CHAR(44) NOT NULL," + "link_code_hash CHAR(44) NOT NULL CONSTRAINT " + + Utils.getConstraintName(schema, codesTable, "link_code_hash", "key") + " UNIQUE," + + "created_at BIGINT NOT NULL," + "CONSTRAINT " + + Utils.getConstraintName(schema, codesTable, null, "pkey") + " PRIMARY KEY (code_id)," + "CONSTRAINT " + + Utils.getConstraintName(schema, codesTable, "device_id_hash", "fkey") + + " FOREIGN KEY (device_id_hash) " + "REFERENCES " + + getConfig(start).getPasswordlessDevicesTable() + + "(device_id_hash) ON DELETE CASCADE ON UPDATE CASCADE);"; } public static void createDeviceWithCode(Start start, TenantIdentifier tenantIdentifier, String email, diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java index d6685638..8c77a36c 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/SessionQueries.java @@ -43,12 +43,10 @@ public class SessionQueries { public static String getQueryToCreateSessionInfoTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String sessionInfoTable = Config.getConfig(start).getSessionInfoTable(); + String schema = getConfig(start).getTableSchema(); + String sessionInfoTable = getConfig(start).getSessionInfoTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + sessionInfoTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + "session_handle VARCHAR(255) NOT NULL," + "user_id VARCHAR(128) NOT NULL," + "refresh_token_hash_2 VARCHAR(128) NOT NULL," @@ -56,48 +54,21 @@ public static String getQueryToCreateSessionInfoTable(Start start) { + "expires_at BIGINT NOT NULL," + "created_at_time BIGINT NOT NULL," + "jwt_user_payload TEXT," - + "use_static_key BOOLEAN NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, null, "pkey") - + " PRIMARY KEY(app_id, tenant_id, session_handle)," - + "CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, "tenant_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + "(app_id, tenant_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, sessionInfoTable, null, "pkey") + " PRIMARY KEY(session_handle)" + " );"; // @formatter:on } - public static String getQueryToCreateTenantIdIndexForSessionInfoTable(Start start) { - return "CREATE INDEX session_info_tenant_id_index ON " - + Config.getConfig(start).getSessionInfoTable() + "(app_id, tenant_id);"; - } - static String getQueryToCreateAccessTokenSigningKeysTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String accessTokenSigningKeysTable = Config.getConfig(start).getAccessTokenSigningKeysTable(); + String schema = getConfig(start).getTableSchema(); + String accessTokenSigningKeysTable = getConfig(start).getAccessTokenSigningKeysTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + accessTokenSigningKeysTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "created_at_time BIGINT NOT NULL," + "value TEXT," - + "CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, null, "pkey") - + " PRIMARY KEY(app_id, created_at_time)," - + "CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, "app_id", "fkey") - + " FOREIGN KEY (app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + "(app_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, accessTokenSigningKeysTable, null, "pkey") + " PRIMARY KEY(created_at_time)" + " );"; // @formatter:on } - public static String getQueryToCreateAppIdIndexForAccessTokenSigningKeysTable(Start start) { - return "CREATE INDEX access_token_signing_keys_app_id_index ON " - + Config.getConfig(start).getAccessTokenSigningKeysTable() + "(app_id);"; - } - - static String getQueryToCreateSessionExpiryIndex(Start start) { - return "CREATE INDEX session_expiry_index ON " - + Config.getConfig(start).getSessionInfoTable() + "(expires_at);"; - } - public static void createNewSession(Start start, TenantIdentifier tenantIdentifier, String sessionHandle, String userId, String refreshTokenHash2, JsonObject userDataInDatabase, long expiry, JsonObject userDataInJWT, diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java index dad5e52d..fedc8dad 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java @@ -22,88 +22,30 @@ public class TOTPQueries { public static String getQueryToCreateUsersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tableName = Config.getConfig(start).getTotpUsersTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + return "CREATE TABLE IF NOT EXISTS " + Config.getConfig(start).getTotpUsersTable() + " (" + "user_id VARCHAR(128) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY (app_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateAppIdIndexForUsersTable(Start start) { - return "CREATE INDEX totp_users_app_id_index ON " - + Config.getConfig(start).getTotpUsersTable() + "(app_id);"; + + "PRIMARY KEY (user_id))"; } public static String getQueryToCreateUserDevicesTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tableName = Config.getConfig(start).getTotpUserDevicesTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "user_id VARCHAR(128) NOT NULL," - + "device_name VARCHAR(256) NOT NULL," + return "CREATE TABLE IF NOT EXISTS " + Config.getConfig(start).getTotpUserDevicesTable() + " (" + + "user_id VARCHAR(128) NOT NULL," + "device_name VARCHAR(256) NOT NULL," + "secret_key VARCHAR(256) NOT NULL," - + "period INTEGER NOT NULL," - + "skew INTEGER NOT NULL," - + "verified BOOLEAN NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY (app_id, user_id, device_name)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "user_id", "fkey") - + " FOREIGN KEY (app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getTotpUsersTable() + "(app_id, user_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateUserIdIndexForUserDevicesTable(Start start) { - return "CREATE INDEX totp_user_devices_user_id_index ON " - + Config.getConfig(start).getTotpUserDevicesTable() + "(app_id, user_id);"; + + "period INTEGER NOT NULL," + "skew INTEGER NOT NULL," + "verified BOOLEAN NOT NULL," + + "PRIMARY KEY (user_id, device_name)," + + "FOREIGN KEY (user_id) REFERENCES " + + Config.getConfig(start).getTotpUsersTable() + "(user_id) ON DELETE CASCADE);"; } public static String getQueryToCreateUsedCodesTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String tableName = Config.getConfig(start).getTotpUsedCodesTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + return "CREATE TABLE IF NOT EXISTS " + Config.getConfig(start).getTotpUsedCodesTable() + " (" + "user_id VARCHAR(128) NOT NULL, " + "code VARCHAR(8) NOT NULL," + "is_valid BOOLEAN NOT NULL," + "expiry_time_ms BIGINT NOT NULL," + "created_time_ms BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id, created_time_ms)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "user_id", "fkey") - + " FOREIGN KEY (app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getTotpUsersTable() + "(app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "tenant_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateUserIdIndexForUsedCodesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS totp_used_codes_user_id_index ON " - + Config.getConfig(start).getTotpUsedCodesTable() + " (app_id, user_id)"; - } - - public static String getQueryToCreateTenantIdIndexForUsedCodesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS totp_used_codes_tenant_id_index ON " - + Config.getConfig(start).getTotpUsedCodesTable() + " (app_id, tenant_id)"; - } - - public static String getQueryToCreateUsedCodesExpiryTimeIndex(Start start) { - return "CREATE INDEX IF NOT EXISTS totp_used_codes_expiry_time_ms_index ON " - + Config.getConfig(start).getTotpUsedCodesTable() + " (app_id, tenant_id, expiry_time_ms)"; + + "PRIMARY KEY (user_id, created_time_ms)," + + "FOREIGN KEY (user_id) REFERENCES " + + Config.getConfig(start).getTotpUsersTable() + "(user_id) ON DELETE CASCADE);"; } private static int insertUser_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId) diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java index 2a37c9dc..818fbf25 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java @@ -45,58 +45,19 @@ public class ThirdPartyQueries { static String getQueryToCreateUsersTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String thirdPartyUsersTable = Config.getConfig(start).getThirdPartyUsersTable(); + String schema = getConfig(start).getTableSchema(); + String thirdPartyUsersTable = getConfig(start).getThirdPartyUsersTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + thirdPartyUsersTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "third_party_id VARCHAR(28) NOT NULL," - + "third_party_user_id VARCHAR(256) NOT NULL," - + "user_id CHAR(36) NOT NULL," + + "third_party_user_id VARCHAR(128) NOT NULL," + + "user_id CHAR(36) NOT NULL CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, "user_id", "key") + " UNIQUE," + "email VARCHAR(256) NOT NULL," + "time_joined BIGINT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, "user_id", "fkey") - + " FOREIGN KEY(app_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + - " (app_id, user_id) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, null, "pkey") - + " PRIMARY KEY (app_id, user_id)" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUsersTable, null, "pkey") + " PRIMARY KEY (third_party_id, third_party_user_id));"; // @formatter:on } - public static String getQueryToThirdPartyUserEmailIndex(Start start) { - return "CREATE INDEX IF NOT EXISTS thirdparty_users_email_index ON " - + Config.getConfig(start).getThirdPartyUsersTable() + " (app_id, email);"; - } - - public static String getQueryToThirdPartyUserIdIndex(Start start) { - return "CREATE INDEX IF NOT EXISTS thirdparty_users_thirdparty_user_id_index ON " - + Config.getConfig(start).getThirdPartyUsersTable() + " (app_id, third_party_id, third_party_user_id);"; - } - - static String getQueryToCreateThirdPartyUserToTenantTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String thirdPartyUserToTenantTable = Config.getConfig(start).getThirdPartyUserToTenantTable(); - // @formatter:off - return "CREATE TABLE IF NOT EXISTS " + thirdPartyUserToTenantTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," - + "user_id CHAR(36) NOT NULL," - + "third_party_id VARCHAR(28) NOT NULL," - + "third_party_user_id VARCHAR(256) NOT NULL," - + "CONSTRAINT " + - Utils.getConstraintName(schema, thirdPartyUserToTenantTable, "third_party_user_id", "key") - + " UNIQUE (app_id, tenant_id, third_party_id, third_party_user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, null, "pkey") - + " PRIMARY KEY (app_id, tenant_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, thirdPartyUserToTenantTable, "user_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id, user_id)" - + " REFERENCES " + Config.getConfig(start).getUsersTable() + - "(app_id, tenant_id, user_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIdentifier, String id, String email, LoginMethod.ThirdParty thirdParty, long timeJoined) diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java index 24f4fab7..4b00384f 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java @@ -39,32 +39,24 @@ public class UserIdMappingQueries { public static String getQueryToCreateUserIdMappingTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); - String userIdMappingTable = Config.getConfig(start).getUserIdMappingTable(); + String schema = getConfig(start).getTableSchema(); + String userIdMappingTable = getConfig(start).getUserIdMappingTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + userIdMappingTable + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "supertokens_user_id CHAR(36) NOT NULL," - + "external_user_id VARCHAR(128) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "supertokens_user_id", "key") - + " UNIQUE (app_id, supertokens_user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "external_user_id", "key") - + " UNIQUE (app_id, external_user_id)," + + "supertokens_user_id CHAR(36) NOT NULL " + + "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "supertokens_user_id", "key") + " UNIQUE," + + "external_user_id VARCHAR(128) NOT NULL" + + " CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "external_user_id", "key") + " UNIQUE," + "external_user_id_info TEXT," - + "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, null, "pkey") - + " PRIMARY KEY(app_id, supertokens_user_id, external_user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "supertokens_user_id", "fkey") - + " FOREIGN KEY (app_id, supertokens_user_id)" - + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" + " ON DELETE CASCADE" - + ");"; + + " CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, null, "pkey") + + " PRIMARY KEY(supertokens_user_id, external_user_id)," + + ("CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "supertokens_user_id", "fkey") + + " FOREIGN KEY (supertokens_user_id)" + + " REFERENCES " + getConfig(start).getUsersTable() + "(user_id)" + + " ON DELETE CASCADE);"); // @formatter:on } - public static String getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable(Start start) { - return "CREATE INDEX userid_mapping_supertokens_user_id_index ON " - + getConfig(start).getUserIdMappingTable() + "(app_id, supertokens_user_id);"; - } - public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, String superTokensUserId, String externalUserId, String externalUserIdInfo) throws SQLException, StorageQueryException { String QUERY = "INSERT INTO " + Config.getConfig(start).getUserIdMappingTable() diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java index 1d2b6231..185ea797 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserMetadataQueries.java @@ -38,23 +38,12 @@ public static String getQueryToCreateUserMetadataTable(Start start) { String tableName = Config.getConfig(start).getUserMetadataTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128) NOT NULL," + "user_metadata TEXT NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY(app_id, user_id)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(user_id)" + " );"; // @formatter:on } - public static String getQueryToCreateAppIdIndexForUserMetadataTable(Start start) { - return "CREATE INDEX user_metadata_app_id_index ON " - + Config.getConfig(start).getUserMetadataTable() + "(app_id);"; - } - public static int deleteUserMetadata(Start start, AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException { String QUERY = "DELETE FROM " + getConfig(start).getUserMetadataTable() diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java index 549cac86..fbe8e260 100644 --- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java +++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java @@ -34,50 +34,29 @@ public class UserRolesQueries { public static String getQueryToCreateRolesTable(Start start) { - String schema = Config.getConfig(start).getTableSchema(); + String schema = getConfig(start).getTableSchema(); String tableName = getConfig(start).getRolesTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "role VARCHAR(255) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY(app_id, role)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "app_id", "fkey") - + " FOREIGN KEY(app_id)" - + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(role)" + " );"; - public static String getQueryToCreateAppIdIndexForRolesTable(Start start) { - return "CREATE INDEX roles_app_id_index ON " + getConfig(start).getRolesTable() + "(app_id);"; + // @formatter:on } public static String getQueryToCreateRolePermissionsTable(Start start) { String tableName = getConfig(start).getUserRolesPermissionsTable(); - String schema = Config.getConfig(start).getTableSchema(); + String schema = getConfig(start).getTableSchema(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," + "role VARCHAR(255) NOT NULL," + "permission VARCHAR(255) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY(app_id, role, permission)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "role", "fkey") - + " FOREIGN KEY(app_id, role)" - + " REFERENCES " + getConfig(start).getRolesTable() + "(app_id, role) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateRoleIndexForRolePermissionsTable(Start start) { - return "CREATE INDEX role_permissions_role_index ON " + getConfig(start).getUserRolesPermissionsTable() - + "(app_id, role);"; - } + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(role, permission)," + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "role", "fkey") + " FOREIGN KEY(role)" + + " REFERENCES " + getConfig(start).getRolesTable() + +"(role) ON DELETE CASCADE );"; - static String getQueryToCreateRolePermissionsPermissionIndex(Start start) { - return "CREATE INDEX role_permissions_permission_index ON " + getConfig(start).getUserRolesPermissionsTable() - + "(app_id, permission);"; + // @formatter:on } public static String getQueryToCreateUserRolesTable(Start start) { @@ -85,35 +64,14 @@ public static String getQueryToCreateUserRolesTable(Start start) { String tableName = getConfig(start).getUserRolesTable(); // @formatter:off return "CREATE TABLE IF NOT EXISTS " + tableName + " (" - + "app_id VARCHAR(64) DEFAULT 'public'," - + "tenant_id VARCHAR(64) DEFAULT 'public'," + "user_id VARCHAR(128) NOT NULL," + "role VARCHAR(255) NOT NULL," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") - + " PRIMARY KEY(app_id, tenant_id, user_id, role)," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "role", "fkey") - + " FOREIGN KEY(app_id, role)" - + " REFERENCES " + getConfig(start).getRolesTable() + "(app_id, role) ON DELETE CASCADE," - + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "tenant_id", "fkey") - + " FOREIGN KEY (app_id, tenant_id)" - + " REFERENCES " + Config.getConfig(start).getTenantsTable() + "(app_id, tenant_id) ON DELETE CASCADE" - + ");"; - // @formatter:on - } - - public static String getQueryToCreateTenantIdIndexForUserRolesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS user_roles_tenant_id_index ON " + getConfig(start).getUserRolesTable() + - "(app_id, tenant_id);"; - } - - public static String getQueryToCreateRoleIndexForUserRolesTable(Start start) { - return "CREATE INDEX IF NOT EXISTS user_roles_app_id_role_index ON " + getConfig(start).getUserRolesTable() + - "(app_id, role);"; - } + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(user_id, role)," + + "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "role", "fkey") + " FOREIGN KEY(role)" + + " REFERENCES " + getConfig(start).getRolesTable() + +"(role) ON DELETE CASCADE );"; - public static String getQueryToCreateUserRolesRoleIndex(Start start) { - return "CREATE INDEX IF NOT EXISTS user_roles_role_index ON " + getConfig(start).getUserRolesTable() - + "(app_id, tenant_id, role);"; + // @formatter:on } public static boolean createNewRoleOrDoNothingIfExists_Transaction(Start start, Connection con, diff --git a/src/test/java/io/supertokens/storage/postgresql/test/FlywayMigrationTest.java b/src/test/java/io/supertokens/storage/postgresql/test/FlywayMigrationTest.java new file mode 100644 index 00000000..2b60e959 --- /dev/null +++ b/src/test/java/io/supertokens/storage/postgresql/test/FlywayMigrationTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.storage.postgresql.test; + +import io.supertokens.ProcessState; +import io.supertokens.storage.postgresql.Start; +import io.supertokens.storageLayer.StorageLayer; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; + +import java.util.HashSet; +import java.util.Set; + +import static io.supertokens.storage.postgresql.queries.GeneralQueries.getAllMigratedScripts; +import static org.junit.Assert.*; + +public class FlywayMigrationTest { + + private static final String BASE = "io.supertokens.storage.postgresql.migrations."; + + private static final String BASELINE_SCRIPT = "<< Flyway Baseline >>"; + private static final String V1__init_database = BASE + "V1__init_database"; + private static final String V2__plugin_version_3_0_0 = BASE + "V2__plugin_version_3_0_0"; + private static final String V3__plugin_version_4_0_0 = BASE + "V3__plugin_version_4_0_0"; + private static final String V4__plugin_version_5_0_0 = BASE + "V4__plugin_version_5_0_0"; + private static final String V5__plugin_version_5_0_4 = BASE + "V5__plugin_version_5_0_4"; + private static final String V6__core_version_7_0_12 = BASE + "V6__core_version_7_0_12"; + + + @Rule + public TestRule watchman = Utils.getOnFailure(); + + @AfterClass + public static void afterTesting() { + Utils.afterTesting(); + } + + @Before + public void beforeEach() { + Utils.reset(); + } + + @Test + public void testThatDefaultConfigLoadsCorrectly() throws Exception { + String[] args = {"../"}; + + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + Set allMigrationScripts = allMigrationScripts(); + Set scripts = getAllMigratedScripts((Start) StorageLayer.getStorage(process.getProcess())); + assertTrue(allMigrationScripts.equals(scripts)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + @Test + public void testWhenV1MigrationFails() throws Exception { + String[] args = {"../"}; + + Utils.setValueInConfig("postgresql_key_value_table_name", "^#1@"); + TestingProcessManager.TestingProcess process = TestingProcessManager.start(args); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED)); + + Set allMigrationScripts = allMigrationScripts(); + Set scripts = getAllMigratedScripts((Start) StorageLayer.getStorage(process.getProcess())); + assertTrue(allMigrationScripts.equals(scripts)); + + process.kill(); + assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED)); + } + + public Set allMigrationScripts() { + Set set = new HashSet<>(); + set.add(BASELINE_SCRIPT); + set.add(V1__init_database); + set.add(V2__plugin_version_3_0_0); + set.add(V3__plugin_version_4_0_0); + set.add(V4__plugin_version_5_0_0); + set.add(V5__plugin_version_5_0_4); + set.add(V6__core_version_7_0_12); + return set; + } +}